#include <exec/types.h>

#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <dos.h>

#include <dlg/file.h>
#include <dlg/misc.h>
#include <dlg/msg.h>
#include <dlg/resman.h>

#include <exec/nodes.h>
#include <exec/lists.h>

#include <link/lang.h>
#include <link/io.h>

#include <proto/dlg.h>
#include <proto/exec.h>
#include <proto/dos.h>

#include <pragmas/dlg.h>

#include "Freq.h"

#define LOGFILE      "Logs:Freq.log"
#define CONFIGFILE   "DLGConfig:Misc/DLGFreq.cfg"
#define FIDOCONFIG   "DLGConfig:Port/FidoNet.settings"
#define MESSAGEFILE  "T:FreqServ.txt"
#define REPORT       "T:FreqServ.report"
#define TEMPRLO      "T:temp.rlo"

#include <private/Version.h>
#define  ObjRev "1"
const UBYTE version[]="\0$VER: DLGFreq " BUILDVER "." ObjRev " ©" COPYRIGHT " by Digerati Dreams "__AMIGADATE__;

struct Magic
{
   struct Node         mg_Node;
   char               *mg_Name;
   char               *mg_Path;
   char               *mg_PW;
   char               *mg_Desc;
};

struct ReqFile
{
   struct Node         rf_Node;
   char               *rf_Name;
   char               *rf_PW;
   BOOL                rf_Matched;
};

struct Dupe
{
   struct Node         dup_Node;
   char               *dup_Name;
   unsigned long       dup_Size;
};

struct Library     *DLGBase = NULL;
struct Library     *NodelistBase;
struct List        *MagicFileList = NULL;
struct List        *LockoutList = NULL;
struct List        *ReqList = NULL;
struct List        *DupeList = NULL;
struct fido        *fidoconfig;

Addr               *Dest;
Addr               *Orig;

BPTR                sout;

char              **SA;

char                LogString[512];
char               *Out = "deadbeef";

// Prototypes

void                LogEvent(char *);
void                ShutDown(int);
void                AddToMagicList(char *, char *, char *, char *, BOOL);
void                FreeMagicList(void);
void                AddToLockoutList(char *);
void                FreeLockoutList(void);
void                AddToReqList(char *, char *);
void                FreeReqList(void);
BOOL                CheckForDupe(char *, unsigned long);
void                FreeDupeList(void);
BOOL                CreateMsg(char *);
BOOL                AppendRem(char *);
BOOL                AppendLoc(char *);
BOOL                AppendRLO(char *);
USHORT              FixNumber(USHORT);

/* Here we go! */

long __stack =  100000L;


