/***************************************
  $Revision: 1.32 $

  DBupdate 

  Status: PARTIALLY REVIEWED, NOT TESTED

  Author(s):       Engin Gunduz

  ******************/ /******************
  Modification History:
        engin (01/03/2000) Created.
		denis (31/08/2001) Modified for new API
  ******************/ /******************
  Copyright (c) 2001,2002                         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 <config.h>
#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_SYS_PARAM_H
#include <sys/param.h>
#endif

extern int  num_sources;

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;

/* sync update flags */
int webupdate = 0;
int help_requested = 0;
int enforced_new = 0;
int origin_string = 0;

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

/* IP of the networkupdate client */
char * netupdclientIP = NULL;

/* two global variables to tally the processed objects */
int count_successful = 0;
int count_unsuccessful = 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;

/* re-direct to humail message */
char *header_type = NULL;
char *text_type = NULL;

/* required configuration variables */
char *tmpdir = NULL;
char *lockdir = NULL;
char *mailcmd = NULL;
char *notitxt = NULL;
char *notihdr = NULL;
char *successtxt = NULL;
char *failuretxt = NULL;
char *helpheader = NULL;
char *notimailtxt = NULL;
char *notinetworktxt = NULL;
char *fwtxt   = NULL;
char *fwhdr   = NULL;
char *fwmailtxt = NULL;
char *mtfwheader = NULL; 
char *mtfwtxt = NULL;
char *mailtxt = NULL;
char *acksig = NULL;
char *defmail = NULL;
char *updlog = NULL;
char *notiflog = NULL;
char *crosslog = NULL;
char *acklog = NULL;
char *forwlog = NULL;
char *humailbox = NULL;
char *autobox = NULL;
char *copyright_notice = NULL;
char *overridecryptedpw = NULL;
char *country = NULL;
char *countries[400];
char *authmethod = NULL;
char *authmethods[20];
char *nicsuffixes[7];
char *current_source = NULL;
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 *allocmnt = NULL;
char *mheader = NULL;
char *DBhost = NULL;
int  DBport;
char *DBuser = NULL;
char *DBname = NULL;
char *DBpasswd = NULL;
ca_updDbSource_t **upd_source_hdl;
/* end of config variables */

char * fingerprint = NULL;
char * keyowner = NULL;

/* 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(rpsl_object_t * obj)
{
  struct ImportKeyObject iKO;
  char * obj_keyID;
  char * key_cert_attr;
  GList * templist;
  GList * certiflist, * next;
  u32 keyID;
  char * key_file_name;
  char ** lines;
  char *value;
  int i;
  FILE * key_file;
  char * temp, * temp2;
  char * error_string;
  rpsl_attr_t *attr;

/*  templist = get_attr_list(obj, "key-cert"); */
   templist = rpsl_object_get_attr(obj, "key-cert");
 attr = (rpsl_attr_t *)(templist->data);
  key_cert_attr = strdup((char *)(attr->value));
/*  key_cert_attr = strdup( (char *)( ((rpsl_attr_t *)(templist->data))->value ) );  */
  rpsl_attr_delete_list(templist);

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

  /* now we must write certif attribute(s) of this key-certif into the key_file_name */
  /* get the certif first */
/*  certiflist = get_attr_list(obj, "certif"); */
  certiflist = rpsl_object_get_attr(obj, "certif");
  if (( key_file = fopen(key_file_name, "w")) == NULL)
  {
     ER_perror(FAC_UP, UP_CANTOPEN, "Can't open temporary file, %s", key_file_name);
     exit(1);
  }
  for ( next = certiflist; next != NULL ; next = g_list_next(next) )
  {
    value = rpsl_attr_get_clean_lines( (rpsl_attr_t *)(next->data) );
	if (value == NULL)
	{ /* if this was an empty attribute, just print an empty line */
      fprintf(key_file, "\n"); 
    }
	else
	{
	  fprintf(key_file, "%s\n", value);
	  free(value);
	}
  
/*    attr = (rpsl_attr_t *)(next->data);
    lines = g_strsplit((char *)(attr->value), "\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);
*/
  }
  fclose(key_file);
  rpsl_attr_delete_list(certiflist);

  strcpy(iKO.iFilename, key_file_name);

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

  /* clean-up: delete the file, free the char *    */
  unlink(key_file_name);
  free(key_file_name);

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





/* 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(rpsl_object_t *object)
{
  char * key_file_name;
  char *value;
  struct ImportKeyObject iKO;
  GList * certiflist, * item, * templist;
  FILE * key_file;
  char keyID[9];
  char * obj_keyID, * key_cert_attr;
  char * error_string = NULL;
   
  key_file_name = (char *)malloc(strlen(tmpdir) + strlen("tmp-key.") + 32);
  sprintf(key_file_name, "%s/tmp-key.%i", tmpdir, pid );

  /* now we must write certif attribute(s) of this key-certif into the key_file_name */
  /* get the certif first */
  certiflist = rpsl_object_get_attr(object, "certif");
  if (( key_file = fopen(key_file_name, "w")) == NULL)
  {
     ER_perror(FAC_UP, UP_CANTOPEN, "Can't open temporary file, %s", key_file_name);
     exit(1);
  }
  for( item = certiflist; item != NULL ; item = g_list_next(item) )
  {
    value = rpsl_attr_get_clean_lines( (rpsl_attr_t *)(item->data) );
	if (value == NULL)
	{ /* if this was an empty attribute, just print an empty line */
      fprintf(key_file, "\n"); 
    }
	else
	{
	  fprintf(key_file, "%s\n", value);
	  free(value);
	}
  }
  fclose(key_file);
  rpsl_attr_delete_list(certiflist);

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

  if (tracing)
  {
    printf("importKeyObj status:\n");
    
    printf("isValid: %d\n", iKO.rc);
    printf("keyID: %08lX\n", (long)(iKO.keyID) );
  }
  snprintf(keyID, 9, "%08lX", iKO.keyID);
  
  if (tracing)
  {
    printf("keyID: [%s]\n", keyID);
  }
  
  templist = rpsl_object_get_attr(object, "key-cert");
  key_cert_attr = rpsl_attr_get_clean_value((rpsl_attr_t *)(templist->data));
  rpsl_attr_delete_list(templist);

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

  if (iKO.rc == iKO_OK && (strcasecmp(obj_keyID, keyID) == 0))
  { /* if PA_ImportKey returned OK 
    and the real keyID is equal to the
    keyID in the 'key-cert' attribute */
    fingerprint = strdup(iKO.fingerPrint); 
    keyowner    = strdup(iKO.keyOwner);

    /* clean-up: delete the file, free the variable */ 
    unlink(key_file_name);
    free(key_file_name);
    
    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_INVALID_ARMOR_HEADER:  error_string = strdup("invalid armor header");break;
        case iKO_MULTIPLE_KEYS:  error_string = strdup("multiple keys in key-cert object");break;
        case iKO_GENERALFAILURE: error_string = strdup("general PGP error");break;
        default:            error_string = strdup("general PGP error");
      }

      /* clean-up: delete the file, free the variable */ 
      unlink(key_file_name);
      free(key_file_name);
      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);
      /* roll-back: we encountered a problem, so we have to remove the key that
         we've added */
      PA_RemoveKey(&iKO);

      /* clean-up: delete the file, free the variable */ 
      unlink(key_file_name);
      free(key_file_name);

	    free(obj_keyID);
      return error_string;
    }
  }
}



/* Gets the keyowner and fingerprint of a PGP key 
   from the keycert object. The keycert object must already
   be in the database (thus, in the keyring) 
   The fingerprint and keyowner will be saved in global variables */
   
void get_keyowner_fingerpr(rpsl_object_t *obj)
{
  GList * templist = NULL;
  char * obj_keyID = NULL;
  struct ImportKeyObject iKO;
  u32 keyID;
  char * key_cert_value;
   
  
  templist = rpsl_object_get_attr(obj, "key-cert");
  key_cert_value = rpsl_attr_get_clean_value((rpsl_attr_t *)(templist->data));
  rpsl_attr_delete_list(templist);

  if (tracing)
  {
    printf("get_keyowner_fingerpr: key_cert_attr is [%s]\n", key_cert_value);
  } 
  
  obj_keyID = strdup(key_cert_value + strlen("PGPKEY-"));
  if (tracing)
  {
    printf("get_keyowner_fingerpr: obj_keyID is [%s]\n", obj_keyID);
  }
 
  sscanf(obj_keyID, "%8X", &keyID); 
  if (tracing)
  {
    printf("get_keyowner_fingerpr: keyID is [%u]\n", keyID);
 }
  
  /* set appropriate fields of iKO */ 
  iKO.keyID = keyID;
  strcpy(iKO.keyRing, pgp_public_key_ring);

  GetFingerPrint(&iKO);
  fingerprint = strdup(iKO.fingerPrint);
  
  GetKeyOwner(&iKO);
  keyowner = strdup(iKO.keyOwner); 

  free(key_cert_value);
  free(obj_keyID);

  if(tracing)
  {
    if (fingerprint != NULL)
	{
      printf("get_keyowner_fingerpr: fingerprint is [%s]\n", fingerprint);
    }
    if (keyowner)
	{
      printf("get_keyowner_fingerpr: keyowner is [%s]\n", keyowner);
    }
  }
}



/* Adds an information message to the ack message if a deprecated MAIL-FROM was found in
   any of the mntner objects checked by the check_auth routine. */
