/*
 * $Header: NetWork:src/RCS/xfreqsh.c,v 1.2 93/01/29 15:06:39 rwm Exp Locker: rwm $
 *
 *    This is a simple program that would be ran from a mailer on receipt of
 *  an inbound file.  This program attempts to take a list of patterns and
 *  match them against the filename of the inbound file, and will run the
 *  given command.  A few special modes exist for handling the specifics of
 *  a file request in three ways:
 *    a) Built in simple file request handler
 *    b) New 'pipe based' request handler for asynch capable file request
 *       handlers.
 *    c) Temporary file based .RLO handling for non-asynch requestors
 *       (IE: Emulate the old method used by Trapdoor and Paragon)
 *
 *  The program makes use of two external files.  The first one is it's 
 *  configuration file which looks like:

#?.req #network:list/okfile.lst network:list/FailREQ.Txt
#? filenote %R "File:%r From:%a(%z:%N/%n.%p)"
#?.RE1 run echo >%o "Welmat:wnotify.cfg"
#?.RE2 echo >%O "Welmat:wnotify.cfg"
#?.pkt run sys:rexxc/rx "address 'ConfMail' 'TOSSPKT %R'"
#?.(MO?|TU?|WE?|TH?|FR?|SA?|SU?) run sys:rexxc/rx "address 'ConfMail' 'TOSSARC %R'"
#?.#?  echo >>t:Dotted.txt "Received Dotted file named %r as %R from %a on line %l"
; This is a comment

 *  It is in the form of a 'filename pattern' and then a 'command line to run'.
 *  The use of the '#' symbol as the first character of the command to run
 *  indicates the use of the built in file request handler.
 *
 *  The second type of file this program uses is the 'filename pathname'
 *  file used by the built-in request handler.  I myself use the LIST
 *  command to generate this file:

list >network:list/okfile.lst uupub: files all lformat "%n %f%n"

 *
 *    This program is provided with NO WARRANTY and is guaruntee'd only to 
 *  take up disk space!
 *
 *   This file is *PUBLIC DOMAIN* and is provided as an example of how
 *  to interface a file request handler to XferQ.  Any author of a file
 *  request program (GPL, ShareWare, Commercial, Whatever) are extremely 
 *  encouraged to rip any section out of these sources in order to add
 *  XferQ support to your own software.  The intention of this program is
 *  to make itself obsolite.  It is not intended to be a very good file
 *  request handler, and I hope that authors will quickly write better and
 *  better replacement handlers.
 *
 *  Russell McOrmond  fidonet#1:1/139   rmcormon@ccs.carleton.ca
 *
 *  Remember, I am just a user of XferQ. If you have any questions about
 *  this file, please try to contact myself.  If you have specific questions
 *  about XferQ, please contact David Jones, it's author.
 *  David Jones  fidonet#1:163/109.8  dej@qpoint.ocunix.on.ca
 */

#include <exec/types.h>
#include <exec/execbase.h>
#include <dos/dos.h>
#include <dos/dosextens.h>
#include "xferq.h"
#include "xferq_pragmas.h"
#include <stdio.h>
#include <proto/exec.h>
#include <utility/hooks.h>
#include <stdlib.h>
#include <string.h>

struct Library *XferqBase;

extern struct DosLibrary *DOSBase;


/*
 *  Handle % character substitutions in the command line string we are
 *  building to execute the command.
 */
void
smartSubString(char *nstring,char *ostring,char *filename,char *fullpath,
  struct NetAddress *tosite,char *moreStuff,char *pipedream)
{
  long zone,net,node,point;
  char *temp;

  /* First, let's look at the address we have and extract the 4 numbers for
     a 4-D FTN address */
  XfqExamObjectTags(tosite,XQ_Zone,&zone,XQ_Net,&net,XQ_Node,&node,
                     XQ_Point,&point,TAG_DONE);



