/***************************************
  $Revision: 1.7 $

  AK (Acknowledgement) module

  Status: REVIEWED, NOT TESTED

  Author(s):       Engin Gunduz

  ******************/ /******************
  Modification History:
        engin (10/06/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 "ack.h"
extern int supress_ack_notif;
extern char * humailbox;
extern char * failuretxt;
extern char * helpheader;
extern char * successtxt;
extern char * defmail;
extern char * acksig;
extern up_subject_struct subject_result;
extern int count_successful;
extern int count_unsuccessful;
extern char *netupdclientIP;

extern int reading_from_mail;
extern int networkupdate;

/* web update flags */
extern int webupdate;
extern int help_requested;
extern int enforced_new;

/*

  AK_add_to_ack: writes a message to the acknowledgement file.
    Also, prints it out to the stdout if it was a networkupdate
    (networkupdate is run through inetd, so stdout is our socket)

*/

void AK_add_to_ack(const char * filename, char * fmt, ...){

  va_list ap;  /* points to each unnamed arg in turn */
  char *p, *sval;
  int ival;
  double dval;
  FILE * ack_file;
  
  if(( ack_file = fopen(filename, "a")) == NULL){
    fprintf(stderr, "Can't open ack file, %s", filename);
  }

    
  /* if this is a network update, print it out first to the 
     stdout (which is socket) */ 
  if(networkupdate){
    va_start(ap, fmt);
    vprintf(fmt, ap);
    fflush(stdout);
    va_end(ap); 
  }

  /* and then to the file */
  va_start(ap, fmt);
  
  for(p = fmt; *p; p++){
    if (*p != '%') {
      fprintf(ack_file, "%c", *p);
      continue;
    }
    switch(*++p) {
    case 'd':
      ival = va_arg(ap, int);
      fprintf(ack_file, "%d", ival);
      break;
    case 'f':
      dval = va_arg(ap, double);  
      fprintf(ack_file, "%f", dval);
      break;
    case 'X':
      ival = va_arg(ap, int);
      fprintf(ack_file, "%X", ival);
      break;
    case 'x':
      ival = va_arg(ap, int);
      fprintf(ack_file, "%x", ival);
      break;
    case 's':
      sval = va_arg(ap, char *);
      fprintf(ack_file, "%s", sval);
      break;
    default:
      putchar(*p);
      break;
    }
  }

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



/* Adds a complete file to the ack file */

void AK_add_file_to_ack(const char * ackfile, const char * filetoadd){

  FILE * ack_file, * file_to_add;
  char   buf[1024];

  if(( ack_file = fopen(ackfile, "a")) == NULL){
    fprintf(stderr, "AK_add_file_to_ack: Can't open ack file, %s\n", ackfile);
  }
  
  if(( file_to_add = fopen(filetoadd, "r")) == NULL){
    
    fprintf(stderr, "AK_add_file_to_ack: Can't open file, %s\n", filetoadd);
    fprintf(ack_file, "\nHelp file could not be found.\nPlease notify the database administrator.\n");
    fclose(ack_file);
    free(buf);

  }else{
 
    while(fgets(buf, 1023, file_to_add) != NULL){
      fprintf(ack_file, "%s", buf);
    }

    fclose(ack_file);
    fclose(file_to_add);
  }
}


/*

  AK_ack_file_name_generate: Generates a unique name for temporary acknowledgement
     files, and also creates it. 

      tmpdir: temporary directory (without a trailing '/')
      prefix: prefix for the temp file
      
      returns: the generated name. 
*/

char * AK_ack_file_name_generate( const char * tmpdir, const char * prefix)
{
   FILE * ack_file;
   char * name;
      
   /* allocate space for name. 32 should be enough for PID */
   name = (char*)malloc(strlen(tmpdir) + strlen(prefix) + 35); 
   
   sprintf(name, "%s/%s.%i", tmpdir, prefix, (int)(getpid()) );

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

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


/* 

AK_send_ack: post-processes and sends the ack message contained in the temp file.
 
   
*/

void AK_send_ack( const char * filename, const char * to_address, const char * mailercommand)
{
    char * mail_command_line = NULL;
    char * supress_file = NULL, * temp_file = NULL;
    FILE * ack_file, * supr_file_hdl, * temp_file_hdl;
    char *txt_replaced;
    char buf[1024];

    /* first check if we the user specified some non-keyword words in
       the subject line (in addition to some valid keywords) 
       If so, we will add a warning to the ack file */
    if (subject_result.result == UP_SUBJ_UNRECOG)
	{
      if (( ack_file = fopen(filename, "a")) == NULL)
	  {
        fprintf(stderr, "Can't open ack file for appending, %s", filename);
      }
	  else
	  {
        fprintf(ack_file, "\nWarning: unknown keywords found in subject line:\n"
                   "%s\n"
                   "Thus, all keywords in subject line were ignored.\n",
                    subject_result.word_list ? subject_result.word_list : "" );
        fclose(ack_file);
      }
      
    /* and now check if the user specified an invalid combination of keywords
       in the subject line, and if so, print an appropriate warning to ack */
    }
	else if (subject_result.result == UP_SUBJ_INVALID_COMB)
	{
      if (( ack_file = fopen(filename, "a")) == NULL)
	  {
        fprintf(stderr, "Can't open ack file for appending, %s", filename);
      }
	  else
	  {
        if ( webupdate )
        {
          fprintf(ack_file, "\nWarning: This combination of options of 'help' and 'new' is not allowed.\n"
                   "Thus, all options were ignored.\n");
        }
        else
        {
          fprintf(ack_file, "\nWarning: This combination of keywords in subject line is not allowed.\n"
                   "Thus, all keywords in subject line were ignored.\n");
        }
        fclose(ack_file);
      }
    }

    /* if the update didn't contain any objects and was not a request for help, put a warning */
    if (count_successful == 0 && count_unsuccessful == 0 && subject_result.result != UP_SUBJ_HELP_REQ)
	{
      AK_add_to_ack(filename, "*** Warning: No objects were found in the message ***\n");
    }

    /* add the ACKSIG to the ack */
    AK_add_to_ack(filename ,"\n%s", acksig);

    /* Here, we will print out the header of the ACK message. It cannot be
       prepared before, since we wouldn't know if the header should use
       SUCCESSTXT or FAILURETXT */
    temp_file = (char *)malloc(strlen(filename) + strlen(".temp") + 2);
    sprintf(temp_file, "%s.temp", filename);
    if(( temp_file_hdl = fopen(temp_file, "w")) == NULL)
	{
      fprintf(stderr, "Can't open temp ack file, %s\n", temp_file);
    }
	else
	{
      if ( subject_result.result == UP_SUBJ_HELP_REQ )
      {
        /* this is a request for help, so we will use the HELPHEADER */
        /* replace the global variables in helpheader */
        txt_replaced = UP_replace_globals(helpheader);
      }
      else if (count_unsuccessful > 0 || (count_successful + count_unsuccessful) == 0)
	  {
        /* At least one of the objects failed or there wasn't any object in the
        update message. We will use FAILURETXT */
        /* replace the global variables in failuretxt */
        txt_replaced = UP_replace_globals(failuretxt);
	  }
	  else
	  {
        /* All the objects in the update message were successful. So, we will
        use SUCCESSTXT */
        /* replace the global variables in successtxt */
        txt_replaced = UP_replace_globals(successtxt);
	  }

      /* print out the success/failure/help txt */ 
      fprintf(temp_file_hdl, "To: %s\n%s\n\n", to_address, txt_replaced);
      free(txt_replaced);
      /* and now copy over the rest of the ack message */
      if (( ack_file = fopen(filename, "r")) == NULL)
	  {
        fprintf(stderr, "Can't open ack file for reading, %s", filename);
      }
	  else
	  {
        while (fgets(buf, 1024, ack_file) != NULL)
		{
          fprintf(temp_file_hdl, "%s", buf);
        }
        fclose(ack_file);
      }
      fclose(temp_file_hdl);
      /* and copy rename the temp file  */
      rename(temp_file, filename);
      free(temp_file);
    }

    /* if we are not supressing acks and notifs, send the ack */
    if (!supress_ack_notif)
	{
      if (to_address != NULL)
	  {
        mail_command_line = (char *)malloc(strlen(mailercommand) + strlen(to_address) 
            + strlen(filename) + 128);
        sprintf(mail_command_line, "%s %s < %s", mailercommand, to_address, filename);
        system(mail_command_line);
      }
    }
	else
	{
      /* if we are supressing acks and notifs, send ack to DEFMAIL  */
      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 ack file, %s", supress_file);
      }
	  else
	  {
        fprintf(supr_file_hdl, "From: %s\nTo: %s\nSubject: Supressed ack mail\n\n",
            humailbox, defmail);
        if (( ack_file = fopen(filename, "r")) == NULL)
		{
          fprintf(stderr, "Can't open ack file for reading, %s", filename);
        }
		else
		{
          while (fgets(buf, 1024, ack_file) != NULL)
		  {
            fprintf(supr_file_hdl, "%s", buf);
          }
          fclose(ack_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);
    }
}



/*

  AK_print_ack: Prints out the given file (the ack file) to the standard output

*/
void AK_print_ack( const char * filename )
{
    FILE * ack_file;
    char buf[1024];

	if (subject_result.result == UP_SUBJ_INVALID_COMB && webupdate)
	{
      if (( ack_file = fopen(filename, "a")) == NULL)
	  {
        fprintf(stderr, "Can't open ack file for appending, %s", filename);
      }
	  else
	  {
        fprintf(ack_file, "\nWarning: This combination of options of 'help' and 'new' is not allowed.\n"
                   "Thus, all options were ignored.\n");
        fclose(ack_file);
      }
    }

    /* if the update didn't contain any objects and was not a request for help, put a warning */
    if (count_successful == 0 && count_unsuccessful == 0 && subject_result.result != UP_SUBJ_HELP_REQ)
	{
      AK_add_to_ack(filename, "*** Warning: No objects were found in the message ***\n");
    }

    if (( ack_file = fopen(filename, "r")) == NULL)
	{
      fprintf(stderr, "Can't open ack file for reading, %s", filename);
    }
	else
	{
      while (fgets(buf, 1024, ack_file) != NULL)
	  {
        printf("%s", buf);
      }
      fclose(ack_file);
    }
}



/*

  AK_delete_ack: deletes the temporary acknowledgement file.

*/

void AK_delete_ack( const char * filename )
{
   unlink(filename);   
}


/*

AK_log_ack: logs the acknowledgements in the "logfilename.date".

*/

void AK_log_ack(const char * filename, const char * logfilename)
{
  FILE * ack_file, * log_file;
  char   buf[1024];
  time_t cur_time;
  char * time_str;
  char * logfile_date;
  char * date;

  if (( ack_file = fopen(filename, "r")) == NULL)
  {
    fprintf(stderr, "Can't open ack 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, "Can't open log file for appending, %s\n", logfile_date);
	free(logfile_date);
    return;
  }
  free(logfile_date);

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

  if (reading_from_mail)
  {
    fprintf(log_file, ">>> time: %s MAIL ACK <<<\n\n", time_str);
  }
  else if (networkupdate)
  {
    fprintf(log_file, ">>> time: %s NETWORKUPDATE (%s) ACK <<<\n\n", 
                  time_str, netupdclientIP ? netupdclientIP : "NULL" );
  }
  else if (webupdate)
  {
    fprintf(log_file, ">>> time: %s WEB UPDATE (%s) ACK <<<\n\n", 
                  time_str, netupdclientIP ? netupdclientIP : "NULL" );
  }
  else
  {
    fprintf(log_file, ">>> time: %s ACK <<<\n\n", time_str);
  }

  while (fgets(buf, 1023, ack_file) != NULL)
  {
    fprintf(log_file, "%s", buf);
  }

  free(time_str);
  fclose(ack_file);
  fclose(log_file);
}








