/***************************************
  $Revision: 1.25 $

  DBupdate 

  Status: NOT REVIEWED, NOT TESTED

  Author(s):       Engin Gunduz

  ******************/ /******************
  Modification History:
        engin (01/03/2000) Created.
  ******************/ /******************
  Copyright (c) 2000                              RIPE NCC

  All Rights Reserved

  Permission to use, copy, modify, and distribute this software and its
  documentation for any purpose and without fee is hereby granted,
  provided that the above copyright notice appear in all copies and that
  both that copyright notice and this permission notice appear in
  supporting documentation, and that the name of the author not be
  used in advertising or publicity pertaining to distribution of the
  software without specific, written prior permission.

  THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
  ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
  AUTHOR 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 TORTIOUS ACTION, ARISING OUT OF
  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 ***************************************/





#include "dbupdate.h"
#include "erroutines.h"
#include "ca_configFns.h"
#include "ca_dictSyms.h"
#include "ca_macros.h"
#include "ca_srcAttribs.h"
#include "notification.h"
#include "gpg.h"

int tracing = 0;
int test_mode = 0;
int reading_from_mail = 0;

/* required configuration variables */
char *tmpdir = NULL;
char *mailcmd = NULL;
char *notitxt = NULL;
char *notimailtxt = NULL;
char *fwtxt   = NULL;
char *fwmailtxt = NULL;
char *mailtxt = NULL;
char *notiflog = NULL;
char *crosslog = NULL;
char *acklog = NULL;
char *forwlog = NULL;
char *humailbox = NULL;
char *overridecryptedpw = NULL;
char *country = NULL;
char *countries[400];
char *pgppath = NULL;
char *pgp_public_key_ring = NULL;
/* end of config variables */

void error_init(int argc, char ** argv) {
  er_path_t erlogstr;

  ER_init(argc, argv);

  erlogstr.fdes = stderr;
  erlogstr.asp  = 0;
  erlogstr.sev  = ER_SEV_W;
  erlogstr.mode = ER_M_SEVCHAR | ER_M_TEXTLONG;

  ER_setpath(& erlogstr);  

} /* error_init() */


/* Takes a key-certif object, extracts its 'certif' attribute and adds
   the key into public keyring */
int import_key(char *obj){

  char * tempfile;
  struct ImportKeyObject iKO;
  GSList * certiflist, * next;
  FILE * key_file;
  char keyID[9];
  
  tempfile = (char *)malloc(strlen(tmpdir) + strlen("tmp-key.") + 32);
  sprintf(tempfile, "%s/tmp-key.%i", tmpdir, getpid());
  printf("DEBUG: tempfile=%s\n", tempfile);

  /* now we must write certif attribute(s) of this key-certif into the tempfile */
  /* get the certif first */
  certiflist = get_attr_list(obj, "certif");
  if(( key_file = fopen(tempfile, "w")) == NULL){
     fprintf(stderr, "Can't open temporary file, %s", tempfile);
     exit(1);
  }
  for( next = certiflist; next != NULL ; next = g_slist_next(next) ){
    fprintf(key_file, "%s\n", (char *)next->data);
  }
  fclose(key_file);

  strcpy(iKO.iFilename, tempfile);
  strcpy(iKO.keyRing, pgp_public_key_ring);
  PA_ImportKey(&iKO);

  printf("importKeyObj status:\n");
    
  printf("isValid: %d\n", iKO.rc);
  printf("keyID: %08lX\n", iKO.keyID);
  snprintf(keyID, 9, "%08lX", iKO.keyID);
  printf("keyID: [%s]\n", keyID);

  //unlink(tempfile);
  free(tempfile);

  if(iKO.rc == iKO_OK){/* if PA_ImportKey returned OK */
    return 1;
  }else{/* if PA_ImportKey returned not OK */
    return 0; 
  }
      
}



/* Checks the object's syntax, retrives the old version of it from the db, 
   and checks auth2. If everything is OK, then sends it to RIPdb, where referential
   integrity is checked, and the object is really committed to the db.
  
     Arguments:
        char * arg: The object,
        credentials_struct credentials: The struct containing the credentials, such as 
          'From:' field of the e-mail update,
        GHashTable * NIC_hdl_hash: A hash containing 
        char * ack_file_name:  The file name, to be used to store ACK message 
*/



