/*
 * qrun.c - Fax spool processor for Renaissoft Qfax 1.0
 * (c) 1994 Robert LeBlanc and Renaissoft
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include "qfax.h"

/* Non-exported function prototypes */

void kill_daemon(void);
void start_daemon(void);
int send_fax(char *dialstring, char *filelist);
char *error_message(int errcode);
void notify_sender(int errcode, char *prefix, char *recipient, char *subject);
int ok_to_call(char *dialstring);
char *make_pagelist(char *prefix);
void store_fax(char *prefix);


char *make_pagelist(char *prefix)
{
/*
   Scans the FAXQUEUE directory for all pages with the same prefix, in
   order to assemble a whitespace-separated list of files to be sent.
*/

  char list[COMMENTLEN];
  char cmd[LINELEN];
  char tmp[LINELEN];
  FILE *ls;

  sprintf(cmd, "%s -1 %s.[0-9][0-9][0-9]", LS, prefix);
  strcpy(list, "");
  ls = popen(cmd, "r");
  while (fscanf(ls, "%s", tmp) != EOF) {
    strcat(list, " ");
    strcat(list, tmp);
  }
  pclose(ls);
  return(list);
}

void store_fax(char *prefix)
{
/*
   Move all files relating to a given fax to STOREDIR, where they can sit
   indefinitely until the owner decides what to do with them.  By moving
   them out of the spool directory, we prevent Qrun from trying to send
   them out again the next time it's invoked.  Presumably this is done
   only once the maximum number of attempts has been made without success.
*/

  char cmd[LINELEN];

  sprintf(cmd, "rm -f %s.try", prefix);
  system(cmd);
  sprintf(cmd, "mv %s.* %s", prefix, STOREDIR);
  system(cmd);
}

int ok_to_call(char *dialstring)
{
/*
   Returns true if it's ok to place the call at this time.  The idea is to
   make sure that long-distance faxes only get sent out between their
   designated hours.
*/

  int result;
  int hstart, hend, mstart, mend, hnow, mnow;
  int vstart, vend;
  char *ptr;
  char timerange[10];
  char tstart[10];
  char tstop[10];
  struct tm  *tp;
  time_t now;

  if ((dialstring[0] == '1') || (dialstring[0] == '0')) {
    strcpy(timerange, LDPERIOD);
    ptr = strchr(timerange, '-');
    *ptr = '\0';
    strcpy(tstart, timerange);
    strcpy(tstop, (++ptr));
    if (strcasecmp(tstart, tstop) == 0) {
      result = 1;
    } else {
      now = time(NULL);
      tp = localtime(&now);
      hnow = tp->tm_hour;
      mnow = tp->tm_min;
      vstart = atoi(tstart);
      vend = atoi(tstop);
      hstart = (vstart / 100);
      if (hstart == 24)
	hstart = 0;
      mstart = (vstart % 100);
      hend = (vend / 100);
      if (hend == 24)
	hend = 0;
      mend = (vend % 100);
      if (hend < hstart) {
	if ((hnow > hstart) || (hnow < hend)) {
	  result = 1;
	} else if ((hnow == hstart) && (mnow >= mstart)) {
	  result = 1;
	} else {
	  result = 0;
	}
      } else if (hend > hstart) {
	if ((hnow > hstart) && (hnow < hend)) {
	  result = 1;
	} else if ((hnow == hstart) && (mnow >= mstart)) {
	  result = 1;
	} else {
	  result = 0;
	}
      } else {
	if ((mnow >= mstart) && (mnow < mend))
	  result = 1;
	else
	  result = 0;
      }
    }
  } else {
    result = 1;
  }
  return (result);
}

void kill_daemon(void)
{
/*
   Kills the most recent active fax answer process and writes a .stop
   file to prevent init() from respawning another one until further
   notice.  This is useful to avoid interruptions from incoming calls
   while we're trying to dial out.
*/

  char cmd[LINELEN];

  sprintf(cmd, "%s stop > /dev/null", FAX);
  system(cmd);
  sleep(2);
}

void start_daemon(void)
{
/*
   Removes any .stop files that might be preventing init() from spawning
   a new fax answer process.  We use this once we're done sending out
   faxes, so that we can continue to receive incoming calls.

   NOTE: You MUST have an entry in your inittab file running "fax answer",
   since this routine does NOT actually restart Efax.
*/

  char cmd[LINELEN];

  sprintf(cmd, "%s start", FAX);
  system(cmd);
}

int send_fax(char *dialstring, char *pagelist)
{
/*
   Calls "send fax" with the given phone number and page list to
   send.  Since Efax does its own error-trapping, we need to modify
   the FAX script to write the resulting error code to a file called
   RESULT just before it terminates, so that we can read and return
   this value for our purposes.
*/

  char cmd[LINELEN];
  int errcode;
  FILE *ifp;

  sprintf(cmd, "%s send %s %s > /dev/null", FAX, dialstring, pagelist);
  system(cmd);
  ifp = fopen(RESULT, "r");
  fscanf(ifp, "%d", &errcode);
  fclose(ifp);
  return (errcode);
}

char *error_message(int errcode)
{
/*
   Returns an error message based on an Efax error code.  These messages
   and codes come from the man page for Efax, and are subject to change.
*/
  char message[255];

  switch(errcode) {
  case 0:
    strcpy(message, "Fax was successfully transmitted.");
    break;
  case 1:
    strcpy(message, "Modem was busy (device in use), retrying later.");
    break;
  case 2:
    strcpy(message, "General error occurred (file not found, disk full, etc.).");
    break;
  case 3:
    strcpy(message, "Modem protocol error occurred.");
    break;
  case 4:
    strcpy(message, "Modem was not responding.");
    break;
  case 5:
    strcpy(message, "Efax was terminated by a signal.");
    break;
  default:
    strcpy(message, "An unknown error has occurred.  Please send in a bug report.");
  }
  return(message);
}