void deprecated_mail_from(char * ack_file_name, int result)
{
  /* auth passed OK but a MAIL-FROM was still found */

  /* an early idea that was not implemented - so do nothing */
  
  /* remove completely during re-structuring */
}


/* writes the object and any attribute and object level error
   messages to the ack file.
*/

void write_errors_to_ack(rpsl_object_t *object, char *arg, char * ack_file_name, char *errmsg)
{
    const GList * attr_list = NULL;
    const GList * attr_list_item = NULL;
    const GList * error_list = NULL;
    const GList * error_list_item = NULL;
    const GList * attr_error_list = NULL;
    const GList * attr_error_list_item = NULL;
    char * arg2;
	const char * value = NULL;
	const char * name = NULL;
	char * attribute = NULL;

	/* syntax error AND not deletion */
/*
	noclass = 0;
	for ( error_list_item = error_list; error_list_item != NULL; error_list_item = g_list_next(error_list_item) )
	{
	  ecode = ((rpsl_error_t *)(error_list_item->data))->code;
      if ( ecode == RPSL_ERR_ONLYCOMMENTS || ecode == RPSL_ERR_BADCLASS
		    || ecode == RPSL_ERR_BADCLASS )
	  {
		noclass = 1;
		break;
	  }
	}
*/
    if ( UP_remove_override_attr(object) )
    {
      arg2 = rpsl_object_get_text(object,0);
      if ( ! arg2 )
      {
        /* if we are processing garbage with a : in the first line, arg2 may be null */
	    arg2 = (char *)malloc(2);
	    strcpy(arg2, "");	/* Don't include object in ack/notif msgs */
      }
    }
	else
	{
	  /* there was an override attr in this object and it has not been removed */
	  arg2 = (char *)malloc(2);
	  strcpy(arg2, "");	/* Don't include object in ack/notif msgs */
	}

/*		 if ( ! noclass )
	{
      type = rpsl_object_get_class(object);
	  key = get_search_key(object, type);
      AK_add_to_ack(ack_file_name, "\nUpdate FAILED: [%s] %s \n***Error: Syntax error in object\n",
                         type, key ? key : "" );
	}
	else 
*/
      AK_add_to_ack(ack_file_name, "\nUpdate FAILED: Syntax error in object\n");

    if ( rpsl_object_has_error(object, RPSL_ERRLVL_CRIT) )
	{
	  /* object not parsed so print out text version of object rather than attributes */
	  AK_add_to_ack(ack_file_name, "%s\n", arg2);
    }
    else
	{
	  /* add attributes along with any attribute error messages */
	  attr_list = rpsl_object_get_all_attr(object);
	  for ( attr_list_item = attr_list; attr_list_item != NULL; attr_list_item = g_list_next(attr_list_item) )
	  {
		name = rpsl_attr_get_name( (rpsl_attr_t *)(attr_list_item->data) );
		if ( strcasecmp(name, "override") != 0 )
		{
		  /* don't include the override attibute, but still include override error messages */
		  value = rpsl_attr_get_value( (rpsl_attr_t *)(attr_list_item->data) );
		  attribute = (char *)malloc( strlen(name) + strlen(value) + 3);
		  sprintf(attribute, "%s: %s", name, value);
          AK_add_to_ack(ack_file_name, "%s\n", attribute);
		  free(attribute);
		}
		attr_error_list = rpsl_attr_errors( (rpsl_attr_t *)(attr_list_item->data) );
		for ( attr_error_list_item = attr_error_list; attr_error_list_item != NULL; attr_error_list_item = g_list_next(attr_error_list_item) )
		{
		  if ( ((rpsl_error_t *)(attr_error_list_item->data))->level > RPSL_ERRLVL_DEBUG )
            AK_add_to_ack(ack_file_name, "***Error: %s\n", ((rpsl_error_t *)(attr_error_list_item->data))->descr);
		}
	  }
    }

    /* now add any object level error messages */
	error_list = rpsl_object_errors(object);
	for ( error_list_item = error_list; error_list_item != NULL; error_list_item = g_list_next(error_list_item) )
	{
	  if ( ((rpsl_error_t *)(error_list_item->data))->level > RPSL_ERRLVL_DEBUG &&
		    ((rpsl_error_t *)(error_list_item->data))->code != RPSL_ERR_BADATTR )
	  {
        AK_add_to_ack(ack_file_name, "***Error: %s\n", ((rpsl_error_t *)(error_list_item->data))->descr);
	  }
	}
    
    if ( errmsg )
      AK_add_to_ack(ack_file_name, "\n%s\n", errmsg);

    AK_add_to_ack(ack_file_name, "\n");
    free(arg2);
}


/* sets up the current source data from the first entry in the
   list of config data for multiple sources.
   Needed for networkupdate.
*/

void set_default_source_data()
{
  if (tracing)
  {
    printf("TRACING: set_default_source_data running\n");
  }
  
  if ( current_source )
    free(current_source);
  current_source = strdup(upd_source_hdl[0]->name);
  update_host = upd_source_hdl[0]->whoisd_host;
  if ( query_host )
    free(query_host);
  query_host = strdup(update_host);
  update_port = upd_source_hdl[0]->updPort;
  query_port = upd_source_hdl[0]->qryPort;
  DBhost = upd_source_hdl[0]->updDb.host;
  DBport = upd_source_hdl[0]->updDb.port;
  DBname = upd_source_hdl[0]->updDb.dbName;
  DBuser = upd_source_hdl[0]->updDb.user;
  DBpasswd = upd_source_hdl[0]->updDb.password; 

  if (tracing)
  {
    /* print out the config variables for debugging */
    printf("CURRENT SOURCE is: [%s]\n", current_source);
    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("DBhost is: [%s]\n", DBhost);
    printf("DBport is: [%i]\n",  DBport);   
    printf("DBname is: [%s]\n", DBname);
    printf("DBuser is: [%s]\n", DBuser);
    printf("DBpasswd is: [%s]\n", DBpasswd);
  }
}


/* takes the source string from an object and sets up the current source
   data from the list of config data for multiple sources 
   
   returns 1 for success and 0 for source not found */

int set_current_source_data(char *obj_source)
{
  int i;
  int found = 0;

  if (tracing)
  {
    printf("TRACING: set_current_source_data running\n");
  }
  
  for ( i=0; i<num_sources; i++ )
  {
    if ( ! strcasecmp( obj_source, upd_source_hdl[i]->name) )
    {
      /* found the source in the list */
      found = 1;
      
      if ( current_source )
        free(current_source);
      current_source = strdup(upd_source_hdl[i]->name);
      update_host = upd_source_hdl[i]->whoisd_host;
      if ( query_host )
        free(query_host);
      query_host = strdup(update_host);
      update_port = upd_source_hdl[i]->updPort;
      query_port = upd_source_hdl[i]->qryPort;
      DBhost = upd_source_hdl[i]->updDb.host;
      DBport = upd_source_hdl[i]->updDb.port;
      DBname = upd_source_hdl[i]->updDb.dbName;
      DBuser = upd_source_hdl[i]->updDb.user;
      DBpasswd = upd_source_hdl[i]->updDb.password; 

      if (tracing)
      {
        /* print out the config variables for debugging */
        printf("CURRENT SOURCE is: [%s]\n", current_source);
        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("DBhost is: [%s]\n", DBhost);
        printf("DBport is: [%i]\n",  DBport);   
        printf("DBname is: [%s]\n", DBname);
        printf("DBuser is: [%s]\n", DBuser);
        printf("DBpasswd is: [%s]\n", DBpasswd);
      }
      
      break;
    }
  }

  if ( ! found  && tracing )
  {
    printf("TRACING: invalid source [%s]\n", obj_source);
  }
  
  return found;
}