int process_object(char * arg, credentials_struct credentials, GHashTable * NIC_hdl_hash, char * ack_file_name,
                   GHashTable * ntfy_hash, GHashTable * forw_hash, GHashTable * cross_hash){
    bool code = true;
    Object *o;
    char * old_version = NULL;
    o = new Object;
    int result = 0;
    int result_from_RIPupd = 0;
    int result_from_import_key = 0;
    char * auto_nic = NULL;
    char * changed_obj = NULL;
    char * obj_with_AUTO_NIC_hdl;
    char * assigned_NIC;
    char * type;

    char * value = NULL;/* these two are for */
    Attr * attr;        /* ack messages only */ 
    
    if(has_ref_to_AUTO_nic_hdl(arg)){/* if this object has refs to AUTO NIC hdls*/
       /* then first replace AUTO NIC hdls with assigned NIC hdls (in NIC_hdl_hash) */
       if((arg = replace_refs_to_AUTO_NIC_hdl(changed_obj, arg, NIC_hdl_hash)) == NULL){
         return UP_ANE; /* AUTO NIC hdl error */
       };
    }
   
    code = o->scan(arg,strlen(arg));
    if(code){
      type = get_type(o);
      /* is the object to be deleted? */
      if(o->isDeleted){
        //printf("DEBUG: This object is to be deleted\n"); 
        old_version = get_old_version(arg);
        if(old_version == NULL){ /* the object doesn't exist in the db! */
          AK_add_to_ack(ack_file_name, "\nDelete FAILED: [%s] %s\nEntry not found\n\n%s\n", 
                        o->type->getName(), get_search_key(o, o->type->getName(), arg), arg);
          return UP_NSO; /* no such object */
        }else {/* the object is in the db */
          if(identical(old_version, arg)){/* if the old & new versions are identical */
            result = check_auth(NULL, old_version, o->type->getName(), credentials);
            if(result == UP_AUTH_OK){ 
              if(tracing) {
                printf("TRACING: Will send the obj to be deleted\n");
              }
              result_from_RIPupd = send_object_db(arg, NULL, "DEL");
              if(result_from_RIPupd == 0){
                AK_add_to_ack(ack_file_name, "\nDelete OK: [%s] %s\n", 
                              o->type->getName(), get_search_key(o, o->type->getName(), arg));
                NT_write_all_ntfs(arg, NULL, tmpdir, ntfy_hash, forw_hash, cross_hash, credentials.from);
              }else{
                AK_add_to_ack(ack_file_name, "\nDelete FAILED: [%s] %s\nReferential integrity failure\n",
                              o->type->getName(), get_search_key(o, o->type->getName(), arg));
              }
              result_from_RIPupd = 0;
            }else{ /* auth failed */
              if(tracing) {
                printf("TRACING: Auth failed\n");
              }

              AK_add_to_ack(ack_file_name, "\nDelete FAILED: [%s] %s\nAuth failed\n",
                            o->type->getName(), get_search_key(o, o->type->getName(), arg));
              NT_write_all_frwds(arg, NULL, tmpdir, ntfy_hash, forw_hash, cross_hash, credentials.from);
              return UP_AUF; /* Auth failed */
            } 
          }else{/* the new & old versions do not match */
            AK_add_to_ack(ack_file_name, "\nDelete FAILED: new & old versions do not match\n");
            return UP_NOM; /* new & old versions do not match */
          }
        }
      }else {/* the object is _not_ to be deleted */
        if(has_AUTO_NIC_hdl(arg)){/* it the object has an AUTO NIC hdl */
          /* then its nic-hdl attribute must be modified so that RIPupdate
             would understand that it must assign a NIC handle to it */
          /* but first check the auth */
          result = check_auth(arg, NULL, o->type->getName(), credentials);
          if(result == UP_AUTH_OK){
            if(tracing) {                                
                printf("TRACING: Will send the obj to be created with AUTO NIC hdl\n");
            }
            auto_nic = (char *)malloc(1024); /* should be enough for a NIC hdl */
            obj_with_AUTO_NIC_hdl = replace_AUTO_NIC_hdl(arg, auto_nic);
            if(tracing) {  
              printf("TRACING:  Called replace_AUTO_NIC_hdl, get [%s]\n", obj_with_AUTO_NIC_hdl);
              printf("TRACING: Will send the obj to be added\n");
            }
            assigned_NIC = (char *)malloc(128); /* this should be enough for a NIC hdl */
            result_from_RIPupd = send_object_db(obj_with_AUTO_NIC_hdl, assigned_NIC, "ADD");
            if(result_from_RIPupd == 0){
              AK_add_to_ack(ack_file_name, "\nNew OK: [%s] %s\n", 
                            o->type->getName(), assigned_NIC);
              NT_write_all_ntfs(NULL, arg, tmpdir, ntfy_hash, forw_hash, cross_hash, credentials.from);
            }else{
              AK_add_to_ack(ack_file_name, "\nNew FAILED: [%s] %s\nReferential integrity failure\n",
                            o->type->getName(), arg);
            }
            result_from_RIPupd = 0;
            if(tracing && assigned_NIC != NULL) {  
              printf("TRACING: send_object_db returned [%s] as assigned NIC hdl\n", assigned_NIC);
            }
            if(assigned_NIC != NULL){
              printf("DEBUG: auto_nic=[%s], assigned_NIC=[%s]\n", auto_nic, assigned_NIC);
              g_hash_table_insert(NIC_hdl_hash, auto_nic, assigned_NIC);
              printf("DEBUG: NIC_hdl_hash has %i pairs\n",g_hash_table_size(NIC_hdl_hash));
            }
            
          }else{
            /* auth failed ! */
            if(tracing) {
              printf("TRACING: Auth failed\n");
            }

            ER_perror(0, result, "");
            AK_add_to_ack(ack_file_name, "\nNew FAILED: [%s] %s\nAuth failed\n",
                          o->type->getName(), get_search_key(o, o->type->getName(), arg));
            NT_write_all_frwds(NULL, arg, tmpdir, ntfy_hash, forw_hash, cross_hash, credentials.from);
            return UP_AUF; /* Auth failed */
          }
        }
        else{ 
          old_version = get_old_version(arg);
          if(old_version != NULL){/* so, this is an update operation */
            result = check_auth(arg, old_version, o->type->getName(), credentials);    
            if(result == UP_AUTH_OK){
              if(tracing) {                                
                printf("TRACING: Will send the obj to be updated\n");
              }
              result_from_RIPupd = send_object_db(arg, NULL, "UPD");
              if(result_from_RIPupd == 0){
                AK_add_to_ack(ack_file_name, "\nUpdate OK: [%s] %s\n",
                              o->type->getName(), get_search_key(o, o->type->getName(), arg));
                NT_write_all_ntfs(old_version, arg, tmpdir, ntfy_hash, forw_hash, cross_hash, credentials.from);
              }else{
                AK_add_to_ack(ack_file_name, "\nUpdate FAILED: [%s]\n%s\nReferential integrity failure\n",
                              o->type->getName(), get_search_key(o, o->type->getName(), arg));
              }
              result_from_RIPupd = 0;
            }else{
              /* auth failed ! */
              if(tracing) {
                printf("TRACING: Auth failed\n");
              }

              AK_add_to_ack(ack_file_name, "\nUpdate FAILED: [%s] %s\nAuth failed\n",
                            o->type->getName(), get_search_key(o, o->type->getName(), arg));
              NT_write_all_frwds(old_version, arg, tmpdir, ntfy_hash, forw_hash, cross_hash, credentials.from);
              return UP_AUF; /* Auth failed */
            }
          }else { /* old_version  == NULL, so, creation */
            result = check_auth(arg, NULL, o->type->getName(), credentials);
            if(result == UP_AUTH_OK){ 
              if(tracing) {                                
                printf("TRACING: Will send the obj to be added\n");
              }
              /* here we must insert code to import the PGP key, if obj type is key-cert */
              if(strcmp(type, "key-cert") == 0){
                result_from_import_key = import_key(arg);
              }else{
                result_from_import_key = 1;
              }
              if(result_from_import_key == 1){/* no PGP problem */
                result_from_RIPupd = send_object_db(arg, NULL, "ADD");
                if(result_from_RIPupd == 0){/* if there was no problem */
                  AK_add_to_ack(ack_file_name, "\nNew OK [%s] %s\n",
                                o->type->getName(), get_search_key(o, o->type->getName(), arg));
                  NT_write_all_ntfs(NULL, arg, tmpdir, ntfy_hash, forw_hash, cross_hash, credentials.from);

                }else{
                  AK_add_to_ack(ack_file_name, "\nNew FAILED: [%s] %s\nReferential integrity failure\n",
                                o->type->getName(), get_search_key(o, o->type->getName(), arg));
                }
                result_from_RIPupd = 0;
              }else{/* there was a problem with PGP key import */
                AK_add_to_ack(ack_file_name, "\nNew FAILED: [%s] %s\nPGP key problem\n",
                                o->type->getName(), get_search_key(o, o->type->getName(), arg));
              }
            }else{
              /* auth failed ! */
              if(tracing) {
                printf("TRACING: Auth failed\n");
              }

              ER_perror(0, result, "");
              AK_add_to_ack(ack_file_name, "\nNew FAILED: [%s] %s\nAuth failed\n",
                            o->type->getName(), get_search_key(o, o->type->getName(), arg));
              NT_write_all_frwds(NULL, arg, tmpdir, ntfy_hash, forw_hash, cross_hash, credentials.from);
              return UP_AUF; /* Auth failed */
            }
          } 
        }
      }
    }else{/* even if obj doesn't parse properly, it may be a legacy object
            which the user wants to delete... */
       if(tracing){   
         printf("TRACING: Object didn't parse\n");   
       }
       AK_add_to_ack(ack_file_name, "\nUpdate FAILED: Syntax error in object\n");
       //////////////////////////////////
       if(o->attrs.head() != NULL){
         for(attr = o->attrs.head(); attr; attr = o->attrs.next(attr)){
           if(attr->len > 0){
             value = (char*)malloc(attr->len);
             strncpy(value, (char *)(arg+attr->offset) ,
               attr->len - 1);
             value[attr->len - 1] = '\0';
             AK_add_to_ack(ack_file_name, "%s\n", value);
             if(!attr->errors.empty()){
               AK_add_to_ack_string(ack_file_name, attr->errors);
             }
             free(value);
           }else{
             if(!attr->errors.empty()){
               AK_add_to_ack_string(ack_file_name, attr->errors);
             }
           }
         }
       }
       if(o->has_error){
         AK_add_to_ack_string(ack_file_name, o->errors);
       }
       AK_add_to_ack(ack_file_name, "\n");
       //////////////////////////////////
       return UP_NIY; /* Not implemented yet */
    }
}


