/*
 * init.c,v 1.1 1994/01/28 17:05:18 franktor Exp
 *
 * This file contains stuff which is called only once when starting
 * the server process.
 */

#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <malloc.h>
#include <sr-general.h>
#include <sr-api.h>
#include <sr-address.h>
#include <high/eapi.h>

#include <high/structcodec.h>
extern elemDesc desc_EapiPacket[];

#include "server.h"

#ifndef __CEXTRACT__
#include "proto.h"
#endif

static const char *progname	= NULL;
const char *config_file		= NULL;
const char *default_config_file	= NULL;

void parse_args(int argc, char **argv)
{
  int i;

  /*
   * Initialise configurable options
   */
  exit_on_idle = 0;
  keeplog = 0;
  ttydebug = 0;
  debug_logfile = "../logs/server.log";

  if (argc >= 1 && argv[0])
     if ((progname = strrchr (argv[0], '/')) != NULL)
	progname++;
     else
	progname = argv[0];
  if (! (progname && *progname))
     progname = "server";

  if (strchr ((default_config_file = (DEFAULT_CONFIG_FILE)), '%'))
  {
     char *cp = malloc (strlen (default_config_file) + strlen (progname));
     sprintf (cp, default_config_file, progname);
     default_config_file = cp;
  }

  /*
   * Parse options
   */
  for (i = 1; i < argc; i++)
  {
    if (!strncmp(argv[i],"-config",strlen(argv[i])))
    {
      if (++i == argc)
      {
        fprintf(stderr, "Specify config-file after -config option.\n");
        exit(1);
      }
      config_file = argv[i];
      continue;
    }
    if (!strncmp(argv[i], "-verbose", strlen(argv[i])))
    {
      facLogLevel (facAll, llevTrace); /* Turn on all debug */
      continue;
    }
    if (!strncmp(argv[i], "-debug", strlen(argv[i])))
    {
      ttydebug = 1;
      continue;
    }
    if (!strncmp(argv[i], "-exit", strlen(argv[i])))
    {
      exit_on_idle = 1;
      continue;
    }
    if (!strncmp(argv[i], "-keeplog", strlen(argv[i])))
    {
      keeplog = 1;
      continue;
    }
    if (!strncmp(argv[i], "-logfile", strlen(argv[i])))
    {
      if (++i == argc)
      {
        fprintf(stderr, "You must specify a filename after the -logfile option.\n");
        exit(1);
      }
      debug_logfile = argv[i];
      continue;
    }
    if (strncmp(argv[i], "-help", strlen(argv[i])))
      fprintf(stderr, "Unknown argument: %s\n", argv[i]);
    fprintf(stderr, "Usage: %s [-config <config_file>] [-debug] [-verbose] [-exit] [-keeplog] [-logfile <filename>]\n", progname);
    fprintf(stderr, "  -config:  Specifies which configuration file to use.\n");
    fprintf(stderr, "            Default is \"%s\".\n", default_config_file);
    fprintf(stderr, "  -debug:   Writes debug-information to terminal.\n");
    fprintf(stderr, "  -verbose: Turns on more debug-information.\n");
    fprintf(stderr, "  -exit:    Exit when the last client disconnects.\n");
    fprintf(stderr, "  -keeplog: Don't delete old logfiles when starting.\n");
    fprintf(stderr, "  -logfile: Specifies which file to output logging to.\n");
    fprintf(stderr, "            Default is \"%s\".\n", debug_logfile);
    exit(1);
  }
}

/*
 * Initialisation, read configuration file
 * Initialise the filedescriptor-sets
 */