void                main(int argc, char *argv[])
{
   int                 a;

   int                 Baud = 0;
   char               *Address = "0:0/0.0";
   char               *RemSysop = "Gilligan";
   char               *In = "deadbeef";

   char               *SysopName = "deadbeef";
   char               *BannerFile = "deadbeef";
   char               *DefaultFile = "deadbeef";

   char                NLPath[78];

   int                 BaseBaud = 1200;
   int                 FileRatio = 0, ByteRatio = 0;
   int                 MaxFiles = -1, MaxBytes = -1;
   int                 NoLimit = -1;

   BOOL                Match = FALSE;
   BOOL                NOPOINTS = FALSE;
   BOOL                NOPAT = FALSE;
   BOOL                BadEvent = FALSE;
   BOOL                Update = TRUE;
   BOOL                NoUnlisted = FALSE;
   BOOL                CopyFile = FALSE;
   BOOL                BlindMagic = FALSE;

   char                TEMPS[513];

   BPTR                fp;

   int                 TotalFiles = 0;
   int                 TotalBytes = 0;

   struct Magic       *ThisNode;
   struct Node        *AddrNode;
   struct ReqFile     *RFile;

   NodeList           *nl;
   NodeDesc           *nd;

   sout = Output();

   // Open dlg.library, minimum of version 2

   if (!(DLGBase = (struct Library *) OpenLibrary("dlg.library", 2)))
   {
      ShutDown(50);
   }

   SA = getlang(NULL);
   if (!SA)
   ShutDown(40);

   if (!(NodelistBase = (struct Library *) OpenLibrary("traplist.library", 5)))
   {
      ShutDown(25);
   }

   // Parse command line

   // LogEvent("*- DLGFreq invoked.");
   LogEvent(SA[3677]);

   for (a = 1; a < argc; a++)
   {
      if (!Stricmp(argv[a], "-B"))
      {
         Baud = atoi(argv[a + 1]);
         // ASPrintf(NULL,LogString,"-  %ld Connection",Baud);
         ASPrintf(NULL, LogString, SA[3678], Baud);
         LogEvent(LogString);
      }

      if (!Stricmp(argv[a], "-A"))
      {
         Address = strdup(argv[a + 1]);
         // ASPrintf(NULL,LogString,"-  Remote Address:
         // %s",Address);
         ASPrintf(NULL, LogString, SA[3679], Address);
         LogEvent(LogString);
      }

      if (!Stricmp(argv[a], "-S"))
      {
         RemSysop = strdup(argv[a + 1]);
         // ASPrintf(NULL,LogString,"-  Sysop: %s",RemSysop);
         ASPrintf(NULL, LogString, SA[3680], RemSysop);
         LogEvent(LogString);
      }

      if (!Stricmp(argv[a], "-I"))
      {
         In = strdup(argv[a + 1]);
         // ASPrintf(NULL,LogString,"-  Inbound: %s",In);
         ASPrintf(NULL, LogString, SA[3681], In);
         LogEvent(LogString);
      }

      if (!Stricmp(argv[a], "-O"))
      {
         Out = strdup(argv[a + 1]);
         // ASPrintf(NULL,LogString,"-  Outbound: %s",Out);
         ASPrintf(NULL, LogString, SA[3682], Out);
         LogEvent(LogString);
      }
   }

   // Check to see if all command line options have been
   // passed.

   if (Baud == 0)
   {
      // LogEvent("|  Baud rate not indicated!");
      LogEvent(SA[3683]);
      // LogEvent("*- DLGFreq Terminated\n");
      LogEvent(SA[3684]);
      ShutDown(10);
   }

   if ((Strnicmp(RemSysop, "Gilligan", 8)) == 0)
   {
      // LogEvent("|  Remote Sysop name not indicated!");
      LogEvent(SA[3685]);
      // LogEvent("*- DLGFreq Terminated\n");
      LogEvent(SA[3684]);
      ShutDown(10);
   }

   if ((Strnicmp(In, "deadbeef", 8)) == 0)
   {
      // LogEvent("|  Inbound file name not indicated!");
      LogEvent(SA[3686]);
      // LogEvent("*- DLGFreq Terminated\n");
      LogEvent(SA[3684]);
      ShutDown(10);
   }

   if ((Strnicmp(Out, "deadbeef", 8)) == 0)
   {
      // LogEvent("|  Outbound file name not indicated!");
      LogEvent(SA[3687]);
      // LogEvent("*- DLGFreq Terminated\n");
      LogEvent(SA[3684]);
      ShutDown(10);
   }

   // Open config file and parse it

   fp = Open(CONFIGFILE, MODE_OLDFILE);

   if (fp == NULL)
   {
      // LogEvent("|  Unable to read config file!");
      LogEvent(SA[3688]);
      // LogEvent("*- DLGFreq Terminated\n");
      LogEvent(SA[3684]);
      ShutDown(10);
   }

   while (FGets(fp, TEMPS, 512))
   {
      char               *par[20];
      char               *d;
      char               *Desc = NULL;

      int                 c;
      int                 len;

      len = strlen(TEMPS);

      strcpy(TEMPS, stpblk(TEMPS));

      if (strlen(TEMPS) == 0)
      continue;
      if (!Strnicmp(TEMPS, ";", 1))
      continue;
      d = strdup(TEMPS);

      c = ArgParse(TEMPS, par, 19);

      if (!Stricmp(par[0], "SYSOPNAME"))
      {
         int                 i;

         SysopName = calloc(1, len);

         ASPrintf(NULL, SysopName, "%s", par[1]);

         for (i = 2; i < c; i++)
         {
            if (!Strnicmp(par[i], ";", 1))
            break;

            ASPrintf(NULL, SysopName, "%s %s", SysopName, par[i]);
         }
      }

      if (!Stricmp(par[0], "NOPOINTS"))
      {
         NOPOINTS = TRUE;
         // LogEvent("|  Freqs not accepted from points");
         LogEvent(SA[3689]);
      }

      if (!Stricmp(par[0], "CopyFile"))
      {
         if (Exists("FILE:TempDownLoads"))
         {
            CopyFile = TRUE;
            // LogEvent("|  Will copy files to TempDownLoads");
            LogEvent(SA[3690]);
         }
         else
         {
            // LogEvent("!  Error! File:TempDownLoads does not
            // exist!");
            LogEvent(SA[3691]);
         }
      }

      if (!Stricmp(par[0], "NOPATMatch"))
      {
         NOPAT = TRUE;
         // LogEvent("|  Only FIRST matched file will be sent");
         LogEvent(SA[3692]);
      }

      if (!Stricmp(par[0], "NoUpdateFD"))
      {
         Update = FALSE;
         // LogEvent("|  Will not update file download count");
         LogEvent(SA[3693]);
      }

      if (!Stricmp(par[0], "NoUnlisted"))
      {
         NoUnlisted = TRUE;
         // LogEvent("|  Will not honor freqs from unlisted
         // systems");
         LogEvent(SA[3694]);
      }

      if (!Stricmp(par[0], "BANNER"))
      {
         BannerFile = strdup(par[1]);
      }

      if (!Stricmp(par[0], "BASEBAUD"))
      {
         BaseBaud = atoi(par[1]);
      }

      if (!Stricmp(par[0], "NoLimit"))
      {
         NoLimit = atoi(par[1]);
      }

      if (!Stricmp(par[0], "FILERATIO"))
      {
         FileRatio = atoi(par[1]);
         if (FileRatio > 0)
         MaxFiles = Baud / BaseBaud * FileRatio;
      }

      if (!Stricmp(par[0], "BYTERATIO"))
      {
         ByteRatio = atoi(par[1]);
         if (ByteRatio > 0)
         MaxBytes = Baud / BaseBaud * ByteRatio;
      }

      if (!Stricmp(par[0], "DEFAULT"))
      {
         DefaultFile = strdup(par[1]);
      }

      if (!Stricmp(par[0], "MAGIC"))
      {
         Desc = strdup(d);

         if (strchr(Desc, ';'))
         {
            Desc = strtok(d, ";");
            Desc = strdup(strtok(NULL, "\n"));
         }
         else
         {
            *Desc = NULL;
         }

         if ((c > 3) && (Desc == NULL))
         {
            if (!Stricmp(par[3], "PW"))
            {
               if (c > 4)
               {
                  if (!Strnicmp(par[4], ";", 1))
                  {
// ASPrintf(NULL,LogString,"!  Config error, MAGIC definition for %s with semicolon",par[2]);
                     ASPrintf(NULL, LogString, SA[3695], par[2]);
                     LogEvent(LogString);
                  }
                  else
                  {
                     AddToMagicList(par[1], par[2], par[4], Desc, BlindMagic);
                  }
               }
               else
               {
// ASPrintf(NULL,LogString,"!  Config error, MAGIC definition for %s has no password!",par[1]);
                  ASPrintf(NULL, LogString, SA[3696], par[1]);
                  LogEvent(LogString);
               }
            }
            else
            {
// ASPrintf(NULL,LogString,"!  Config error, improper formatting for MAGIC definition %s!",par[1]);
               ASPrintf(NULL, LogString, SA[3697], par[1]);
               LogEvent(LogString);
            }
         }
         else
         {
            AddToMagicList(par[1], par[2], NULL, Desc, BlindMagic);
         }
      }

      if (!Stricmp(par[0], "LOCKOUT"))
      {
         // Make sure the address has a point number

         if (!DLGPatternMatch("*.*", par[1]))
         {
            char               *t;

            t = calloc(1, strlen(par[1]) + 3);
            ASPrintf(NULL, t, "%s.0\0", par[1]);

            par[1] = strdup(t);
            free(t);
         }

         AddToLockoutList(par[1]);
      }

      if (!Stricmp(par[0], "BLINDMAGIC"))
      {
         BlindMagic = TRUE;
      }

   }

   Close(fp);

   // Impose no limits if baud rate is high enough

   if ((NoLimit != -1) && (NoLimit <= Baud))
   {
      MaxFiles = -1;
      MaxBytes = -1;
      // LogEvent("|  No limit imposed on this session");
      LogEvent(SA[3698]);
   }

   // Parse the address into an address struct

   Dest = calloc(1, sizeof(Addr));

   if (!Dest)
   {
      // LogEvent("!  Error allocating memory for address struct!");
      LogEvent(SA[3699]);
      ShutDown(20);
   }

   if (NLParseAddr(Dest, Address, NULL))
   {
      // LogEvent("!  Invalid remote address specification!");
      LogEvent(SA[3700]);
      ShutDown(10);
   }

   // What's OUR address?

   Orig = calloc(1, sizeof(Addr));

   if (!Orig)
      {
         // LogEvent("!  Error allocating memory for address struct!");
         LogEvent(SA[3699]);
         ShutDown(20);
      }

   fidoconfig = calloc(1, sizeof(struct fido));

   if (!fidoconfig)
      {
         // LogEvent("!  Error allocating memory for fido settings!");
         LogEvent(SA[3701]);
         ShutDown(20);
      }

   fp = Open(FIDOCONFIG, MODE_OLDFILE);

   if (!fp)
      {
         // LogEvent("!  Cannot read Fido settings!");
         LogEvent(SA[3702]);
         ShutDown(10);
      }

   FRead(fp, fidoconfig, sizeof(struct fido), 1);

   Close(fp);

   Orig->Zone = fidoconfig->zone;
   Orig->Net = fidoconfig->net;
   Orig->Node = fidoconfig->node;
   Orig->Point = fidoconfig->point;

// Do this for parsing the nodelist later

   strcpy(NLPath, fidoconfig->nodelistpath);

   free(fidoconfig);

// Make sure the address has a point number

   if (!DLGPatternMatch("*.*", Address))
      {
         char               *t;

         t = calloc(1, strlen(Address) + 3);
         ASPrintf(NULL, t, "%s.0\0", Address);

         Address = strdup(t);
         free(t);
      }

   // Copy the banner file to an outgoing message for the
   // remote sysop.

   Copy(BannerFile, MESSAGEFILE);

   // Parse the REQ file

   fp = Open(In, MODE_OLDFILE);

   if (fp == NULL)
      {
         // LogEvent("|  Unable to read Request file!");
         LogEvent(SA[3703]);
         // LogEvent("*- DLGFreq Terminated\n");
         LogEvent(SA[3684]);
         ShutDown(10);
      }

   while (FGets(fp, TEMPS, 512))
   {
      int                 c;

      char               *FileName[4];

      c = ArgParse(TEMPS, FileName, 3);

      if (c)
      {
         if (c >= 2)
         {
            char               *t;

            t = strdup(FileName[1]);
            strmid(FileName[1], t, 2, 6);
            AddToReqList(FileName[0], t);
         }
         else
         {
            AddToReqList(FileName[0], NULL);
         }
      }
   }

   Close(fp);

   // ASPrintf(NULL,LogString,"***File Request Report\n\nNode: 
   // %s\nSysop: %s\nConnection:
   // %ld\n=====\n\n",Address,RemSysop,Baud);
   ASPrintf(NULL, LogString, SA[3704], Address, RemSysop, Baud);
   AppendLoc(LogString);

// Shut down the session if points are locked out

   if (NOPOINTS)
   {
      if (Dest->Point != 0)
      {
         // AppendRem("\n\nSorry, file requests from points are not accepted.\n\n");
         AppendRem(SA[3705]);
         // AppendLoc("\n\nFailed attempt from point.\n\n");
         AppendLoc(SA[3706]);
         // LogEvent("|  Freq is from a point; points are locked out.");
         LogEvent(SA[3707]);
         BadEvent = TRUE;
      }
   }

// Shut down the session if unlisted systems are not allowed

   nl = NLOpen(NLPath, 0L);

   if (nl != NULL)
   {
      nd = NLIndexFind(nl, Dest, 0L);

      if (nd == NULL)
      {
         if (NoUnlisted == TRUE)
         {
            // AppendRem("\n\nSorry, file requests from unlisted
            // systems are not accepted.\n\n");
            AppendRem(SA[3708]);
            // AppendLoc("\n\nFailed attempt from unlisted system.\n\n");
            AppendLoc(SA[3709]);
            // LogEvent("|  Freq is from a unlisted system; unlisted
            // systems are locked out.");
            LogEvent(SA[3710]);
            BadEvent = TRUE;
         }
         else
         {
            // LogEvent("|  Unlisted system");
            LogEvent(SA[3711]);
         }
      }
      else
      {
         NLFreeNode(nd);
      }

      NLClose(nl);
   }
   else
   {
      // ASPrintf(NULL,LogString,"!  Could not open nodelist on path %s",NLPath);
      ASPrintf(NULL, LogString, SA[3712], NLPath);
      LogEvent(LogString);
   }

// Shut down the session if node is locked out

   if (LockoutList)
   {
      for (AddrNode = LockoutList->lh_Head; AddrNode->ln_Succ; AddrNode = AddrNode->ln_Succ)
      {
         if (DLGPatternMatch(AddrNode->ln_Name, Address))
         {
            // ASPrintf(NULL,LogString,"|  Address %s matches lockout
            // pattern %s",Address,AddrNode->ln_Name);
            ASPrintf(NULL, LogString, SA[3713], Address, AddrNode->ln_Name);
            LogEvent(LogString);
            // AppendRem("\n\nSorry, your node is locked out from
            // making file requests.\n\n");
            AppendRem(SA[3714]);
            // ASPrintf(NULL,LogString,"\n\nFailed attempt from locked
            // out node %s.\n\n",Address);
            ASPrintf(NULL, LogString, SA[3715], Address);
            AppendLoc(LogString);
            BadEvent = TRUE;
         }
      }
   }

   if (!BadEvent)
   {
      ASPrintf(NULL,LogString,"============================================================================\n");
// AppendRem("      FILE     SIZE  DESCRIPTION\n");
      AppendRem(SA[3716]);
      AppendRem(LogString);
// AppendLoc("      FILE     SIZE  DESCRIPTION\n");
      AppendLoc(SA[3716]);
      AppendLoc(LogString);

// First, we check to see if a file matches our magic file list.

      if (MagicFileList)
      {
        // Cycle through our requested file list

         for (RFile = (struct ReqFile *) ReqList->lh_Head; RFile->rf_Node.ln_Succ; RFile = (struct ReqFile *) RFile->rf_Node.ln_Succ)
         {
           // For each requested file, cycle through list of magic files

            for (ThisNode = (struct Magic *) MagicFileList->lh_Head; ThisNode->mg_Node.ln_Succ; ThisNode = (struct Magic *) ThisNode->mg_Node.ln_Succ)
            {
               if ((MaxFiles != -1) && (TotalFiles == MaxFiles))
               {
// ASPrintf(NULL,LogString,"%10.10s           Exceeds File Limit (%d)\n",ThisNode->mg_Name,MaxFiles);
                  ASPrintf(NULL, LogString, SA[3717], ThisNode->mg_Name, MaxFiles);
                  AppendRem(LogString);
// LogEvent("|  Max files limit reached");
                  LogEvent(SA[3718]);
                  break;
               }

                  // using scricmp instead of DLGPatternMatch because we don't
                  // want to send the wrong file by mistake

               if (!Stricmp(ThisNode->mg_Name, RFile->rf_Name))
               {
                  unsigned long       fsize = 0;

                  Match = TRUE;

                  // Check for password

                  if (ThisNode->mg_PW != NULL)
                  {
                     if (Stricmp(ThisNode->mg_PW, RFile->rf_PW))
                     {
                        // ASPrintf(NULL,LogString,"%10.10s           Incorrect
                        // password\n",ThisNode->mg_Name);
                        ASPrintf(NULL, LogString, SA[3719], ThisNode->mg_Name);
                        AppendRem(LogString);
                        AppendLoc(LogString);
                        // ASPrintf(NULL,LogString,"!  Incorrect password for %s:
                        // %s",ThisNode->mg_Name,RFile->rf_PW);
                        ASPrintf(NULL, LogString, SA[3720], ThisNode->mg_Name, RFile->rf_PW);
                        LogEvent(LogString);
                        Remove((struct Node *) RFile);
                        free(RFile);
                        continue;
                     }
                     else
                     {
                        // ASPrintf(NULL,LogString,"|  Password match for magic
                        // name %s",RFile->rf_Name);
                        ASPrintf(NULL, LogString, SA[3721], RFile->rf_Name);
                        LogEvent(LogString);
                     }
                  }
                  
                  // Added 11/5/96 -- for Mike M.
                  if(!Exists(ThisNode->mg_Path))
                  {
                     ASPrintf(NULL,LogString,"%s could not be located.",ThisNode->mg_Path);
                     AppendRem(LogString);
                     AppendLoc(LogString);
                     ASPrintf(NULL,LogString,"! %s could not be located! Check your config!!");
                     LogEvent(LogString);
                     Remove((struct Node *) RFile);
                     free(RFile);
                  }

                  FileSize(ThisNode->mg_Path, &fsize);

                  if ((MaxBytes != -1) && ((TotalBytes + fsize) > MaxBytes))
                  {
                     // ASPrintf(NULL,LogString,"%10.10s           Exceeds byte
                     // limit (%d)\n",ThisNode->mg_Name,MaxBytes);
                     ASPrintf(NULL, LogString, SA[3722], ThisNode->mg_Name, MaxBytes);
                     AppendRem(LogString);
                     // LogEvent("|  Max bytes limit exceeded");
                     LogEvent(SA[3723]);
                  }
                  else
                  {
                     if (CopyFile)
                     {
                        char               *x;

                        x = calloc(1, strlen(ThisNode->mg_Path));
                        stcgfn(x, ThisNode->mg_Path);

                        ASPrintf(NULL, LogString, "File:TempDownLoads/%s", x);
                        free(x);
                        x = strdup(LogString);

                        if (0 == Copy(ThisNode->mg_Path, x))
                        {
                           ASPrintf(NULL, LogString, "^%s\n", x);
                        }
                        else
                        {
                           // ASPrintf(NULL,LogString,"!  Error copying to %s",x);
                           ASPrintf(NULL, LogString, SA[3724], x);
                           LogEvent(LogString);
                           ASPrintf(NULL, LogString, "%s\n", ThisNode->mg_Path);
                        }
                     }
                     else
                     {
                        ASPrintf(NULL, LogString, "%s\n", ThisNode->mg_Path);
                     }

                     if (!CheckForDupe(ThisNode->mg_Path, fsize))
                     {
                        AppendRLO(LogString);
                        ASPrintf(NULL, LogString, "%-10.10s %8.8ld  %-50.50s\n", ThisNode->mg_Name, fsize, (ThisNode->mg_Desc == NULL) ? "None" : ThisNode->mg_Desc);
                        AppendRem(LogString);
                        AppendLoc(LogString);
                        TotalFiles = TotalFiles + 1;
                        TotalBytes = TotalBytes + fsize;
                     }
                     else
                     {
                        // ASPrintf(NULL,LogString,"|  Dupe file:
                        // %s\n",ThisNode->mg_Path);
                        ASPrintf(NULL, LogString, SA[3725], ThisNode->mg_Path);
                        LogEvent(LogString);
                     }

                     // ASPrintf(NULL,LogString,"|  Sending magic file name %s
                     // [%s]",RFile->rf_Name,ThisNode->mg_Path);
                     ASPrintf(NULL, LogString, SA[3726], RFile->rf_Name, ThisNode->mg_Path);
                     LogEvent(LogString);
                  }

      // If we matched a magic file name, win or lose, we remove it
      // from the list so that we don't parse it again.

                  Remove((struct Node *) RFile);
                  free(RFile);
               }
            }
         }
      }

// No need to do this if list is empty

      if (!IsListEmpty(ReqList))
      {
         if ((TotalFiles < MaxFiles) || (MaxFiles == -1))
         {
            unsigned long       isize = 0;
            struct FREQ_INDEX  *fi = NULL;

  // Now, to read in the DLG areas file

            FileSize(IndexFile, &isize);

            fi = calloc(1, isize);

            if (fi != NULL)
            {
               BPTR                ifh;
               unsigned long       i = 0;

               ifh = Open(IndexFile, MODE_OLDFILE);

               if (ifh != NULL)
               {
                  Read(ifh, fi, isize);
                  Close(ifh);
               }
               else
               {
// LogEvent("!  Could not open index file to read!");
                  LogEvent(SA[3727]);
               }

               for (i = 0; i < isize / sizeof(struct FREQ_INDEX); i++)
               {

                  if ((MaxFiles != -1) && (TotalFiles == MaxFiles))
                  {
                     // Have to include this in its own loop or it would
                     // repeat every time.  We only want to make the
                     // notification once.

                     if (TotalFiles == MaxFiles)
                     {
// ASPrintf(NULL,LogString,"%10.10s           Exceeds file
// limit (%ld)\n",RFile->rf_Name,MaxFiles);
                        ASPrintf(NULL, LogString, SA[3728], RFile->rf_Name, MaxFiles);
                        AppendRem(LogString);
// LogEvent("|  Max files limit reached");
                        LogEvent(SA[3718]);
                     }
                     break;
                  }

                  // For each file, compare with requested file list

                  for (RFile = (struct ReqFile *) ReqList->lh_Head; RFile->rf_Node.ln_Succ; RFile = (struct ReqFile *) RFile->rf_Node.ln_Succ)
                  {
                     if (DLGPatternMatch(RFile->rf_Name, fi[i].Name))
                     {
                        char                p[512];
                        char                t[512];

                        if (GetPath(p, fi[i].Area, NULL, fi[i].Name) >= 0)
                        {
                           unsigned long       fsize = 0;

                           Match = TRUE;

                           ASPrintf(NULL, t, "%s%s", p, fi[i].Name);

                           FileSize(t, &fsize);

                           if (fsize)
                           {
                              if ((MaxBytes != -1) && ((TotalBytes + fsize) > MaxBytes))
                              {
// ASPrintf(NULL,LogString,"%10.10s           Exceeds byte
// limit (%ld)\n",fi[i].Name,MaxBytes);
                                 ASPrintf(NULL, LogString, SA[3729], fi[i].Name, MaxBytes);
                                 AppendRem(LogString);
// LogEvent("|  Max bytes limit exceeded");
                                 LogEvent(SA[3723]);
                              }
                              else
                              {
                                 BPTR                qfh = NULL;
                                 char               *qd = NULL;
                                 int                 q = 0;
                                 struct File_Header *fhead = NULL;
                                 struct QuickFile   *qf = NULL;

                                 if (CopyFile)
                                 {
                                    ASPrintf(NULL, LogString, "File:TempDownLoads/%s", fi[i].Name);

                                    if (!Copy(t, LogString))
                                    {
                                       ASPrintf(NULL, LogString, "^File:TempDownLoads/%s\n", fi[i].Name);
                                       AppendRLO(LogString);
                                    }
                                    else
                                    {
// ASPrintf(NULL,LogString,"!  Error copying to %s",t);
                                       ASPrintf(NULL, LogString, SA[3724], t);
                                       LogEvent(LogString);
                                    }
                                 }
                                 else
                                 {
                                    ASPrintf(NULL, LogString, "%s", t);
                                    AppendRLO(LogString);
                                 }

                                 qf = calloc(1, sizeof(struct QuickFile));

                                 ASPrintf(NULL, LogString, "FILE:%ld/file.dat", fi[i].Area);

                                 qfh = Open(LogString, MODE_OLDFILE);

                                 while (Read(qfh, qf, sizeof(struct QuickFile)))
                                 {
                                    if (!Stricmp(qf->filename, fi[i].Name))
                                       break;
                                 }

                                 Close(qfh);

                                 qd = stpblk(strdup(qf->desc));

                                 for (q = 0; q < strlen(qd); q++)
                                 {
                                    if ((qd[q] == '\n') || (qd[q] == '\r'))
                                       qd[q] = '\0';
                                 }

                                 ASPrintf(NULL, LogString, "%-10.10s %8.8ld  %-50.50s ...\n", fi[i].Name, fsize, qd);
                                 AppendRem(LogString);
                                 AppendLoc(LogString);
                                 TotalFiles++;
                                 TotalBytes = TotalBytes + fsize;

// ASPrintf(NULL,LogString,"|  Sending file %s",t);
                                 ASPrintf(NULL, LogString, SA[3730], t);
                                 LogEvent(LogString);

                                 RFile->rf_Matched = TRUE;

                                 // If extended pattern matching is turned off,
                                 // remove the file from the queue

                                 if (NOPAT == TRUE)
                                 {
                                    Remove((struct Node *) RFile);
                                    free(RFile);
                                 }

                                 // Update the Times Downloaded field Unless
                                 // it is disabled

                                 if (Update == TRUE) ;
                                 {
                                    fhead = calloc(1, sizeof(struct File_Header));

                                    if (fhead != NULL)
                                    {
                                       BPTR                fh = NULL;

                                       ASPrintf(NULL, LogString, "File:%ld/%ld.fd", fi[i].Area, qf->number);

                                       fh = Open(LogString, MODE_READWRITE);

                                       if (fh != NULL)
                                       {
                                          Read(fh, fhead, sizeof(struct File_Header));

                                          fhead->Times_Downloaded++;
                                          Seek(fh, 0, OFFSET_BEGINNING);
                                          Write(fh, fhead, sizeof(struct File_Header));

                                          Close(fh);
                                       }
                                       else
                                       {
// ASPrintf(NULL,LogString,"!  Could not open
// File:%ld/%ld.fd to update!",fi[i].Area,qf->number);
                                          ASPrintf(NULL, LogString, SA[3731], fi[i].Area, qf->number);
                                          LogEvent(LogString);
                                       }
                                    }
                                    else
                                    {
// LogEvent("!  Error allocating memory for a file
// header!");
                                       LogEvent(SA[3732]);
                                    }

                                    free(fhead);
                                 }
                              }
                           }
                           else
                           {
// ASPrintf(NULL,LogString,"!  File %s is missing!",t);
                              ASPrintf(NULL, LogString, SA[3733], t);
                              LogEvent(LogString);
                           }
                        }
                        else
                        {
// ASPrintf(NULL,LogString,"!  File %s in area %ld could
// not be located!",fi[i].Name,fi[i].Area);
                           ASPrintf(NULL, LogString, SA[3734], fi[i].Name, fi[i].Area);
                           LogEvent(LogString);
                        }
                     }
                  }
               }

               free(fi);
            }
         }
      }

         if (!IsListEmpty(ReqList))
         {
            for (RFile = (struct ReqFile *) ReqList->lh_Head; RFile->rf_Node.ln_Succ; RFile = (struct ReqFile *) RFile->rf_Node.ln_Succ)
            {
               if (RFile->rf_Matched == FALSE)
               {
// ASPrintf(NULL,LogString,"%10.10s           No matches
// found.\n",RFile->rf_Name);
                  ASPrintf(NULL, LogString, SA[3735], RFile->rf_Name);
                  AppendRem(LogString);
               }
            }
         }

         ASPrintf(NULL,LogString,"============================================================================\n");
         AppendRem(LogString);
         AppendLoc(LogString);

         if (!Match)
         {
// AppendRem("\n\nNo matches found for any requested file names.\n");
            AppendRem(SA[3736]);
// LogEvent("|  No matches for requested file names.");
            LogEvent(SA[3737]);

  /* If no matches were found, and IF we have a default
   * file defined, ** we'll send it here. */

            if (Stricmp(DefaultFile, "DEADBEEF"))
            {
               ASPrintf(NULL, LogString, "%s\n", DefaultFile);
               AppendRLO(LogString);
// ASPrintf(NULL,LogString,"Sending default file [%s]
// instead.\n",DefaultFile);
               ASPrintf(NULL, LogString, SA[3738], DefaultFile);
               AppendRem(LogString);
// ASPrintf(NULL,LogString,"Sent default file
// [%s].\n",DefaultFile);
               ASPrintf(NULL, LogString, SA[3739], DefaultFile);
               AppendLoc(LogString);

// ASPrintf(NULL,LogString,"|  Sending default file
// %s",DefaultFile);
               ASPrintf(NULL, LogString, SA[3740], DefaultFile);
               LogEvent(LogString);
            }
         }
         else
         {
// ASPrintf(NULL,LogString,"\n\n%d files sent, %d total
// bytes\n",TotalFiles,TotalBytes);
            ASPrintf(NULL, LogString, SA[3741], TotalFiles, TotalBytes);
            AppendRem(LogString);
            AppendLoc(LogString);
         }
      }

   AppendRem("\nEnd of session\n");

/* Write message to sysop */

   if (!CreateMsg(RemSysop))
   {
// LogEvent("|  Error creating outgoing message!");
      LogEvent(SA[3742]);
   }
   else
   {
      char               *filepath;
      char               *filepath2;

      filepath = strdup(Out);
      filepath2 = strdup(Out);
      stcgfp(filepath, Out);
      ASPrintf(NULL, filepath2, "%s00000011.PKT", filepath);
      Copy("Ram:00000011.PKT", filepath2);
      DeleteFile("Ram:00000011.PKT");
      ASPrintf(NULL, LogString, "^%s", filepath2);
      AppendRLO(LogString);
   }

   if (Stricmp(SysopName, "DEADBEEF"))
   {
// LogEvent("|  Sending report to sysop");
      LogEvent(SA[3743]);
      ASPrintf(NULL, TEMPS, "DLG:SendMsg -f \"DLG FreqServer\" -s \"File Request Report\" -b %s -q -n -r \"%s\"", REPORT, SysopName);
      system(TEMPS);
   }

   SmartRename(TEMPRLO, Out);
// ASPrintf(NULL,LogString,"|  Moved temp.rlo to %s.",Out);
   ASPrintf(NULL, LogString, SA[3744], Out);
   LogEvent(LogString);

// LogEvent("*- End DLGFreq\n");
   LogEvent(SA[3745]);

   ShutDown(0);
}