  /* Now let's iterate through the old stringcopying any text */
  while ((*nstring = *ostring) != '\0') {

    /* until we find a '%' where we need to do some special processing */
    if (*nstring =='%') {
      *nstring='\0';
      switch(*(++ostring)) {
        case 'r': /* do filename */
          sprintf(nstring,"%s%s",nstring,filename);
          break;

        case 'R': /* do fullpath */
          sprintf(nstring,"%s%s",nstring,fullpath);
          break;

        case 'a': /* do full address */
          /* New string built that will be fully 5-d with defaults/etc */
          temp=XfqPutAddressTags(tosite,XQ_Mandatory,XQADDR_3D,
              XQ_Optional,XQADDR_ANYTHING,TAG_DONE);

          /* it is possible for PutAddress to fail, so we make sure. */
          if(temp) {
            sprintf(nstring,"%s%s",nstring,temp);
            XfqDropObject(temp); /* always give XferQ it's memory back */
          }
          break;

        case 'z': /* do Zone */
          sprintf(nstring,"%s%d",zone);
          break;

        case 'N': /* do Net */
          sprintf(nstring,"%s%d",nstring,net);
          break;

        case 'n': /* do node */
          sprintf(nstring,"%s%d",nstring,node);
          break;

        case 'p': /* do Point */
          sprintf(nstring,"%s%d",nstring,point);
          break;

        case 's': /* Extra Stuff */
          sprintf(nstring,"%s%s",nstring,moreStuff);
          break;

        case 'O':
        case 'o': /* do Request Outfile */
          /* let's check if this is the first time we asked for this special
             filename.  If this is not the first, we just give the same
             filename an additional time */
          if(strlen(pipedream)==0) {
            /* Build some unique name for the PIPE: or T: filename */
            sprintf(pipedream,"%s:ReqOut%lx",((*ostring == 'o') ? "PIPE" : "T") ,
                 FindTask(NULL));
          }
          sprintf(nstring,"%s%s",nstring,pipedream);
          break;
      }

      /* now let's move to the new end of the string */
      while (*nstring != '\0') nstring++;
      nstring--;
    }
    nstring++;
    ostring++;
  }
}

/* 
 *    AddRequest receives a string with the address in it, and the line
 *  that would normally be put into a .RLO file.  This routine can be used
 *  as a drop-in replacement (Or option) in a file request handler.
 * 
 */

void AddRequest(char *site,char *thepath)
{  
   long flags;

   flags=XQ_IMMEDIATE;   /* XQ_IMMEDIATE is a flag that says that the file
                            should be sent only during the current session.
                            We don't want file requests to be kept if we hang
                            up on the remote site */

   switch(thepath[0]) {

     case '-':
     case '^':
        thepath++;
        flags|=XQ_DELETE; /* XQ_DELETE is a flag that says that the file
                             should be deleted once it is sent.  This would
                             be the flag used for response .PKT files, and
                             other response files */
        break;

     case '#':
        thepath++;
        flags|=XQ_TRUNCATE; /* Added for completeness, the XQ_TRUNCATE flag
                             will cause the file to be truncated to a length
                             of 0 */
        break;

     case '@':
     case ';':
        return; /* Not sure if this is ever used, but these lines are ignored */
        break;
   }


  /* The XfqAddWorkQuick takes very simple parameters.  A default of 120 is
     used for the priority of the files.  I believe that file requests should
     be sent BEFORE any mail bundles (Which will be re-queued up for
     transmission at a later time, while file requests are not
   */
  if(!XfqAddWorkQuick(site,thepath,NULL,120,flags)) {
    char *errmsg=XfqErrorMsg(XfqGetError());

   /* If an error occurs while adding the file I just print a message.
      Other authors may wish to do other maintenance, such as deleting 
      a .PKT file that was a response.  The most common reason why this
      add would fail would be that the session with the site has terminated.
      Full error code values for XfqGetError() are contained in the
      XferQ manual
    */
    printf("FileError %s while adding %s\n",errmsg,thepath);

    XfqDropObject(errmsg); /* The error message was a string, and the memory
                              for that object is now given back to XferQ */
  }
}

void justRunIt(char *command,char *site,char *pipedream)
{
  struct TagItem systag[2];
  FILE *req;
  char thepath[256];
  char *temp;

  systag[1].ti_Tag = TAG_END;
   
  printf("Executing: %s\n",command);
  System(command,systag);

  /* If the user specified a %o or %O on the command line, then we are going
     to attempt to read from the given filename.  If it was a PIPE: filename,
     then we assume that the user has done a 'run ...' for their file
     requestor.  If it was a T: filename, we assume they did not run, and the
     request program has now returned.
   */
  if((strlen(pipedream)>0) && (req=fopen(pipedream,"r"))) {
    while (fgets(thepath, 127 , req)) {
      temp=thepath;

      /* throw away any return or linefeed characters at the end of a line */
      while((*temp!=13) && (*temp!=10) && (*temp)) temp++;
      temp[0]='\0';

      /* and then just use our simple AddRequest() routine to add the file to
         XferQ
       */
      if(strlen(thepath))
        AddRequest(site,thepath);
    }
    fclose(req);
    if(pipedream[0]='T') unlink(pipedream);  /* remove the temporary file */
  }
}

BOOL ItMatches(char *arg1,char *arg2)
{
  char *q;
  char pat[256];

  /* Convert both to lower case for case insensitive comparason */
  for(q=arg1; *q!= '\0'; q++ ) *q=tolower(*q);
  for(q=arg2; *q!= '\0'; q++ ) *q=tolower(*q);

  /* Use the AmigaDos 2.0 pattern matching to do the comparason */
  ParsePattern(arg2,pat,255);
  return((BOOL)MatchPattern(pat, arg1));
}