void initialise() {
  FILE *fp;
  char *word;
  char buf[BUF_SIZE];
  SR_Address *where;
  char *msg;		

  if (OSI_Initialise (llevAll, stderr) == False)
  {
    LOG(facHigh, llevExceptions, "Failed to establish connection, exiting.");
    exit(1);
  }
  line = 0;

  first_client = NULL;
  first_database_server = NULL;
  first_database_server_connection = NULL;
  localname = NULL;

  FD_ZERO(&eapi_read);
  FD_ZERO(&eapi_write);
  FD_ZERO(&eapi_exceptions);

  if(!config_file && (config_file = (char *) getenv("SERVER_CONFIG")) == NULL)
    config_file = default_config_file;
  fp = fopen(config_file,"r");
  if ( fp == NULL)
  {
    LOG(facHigh, llevDebug, "Failed to open configuration file %s.",config_file);
    if (!((config_file = strrchr (config_file, '/')) &&
	  (fp = fopen (++config_file, "r"))))
      fp = fopen("server-config", "r");
  }
  if( fp == NULL)
  {
    LOG(facHigh, llevAll, "Failed to open configuration file config-file.");
    fprintf(stderr, "Failed to open configuration.  Try the -config option.\n");
    exit(-1);
  }
  while((word = get_word(fp)) != NULL) {
    if(!strcmp(word,"server"))
      read_server(fp);
    else if(!strcmp(word,"localname"))
      read_localname(fp);
    else
      LOG(facHigh, llevAll, "Illegal configuration variable in line %d: %s",
          line,word);
  }
  fclose(fp);

  if (localname == NULL)
  {
    LOG(facHigh, llevExceptions, "Failed to find localname in config-file.");
    fprintf(stderr, "Failed to find localname in config-file, exiting.");
    exit(1);
  }

  strcpy(buf,localname);
  if ( !strchr ( buf, '.' ) )
  {
     if (strlen(buf) && buf[strlen(buf)-1] != '@')
	strcat(buf, "@");
     strcat(buf,"cn=SR BASIC");
  }
  LOG(facHigh, llevDebug, "Localname: \"%s\".",buf);

  if ( !str2sr_address ( buf, &where, &msg ) )
  {
     LOG ( facHigh, llevNotice, "Failed to resolve address %s: %s", buf, msg );
     fprintf ( stderr, "Failed to resolve address %s: %s", buf, msg );
     exit ( 1 );
  }

  if (!SR_CreateListener( where, AcceptAssoc))
  {
    LOG(facHigh, llevAll, "Failed to create listener");
    exit(1);
  }

  /*
   * Initialise the converter library:
   */
  init_converter();

  /*
   * Bind the different internal converters to OIDs.
   * This should probably be moved to the configuration file as well.
   * The third, optional, argument, specifies transfer syntax.
   * If it is absent, text-format is assumed.
   */
  add_converter("NORMARC-text", SR_RECORDSYNTAX_NORMARC, NULL);
  add_converter("NORMARC-data", "1.0.10163.5.99", ISO2709_TRANSFERSYNTAX_CHARENC); /* Debug */

  LOG( facHigh, llevDebug, "Done initializing.");
}

void
add_converter(const char *func, const char *abstract, const char *transfer)
{
  ConvertInfo *new = (ConvertInfo *) malloc(sizeof(ConvertInfo));
  new->converter_name = strdup(func);
  new->abstractSyntax = OID_str2Oid (abstract);
  if (transfer)
    new->transferSyntax = OID_str2Oid (transfer);
  else
    new->transferSyntax = OID_NULLPTR;
  new->next = first_convert_info;
  first_convert_info = new;
}

char *get_word(FILE *fp) {
  static char buf[BUF_SIZE];
  int pos = 0, ch, spaces = 1;
  int quote = 0; /* True if we see a start of a quoted wordd */
  
  for(; (ch = getc(fp)) != EOF; ) {
    switch(ch) {
    case '\n':
      line++;
    case '\0':
    case '\t':
    case ' ':
      if(quote)
      {
        buf[pos++] = ch;
        break;
      }
      if(spaces)
        break;
      buf[pos] = '\0';
      return buf;
    case '\"':
      quote = (!quote);
      break;
    case '#':
      if (quote)
      {
        buf[pos++] = ch;
        break;
      }
      line++;
      for(; (ch = getc(fp)) != EOF && ch != '\n'; );
      if (ch == EOF)
        return NULL;
      break;
    case ',':
      if (quote)
      {
        buf[pos++] = ch;
        break;
      }
      spaces = 1;
      buf[pos++] = ch;
      break;
    default:
      spaces = 0;
      buf[pos++] = ch;
    }
  }
  return NULL;
}

config_vars get_config_var(FILE *fp) {
  char *word = get_word(fp);

  if(word == NULL)
    return eapi_config_abort;
  if(!strcmp(word,"databases"))
    return eapi_config_databases;
  if(!strcmp(word,"method"))
    return eapi_config_method;
  if(!strcmp(word,"ip_address"))
    return eapi_config_ip_address;
  if(!strcmp(word,"ip_port"))
    return eapi_config_ip_port;
  if(!strcmp(word,"program"))
    return eapi_config_program;
  if(!strcmp(word,"handle_several_clients"))
    return eapi_config_handle_several_clients;
  if(!strcmp(word,"max_result_sets"))
    return eapi_config_max_result_sets;
  if(!strcmp(word,"configurable_result_set_names"))
    return eapi_config_configurable_result_set_names;
  if(!strcmp(word,"query_syntax"))
    return eapi_config_query_syntax;
  if(!strcmp(word,"can_interrupt"))
    return eapi_config_can_interrupt;
  if(!strcmp(word,"end"))
    return eapi_config_end;
  if(!strcmp(word,"supported_syntaxes"))
    return eapi_config_supported_syntaxes;
  LOG(facHigh, llevAll, "Illegal config variable in line %d: %s",line,word);
  return eapi_config_illegal;
}