/* 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.
   Also now determines which source to update for each object.
  
     Arguments:
        char * incoming: 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 
        GHashTable * ntfy_hash, 
        GHashTable * forw_hash, 
        GHashTable * cross_hash: hashes that contain files that have notifications. 
*/



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)
{
    rpsl_object_t *object = NULL;
    rpsl_object_t *old_obj = NULL;
    rpsl_object_t *external_syntax_obj = NULL;
    rpsl_object_t *obj_with_AUTO_NIC_hdl = NULL;
    rpsl_object_t *formatted_object = NULL;
    rpsl_object_t *generated_obj = NULL;
    const GList * error_list = NULL;
    const GList * error_list_item = NULL;
    const GList * attr_list = NULL;
    const GList * attr_list_item = NULL;
    const GList * attr_error_list = NULL;
    const GList * attr_error_list_item = NULL;
    char * old_version = NULL;
    int result = 0;
    char * result_from_import_key = NULL;
    char * result_from_delete_key = NULL;
    char * auto_nic = NULL;
    char * changed_obj_str = NULL;
	char *obj_with_AUTO_NIC_hdl_str;
    char * assigned_NIC;
    char * formatted_object_str;
    const char * type;
	char *lctype;
    char * arg;
    char * arg2;
    char * generated_obj_str;
	const char * value = NULL;
	const char * name = NULL;
	char * attribute = NULL;
	char *key;
    char *obj_source = NULL;
    up_ripupd_result_struct * result_from_RIPupd;
    external_syntax_struct * external_syntax_results;
	gint elevel, ecode;
	gchar *edescr = NULL;

    arg = strdup(incoming);
	
	object = rpsl_object_init(arg);
	error_list = rpsl_object_errors(object);
	if (tracing && error_list)
	{
	  printf("TRACING: errors found on initial object parse\n");
	  for ( error_list_item = error_list; error_list_item != NULL; error_list_item = g_list_next(error_list_item) )
	  {
	    elevel = ((rpsl_error_t *)(error_list_item->data))->level;
		ecode = ((rpsl_error_t *)(error_list_item->data))->code;
		edescr = strdup(((rpsl_error_t *)(error_list_item->data))->descr);
		
		printf("TRACING: level [%d]  code [%d]  [%s]\n", elevel, ecode, edescr);
		free(edescr);
	  }
	}
	if ( error_list )
	{
	  if ( ((rpsl_error_t *)(error_list->data))->code == RPSL_ERR_ONLYCOMMENTS )
	    return UP_NOOBJECT;
	}
    
    /* find source from object and set current source data */
    attr_list = rpsl_object_get_attr(object, "source");
	if ( attr_list )
	{
      obj_source = rpsl_attr_get_clean_value((rpsl_attr_t *)(attr_list->data));
      result = set_current_source_data(obj_source);
      if ( ! result )
      {
        /* source in object not recognised */
        write_errors_to_ack(object, arg, ack_file_name, "***Error: No such source");
        rpsl_object_delete(object);
        free(arg);
        return UP_INT;  /* need a new return code here for object source not found in config list */
/*        return UP_SNF;   */
      }
    }
    else
    {
      /* should only happen if the object has no source: attribute */
      write_errors_to_ack(object, arg, ack_file_name, NULL);
      rpsl_object_delete(object);
      free(arg);
      return UP_SYN;
    }

    if ( !rpsl_object_has_error(object, RPSL_ERRLVL_ERROR) 
			&& has_ref_to_AUTO_nic_hdl(object) )
	{  /* parsed with no systax errors */
	  if (tracing)
	  { 
        printf("TRACING: the object has ref to AUTO nic-hdl\n");
	  }

	  /* 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_str = replace_refs_to_AUTO_NIC_hdl(object, NIC_hdl_hash, arg)) == NULL)
	  {
		type = rpsl_object_get_class(object);

		free(arg);
    	if ( UP_remove_override_attr(object) )
        {
    	  arg = rpsl_object_get_text(object,0);
          if ( ! arg )
          {
            /* if we are processing garbage with a : in the first line, arg will be null */
	        arg = (char *)malloc(2);
	        strcpy(arg, "");	/* Don't include object in ack/notif msgs */
          }
        }
		else
		{
		  /* there was an override attr in this object and it has not been removed */
		  arg = (char *)malloc(2);
		  strcpy(arg, "");	/* Don't include object in ack/notif msgs */
		}

        AK_add_to_ack(ack_file_name, "\nUpdate FAILED: [%s] Unknown AUTO NIC handle referenced\n%s\n", 
                type ? type : "unknown-type", arg);

        rpsl_object_delete(object);
        free(arg); 
        return UP_ANE; /* AUTO NIC hdl error */
      }
	  else
	  { /* in this case, we must use changed_obj_str instead of arg */

        free(arg); 
        arg = changed_obj_str; 
      };
    }
  
    
    if ( !rpsl_object_has_error(object, RPSL_ERRLVL_ERROR) )
	{
	  /* parsed with no systax errors */
    
      /* find source from object and set current source data */
      attr_list = rpsl_object_get_attr(object, "source");
	  if ( attr_list )
	  {
        obj_source = rpsl_attr_get_clean_value((rpsl_attr_t *)(attr_list->data));
        result = set_current_source_data(obj_source);
        if ( ! result )
        {
          write_errors_to_ack(object, arg, ack_file_name, "***Error: No such source");
          rpsl_object_delete(object);
          free(arg);
          return UP_INT;  /* need a new return code here for object source not found in config list */
  /*        return UP_SNF;   */
        }
      }
      else
      {
        /* should only happen if the object has no source: attribute */
        write_errors_to_ack(object, arg, ack_file_name, NULL);
        rpsl_object_delete(object);
        free(arg);
        return UP_SYN;
      }

      type = rpsl_object_get_class(object);
      /* is the object to be deleted? */
      if ( rpsl_object_is_deleted(object) )
	  {
        old_version = get_old_version(object, arg);

        if(old_version == NULL)
		{  /* the object doesn't exist in the db! */

		  if (tracing)
		  { 
    		printf("TRACING: the object doesn't exist in the db\n");
		  }

		  free(arg);
    	  if ( UP_remove_override_attr(object) )
          {
    		arg = rpsl_object_get_text(object,0);
            if ( ! arg )
            {
              /* if we are processing garbage with a : in the first line, arg will be null */
	          arg = (char *)malloc(2);
	          strcpy(arg, "");	/* Don't include object in ack/notif msgs */
            }
          }
		  else
		  {
			/* there was an override attr in this object and it has not been removed */
			arg = (char *)malloc(2);
			strcpy(arg, "");	/* Don't include object in ack/notif msgs */
		  }

          AK_add_to_ack(ack_file_name, "\nDelete FAILED: [%s] %s\n%s\n***Error: Entry not found\n\n",
                        type, get_search_key(object, type), arg);

          rpsl_object_delete(object);
          free(arg); 
          return UP_NSO; /* no such object */
        }
		else 
		{  /* the object is in the db */

		  if (tracing)
		  { 
    		printf("TRACING: the object is in the db\n");
		  }

		  lctype = strdup(type);
		  g_strdown(lctype);
          if (    identical(old_version, object)      /* if the old & new versions are identical */
               || (strcmp(lctype, "key-cert") == 0)   /* or it is a key-cert object  */
               || (strcmp(lctype, "inet6num") == 0) ) /* or it is an inet6num object */
		  {  
            result = check_auth(NULL, object, type, credentials);
            if(result == UP_AUTH_OK || result == UP_OKM)
			{ 
              if (tracing)
			  {
                printf("TRACING: Will send the obj to be deleted\n");
              }
              if(strcmp(type, "key-cert") == 0)
			  {
                result_from_delete_key = delete_key(object);
              }
			  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(object, NULL, "DEL");
                if (result_from_RIPupd->result == 0)
				{
                  AK_add_to_ack(ack_file_name, "\nDelete OK: [%s] %s\n", 
                                type, get_search_key(object, type));
                  deprecated_mail_from( ack_file_name, result );
                  NT_write_all_ntfs(old_version, NULL, NULL, tmpdir, ntfy_hash, forw_hash, cross_hash, 
                                    credentials.from);

                  rpsl_object_delete(object);
				  free(result_from_RIPupd);
                  free(arg); 
		          free(old_version);
                  return UP_OK;
                }
				else
				{
                  AK_add_to_ack(ack_file_name, "\nDelete FAILED: [%s] %s\n%s\n",
                                type, get_search_key(object, type),
                                result_from_RIPupd->error_str);
                  deprecated_mail_from( ack_file_name, result );

                  rpsl_object_delete(object);
				  free(result_from_RIPupd->error_str);
				  free(result_from_RIPupd);
                  free(arg); 
		          free(old_version);
                  return UP_INT;
                }
              }
			  else
			  {
                 AK_add_to_ack(ack_file_name, "\nDelete FAILED: [%s] %s\n%s\n",
                                type, get_search_key(object, type),
                                result_from_delete_key);
                 deprecated_mail_from( ack_file_name, result );

                 rpsl_object_delete(object);
                 free(arg); 
		         free(old_version);
                 free(result_from_delete_key); 
                 return UP_INT;
              }
            }
			else
			{ /* auth failed */
              if (tracing) 
			  {
                printf("TRACING: Auth failed\n");
              }

		      free(arg);
    		  if ( UP_remove_override_attr(object) )
              {
    			arg = rpsl_object_get_text(object,0);
                if ( ! arg )
                {
                  /* if we are processing garbage with a : in the first line, arg may be null */
	              arg = (char *)malloc(2);
	              strcpy(arg, "");	/* Don't include object in ack/notif msgs */
                }
              }
			  else
			  {
				/* there was an override attr in this object and it has not been removed */
				arg = (char *)malloc(2);
				strcpy(arg, "");	/* Don't include object in ack/notif msgs */
			  }

              AK_add_to_ack(ack_file_name, "\nDelete FAILED: [%s] %s\nAuthorisation failed, request forwarded to maintainer.\n%s\n",
                            type, get_search_key(object, type), arg);
              deprecated_mail_from( ack_file_name, result );
              NT_write_all_frwds(old_version, NULL, tmpdir, ntfy_hash, forw_hash, cross_hash, credentials.from);

              rpsl_object_delete(object);
              free(arg); 
		      free(old_version);
              return UP_AUF; /* Auth failed */
            } 
          }  /* end of identical match */
		  else
		  {  /* the new & old versions do not match */
		    free(arg);
    		if ( UP_remove_override_attr(object) )
            {
    		  arg = rpsl_object_get_text(object,0);
              if ( ! arg )
              {
                /* if we are processing garbage with a : in the first line, arg may be null */
	            arg = (char *)malloc(2);
	            strcpy(arg, "");	/* Don't include object in ack/notif msgs */
              }
            }
			else
			{
			  /* there was an override attr in this object and it has not been removed */
			  arg = (char *)malloc(2);
			  strcpy(arg, "");	/* Don't include object in ack/notif msgs */
			}

            AK_add_to_ack(ack_file_name, "\nDelete FAILED: [%s] %s\n%s\n***Error: new & old versions do not match\n",
                 type, get_search_key(object, type), arg);

            rpsl_object_delete(object);
            free(arg); 
		    free(old_version);
            return UP_NOM; /* new & old versions do not match */
          }
        }  /* end of the object is in the db */
      } /* end of the object is to be deleted */
	  else 
	  { /* the object is _NOT_ to be deleted */
        
        if (has_AUTO_NIC_hdl(object))
		{ /* if the object has an AUTO NIC hdl */
 		  external_syntax_obj = rpsl_object_copy(object);
          external_syntax_results = UP_check_external_syntax(external_syntax_obj);
          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(external_syntax_obj, NULL, type, credentials);
            if (result == UP_AUTH_OK || result == UP_OKM)
			{
              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_obj, auto_nic);
              if (tracing)
			  {  
			    obj_with_AUTO_NIC_hdl_str = rpsl_object_get_text(obj_with_AUTO_NIC_hdl,0);
                printf("TRACING:  Called replace_AUTO_NIC_hdl, get [%s]\n", obj_with_AUTO_NIC_hdl_str);
                printf("TRACING: Will send the obj to be added\n");
				free(obj_with_AUTO_NIC_hdl_str);
				obj_with_AUTO_NIC_hdl_str = NULL;
              }
              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->result == 0)
			  {
                AK_add_to_ack(ack_file_name, "\nNew OK: [%s] %s\n", type, assigned_NIC);
                deprecated_mail_from( ack_file_name, result );

                /* replace the AUTO nic hdl with the assigned one (for reporting purposes, in the notif mesg) */
                formatted_object = UP_put_assigned_NIC(external_syntax_obj, assigned_NIC);

                formatted_object_str = rpsl_object_get_text(formatted_object,0);
                NT_write_all_ntfs(NULL, arg, formatted_object_str, tmpdir, ntfy_hash, forw_hash, cross_hash, 
                                  credentials.from);
                
                result_from_RIPupd->result = 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));
                  }
                }
                
				rpsl_object_delete(formatted_object);
				rpsl_object_delete(obj_with_AUTO_NIC_hdl);
				rpsl_object_delete(external_syntax_obj);
                rpsl_object_delete(object);
				free(result_from_RIPupd);
				free(formatted_object_str);
				free(external_syntax_results->new_obj);
