/*
sipd - SIP redirect, registration and proxy server

Copyright 1998-2001 by Columbia University; all rights reserved 

Permission to use, copy, modify, and distribute this software and its
documentation for research and educational purposes and without fee is
hereby granted to non-profit organizations, provided that the above
copyright notice appear in all copies and that both that the copyright
notice and warranty disclaimer appear in supporting documentation, and
that the names of the copyright holders or any of their entities not be
used in advertising or publicity pertaining to distribution of the
software without specific, written prior permission.  Use of this
software in whole or in parts for direct commercial advantage requires
explicit prior permission. 

The copyright holders disclaim all warranties with regard to this
software, including all implied warranties of merchantability and
fitness.  In no event shall the copyright holders be liable for any
special, indirect or consequential damages or any damages whatsoever
resulting from loss of use, data or profits, whether in an action of
contract, negligence or other tortuous action, arising out of or in
connection with the use or performance of this software.

Written by Henning Schulzrinne, Jonathan Lennox, Sankaran
  Narayanan, Kundan Singh, Yan Xu, Panagiotis Sebos, and Xiaotao Wu
*/

#include <stdlib.h>    /* getopt() */
#include <unistd.h>    /* close() */
#include <stdio.h>     /* fopen() */
#include <string.h>    /* strdup(), strchr() */
#include <pthread.h>   /* pthread_ */
#include <signal.h>    /* signal() */
#include <sys/param.h> /* MAXPATHLEN */

#include "dstring.h"
#include "error.h"
#include "fullpath.h"
#include "raise-rlimits.h"
#include "strdupn.h"   /* strassign */
#include "tcp.h"
#include "udp.h"
#include "ssl_tcp.h"
#include "sysdep.h"

#include "tripthread.h"    /* TRIP module */

#include "config.h"
#include "sipsnmp_interface.h"

#include "dbapi.h"

#include "log.h"       /* Log() */ 
#include "log_interface.h" 

#include "license.h"   /* check_registration */
#include "conf-util.h" /* GetDBURI */
#include "sipd.h"
#include "sipd-config.h"
#ifdef USE_JAVA
#include "jvm.h" /* createJVM() */
#endif

#ifndef USE_MYSQL
#error "sipd needs MySQL database. On unix configure with --with-mysql option. On Win32 add USE_MYSQL macro in libsip, and sipd projects' settings."
#endif

/* Prototypes for external functions without their own header files.
 * (They really ought to have them.) */

/* In RTSP, missing content means zero.  In SIP, it means infinite. */
const int missing_content_length_means_zero = 0; 

/* The name of this server */
const char *server_name = SERVER_NAME;
const char* app_name = "sipd";
const char* license_code = "irt_cinema_1.20+";

/*
 * usage()
 * Usage information. Exits program with status code 1.
 */
void usage(char *prog)
{
  fprintf(stderr,
          "SIP Proxy, Redirect and Registration Server %s (built %s %s)\n",
          VERSION, __DATE__, __TIME__);
  fprintf(stderr, "Copyright 1997-2001 Columbia University\n");
  fprintf(stderr, "Usage: %s [options]\n"
          " -D DatabaseURI : User name, password, hostname of sip database. \n"
          "                  e.g., sql://user:pwd@hostname/sip\n"
          " -f ConfigFile  : Config file containing DatabaseURI. Default is cinema_db.conf\n"
          " -d  category   : Debug (Verbose) mode for the category.\n"
          "                 categories are all, sql, net, misc, thread.\n"
          " -X             : Non daemon mode.\n"
          " -v             : Show version information and usage.\n"
          " -S             : Server Name (e.g., cs.columbia.edu)\n"
          "                  This will be used to locate the configuration\n"
          "                  entry if database contains information for\n"
          "                  multiple servers\n",
          prog);
  exit(1);
} /* usage */


/*
 * ParseCommandLine()
 * Parse sipd's command line, and fill in the commandline global.
 *  
 * If the database URI is not specified through the -D parameter, then
 * check if the -f parameter giving a filename is present. If the file
 * name is present, read the first line, and check whether it is a valid
 * database-uri.
 *
 * Also, check whether the -S parameter is specified. If it is, our 
 * ServerName parameter should be the value given as its argument.
  */
