#!/usr/local/bin/bash
#
# Copyright (C) 2004 SIPfoundry Inc.
# Licensed by SIPfoundry under the LGPL license.
#
# Copyright (C) 2004 Pingtel Corp.
# Licensed to SIPfoundry under a Contributor Agreement.


Action=RUN
Status=0
Args=""

# Declare script variables in a manner that unit test
# (or end user I suppose) can override
: ${SslDir:=/usr/local/etc/sipxpbx/ssl}
: ${AuthoritiesDir:=$SslDir/authorities}
: ${JavaCmd:=`/usr/local/bin/sipx-config --java`}
: ${ConfigDefs:=/usr/local/etc/sipxpbx/config.defs}
: ${Configpp:=/usr/local/bin/configpp}
: ${ConfigFiles:=/usr/local/etc/sipxpbx/sipxconfig.properties /usr/local/etc/sipxpbx/topology.xml}
: ${PidFile:=/var/run/sipxpbx/sipxconfig.pid}
: ${DatabaseProject:=/usr/local/etc/sipxpbx/database/database.xml}
: ${LibDir:=/usr/local/share/sipxpbx/lib}
: ${Psql:=psql}
: ${ServiceDir:=/usr/local/etc/rc.d}
: ${SubstituteUser:=su}
: ${Chown:=chown}
: ${LogDir:=/var/log/sipxpbx}
: ${TmpDir:=/var/sipxdata/tmp}
: ${RunningUser:=sipx}

TEST_WRITE_ACCESS="\
  /usr/local/etc/sipxpbx/config.defs \
  /usr/local/etc/sipxpbx/proxy-config.in \
  /usr/local/etc/sipxpbx/registrar-config.in \
  /usr/local/etc/sipxpbx/sipxpresence-config.in \
  /usr/local/etc/sipxpbx/authproxy-config.in \
  /usr/local/etc/sipxpbx/sipxpark-config.in \
  /usr/local/etc/sipxpbx/status-config.in \
  /usr/local/etc/sipxpbx/callresolver-config.in \
  /usr/local/etc/sipxpbx/sipxdialog-config.in \
  /usr/local/etc/sipxpbx/status-plugin.xml.in \
  /usr/local/etc/sipxpbx/authrules.xml.in \
  /usr/local/etc/sipxpbx/fallbackrules.xml.in \
  /usr/local/etc/sipxpbx/mappingrules.xml.in \
  /usr/local/etc/sipxpbx/forwardingrules.xml.in"

Database=SIPXCONFIG
RequiredDatabaseVersion=4
IndexDir=${TmpDir}/index

# This function determines the correct service name for Postgres.
postgresService() {
    # If the user has already specified $Service, do not modify it.
    if test -n "$POSTGRES_SERVICE"
    then
        echo -e "$POSTGRES_SERVICE"
    fi
    
    if [ -f /etc/init.d/rhdb ]
    then
	    # Red Hat Enterprise uses the name rhdb.
        echo -e rhdb
    fi
    
    # Most other distributions use the name postgresql.
    echo -e postgresql
}

# Search/replace variables used in config.defs into config files
runConfigpp() {
    
    # If the "config.defs" file exists and the <name>.in file exists for a
    # configuration file, then run the config preprocessor to generate the
    # fully resolved configuration file.
    if [ -f "$ConfigDefs" ]
    then
      for i in $ConfigFiles ; do
        if [ -f "${i}.in" ]
          then
            ${Configpp} --defs "${ConfigDefs}" --in "${i}.in" --out "$i"
          fi
      done
    fi
}

# Create a truststore of the same Certificate Authorities the
# other SIP servers use.
createAuthoritiesTruststore() {    
    # List of authorities accepted system-wide
    if [ -d $AuthoritiesDir ]
    then
      TrustStore=$SslDir/.authorities.jks
      Certificates=`find $AuthoritiesDir | egrep -i '.(crt|crl)$'`
      /usr/local/bin/ssl-cert/create-ssl-truststore.sh --checksum $SslDir/.authorities-contents.md5 --truststore $TrustStore  $Certificates
      # password on command line is not security issue. catrust and directory has appropriate
      # permissions, password is required by keystores
      TrustStoreOpts="\
	  -Djavax.net.ssl.trustStore=$TrustStore \
	  -Djavax.net.ssl.trustStoreType=JKS \
	  -Djavax.net.ssl.trustStorePassword=changeit"
    else
      # make empty as extra measure to ensure doesn't come from env. var
      # this is when Java's CA database has required certs
      TrustStoreOpts=""
    fi
}

    
# Take a list of files or directories 
# and expand pathnames to create a string for PATH-based variables 
classpath() {
    Cpath=""
    for f in $@; do
      Cpath=$Cpath:$f
    done
    # trim first colon
    echo ${Cpath:1}
}