/* A temporary function to test if there is a PGP signed portion in the file */
int scan_for_PGP(const char * filename){

  FILE *file;
  char *line;

  line = (char *)malloc(1024);
  if((file = fopen(filename, "r")) == NULL){
    printf("Couldn't open the file %s: %s\n", filename, strerror(errno));
    exit(1);  
  }
  while(fgets(line, 1024, file) != NULL){
    if(strstr(line, "-----BEGIN PGP") == line){/* yes, we have a PGP signed portion, so return true */
      fclose(file);
      free(line);
      return 1;
    }
  } 
  fclose(file);
  free(line);
  return 0; /* no, we din't have any PGP signed portions */
}





/* processes the objects in the given file */
void process_file(char * filename, credentials_struct credentials, 
                  GHashTable * AUTO_NIC_hdl_hash, char * ack_file_name, 
                  GHashTable * ntfy_hash, GHashTable * forw_hash, GHashTable * cross_hash){

FILE * input_file;
GSList *list_of_objects = NULL, *list_of_objects2 = NULL;   
GSList *next = NULL;
int object_count = 0;
char *object = NULL;
char *line;
int result = 0;
struct VerifySignObject vSO, *pvSO;
//char keyring[100] = "/home/engin/.gnupg/pubring.gpg";


  /* allocate space for pgp_struct, it will be set to pgp key ID when we encounter a pgp signed message */
  credentials.pgp_struct == (char *)malloc(10);

  line = (char *)malloc(1024);

  /* if we have PGP signed portions in the message */ 
  if(scan_for_PGP(filename)){ 
    strcpy(vSO.outputPath, "/tmp");
    strcpy(vSO.iDocSigFilename, filename);
    strcpy(vSO.iSigFilename, "");
    strcpy(vSO.keyRing, pgp_public_key_ring/*keyring*/);

    PA_VerifySignature(&vSO);

    pvSO = vSO.next;
    while (pvSO != NULL) {
      printf("isValid: %d\n", pvSO->isValid);
      printf("key ID: %x\n",  pvSO->keyID);
      printf("oStream is %s\n", pvSO->oStream);
    

      if(pvSO->isValid == 1){/* if this PGP signed portion is valid, read it */
        if((input_file = fopen( pvSO->oStream/*filename*/, "r")) == NULL){
           printf("Couldn't open the file %s: %s\n", filename, strerror(errno));
           exit(1);  
        }
        AK_add_to_ack(ack_file_name, "\n*** Beginning of PGP signed part from key ID %X\n", pvSO->keyID);
        sprintf(credentials.pgp_struct, "%.8X", pvSO->keyID);
        printf("DEBUG:  credentials.pgp_struct=[%s]\n", credentials.pgp_struct);  
        while(fgets(line, 1024, input_file) != NULL){
          /* first, if it is a pasword, save it, but do not regard it as an attrib */ 
          if(strstr(line, "password:") == line){
            if(tracing){
              printf("DEBUG: This is a password\n");
            }
            credentials.password_list = g_slist_append(credentials.password_list, 
                                          g_strstrip(strdup(line + strlen("password:"))));
            continue;
          }
          /* if the length of the line read is 2, then this is an empty line ("\n\r")*/
          if(strlen(line) == 2){
            if(object != NULL){
              list_of_objects = g_slist_append(list_of_objects, object);
              object = NULL;
            }
          }else{
            /* if the line contains only the EOL sequence "\n\r" */
            if(object == NULL && strlen(line) != 2){
              object = (char *)malloc(strlen(line));
              object = strdup(line);
            }
            else{
              object = (char *)realloc(object, strlen(object) + strlen(line));
              object = strcat(object, line);
            }
          }
        
        }
        fclose(input_file);

        /* now, if at the very and of the input file there wasn't an 
           empty line, we have to add the remaining object in the 'object'
           variable */
        if(object != NULL){
           //cout << "The object was" << endl << object << endl;
           list_of_objects = g_slist_append(list_of_objects, object);
           object = NULL;
        }
  
  
  
        if(tracing) {
           printf("TRACING: Will process the objects in the list\n");
        }
        next = list_of_objects;
        object_count = 0;
        for( next = list_of_objects; next != NULL ; next = g_slist_next(next) ){
          object_count++;
  
          if(tracing) {
            cout << "TRACING: Got an object from the list" << endl;
            cout << (char *)next->data << endl;
          }
       
          if(has_ref_to_AUTO_nic_hdl((char *)next->data)){/* defer the processing */
            if(tracing) {
              printf("TRACING: this object has a ref to an AUTO NIC hdl\n");
            }
            list_of_objects2 = g_slist_append(list_of_objects2, strdup((char *)next->data));
          }else{
            result = 0;
            result = process_object((char *)next->data, credentials, AUTO_NIC_hdl_hash, ack_file_name, 
                                     ntfy_hash, forw_hash, cross_hash);
          }
        }

        if(tracing) {
          printf("TRACING: list_of_objects2 has %d entries\n", g_slist_length(list_of_objects2));
        }
    
        if(tracing) {
          printf("TRACING: will start to process the second list\n");
        }
     
        for( next = list_of_objects2; next != NULL ; next = g_slist_next(next) ){
          if(tracing) {
            printf("TRACING: Will process object: %s\n", (char *)next->data);
          }
          result = process_object((char *)next->data, credentials, AUTO_NIC_hdl_hash, ack_file_name, 
                                   ntfy_hash, forw_hash, cross_hash);
        }
        /* empty the object lists (this must be done by properly freeing the memory taken by them!) */
        list_of_objects = NULL;
        list_of_objects2 = NULL;
        AK_add_to_ack(ack_file_name, "\n*** End of PGP signed part from key ID %X\n", pvSO->keyID);  

        }else{
          AK_add_to_ack(ack_file_name, "\n***  Ignoring PGP signed part from key ID %X", pvSO->keyID); 
          AK_add_to_ack(ack_file_name, "\n***  Bad signature or key is not in public key ring or other error\n\n"); 
        }

      pvSO = pvSO->next;
      /* empty out credentials.pgp_struct for the next loop  */
      strcpy(credentials.pgp_struct,"");
      }

    }
    else{/* the file doesn't contain PGP signed portions */ 
     if((input_file = fopen(filename, "r")) == NULL){
         printf("Couldn't open the file %s: %s\n", filename, strerror(errno));
         exit(1);  
     }

  
    while(fgets(line, 1024, input_file) != NULL){
      /* first, if it is a pasword, save it, but do not regard it as an attrib */ 
      if(strstr(line, "password:") == line){
        if(tracing){
          printf("DEBUG: This is a password\n");
        }
        credentials.password_list = g_slist_append(credentials.password_list, 
                                      g_strstrip(strdup(line + strlen("password:"))));
        continue;
      }
      /* if the length of the line read is 2, then this is an empty line ("\n\r")*/
      if(strlen(line) == 2){
        if(object != NULL){
           list_of_objects = g_slist_append(list_of_objects, object);
           object = NULL;
        }
      }else{
        /* if the line contains only the EOL sequence "\n\r" */
        if(object == NULL && strlen(line) != 2){
          object = (char *)malloc(strlen(line));
          object = strdup(line);
        }
        else{
          object = (char *)realloc(object, strlen(object) + strlen(line));
          object = strcat(object, line);
        }
      }
      
    }
    fclose(input_file);

    /* now, if at the very and of the input file there wasn't an 
       empty line, we have to add the remaining object in the 'object'
       variable */
    if(object != NULL){
       //cout << "The object was" << endl << object << endl;
       list_of_objects = g_slist_append(list_of_objects, object);
       object = NULL;
    }



    if(tracing) {
       printf("TRACING: Will process the objects in the list\n");
    }
    next = list_of_objects;
    object_count = 0;
    for( next = list_of_objects; next != NULL ; next = g_slist_next(next) ){
      object_count++;

      if(tracing) {
        cout << "TRACING: Got an object from the list" << endl;
        cout << (char *)next->data << endl;
      }
      
      if(has_ref_to_AUTO_nic_hdl((char *)next->data)){/* defer the processing */
        if(tracing) {
          printf("TRACING: this object has a ref to an AUTO NIC hdl\n");
        }
        list_of_objects2 = g_slist_append(list_of_objects2, strdup((char *)next->data));
      }else{
        result = 0;
        result = process_object((char *)next->data, credentials, AUTO_NIC_hdl_hash, ack_file_name, 
                                 ntfy_hash, forw_hash, cross_hash);
      }
    }

    if(tracing) {
      printf("TRACING: list_of_objects2 has %d entries\n", g_slist_length(list_of_objects2));
    }
  
    if(tracing) {
      printf("TRACING: will start to process the second list\n");
    }
  
    for( next = list_of_objects2; next != NULL ; next = g_slist_next(next) ){
      if(tracing) {
        printf("TRACING: Will process object: %s\n", (char *)next->data);
      }
      result = process_object((char *)next->data, credentials, AUTO_NIC_hdl_hash, ack_file_name, 
                               ntfy_hash, forw_hash, cross_hash);
    }
  
  }
  
}/* process_file */






