/***************************************
  $Revision: 1.62 $

  DBupdate 

  Status: NOT REVIEWED, 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 "UP_extrnl_syntax.h"
#include "UP_subject.h"
#include "er_yacc_helper.h"
#include "erroutines.h"
#include "ca_configFns.h"
#include "ca_dictionary.h"
#include "ca_macros.h"
#include "ca_srcAttribs.h"
#include "notification.h"
#include "gpg.h"
#include "mail_parser.h"
#include "process.h"

#ifdef HAVE_PARAM_H
#include <sys/param.h>
#endif


int tracing = 0;
int test_mode = 0;
int supress_ack_notif = 0;
int print_out_ack = 0;

/* do we process a mail */
int reading_from_mail = 0;

/* are we processing networkupdate? */
int networkupdate = 0;

/* sender of the mail, in case of a mail update */
char *update_mail_sender = NULL;
char *update_mail_subject = NULL;
char *update_mail_date = NULL;
char *update_mail_ID = NULL;
char *update_mail_cc = NULL;

/* required configuration variables */
char *tmpdir = NULL;
char *lockdir = NULL;
char *mailcmd = NULL;
char *notitxt = NULL;
char *notimailtxt = NULL;
char *fwtxt   = NULL;
char *fwmailtxt = NULL;
char *mtfwheader = NULL; 
char *mtfwtxt = NULL;
char *mailtxt = NULL;
char *acksig = NULL;
char *defmail = NULL;
char *notiflog = NULL;
char *crosslog = NULL;
char *acklog = NULL;
char *forwlog = NULL;
char *humailbox = NULL;
char *autobox = NULL;
char *overridecryptedpw = NULL;
char *country = NULL;
char *countries[400];
char *sources[100];
char *pgppath = NULL;
char *gpgcmd = NULL;
char *pgp_public_key_ring = NULL;
char *autodbmhelp = NULL; 
char *update_host = NULL;
int  update_port;
char *query_host = NULL;
int  query_port;
char *cn_subject_add = NULL;
char *cn_subject_del = NULL;
char *cn_explain_add = NULL;
char *cn_explain_del = NULL;
char *cn_overlap_add = NULL;
char *cn_overlap_del = NULL;
char *cno_subject_add = NULL;
char *cno_subject_del = NULL;
char *cno_explain_add = NULL;
char *cno_explain_del = NULL;
char *cno_overlap_add = NULL;
char *cno_overlap_del = NULL;
char *mheader = NULL;
char *DBhost = NULL;
int  DBport;
char *DBuser = NULL;
char *DBname = NULL;
char *DBpasswd = NULL;
/* end of config variables */

/* result of subject line processing is kept in the following struct. */
up_subject_struct subject_result;


/* hostname and pid are used all over the program, so we save them into these variables */
char hostname[MAXHOSTNAMELEN];
//char * hostname;
int pid;

/* name of the lock file, which is used for the crash recovery mechanism */
char * lock_file_name;

void error_init(int argc, char ** argv) {

  ER_init("dbupdate", 1);
  

} /* error_init() */






/* 'lockfile' struct is for keeping both the name of the lock file and the file descriptor
    of it, which is open during the execution of dbupdate. We need the filedes to close it,
    when dbupdate finishes, and the name to delete the file. */
typedef struct {
   char * lockname;
   int filedes;
} lockfilestruct;


lockfilestruct lockfile;



/* Deletes the key defined in the incoming object (a key-cert object)
   from the public keyring. Returns NULL if there was no error,
   returns an error message if there is an error */
