/***************************************
  $Revision: 1.11 $

  NT (Notifications) module

  Status: REVIEWED, NOT TESTED

  Author(s):       Engin Gunduz

  ******************/ /******************
  Modification History:
        engin (06/07/2000) Created.
		denis (25/09/2001) Modified for new API
  ******************/ /******************
  Copyright (c) 2000,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 "notification.h"
extern int supress_ack_notif;
extern char * defmail;
extern int reading_from_mail;
extern char * forwlog;

/*  Generates a unique file name and returns the full path of the filename 
    for storing notification message.  Creates the file at the same time. 
    May use PID or time or both to ensure uniqueness.  */
      
char * NT_ntfy_filename_generate( const char * tmpdir, const char * e_mail)
{
   FILE * ntfy_file;
   char * name;
   char * replaced_notimailtxt;
   char * replaced_notinetworktxt;
       
   /* allocate space for name.  32 should be enough for PID */
   name = (char*)malloc(strlen(tmpdir) + strlen(e_mail) + strlen("notify") +36 ); 
 
   sprintf(name, "%s/%s-%s.%i", tmpdir, "notify", e_mail, (int)(getpid()) );

   /* create the file */
   if (( ntfy_file = fopen(name, "w")) == NULL)
   {
     fprintf(stderr, "Can't open notification file for creating, %s", name);
   }

   fprintf(ntfy_file, "To: %s\nFrom: %s\nSubject: Notification of RIPE Database changes\nReply-To: %s\n\n%s\n", e_mail, humailbox, humailbox, notitxt);
   if (reading_from_mail)
   {
     replaced_notimailtxt = UP_replace_globals(notimailtxt);
     fprintf(ntfy_file, "%s\n\n", replaced_notimailtxt);
     free(replaced_notimailtxt);
   }

   if (networkupdate)
   {
     replaced_notinetworktxt = UP_replace_globals(notinetworktxt);
     fprintf(ntfy_file, "%s\n\n", replaced_notinetworktxt);
     free(replaced_notinetworktxt);
   }
   
   /* close it */
   fclose(ntfy_file);
    
   return name;
}




/* Generates a unique file name and returns the full path of the filename 
   for storing forwarded message. Creates the file at the same time.  */ 
char * NT_forwd_filename_generate( const char * tmpdir, const char * e_mail)
{
   FILE * forwd_file;
   char * name;
   char * replaced_fwmailtxt;
          
   /* allocate space for name.  32 should be enough for PID */
   name = (char*)malloc(strlen(tmpdir) + strlen(e_mail) + strlen("forwd") +36 ); 
   
   sprintf(name, "%s/%s-%s.%i", tmpdir, "forwd", e_mail, (int)(getpid()) );
   /* create the file */
   if (( forwd_file = fopen(name, "w")) == NULL)
   {
     fprintf(stderr, "Can't open forward file, %s", name);
   }

   fprintf(forwd_file, "To: %s\nFrom: %s\nSubject:  Requested RIPE database object changes \nReply-To: %s\n\n%s\n", e_mail, humailbox, humailbox, fwtxt);
   if (reading_from_mail)
   {
     replaced_fwmailtxt = UP_replace_globals(fwmailtxt);
     fprintf(forwd_file, "\n%s\n", replaced_fwmailtxt);
     free(replaced_fwmailtxt);
   }

   /* close it */
   fclose(forwd_file);
    
   return name;
}




/* Generates a unique file name and returns the full path of the filename 
   for storing cross notification message. Creates the file at the same time.  */ 
char * NT_cross_filename_generate( const char * tmpdir, const char * e_mail, int mode)
{
   FILE * cross_file;
   char * name;
      
   /* allocate space for name.  32 should be enough for PID */
   name = (char*)malloc(strlen(tmpdir) + strlen(e_mail) + strlen("cross") +36 ); 
   
   sprintf(name, "%s/%s-%s.%i", tmpdir, "cross", e_mail, (int)(getpid()) );
   /* create the file */
   if (( cross_file = fopen(name, "w")) == NULL)
   {
     fprintf(stderr, "Can't open cross notif file, %s", name);
   }

   if (mode == ADDITION)
   {
     fprintf(cross_file, "To: %s\nFrom: %s\n%s\nReply-To: %s\n\n", e_mail, humailbox, cno_subject_add, humailbox);
   }
   else
   {
     fprintf(cross_file, "To: %s\nFrom: %s\n%s\nReply-To: %s\n\n", e_mail, humailbox, cno_subject_del, humailbox);
   }
   
   /* close it */
   fclose(cross_file);
    
   return name;
}





/* Generates a unique file name and returns the full path of the filename for 
   storing notification message. Creates the file at the same time.  */
char * NT_crossntfy_filename_generate( const char * tmpdir, const char * e_mail)
{
   FILE * cross_file;
   char * name;
      
   /* allocate space for name.  32 should be enough for PID */
   name = (char*)malloc(strlen(tmpdir) + strlen(e_mail) + strlen("cross") +36 ); 
   
   sprintf(name, "%s/%s-%s.%i", tmpdir, "cross", e_mail, (int)(getpid()) );

   /* create the file */
   if (( cross_file = fopen(name, "w")) == NULL)
   {
     fprintf(stderr, "Can't open cross file, %s", name);
   }

   /* close it */
   fclose(cross_file);
    
   return name;
}



