/* 
 * $Id: notify.c,v 1.35 1999/03/20 21:56:59 gerald Exp $
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <irr_notify.h>

extern char *test_addr;

/* '\0' terminated set of email addrs.
 * Theses addrs are used to notify users
 * that authorised changes have occured
 * to their objects.
 */
char notify_addrs[MAX_ADDR_SIZE];
char *nnext;
int nndx[MAX_NDX_SIZE];
int num_notify;

/* '\0' terminated set of email addrs.
 * These addrs are used to notify users of
 * unauthorised updates.
 */
char forward_addrs[MAX_ADDR_SIZE];
char *fnext;
int fndx[MAX_NDX_SIZE];
int num_forward;

/* For uniform treatment, even though
 * there is only one sender, make it
 * like a forward_addr or notify_addr case
 */
char sender_addrs[256];
char *snext;
int sndx[1];
int num_sender;

char buf[MAXLINE];
/* local yokel's */
void perform_transactions (trace_t *tr, FILE *, ret_info_t *, int, char *, int);
int parse_transaction_file (trace_t *tr, char *, FILE *, ret_info_t *, char *, int, int);
void remove_tempfiles (trace_t *tr);

/* Control the action of performing the udpates and sending
 * out notifications.  Briefly, for auth failures all referenced
 * upd_to: fields will be notified.  For error-free updates (ie,
 * no syntax and no auth errors) notifications will be sent to
 * all referenced notify: and mnt_nfy: field addresses.  The 
 * sender is always notified unless one of the flags is set
 * (see below).
 *
 * notify () will send the updates to IRRd.  If IRRd encounters
 * an error and/or cannot apply the update the IRRd error message 
 * will be relayed in the notification.
 *
 * 'sender_only' = 1 causes notify () to only send notifications to 
 * the sender.  'null_notification' = 1 causes notify () to skip
 * all notifications.  In this case, notify () will send the
 * updates to IRRd but no notifications will be sent. '*tmpfname'
 * is the file template for 'mktemp ()' to use to build
 * the notification files.  There will be one notification file
 * per unique notify/updto email address.
 *
 * Return:
 *   void
 *   Results of the updates are included in the notifications.
 */
void notify (trace_t *tr, char *tmpfname, FILE *fin, 
	     int null_submission, int null_notification, int dump_stdout,
	     char *IRRd_HOST, int IRRd_PORT, char *db_admin, FILE *ack_fd) {
  ret_info_t rstart;

  /*fprintf (dfile, "Enter notify()\n");*/

  rstart.first = rstart.last = NULL;
  perform_transactions (tr, fin, &rstart, null_submission, IRRd_HOST, IRRd_PORT);

  /* just to be safe rewind */
  if (fseek (fin, 0L, SEEK_SET) != 0) {
    fprintf (stderr, "ERROR: cannot rewind input file, exit!\n");
    exit (0);
  }

  /* init global data structures */
  nnext = notify_addrs;
  fnext = forward_addrs;
  snext = sender_addrs;
  num_notify = num_forward = num_sender = 0;
  /* pointer to array of file names and handles to 
   * notify/forward mail replies.
   */
  num_hdls = 0; 

  parse_transaction_file (tr, tmpfname, fin, &rstart, db_admin, 
			  null_notification, dump_stdout);
  send_notifies (tr, null_notification, ack_fd);
  remove_tempfiles (tr);

  /*fprintf (dfile, "Exit notify ()\n");*/
}



int parse_transaction_file (trace_t *tr, char *tmpfname, FILE *fin, ret_info_t *start,
			    char *db_admin, int null_notification, int dump_stdout) {
  trans_info_t trans_info;
  char buf[MAXLINE];
  irrd_result_t *p = start->first;
  long offset = 0;

  while (fgets (buf, MAXLINE - 1, fin) != NULL) {
    if (strncmp (HDR_START, buf, strlen (HDR_START) - 1))
      continue;
    
    /* fprintf (dfile, "found a start header %s strlen-(%d)\n", buf, strlen (buf));*/
    
    if (parse_header (tr, fin, &offset, &trans_info))
      return 0; /* illegal hdr field found or EOF 
		 * (ie, no object after hdr) 
		 */

    /* Make sure sender is not getting duplicate notifications */
    if (trans_info.hdr_fields & FROM_F)
      remove_sender (trans_info.sender_addrs, trans_info.notify_addrs,
		     &(trans_info.nnext));
    else if (dump_stdout) { /* We have a tcp user, ie, an IRRj user */
      trans_info.hdr_fields |= FROM_F;
      strcpy (trans_info.sender_addrs, TCP_USER);
      trans_info.snext += strlen (trans_info.sender_addrs) + 1;
    }

    /* JW this is for debug only */
    /* print_hdr_struct (dfile, &trans_info); */
    chk_email_fields (&trans_info);
    if (!(trans_info.hdr_fields & OP_F))
      trans_info.op = strdup ("UPDATE");

    if (!(trans_info.hdr_fields & OBJ_KEY_F))
      trans_info.obj_key = strdup ("");

    if (!(trans_info.hdr_fields & OBJ_TYPE_F))
      trans_info.obj_type = strdup ("");

    if (chk_hdr_flds (trans_info.hdr_fields)) {
      build_notify_responses (tr, tmpfname, fin, &trans_info, p, db_admin, 
			      MAX_IRRD_OBJ_SIZE, null_notification);
      if ((trans_info.hdr_fields & OLD_OBJ_FILE_F) &&
	  (*trans_info.old_obj_fname < '0' || *trans_info.old_obj_fname > '9'))
	unlink (trans_info.old_obj_fname);
    }
    /*else
      fprintf (dfile, "nofify () bad hdr file found, skipping...\n");*/

    free_ti_mem (&trans_info);
    p = p->next;
  }

  return 1;
}