char * delete_key(char * obj){

  struct ImportKeyObject iKO;
  char * obj_keyID;
  char * key_cert_attr;
  GSList * templist, * certiflist, * next;
  u32 keyID;
  char * tempfile;
  char ** lines;
  int i;
  FILE * key_file;
  char * temp, * temp2;
  char * error_string;

  templist = get_attr_list(obj, "key-cert");
  key_cert_attr = strdup((char *)templist->data);
  g_slist_free(templist);

  tempfile = (char *)malloc(strlen(tmpdir) + strlen("tmp-key.") + 32);
  sprintf(tempfile, "%s/tmp-key.%i", tmpdir, pid /*getpid()*/);

  /* 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);
     ER_perror(FAC_UP, UP_CANTOPEN, "Can't open temporary file, %s", tempfile);
     exit(1);
  }
  for( next = certiflist; next != NULL ; next = g_slist_next(next) ){
    lines = g_strsplit((char *)next->data, "\n", 0);
    if(lines[0] == NULL){/* if this was an empty attribute, just print an empty line */
      fprintf(key_file, "\n"); 
    }
    for(i = 0; lines[i] != NULL; i++){
      temp = strdup(lines[i]);
      if(i != 0 && temp[0] == '+'){/* if it begins with a plus */
        temp2 = strdup(temp + 1);
        g_strstrip(temp2);
        fprintf(key_file, "%s\n", temp2);
        free(temp);free(temp2);
      }else{
        g_strstrip(temp);
        fprintf(key_file, "%s\n", temp);
        free(temp); 
      }
    }
    g_strfreev(lines);
    //fprintf(key_file, "%s\n", (char *)next->data);
  }
  fclose(key_file);
  g_slist_free(certiflist);

  strcpy(iKO.iFilename, tempfile);

  if(tracing){
    printf("TRACING: delete_key: key_cert_attr: [%s]\n", key_cert_attr);
  }
  
  obj_keyID = strdup(key_cert_attr + strlen("PGPKEY-"));

  if(tracing){
    printf("TRACING: delete_key: obj_keyID: [%s]\n", obj_keyID);
  }
  
  keyID = strtoul(obj_keyID, NULL, 16);

  if(tracing){
    printf("TRACING: delete_key: keyID is: %u, %X\n", keyID, keyID);
  }
  
  
  
  strcpy(iKO.keyRing, pgp_public_key_ring);
  PA_RemoveKey(&iKO);

  if(tracing){
    printf("TRACING: importKeyObj status:\n");
    printf("TRACING: isValid: %d\n", iKO.rc);
  }
  


  unlink(tempfile);
  if(iKO.rc == iKO_OK){/* if PA_RemoveKey returned OK */
    return NULL;
  }else{/* if PA_RemoveKey returned not OK */
      switch(iKO.rc){
        case iKO_UNCHANGED:      error_string = strdup("the key is already in the keyring");break; 
        case iKO_NOUSERID:       error_string = strdup("no user ID could be extracted");break;
        case iKO_GENERAL:        error_string = strdup("general PGP error");break;
        case iKO_NOTVALIDUSERID: error_string = strdup("no valid user ID ");break;
        case iKO_NOPUBLICKEY:    error_string = strdup("no public key in the object");break;
        case iKO_NODEFAULTPUBLICKEYRING: error_string = strdup("general PGP error");break;
        case iKO_CRC_ERROR:      error_string = strdup("CRC error in the certificate");break;
        case iKO_NO_OPENPGP_DATA:error_string = strdup("no OpenPGP data in the object");break;
        case iKO_NO_IN_FILES:    error_string = strdup("general PGP error");break;
        case iKO_GENERALFAILURE: error_string = strdup("general PGP error");break;
        default:            error_string = strdup("general PGP error");
      }
      return error_string; 
  }

  return NULL;
}


/* Takes a key-certif object, extracts its 'certif' attribute and adds
   the key into public keyring
   If there is no problem, it returns NULL
   If there is a problem, then it returns a string which contains an error
   message */
char * import_key(char *obj){

  char * tempfile;
  struct ImportKeyObject iKO;
  GSList * certiflist, * next, * templist;
  FILE * key_file;
  char keyID[9];
  char * obj_keyID, * key_cert_attr;
  char * error_string = NULL;
  char ** lines;
  int i;
  char * temp, * temp2;
   
  tempfile = (char *)malloc(strlen(tmpdir) + strlen("tmp-key.") + 32);
  sprintf(tempfile, "%s/tmp-key.%i", tmpdir, pid /*getpid()*/);

  /* 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){
     ER_perror(FAC_UP, UP_CANTOPEN, "Can't open temporary file, %s", tempfile);
     exit(1);
  }
  for( next = certiflist; next != NULL ; next = g_slist_next(next) ){
    lines = g_strsplit((char *)next->data, "\n", 0);
    if(lines[0] == NULL){/* if this was an empty attribute, just print an empty line */
      fprintf(key_file, "\n"); 
    }
    for(i = 0; lines[i] != NULL; i++){
      temp = strdup(lines[i]);
      if(i != 0 && temp[0] == '+'){/* if it begins with a plus */
        temp2 = strdup(temp + 1);
        g_strstrip(temp2);
        fprintf(key_file, "%s\n", temp2);
        free(temp);free(temp2);
      }else{
        g_strstrip(temp);
        fprintf(key_file, "%s\n", temp);
        free(temp); 
      }
    }
    g_strfreev(lines);
    //fprintf(key_file, "%s\n", (char *)next->data);
  }
  fclose(key_file);
  g_slist_free(certiflist);

  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);

  
  templist = get_attr_list(obj, "key-cert");
  key_cert_attr = strdup((char *)templist->data);
  g_slist_free(templist);

  printf("key_cert_attr: [%s]\n", key_cert_attr);
  obj_keyID = strdup(key_cert_attr + strlen("PGPKEY-"));
  printf("obj_keyID: [%s]\n", obj_keyID);
  if(iKO.rc == iKO_OK && (strcmp(obj_keyID, keyID) == 0)){/* if PA_ImportKey returned OK 
                                                            and the real keyID is equal to the
                                                            keyID in the 'key-cert' attribute */
    return NULL;
  }else{/* if PA_ImportKey returned not OK or obj_keyID, keyID didn't match */
    if(iKO.rc != iKO_OK){
      switch(iKO.rc){
        case iKO_UNCHANGED:      error_string = strdup("the key is already in the keyring");break; 
        case iKO_NOUSERID:       error_string = strdup("no user ID could be extracted");break;
        case iKO_GENERAL:        error_string = strdup("general PGP error");break;
        case iKO_NOTVALIDUSERID: error_string = strdup("no valid user ID ");break;
        case iKO_NOPUBLICKEY:    error_string = strdup("no public key in the object");break;
        case iKO_NODEFAULTPUBLICKEYRING: error_string = strdup("general PGP error");break;
        case iKO_CRC_ERROR:      error_string = strdup("CRC error in the certificate");break;
        case iKO_NO_OPENPGP_DATA:error_string = strdup("no OpenPGP data in the object");break;
        case iKO_NO_IN_FILES:    error_string = strdup("general PGP error");break;
        case iKO_GENERALFAILURE: error_string = strdup("general PGP error");break;
        default:            error_string = strdup("general PGP error");
      }
      return error_string; 
    }else{
      error_string = (char *)malloc(1024);/* this should be enough */
      sprintf(error_string, "Keyid for this certificate (%s) is not the same as the PGPKEY field (%s)", 
                 keyID, obj_keyID);
      return error_string;
    }
  }
      
}