/* Adds the e-mail to the notify hash, generating appropriate temp files */
void NT_add_to_ntfy_hash(GHashTable * ntfy_hash, char * e_mail)
{
  if (g_hash_table_lookup(ntfy_hash ,e_mail) == NULL)
  { /* there is no such entry, so create it */

    g_hash_table_insert(ntfy_hash, strdup(e_mail), NT_ntfy_filename_generate(tmpdir, e_mail));
  }
}



/* Adds the e-mail to the forw hash, generating appropriate temp files */
void NT_add_to_frwd_hash(GHashTable * frwd_hash, char * e_mail)
{
  if (g_hash_table_lookup(frwd_hash ,e_mail) == NULL)
  { /* there is no such entry, so create it */
    g_hash_table_insert(frwd_hash, strdup(e_mail), NT_forwd_filename_generate(tmpdir, e_mail));
  }
    
}



/* Adds the e-mail to the cross hash, generating appropriate temp files */
void NT_add_to_cross_hash(GHashTable * cross_hash, const char * e_mail, int mode)
{
  /*  if e-mail is NULL, immediately return */
  if (e_mail == NULL)
  {
    return;
  }

  if (g_hash_table_lookup(cross_hash ,e_mail) == NULL)
  { /* there is no such entry, so create it */
    g_hash_table_insert(cross_hash, strdup(e_mail), NT_cross_filename_generate(tmpdir, e_mail, mode));
  }
}



/* Adds the e-mails in a linked list to the hash */
void NT_add_to_ntfy_hash_list(GHashTable * ntfy_hash, GList * e_mail_list)
{
   GList * temp = NULL;

   for (temp = e_mail_list; temp != NULL; temp = g_list_next(temp))
   {
     NT_add_to_ntfy_hash( ntfy_hash, (char *)(temp->data) );
   }
}



/* Adds the e-mails in a linked list to the hash */
void NT_add_to_frwd_hash_list(GHashTable * frwd_hash, GList * e_mail_list)
{
   GList * temp = NULL;

   for (temp = e_mail_list; temp != NULL; temp = g_list_next(temp))
   {
     NT_add_to_frwd_hash(frwd_hash, (char *)temp->data);
   }
}



/* Adds the e-mails in a linked list to the hash */
void NT_add_to_cross_hash_list(GHashTable * cross_hash, GList * e_mail_list, int mode)
{
   GList * temp = NULL;

   for (temp = e_mail_list; temp != NULL; temp = g_list_next(temp))
   {
     NT_add_to_cross_hash(cross_hash, (char *)temp->data, mode);
   }
}



/* Appends the argument strings to the file.  */
void NT_add_to_ntfy( char * filename, char * fmt, ... )
{
  va_list ap;  /* points to each unnamed arg in turn */
  FILE * ntfy_file;
 
  if (tracing)
  {
    printf("TRACING: NT_add_to_ntfy\n"); 
  }
  if (( ntfy_file = fopen(filename, "a")) == NULL)
  {
    fprintf(stderr, "Can't open notification file for writing, %s\n", filename);
    return;
  }
    
  va_start(ap, fmt);
  vfprintf(ntfy_file, fmt, ap);

  va_end(ap); /* clean up */
  fclose(ntfy_file);
}



/* Appends the argument strings to the file.  */
void NT_add_to_cross(const char * e_mail, GHashTable * hash, char * fmt, ...)
{
  va_list ap;  /* points to each unnamed arg in turn */
  FILE * cross_file = NULL;
  char * filename = NULL;

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

  /* if e-mail is NULL, immediately return */
  if(e_mail == NULL)
  {
    return;
  }
   
  if ( (filename = (char *)g_hash_table_lookup(hash, find_email_address(e_mail))) == NULL )
  {
    fprintf(stderr, "Can't find a cross notification file for e-mail %s\n", e_mail);
    return;
  }

  if ( ( cross_file = fopen(filename, "a")) == NULL )
  {
    fprintf(stderr, "Can't open cross notification file for writing, %s\n", filename);
  }
    
  va_start(ap, fmt);
  vfprintf(cross_file, fmt, ap);

  va_end(ap); /* clean up */
  fclose(cross_file);
}




/* Appends the argument string to the temp notif files in the list */
void NT_add_to_ntfy_list(GList * list, GHashTable * hash, char * arg)
{
  GList * temp = NULL;

  for(temp = list; temp != NULL; temp = g_list_next(temp))
  {
    NT_add_to_ntfy((char *)g_hash_table_lookup(hash, ((char *)temp->data)), "%s", arg);
  }
}