/* Close and remove all the notification temp files.
 *
 * Return:
 *  void
 */
void remove_tempfiles (trace_t *tr) {
  int i;

  for (i = 0; i < num_hdls; i++) {
    fclose (msg_hdl[i].fp);
    remove (msg_hdl[i].fname);
  }
}

void perform_transactions (trace_t *tr, FILE *fin, ret_info_t *start,
			   int null_submission, char *IRRd_HOST, int IRRd_PORT) {
  char buf[MAXLINE], *ret_code;
  int fd, num_trans = 0;
  int open_conn = 0, abort_trans = 0;
  long offset;
  trans_info_t ti;
  irrd_result_t *p;
  
  /* fprintf (dfile, "\n----\nEnter perform_transactions()\n");*/

  /* rewind */
  fseek (fin, 0L, SEEK_SET);

  while (fgets (buf, MAXLINE - 1, fin) != NULL) {
    if (strncmp (HDR_START, buf, strlen (HDR_START) - 1))
      continue;
    /* JW commented out to dup rawhoisd 
       if (!abort_trans)
       */
    offset = ftell (fin);
    /* fprintf (dfile, "HDR_START offset (%ld)\n", offset);*/
    /* illegal hdr field found or EOF or no object after hdr */
    if (parse_header (tr, fin, &offset, &ti)) {
      abort_trans = 1;
      /* fprintf (dfile, "calling update_trans_outcome_list (internal error)...\n"); */
      update_trans_outcome_list (tr, start, &ti, offset, INTERNAL_ERROR_RESULT,
				 "\" Internal error: malformed header!\"\n");
      free_ti_mem (&ti);
      continue;
    }
    else if (update_has_errors (&ti))
      abort_trans = 1;
    else if (null_submission == 0) {
      update_trans_outcome_list (tr, start, &ti, offset, NULL_SUBMISSION, NULL);
      continue;
    }

    update_trans_outcome_list (tr, start, &ti, offset, 0, NULL);
    free_ti_mem (&ti);
  }

  /* JW commented out to dup rawhoisd 
   * want to bring back for transaction semantic support
  if (abort_trans)
    reinit_return_list (dfile, start, SKIP_RESULT);
  else {
  */
    for (p = start->first; p != NULL; p = p->next) {
      /* JW want to bring back in later, dup rawhoisd
      if (p->svr_res & NOOP_RESULT)
	continue;
	*/
      /* JW take this section out to reverse rawhoisd behavior */
      if (p->svr_res & NOOP_RESULT           ||
	  p->svr_res & USER_ERROR            ||
	  p->svr_res & INTERNAL_ERROR_RESULT ||
	  p->svr_res & NULL_SUBMISSION) {
	trace (NORM, tr, "Object aborted!\n");
	continue;
      }

      if (EOF == fseek (fin, p->offset, SEEK_SET))
	  fprintf (stderr, "ERROR: fseek (%ld)\n", p->offset);
      else {
	fgets (buf, MAXLINE - 1, fin);
	/*fprintf (dfile, "irrd_trans () line: %s", buf);*/
	fseek (fin, p->offset, SEEK_SET);
      }
      /* fprintf (dfile, "perform_trans () calling irrd_transaction ()...\n");*/
      ret_code = irrd_transaction (tr, (char *) WARN_TAG, &fd, fin, p->op, 
				   p->source, ++num_trans, &open_conn, 
				   IRRd_HOST, IRRd_PORT);
      /* fprintf (dfile, "perform_trans () back from irrd_transaction () ret_code (%s)\n", ret_code);*/
      put_transaction_code (tr, p, ret_code);
      /* JW take out
      free (ret_code);
      */
    }

    if (open_conn) {
      end_irrd_session (tr, fd); /* send '!q' */
      fflush (fin);   /* JW only needed when irrd commands are sent to terminal */
      close_connection (fd);
    }
  /* JW want to bring back in later, dup rawhoisd
  }
  */
    /* fprintf (dfile, "Exit perform_transactions()\n----\n");*/
}