/* 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 * incoming, 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;
    char * result_from_import_key = NULL;
    char * result_from_delete_key = NULL;
    char * auto_nic = NULL;
    char * changed_obj = NULL;
    char * obj_with_AUTO_NIC_hdl;
    char * assigned_NIC;
    char * type;
    char * arg;
    external_syntax_struct * external_syntax_results;


    char * value = NULL;/* these two are for */
    Attr * attr;        /* ack messages only */ 

    arg = strdup(incoming);
    
    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((changed_obj = replace_refs_to_AUTO_NIC_hdl(changed_obj, arg, NIC_hdl_hash)) == NULL){
         /* try to parse the object only for reporting purposes */
         o->scan_silent(arg,strlen(arg));
         AK_add_to_ack(ack_file_name, "\nUpdate FAILED: [%s] Unknown AUTO NIC handle referenced\n%s\n", 
                 (o && o->type) ? o->type->getName() : "unknown-type", arg);
         delete(o);
         return UP_ANE; /* AUTO NIC hdl error */
         
       }else{ /* in this case, we must use changed_obj instead of arg */

         free(arg); 
         arg = changed_obj; 

       };
    }
  
    
    code = o->scan_silent(arg,strlen(arg));
    if(code){
      type = get_class_type(o);
      /* is the object to be deleted? */
      if(o->isDeleted){
        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");
              }
              if(strcmp(type, "key-cert") == 0){
                result_from_delete_key = delete_key(arg);
              }else{
                result_from_delete_key = NULL;
              }
              /* if there was no problem with key deletion from the key-ring */
              if(result_from_delete_key == NULL){
                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{
                 AK_add_to_ack(ack_file_name, "\nDelete FAILED: [%s] %s\n%s\n",
                                o->type->getName(), get_search_key(o, o->type->getName(), arg), result_from_delete_key);
              }
            }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)){/* if the object has an AUTO NIC hdl */
          external_syntax_results = UP_check_external_syntax(o, arg);
          if(     external_syntax_results->result != UP_EXTSYN_ERR 
               && external_syntax_results->result != UP_EXTSYN_ERR_WARN){/* if there is no error */
            /* 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(external_syntax_results->new_obj, 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){
                if(tracing){
                  printf("TRACING: auto_nic=[%s], assigned_NIC=[%s]\n", auto_nic, assigned_NIC);
                }
                g_hash_table_insert(NIC_hdl_hash, auto_nic, assigned_NIC);
                if(tracing){
                  printf("TRACING: NIC_hdl_hash has %i pairs\n",g_hash_table_size(NIC_hdl_hash));
                }
              }
              
            }else{
              /* auth failed ! */
              if(tracing) {
                printf("TRACING: Auth failed\n");
              }
  
              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{/* external syntax check failed */
            AK_add_to_ack(ack_file_name, "\nUpdate FAILED: [%s] %s\n%s%s",
                   o->type->getName(), get_search_key(o, o->type->getName(), arg), 
                   arg, external_syntax_results->error_str);
          }
        }
        else{ 
          old_version = get_old_version(arg);
          if(old_version != NULL){/* so, this is an update operation */
            if( (!reading_from_mail) || 
                (subject_result.result != UP_SUBJ_NEW_ENFORCED)){/* If the user didn't enforce 
                                                                    creation in the subject line */
              external_syntax_results = UP_check_external_syntax(o, arg);
              if(    external_syntax_results->result != UP_EXTSYN_ERR 
                  && external_syntax_results->result != UP_EXTSYN_ERR_WARN){/* if there is no error */
                /* if the old version & the new one are not identical */ 
                if(identical(old_version, arg) != 1){
                  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(external_syntax_results->new_obj, 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] %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{/* if the old and new versions of the object are the same */
                  if(tracing) {                                
                      printf("TRACING: The obj sent is identical to the one in the DB (NOOP)\n");
                  }
                  AK_add_to_ack(ack_file_name, "\nUpdate NOOP: [%s] %s\n",
                                    o->type->getName(), get_search_key(o, o->type->getName(), arg));
   
                }
              }else{/* if there is an error in external syntax checks */
                AK_add_to_ack(ack_file_name, "\nUpdate FAILED: [%s] %s\n%s%s",
                     o->type->getName(), get_search_key(o, o->type->getName(), arg), 
                     arg, external_syntax_results->error_str);
              }

            }else{/* if the user enforced creation (using NEW keyword) in the subject line */

              AK_add_to_ack(ack_file_name, "\nUpdate FAILED: [%s] %s\n%s\n"
                   "*ERROR*:  Object already exists\n",
                   o->type->getName(), get_search_key(o, o->type->getName(), arg), 
                   arg);
              
            }
          }else { /* old_version  == NULL, so, creation */
            external_syntax_results = UP_check_external_syntax(o, arg);
            if(    external_syntax_results->result != UP_EXTSYN_ERR 
                && external_syntax_results->result != UP_EXTSYN_ERR_WARN){/* if there is no error */
              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");
                }
                 /* if the object is a key-cert object, then we must import the PGP key */
                if(strcmp(type, "key-cert") == 0){
                  result_from_import_key = import_key(arg);
                }else{
                  result_from_import_key = NULL;
                }
                if(result_from_import_key == NULL){/* no PGP problem */
                  result_from_RIPupd = send_object_db(external_syntax_results->new_obj, 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\n%s\n",
                                  o->type->getName(), get_search_key(o, o->type->getName(), arg),
                                  result_from_import_key);
                }
              }else if(result == UP_FWD){ /* this was a maintainer or as-block creation request, so
                                             forward it to <HUMAILBOX> */

                if(tracing){

                  printf("TRACING: Maintainer or as-block request will be forwarded to <HUMAILBOX>\n");
                  
                }

                AK_add_to_ack(ack_file_name, "\nNew FAILED: [%s] %s\n%s\n"
                              "*ERROR*:     %s objects cannot be created automatically\n"
                              "*ERROR*:     This object has been forwarded to %s\n"
                              "*ERROR*:     for authorisation.\n"
                              "*ERROR*:     No further action from your part is required\n",
                              o->type->getName(), get_search_key(o, o->type->getName(), arg),
                              external_syntax_results->new_obj, o->type->getName(), humailbox);

                /* and forward this creation request to <HUMAILBOX> */
                NT_forw_create_req(external_syntax_results->new_obj);
                
              }else{
                /* auth failed ! */
                if(tracing) {
                  printf("TRACING: Auth failed\n");
                }

                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{
              AK_add_to_ack(ack_file_name, "\nNew FAILED: [%s] %s\n%s%s",
                              o->type->getName(), get_search_key(o, o->type->getName(), arg), 
                              arg, external_syntax_results->error_str);
            }
          } 
        }

      }
    }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; /* XXX Not implemented yet */
    }
}