void read_localname(FILE *fp) {
  char *word;
  char *tmp;
  word = get_word(fp);
  if (!word)
    return;
  if(strchr(word,'%'))
  {
    char buf[BUF_SIZE], name[BUF_SIZE];
    name[BUF_SIZE - 1] = '\0';
    if (gethostname(name,BUF_SIZE - 1) == (-1))
    {
      LOG(facHigh, llevExceptions, "Failed to get hostname.");
      return;
    }
    if ((tmp = strchr (name, '.')))
      *tmp = '\0';
    (void) sprintf(buf,word,name);
    localname = strdup(buf);
    return;
  }
  else
    localname = strdup(word);
  LOG(facHigh, llevTrace, "config-file: Localname = \"%s\"", localname);
}

void read_server(FILE *fp) {
  DatabaseServer *dbs = (DatabaseServer *) malloc(sizeof(DatabaseServer));
  char *value;
  Boolean initialised_method = False;

  dbs->can_interrupt = False;
  dbs->handle_several_clients = False;
  dbs->configurable_result_set_names = False;
  dbs->next = (DatabaseServer *) NULL;
  dbs->databases = (DatabaseName *) NULL;
  dbs->supported_syntaxes = (SupportedSyntaxes *) NULL;

  value = get_word(fp);
  if (value == NULL)
    goto abort;
  dbs->name = strdup(value);
  LOG(facHigh, llevDebug, "Configuring server %s.",value);

  for(;;)
  {
    config_vars variable = get_config_var(fp);

    if (variable == eapi_config_abort)
      goto abort;
    if (variable != eapi_config_databases &&
        variable != eapi_config_supported_syntaxes  &&
        variable != eapi_config_end &&
        variable != eapi_config_illegal)
    {
      value = get_word(fp);
      if (value == NULL)
        goto abort;
    }

    switch(variable)
    {
    case eapi_config_method:
      initialised_method = True;
      if (!strcmp(value,"tcpip"))
        dbs->connection.method = connect_tcpip;
      else if(!strcmp(value,"pipe"))
        dbs->connection.method = connect_pipe;
      else
        LOG(facHigh, llevDebug, "Illegal connection method in line %d: %s",
            line,value);
      break;
    case eapi_config_ip_address:
      if (dbs->connection.method != connect_tcpip)
        LOG(facHigh, llevExceptions, "Configuring addres without tcpip.");
      dbs->connection.u.ip.address = strdup(value);
      break;
    case eapi_config_ip_port:
      if (dbs->connection.method != connect_tcpip)
        LOG(facHigh, llevExceptions, "Configuring port without tcpip.");
      dbs->connection.u.ip.port = (unsigned int) atoi(value);
      break;
    case eapi_config_program:
      if (dbs->connection.method != connect_pipe)
        LOG(facHigh, llevExceptions, "Configuring program without pipe.");
      dbs->connection.u.program = strdup(value);
      break;
    case eapi_config_supported_syntaxes:
      while((value = get_word(fp)) != NULL && strcmp(value,"end"))
        add_supported_syntaxes(dbs,value);
      if (value == NULL)
        goto abort;
      break;
    case eapi_config_databases:
      while((value = get_word(fp)) != NULL && strcmp(value,"end"))
        add_databases(dbs,value);
      if (value == NULL)
        goto abort;
      break;
    case eapi_config_handle_several_clients:
      if (!strcmp(value,"yes"))
        dbs->handle_several_clients = True;
      else if (strcmp(value,"no"))
        dbs->handle_several_clients = False;
      break;
    case eapi_config_max_result_sets:
      dbs->max_result_sets = atoi(value);
      break;
    case eapi_config_configurable_result_set_names:
      if (strcmp(value,"yes"))
        dbs->configurable_result_set_names = True;
      else if (strcmp(value,"no"))
        dbs->configurable_result_set_names = False;
      break;
    case eapi_config_can_interrupt:
      if (strcmp(value,"yes"))
        dbs->can_interrupt = True;
      else
        dbs->can_interrupt = False;
      break;
    case eapi_config_end:
      if (initialised_method == False)
        LOG(facHigh, llevExceptions, "Added server without configuring method.");
      add_database_server(dbs);
      return;
    case eapi_config_query_syntax:
    default:
      break;
    }
  }
  abort:
    LOG(facHigh, llevAll, "Premature EOF in configuration file, line %d.",line);
    free(dbs);
    return;
}