# Incase system failed suddenly, clear previous locks
clearIndexLocks() {
  rm ${IndexDir}/*.lock 2>/dev/null
}

# Turn control over to ant to run database command
databaseCommand() {

    runConfigpp

    databaseCheck

    clearIndexLocks

    Classpath=`classpath \
        /usr/local/etc/sipxpbx \
        ${LibDir}/*.jar`
        
    # XCF-1275 - Do not want to suppress output if there is an error
    # however want to be quiet if everything is ok for clean rpm install
    # output
    CmdOut=`$JavaCmd \
        -Djava.net.preferIPv4Stack=true \
        -classpath $Classpath \
        org.apache.tools.ant.launch.Launcher \
        -emacs \
        -quiet \
        -Dlib.dir=${LibDir} \
        -Dlog.dir=${LogDir} \
        -Dtmp.dir=${TmpDir} \
        -f ${DatabaseProject} \
        $@`
    if [ $? != "0" ]; then
       echo "$CmdOut" | grep -v '^Unable to locate tools.jar' >&2
    fi       
}

runningUserCheck() {
  CurrentUser=`id -un`
  if [ "$CurrentUser" != "$RunningUser" ]; then
    echo "Only user $RunningUser can run this command." 1>&2
    return 1
  fi
  return 0
}
    
# Run Web interface
onWebUi() {

    runningUserCheck || exit 1 

    runConfigpp   

    databaseCommand upgrade
    databaseVersionCheck

    createAuthoritiesTruststore
    /usr/local/bin/ssl-cert/create-ssl-keystore.sh
    
    JavaOpts="$SIPXCONFIG_OPTS"

    clearIndexLocks

    X509Algorithm=`$JavaCmd \
	    -classpath "$Classpath" \
	    org.sipfoundry.sipxconfig.common.X509Selector`

    # Used in sipxconfig-jetty.xml only
    SystemProps="\
        -Djava.io.tmpdir=${TmpDir} \
        -Djetty.lib.dir=/usr/local/share/sipxpbx/lib \
        -Djetty.conf.dir=/usr/local/etc/sipxpbx \
        -Djetty.log.dir=${LogDir} \
        -Djetty.x509.algorithm=${X509Algorithm} \
        -Dorg.apache.lucene.lockdir=${IndexDir} \
        -Dorg.apache.commons.logging.Log=org.apache.commons.logging.impl.Log4JLogger"

    Classpath=`classpath \
        /usr/local/etc/sipxpbx \
        ${LibDir}/*.jar`

    echo $$ > ${PidFile}
    
    exec $JavaCmd \
	    $SystemProps \
	    $JavaOpts \
	    $TrustStoreOpts \
	    -classpath "$Classpath" \
	    org.mortbay.jetty.Server /usr/local/etc/sipxpbx/sipxconfig-jetty.xml
}

# Return false if postgres is 
#  1. not running 
#  2. running but not setup to communicate w/java or ${Psql} command
#  3. Database is not created
databaseCheck() {
    ${Psql} --list --field-separator , --no-align --username pgsql \
    | cut -d , -f 1 \
    | grep "${Database}" > /dev/null 2>&1
}

# Check the database version.  If it is wrong, then exit, because it's dangerous to
# run sipXconfig on top of the wrong database version.
databaseVersionCheck() {
    dbver=`${Psql} --tuples-only --no-align \
           --command "select max(vh.version) from version_history vh;" \
           ${Database} pgsql`
    if [ $? -ne 0 -o "$dbver" = "" ]
    then
        echo "Error: could not get the database version.  Exiting now."
        exit 1
    fi
    if [ "$dbver" -ne $RequiredDatabaseVersion ]
    then
        if [ $dbver -eq 0 ]
        then
            cat <<EOF 
    Error: bad ${Database} database version.  
    This database was created by a prerelease version of sipXconfig.  
    You should drop and recreate the database by running:

        /usr/local/bin/sipxconfig.sh --database drop create

    Exiting now."
EOF
        else
            cat <<EOF
    Error: bad ${Database} database version.
    Expected version $RequiredDatabaseVersion, got version $dbver.  
    This database was created or modified by a newer version of sipXconfig.  
    Exiting now."
EOF
        fi
        exit 1
    fi
}

# Called as root in sipxpbx startup script to do initial setup,
# but can be called manually and is harmless if called multiple times.
onSetup() {
   if ! databaseCheck
   then
       echo "Initializing configuration database..."
       postgresSetup
       databaseCommand create
       echo -n "Configuration database initialized"
   fi
}

# Configure postgres to accept TCP connections for communication
# from Java
postgresSetup() {

  # Set up the server.
  Service=postgresql

  # May not by running, so eat up error (ENG-314)
  ${ServiceDir}/${Service} stop 2>&1 1> /dev/null

  # Custom
  if test -z $PGDATA
  then
      PGDATA=~pgsql/data
  fi

  # Postgres db is initialized on startup on Redhat, but not on other
  # distros so unless we put a "if distro=rh"  we need to init here
  if [ ! -f $PGDATA/PG_VERSION ] || [ ! -d $PGDATA/base ]
  then
      # XXX only works if user has added postgresql_enable="YES" to
      # an rc.conf file.
      sh /usr/local/rc.d/postgresql initdb
  fi

  # Create backup file (possibly) requiring update
  if [ ! -f $PGDATA/pg_hba.conf-sipx.bak ]
  then
     cp $PGDATA/pg_hba.conf $PGDATA/pg_hba.conf.sipx.bak
  fi

  # Will allow this script to add user.  Needs to be listed before
  # other permission or it will not take effect.
  if ! grep '^local *all *all *trust\b*$' $PGDATA/pg_hba.conf >/dev/null
  then
     echo "local all all trust" > $PGDATA/pg_hba.conf.tmp
     cat $PGDATA/pg_hba.conf >> $PGDATA/pg_hba.conf.tmp
     mv $PGDATA/pg_hba.conf.tmp $PGDATA/pg_hba.conf
  fi

  # Will allow jdbc to connect.  Needs to be listed before
  # other permission or it will not take effect.
  if ! grep '^host *all *all *127.0.0.1 *255.255.255.255 *trust\b*$' $PGDATA/pg_hba.conf >/dev/null
  then
     echo "host all all 127.0.0.1 255.255.255.255 trust" > $PGDATA/pg_hba.conf.tmp
     cat $PGDATA/pg_hba.conf >> $PGDATA/pg_hba.conf.tmp
     mv $PGDATA/pg_hba.conf.tmp $PGDATA/pg_hba.conf
  fi

  # Open up TCP/IP connections
  gsed -i-sipx.bak -e 's/\#tcpip_socket\s=\sfalse/tcpip_socket = true/g' \
          $PGDATA/postgresql.conf
  ${Chown} pgsql:pgsql $PGDATA/postgresql.conf

  # Postmaster to allow connections
  echo "-i" > $PGDATA/postmaster.opts.default
  chmod 664 $PGDATA/postmaster.opts.default
  ${Chown} pgsql:pgsql $PGDATA/postmaster.opts.default

  ${ServiceDir}/$Service start
}

# Have postgres start automatically with system reboot
# XXX Don't worry, we don't call this under FreeBSD
setPostgresRunlevels() {
    # Arrange for Postgres to be started automatically in runlevels 3
    # and 5.
    # Check if we can use chkconfig.
    if [ -f /sbin/chkconfig ]
    then
        # We have to specify the runlevels because the default set of
        # runlevels for Postgres is empty.
        /sbin/chkconfig --level 35 $Service on
    else
        # The user will have to do it manually.
        echo chkconfig does not exist.
        echo You need to create /etc/rc?.d/{S85,K15}$Service
        echo to start postgres for runlevels 3 and 5.
    fi
}

# simple diagnostics
configtest() {
    if ! databaseCheck
    then
      cat <<EOF
    Postgres configuration database not detected.
    Run (as root:)

       /usr/local/bin/sipxconfig.sh --setup
EOF
      Status=1
    fi
    /usr/local/bin/sipx-validate-xml /usr/local/etc/sipxpbx/topology.xml.in
    Status=$(($Status+$?))

    for f in $TEST_WRITE_ACCESS; do
      if [ -f $f ]; then
        if [ ! -w $f ]; then
          echo "WARNING: user $RunningUser requires write access to file $f." >&2
          Status=1
        fi
      fi
    done

    # Print out a success message if everything is OK, to give the user some feedback.
    # If there is an error then the test will yield an error message.
    if [ $Status = 0 ]
    then
        echo "The config test succeeded."
    fi
}

while [ $# -ne 0 ]
do
    case ${1} in
        -c|--configtest)
            Action=CONFIGTEST
            ;;
            
        -s|--setup)
            Action=SETUP
            ;;

        -d|--database)
            Action=DATABASE
            shift
            Args="${@}"
            break 2
            ;;    

        --nop)
            Action=NOP
            ;;

        -h|--help|*)
            Action=HELP
            ;;
    esac           

    shift # always consume 1
done

if [ ${Action} = CONFIGTEST ]
then
    configtest
    exit $Status
elif [ ${Action} = DATABASE ] 
then
    databaseCommand $Args
elif [ ${Action} = RUN ] 
then
    onWebUi
elif [ ${Action} = SETUP ]
then
    onSetup
elif [ ${Action} = HELP ]
then

cat <<USAGE
Usage: sipxconfig.sh [-d|--database commands ...]
                     [-c|--configtest]
                     [-s|--setup]
                     [-h|--help]
                     
Start Web UI administration for sipXpbx and for remotely provisioned
SIP-based phones.

Options include:

  none                 Runs web admin ui

  --database commands  Runs an operation on the database. Database commands 
                       are detailed below 

  --configtest         Run diagnostics

  --setup              Initialize postgresql for communicating with sipxconfig
                       and create initial database. Will most likely need 
                       root permissions.

Common Database commands include:

   drop                Drops ${Database} database

   create              Create new ${Database} database

   upgrade             Upgrade database from a previous version by applying
                       patches to the database schema.  This will only 
                       upgrade if nec.

   -p                  Full list all available commands

Notable environment variables:

    POSTGRES_SERVICE   a guess is made to determine the name for the
                       Postgres service.
                       If the guess is incorrect, then set this to the name of
                       the script in /etc/init.d that starts/stops
                       the Postgres database.  The possibilities that
                       we are aware of are "postgresql" and "rhdb".

USAGE

fi