static void ParseCommandLine(int argc, char *argv[])
{
  char c;
  extern char *optarg;            /* command line argument value */

  char config_file[256] = {0};
  
  /* Parse command line options. */
  commandline.DatabaseURI  = NULL;
  commandline.do_daemon   = 1;
  commandline.debug_flag  = 0;

  while ((c = getopt(argc, argv, "f:D:S:d:vX")) != EOF) {
    switch (c) {
    case 'D':
      strassign(&commandline.DatabaseURI, optarg);
      break;
    case 'S':
      strassign(&commandline.ServerName, optarg);
      break;
    case 'f':
      strncpy(config_file, optarg, sizeof(config_file));
      break;
    case 'd':
      commandline.debug_flag = 1;
      DebugAllowModule(optarg, 1);
      break;
    case 'X':
      commandline.do_daemon = 0;
      break;
    case 'v': 
    case '?':
    default:
      usage(argv[0]);
      break;
    }
  }

  /* debug_flag is a global used by libcine, in error.c. */
  debug_flag = commandline.debug_flag;

  /* if the user did not give a database_uri, then see
   * whether a config file was specified. call GetDBURI
   * with it or NULL to get a DB uri.
   */
  if (commandline.DatabaseURI == NULL) {
    commandline.DatabaseURI = GetDBURI(config_file);
    if (!commandline.DatabaseURI) {
      error("ParseCommandLine", "Unable to get database information\n"
            "Please use -D or -f to specify database uri configuration\n");
      exit(0);
    }
  } 
  /* if the uri is there, change it from sql to fastsql */
  if (strstr(commandline.DatabaseURI, "sql://") == 
      commandline.DatabaseURI) {
    strcpy(config_file, "fast");
    strcat(config_file, commandline.DatabaseURI);
    strassign(&commandline.DatabaseURI, config_file);
  }
  
  return;
  
} /* ParseCommandLine */

#ifdef USE_TLS
/* RunTLSThread()
 * Start the TLS thread
*/
static void RunTLSThread(pthread_t *tid) 
{
  struct ssl_thread_arg* ssl_arg;
  int e;
  *tid = 0;
  ssl_arg = (struct ssl_thread_arg *) 
             calloc(1, sizeof(struct ssl_thread_arg));

  ssl_arg->port = config.port + 1;
  strassign(&ssl_arg->keyMaterial.CAListFile, config.KeyMaterial.CAListFile);
  strassign(&ssl_arg->keyMaterial.ServerPrivKey, config.KeyMaterial.ServerPrivKey);
  strassign(&ssl_arg->keyMaterial.CertFile, config.KeyMaterial.CertFile);
  strassign(&ssl_arg->keyMaterial.DHParamFile, config.KeyMaterial.DHParamFile);
  strassign(&ssl_arg->keyMaterial.Password, config.KeyMaterial.Password);
  strassign(&ssl_arg->keyMaterial.RandomFile, config.KeyMaterial.RandomFile);
  strassign(&ssl_arg->primaryIP, config.PrimaryIP);
  strassign(&ssl_arg->primaryIP6, config.PrimaryIP6);

  if ((e = pthread_create(tid, NULL, 
                          ReceiveSSL, (void *)ssl_arg)) != 0) {
    fatal("main", "Cannot create SSL thread: %s\n", strerror(e));
   }
  return;
} /* RunTLSThread */
#endif


#ifdef USE_SNMP
/*
 * RunSNMPThread()
 * Start the SNMP thread.
 */
static void RunSNMPThread(pthread_t *tid)
{
  SnmpData *p_snmpData;
  int e;

  *tid = NULL;
 
  if (!config.StartSnmp)
    return;

  p_snmpData = (SnmpData*) calloc(1, sizeof(SnmpData));
  p_snmpData->sub_agent = SUB_AGENT; /* right now, keep it as subagent. */
  p_snmpData->agentx_socket_path = config.AgentxSocketPath;
  
  if ((e = pthread_create(tid, NULL, 
                          sipsnmp_main, (void*)p_snmpData)) != 0) {
    error("RunSNMPThread", "Cannot create snmp thread: %s\n",strerror(e));
  }
} /* RunSNMPThread */
#endif /* USE_SNMP */

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//TRIPThread started here.
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/* 
 *RunTRIPThread()
 * Start the TRIP thread
*/
#ifdef USE_TRIP
static void RunTRIPThread(pthread_t *tid)
{
  struct trip_thread_arg* trip_arg;
  int e;
  char hostname[1024];

  if(!config.StartTrip)
    return;

  //Used for the host, port, and TRIP PS name.
  *tid = 0;
  trip_arg = (struct trip_thread_arg *) 
    calloc(1, sizeof(struct trip_thread_arg));

  if ( gethostname(hostname, sizeof(hostname)) == 0) {
    strassign(&trip_arg->host, hostname);
  }
  else {
    /* Gethostname failed. use ProxyName. */
    strassign(&trip_arg->host, config.ProxyName);
  }
  
  trip_arg->port = config.port;

  if ((e = pthread_create(tid, NULL, 
                          TRIPThread, (void *)trip_arg)) != 0) {
    fatal("main", "Cannot create TRIP thread: %s\n", strerror(e));
  }
  return;
} /* RunTRIPThread */
#endif
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