/* 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;


  
     line = (char *)malloc(1024);

     if((input_file = fopen(filename, "r")) == NULL){
         ER_perror(FAC_UP, UP_CANTOPEN, "Couldn't open the file %s: %s\n", filename, strerror(errno));
         exit(1);  
     }

  
    while(fgets(line, 1023, 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("TRACING: This is a password\n");
        }
        credentials.password_list = g_slist_append(credentials.password_list, 
                                      g_strstrip(strdup(line + strlen("password:"))));
        continue;
      }
      line = UP_remove_EOLs(line); /* remove '\n's and '\r' first */
      /* remove trailing white space */
      line = g_strchomp(line); 
      if(strlen(line) == 0){/* then, this was an empty line */
        if(object != NULL){
           list_of_objects = g_slist_append(list_of_objects, object);
           if(tracing){
             printf("TRACING: added an object: [%s]\n", object);
           }
           object = NULL;
        }
      }else{
        if(object == NULL && strlen(line) != 0){
          object = (char *)malloc(strlen(line) + 2);
          object = strcpy(object, line);
          object = strcat(object, "\n"); /* add EOL again (we removed it before) */
        }
        else{
          object = (char *)realloc(object, strlen(object) + strlen(line) + 2);
          object = strcat(object, line);
          object = strcat(object, "\n");
        }
      }
      
    }
    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){
       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(tmpdir) + strlen("/dbupdate-tmp.") + 32 ); 
   
   sprintf(name, "%s/dbupdate-tmp.%i", tmpdir, pid /*getpid()*/);

     
   return name;
      
}


