/* 
** config.c
** Functions for reading configurations (file and command line)
*/

#include "list.h"
#include "config.h"
#include "debug.h"
#include "db.h"
#include "hash.h"
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <ctype.h>
#include <sys/types.h>
#include <regex.h>
#include <getopt.h>

#define LINE_BUFFER_SIZE 516
#define HASH_TABLE_INIT_SIZE 30

struct hash_table table, *config_items;
int read_config_file();

static struct option long_options[] =
{
  {"MAIL_HOST",	   required_argument,    0, 0},
  {"MAIL_USER",	   required_argument,    0, 0},
  {"MAIL_PASS",	   required_argument,	 0, 0},
  {"MAIL_PASS",	   required_argument,	 0, 0},
  {"MAILDATABASE",	required_argument,	 0, 0},
  {"MAIL_HOST_READ",	required_argument,    0, 0},
  {"MAIL_USER_READ",	required_argument,    0, 0},
  {"MAIL_PORT_READ",	required_argument,    0, 0},
  {"MAILDATABASE_READ", required_argument, 0, 0},
  {"AUTH_HOST",	   required_argument,    0, 0},
  {"AUTH_USER",	   required_argument,    0, 0},
  {"AUTH_PASS",	   required_argument,	 0, 0},
  {"USERDATABASE",	required_argument,	 0, 0},
  {"AUTH_HOST_READ",	required_argument,    0, 0},
  {"AUTH_USER_READ",	required_argument,    0, 0},
  {"AUTH_PORT_READ",	required_argument,    0, 0},
  {"USERDATABASE_READ", required_argument, 0, 0},
  {"POP3D_BIND_IP", required_argument, 0, 0},
  {"POP3D_BIND_PORT", required_argument, 0, 0},
  {0, 0, 0, 0}
};


/*
** Init configuration parameters. 
** 1 First read the configuration file 'CONFIG_FILE' or the one specified by 
**   file_name. 
** 2 Then parse command line parameters from argv. 
** 
** Note: Only parameters starting with '--' are parsed. The '-' parameters 
**       are handled elsewhere (i.e. in the different main functions).
** Note2:The argc and argv is modified to not include any '--' parameters.
**       This is because 'init_config' doesn't want to mess up for the original
**       code which depends on the position of some parameters (e.g. main.c)
*/
int init_config(int *argc, char *argv[], char *file_name)
{
   int c, option_index=0, i, error=0, cp_argc=*argc;
   char *cp_argv[cp_argc];

   read_config_file(file_name);

   // Make a copy of argv. (getopt messes up the order 
   // of the arguments..?)
   for(i=0; i<cp_argc; i++)
   {
      cp_argv[i] = (char *) strdup(argv[i]);
   }

   opterr=0;   // Turn off the error message `getopt' prints for unrecognized options.
   while ((c=getopt_long_only(cp_argc, cp_argv, "", long_options,
			  &option_index)) != EOF)
   {
      switch(c) {
         case 0:
             insert((char*)long_options[option_index].name, 
                    optarg, config_items);
             trace (TRACE_INFO, "init_config(): adding %s with value '%s'", 
                    (char*)long_options[option_index].name, optarg);
             break;
         default:
            // Only bother with parameters starting with '--'
            if (strncmp("--", cp_argv[optind-1], 2)==0)
            {
               fprintf(stderr, "Illegal parameter '%s' (or missing argument)\n", cp_argv[optind-1]);
               error++;
            }
         break;
      }
   }
   
   // Update argc and argv to not contain any '--' parameters
   for(i=cp_argc-1; i>0; i--)
   {
      if (strncmp("--", argv[i], 2)==0)
      {
         *argc=i;
      }
   }
   for(i=cp_argc-1; i>*argc; i--)
   {
      argv[i]=NULL;
   }
   
   return error;
}


/* 
** Read the configuration file. 
*/
int read_config_file (char *file_name)
{
   FILE *configfile;
   char *readbuf, *field, *val, *fname, *workptr;
   int i=0;
	
   readbuf = (char *)malloc(LINE_BUFFER_SIZE);
	
   fname = CONFIG_FILE;
   if (file_name != NULL)
      fname = file_name;
	
   trace (TRACE_INFO, "read_config_file(): reading configuration for %s...", fname); 
   configfile = fopen(fname, "r");     // open the configuration file
   if (configfile == NULL)             // error test
   {
      fprintf (stderr,"Error: can not open input file %s\n", fname);
      return 1;
   }
	
	// Construct the hash table
	config_items=&table;
   construct_table(config_items, HASH_TABLE_INIT_SIZE);
      	
   while ( NULL != fgets (readbuf, LINE_BUFFER_SIZE, configfile))
   {
      i++;
      
      // clip off optional comment tail indicated by a '#'
      if( NULL != (workptr = strchr( readbuf, '#' )))
         *workptr = '\0';
      else
         workptr = readbuf + strlen( readbuf );
         
      // clip off trailing and leading white space
      workptr--;
      while( isspace( *workptr ) && workptr >= readbuf )
         *workptr-- = '\0';
      workptr = readbuf;
      while( isspace( *workptr ))
         workptr++;
      if( 0 == strlen( workptr ))
         continue;
         
      field = strtok(workptr, " =");
      if( field != NULL )
      {
         // strip leading white and a 'late' = from value part.
         val = strtok( NULL, "" );
         while( isspace( *val ))
             val++;
         if( '=' == *val )
             val++;
         while( isspace( *val ))
             val++;
             
         if (!val)
         {
            fprintf (stderr,"Config read error on line: %d\n", i);
            fprintf (stderr,"readbuf: %s val: %s field: %s\n", readbuf, val, field);
         }
         else
         {
            insert(field, val, config_items);
            trace (TRACE_INFO, "read_config_file(): param %s value '%s'", 
                   field, val);
         }
   	}
	}
	fclose(configfile);
   return 0;
}

/* 
** Search and return the config item specified by 'item'. If 'item'
** isn't found return the 'def' value instead.
*/
char *get_config_item (char *def, char *item)
{
   char *result = NULL;
   void *value;
   
   // Check if the item table has been initialized.
   if( config_items == NULL )
   {
      read_config_file(NULL);
   }

   // Search the config_items hash table for the value specified by 'item'
   value = lookup(item, config_items);
   if (value!=NULL)
   {
      result=value;
      trace (TRACE_DEBUG, "get_config_item(): '%s' value [%s]", item, result);
   }
   else
   {
      result=def;
      trace (TRACE_DEBUG, "get_config_item(): '%s' def [%s]", item, result);
   }
   
   // returns null if *def=NULL and not in configuration file
   
   return result;
}


/* 
** Search and return the config item specified by 'item'. If 'item'
** isn't found return the 'def' value instead.
*/
int get_config_item_int (int def, char *item)
{
   int result;
   void *value;
   
   // Check if the item table has been initialized.
   if( config_items == NULL )
   {
      read_config_file(NULL);
   }

   // Search the config_items hash table for the value specified by 'item'
   value = lookup(item, config_items);
   if (value!=NULL)
   {
      result=atoi(value);
      trace (TRACE_DEBUG, "get_config_item(): '%s' value [%i]", item, result);
   }
   else
   {
      result=def;
      trace (TRACE_DEBUG, "get_config_item(): '%s' def [%i]", item, result);
   }
   
   return result;
}