/*				free(external_syntax_results->error_str);*/
/*				free(external_syntax_results->warning_str);*/
				free(external_syntax_results);
                free(arg); 
                return UP_OK;
              }
			  else
			  {
    			if ( UP_remove_override_attr(object) )
                {
    			  arg2 = rpsl_object_get_text(object,0);
                  if ( ! arg2 )
                  {
                    /* if we are processing garbage with a : in the first line, arg2 may be null */
	                arg2 = (char *)malloc(2);
	                strcpy(arg2, "");	/* Don't include object in ack/notif msgs */
                  }
                }
				else
				{
				  /* there was an override attr in this object and it has not been removed */
				  arg2 = (char *)malloc(2);
				  strcpy(arg2, "");	/* Don't include object in ack/notif msgs */
				}

                AK_add_to_ack(ack_file_name, "\nNew FAILED: [%s]\n%s\n%s\n",
                              type, arg2, result_from_RIPupd->error_str);
                deprecated_mail_from( ack_file_name, result );

				rpsl_object_delete(obj_with_AUTO_NIC_hdl);
				rpsl_object_delete(external_syntax_obj);
                rpsl_object_delete(object);
				free(result_from_RIPupd);
				free(external_syntax_results->new_obj);
				free(external_syntax_results);
				free(arg2);
                free(arg); 
                return UP_INT;
              }
            }
			else
			{
              /* auth failed ! */
              if (tracing)
			  {
                printf("TRACING: Auth failed\n");
              }
  
    		  if ( UP_remove_override_attr(object) )
              {
    			arg2 = rpsl_object_get_text(object,0);
                if ( ! arg2 )
                {
                  /* if we are processing garbage with a : in the first line, arg2 may be null */
	              arg2= (char *)malloc(2);
	              strcpy(arg2, "");	/* Don't include object in ack/notif msgs */
                }
              }
			  else
			  {
				/* there was an override attr in this object and it has not been removed */
				arg2 = (char *)malloc(2);
				strcpy(arg2, "");	/* Don't include object in ack/notif msgs */
			  }

              AK_add_to_ack(ack_file_name, "\nNew FAILED: [%s] %s\nAuthorisation failed, request forwarded to maintainer.\n%s\n",
                            type, get_search_key(object, type), arg2);
              deprecated_mail_from( ack_file_name, result );
              NT_write_all_frwds(NULL, arg, tmpdir, ntfy_hash, forw_hash, cross_hash, credentials.from);

		      rpsl_object_delete(external_syntax_obj);
              rpsl_object_delete(object);
			  free(external_syntax_results->new_obj);
		      free(external_syntax_results);
		      free(arg2);
              free(arg); 
              return UP_AUF; /* Auth failed */
            }
          }
		  else
		  { /* external syntax check failed */
    		if ( UP_remove_override_attr(object) )
            {
    		  arg2 = rpsl_object_get_text(object,0);
                if ( ! arg2 )
                {
                  /* if we are processing garbage with a : in the first line, arg2 may be null */
	              arg2= (char *)malloc(2);
	              strcpy(arg2, "");	/* Don't include object in ack/notif msgs */
                }
              }
			else
			{
			  /* there was an override attr in this object and it has not been removed */
			  arg2 = (char *)malloc(2);
			  strcpy(arg2, "");	/* Don't include object in ack/notif msgs */
			}

            AK_add_to_ack(ack_file_name, "\nUpdate FAILED: [%s] %s\n%s%s\n",
                   type, get_search_key(object, type), 
                   arg2, external_syntax_results->error_str);

		    rpsl_object_delete(external_syntax_obj);
            rpsl_object_delete(object);
			free(external_syntax_results->new_obj);
			free(external_syntax_results);
		    free(arg2);
            free(arg); 
            return UP_SYN;
          }
        }  /* end of the object has an AUTO NIC hdl */
        else
		{ /* process object without an AUTO NIC hdl */
          old_version = get_old_version(object, arg);
          if (old_version != NULL)
		  { 
		    /* so, this is an update operation */
            if (tracing)
              printf("TRACING: this is an UPDATE\n");

            if ( (!reading_from_mail && !webupdate) || 
                (subject_result.result != UP_SUBJ_NEW_ENFORCED))
			{ 
			  /* If the user didn't enforce creation in the subject line */
		      external_syntax_obj = rpsl_object_copy(object);
              external_syntax_results = UP_check_external_syntax(external_syntax_obj);
              if (    external_syntax_results->result != UP_EXTSYN_ERR 
                   && external_syntax_results->result != UP_EXTSYN_ERR_WARN)
			  { 
			    /* if there is no error */
                if ( identical(old_version, external_syntax_obj) != 1 )
				{
                  /* if the old version & the new one are not identical */ 
	              old_obj = rpsl_object_init(old_version);
	              error_list = rpsl_object_errors(old_obj);
	              /***************************** DO SOME ERROR CHECKING / REPORTING HERE ***************/

                  result = check_auth(object, old_obj, type, credentials);    
                  if (result == UP_AUTH_OK || result == UP_OKM)
				  {
                    if (tracing)
					{                                
                      printf("TRACING: Will send the obj to be updated\n");
                    }

                    generated_obj = rpsl_object_copy(external_syntax_obj);
                    if (strcasecmp(type, "key-cert") == 0)
					{
                      get_keyowner_fingerpr(object);/* get keyowner and fingerprint, 
                                                       save them into global vars  */
                      generated_obj_str = UP_generate_kc_attrs(generated_obj);
				      free(external_syntax_results->new_obj);
                      external_syntax_results->new_obj = generated_obj_str;
                    }
                   
                    result_from_RIPupd = send_object_db(generated_obj, NULL, "UPD");

    				if ( UP_remove_override_attr(object) )
                    {
    				  arg2 = rpsl_object_get_text(object,0);
                      if ( ! arg2 )
                      {
                        /* if we are processing garbage with a : in the first line, arg2 may be null */
	                    arg2= (char *)malloc(2);
	                    strcpy(arg2, "");	/* Don't include object in ack/notif msgs */
                      }
                    }
					else
					{
					  /* there was an override attr in this object and it has not been removed */
					  arg2 = (char *)malloc(2);
					  strcpy(arg2, "");	/* Don't include object in ack/notif msgs */
					}

                    if (result_from_RIPupd->result == 0)
					{
                      AK_add_to_ack(ack_file_name, "\nUpdate OK: [%s] %s\n",
                                    type, get_search_key(object, type));
                      deprecated_mail_from( ack_file_name, result );

                      NT_write_all_ntfs(old_version, arg, external_syntax_results->new_obj, tmpdir, ntfy_hash, forw_hash, cross_hash, 
                                        credentials.from);

				      rpsl_object_delete(generated_obj);
				      rpsl_object_delete(old_obj);
				      rpsl_object_delete(external_syntax_obj);
                      rpsl_object_delete(object);
				      free(result_from_RIPupd);
					  free(old_version);
				      free(external_syntax_results->new_obj);
					  free(external_syntax_results);
				      free(arg2);
                      free(arg); 
                      return UP_OK;
                    }
					else
					{
                      AK_add_to_ack(ack_file_name, "\nUpdate FAILED: [%s] %s\n%s\n%s\n",
                                    type, get_search_key(object, type),
                                    arg2, result_from_RIPupd->error_str);
                      deprecated_mail_from( ack_file_name, result );

				      rpsl_object_delete(generated_obj);
				      rpsl_object_delete(old_obj);
				      rpsl_object_delete(external_syntax_obj);
                      rpsl_object_delete(object);
				      free(result_from_RIPupd->error_str);
				      free(result_from_RIPupd);
					  free(old_version);
				      free(external_syntax_results->new_obj);
					  free(external_syntax_results);
				      free(arg2);
                      free(arg); 
                      return UP_INT;
                    }
                  }
				  else if (result == UP_NAM)
				  {
                    /* name of a person/role object cannot be changed */
                    if (tracing)
					{
                      printf("TRACING: name of a person/role object cannot be changed\n");
                    }
                    
    				if ( UP_remove_override_attr(object) )
                    {
    				  arg2 = rpsl_object_get_text(object,0);
                      if ( ! arg2 )
                      {
                        /* if we are processing garbage with a : in the first line, arg2 may be null */
	                    arg2= (char *)malloc(2);
	                    strcpy(arg2, "");	/* Don't include object in ack/notif msgs */
                      }
                    }
					else
					{
					  /* there was an override attr in this object and it has not been removed */
					  arg2 = (char *)malloc(2);
					  strcpy(arg2, "");	/* Don't include object in ack/notif msgs */
					}

                    AK_add_to_ack(ack_file_name, "\nUpdate FAILED: [%s] %s\n%s\n***Error: Name of a person or role object cannot be changed.\n",
                                  type, get_search_key(object, type), arg2);

				    rpsl_object_delete(old_obj);
				    rpsl_object_delete(external_syntax_obj);
                    rpsl_object_delete(object);
					free(old_version);
				    free(external_syntax_results->new_obj);
					free(external_syntax_results);
				    free(arg2);
                    free(arg); 
                    return UP_NAM; /* name of a person/role object cannot be changed */


                  }
                  else
				  {
                    /* auth failed ! */
                    if (tracing)
					{
                      printf("TRACING: Auth failed\n");
                    }
      
    				if ( UP_remove_override_attr(object) )
                    {
    				  arg2 = rpsl_object_get_text(object,0);
                      if ( ! arg2 )
                      {
                        /* if we are processing garbage with a : in the first line, arg2 may be null */
	                    arg2= (char *)malloc(2);
	                    strcpy(arg2, "");	/* Don't include object in ack/notif msgs */
                      }
                    }
					else
					{
					  /* there was an override attr in this object and it has not been removed */
					  arg2 = (char *)malloc(2);
					  strcpy(arg2, "");	/* Don't include object in ack/notif msgs */
					}

                    /* we can now have a hierarchical authorisation failure in an update
                       if an inet6num status is set to ASSIGNED when there are more specific objects */
                    if (result == UP_HOF)
                      AK_add_to_ack(ack_file_name, "\nUpdate FAILED: [%s] %s\nHierarchical Authorisation failed, request forwarded to maintainer.\n%s\n",
                                  type, get_search_key(object, type), arg2);
                    else
                      AK_add_to_ack(ack_file_name, "\nUpdate FAILED: [%s] %s\nAuthorisation failed, request forwarded to maintainer.\n%s\n",
                                  type, get_search_key(object, type), arg2);
                    deprecated_mail_from( ack_file_name, result );

                    NT_write_all_frwds(old_version, arg, tmpdir, ntfy_hash, forw_hash, cross_hash, credentials.from);

				    rpsl_object_delete(old_obj);
				    rpsl_object_delete(external_syntax_obj);
                    rpsl_object_delete(object);
					free(old_version);
				    free(external_syntax_results->new_obj);
					free(external_syntax_results);
				    free(arg2);
                    free(arg); 
                    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",
                                    type, get_search_key(object, type));

				  rpsl_object_delete(external_syntax_obj);
                  rpsl_object_delete(object);
			      free(old_version);
				  free(external_syntax_results->new_obj);
			      free(external_syntax_results);
                  free(arg); 
                  return UP_OK;
                }
              }
			  else
			  { /* if there is an error in external syntax checks */
    			if ( UP_remove_override_attr(object) )
                {
                  arg2 = rpsl_object_get_text(object,0);
                  if ( ! arg2 )
                  {
                    /* if we are processing garbage with a : in the first line, arg2 may be null */
	                arg2= (char *)malloc(2);
	                strcpy(arg2, "");	/* Don't include object in ack/notif msgs */
                  }
                }
				else
				{
				  /* there was an override attr in this object and it has not been removed */
				  arg2 = (char *)malloc(2);
				  strcpy(arg2, "");	/* Don't include object in ack/notif msgs */
				}

                AK_add_to_ack(ack_file_name, "\nUpdate FAILED: [%s] %s\n%s%s\n",
                     type, get_search_key(object, type), 
                     arg2, external_syntax_results->error_str);

				rpsl_object_delete(external_syntax_obj);
                rpsl_object_delete(object);
			    free(old_version);
				free(external_syntax_results->new_obj);
			    free(external_syntax_results);
                free(arg2); 
                free(arg); 
                return UP_SYN;
              }
            }
			else
			{ /* if the user enforced creation (using NEW keyword) in the subject line */
    		  if ( UP_remove_override_attr(object) )
              {
    			arg2 = rpsl_object_get_text(object,0);
                if ( ! arg2 )
                {
                  /* if we are processing garbage with a : in the first line, arg2 may be null */
	              arg2= (char *)malloc(2);
	              strcpy(arg2, "");	/* Don't include object in ack/notif msgs */
                }
              }
			  else
			  {
				/* there was an override attr in this object and it has not been removed */
				arg2 = (char *)malloc(2);
				strcpy(arg2, "");	/* Don't include object in ack/notif msgs */
			  }

              AK_add_to_ack(ack_file_name, "\nUpdate FAILED: [%s] %s\n%s\n"
                   "***Error:  Object already exists\n",
                   type, get_search_key(object, type), arg2),

              rpsl_object_delete(object);
			  free(old_version);
              free(arg2); 
              free(arg); 
              return UP_INT;
              
            }
          }  /* end of this is an update operation */
		  else
		  { /* old_version  == NULL, so, creation */
            if (tracing)
              printf("TRACING: this is a CREATION\n");

		    external_syntax_obj = rpsl_object_copy(object);
            external_syntax_results = UP_check_external_syntax(external_syntax_obj);
            if (    external_syntax_results->result != UP_EXTSYN_ERR 
                 && external_syntax_results->result != UP_EXTSYN_ERR_WARN)
			{
			  /* if there is no error */
              result = check_auth(object, NULL, type, credentials);
              if (result == UP_AUTH_OK || result == UP_OKM)
			  { 
                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(object);
                }
				else
				{
                  result_from_import_key = NULL;
                }
                if (result_from_import_key == NULL)
				{ 
				  /* no PGP problem */
				  generated_obj = rpsl_object_copy(external_syntax_obj);
                  if (strcasecmp(type, "key-cert") == 0)
				  {
				    /* if the object is a key-cert object */
                    generated_obj_str = UP_generate_kc_attrs(generated_obj);
 				    free(external_syntax_results->new_obj);
                    external_syntax_results->new_obj = generated_obj_str;
                  }
                  result_from_RIPupd = send_object_db(generated_obj, NULL, "ADD");                     

                  if (result_from_RIPupd->result == 0)
				  { 
				    /* if there was no problem */
                    AK_add_to_ack(ack_file_name, "\nNew OK: [%s] %s\n",
                                  type, get_search_key(object, type));
                    deprecated_mail_from( ack_file_name, result );

                    NT_write_all_ntfs(NULL, arg, external_syntax_results->new_obj, tmpdir, 
					                   ntfy_hash, forw_hash, cross_hash, credentials.from);

				    rpsl_object_delete(generated_obj);
				    rpsl_object_delete(external_syntax_obj);
                    rpsl_object_delete(object);
				    free(result_from_RIPupd);
				    free(external_syntax_results->new_obj);
					free(external_syntax_results);
                    free(arg); 
                    return UP_OK;

                  }
				  else
				  {
                    AK_add_to_ack(ack_file_name, "\nNew FAILED: [%s] %s\n%s\n",
                                  type, get_search_key(object, type),
                                  result_from_RIPupd->error_str);
                    deprecated_mail_from( ack_file_name, result );

				    rpsl_object_delete(generated_obj);
				    rpsl_object_delete(external_syntax_obj);
                    rpsl_object_delete(object);
				    free(result_from_RIPupd->error_str);
				    free(result_from_RIPupd);
				    free(external_syntax_results->new_obj);
					free(external_syntax_results);
                    free(arg); 
                    return UP_INT;
                  }
                }
				else
				{
				  /* there was a problem with PGP key import */
                  AK_add_to_ack(ack_file_name, "\nNew FAILED: [%s] %s\n%s\n",
                                  type, get_search_key(object, type),
                                  result_from_import_key);

				  rpsl_object_delete(external_syntax_obj);
                  rpsl_object_delete(object);
                  free(result_from_import_key);
				  free(external_syntax_results->new_obj);
				  free(external_syntax_results);
                  free(arg); 
                  return UP_INT;
                }
              }
			  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 or irt request will be forwarded to <HUMAILBOX>\n");
                }

    			if ( UP_remove_override_attr(object) )
                {
    			  arg2 = rpsl_object_get_text(object,0);
                  if ( ! arg2 )
                  {
                    /* if we are processing garbage with a : in the first line, arg2 may be null */
	                arg2= (char *)malloc(2);
	                strcpy(arg2, "");	/* Don't include object in ack/notif msgs */
                  }
                }
				else
				{
				  /* there was an override attr in this object and it has not been removed */
				  arg2 = (char *)malloc(2);
				  strcpy(arg2, "");	/* Don't include object in ack/notif msgs */
				}

                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",
                              type, get_search_key(object, type),
                              arg2, type, humailbox);

                if ( ! strcmp(type, "mntner") )
                {
                  header_type = strdup("Maintainer");
                  text_type = strdup("maintainer");
                }
                else if ( ! strcmp(type, "as-block") )
                {
                  header_type = strdup("as-block");
                  text_type = strdup("as-block");
                }
                else if ( ! strcmp(type, "irt") )
                {
                  header_type = strdup("irt");
                  text_type = strdup("irt");
                }
                else
                {
                  header_type = strdup("");
                  text_type = strdup("");
                }

                /* and forward this creation request to <HUMAILBOX> */
                NT_forw_create_req(external_syntax_results->new_obj);

				rpsl_object_delete(external_syntax_obj);
                rpsl_object_delete(object);
				free(external_syntax_results->new_obj);
				free(external_syntax_results);
				free(arg2);
                free(arg); 
                free(header_type);
                free(text_type);
                return UP_AUF;
              }
			  else if (result == UP_HOF)
			  {
			    /* hierarchical authorisation failed */
                if (tracing)
				{
                  printf("TRACING: Auth failed\n");
                }

    			if ( UP_remove_override_attr(object) )
                {
    			  arg2 = rpsl_object_get_text(object,0);
                  if ( ! arg2 )
                  {
                    /* if we are processing garbage with a : in the first line, arg2 may be null */
	                arg2= (char *)malloc(2);
	                strcpy(arg2, "");	/* Don't include object in ack/notif msgs */
                  }
                }
				else
				{
				  /* there was an override attr in this object and it has not been removed */
				  arg2 = (char *)malloc(2);
				  strcpy(arg2, "");	/* Don't include object in ack/notif msgs */
				}

                AK_add_to_ack(ack_file_name, "\nNew FAILED: [%s] %s\nHierarchical authorisation failed, request forwarded to maintainer.\n%s\n",
                              type, get_search_key(object, type), arg2);

                NT_write_all_frwds(NULL, arg, tmpdir, ntfy_hash, forw_hash, cross_hash, credentials.from);

				rpsl_object_delete(external_syntax_obj);
                rpsl_object_delete(object);
				free(external_syntax_results->new_obj);
				free(external_syntax_results);
				free(arg2);
                free(arg); 
                return UP_AUF;
              }
			  else
			  {
                /* auth failed ! */
                if (tracing)
				{
                  printf("TRACING: Auth failed\n");
                }

    			if ( UP_remove_override_attr(object) )
                {
    			  arg2 = rpsl_object_get_text(object,0);
                  if ( ! arg2 )
                  {
                    /* if we are processing garbage with a : in the first line, arg2 may be null */
	                arg2= (char *)malloc(2);
	                strcpy(arg2, "");	/* Don't include object in ack/notif msgs */
                  }
                }
				else
				{
				  /* there was an override attr in this object and it has not been removed */
				  arg2 = (char *)malloc(2);
				  strcpy(arg2, "");	/* Don't include object in ack/notif msgs */
				}

                AK_add_to_ack(ack_file_name, "\nNew FAILED: [%s] %s\nAuthorisation failed, request forwarded to maintainer.\n%s\n",
                              type, get_search_key(object, type), arg2);
                deprecated_mail_from( ack_file_name, result );

                NT_write_all_frwds(NULL, arg, tmpdir, ntfy_hash, forw_hash, cross_hash, credentials.from);

				rpsl_object_delete(external_syntax_obj);
                rpsl_object_delete(object);
				free(external_syntax_results->new_obj);
				free(external_syntax_results);
				free(arg2);
                free(arg); 
                return UP_AUF; /* Auth failed */
              }
            }
			else
			{
    		  if ( UP_remove_override_attr(object) )
              {
    			arg2 = rpsl_object_get_text(object,0);
                if ( ! arg2 )
                {
                  /* if we are processing garbage with a : in the first line, arg2 may be null */
	              arg2= (char *)malloc(2);
	              strcpy(arg2, "");	/* Don't include object in ack/notif msgs */
                }
              }
			  else
			  {
				/* there was an override attr in this object and it has not been removed */
				arg2 = (char *)malloc(2);
				strcpy(arg2, "");	/* Don't include object in ack/notif msgs */
			  }

              AK_add_to_ack(ack_file_name, "\nNew FAILED: [%s] %s\n%s%s\n",
                              type, get_search_key(object, type), 
                              arg2, external_syntax_results->error_str);

			  rpsl_object_delete(external_syntax_obj);
              rpsl_object_delete(object);
			  free(external_syntax_results->new_obj);
			  free(external_syntax_results);
			  free(arg2);
              free(arg); 
              return UP_SYN;
            }
          }  /* end of creation */
        }  /* end of process object without an AUTO NIC hdl */
      }  /* end of the object is _not_ to be deleted */
    }  /* end of parsed with no systax errors */
	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, check for legacy object deletion\n");   
       }
       /* if it is for deletion */
       if (rpsl_object_is_deleted(object))
	   {
         /* here delete it */
         type = rpsl_object_get_class(object);
         old_version = get_old_version(object, arg);

		 if (tracing)
		 { 
    	   printf("TRACING: old_version: [\n%s]\n arg: [\n%s]\n", old_version, arg);
		 }

         if (old_version == NULL)
		 { 
		   /* the object doesn't exist in the db! */
		   free(arg);
    	   if ( UP_remove_override_attr(object) )
           {
    		 arg = rpsl_object_get_text(object,0);
             if ( ! arg )
             {
               /* if we are processing garbage with a : in the first line, arg may be null */
	           arg = (char *)malloc(2);
	           strcpy(arg, "");	/* Don't include object in ack/notif msgs */
             }
           }
		   else
		   {
			 /* there was an override attr in this object and it has not been removed */
			 arg = (char *)malloc(2);
			 strcpy(arg, "");	/* Don't include object in ack/notif msgs */
		   }

           AK_add_to_ack(ack_file_name, "\nDelete FAILED: [%s] %s\n***Error: Entry not found\n\n%s\n", 
                          type, get_search_key(object, type), arg);

           rpsl_object_delete(object);
           free(arg); 
           return UP_NSO; /* no such object */
         }
		 else
		 {

			if (tracing)
			{ 
    		  printf("TRACING: legacy object is in the database\n");
			}

		    /* the object is in the db */
            if ( identical(old_version, object) )
			{
			  /* if the old & new versions are identical */

			  if (tracing)
			  { 
    			printf("TRACING: old & new versions are identical\n");
			  }
              result = check_auth(NULL, object, type, credentials);
              if (result == UP_AUTH_OK || result == UP_OKM)
			  { 
                if (tracing)
				{
                  printf("TRACING: Will send the obj to be deleted\n");
                }
                if (strcmp(type, "key-cert") == 0)
				{
                  result_from_delete_key = delete_key(object);
                }
				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(object, NULL, "DEL");
                  if (result_from_RIPupd->result == 0)
				  {
                    AK_add_to_ack(ack_file_name, "\nDelete OK: [%s] %s\n", 
                                  type, get_search_key(object, type));
                    deprecated_mail_from( ack_file_name, result );
                    NT_write_all_ntfs(old_version, NULL, NULL, tmpdir, ntfy_hash, forw_hash, cross_hash, 
                                      credentials.from);

                	rpsl_object_delete(object);
					free(result_from_RIPupd);
                	free(arg); 
		        	free(old_version);
                    return UP_OK;
                  }
				  else
				  {
                    AK_add_to_ack(ack_file_name, "\nDelete FAILED: [%s] %s\n%s\n",
                                  type, get_search_key(object, type),
                                  result_from_RIPupd->error_str);
                    deprecated_mail_from( ack_file_name, result );

                	rpsl_object_delete(object);
				    free(result_from_RIPupd->error_str);
					free(result_from_RIPupd);
                	free(arg); 
		        	free(old_version);
                    return UP_INT;
                  }
                }
				else
				{
                   AK_add_to_ack(ack_file_name, "\nDelete FAILED: [%s] %s\n%s\n",
                                  type, get_search_key(object, type), result_from_delete_key);

                   rpsl_object_delete(object);
                   free(arg); 
		           free(old_version);
                   free(result_from_delete_key); 
                   return UP_INT;
                }
              }
			  else
			  { /* auth failed */
                if (tracing)
				{
                  printf("TRACING: Auth failed\n");
                }

    			if ( UP_remove_override_attr(object) )
                {
    			  arg2 = rpsl_object_get_text(object,0);
                  if ( ! arg2 )
                  {
                    /* if we are processing garbage with a : in the first line, arg2 may be null */
	                arg2= (char *)malloc(2);
	                strcpy(arg2, "");	/* Don't include object in ack/notif msgs */
                  }
                }
				else
				{
				  /* there was an override attr in this object and it has not been removed */
				  arg2 = (char *)malloc(2);
				  strcpy(arg2, "");	/* Don't include object in ack/notif msgs */
				}

                AK_add_to_ack(ack_file_name, "\nDelete FAILED: [%s] %s\nAuthorisation failed, request forwarded to maintainer.\n%s\n",
                              type, get_search_key(object, type), arg2);
                deprecated_mail_from( ack_file_name, result );
                NT_write_all_frwds(arg, NULL, tmpdir, ntfy_hash, forw_hash, cross_hash, credentials.from);

            	rpsl_object_delete(object);
            	free(arg2); 
            	free(arg); 
		    	free(old_version);
                return UP_AUF; /* Auth failed */
              } 
            }  /* end of identical match */
			else
			{
			  /* the new & old versions do not match */
    		  if ( UP_remove_override_attr(object) )
              {
    			arg2 = rpsl_object_get_text(object,0);
                if ( ! arg2 )
                {
                  /* if we are processing garbage with a : in the first line, arg2 may be null */
	              arg2= (char *)malloc(2);
	              strcpy(arg2, "");	/* Don't include object in ack/notif msgs */
                }
              }
			  else
			  {
				/* there was an override attr in this object and it has not been removed */
				arg2 = (char *)malloc(2);
				strcpy(arg2, "");	/* Don't include object in ack/notif msgs */
			  }

              AK_add_to_ack(ack_file_name, "\nDelete FAILED: [%s] %s \n***Error: new & old versions do not match\n%s\n",
                              type, get_search_key(object, type), arg2);

              rpsl_object_delete(object);
              free(arg2); 
		      free(arg);
		      free(old_version);
              return UP_NOM; /* new & old versions do not match */
            }
          }  /* end of the object is in the db */
       } /* end of the object is to be deleted */
	   else
	   {
	     /* syntax error AND not deletion */
       
         write_errors_to_ack(object, arg, ack_file_name, NULL);
         rpsl_object_delete(object);
		 free(arg);
         return UP_SYN; /* syntax error */
       }
    }
}