/* Sends the notification message which is stored in the temporary filefilename.  */
void NT_send_ntfy( const char * filename, const char * to_address, const char * mailercommand)
{
    char * mail_command_line = NULL;
    char * supress_file = NULL;
    FILE * notif_file, * supr_file_hdl;
    char buf[1024];

    /* if we are not supressing acks and notifs, send the notif */
    if (!supress_ack_notif)
	{
      if (to_address != NULL)
	  {
        mail_command_line = (char *)malloc(strlen(mailercommand) + strlen(filename) + 128);
        sprintf(mail_command_line, "%s %s < %s", mailercommand, to_address, filename);
        system(mail_command_line);
      }
    }
    /* if we are supressing acks and notifs, send notif to DEFMAIL  */
	else
	{
      supress_file = (char *)malloc(strlen(filename) + strlen(".supress") + 2); 
      sprintf(supress_file, "%s.supress", filename);
      if (( supr_file_hdl = fopen(supress_file, "w")) == NULL)
	  {
        fprintf(stderr, "Can't open supress notif file, %s", supress_file);
      }
	  else
	  {
        fprintf(supr_file_hdl, "From: %s\nTo: %s\nSubject: Supressed notif mail\n\n",
            humailbox, defmail);
        if (( notif_file = fopen(filename, "r")) == NULL)
		{
          fprintf(stderr, "Can't open notif file for reading, %s", filename);
        }
		else
		{
          while (fgets(buf, 1023, notif_file) != NULL)
		  {
            fprintf(supr_file_hdl, buf);
          }
          fclose(notif_file);
        }
      }
      fclose(supr_file_hdl);
      mail_command_line = (char *)malloc(strlen(mailercommand) + strlen(defmail) 
            + strlen(supress_file) + 128);
      sprintf(mail_command_line, "%s %s < %s", mailercommand, defmail, supress_file);
      system(mail_command_line);
      unlink(supress_file);
      free(supress_file);
    }
}



/* Adds the notification message which is in the filename into "logfilename.date". */
void NT_log_ntfy( const char * filename, const char * logfilename)
{
  FILE * notif_file, * log_file;
  char * buf;
  time_t cur_time;
  char * time_str;
  char * logfile_date;
  char * date;

  if (tracing)
  {
    printf("TRACING: NT_log_ntfy is running: filename [%s]  logfilename [%s]\n", filename, logfilename);
  }

  buf = (char *)malloc(1024);
  if (( notif_file = fopen(filename, "r")) == NULL)
  {
    fprintf(stderr, "NT_log_ntfy: Can't open notification file for reading, [%s]\n", filename);
    return;
  }

  /* construct the "logfilename.date" string */
  logfile_date = (char *)malloc(strlen(logfilename) + 10);
  date = UP_get_current_date();
  snprintf(logfile_date, strlen(logfilename) + 10, "%s.%s", logfilename, date);
  free(date);

  if (( log_file = fopen(logfile_date, "a")) == NULL)
  {
    fprintf(stderr, "NT_log_ntfy: Can't open log file, %s\n", logfilename);
    return;
  }

  /* get time */
  cur_time = time(NULL);
  time_str = strdup(ctime(&cur_time));
  /* cut the '\n' at the end */
  time_str[strlen(time_str) - 1] = '\0';

  fprintf(log_file, ">>> time: %s NOTIF <<<\n\n", time_str);


  while ( (buf=fgets(buf, 1024, notif_file)) != NULL )
  {
    fprintf(log_file, "%s", buf);
  }
  free(buf);

  fclose(notif_file);
  fclose(log_file);
}


/* Deletes the temporary notification file. */ 
void NT_delete_ntfy( const char * filename)
{
   unlink(filename);
}


/* The function required for NT_send_ntfy_list */
void  nt_gfunc_send(gpointer key, gpointer value, gpointer user_data)
{
  NT_send_ntfy((char *)value, (char *)key, (char *)user_data);
}


       
/* Sends the notification messages whose temp files are stored in filehash. */              
void NT_send_ntfy_list( GHashTable * filehash, char * mailercommand)
{
  g_hash_table_foreach( filehash, (GHFunc)nt_gfunc_send, mailercommand);
}




/* The function required for NT_log_ntfy_list */
void  nt_gfunc_log(gpointer key, gpointer value, gpointer user_data)
{
  NT_log_ntfy((char *)value, (char *)user_data);
}




/* Logs the notification whose temp files are in filehash to log_file. */
void NT_log_ntfy_list( GHashTable * filehash, char * log_file)
{
   g_hash_table_foreach( filehash, (GHFunc)nt_gfunc_log, log_file);
}



/* The function required for NT_delete_ntfy_list */
void  nt_gfunc_delete(gpointer key, gpointer value, gpointer user_data)
{
  NT_delete_ntfy((char *)value);
}



/* Deletes the temporary notification messages in the filehash. Empties and frees 
   the hash too.  */
void NT_delete_ntfy_list( GHashTable * filehash)
{
  g_hash_table_foreach(filehash, (GHFunc)nt_gfunc_delete, NULL);
  g_hash_table_destroy(filehash);
}


/* to be used with g_hash_table_foreach in NT_unify_list.
   Adds the 'value' to the list (a GList) */
/* void nt_add_to_list(char * key, rpsl_attr_t * value, GList ** list)
{
  *list = g_list_append(*list, strdup(value));
}
*/


/* to be used with g_hash_table_foreach in NT_unify_list.
   frees the 'key' and 'value' in the list (a GList) */
void nt_free_list(char * key, char * value, void *nothing)
{
  if ( key != NULL )
    free(key);
  if ( value != NULL )
    free(value);
}



