/*
   qfaxutil.c - Library of shared functions for Renaissoft QFax 1.0
   (c) 1994 Robert LeBlanc and Renaissoft
*/

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

char *getsline(FILE *p)
{
/*
   Gets a newline terminated line from a file, strips the newline and
   returns the remaining string.
*/

  char line[LINELEN];

  fgets(line, LINELEN, p);
  line[strlen(line)-1] = '\0';
  return(line);
}

char *getline(FILE *p)
{
/*
   Returns the next non-comment/non-blank line from a file.
*/

  char line[LINELEN];
  char test[LINELEN];
  int good = 0;

  while (!good) {
    strcpy(line, getsline(p));
    strcpy(test, "");
    sscanf(line, "%s", test);
    if ((test[0] != '#') && (test[0] != '\0'))
      good = 1;
  }
  return(line);
}

char *getfield(char *line)
{
/*
   Skips the first non-whitespace token in a string and any trailing
   whitespace until the next non-whitespace character, then returns the
   remainder of the line.  Useful for reading multi-word data fields
   from a string.
*/

  char field[LINELEN];
  int i = 0;
  int j = 0;

  strcpy(field, "");
  while((line[i] != ' ') && (line[i] != '\t')) i++;
  while((line[i] == ' ') || (line[i] == '\t')) i++;
  for (j=i-1; j < strlen(line); j++)
    field[j-(i-1)] = line[j+1];
  field[j-1] = '\0';
  return(field);
}

void mail_user(char *user, char *subject, char *message)
{
/*
   Sends a brief message to a (local) user by e-mail.
*/

  char tmp[LINELEN];

  sprintf(tmp, "echo \"%s\" | mail -s \"%s\" %s", message, subject, user);
  system(tmp);
}

char *psfix(char *str)
{
/*
   As '(' and ')' have to be escaped in PostScript, make sure we modify
   a string as necessary to return a "PostScript-safe" equivalent.
*/

  int i = 0;
  int j = 0; 
  char tmp[LONGLEN];

  while (i < strlen(str)) {
    if (str[i] == '(') {
      tmp[j++] = '\\';
      tmp[j++] = '(';
    } else if (str[i] == ')') {
      tmp[j++] = '\\';
      tmp[j++] = ')';
    } else {
      tmp[j++] = str[i];
    }
    i++;
  }
  tmp[j] = '\0';
  return (tmp);
}

void read_config(Fax *f)
{
/*
   Reads the Qfax configuration file (fax.rc) to determine some
   initial settings for such things as page size, fonts to use for
   the cover page, label text, and information about your company.
*/

  FILE *cf;
  char tmp[LINELEN];

  if ((cf = fopen(CONFIG, "r")) == NULL) {
    mail_user(f->fperson.username, "Qfax Error Message", "Can't read fax config file!");
    exit(EXIT_FAILURE);
  }

  /* company info */
  strcpy(f->fcompany.logoname, getline(cf));
  strcpy(f->fcompany.fullname, getline(cf));
  strcpy(f->fcompany.street, getline(cf));
  strcpy(f->fcompany.city, getline(cf));
  strcpy(f->fcompany.postcode, getline(cf));
  strcpy(f->fcompany.phone, getline(cf));
  strcpy(f->fcompany.fax, getline(cf));
  strcpy(f->fcompany.domain, getline(cf));
  
  /* e-mail info */
  strcpy(f->labels.headerstart, getline(cf));
  strcpy(f->labels.headerend, getline(cf));

  /* page size info */
  strcpy(tmp, getline(cf));
  sscanf(tmp, "%d%d", &(f->pwidth), &(f->pheight));

  /* font info */
  strcpy(tmp, getline(cf));
  sscanf(tmp, "%s%d", f->fonts.logo.name, &(f->fonts.logo.size));
  strcpy(tmp, getline(cf));
  sscanf(tmp, "%s%d", f->fonts.address.name, &(f->fonts.address.size));
  strcpy(tmp, getline(cf));
  sscanf(tmp, "%s%d", f->fonts.title.name, &(f->fonts.title.size));
  strcpy(tmp, getline(cf));
  sscanf(tmp, "%s%d", f->fonts.main.name, &(f->fonts.main.size));
  strcpy(tmp, getline(cf));
  sscanf(tmp, "%s%d", f->fonts.label.name, &(f->fonts.label.size));

  /* label info */
  strcpy(f->labels.title, getline(cf));
  strcpy(f->labels.tocompany, getline(cf));
  strcpy(f->labels.to, getline(cf));
  strcpy(f->labels.tovoice, getline(cf));
  strcpy(f->labels.tofax, getline(cf));
  strcpy(f->labels.from, getline(cf));
  strcpy(f->labels.email, getline(cf));
  strcpy(f->labels.date, getline(cf));
  strcpy(f->labels.pages, getline(cf));
  strcpy(f->labels.regarding, getline(cf));
  strcpy(f->labels.comments, getline(cf));

  fclose(cf);
}