/* 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;
char * lwrcase_line;
int result = 0;
rpsl_object_t *obj = NULL;
const GList * error_list = NULL;

    if (tracing)
    {
      printf("TRACING: process_file running\n");
    }
  
    line = (char *)malloc(1024);

    if ((input_file = fopen(filename, "r")) == NULL)
	{
        if (tracing)
        {
          printf("TRACING: Couldn't open the file %s: %s\n", filename, strerror(errno));
        }
         ER_perror(FAC_UP, UP_CANTOPEN, "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 */ 
      lwrcase_line = strdup(line);
      g_strdown(lwrcase_line);
      if (strstr(lwrcase_line, "password:") == lwrcase_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:"))));
        free(lwrcase_line);
        continue;
      }
      free(lwrcase_line);
      
      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);
	free(line);

    /* 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) )
	{
      if (UP_is_object((char *)next->data))
	  {
	    /* if this looks like an object */
      
        object_count++;
        if(tracing)
		{
          printf("TRACING: Got an object from the list\n");
          printf("[%s]\n", (char *)next->data );
        }
        
	    obj = rpsl_object_init( (char *)next->data );
	    error_list = rpsl_object_errors(obj);

        if ( ! rpsl_object_has_error(obj, RPSL_ERRLVL_ERROR) && has_ref_to_AUTO_nic_hdl(obj) )
		{
		  /* if object has a reference to an auto- nichdl and no syntax errors
		     then defer the processing to allow the auto- nichdl to be created first */
          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);
          /* keep a tally */
          if (result == UP_NOOBJECT)
		  {
		    /* do nothing and don't increment any counts */
		  }
          else if (result == UP_OK)
		  {
            count_successful++;
          }
		  else
		  {
            count_unsuccessful++;
          }
         }
		
        rpsl_object_delete(obj);
      }
	  else
	  {
	    /* this does not look like an object (a signature? some other text?) */
        
        AK_add_to_ack(ack_file_name, "\nThe following paragraph does not look like an object,\n"
                                     "so ignoring it:\n%s\n", (char *)next->data);
      }
    }


    if (tracing)
	{
      printf("TRACING: list_of_objects2 has %d entries\n", g_slist_length(list_of_objects2));
      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);
      /* keep a tally */
      if (result == UP_NOOBJECT)
	  {
		/* do nothing and don't increment any counts */
	  }
      else if (result == UP_OK)
	  {
        count_successful++;
      }
	  else
	  {
        count_unsuccessful++;
      }
    }
}/* 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.") + 34 ); 
   
   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;

  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 */