/* "unifies" a list in a case insensitive manner */
GList * NT_unify_list(GList * in_list)
{
  GHashTable * unification_hash;
  GList ** out_list;
  GList * temp;
  GList *return_list = NULL;
  char * key, * value;
  int strcmp();

  /* allocate space for out_list */ 
  out_list = (GList **)malloc(sizeof(GList *));
  *out_list = NULL;

  /* initialize the hash to be used for unification process */
  unification_hash = g_hash_table_new(g_str_hash, g_str_equal);

  /* first put the list elements into a hash, to unify them */
  for (temp = in_list; temp != NULL; temp = g_list_next(temp))
  {
    /* convert the email address into lowercase, for comparison reasons only */
    key = rpsl_attr_get_clean_value((rpsl_attr_t *)(temp->data));
    value = strdup(key);
    g_strdown(key);
    
    if (g_hash_table_lookup(unification_hash, key) == NULL)
	{ /* if it is not already in the hash table, add to the hash and append to new list */
      g_hash_table_insert(unification_hash, key, value);
      *out_list = g_list_insert_sorted( *out_list, strdup(value), strcmp );
/*       *out_list = g_list_append( *out_list, strdup(value) ); */
    }
	else
	{  /* it is a duplicate email address, don't append to new list */
	  free(key);
	  free(value);
	}
  }

  /* now, delete the elements in the hash */
  g_hash_table_foreach(unification_hash, (GHFunc)nt_free_list, NULL);

  g_hash_table_destroy(unification_hash);
  
  return_list = *out_list;
  free(out_list);
  return return_list;
}



/* Gets GLists of irt atributes from old and new object. Compares these
   lists and returns a list of diferences */

/* if option==1, return list contains only newly deleted irts
   if option==2, return list contains only newly added irts
   if option==3, return list contains both */

GList *NT_compare_lists(GList *old_irts, GList *new_irts, int option)
{
  typedef struct irt_details
  {
    gchar *irt_name;
	rpsl_attr_t *irts;
	gint   matched;
  } irt_details_t;
  
  GList *old_irt_details = NULL;
  GList *new_irt_details = NULL;
  GList *old_irts_item = NULL;
  GList *new_irts_item = NULL;
  GList *return_list = NULL;
  irt_details_t *irt_details;
  char *irt_name;
  
  if (tracing)
  { 
    printf("TRACING: NT_compare_lists is running: option: [%d]\n", option);
  }

  /* collect data from the old_irts */
  for ( old_irts_item = old_irts; old_irts_item != NULL; old_irts_item = g_list_next(old_irts_item) )
  {
    irt_details = (irt_details_t *)malloc(sizeof(irt_details_t));
	/* get irt name from attr */
	irt_name = rpsl_attr_get_clean_value( (rpsl_attr_t *)(old_irts_item->data) );
	/* enter details into irt_details structure */
	irt_details->irts = (rpsl_attr_t *)(old_irts_item->data);
	irt_details->irt_name = irt_name;
	irt_details->matched = 0;
	/* append irt_details structure to old_irt_details list */
	old_irt_details = g_list_append(old_irt_details, irt_details);
  }
  
  /* collect data from the new_irts and compare with the old in the same loop */
  for ( new_irts_item = new_irts; new_irts_item != NULL; new_irts_item = g_list_next(new_irts_item) )
  {
    irt_details = (irt_details_t *)malloc(sizeof(irt_details_t));
	/* get irt name from attr */
	irt_name = rpsl_attr_get_clean_value( (rpsl_attr_t *)(new_irts_item->data) );
	/* enter details into irt_details structure */
	irt_details->irts = (rpsl_attr_t *)(new_irts_item->data);
	irt_details->irt_name = irt_name;
	irt_details->matched = 0;

	/* compare the name with the names from the old list  */
	for ( old_irts_item = old_irt_details; old_irts_item != NULL; old_irts_item = g_list_next(old_irts_item) )
    {
	  if ( ! strcmp(irt_name, ((irt_details_t *)(old_irts_item->data))->irt_name ) )
	  {
	    irt_details->matched = 1;
		((irt_details_t *)(old_irts_item->data))->matched = 1;
		break;
	  }
	}

	/* append irt_details structure to new_irt_details list */
	new_irt_details = g_list_append(new_irt_details, irt_details);
  }
  
  /* we now want a list of irts taken from the old and new irt_details lists
     where the matched flag is _NOT_ set. These will only exist in one list
	 and have therefore just been added/deleted */
  /* if option==1, return list contains only newly deleted irts
     if option==2, return list contains only newly added irts
     if option==3, return list contains both */
  if ( option == 1 || option == 3 )
  {   
    for ( old_irts_item = old_irt_details; old_irts_item != NULL; old_irts_item = g_list_next(old_irts_item) )
    {
      if ( ! ((irt_details_t *)(old_irts_item->data))->matched )
	  {
        if (tracing)
        { 
          printf("TRACING: NT_compare_lists: adding old irt to return list [%s]\n", ((irt_details_t *)(new_irts_item->data))->irt_name);
        }

	    return_list = g_list_append(return_list, ((irt_details_t *)(old_irts_item->data))->irts );
	  }
	  free ( ((irt_details_t *)(old_irts_item->data))->irt_name );
    }
  }
  g_list_free(old_irt_details);
  if ( option == 2 || option == 3 )
  {   
    for ( new_irts_item = new_irt_details; new_irts_item != NULL; new_irts_item = g_list_next(new_irts_item) )
    {
      if ( ! ((irt_details_t *)(new_irts_item->data))->matched )
	  {
        if (tracing)
        { 
          printf("TRACING: NT_compare_lists: adding new irt to return list [%s]\n", ((irt_details_t *)(new_irts_item->data))->irt_name);
        }

	    return_list = g_list_append(return_list, ((irt_details_t *)(new_irts_item->data))->irts );
	  }
	  free ( ((irt_details_t *)(new_irts_item->data))->irt_name );
    }
  }
  g_list_free(new_irt_details);
  
  return return_list;
}