void notify_sender(int errcode, char *prefix, char *recipient, char *subject)
{
/*
   Sends the (local) sender of the fax an e-mail message notifying him that
   an attempt to send the fax was made, and the results of this attempt.
   The time in the e-mail header should serve as a timestamp record of when
   the fax was last attempted, and the recipient's name and the subject of
   the fax are quoted for reference.
*/

  char cmd[LINELEN];
  char tmp[LINELEN];
  char sender[ALIASLEN];
  char letter[COMMENTLEN];
  char timestr[SHORTLEN];
  char *ptr;
  FILE *ifp;
  int code;

  strcpy(tmp, prefix);
  ptr = strrchr(tmp, '.');
  *ptr = '\0';
  ptr = strrchr(tmp, '.');
  strcpy(sender, (++ptr));
  strcpy(letter, "An attempt to send your fax\n\n");
  sprintf(tmp, ">>>      To: %s\n", recipient);
  strcat(letter, tmp);
  sprintf(tmp, ">>> Subject: %s\n\n", subject);
  strcat(letter, tmp);
  strcat(letter, "was made, with the following results:\n\n");
  sprintf(tmp, "%s.try", prefix);
  if ((ifp = fopen(tmp, "r")) != NULL) {
    while (fscanf(ifp, "%d\n", &code) != EOF) {
      strcpy(timestr, getsline(ifp));
      sprintf(tmp, "%s: [%d] %s\n", timestr, code, error_message(code));
      strcat(letter, tmp);
    }
    fclose(ifp);
  }
  strcpy(timestr, timestring());
  sprintf(tmp, "%s: [%d] %s\n", timestr, errcode, error_message(errcode));
  strcat(letter, tmp);
  if (errcode > 0) {
    strcpy(cmd, prefix);
    ptr = strrchr(cmd, '/');
    strcpy(cmd, (++ptr));
    sprintf(tmp, "\nYour fax, %s.*,has been moved to %s.\n",
	    cmd, STOREDIR);
    strcat(letter, tmp);
    sprintf(tmp, "To send it again, move these files back to %s.\n", FAXQUEUE);
    strcat(letter, tmp);
  }
  sprintf(cmd, "echo \"%s\" | mail -s \"Fax Transmission Notification\" %s",
	  letter, sender);
  system(cmd);
}

void main(void)
{
  int i, faxes, errcode, code, tries;
  char cmdlist[MAXFAXES][LINELEN];
  char dialstring[PHONELEN];
  char recipient[LONGLEN];
  char subject[LINELEN];
  char prefix[LINELEN];
  char timestr[SHORTLEN];
  char tmp[LINELEN];
  FILE *ifp, *ofp;

  /*
     Assemble a list of the faxes waiting in FAXQUEUE to go out.
  */

  faxes = make_cmdlist(cmdlist);
  if (!faxes)

  /*
     No faxes waiting to be sent, so we have nothing to do.
   */

    exit(EXIT_SUCCESS);

  /*
     Kill any existing fax answer daemon, just to be sure that we aren't
     interrupted before we seize the line.
   */

  kill_daemon();
  for (i=0; i < faxes; i++) {
    get_cmdinfo(cmdlist[i], dialstring, recipient, subject);
    if (ok_to_call(dialstring)) {
      strcpy(prefix, make_prefix(cmdlist[i]));
      errcode = send_fax(dialstring, make_pagelist(prefix));
      if (!errcode) {

        /* No errors, delete the fax and notify the sender */

        notify_sender(errcode, prefix, recipient, subject);
        delete_fax(prefix);
      } else {

        /* 
	   An error occurred, so let's see whether we should
	   try again or give up.
         */

        sprintf(tmp, "%s.try", prefix);
        if ((ifp = fopen(tmp, "r")) != NULL) {

	  /* We've tried to send this fax before */

	  tries = 0;
	  while (!feof(ifp)) {
	    fscanf(ifp, "%d\n", &code);
	    fgets(timestr, SHORTLEN, ifp);
	    tries++;
          }
	  fclose(ifp);
	  if (tries >= MAXTRIES) {

	    /*
	       We've reached our limit; give up, store the fax and
	       notify the sender.
	     */

	    notify_sender(errcode, prefix, recipient, subject);
	    store_fax(prefix);
	  } else {

	    /*
	       Try again next time Qrun is invoked, but make a note
	       of the fact that we tried, writing the errorcode and
	       the time/date to the *.try file.
	     */

	    ofp = fopen(tmp, "a");
	    strcpy(timestr, timestring());
	    fprintf(ofp, "%d\n%s\n", errcode, timestr);
	    fclose(ofp);
	  }
        } else {

	  /*
	     We've never tried to send this fax before, so we make a
	     record of the fact that we've failed once now.
	   */

	  ofp = fopen(tmp, "a");
	  strcpy(timestr, timestring());
	  fprintf(ofp, "%d\n%s\n", errcode, timestr);
	  fclose(ofp);
        }
      }
      sleep(15);
    }
  }

  /* 
     Restart the fax answer daemon so we can continue to receive
     incoming faxes, now that we're done sending.
   */

  start_daemon();
  exit(EXIT_SUCCESS);
}