/*  Generates a unique file name and returns the full path of the filename 
    for storing notification message.  */
      
char * generate_upd_file(){

   char * name;
     
   /* allocate space for name.  32 should be enough for PID */
   name = (char*)malloc(strlen("/tmp/dbupdate-tmp.") + strlen("notify") +32 ); 
   
   sprintf(name, "/tmp/dbupdate-tmp.%i", getpid());

     
   return name;
      
}





/* main */
void main(int argc, char **argv, char **envp){
  //init_and_set_options(argc, argv, envp);

  int count = 0;
  int i,j;

  char ** temp_vector;
  char * temp;
  char * temp_upd_file = NULL;
  char *input_file_name = NULL;
  GHashTable *AUTO_NIC_hdl_hash;
  credentials_struct credentials;


  GHashTable *ntfy_hash, *forw_hash, *cross_hash;
  

  char *mail_command_line, * ack_file_name;
  char *config_file_name = NULL;
  

  /* for using MM module */
  int retcode;
  MM_header *mail_header = NULL;
  MM_xmp_list *part_list;
  MM_xmp *partptr;
  long debug = 0;
    
  /* optarg & optind are necessary to use getopt(3C) */ 
  extern char *optarg;
  extern int optind;


  /* create notification hashes */
  ntfy_hash = g_hash_table_new(g_str_hash, g_str_equal);
  forw_hash = g_hash_table_new(g_str_hash, g_str_equal);

      
  credentials.password_list = NULL;
  credentials.from = NULL;
  int ch;
  char * to_address = NULL;

  AUTO_NIC_hdl_hash = g_hash_table_new(g_str_hash, g_str_equal);       
  error_init(argc, argv);
  
      
              

  while ((ch = getopt(argc, argv, "MtTf:c:")) != -1){
          switch(ch) {
          case 'M':
                  reading_from_mail = 1;
                  break;
          case 'f':
                  input_file_name = strdup(optarg);
                  break;
          case 'c':
                  config_file_name = strdup(optarg);
                  break;
          case 't': 
                  tracing = 1;
                  break;
          case 'T':
                  test_mode = 1; 
                  break;       
          case '?':
          default:
                  printf("Unknown option\n");exit(1);
          }
  }


  /* config stuff */
  ca_populateDictionary(dictionary, VARS);
  /* if -c flag is given, use the named file as config file, otherwise use
     default filename */ 
  if( config_file_name != NULL){
    ca_readConfig(config_file_name, confVars, VARS);
  }else{
    ca_readConfig("dbupdate.conf", confVars, VARS);
  }

  tmpdir = ca_get_tmpdir;
  tmpdir = g_strstrip(tmpdir);
  mailcmd = ca_get_mailcmd;
  mailcmd = g_strstrip(mailcmd);
  notitxt = ca_get_notitxt;
  mailtxt = ca_get_mailtxt; 
  crosslog = ca_get_crosslog;
  fwtxt = ca_get_fwtxt;
  humailbox = ca_get_humailbox;
  humailbox = g_strstrip(humailbox);
  overridecryptedpw = ca_get_overridecryptedpw;
  overridecryptedpw = g_strstrip(overridecryptedpw);
  acklog = ca_get_acklog;
  acklog = g_strstrip(acklog);
  notiflog = ca_get_notiflog;
  notiflog = g_strstrip(notiflog);
  notimailtxt = ca_get_notimailtxt;  
  forwlog = ca_get_forwlog;
  forwlog = g_strstrip(forwlog);
  fwmailtxt = ca_get_fwmailtxt;
  country = ca_get_country;
  pgppath = ca_get_pgppath;
  pgppath = g_strstrip(pgppath);
  pgp_public_key_ring = (char *)malloc(strlen(pgppath) + strlen("/pubring.gpg") + 2);
  sprintf(pgp_public_key_ring ,"%s/pubring.gpg", pgppath);
  if(test_mode != 1){/* if it is not already set to 1 (from command line), read from config */
    test_mode = ca_get_testmode;
  }
  /* construct country array from country string variable */
  
  temp_vector = g_strsplit(country, "\n", 0);
  for(i=0, j=0; temp_vector[i] != NULL; i++){
    temp_vector[i] == g_strstrip(temp_vector[i]);
    if(strlen(temp_vector[i]) > 0){
      countries[j] = strdup(temp_vector[i]);
      g_strup(countries[j]);
      printf("DEBUG: got a country country[%i] =.%s.,\n", j, countries[j]);
      j++;
    }
  }
  countries[j] = NULL; /* mark the end of array */
  printf("DEBUG: countries[%i] = NULL\n", j);

#if 0
  //test
  tmpdir =strdup("/home/engin/REIMP/ncc/RIP/bin/dbupdate");
  mailcmd=strdup("/usr/lib/sendmail -fbit-bucket -t");
  notitxt=strdup(" Dear Colleague,

 This is to notify you that some object(s) in the RIPE database
 which you either maintain or are listed as to-be-notified have
 been added, deleted or changed.

 The objects below are the old and new entries for these objects
 in the database. In case of DELETIONS, the deleted object is
 displayed. NOOPs are not reported.");
  mailtxt=strdup("");
  crosslog=strdup("./crosslog");
  fwtxt=strdup("Dear Maintainer,

  This is to notify you that some objects in which you are mentioned as
  a maintainer were requested to be changed, but *failed* the proper
  authorisation for any of the mentioned maintainers.
  Please contact the sender of these changes about changes that need
  to be made to the following objects.
");
  humailbox=strdup("bit-bucket@ripe.net");
  overridecryptedpw=strdup("ren5C38li6ADw");
  acklog=strdup("./acklog");
  notiflog=strdup("./notiflog");
  notimailtxt=strdup("The update causing these changes had the following mail headers:

 - From:    $FROM
 - Cc:      $CC
 - Subject: $SUBJECT
 - Date:    $MDATE
 - Msg-Id:  $MSGID

 RIPE Database Notification Department");
  forwlog=strdup("./forwlog");
  fwmailtxt=strdup("The mail message causing these failures had the following mail headers:

  - From:    $FROM
  - Cc:      $CC
  - Subject: $SUBJECT
  - Date:    $MDATE
  - Msg-Id:  $MSGID

  RIPE Database Maintainer Forwarding Department");
  pgppath=strdup("/home/engin/.gnupg");
  pgp_public_key_ring =strdup("/home/engin/.gnupg/pubring.gpg");
  country = strdup("NL\nEU");
  temp_vector = g_strsplit(country, "\n", 0);
  for(i=0, j=0; temp_vector[i] != NULL; i++){
    temp_vector[i] == g_strstrip(temp_vector[i]);
    if(strlen(temp_vector[i]) > 0){
      countries[j] = strdup(temp_vector[i]);
      g_strup(countries[j]);
      printf("DEBUG: got a country country[%i] =.%s.,\n", j, countries[j]);
      j++;
    }
  }
  countries[j] = NULL; /* mark the end of array */
 
  //end of test, delete later 
#endif
    
  printf("TMPDIR is: [%s]\n", tmpdir);
  printf("MAILCMD is: [%s]\n", mailcmd);
  printf("NOTITXT is: [%s]\n", notitxt);
  printf("CROSSLOG is: [%s]\n", crosslog);
  printf("FWTXT is: [%s]\n", fwtxt);
  printf("HUMAILBOX is: [%s]\n", humailbox);
  printf("OVERRIDECRYPTEDPW is: [%s]\n", overridecryptedpw);
  printf("ACKLOG is: [%s]\n", acklog);
  printf("NOTIFLOG is: [%s]\n", notiflog);
  printf("FORWLOG is: [%s]\n", forwlog);
  printf("NOTIMAILTXT is: [%s]\n", notimailtxt);
  printf("FWMAILTXT is: [%s]\n", fwmailtxt);
  printf("COUNTRY is: [%s]\n", country);
  printf("PGPPATH is: [%s]\n", pgppath);
   
  printf("TESTMODE is: [%i]\n", test_mode);
  /* end of config stuff */

  
    
  /* initialize the parser */
  schema.initialize();


  /* Generate a name for temporary file for storing acks (AK_ack_file_name_generate
      also creates it) */
  ack_file_name = AK_ack_file_name_generate(tmpdir, ACK_FILE_PREFIX);
  

  /* Allocate memory for the header */
  mail_header = (MM_header *)malloc(sizeof(MM_header));

  /* Initialize the list of extracted MIME parts */
  part_list = (MM_xmp_list *)malloc(sizeof(MM_xmp_list));
  MM_xmp_list_init (part_list);

  
  if(reading_from_mail){
    if(input_file_name != NULL){
      if((retcode = MM_decode(input_file_name, mail_header, part_list, 1, 0)) != 0){
        printf("DEBUG: MM_decode returned %i\n", retcode);
        exit(retcode);
      }
      
    }else{/* input_file_name == NULL */
      temp_upd_file = generate_upd_file();
      MM_store("-", temp_upd_file, 0);
      if((retcode = MM_decode(temp_upd_file, mail_header, part_list, 1, 0)) != 0){
        printf("DEBUG: MM_decode returned %i\n", retcode);
        exit(retcode);
      }

    }
        unlink(temp_upd_file);
        printf ("Mail headers:\n\n");
        printf ("From - [%s]",mail_header->from);
        /* some MAIL-FROM's in mntner auths contain "From: " string too,
           so we have to have it in credential.from */
        temp = (char *)malloc(strlen(mail_header->from) + strlen("From: ") + 1);
        sprintf(temp, "From: %s", mail_header->from);
        temp[strlen(temp) - 4] = '\0'; /* cut two '\r\n's at the end */
        credentials.from = temp;
        printf ("credentials.from = [%s]\n", credentials.from );
        mail_header->subject[strlen(mail_header->subject) - 4] = '\0';
        printf ("Subject - [%s]",mail_header->subject);
        printf ("Date - [%s]",mail_header->date);
        printf ("Message-ID - [%s]",mail_header->message_id);
        printf ("Reply-To - [%s]",mail_header->reply_to); 
        printf ("Cc - [%s]",mail_header->cc);
        to_address = find_to_address(credentials.from);
        AK_add_to_ack(ack_file_name, "To: %s\nFrom: %s\nSubject: Re: %s \nReply-To: %s\n\nAcknowledgement message from database software, beta version\n", to_address, humailbox, mail_header->subject, humailbox);
        if(credentials.from != NULL){
          AK_add_to_ack(ack_file_name, "\n[%s]\n", credentials.from);
        }

        partptr = part_list->head;
        while (partptr != NULL){
          
          printf("-----------------------------------------\n");
          printf ("Section: %s\n",partptr->number);
          printf ("Content-type: %s\n",partptr->type);
          if (partptr->supported){
            printf ("Supported\n");
            printf ("Filename is [%s]\n", partptr->file);
            process_file(partptr->file, credentials, 
                  AUTO_NIC_hdl_hash, ack_file_name, 
                  ntfy_hash, forw_hash, cross_hash);
          }
          else
            printf ("Unsupported\n");
          printf ("\n\n");
          partptr = partptr->next;
        }
      

      /* Clean up the temporary files */
      MM_cleanup(part_list, 0);

      
  }else{/* not reading from the mail message */
    if(input_file_name != NULL){
      process_file(input_file_name, credentials, 
                  AUTO_NIC_hdl_hash, ack_file_name, 
                  ntfy_hash, forw_hash, cross_hash);
    }
      
  }  


  if(reading_from_mail && to_address != NULL){
    AK_send_ack(ack_file_name, to_address, mailcmd);
  }
  AK_log_ack(ack_file_name, acklog);
  AK_delete_ack(ack_file_name);

  NT_send_ntfy_list(ntfy_hash, mailcmd);
  NT_log_ntfy_list(ntfy_hash, notiflog); 
  NT_delete_ntfy_list(ntfy_hash);

  NT_send_ntfy_list(forw_hash, mailcmd);
  NT_log_ntfy_list(forw_hash, forwlog); 
  //NT_delete_ntfy_list(forw_hash);

  

  if(tracing) {
    printf("TRACING: END\n");
  }


}