/* Gets old and new objects supplied, forms lists of any irt objects referenced 
   by these. Returns a GList of irt-nfy for any irt objects that heve been added
   or deleted.
*/
GList *NT_check_irtnfy(rpsl_object_t *old_obj, rpsl_object_t *new_obj)
{
  GList *old_irts = NULL; 
  GList *new_irts = NULL; 
  GList *changed_irts = NULL; 

  if (old_obj != NULL)
    old_irts = get_irts(old_obj);
  if (new_obj != NULL)
    new_irts = get_irts(new_obj);

  if ( old_irts != NULL && new_irts!= NULL )
  {
    /* compare lists for additions and deletions */
	changed_irts = NT_compare_lists(old_irts, new_irts, 3);
	return get_irtnfy_vector(changed_irts);
  }
  else if ( old_irts != NULL )
  {
    /* these irts have been deleted */
	return get_irtnfy_vector(old_irts);
  }
  else if ( new_irts != NULL )
  {
    /* these irts have been added */
	return get_irtnfy_vector(new_irts);
  }
  else
    return NULL;  /* no irt objects at all */
}


/* Gathers e-mail boxes to which we will send normal notification messages. It 
   takes old and new object strings, looks up maintainers and less specific inetnums/domains/routes 
   when necessary, finds the addresses (in mnt-nfy and notify attributes) and returns 
   a list of email addresses as strings. 
   Also now checks for irt-nfy in any irt objects that have been added or deleted */
GList * NT_gather_ntfy_addresses( const char * old_object_str, const char * new_object_str)
{
  GList *return_list = NULL, *temp = NULL;
  GList *mntners = NULL; 
  const GList *error_list = NULL; 
  rpsl_object_t *old_obj = NULL;
  rpsl_object_t *new_obj = NULL;

  if (tracing)
  { 
    printf("TRACING: NT_gather_ntfy_addresses is running: old_object_str : [%s]; new_object_str: [%s]\n", old_object_str ? old_object_str : "", new_object_str ? new_object_str : "");
  }

  if (old_object_str != NULL && new_object_str != NULL)
  { /* it was an update */
	old_obj = rpsl_object_init(old_object_str);
	error_list = rpsl_object_errors(old_obj);
	new_obj = rpsl_object_init(new_object_str);
	error_list = rpsl_object_errors(old_obj);

    /* start with the 'notify' in the object itself */
    temp = get_attr_list(old_obj, "notify");
    mntners = get_mntners(old_obj);
	/* now add the 'mnt-by' from any of the mntners in the old object only */
    temp = g_list_concat(temp, get_mntnfy_vector(mntners));
	/* now add the 'irt-by' from any of the irts in the old and new objects
	   if they have just been added or deleted */
	temp = g_list_concat(temp, NT_check_irtnfy(old_obj, new_obj));
  }
  else if (old_object_str == NULL && new_object_str != NULL)
  { /* it was a creation */
	new_obj = rpsl_object_init(new_object_str);
	error_list = rpsl_object_errors(new_obj);

    if ( ! rpsl_object_has_error( new_obj, RPSL_ERRLVL_ERROR ) )
	{
      /* start with the 'notify' in the object itself */
      temp = get_attr_list(new_obj, "notify");
      mntners = get_mntners(new_obj);
	  /* now add the 'mnt-by' from any of the mntners in the new object only */
      temp = g_list_concat(temp, get_mntnfy_vector(mntners));
	  /* now add the 'irt-by' from any of the irts in the new object
		 as they have just been added */
	  temp = g_list_concat(temp, NT_check_irtnfy(old_obj, new_obj));
	}
  }
  else if (old_object_str != NULL && new_object_str == NULL)
  { /* it was a deletion */
	old_obj = rpsl_object_init(old_object_str);
	error_list = rpsl_object_errors(old_obj);

    /* start with the 'notify' in the object itself */
    temp = get_attr_list(old_obj, "notify");
    mntners = get_mntners(old_obj);
	/* now add the 'mnt-by' from any of the mntners in the old object only */
    temp = g_list_concat(temp, get_mntnfy_vector(mntners));
	/* now add the 'irt-by' from any of the irts in the old object
	   as they have just been deleted */
	temp = g_list_concat(temp, NT_check_irtnfy(old_obj, new_obj));
  }

  /* we have to 'unify' the list here!, return_list is now a list of malloc'd email address strings */
  return_list = NT_unify_list(temp);
  rpsl_attr_delete_list( temp );
  if ( old_obj )
    rpsl_object_delete(old_obj);
  if ( new_obj )
    rpsl_object_delete(new_obj);

  if (tracing)
  {
    printf( "TRACING: notif email addresses\n" );
    for ( temp=return_list; temp!=NULL; temp=g_list_next(temp) )
      printf( "TRACING: [%s]\n", (char *)(temp->data) );
  }

  return return_list;
}