int read_db(Fax *f, char *dbfile)
{
/*
   Scans the given fax database file (~/.fax or fax.db) to try to find a
   match for the recipient and his/her company.  As an example, mail to
   "joe@megacorp.fax" would first cause the program to look up the alias
   "megacorp" in its database.  If found, the program then checks the
   list of known users at megacorp, looking specifically for the alias
   "joe".  If it finds a match in the database, it knows then that "joe"
   is "Joseph P. Smith", "megacorp" is "MegaCorp International", and
   voice and fax numbers are read.

   If no match is found, the routine returns an error code, which just
   means the recipient wasn't found in THIS database file.
*/

  FILE *db;
  char tmp[LINELEN];
  char header[LINELEN];
  int done = 0;

  if ((db = fopen(dbfile, "r")) == NULL) {
    return(1);
  }
  strcpy(tmp, getsline(db));
  while (!done && !feof(db)) {
    sscanf(tmp, "%s", header);
    if (strcasecmp(header, f->tcompany.alias) == 0) {
      strcpy(f->tcompany.fullname, getfield(tmp));
      strcpy(f->tcompany.phone, getsline(db));
      strcpy(f->tcompany.fax, getsline(db));
      strcpy(tmp, getsline(db));
      while (!done && !feof(db)) {
	sscanf(tmp, "%s", header);
	if (strcasecmp(header, f->tperson.alias) == 0) {
	  strcpy(f->tperson.fullname, getfield(tmp));
	  done = 1;
	} else if (tmp[0] == '+') {
	  return(1);
	}
	if (!done) 
	  strcpy(tmp, getsline(db));
      }
    } else {
      while (strcasecmp(getsline(db), "+") != 0)
	;
    }
    if (!done)
      strcpy(tmp, getsline(db));
  }
  fclose(db);
  if (!done) {
    return(1);
  }
  return(0);
}