/* create_lock_file: creates a lock file in lockdir and locks it. This is a 
   part of crash recovery. Must be called in the beginning of the run. At the
   end, the file must be removed. */
/* The idea: Create the "lock" file, and lock it. When another process starts
   running, it checks the existing lock files. If some exists, then it checks
   if it is locked or not. It not locked, then assumes that the corresponding
   dbupdate is alredy running. If not locked, assumes that it has crashed. 
   (note: when a process crashes, the kernel releases all the files locked by
   this process [by the OS]) 
   Problem: locking doesn't work properly on some NFS implementations. */

lockfilestruct create_lock_file(){
  
  lockfilestruct lock;
  int file;
  int length;

  /* allocate space for file name */
  length = strlen(lockdir) +  strlen(hostname) + 32;
  lock.lockname = (char *)malloc(length + 1);

  snprintf(lock.lockname, length, "%s/dbupdate.%s.%ld", lockdir, hostname, pid /*getpid()*/);

  /* we will lock the file, so we have to use open(), but not fopen() (see man 
     page of lockf(3C)) */
  if(( file = open(lock.lockname, O_RDWR|O_CREAT)) == -1){
    ER_perror(FAC_UP, UP_CANTOPEN, "Can't open lock file, %s", lock.lockname);
    exit(1);
  }
 
  if(lockf(file, F_LOCK, 0) == -1){
    ER_perror(FAC_UP, UP_CANTLOCK, "Can't lock the file, %s", lock.lockname);
    exit(1);
  }; 

  lock.filedes = file;

  return lock;

}





/* remove_lock_file(): unlocks and removes the file  */
void remove_lock_file(lockfilestruct lockfile){

  close(lockfile.filedes); /* this will remove the lock at the same time */ 
  unlink(lockfile.lockname);

}



/* writes the checkpoint file with the specified state */
void write_checkpoint(int state){

  char * filename;
  char * tmpfilename;
  int length;
  FILE * file;

  if(tracing){
    printf("TRACING: write_checkpoint, state=[%i]\n", state); 
  }
  length = strlen(lockdir) +  strlen(hostname) + 64;
  filename    = (char *)malloc(length + 1);
  tmpfilename = (char *)malloc(length + 5);
 
  snprintf(filename,    length, "%s/dbupdate.checkpoint.%s.%ld",     lockdir, hostname, pid );
  snprintf(tmpfilename, length, "%s/dbupdate.checkpoint.%s.%ld.tmp", lockdir, hostname, pid );

  if(( file = fopen(tmpfilename, "w")) == NULL){
    //fprintf(stderr, "Can't open temp checkpoint file, %s", tmpfilename);
    ER_perror(FAC_UP, UP_CANTOPEN, "Can't open temp checkpoint file, %s", tmpfilename);
    exit(1);
  }

  fprintf(file, "[STATE]\n%i\n", state);

  fprintf(file, "[FLAGS]\n");
  /* should print the flags here */
  
  fprintf(file, "[PARTS]\n");
  /* should print the parts (filenames) here */

  fprintf(file, "[OBJECTS1]\n");

  fprintf(file, "[OBJECTS2]\n");

  fprintf(file, "[ACKFILE]\n");
  
  fprintf(file, "[NOTIFFILES]\n");

  fprintf(file, "[TIDS1]\n");
  
  fprintf(file, "[TIDS2]\n");

  fprintf(file, "[NICHDLHASH]\n");

  fprintf(file, "[CURRENTOBJECT]\n");

  fprintf(file, "[CURRENTPART]\n");

  
  fclose(file);

  rename(tmpfilename, filename);

  /* free the char *'s */
  free(tmpfilename);
  free(filename);

}