void  AddToMagicList(char *File, char *Path, char *PW, char *Desc, BOOL Blind)
{
   char               *p, *f, *s;

   struct Magic       *FileNode;
   struct SearchCookie *sc;

   f = calloc(1, strlen(Path) + 10);
   p = calloc(1, strlen(Path) + 10);
   s = calloc(1, 512);

   stcgfn(f, Path);
   stcgfp(p, Path);

   if(!Blind)
   {
      sc = SearchStart(p, f);

      s = SearchNext(sc);

      SearchEnd(sc);
   
      free(sc);
   }
   else
   {
      s = strdup(f);
   }
   
   free(f);

   if (s == NULL)
   {
// ASPrintf(NULL,LogString,"!  Failed to match filename %s for magic name %s!",Path,File);
      ASPrintf(NULL, LogString, SA[3746], Path, File);
      LogEvent(LogString);
      free(p);
      free(s);
      return;
   }

   f = calloc(1, strlen(s) + strlen(p) + 10);

   strmfp(f, p, s);

/* 
 * If this is the first entry, we create the list, else we
 * simply add one to the end of the list.
 */

   if (!MagicFileList)
   {
      MagicFileList = calloc(1, sizeof(struct List));
      NewList(MagicFileList);
   }

   FileNode = calloc(1, sizeof(struct Magic));

   FileNode->mg_Name = strdup(File);
   FileNode->mg_Path = strdup(f);

   if (PW != NULL)
      FileNode->mg_PW = strdup(PW);

   if (Desc != NULL)
      FileNode->mg_Desc = strdup(Desc);

   free(f);
   free(s);
   free(p);

   AddTail(MagicFileList, (struct Node *) FileNode);

   return;
}