/*
 * DoRequest() handles the 'built in' file request handler.  This request
 * handler is based on a text file which has 'filename pathname' pairs in it.
 * If the file that is being requested matches the given filename, then the
 * file pointed to by the pathname field is sent to the remote site.
 *
 *  fullpath  - pathname to the .REQ file to be read.
 *  tosite    - XferQ style address object for the site we are talking to.
 *  fileslist - pathname of this 'filename pathname' text file.
 *  nofile    - A file to be added if the request failed for some reason.
 *
 */
void DoRequest(char *fullpath,struct NetAddress *tosite,char *fileslist,
char *nofile)
{
  FILE *req=NULL,*list=NULL;
  int i,
      error=FALSE,
      password,
      foundit;
  char *thepath,*temp,
    /* Build new Address string that will be full 5-D fidonet if FTN address */
    *tositestr=XfqPutAddressTags(tosite,XQ_Mandatory,XQADDR_3D,
                   XQ_Optional,XQADDR_ANYTHING,TAG_DONE);

  printf("Request File: %s from %s\n",fullpath,
             (tositestr) ? tositestr : "Unknown");

  if ((req = fopen(fullpath,"r")) && (list= fopen(fileslist,"r"))) {
    char wantfile[128],thefile[128],buf[256];

    memset(wantfile,0,128);
   /* read a line from the .REQ file */
    while (!error && fgets(wantfile, 127 , req)) {
      /* bypass comments in .REQ file */
      if(wantfile[0]==';') continue;

      /* get rid of any returns or linefeeds at the end of the line */
      temp=wantfile;
      while((*temp!=13) && (*temp!=10) && (*temp)) temp++;
      temp[0]='\0';

      foundit=FALSE;
      i=0;

      /* grab the actual 'filename' part of the line in the .REQ file in
         case there is a password here */
      while (((thefile[i]=wantfile[i])!=' ') && thefile[i]) i++;        
      thefile[i]='\0';

      /* move to the beginning of the files list ready to begin search */
      if(!(error=fseek(list,0l,0))) {
        memset(buf,0,256);
        foundit=FALSE;

        /* get a line out of the fileslist */
        while(fgets(buf,256,list)) {

          /* remove any return or linefeed characters at the end of the line */
          temp=buf;
          while((*temp!=13) && (*temp!=10) && (*temp)) temp++;
          temp[0]='\0';

          thepath=buf;
          password=FALSE;

          /* skip to the first space, or the end of the line */
          while ((*thepath!=' ') && (*thepath)) thepath++;

          /* check if we have a password to match */
          if ((thepath[0]) && (thepath[1]=='!')) {
            thepath++;
            /* Skip over the password as it has to match too */
            while ((*thepath!=' ') && (*thepath!='\0')) thepath++;
            password=TRUE;
          }
          *thepath='\0';
          thepath++;


          if(!password) {
            /* check to see if it matches, making use of the line out of the
               .REQ file that has all the password stuff stripped off 
             */
            if(ItMatches(buf,thefile)) {
              printf("Send %s to %s for %s\n",thepath,
                      (tositestr) ? tositestr : "Unknown",thefile);
              AddRequest(tositestr,thepath);
              foundit=TRUE;
            }
          } else {
            /* Check to see if it matches, making use of the full line out of
               the .REQ file which includes the password stuff. Be carefull 
               of pattern matched passwords - password protection doesn't 
               really exist with this silly request handler 
             */
            if(ItMatches(buf,wantfile))  {
              printf("Send %s to %s for %s\n",thepath,
                   (tositestr) ? tositestr : "Unknown",wantfile);
              AddRequest(tositestr,thepath);
              foundit=TRUE;
            }
          }
        }

        /* Check if we ever found a match, and if not we will send our 'nofile'
           file to the remote */
        if (!foundit) {
          printf("Couldn't find %s for %s\n",wantfile,
                 (tositestr) ? tositestr : "Unknown");
          AddRequest(tositestr,nofile);
        }
      }
    }
  } else printf("FileError (%lx,%lx)\n",req,list);

  if(tositestr) XfqDropObject(tositestr);
  if(list) fclose(list);
  if(req) fclose(req);
}

/*
 *  The main routine in the program.
 *
 *  The flow for XferQ is very simple:
 *       - first, do a XfqHoldMailer() for the address that the requests are
 *         going to.  This signifies to the mailer that it should wait as you
 *         might be adding some files.
 *       - Add any files that you wish using XfqAddWorkQuick() and the 
 *         XQ_IMMEDIATE flag which signifies that files should only be added
 *         if the remote is still connected to us.
 *       - When all files have been added, do an XfqReleaseMailer() and
 *         then exit.
 *
 */