void make_cover(Fax *f)
{
/*
   The template cover page (cover-template.ps) has a number of variables
   hardwired into it, but these values are uninitialized.  This routine
   makes a copy of the template page, inserting the necessary values into
   the copy so that it can be made into a proper PostScript program, for
   easy conversion to G3 format by Efax.

   The output filename for the copy of the cover page is of the form:

               FAXQUEUE/faxcover.user.timestamp.ps

	       e.g. /usr/spool/fax/sendq/faxcover.tom.26Aug230715.ps

   The timestamp information is identical to the value used to stamp the
   associated fax file.

   The resulting *.ps file is Adobe PS 2.0 compatible, and can be used
   for any conceivable purpose, but its main purpose is to serve as a fax
   cover page for one specific fax message.  "fax make" is called again to
   convert this PostScript file into a G3 fax.
*/

  FILE *ifp;
  FILE *of;
  char tmp[LINELEN];
  char t1[LINELEN], t2[LINELEN], t3[LINELEN];

  ifp = fopen(TEMPLATE, "r");
  sprintf(tmp, "%s/faxcover.%s.%s.ps", FAXQUEUE, f->fperson.username,
	  f->tstamp);
  of = fopen(tmp, "w");
  fgets(tmp, LINELEN, ifp);
  while (!feof(ifp)) {
    if (strstr(tmp, "% *** Renaissoft QFAX") == NULL) {
      fputs(tmp, of);
    } else {
      fputs(tmp, of);
      fprintf(of, "\n");
      fprintf(of, "%% Page size definitions\n");
      fprintf(of, "/PWidth %d def\n", f->pwidth);
      fprintf(of, "/PHeight %d def\n", f->pheight);
      fprintf(of, "\n");
      fprintf(of, "%% Font definitions\n");
      fprintf(of, "/LogoFont /%s def\n", f->fonts.logo.name);
      fprintf(of, "/LogoFontSize %d def\n", f->fonts.logo.size);
      fprintf(of, "/AddressFont /%s def\n", f->fonts.address.name);
      fprintf(of, "/AddressFontSize %d def\n", f->fonts.address.size);
      fprintf(of, "/TitleFont /%s def\n", f->fonts.title.name);
      fprintf(of, "/TitleFontSize %d def\n", f->fonts.title.size);
      fprintf(of, "/MainLabelFont /%s def\n", f->fonts.main.name);
      fprintf(of, "/MainLabelFontSize %d def\n", f->fonts.main.size);
      fprintf(of, "/LabelFont /%s def\n", f->fonts.label.name);
      fprintf(of, "/LabelFontSize %d def\n", f->fonts.label.size);
      fprintf(of, "\n");
      fprintf(of, "%% Label definitions\n");
      fprintf(of, "/label-title (%s) def\n",
	      psfix(f->labels.title));
      fprintf(of, "/label-to-company (%s) def\n",
	      psfix(f->labels.tocompany));
      fprintf(of, "/label-to (%s) def\n",
	      psfix(f->labels.to));
      fprintf(of, "/label-to-voice-number (%s) def\n",
	      psfix(f->labels.tovoice));
      fprintf(of, "/label-to-fax-number (%s) def\n",
	      psfix(f->labels.tofax));
      fprintf(of, "/label-from (%s) def\n",
	      psfix(f->labels.from));
      fprintf(of, "/label-from-email (%s) def\n",
	      psfix(f->labels.email));
      fprintf(of, "/label-todays-date (%s) def\n",
	      psfix(f->labels.date));
      fprintf(of, "/label-page-count (%s) def\n",
	      psfix(f->labels.pages));
      fprintf(of, "/label-regarding (%s) def\n",
	      psfix(f->labels.regarding));
      fprintf(of, "/label-comments (%s) def\n",
	      psfix(f->labels.comments));
      fprintf(of, "\n");
      fprintf(of, "%% Variable definitions\n");
      fprintf(of, "/from-company (%s) def\n",
	      psfix(f->fcompany.logoname));
      fprintf(of, "/from-company-full-name (%s) def\n",
	      psfix(f->fcompany.fullname));
      strcpy(t1, psfix(f->fcompany.street));
      strcpy(t2, psfix(f->fcompany.city));
      strcpy(t3, psfix(f->fcompany.postcode));
      fprintf(of, "/from-company-address (%s \\267 %s \\267 %s) def\n",
	      t1, t2, t3);
      strcpy(t1, psfix(f->fcompany.phone));
      strcpy(t2, psfix(f->fcompany.fax));
      fprintf(of, "/from-company-phones (Tel: %s \\267 Fax: %s) def\n",
	      t1, t2);
      fprintf(of, "/to-company (%s) def\n",
	      psfix(f->tcompany.fullname));
      fprintf(of, "/to (%s) def\n",
	      psfix(f->tperson.fullname));
      fprintf(of, "/to-voice-number (%s) def\n",
	      psfix(f->tcompany.phone));
      fprintf(of, "/to-fax-number (%s) def\n",
	      psfix(f->tcompany.fax));
      fprintf(of, "/from (%s) def\n",
	      psfix(f->fperson.fullname));
      fprintf(of, "/from-email (%s@%s) def\n",
	      f->fperson.username, f->fcompany.domain);
      fprintf(of, "/todays-date (%s) def\n",
	      psfix(f->date));
      fprintf(of, "/page-count (%d) def\n",
	      f->pages);
      fprintf(of, "/regarding (%s) def\n",
	      psfix(f->subject));
      fprintf(of, "/comments (%s) def\n",
	      psfix(f->comments));
      fprintf(of, "\n");
    }
    fgets(tmp, LINELEN, ifp);
  }
  fclose(of);
  fclose(ifp);
  sprintf(tmp, "%s make %s/faxcover.%s.%s.ps > /dev/null", FAX, FAXQUEUE,
	  f->fperson.username, f->tstamp);
  system(tmp);
}