int main(int argc, char **argv, char **envp){
  /* init_and_set_options(argc, argv, envp); */

  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;
  FILE * upd_file;
  char c;
  char * mheader_replaced = NULL;
  char * mailtxt_replaced = NULL;

  GHashTable *ntfy_hash, *forw_hash, *cross_hash;
  

  char * 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;
  char * dummy_keyid;

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

  int ch;
  char * to_address = NULL;
  char * subject = NULL;
  char * reply_to = NULL;
    

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

  AUTO_NIC_hdl_hash = g_hash_table_new(g_str_hash, g_str_equal);       
  
  /* initialise the rpsl dictionary */
  rpsl_load_dictionary(RPSL_DICT_FRONT_END);

  while ((ch = getopt(argc, argv, "MtSTf:c:snwheo:")) != -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 'w':
                  webupdate = 1;
                  break;
          case 'h':
          /* help request from sync updates */
                  help_requested = 1;
                  break;
          case 'e':
          /* enforced new from sync updates */
                  enforced_new = 1;
                  break;
          case 'o':
          /* log a string from sync updates containing the origin IP address*/
                  netupdclientIP = strdup(optarg);
                  break;
          case '?':
          default:
                  printf("Unknown option\n"); exit(1);
          }
  }

  if (tracing)
  {
    printf("TRACING: options set\n");
    printf("TRACING: reading_from_mail [%d]\n", reading_from_mail);
    printf("TRACING: networkupdate [%d]\n", networkupdate);
    printf("TRACING: webupdate [%d]\n", webupdate);
    printf("TRACING: help_requested [%d]\n", help_requested);
    printf("TRACING: enforced_new [%d]\n", enforced_new);
    printf("TRACING: netupdclientIP [%s]\n", netupdclientIP ? netupdclientIP : "NULL");
  }

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

  error_init(argc, argv);


  tmpdir = ca_get_tmpdir;
  PA_SetTmpDir(tmpdir);
  tmpdir = g_strstrip(tmpdir);
  lockdir = ca_get_lockdir;
  mailcmd = ca_get_mailcmd;
  mailcmd = g_strstrip(mailcmd);
  notitxt = ca_get_notitxt;
  notihdr = ca_get_nheader;
  mailtxt = ca_get_mailtxt; 
  successtxt = ca_get_successtxt;
  failuretxt = ca_get_failuretxt;
  helpheader = ca_get_helpheader;
  defmail = ca_get_defmail; defmail = UP_remove_EOLs(defmail);
  crosslog = ca_get_crosslog;
  fwtxt = ca_get_fwtxt;
  fwhdr = ca_get_fwheader;
  acksig = ca_get_acksig;
  humailbox = ca_get_humailbox;
  humailbox = g_strstrip(humailbox);
  autobox = ca_get_autobox;
  overridecryptedpw = ca_get_overridecryptedpw;
  overridecryptedpw = g_strstrip(overridecryptedpw);
  updlog = ca_get_updlog;
  acklog = ca_get_acklog;
  notiflog = ca_get_notiflog;
  notimailtxt = ca_get_notimailtxt;  
  notinetworktxt = ca_get_notinetworktxt;
  forwlog = ca_get_forwlog;
  fwmailtxt = ca_get_fwmailtxt;
  mtfwheader = ca_get_mtfwheader;
  mtfwtxt = ca_get_mtfwtxt;
  country = ca_get_country;
  authmethod = ca_get_authmethod;
  pgppath = ca_get_pgppath;
  gpgcmd = ca_get_gpgcmd;
  PA_SetGPGCmd(gpgcmd);
  autodbmhelp = ca_get_autodbmhelp;
  allocmnt = ca_get_allocmnt; allocmnt = UP_remove_EOLs(allocmnt);
  /* convert all '\t's, '\n's and '\r's in allocmnt to white spaces */
  for(i=0;i<strlen(allocmnt);i++){
    if(allocmnt[i] == '\r' || allocmnt[i] == '\n' || allocmnt[i] == '\t'){
      allocmnt[i] = ' ';
    }
  }
  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;
  copyright_notice = ca_get_pw_resp_header;
  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);
  PA_SetKeyRing(pgp_public_key_ring);
  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 */
  /* we now allow multiple sources */
  /* upd_source_hdl is a pointer to an array of pointers to source data */
  upd_source_hdl = ca_get_UpdSourceHandle(CA_UPDSOURCE);

  if (upd_source_hdl[0] == NULL)
  {
    printf("There must be at least one updateable source in the config file. Exiting.\n");
    ER_perror(FAC_UP, UP_CONFERR, "There must be at least one updateable source in"
                                  " the config file. Exiting.");
    exit(1);
  }