/* Gathers e-mail boxes to which we will forward messages (or rather, objects). It 
   an object, looks up maintainers, finds the addresses (in upd-to attributes) and returns 
   a list of them. */
GList * NT_gather_frwd_addresses(char * object_str)
{
  GList *temp = NULL;
  GList *attr_item = NULL;
  GList *email_list = NULL;
  char *email;
  const GList *error_list = NULL; 
  rpsl_object_t *object;
  GList * mntners = NULL; 

  object = rpsl_object_init(object_str);
  error_list = rpsl_object_errors(object);

  mntners = get_mntners(object);
  /* get a list of upd-to attributes */
  temp = get_updto_vector(mntners);
  /* now extract the email text strings from the values of these attributes */
  for ( attr_item = temp; attr_item != NULL ; attr_item = g_list_next(attr_item) )
  {
    email = rpsl_attr_get_clean_value((rpsl_attr_t *)(attr_item->data));
printf("NT_gather_frwd_addresses: email [%s]\n", email );
    email_list = g_list_append(email_list, email );
  }
  return email_list;
}



/* Accepts a parsed route object and returns a list of overlapping routes */
overlap_routes get_overlapping_routes_list(rpsl_object_t * object)
{
  char * route_prefix = NULL;
  GList * tmp_list;
  char * result;
  char * query_string;  
  overlap_routes result_routes;
    
  result_routes.less_spec = NULL;
  result_routes.exact_match = NULL;
  result_routes.more_spec = NULL;
      
  tmp_list = rpsl_object_get_attr(object, "route");  

  if (tmp_list != NULL && tmp_list->data != NULL)
  {
    route_prefix = rpsl_attr_get_clean_value((rpsl_attr_t *)(tmp_list->data));
  }
  else
  {
    return result_routes; /* then, this wasn't a route object */
  }
  
  /* get the less specific route objects */
  /* form the query string */
  query_string = (char *)malloc(strlen("-Troute -r -l ") + strlen(route_prefix) + 2);
  sprintf(query_string, "-Troute -r -l %s", route_prefix);

  /* get the results */ 
  result = send_and_get(query_host, query_port, query_string);
  free(query_string);

  /* and fill in the result field  */
  result_routes.less_spec = take_objects(result);

  /* get the exact match route objects */
  /* form the query string */
  query_string = (char *)malloc(strlen("-Troute -r -x ") + strlen(route_prefix) + 2);
  sprintf(query_string, "-Troute -r -x %s", route_prefix);

  /* get the results */ 
  result = send_and_get(query_host, query_port, query_string);
  free(query_string);
  

  /* filter out the route object itself */
  result = UP_filter_out_same_origins(result, object);

  /* and fill in the result field  */
  if (result != NULL)
  {
    result_routes.exact_match = take_objects(result);
  }

  /* get the more specific route objects */
  /* form the query string */
  query_string = (char *)malloc(strlen("-Troute -r -M ") + strlen(route_prefix) + 2);
  sprintf(query_string, "-Troute -r -M %s", route_prefix);

  /* get the results */ 
  result = send_and_get(query_host, query_port, query_string);
  free(query_string);

  /* and fill in the result field  */
  result_routes.more_spec = take_objects(result);

  /* Return the results */
  return result_routes;
}



/* Gets old and new versions of the object, and creates temporary notification
   files when necessary, and then writes appropriate strings into those
   temporary files. */