/* removes check point file */
void remove_checkpoint(){

  char * filename;
  int length;
  FILE * file;

  if(tracing){
    printf("TRACING: remove_checkpoint\n"); 
  }
  
  length = strlen(lockdir) +  strlen(hostname) + 64;
  filename    = (char *)malloc(length + 1);
 
  snprintf(filename,    length, "%s/dbupdate.checkpoint.%s.%ld",     lockdir, hostname, pid );

  unlink(filename);
 
  /* free the char * */
  free(filename);

}







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

  int count = 0;
  int i,j;
  int no_of_updateables = 0;
  char ** temp_vector;
  char * temp;
  char * temp_upd_file = NULL;
  char *input_file_name = NULL;
  GHashTable *AUTO_NIC_hdl_hash;
  credentials_struct credentials;
  FILE * upd_file;
  char c;
  char * mheader_replaced = NULL;
  char * mailtxt_replaced = NULL;

  /* temp variables to read from conf */
  char * source = NULL, * canupd = NULL;
  ca_dbSource_t *source_hdl;
  ca_updDbSource_t *upd_source_hdl;

  GHashTable *ntfy_hash, *forw_hash, *cross_hash;
  

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


  /* to use EP module */
  EP_Mail_DescrPtr p; 
  EPTokenPtr pt;
  EPTokenPtr list_item;
  EPTokenKeysPtr ptk;

  char * temp_keyid;

  /* a variable to be used to know if the part is pgp_signed or not */
  int pgp_signed = 0;

  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);
  cross_hash = g_hash_table_new(g_str_hash, g_str_equal);
      
  credentials.password_list = NULL;
  credentials.from = NULL;
  int ch;
  char * to_address = NULL;
  char * subject = NULL;
  char * reply_to = NULL;

  AUTO_NIC_hdl_hash = g_hash_table_new(g_str_hash, g_str_equal);       
  
      
              

  while ((ch = getopt(argc, argv, "MtSTf:c:sn")) != -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;
          /* Test mode? In test mode, creation of mntners and as-blocks is possible, without overriding */        
          case 'T':
                  test_mode = 1; 
                  break;
          /* Supress acks and notifications? If yes, the acks and notifs will go to DEFMAIL config var */        
          case 'S': 
                  supress_ack_notif = 1;
                  break;
          /* Print out the ack to stdout? */        
          case 's':
                  print_out_ack = 1;
                  break;
          /* are we processing networkupdate? (invoked via inetd) */        
          case 'n':
                  networkupdate = 1;
                  break;
          case '?':
          default:
                  printf("Unknown option\n"); exit(1);
          }
  }


  /* config stuff */
  /* 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);*/
    ca_init(config_file_name);
  }else{
    /*ca_readConfig("dbupdate.conf", confVars, VARS);*/
    ca_init("dbupdate.conf");
  }

  error_init(argc, argv);


  tmpdir = ca_get_tmpdir;
  tmpdir = g_strstrip(tmpdir);
  lockdir = ca_get_lockdir;
  mailcmd = ca_get_mailcmd;
  mailcmd = g_strstrip(mailcmd);
  notitxt = ca_get_notitxt;
  mailtxt = ca_get_mailtxt; 
  defmail = ca_get_defmail; defmail = UP_remove_EOLs(defmail);
  crosslog = ca_get_crosslog;
  fwtxt = ca_get_fwtxt;
  acksig = ca_get_acksig;
  humailbox = ca_get_humailbox;
  humailbox = g_strstrip(humailbox);
  autobox = ca_get_autobox;
  overridecryptedpw = ca_get_overridecryptedpw;
  overridecryptedpw = g_strstrip(overridecryptedpw);
  acklog = ca_get_acklog;
  notiflog = ca_get_notiflog;
  notimailtxt = ca_get_notimailtxt;  
  forwlog = ca_get_forwlog;
  fwmailtxt = ca_get_fwmailtxt;
  mtfwheader = ca_get_mtfwheader;
  mtfwtxt = ca_get_mtfwtxt;
  country = ca_get_country;
  pgppath = ca_get_pgppath;
  gpgcmd = ca_get_gpgcmd;
  autodbmhelp = ca_get_autodbmhelp;
  cn_subject_add = ca_get_cn_subject_add; cn_subject_add = UP_remove_EOLs(cn_subject_add);
  cn_subject_del = ca_get_cn_subject_del; cn_subject_del = UP_remove_EOLs(cn_subject_del);
  cn_explain_add = ca_get_cn_explain_add;
  cn_explain_del = ca_get_cn_explain_del;
  cn_overlap_add = ca_get_cn_overlap_add;
  cn_overlap_del = ca_get_cn_overlap_del;
  cno_subject_add = ca_get_cno_subject_add; cno_subject_add = UP_remove_EOLs(cno_subject_add); 
  cno_subject_del = ca_get_cno_subject_del; cno_subject_del = UP_remove_EOLs(cno_subject_del);
  cno_explain_add = ca_get_cno_explain_add;
  cno_explain_del = ca_get_cno_explain_del;
  cno_overlap_add = ca_get_cno_overlap_add;
  cno_overlap_del = ca_get_cno_overlap_del;
  mheader = ca_get_mheader;
  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;
  }
  /* retrieve source variables */
  upd_source_hdl = ca_get_UpdSourceHandle(CA_UPDSOURCE);

  if(upd_source_hdl == NULL){
    printf("There must be one updateable source in the config file. Exiting.\n");
    ER_perror(FAC_UP, UP_CONFERR, "There must be one updateable source in"
                                  " the config file. Exiting.");
    exit(1);
  }else{
    if(tracing){
      printf("\nTRACING: The upd_source_hdl is: %s\n", upd_source_hdl->name);
    }
    sources[0] = strdup(upd_source_hdl->name);
    update_host = upd_source_hdl->whoisd_host;
    query_host = strdup(update_host);
    update_port = upd_source_hdl->updPort;
    query_port = upd_source_hdl->qryPort;
    DBhost = upd_source_hdl->updDb.host;
    DBport = upd_source_hdl->updDb.port;
    DBname = upd_source_hdl->updDb.dbName;
    DBuser = upd_source_hdl->updDb.user;
    DBpasswd = upd_source_hdl->updDb.password; 
  }

  

  /* 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]);
      j++;
    }
  }
  countries[j] = NULL; /* mark the end of array */
  
  if(tracing){
    /* print out the config variables for debugging */
    printf("TRACING: countries[%i] = NULL\n", j);
 
    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("AUTOBOX is: [%s]\n", autobox);
    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("UPDATE_HOST is: [%s]\n", update_host);
    printf("UPDATE_PORT is: [%i]\n", update_port);
    printf("QUERY_HOST is: [%s]\n",  query_host);
    printf("QUERY_PORT is: [%i]\n",  query_port);   
    printf("LOCKDIR is: [%s]\n", lockdir); 
    printf("TESTMODE is: [%i]\n", test_mode);
    printf("CNO_SUBJECT_ADD is: [%s]\n", cno_subject_add);
    printf("CNO_SUBJECT_DEL is: [%s]\n", cno_subject_del);
  }
  /* end of config stuff */


  /* set hostname global variable */
  gethostname(hostname, MAXHOSTNAMELEN);

  /* set pid global variable */
  pid = getpid(); 

  /* create the lock file and lock it */
  lockfile = create_lock_file();

    
  /* 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);

  /* initialize credentials.pgp_key_list */
  credentials.pgp_key_list = NULL;


  
 
  if(reading_from_mail){
    if(input_file_name != NULL){
      temp_upd_file = generate_upd_file();
      if(tracing){
        printf("TRACING: temp_upd_file is [%s]\n", temp_upd_file);
      }
      MM_store(input_file_name, temp_upd_file, 0);
      p = EP_ParseMail(input_file_name, tmpdir, pgp_public_key_ring, gpgcmd);
      
    }else{/* input_file_name == NULL */
      temp_upd_file = generate_upd_file();
      MM_store("-", temp_upd_file, 0);
      p = EP_ParseMail(temp_upd_file, tmpdir, pgp_public_key_ring, gpgcmd);

    }

    /* write off the checkpoint file */
    write_checkpoint(1);

    /* the new stuff using the EP module's interface */
    if(tracing){
      printf("\nTRACING: From field is: [%s]\n", p->from->field);
    }

    temp = (char *)malloc(strlen(p->from->field) + strlen("From: ") + 1);
    sprintf(temp, "From: %s", p->from->field);
    /* cut off the '\n's and '\r's at the end of temp */
    UP_remove_EOLs(temp);
    
    credentials.from = temp;
    credentials.from_email = strdup(p->from->field);

    update_mail_sender = strdup(p->from->field); 
    update_mail_sender = UP_remove_EOLs(update_mail_sender);
               
    if(p->subject != NULL && p->subject->field != NULL){
      subject = strdup(p->subject->field);
    }else{
      subject = strdup("");
    }
    
    /* cut off the '\n' and '\r' from the end */
    UP_remove_EOLs(subject);
    
    update_mail_subject = strdup(subject);

    /* parse the subject line */ 
    subject_result = UP_subject_process(update_mail_subject); 


    if(p->reply_to != NULL && p->reply_to->field != NULL){
      reply_to = strdup(p->reply_to->field);
    }else{
      reply_to = strdup("");
    }

    
    /* cut off the '\n' and '\r' from the end */                    
    UP_remove_EOLs(reply_to);

    

    to_address = find_email_address(credentials.from);

    /* if Reply_To was available in the incoming header, then use it */
    if(strlen(reply_to) > 0){
      to_address = (char *)realloc(to_address, strlen(reply_to) + 1);
      to_address = strcpy(to_address, reply_to);
    }

    if(p->message_id != NULL && p->message_id->field != NULL){
      update_mail_ID = strdup(p->message_id->field);
    }else{
      update_mail_ID = strdup("");
    }

    /* cut off the '\n' and '\r' from the end */
    UP_remove_EOLs(update_mail_ID);
    

    if(p->date != NULL && p->date->field != NULL){
      update_mail_date = strdup(p->date->field);
    }else{
      update_mail_date = strdup("");
    }

    /* cut off the '\n' and '\r' from the end  */
    UP_remove_EOLs(update_mail_date);

    
    if(tracing){
      printf("\nEP_ShowTree outputs:\n");
      EP_ShowTree(p->tree);
    }
    
    pt = EP_GetTokens(p->tree, NULL, NULL);

    if(tracing){
      /* Print the list out (debugging) */
      printf("\nEP_PrintTokens outputs:\n");
      EP_PrintTokens(pt);
    }

    /* replace the global variables in mheader */
    mheader_replaced = UP_replace_globals(mheader);
    /* replace the global variables in mailtxt */
    mailtxt_replaced = UP_replace_globals(mailtxt);

    /* If this wasn't only a help request, then we need to process the input */
    if(subject_result.result != UP_SUBJ_HELP_REQ){
      
      /* Print out the header of the ackonwledgement */
      AK_add_to_ack(ack_file_name, "To: %s\n%s\n\nAcknowledgement message from"
        " database software\n\n%s\n", to_address, mheader_replaced, mailtxt_replaced);

      /* ... and now process the items in the list */
      list_item = pt;
      while (list_item != NULL) {
        if(tracing){
          printf("\n\nWill process: %s, MIMEtype: %d\n", list_item->file, list_item->MIMEContentType);
        }
        /* initialize pgp_key_list (XXX This should be a proper freeing of the list) */
        credentials.pgp_key_list = NULL;
        ptk = list_item->keys;
        if(ptk != NULL){
          AK_add_to_ack(ack_file_name, "==== BEGIN PGP SIGNED PART (keyID(s):");
          pgp_signed = 1; 
          while (ptk != NULL) {
            if(tracing){
              printf("TRACING:     key: %.8X, isValid: %i\n", 
                   ptk->keyID, ptk->isValidPGPSignature);
            }
            temp_keyid = (char *)malloc(10);
            sprintf(temp_keyid, "%.8X", ptk->keyID);
            if(tracing){
              printf("TRACING: This key will be added to the list: [%s]\n", temp_keyid);
            }
            AK_add_to_ack(ack_file_name, " %s", temp_keyid);
            credentials.pgp_key_list = g_slist_append (credentials.pgp_key_list, temp_keyid);
            ptk = ptk->next;
            if(ptk != NULL){
              AK_add_to_ack(ack_file_name, ",");
            }else{
              AK_add_to_ack(ack_file_name, ") ====\n");
            }
          }
        }
        process_file(list_item->file, credentials, 
                     AUTO_NIC_hdl_hash, ack_file_name, 
                     ntfy_hash, forw_hash, cross_hash);
        if(pgp_signed){
          AK_add_to_ack(ack_file_name, "==== END PGP SIGNED PART ====\n\n");
          pgp_signed = 0;
        }
        list_item = list_item->next;
      }

    }else{/* this was only a help request (inferred from the "Subject" line of the upd message) */

      /* Print out the header of the acknowledgement */
      AK_add_to_ack(ack_file_name, "To: %s\n%s\n\nHelp file requested so body of message ignored.\n\n\n"
          "============================================================\n\n", 
          to_address, mheader_replaced);

      AK_add_file_to_ack(ack_file_name, autodbmhelp);
      AK_add_to_ack(ack_file_name, "\n============================================================");
       
    }
    
    EP_CleanTokens(pt);

    EP_MailDescrCleanUp(p);

    /* if we have created a temporary file for update, delete it */
    if(temp_upd_file != NULL){

      unlink(temp_upd_file);
      
    }

  }else if(networkupdate){

    /* process networkupdate. Since we use inetd, we just process stdin */
    process_networkupdate(credentials, AUTO_NIC_hdl_hash, ack_file_name, 
                          ntfy_hash, forw_hash, cross_hash); 

  }else{/* not reading from the mail message or from network */
    if(input_file_name != NULL){

      write_checkpoint(1);
      process_file(input_file_name, credentials, 
                  AUTO_NIC_hdl_hash, ack_file_name, 
                  ntfy_hash, forw_hash, cross_hash);
    }else{/* the filename is not given, so we have to write 
             stdin to a temp file, and give it to process_file */
       temp_upd_file = generate_upd_file();
       if(tracing){
         printf("TRACING: main: temp_upd_file=%s\n", temp_upd_file);
       }
       if(( upd_file = fopen(temp_upd_file, "a")) == NULL){
         ER_perror(FAC_UP, UP_CANTOPENW, "Can't open ack file, %s", temp_upd_file);
       }

       while((c = getchar()) != EOF){
         fprintf(upd_file, "%c",c);
       }
       fclose(upd_file);

       write_checkpoint(1);
       process_file(temp_upd_file, credentials, 
                  AUTO_NIC_hdl_hash, ack_file_name, 
                  ntfy_hash, forw_hash, cross_hash);
       unlink(temp_upd_file);
        
    }
      
  }  


  /* send the ack */
  if(reading_from_mail && to_address != NULL){
    AK_send_ack(ack_file_name, to_address, mailcmd);
  }

  /* if our update wasn't a mail update OR we have been asked explicitely
     to print out the ack to the stdout, print it */
  if(!reading_from_mail || print_out_ack){
    AK_print_ack(ack_file_name);
  }
  
  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);


  NT_send_ntfy_list(cross_hash, mailcmd);
  NT_log_ntfy_list(cross_hash, crosslog); 
  NT_delete_ntfy_list(cross_hash);
      
  /* remove the lock file */
  remove_lock_file(lockfile);
 
  /* remove checkpoint file */
  remove_checkpoint();

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


}