/* check_license
 *  Check whether the running system has a valid license
 *  (if compiled with DO_LICENSING option), and terminate
 *  if none exists.
*/
static void check_license() {

#ifdef DO_LICENSING
  cinema_license_info licinfo;

  licinfo.license_uri = config.UserConfig;
  licinfo.app_name = (char*)app_name;
  licinfo.internal_code = (char*)license_code;

  if (check_registration(&licinfo) < 0) {
    error("check_license", "No valid licenses found. Get a license by "
          "visiting http://www.sipcomm.com/license/columbia/default.asp\n");
  }
#endif /* DO_LICENSING */

 return;
} /* check_license */

/* 
 * RunThreads()
 * Start the main socket listening threads (tcp and udp) for the server.
 * Also start TLS and SNMP, if configured.
 */
static void RunThreads(pthread_t tid[4], int *started_threads) {
  int e;                          /* thread error condition */
  struct tcp_thread_arg *tcp_arg; /* params to TCP/UDP threads */
  struct udp_thread_arg *udp_arg; 
  int next;

  tid[0] = 0;
  tid[1] = 0;
  next = *started_threads = 2;

  /* Allocate tcp_arg and create TCP thread */
  tcp_arg = (struct tcp_thread_arg *) calloc(1, sizeof(struct tcp_thread_arg));
  tcp_arg->port = config.port;
  strassign(&tcp_arg->primaryIP, config.PrimaryIP);
  strassign(&tcp_arg->primaryIP6, config.PrimaryIP6);

  if ((e = pthread_create(&tid[0], NULL, 
                          ReceiveTCP, (void *)tcp_arg)) != 0) {
    fatal("main", "Cannot create TCP thread: %s\n", strerror(e));
  }
  
  /* Allocate udp_arg and create UDP thread */
  udp_arg = (struct udp_thread_arg *) calloc(1, sizeof(struct udp_thread_arg));
  udp_arg->port = config.port;
  strassign(&udp_arg->primaryIP, config.PrimaryIP);
  strassign(&udp_arg->primaryIP6, config.PrimaryIP6);

  if ((e = pthread_create(&tid[1], NULL, 
                          ReceiveUDP, (void *)udp_arg)) != 0) {
    fatal("main", "Cannot create UDP thread: %s\n", strerror(e));
  }
  
  if (config.StartSSL) {
#ifdef USE_TLS
    tid[next] = 0;
    RunTLSThread(&tid[next]);
    next++;
    (*started_threads)++;
#else
    error("RunThreads", "Cannot start TLS: not enabled in server\n");
#endif
 }

  if (config.StartSnmp) {
#ifdef USE_SNMP
    tid[next] = 0;
    RunSNMPThread(&tid[next]);
    (*started_threads)++;
#else
    error("RunThreads", "Cannot start SNMP: not enabled in server\n");
#endif
  }


  if (config.StartTrip) {
    //en107 
    //STart the TRIP location server here
    //call a pthread_create_detached function to
    //create a thread to handle TRIP.
    tid[next] = 0;
    RunTRIPThread(&tid[next]);
    next++;
    (*started_threads)++;
  }

  /* No need to free tcp|udp|ssl_arg. The created thread frees them */
  return;
} /* RunTRIPThreads */


/*
 * main()
 * Initialize and start the server.
 */
int main(int argc, char *argv[])
{
  pthread_t tid[4];             /* TCP, UDP, [SSL, SNMP] */
  int act_threads;

  extern void InitLibSip();
  extern void do_daemon(void);
  extern void setupExitThread(void);

  memset(tid, 0, sizeof(tid));

  startupSocket();

  /* Errors that occur before sipd.conf is read should go to stderr. */
  ErrorInit("stderr");

  /* make Log function the callback for libsip logging. */
  LogData = Log;

  raise_rlimits();

  ParseCommandLine(argc, argv);

  ConfigInit(0);

  check_license(); /* may terminate program if invalid */
  
  if (commandline.do_daemon) do_daemon();

  atexit(ConfigCleanup);
#ifndef WIN32
  signal(SIGHUP,  ConfigInit);
  signal(SIGPIPE, SIG_IGN); 
#endif

  InitLibSip();      /* initialize libsip */

  setupExitThread();

  RunThreads(tid, &act_threads);
  
#ifdef USE_JAVA
  if (createJVM() < 0) { /* create JVM */
    error("main", "JVM creation error. \n");
  }
  else {
    debug(DEBUG_MISC, "main", "JVM is created. \n");
  }
#endif 

  debug(DEBUG_MISC, "main", "sipd server %s (built %s %s) listening on port %d\n",
	VERSION, __DATE__, __TIME__, config.port);
  
  /* Wait for thread completion (which shouldn't happen). */
  WaitForMultipleObjects(act_threads, tid, TRUE, INFINITE);
  
  debug(DEBUG_MISC, "main", "main returns\n");
  exit(0);
} /* main */