/*  now using multiple sources, 
    this data set up for each object in update message
  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++]);
    }
  }
  countries[j] = NULL; /* mark the end of array */
  g_strfreev(temp_vector);
  if (tracing)
  {
    printf("TRACING: number of countries [%i]\n", j);
  }

  /* construct authmethods array from authmethod string variable */
  
  temp_vector = g_strsplit(authmethod, "\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)
	{
      authmethods[j] = strdup(temp_vector[i]);
      g_strup(authmethods[j++]);
    }
  }
  authmethods[j] = NULL; /* mark the end of array */
  g_strfreev(temp_vector);

  /* hard code the nicsuffixes for now, but should be a configurable variable */

  nicsuffixes[0] = strdup("RIPE");
  nicsuffixes[1] = strdup("ORG");
  nicsuffixes[2] = strdup("ARIN");
  nicsuffixes[3] = strdup("RADB");
  nicsuffixes[4] = strdup("APNIC");
  nicsuffixes[5] = strdup("RIPN");
  nicsuffixes[6] = NULL;
  
  if (tracing)
  {
    /* print out the config variables for debugging */
    printf("TMPDIR is: [%s]\n", tmpdir);
    printf("MAILCMD is: [%s]\n", mailcmd);
    printf("NOTITXT is: [%s]\n", notitxt);
    printf("NOTIHDR is: [%s]\n", notihdr);
    printf("CROSSLOG is: [%s]\n", crosslog);
    printf("FWTXT is: [%s]\n", fwtxt);
    printf("FWHDR is: [%s]\n", fwhdr);
    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("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(); */

  /* 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 || networkupdate || webupdate)
  {
    if (networkupdate)
    {
      set_default_source_data();
      process_networkupdate(credentials, AUTO_NIC_hdl_hash, ack_file_name, 
                            ntfy_hash, forw_hash, cross_hash); 
    }
    if (input_file_name != NULL)
    {
      temp_upd_file = generate_upd_file();
      if (tracing)
      {
        printf("TRACING: temp_upd_file is [%s]\n", temp_upd_file);
      }

      /* first log the input in the upd log file */
      UP_add_to_upd_log(input_file_name);
 
      
      MM_store(input_file_name, temp_upd_file, 0, networkupdate | webupdate);
      /*
      PA_SetGPGCmd(gpgcmd);
      PA_SetKeyRing(pgp_public_key_ring);
      */
      p = EP_ParseMail(temp_upd_file, tmpdir);
      
    }
    else
    { /* input_file_name == NULL */
      temp_upd_file = generate_upd_file();
      MM_store("-", temp_upd_file, 0, networkupdate | webupdate);

      /* first log the input in the upd log file */
      UP_add_to_upd_log(temp_upd_file);
      
      /*
      PA_SetGPGCmd(gpgcmd);
      PA_SetKeyRing(pgp_public_key_ring);
      */
      p = EP_ParseMail(temp_upd_file, tmpdir);

    }

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

    /* the new stuff using the EP module's interface */

    if(p->from != NULL && p->from->field != NULL)
    {
      credentials.from = (char *)malloc(strlen(p->from->field) + strlen("From:") + 1);
      sprintf(credentials.from, "From:%s", p->from->field);
      credentials.from_email = strdup(p->from->field); /* This doesn't contain "From:" */
      /* cut off the '\n's and '\r's at the end */
      UP_remove_EOLs(credentials.from);
      UP_remove_EOLs(credentials.from_email);

      /* now, there is a problem with EP module (or c-client): the p->from->field
         would contain only the first line if the "From" field has multiple lines.
         As a temp solution, we will add the second line (if it exists) explicitely */
      /* This is not a problem, it is the way the imap is written.
         It returns multiple lines as a linked list, so loop for all the lines */
      while ( (p->from = p->from->next) != NULL && p->from->field != NULL)
      { /* there is another line */
        credentials.from = (char *)realloc(credentials.from, strlen(credentials.from) + strlen(p->from->field) + 1);
        strcat(credentials.from, p->from->field);
        credentials.from_email = (char *)realloc(credentials.from_email, strlen(credentials.from_email) + strlen(p->from->field) + 1);
        strcat(credentials.from_email, p->from->field);
        UP_remove_EOLs(credentials.from);
        UP_remove_EOLs(credentials.from_email);
      }
    }
    else
    {
      credentials.from = strdup("");
      credentials.from_email = strdup("");
    }
    
    update_mail_sender = strdup(credentials.from_email); 
    
    if (tracing)
    {
      printf("TRACING: From field is: [%s]\n", credentials.from);
      printf("TRACING: update_mail_sender is: [%s]\n", update_mail_sender);
    }
    

    if(p->cc != NULL && p->cc->field != NULL)
    {
      update_mail_cc = strdup(p->cc->field);
      /* cut off the '\n's and '\r's at the end */
      UP_remove_EOLs(update_mail_cc);

      /* loop for all multiple lines */
      while ( (p->cc = p->cc->next) != NULL && p->cc->field != NULL)
      { /* there is another line */
        update_mail_cc = (char *)realloc(update_mail_cc, strlen(update_mail_cc) + strlen(p->cc->field) + 1);
        strcat(update_mail_cc, p->cc->field);
        UP_remove_EOLs(update_mail_cc);
      }
    }
    else
      update_mail_cc = strdup("");
    
    if (tracing)
    {
      printf("TRACING: Cc field is: [%s]\n", update_mail_cc);
    }
    
               
    if(p->subject != NULL && p->subject->field != NULL)
    {
      subject = strdup(p->subject->field);
      /* cut off the '\n' and '\r' from the end */
      UP_remove_EOLs(subject);

      /* loop for all multiple lines */
      while ( (p->subject = p->subject->next) != NULL && p->subject->field != NULL)
      { /* there is another line */
        subject = (char *)realloc(subject, strlen(subject) + strlen(p->subject->field) + 1);
        strcat(subject, p->subject->field);
        UP_remove_EOLs(subject);
      }
    }
    else
      subject = strdup("");
    
    update_mail_subject = strdup(subject);

    if (reading_from_mail)
    {
      /* parse the subject line */ 
      subject_result = UP_subject_process(update_mail_subject); 
      if (tracing)
        printf("TRACING: subject processing result [%d]\n", subject_result.result);
    }
    else
    {
      /* web update (or networkupdate) */
      /* process the flags */
      subject_result = UP_subject_flags();
      if (tracing)
        printf("TRACING: subject flags result [%d]\n", subject_result.result);
    }

    if(p->reply_to != NULL && p->reply_to->field != NULL)
    {
      reply_to = strdup(p->reply_to->field);
      /* cut off the '\n' and '\r' from the end */                    
      UP_remove_EOLs(reply_to);

      /* loop for all multiple lines */
      while ( (p->reply_to = p->reply_to->next) != NULL && p->reply_to->field != NULL)
      { /* there is another line */
        reply_to = (char *)realloc(reply_to, strlen(reply_to) + strlen(p->reply_to->field) + 1);
        strcat(reply_to, p->reply_to->field);
        UP_remove_EOLs(reply_to);
      }
    }
    else
      reply_to = strdup("");

    
    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);
      to_address = find_email_address(to_address); /* so that we take only the email address */
    }

    if (p->message_id != NULL && p->message_id->field != NULL)
    {
      update_mail_ID = strdup(p->message_id->field);
      /* cut off the '\n' and '\r' from the end */
      UP_remove_EOLs(update_mail_ID);

      /* loop for all multiple lines */
      while ( (p->message_id = p->message_id->next) != NULL && p->message_id->field != NULL)
      { /* there is another line */
        update_mail_ID = (char *)realloc(update_mail_ID, strlen(update_mail_ID) + strlen(p->message_id->field) + 1);
        strcat(update_mail_ID, p->message_id->field);
        UP_remove_EOLs(update_mail_ID);
      }
    }
    else
      update_mail_ID = strdup("");
    

    if(p->date != NULL && p->date->field != NULL)
    {
      update_mail_date = strdup(p->date->field);
      /* cut off the '\n' and '\r' from the end  */
      UP_remove_EOLs(update_mail_date);

      /* loop for all multiple lines */
      while ( (p->date = p->date->next) != NULL && p->date->field != NULL)
      { /* there is another line */
        update_mail_date = (char *)realloc(update_mail_date, strlen(update_mail_date) + strlen(p->date->field) + 1);
        strcat(update_mail_date, p->date->field);
        UP_remove_EOLs(update_mail_date);
      }
    }
    else
      update_mail_date = strdup("");
    
    if (tracing)
    {
      printf("\nEP_ShowTree outputs:\n");
      EP_ShowTree(p->tree);
    }
    
    pt = EP_GetTokens(p->tree, NULL, 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)
    {
      /* ... 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);
        }

	    /* Start with passwords from pieces of mail up in the structure*/
	    credentials.password_list = list_item->passwords;
        /* 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);
            dummy_keyid = strdup("00000000");
            if(tracing)
            {
              printf("TRACING: This key will be added to the list: [%s]\n", temp_keyid);
            }

            if((ptk->isValidPGPSignature) == vS_IS_VALID)
            {

              AK_add_to_ack(ack_file_name, " %s", temp_keyid);
              credentials.pgp_key_list = g_slist_append (credentials.pgp_key_list, temp_keyid);
            }
            else
            {
              /* if the signature was bad, just add a dummy key ID, '00000000' */
              if(tracing)
              {
                printf("TRACING: bad signature, adding a dummy key ID\n");
              }

              AK_add_to_ack(ack_file_name, " %s", dummy_keyid);
              credentials.pgp_key_list = g_slist_append (credentials.pgp_key_list, dummy_keyid);
            }

            free(dummy_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_to_ack(ack_file_name, "\n============================================================\n\n");

      AK_add_file_to_ack(ack_file_name, autodbmhelp);
      AK_add_to_ack(ack_file_name, "\n============================================================\n\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
  { /* not reading from the mail message or from network */
    if (input_file_name != NULL)
    {
      /* first log the input in the upd log file */
      UP_add_to_upd_log(input_file_name);
      
      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);

      /* now log the input in the upd log file */
      UP_add_to_upd_log(temp_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);
    }
  }  


  /* post-process and 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();

  free(ack_file_name);

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