void                FreeMagicList()
{
   struct Magic       *ThisNode;
   struct Magic       *ThatNode;

   ThisNode = (struct Magic *) MagicFileList->lh_Head;

   while (ThatNode = (struct Magic *) (ThisNode->mg_Node.ln_Succ))
   {
      free(ThisNode);
      ThisNode = ThatNode;
   }

   free(MagicFileList);

   return;
}

void                AddToReqList(char *File, char *PW)
{
   struct ReqFile     *FileNode;

/* If this is the first entry, we create the list, else we
      * simply add one ** to the end of the list. */

   if (!ReqList)
   {
      ReqList = calloc(1, sizeof(struct List));

      NewList(ReqList);
   }

   FileNode = calloc(1, sizeof(struct ReqFile));

   FileNode->rf_Name = strdup(File);

   if (PW)
      FileNode->rf_PW = strdup(PW);

   FileNode->rf_Matched = FALSE;

   AddTail(ReqList, (struct Node *) FileNode);

   return;
}

void                FreeReqList()
{
   struct ReqFile     *ThisNode;
   struct ReqFile     *ThatNode;

   ThisNode = (struct ReqFile *) ReqList->lh_Head;

   while (ThatNode = (struct ReqFile *) (ThisNode->rf_Node.ln_Succ))
   {
      free(ThisNode);
      ThisNode = ThatNode;
   }

   free(ReqList);

   return;
}