void main(argc,argv)
int argc;
char **argv;
{
  int ret=0,mailerwait=NULL,delfile=FALSE;
  struct NetAddress *tosite;
  char *moreStuff,*command,*config,*filename,*fullpath;
  FILE *myconfig;
  char pattern[128];
  char mycommand[128];
  char pipedream[255];
  BPTR mylock;

  /* xferq.library and most related tools require a V37 kickstart as a minimum 
   */
  if((DOSBase->dl_lib.lib_Version) < 37) {
    fprintf(stderr,"You need to upgrade you're O.S.\n");
    exit(666);
  }
    
  if ((argc!=5) && (argc!=6)) {
    fprintf(stderr,"USAGE: %s Config FileName FullPath Address [\"more stuff\"]\n",argv[0]);
    exit(1);
  }

  /* open the library, making sure we get at least version 1 */
  if((XferqBase=OpenLibrary("xferq.library",1l))==NULL) {
     ret=95;
     fprintf(stderr,"Can't open XferQ\n");
     exit(95);
  }

  config=argv[1];
  filename=argv[2];
  fullpath=argv[3];

  /* get a pointer to an XferQ address object. We require a 3 dimensional
     zone:net/node to be put as a minimum, but a full N-dimensional address
     can be typed as an option 
   */
  tosite=XfqGetAddressTags(argv[4],NULL,
            XQ_Mandatory, XQADDR_3D,
            XQ_Optional, XQADDR_ANYTHING,TAG_DONE);
  if(argc==6) moreStuff = argv[5];
        else  moreStuff = "";


  if (!tosite) {
     ret=94;
     fprintf(stderr,"Can't decode address\n");
  } else
    mailerwait=XfqHoldMailer(tosite);

  pipedream[0]='\0';

  if(ret==0) {
    /* open the configuration file so we can find out what we are going to do
       with this file */
    if(myconfig=fopen(config,"r")) {
      memset(pattern,0,128);

      /* as long as there are more lines in the config file and the inbound
         file request still exists, we will loop looking for a match */

      while (fgets(pattern, 127 , myconfig) && 
                  (mylock=Lock(argv[1],SHARED_LOCK))) {
        UnLock(mylock);
        pipedream[0]='\0';
        if ((pattern[0]!=';') && (pattern[0]!='%')) { /* bypass comments */

          /* strip return and linefeed characters at the end of the line */
          command=pattern;
          while((*command!=13) && (*command!=10) && (*command)) command++;
          command[0]='\0';

          /* find the first word on the line - it is the file-pattern that we
             want to check for a match with the inbound filename 
           */
          command=pattern;
          while((*command!=' ') && (*command)) command++;
          if(*command) {
            command[0]='\0';
            command++;
          }

          /* Command is now everything else that was on the line after we
             strip any extra spaces */
          while((*command==' ') && (*command)) command++;

          /* check to see if the inbound filename matches our current
             filename-pattern.
           */
          if(ItMatches(filename,pattern)) {

            /* do all of our % character substitutions on that command line */
            smartSubString(mycommand,command,filename,fullpath,tosite,moreStuff,
               pipedream);

            switch(mycommand[0]) {
              case '\0':
                 /* skip blank broken lines */
                 break;

              case '#':   /* the '#' is a special character that indicates that
                             we wish to utilize the built in file request
                             handling. */
                 {
                   char *nofile;

                   /* two words are extracted.  The first word is the full
                      pathname to a file containing 'filename pathname' pairs
                      that will be searched against.  The second word is the
                      full pathname to a file that will be sent if no matching
                      file is found
                     */

                   nofile=mycommand+1;
                   while((*nofile!=' ') && (*nofile)) nofile++;
                   if(*nofile) {
                     nofile[0]='\0';
                     nofile++;
                   }
                   DoRequest(fullpath,tosite,mycommand+1,nofile);
                   delfile=TRUE;  /* now that we have processed it, mark the
                                     .REQ file to be deleted */
                 }
                 break;

              default: /* by default, just run the given command */
                 justRunIt(mycommand,argv[4],pipedream);
                 break;
            }
	  }
	}
      }
    }
    if (delfile) unlink(fullpath);
  }
  /* if our address was OK and we successfully did a XfqHoldMailer, we
     better now release that mailer or it could be waiting for no reason */
  if(tosite && mailerwait) mailerwait=XfqReleaseMailer(tosite);

  /* If we got a site object, return the memory back to XferQ */
  if(tosite) XfqDropObject(tosite);

  if(XferqBase) CloseLibrary(XferqBase);
  exit(ret);
}