void NT_write_all_ntfs(char * old_object, char * new_object, char * formatted_object,
                       const char * tempdir,
                       GHashTable * ntfy_hash,  GHashTable * forwd_hash, GHashTable * cross_hash, 
                       char * from_address)
{ 
   GList * e_mail_list = NULL;
   GList * temp = NULL;
   const GList *error_list = NULL; 
   char * e_mail_address = NULL;
   overlap_routes overlapping_routes;
   rpsl_object_t *object;
   char *arg2;

   if ( reading_from_mail )
   {
     /* from_address may contain also the name, like "Johnny Bravo <johnny@inter.net>",
        so extract the e-mail address from it */
     e_mail_address = find_email_address(from_address); 

     if (tracing)
     {
       printf("TRACING: NT_write_all_ntfs: from_address=[%s], e_mail_address=[%s]\n", from_address, e_mail_address);
     }
   }
   if (old_object != NULL && new_object != NULL)
   { 
     /* it was an update */
	 object = rpsl_object_init(formatted_object ? formatted_object : new_object);
	 error_list = rpsl_object_errors(object);

     if ( UP_remove_override_attr(object) )
       arg2 = rpsl_object_get_text(object, RPSL_STD_COLUMN);
	 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 */
	 }

     rpsl_object_delete(object);
   
     e_mail_list = NT_gather_ntfy_addresses(old_object, new_object);
     NT_add_to_ntfy_hash_list(ntfy_hash, e_mail_list); 
     NT_add_to_ntfy_list(e_mail_list, ntfy_hash, "---\nPREVIOUS OBJECT:\n\n");
     NT_add_to_ntfy_list(e_mail_list, ntfy_hash, old_object);
     NT_add_to_ntfy_list(e_mail_list, ntfy_hash, "\n\nREPLACED BY:\n\n");
     NT_add_to_ntfy_list(e_mail_list, ntfy_hash, arg2);
     NT_add_to_ntfy_list(e_mail_list, ntfy_hash, "\n");
   }
   else if (old_object == NULL && new_object != NULL)
   { 
     /* it was a creation */
	 object = rpsl_object_init(formatted_object ? formatted_object : new_object);
	 error_list = rpsl_object_errors(object);

     if ( UP_remove_override_attr(object) )
       arg2 = rpsl_object_get_text(object, RPSL_STD_COLUMN);
	 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 */
	 }

     rpsl_object_delete(object);
   
     e_mail_list = NT_gather_ntfy_addresses(old_object, new_object);
     NT_add_to_ntfy_hash_list(ntfy_hash, e_mail_list); 
     NT_add_to_ntfy_list(e_mail_list, ntfy_hash, "---\nOBJECT BELOW CREATED:\n\n");
     NT_add_to_ntfy_list(e_mail_list, ntfy_hash, arg2);
     NT_add_to_ntfy_list(e_mail_list, ntfy_hash, "\n");

     /* We'll deal with cross notifications only when we create or delete route objects */
	 object = rpsl_object_init(new_object);
	 error_list = rpsl_object_errors(object);

     if (strcmp(rpsl_object_get_class(object), "route") == 0)
	 {
       overlapping_routes = get_overlapping_routes_list(object);
       if (overlapping_routes.less_spec != NULL || overlapping_routes.exact_match != NULL ||
          overlapping_routes.more_spec != NULL )
	   {
         NT_add_to_cross_hash(cross_hash, e_mail_address, ADDITION);
         NT_add_to_cross(e_mail_address, cross_hash, "%s\n\n%s\n\n%s\n\n", cno_explain_add, new_object, cno_overlap_add);
         if (overlapping_routes.less_spec != NULL)
		 {
           NT_add_to_cross(from_address, cross_hash, "LESS SPECIFIC MATCHES\n\n");
           for (temp = overlapping_routes.less_spec; temp != NULL; temp = g_list_next(temp))
		   {
             NT_add_to_cross(from_address, cross_hash, "%s\n\n", (char *)temp->data);
           }
         }
         if (overlapping_routes.exact_match != NULL)
		 {
           NT_add_to_cross(from_address, cross_hash, "EXACT MATCHES\n\n");
           for (temp = overlapping_routes.exact_match; temp != NULL; temp = g_list_next(temp))
		   {
             NT_add_to_cross(from_address, cross_hash, "%s\n\n", (char *)temp->data);
           }
         }
         if (overlapping_routes.more_spec != NULL)
		 {
           NT_add_to_cross(from_address, cross_hash, "MORE SPECIFIC MATCHES\n\n");
           for (temp = overlapping_routes.more_spec; temp != NULL; temp = g_list_next(temp))
		   {
             NT_add_to_cross(from_address, cross_hash, "%s\n\n", (char *)temp->data);
           }
         }
       }
     }
	 rpsl_object_delete(object);
   }
   else if (old_object != NULL && new_object == NULL)
   { /* it was a deletion */
     old_object = delete_delete_attrib(old_object);
	 object = rpsl_object_init(old_object);
	 error_list = rpsl_object_errors(object);

     if ( UP_remove_override_attr(object) )
       arg2 = rpsl_object_get_text(object, RPSL_STD_COLUMN);
	 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 */
	 }

     e_mail_list = NT_gather_ntfy_addresses(old_object, new_object);
     NT_add_to_ntfy_hash_list(ntfy_hash, e_mail_list); 
     NT_add_to_ntfy_list(e_mail_list, ntfy_hash, "---\nOBJECT BELOW DELETED:\n\n");
     NT_add_to_ntfy_list(e_mail_list, ntfy_hash, arg2);
     NT_add_to_ntfy_list(e_mail_list, ntfy_hash, "\n");

     /* We'll deal with cross notifications only when we create or delete route objects */
     if (strcmp(rpsl_object_get_class(object), "route") == 0)
	 {
       overlapping_routes = get_overlapping_routes_list(object);
       if (overlapping_routes.less_spec != NULL || overlapping_routes.exact_match != NULL ||
          overlapping_routes.more_spec != NULL )
	   {
         NT_add_to_cross_hash(cross_hash, e_mail_address, DELETION);
         NT_add_to_cross(e_mail_address, cross_hash, "%s\n\n%s\n\n%s\n\n", cno_explain_del, old_object, cno_overlap_del);
         if (overlapping_routes.less_spec != NULL)
		 {
           NT_add_to_cross(from_address, cross_hash, "LESS SPECIFIC MATCHES\n\n");
           for (temp = overlapping_routes.less_spec; temp != NULL; temp = g_list_next(temp))
		   {
             NT_add_to_cross(from_address, cross_hash, "%s\n\n", (char *)temp->data);
           }
         }
         if (overlapping_routes.exact_match != NULL)
		 {
           NT_add_to_cross(from_address, cross_hash, "EXACT MATCHES\n\n");
           for (temp = overlapping_routes.exact_match; temp != NULL; temp = g_list_next(temp))
		   {
             NT_add_to_cross(from_address, cross_hash, "%s\n\n", (char *)temp->data);
           }
         }
         if (overlapping_routes.more_spec != NULL)
		 {
           NT_add_to_cross(from_address, cross_hash, "MORE SPECIFIC MATCHES\n\n");
           for (temp = overlapping_routes.more_spec; temp != NULL; temp = g_list_next(temp))
		   {
             NT_add_to_cross(from_address, cross_hash, "%s\n\n", (char *)temp->data);
           }
         }
       }
     }
	 rpsl_object_delete(object);
   }
   free(arg2);
}