void                AddToLockoutList(char *Address)
{
   struct Node        *AddressNode;

/* If this is the first entry, we create the list, else we
      * simply add one ** to the end of the list. */

   if (!LockoutList)
   {
      LockoutList = calloc(1, sizeof(struct List));
      NewList(LockoutList);
   }

   AddressNode = calloc(1, sizeof(struct Node));

   AddressNode->ln_Name = strdup(Address);

   AddTail(LockoutList, AddressNode);

   return;
}

void                FreeLockoutList()
{
   struct Node        *ThisNode;
   struct Node        *ThatNode;

   ThisNode = LockoutList->lh_Head;

   while (ThatNode = ThisNode->ln_Succ)
   {
      free(ThisNode);
      ThisNode = ThatNode;
   }

   free(LockoutList);

   return;
}

BOOL                CheckForDupe(char *File, unsigned long Size)
{
   struct Dupe        *ThisNode;
   struct Dupe        *ThatNode;

// If this is the first entry, we create the list, else we
// simply add one
// to the end of the list.

   if (!DupeList)
   {
      DupeList = calloc(1, sizeof(struct List));
      NewList(DupeList);
   }
   else
   {
      ThisNode = (struct Dupe *) DupeList->lh_Head;

      while (ThatNode = (struct Dupe *) (ThisNode->dup_Node.ln_Succ))
      {
         if ((!Stricmp(ThisNode->dup_Name, File)) && (ThisNode->dup_Size == Size))
            return (TRUE);

         ThisNode = ThatNode;
      }
   }

   ThisNode = calloc(1, sizeof(struct Dupe));

   ThisNode->dup_Name = strdup(File);

   ThisNode->dup_Size = Size;

   AddTail(DupeList, (struct Node *) ThisNode);

   return (FALSE);
}