void insert_cover(Fax *f)
{
/*
   This routine inserts the cover page as page 1 of the fax collection.
   Since Efax numbers its files by page number (e.g. *.001, *.002, etc.),
   this involves renaming the existing files to increment their page
   number by one, then renaming the cover page to *.001.
*/

  int i;
  char file1[LONGLEN], file2[LONGLEN];
  char cmd[LINELEN];

  for (i = f->pages; i > 0; i--) {
    sprintf(file1, "%s/fax.%s.%s.%03d", FAXQUEUE, f->fperson.username,
	    f->tstamp, i);
    if (fopen(file1, "r") != NULL) {
      sprintf(file2, "%s/fax.%s.%s.%03d", FAXQUEUE, f->fperson.username,
	      f->tstamp, i+1);
      sprintf(cmd, "mv %s %s", file1, file2);
      system(cmd);
    }
  }
  sprintf(cmd, "mv %s/faxcover.%s.%s.ps.001 %s/fax.%s.%s.001",
	  FAXQUEUE, f->fperson.username, f->tstamp, FAXQUEUE,
	  f->fperson.username, f->tstamp);
  system(cmd);
}

void queue_fax(Fax *f)
{
/*
   Once the fax pages have been made and the cover page inserted, the
   next step is to create a little "command file" similar to a UUCP 'X'
   file.  Our convention is to name this file by this format:

                FAXQUEUE/fax.user.timestamp.cmd

		e.g. /usr/spool/fax/sendq/fax.tom.26Aug230715.cmd

   We write to this file the phone number to be dialed, the name of
   the recipient and the subject of the message.  When the Qrun script
   is invoked (presumably by cron or something similar), it reads this file
   to determine what number to dial to send the fax.  The recipient and
   subject information is used to notify the sender by e-mail about the
   status of his/her fax.
*/

  int i = 0;
  int j = 0;
  char tmp[LONGLEN];
  char dialstr[LONGLEN];
  FILE *of;

  strcpy(dialstr, "");
  for (i=0; i < strlen(f->tcompany.fax); i++) {
    if ((f->tcompany.fax[i] >= '0') && (f->tcompany.fax[i] <= '9'))
      dialstr[j++] = f->tcompany.fax[i];
  }
  dialstr[j] = '\0';
  sprintf(tmp, "%s/fax.%s.%s.cmd", FAXQUEUE, f->fperson.username, f->tstamp);
  of = fopen(tmp, "w");
  fputs(dialstr, of);
  fputs("\n", of);
  fputs(f->tperson.fullname, of);
  fputs("\n", of);
  fputs(f->subject, of);
  fputs("\n", of);
  fclose(of);
}

void cleanup(Fax *f)
{
/*
   Delete any intermediate files that were generated by Qfax and Efax,
   leaving behind only the fax pages (including the cover page) and the
   command file.
*/

  char cmd[LINELEN];

  sprintf(cmd, "rm -f %s/fax.%s.%s %s/faxcover.%s.%s* %s/tmp.%s.%s",
	  FAXQUEUE, f->fperson.username, f->tstamp,
	  FAXQUEUE, f->fperson.username, f->tstamp,
	  FAXQUEUE, f->fperson.username, f->tstamp);
  system(cmd);
}

char *timestring(void)
{
/*
   Returns the system time/date from the "date" command as a string.
*/

  char t[SHORTLEN];
  FILE *opp;

  opp = popen("date", "r");
  strcpy(t, getsline(opp));
  pclose(opp);
  return(t);
}