/* Gets old and new versions of the object, and creates temporary notification
   files when necessary, and then writes appropriate strings into those
   temporary files. */
void NT_write_all_frwds(char * old_object_str, char * new_object_str, const char * tempdir,
                       GHashTable * ntfy_hash,  GHashTable * forwd_hash, GHashTable * cross_hash, 
                       const char * from_address)
{ 
   GList *e_mail_list = NULL;
   rpsl_object_t *object;
   char *arg2;
   const GList * error_list = NULL;

   if (tracing)
   {
     printf("TRACING: NT_write_all_frwds is running\n");
   }
   
   if ( new_object_str )
   {
	 object = rpsl_object_init(new_object_str);
	 error_list = rpsl_object_errors(object);

     if ( UP_remove_override_attr(object) )
       arg2 = rpsl_object_get_text(object, RPSL_STD_COLUMN);
	 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 (old_object_str != NULL && new_object_str != NULL)
   { /* it was an update */
     e_mail_list = NT_gather_frwd_addresses(old_object_str);
     NT_add_to_frwd_hash_list(forwd_hash, e_mail_list);
     NT_add_to_ntfy_list(e_mail_list, forwd_hash, "----\nUPDATE REQUESTED FOR:\n\n");
     NT_add_to_ntfy_list(e_mail_list, forwd_hash, arg2);
   }
   else if (old_object_str == NULL && new_object_str != NULL)
   { /* it was a creation */
     e_mail_list = NT_gather_frwd_addresses(new_object_str);
     NT_add_to_frwd_hash_list(forwd_hash, e_mail_list);
     NT_add_to_ntfy_list(e_mail_list, forwd_hash, "----\nADDITION REQUESTED FOR:\n\n");
     NT_add_to_ntfy_list(e_mail_list, forwd_hash, arg2);
   }
   else if (old_object_str != NULL && new_object_str == NULL)
   { /* it was a deletion */
     e_mail_list = NT_gather_frwd_addresses(old_object_str);
     NT_add_to_frwd_hash_list(forwd_hash, e_mail_list);
     NT_add_to_ntfy_list(e_mail_list, forwd_hash, "----\nDELETION REQUESTED FOR:\n\n");
     NT_add_to_ntfy_list(e_mail_list, forwd_hash, old_object_str);
   }
}



/* Sends the creation forward message which is stored in the temporary file filename.  */
void NT_send_forw_creation( const char * filename, const char * to_address, const char * mailercommand)
{
    char * mail_command_line = NULL;

    if (to_address != NULL)
	{
      mail_command_line = (char *)malloc(strlen(mailercommand) + strlen(filename) + 128);
      sprintf(mail_command_line, "%s %s < %s", mailercommand, to_address, filename);
      system(mail_command_line);
      free(mail_command_line);
    }
}


/* NT_forw_create_req forwards the maintainer, as-block and irt creation requests
   to <HUMAILBOX> */
void NT_forw_create_req(const char * object_str)
{
   FILE * forw_file;
   char * filename;
   char * replaced_mtfwheader;
   char * replaced_mtfwtxt;

   /* allocate space for name.  32 should be enough for PID */
   filename = (char*)malloc(strlen(tmpdir) + strlen("creat-forw") +34 ); 
   
   sprintf(filename, "%s/%s.%i", tmpdir, "creat-forw", (int)(getpid()) );

   /* create the file */
   if (( forw_file = fopen(filename, "w")) == NULL)
   {
     fprintf(stderr, "NT_forw_create_req: Can't open creation forward file for creating, %s", filename);
   }

   replaced_mtfwheader = UP_replace_globals(mtfwheader);
   replaced_mtfwtxt = UP_replace_globals(mtfwtxt);
   
   fprintf(forw_file, "%s\n\n", replaced_mtfwheader);
   
   if (reading_from_mail)
   {
     fprintf(forw_file, "%s\n\n", replaced_mtfwtxt);
   }

   /* print the object */
   fprintf(forw_file, "%s\n\n", object_str);
    
   /* close it */
   fclose(forw_file);
    
   /* send it */ 
   NT_send_forw_creation(filename, humailbox, mailcmd);

   /* log it */
   NT_log_ntfy(filename, forwlog);
   
   /* delete it */
   unlink(filename);
 
   /* free the mem */ 
   free(filename);
   free(replaced_mtfwheader);
   free(replaced_mtfwtxt);
}