void                FreeDupeList()
{
   struct Dupe        *ThisNode;
   struct Dupe        *ThatNode;

   ThisNode = (struct Dupe *) DupeList->lh_Head;

   while (ThatNode = (struct Dupe *) (ThisNode->dup_Node.ln_Succ))
   {
      free(ThisNode);
      ThisNode = ThatNode;
   }

   free(DupeList);

   return;
}

BOOL                CreateMsg(char *To)
{
   BPTR                pktfp;

   struct FidoHeader
   {
      UWORD               OrigNode;
      UWORD               DestNode;
      UWORD               Year;
      UWORD               Month;
      UWORD               Day;
      UWORD               Hour;
      UWORD               Minute;
      UWORD               Second;
      UWORD               Baud;
      UWORD               PktType;
      UWORD               OrigNet;
      UWORD               DestNet;
      UWORD               ProdCode;
      char                Password[8];
      UWORD               OrigZone;
      UWORD               DestZone;
      char                Fill[20];


// Packed message

      UWORD               MsgType;
      UWORD               OrigNode2;
      UWORD               DestNode2;
      UWORD               OrigNet2;
      UWORD               DestNet2;
      UWORD               Attribute;
      UWORD               Cost;
      char                Date[20];
   }
                      *header;

   UBYTE               clock[8];

   getclk(clock);

   header = calloc(1, sizeof(struct FidoHeader));

   header->OrigNode = FixNumber(Orig->Node);
   header->DestNode = FixNumber(Dest->Node);
   header->Year = FixNumber((UWORD) (clock[1] - 1980));
   header->Month = FixNumber((UWORD) (clock[2] - 1));
   header->Day = FixNumber((UWORD) (clock[3]));
   header->Hour = FixNumber((UWORD) (clock[4]));
   header->Minute = FixNumber((UWORD) (clock[5]));
   header->Second = FixNumber((UWORD) (clock[6]));
   header->Baud = FixNumber(0);
   header->PktType = FixNumber(2);
   header->OrigNet = FixNumber(Orig->Net);
   header->DestNet = FixNumber(Dest->Net);
   header->ProdCode = FixNumber(254);
// Password is empty
   header->OrigZone = FixNumber(Orig->Zone);
   header->DestZone = FixNumber(Dest->Zone);
// Filler is empty

// Begin packed message

   header->MsgType = FixNumber(2);
   header->OrigNode2 = FixNumber(Orig->Node);
   header->DestNode2 = FixNumber(Dest->Node);
   header->OrigNet2 = FixNumber(Orig->Net);
   header->DestNet2 = FixNumber(Dest->Net);
   header->Attribute = FixNumber(1);
   header->Cost = FixNumber(0);
   MDate(header->Date);

   pktfp = Open("RAM:00000011.PKT", MODE_NEWFILE);

   if (pktfp == NULL)
   {
// LogEvent("!  Could not open outgoing message packet!");
      LogEvent(SA[3747]);
      free(header);
      return (FALSE);
   }

   FWrite(pktfp, header, sizeof(struct FidoHeader), 1);

   free(header);
   Flush(pktfp);
   FPuts(pktfp, To);
   Flush(pktfp);
   FPutC(pktfp, 0);
   Flush(pktfp);
// FPuts(pktfp,"DLG Freq Server");
   FPuts(pktfp, SA[3748]);
   Flush(pktfp);
   FPutC(pktfp, 0);
   Flush(pktfp);
// FPuts(pktfp,"File Request Report");
   FPuts(pktfp, SA[3749]);
   Flush(pktfp);
   FPutC(pktfp, 0);
   Flush(pktfp);

// INTL kludge

   if (Dest->Zone != Orig->Zone)
   {
      AFPrintf(NULL, pktfp, "%cINTL %d:%d/%d %d:%d/%d\r", 1, Dest->Zone, Dest->Net, Dest->Node, Orig->Zone, Orig->Net, Orig->Node);
      Flush(pktfp);
   }

// TOPT/FMPT kludge

   if (Dest->Point != 0)
   {
      AFPrintf(NULL, pktfp, "%cTOPT %d\r", 1, Dest->Point);
      Flush(pktfp);
   }

   if (Orig->Point != 0)
   {
      AFPrintf(NULL, pktfp, "%cFMPT %d\r", 1, Orig->Point);
      Flush(pktfp);
   }

   Close(pktfp);

   Cat("RAM:00000011.PKT", MESSAGEFILE, NULL);

// Tack two nulls on end of message

   pktfp = Open("RAM:00000011.PKT", MODE_READWRITE);
   Seek(pktfp, 0, OFFSET_END);
   FPutC(pktfp, 0);
   FPutC(pktfp, 0);
   Flush(pktfp);
   Close(pktfp);

   return (TRUE);
}