int make_cmdlist(char cmdlist[MAXFAXES][LINELEN])
{
/*
   Creates a list of all the *.cmd files in the FAXQUEUE directory,
   returning the number of files found and implicitly returning the
   array of filenames.  Since each of these files represents one
   fax transmission, we can use this to determine how many sends to
   set up.
*/

  char cmd[LINELEN];
  FILE *ls;
  int i;

  freopen("/dev/null", "w", stderr);
  setbuf(stderr, NULL);
  sprintf(cmd, "%s -1 %s/fax.*.cmd", LS, FAXQUEUE);
  ls = popen(cmd, "r");
  i = 0;
  while (fscanf(ls, "%s", cmdlist[i]) != EOF)
    i++;
  pclose(ls);
  freopen("/dev/console", "w", stderr);
  return(i);
}

char *make_prefix(char *filename)
{
/*
   Extracts the prefix from a filename, specifically everything upto
   (but not including) the last '.'.  This strips away any ".cmd" or
   ".001" extender, so that what we're left with is common to all
   files that are part of this fax.
*/

  char tmp[LINELEN];
  char *ptr;

  strcpy(tmp, filename);
  ptr = strrchr(tmp, '.');
  *ptr = '\0';
  return(tmp);
}

void get_cmdinfo(char *cmdfile, char *dialstring, char *recipient,
		 char *subject)
{
/*
   Reads a *.cmd file and extracts the phone number to dial, the name
   of the recipient, and the subject of the fax.
*/

  FILE *ifp;

  ifp = fopen(cmdfile, "r");
  strcpy(dialstring, getline(ifp));
  strcpy(recipient, getline(ifp));
  strcpy(subject, getline(ifp));
  fclose(ifp);
}

void delete_fax(char *prefix)
{
/*
   Deletes all files relating to a given fax, presumably to be done only
   when the fax has been successfully sent and is no longer needed.  By
   deleting it from the spool directory, we prevent it from being sent
   again the next time Qrun is invoked.
*/

  char cmd[LINELEN];

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

char *get_sender(char *prefix)
{
/*
   Extracts the username of the sender of a fax message from the fax prefix.
*/

  char tmp[LINELEN];
  char sender[ALIASLEN];
  char *ptr;

  strcpy(tmp, prefix);
  ptr = strrchr(tmp, '.');
  *ptr = '\0';
  ptr = strrchr(tmp, '.');
  strcpy(sender, (++ptr));
  return(sender);
}

void shutdown(Fax *f)
{
/*
   Exit gracefully, cleaning up any messes we might have left behind
   when an error occurred.
*/

  char cmd[LINELEN];

  sprintf(cmd, "rm -f %s/fax.%s.%s*", FAXQUEUE, f->fperson.username,
	  f->tstamp);
  system(cmd);
  exit(EXIT_FAILURE);
}

void lookup_db(Fax *f)
{
/*
   Tries first to look up the recipient's name and company in the
   "~/.fax" file, if it exists, then looks in the central "fax.db"
   file if necessary.  If it fails completely, the user gets a
   piece of e-mail about the problem.
*/

  char msg[LINELEN];
  char homedb[LONGLEN];
  char user[ALIASLEN];
  
  strcpy(user, f->fperson.username);
  if (strcasecmp(user, "root") == 0)
    strcpy(homedb, "/.fax");
  else
    sprintf(homedb, "/home/%s/.fax", user);
  if (read_db(f, homedb)) {
    if (read_db(f, DATABASE)) {
      sprintf(msg, "Can't find '%s' at '%s'!",
	      f->tperson.alias, f->tcompany.alias);
      mail_user(f->fperson.username, "Qfax Error Message", msg);
      shutdown(f);
    }
  }
}

char *time_stamp(void)
{
/*
   Returns the current time and date in the form "ddmonhhmmss",
   e.g. "26Aug231605", for use as a readable time stamp.
*/

  char tmp[SHORTLEN];
  time_t now;

  now = time(NULL);
  strftime(tmp, SHORTLEN, "%d%b%H%M%S", localtime(&now));
  return (tmp);
}