void                ShutDown(int FailCode)
{
   if (NodelistBase)
      CloseLibrary(NodelistBase);

   if (DLGBase)
      CloseLibrary(DLGBase);

   if (MagicFileList)
      FreeMagicList();

   if (ReqList)
      FreeReqList();

   if (LockoutList)
      FreeLockoutList();

   if (DupeList)
      FreeDupeList();

   if (Exists(MESSAGEFILE))
      DeleteFile(MESSAGEFILE);

   if (Exists(REPORT))
      DeleteFile(REPORT);

   exit(FailCode);
}

void                LogEvent(char *LogThis)
{
   AppendFile(LOGFILE, LogThis);
}

BOOL                AppendRem(char *Event)
{
   BPTR                fp;

   fp = Open(MESSAGEFILE, MODE_READWRITE);

   if (fp == NULL)
      {
// LogEvent("|  Unable to open outgoing Message file!");
         LogEvent(SA[3750]);
         return (FALSE);
      }

   Seek(fp, 0, OFFSET_END);
   FPuts(fp, Event);

   Close(fp);
   return (TRUE);
}

BOOL                AppendLoc(char *Event)
{
   BPTR                fp;

   fp = Open(REPORT, MODE_READWRITE);

   if (fp == NULL)
   {
// LogEvent("|  Unable to open report file!");
      LogEvent(SA[3751]);
      return (FALSE);
   }

   Seek(fp, 0, OFFSET_END);
   FPuts(fp, Event);

   Close(fp);
   return (TRUE);
}

BOOL                AppendRLO(char *Name)
{
   BPTR                fp;
   char               *t;

   t = strdup(strtok(Name, "\n"));

   fp = Open(TEMPRLO, MODE_READWRITE);

   if (fp == NULL)
   {
// LogEvent("|  Unable to open t:temp.rlo!");
      LogEvent(SA[3752]);
      return (FALSE);
   }

   Seek(fp, 0, OFFSET_END);
   AFPrintf(NULL, fp, "%s\n", t);

   Close(fp);

   return (TRUE);
}

USHORT FixNumber(USHORT number)
{
   UBYTE lo;
   UBYTE hi;
   USHORT result;
   
   lo=(number & 0xff00)/256;
   hi=(number & 0x00ff);

   result=hi*256+lo;
   return(result);
}

