#include "DLGTick.h"

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

int   DoFlow(char *, char *, char *, char, int);
int   CreateTICName(char *);
int   BuildZMsg(char *, char *, char *, char *, BOOL, int);
int   GetPW(char *, char *);
int   BuildMulti(char *, char *, char *, char *, BOOL, int);
int   DoWeHaveATickDir(char *);
BOOL  BuildSeenBy(char *, char *);
int   HashCodes(char *,char *,char *,char *,ULONG);
void  ReplaceFrom(char *FileName);


/// SendLoop()
BOOL SendLoop( char *Target,char *TargPath,char *Echo,char *Desc,
               BOOL Ticked, char *TickFile, struct TicAddr *Addr)
{
   /*
   ** SendLoop() -   Process a file into tick echos and DLG local areas.
   **                Can be called from within a tick loop or a hatch loop.
   **
   ** Inputs:  Target   - the name of the file being processed
   **          TargPath - the path + filename of the file being processed
   **          Echo     - the tagname of the tick echo being processed for
   **          Desc     - Description of the file being processed
   **          Ticked   - TRUE if this came from DoTick(), FALSE if from Hatch()
   **          TickFile - Name of original TICK file if from a TICK loop
   **          Addr     - Addres of sender if this file was ticked in.
   */

   BOOL  InArea = FALSE;

   BPTR  mfh = NULL;
   BPTR  cfh = NULL;

   char *Storage = NULL;
   char  t[1000];
   char  OutTicName[128];
   char  FinalPath[512];

   ULONG Size=0;


   Log("-> SendLoop()");

   // Get size of file
   FileSize(TargPath,&Size);

///   Execute program if it is set to do so for this area

   if(TicArea[AIndex].Execute != NULL)
   {
      char *Execute;

      Execute = strdup(TicArea[AIndex].Execute);
      HashCodes(Execute,TargPath,Target,TicArea[AIndex].AreaName,Size);
      Spawn(NULL,sout,Execute);
   }
//-
///   Do for local file area
   if(TicArea[AIndex].Loc == TRUE)
   {
      if(!DLGFile(TargPath,Target,Desc,Echo,TicArea[AIndex].DLGArea,Ticked,TickFile,TicArea[AIndex].Replace,TicArea[AIndex].Free))
      {
         return(FALSE);
      }
   }
//-
///   Do for passthrough file area
   if(TicArea[AIndex].Pass == TRUE)
   {
      /* move the file to our tick-only area, which happens to be TICK:area */

      AFPrintf(NULL,sout,"\t\tCopying file to Tick:%s\n",Echo);

      Storage = calloc(1,strlen(Echo) + strlen(Target) + 20);
      ASPrintf(NULL,Storage,"TICK:%s/%s",Echo,Target);

      DoWeHaveATickDir(Echo);

///   if target and storage are the same, return FALSE
      if(!Stricmp(TargPath,Storage))
      {
         AFPrintf(NULL,sout,"Source and Destination for file move identical\n");
         Log(" ! Source and Destination for file are identical - reconfigure?");
         free(Storage);
         return(FALSE);
      }
//-

///   If file already exists and we are not REPLACEing, reject
      if((TicArea[AIndex].Replace == FALSE) && (Exists(Storage)))
      {
         AFPrintf(NULL,sout,"%s rejected -- duplicate\n",Ticked?"Tick":"Hatch");
         ASPrintf(NULL,logstring," ! %s rejected -- duplicate",Ticked?"Tick":"Hatch");
         Log(logstring);

         if(Ticked)
            MoveToBad(TickFile,TargPath,Target,"DUPLICATE",Desc,Echo);

         return(FALSE);
      }
//-

///   Copy file to storage
      {
         long rc;

         rc = Copy(TargPath,Storage);

///      If copy OK, set comment.
         if(rc == 0)
         {
            SetComment(Storage,Ticked?"Ticked Here":"Hatched Here");
         }
//-

         if(rc == 1)
         {
            Log(" ! Copy of file failed -- can't open source!");
            return(FALSE);
         }

         if(rc == 2)
         {
            Log(" ! Copy of file failed -- can't open destination!");
            return(FALSE);
         }

         if(rc == -1)
         {
            Log(" ! Copy of file failed -- destination drive full!");
            return(FALSE);
         }

      }
//-
   }
//-

   // Remove file if we ticked it; otherwise, leave it alone
   if(Ticked == TRUE)
      DeleteFile(TargPath);

/// Set target path
   if(TicArea[AIndex].Pass == TRUE)
   {
      // Could be local, but passthru exists so we prefer to use it.
      strcpy(FinalPath,Storage);
   }
   else
   {
      // must be local only
      if(GetPath(TargPath,TicArea[AIndex].DLGArea,NULL,NULL) == -1)
      {
         Log(" ! Error getting file path!");
         return(FALSE);
      }

      strmfn(FinalPath,NULL,TargPath,Target,NULL);
   }

   if (Storage) free(Storage);
//-
/// Add our downlinks to the seen-bys in the master tic file
   if(!BuildSeenBy(Echo,Addr->Address))
   {
      Log(" ! Failure to build seen-bys for master tic template!");
      return(FALSE);
   }
//-
/// Now we tick our files to our downlinks

/// Open master TIC file template copy

   mfh = Open("T:Before.Master",MODE_READWRITE);

   if(!mfh)
   {
      Log(" ! Could not open master TIC file!");
      return(FALSE);
   }
//-

/// Open config file

   cfh = Open("FIDO:DLGMail.TIC",MODE_OLDFILE);

   if(!cfh)
   {
      Log(" ! Could not open DLGMAIL.TIC file!");
      Close(mfh);
      return(FALSE);
   }
//-

/// Loop through config file and find potential destinations

   InArea = FALSE;

   while (FGets(cfh, t, 999))
   {
      BOOL  AlreadySent = FALSE;
      char *par[20];
      int   c;

      strcpy(t, stpblk(t));
      if (strlen(t) == 0)  continue;         // Skip empty lines
      if (!Strnicmp(t, ";", 1)) continue;    // Skip comments
      c = ArgParse(t, par, 19);

///   AREA
      if(!Stricmp(par[0],"AREA"))
      {
         if(InArea == TRUE)
         {
            // Finished reading, exit
            break;
         }

         if(!Stricmp(par[1],TicArea[AIndex].AreaName))
         {
            InArea = TRUE;
         }

         continue;
      }
//-

///   TO
      if(!Stricmp(par[0],"TO"))
      {
         if(InArea == TRUE)
         {
            BOOL Reverse;
            char s[1000];
            char Flavor = '!';
            char PW[50];
            int j;

            // We're in the right area, this is potentially valid
            if(Addr)
            {
               if(CompareFidoAddress(Addr->Address,par[1]))
               {
                  // don't send to this node, this was who sent it to us
                  continue;
               }
            }

///         Loop through master tic template and see if this address has got file already
            AlreadySent = FALSE;
            Seek(mfh,0,OFFSET_BEGINNING);

            while(FGets(mfh,s,999))
            {
               char *p[5];
               int  cnt;

               strcpy(s,stpblk(s));
               if(strlen(s) == 0) continue;
               cnt = ArgParse(s,p,4);

               if(!Stricmp(p[0],"SEENBY"))
               {
                  // Check seenby
                  if(CompareFidoAddress(p[1],par[1]))
                  {
                     // We don't want to send to someone that already has it
                     AlreadySent = TRUE;
                     break;
                  }

                  continue;
               }
            }
//-

            // Don't dupe a file to someone that already got it
            if(AlreadySent == TRUE) continue;

///         Go through par[2] and parse it; it is supposedly the flow flags
            Upper(par[2]);

            for(j = 0; j < strlen(par[2]); j++)
            {
///            Switch on par[2][j]
               switch (par[2][j])
               {
                  case 'C':
                     Flavor = 'C';
                     break;

                  case 'N':
                  case 'F':
                     Flavor = 'F';
                     break;

                  case 'D':
                     Flavor = 'D';
                     break;

                  case 'H':
                     Flavor = 'H';
                     break;

                  case '*':
                     Flavor = '*';
                     break;

                  case 'R':
                  case 'W':
                     Reverse = TRUE;
                     break;

                  case '+':
                     break;

                  default:
                     break;
               }
//-
            }
//-

            // Skip sending file to node if no flavor specified or found
            if( (Flavor == '*') || (Flavor == '!'))
            {
               AFPrintf(NULL,sout,"Flavor = * or !, not sending\n");
               continue;
            }

            AFPrintf(NULL,sout,"\t\tSending file %s to %s\n",Target,par[1]);
            ASPrintf(NULL,logstring," > Sending file %s to %s",Target,par[1]);
            Log(logstring);

///         Create outgoing tic file
            CreateTICName(OutTicName);

            Copy(MASTER,OutTicName);
            SetComment(OutTicName,par[1]);

            if( GetPW(par[1],PW))
            {
               StripQuote(PW);
               AppendToTIC(OutTicName,PW);
            }

            ReplaceFrom(OutTicName);

//-

///         Create flow file

            DoFlow(FinalPath,OutTicName,par[1],Flavor,Reverse);
            continue;
//-
         }

         continue;
      }
//-
   }
//-

   if(mfh) Close(mfh);
   DeleteFile("T:Before.Master");
//-
/// Loop through config file and make announcements

   Seek(cfh,0,OFFSET_BEGINNING);
   InArea = FALSE;

   while (FGets(cfh, t, 999))
   {
      char *par[20];
      char *dup;
      int   c;

      strcpy(t, stpblk(t));
      dup = strdup(t);

      if (strlen(t) == 0)  continue;         // Skip empty lines

      if (!Strnicmp(t, ";", 1)) continue;    // Skip comments

      c = ArgParse(t, par, 19);

///   AREA
      if(!Stricmp(par[0],"AREA"))
      {
         if(InArea == TRUE)   // Finished reading, exit
            break;

         if(!Stricmp(par[1],TicArea[AIndex].AreaName))
            InArea = TRUE;

         continue;
      }
//-
///   ANNOUNCE
      if(!Stricmp(par[0],"ANNOUNCE"))
      {
         if(InArea == TRUE)
         {
            BOOL in = FALSE;
            char *d;
            int k;
            int j = 0;

            d = strdup(dup);

            for(k=8;k<strlen(dup);k++)
            {
               if(in)
               {
                  if((dup[k] == '\n')  || (dup[k] == ';'))
                  {
                     d[j] = '\0';
                     break;
                  }

                  d[j] = dup[k];
                  j++;
               }
               else
               {
                  if( isspace(dup[k]))
                  {
                     continue;
                  }
                  else
                  {
                     in = TRUE;
                     d[j] = dup[k];
                     j++;
                  }
               }
            }

            BuildZMsg(Target, Echo, Desc, d, Ticked, Size);
         }

         continue;
      }
//-

   }
//-

   if(cfh) Close(cfh);

   Log("<- End SendLoop()");
   return(TRUE);
}
//-
/// BuildSeenBy()
BOOL BuildSeenBy(char *Echo, char *Addr)
{
   /*
   ** BuildSeenBy - appends seen-bys for our downlinks to the outgoing
   **               master tic file template.
   **
   ** Inputs:  Echo - the name of the echo we're ticking
   **          Addr - the address of the system we got this file from
   **                 NULL if we're hatching
   **
   ** Return: TRUE of OK, FALSE if failed
   */

   BOOL InArea = FALSE;

   BPTR fh = NULL;
   BPTR cfg = NULL;

   char t[1000];


   Copy(MASTER,"T:Before.Master");

   fh = Open(MASTER,MODE_READWRITE);

   if(!fh)
   {
      return(FALSE);
   }

   cfg = Open("Fido:DLGMail.TIC",MODE_OLDFILE);

   if(!cfg)
   {
      Close(fh);
      return(FALSE);
   }

   Seek(fh,0,OFFSET_BEGINNING);

/// Loop through config file and find potential destinations

   InArea = FALSE;

   while (FGets(cfg, t, 999))
   {
      BOOL  AlreadySent = FALSE;
      char *par[20];
      int   c;

      strcpy(t, stpblk(t));

      if (strlen(t) == 0)  continue;         // Skip empty lines

      if (!Strnicmp(t, ";", 1)) continue;    // Skip comments

      c = ArgParse(t, par, 19);

///   AREA
      if(!Stricmp(par[0],"AREA"))
      {
         if(InArea == TRUE)
         {
            // Finished reading, exit
            break;
         }

         if(!Stricmp(par[1],TicArea[AIndex].AreaName))
         {
            InArea = TRUE;
         }

         continue;
      }
//-

///   TO
      if(!Stricmp(par[0],"TO"))
      {
         if(InArea == TRUE)
         {
            char s[1000];

            // We're in the right area, this is potentially valid
            if(Addr)
            {
               if(CompareFidoAddress(Addr,par[1]))
               {
                  // don't send to this node, this was who sent it to us
                  continue;
               }
            }

///         Loop through master tic template and see if this address has got file already
            Seek(fh,0,OFFSET_BEGINNING);
            while(FGets(fh,s,999))
            {
               char *p[5];
               int  cnt;

               strcpy(s,stpblk(s));
               if(strlen(s) == 0) continue;
               cnt = ArgParse(s,p,4);

               if(!Stricmp(p[0],"SEENBY"))
               {
                  // Check seenby
                  if(CompareFidoAddress(p[1],par[1]))
                  {
                     // We don't want to send to someone that already has it
                     AlreadySent = TRUE;
                     break;
                  }

                  continue;
               }
            }
//-

            // Don't dupe a file to someone that already got it
            if(AlreadySent == TRUE) continue;

            // Put node in seen-by
            Seek(fh,0,OFFSET_END);
            AFPrintf(NULL,fh,"SEENBY %s\r\n",par[1]);
         }

         continue;
      }
//-

   }
//-

   if(fh) Close(fh);
   if(cfg) Close(cfg);

   return(TRUE);
}
//-
/// DoFlow()
int DoFlow(char *filepath,char *ticfile,char *address,char flavor,int whiner)
{
   char flow[100];
   char z[10],N[10],n[10],p[10];
   BPTR fp;

   strsfn(address,z,N,n,p);
   ASPrintf(NULL,flow,"OUTBOUND:%d.%d.%d.%d.%cLO",atoi(z),atoi(N),atoi(n),atoi(p),flavor);
   Upper(flow);

   fp = Open(flow,MODE_READWRITE);

   if(fp)
   {
      Seek(fp,0,OFFSET_END);

      if(whiner)
      {
         AFPrintf(NULL,fp,"%s\n^%s\n",filepath,ticfile);
      }
      else
      {
         AFPrintf(NULL,fp,"^%s\n%s\n",ticfile,filepath);
      }

      Close(fp);
   }
   return(1);
}
//-
/// CreateTICName()
int CreateTICName(char *string)
{
   /* create the tic file name (including path) */

   unsigned char clock[10];
   ULONG t;
   char name[50];

   ULONG N;
   ULONG n;

   N=_MyNet;
   N=N<<17;

   n=_MyNode;
   n=n&0xef;
   n=n<<2;

   while(1)
   {
      char jgt[100];

      getclk(clock);
      t=((clock[2]<<28)+(clock[3]<<23)+(clock[4]<<18)+(clock[5]<<12)+(clock[6]<<6)+(clock[7]/2));

      LToX(t,jgt);

      ASPrintf(NULL,name,"%08.8s.TIC",jgt);

      name[0]='T';
      name[1]='K';

      ASPrintf(NULL,string,"OUTBOUND:%s",name);

      if(!(Exists(string))) break;
   }
   return(1);
}
//-
/// BuildZMsg
BuildZMsg(char *file, char *area, char *desc, char *msgarea, BOOL ticked, int size)
{
   /* msg area might be a name for a private message :-) */

   BPTR fp;

   if(!Strnicmp(msgarea,"MULTI",5))
   {
      ASPrintf(NULL,logstring,"-> BuildZMsg: routing %s",msgarea);
      Log(logstring);
      return(BuildMulti(file,area,desc,msgarea,ticked,size));
   }

   DeleteFile(TICKREPLY);

   fp = Open(TICKREPLY,MODE_NEWFILE);

   if(fp)
   {
      // Changed 2.63.2.29
      AFPrintf(NULL,fp,"FROM: %s\n",TicArea[AIndex].Uploader);

      if(atoi(msgarea))
      {
         AFPrintf(NULL,fp,"TO: All\n");
         AFPrintf(NULL,fp,"AREA: %d\n",atoi(msgarea));
         NotateExp(atoi(msgarea));
      }
      else
      {
         AFPrintf(NULL,fp,"TO: %s\n",msgarea);
         AFPrintf(NULL,fp,"TYPE: P\n");
      }

      if(ticked)
      {
         char *tempy;

         tempy = strdup(file);
         Upper(tempy);

         AFPrintf(NULL,fp,"SUBJECT: File [%s] Tick'ed By DLGTick\n",tempy);
      }
      else
      {
         char *tempy;

         tempy = strdup(file);
         Upper(tempy);

         AFPrintf(NULL,fp,"SUBJECT: File [%s] Hatched By DLGTick\n",tempy);
      }

      Upper(area);

      AFPrintf(NULL,fp,"BODY:\n");
      AFPrintf(NULL,fp,"      The following file: ");
      AFPrintf(NULL,fp,"%s\n",file);

      AFPrintf(NULL,fp,"      Of size (in bytes): ");
      AFPrintf(NULL,fp,"%d\n\n",size);

      if(ticked)
         AFPrintf(NULL,fp,"has been ticked through: ");
      else
         AFPrintf(NULL,fp,"   has been hatched from: ");

      AFPrintf(NULL,fp,"%d:%d/%d.%d\n\n",_MyZone,_MyNet,_MyNode,_MyPoint);
      AFPrintf(NULL,fp,"       in file echo area: ");
      AFPrintf(NULL,fp,"%s\n\n\n",area);
      AFPrintf(NULL,fp,"It was supplied with the following description:\n\n");
      AFPrintf(NULL,fp,"%s\n",desc);

      Close(fp);
      SendMsg(TICKREPLY);
      DeleteFile(TICKREPLY);
   }
   return(1);
}
//-
/// GetPW
GetPW(char *ticto, char *pwstring)
{
   BPTR fh = NULL;

   char buffer[100];


   fh = Open("Fido:DLGMail.TIC",MODE_OLDFILE);

   if(!fh)
   {
      strcpy(pwstring,"");
      return(0);
   }

   while(FGets(fh,buffer,99))
   {
      char *par[5];
      int   c;

      strcpy(buffer, stpblk(buffer));

      if (strlen(buffer) == 0)  continue;         // Skip empty lines
      if (!Strnicmp(buffer, ";", 1)) continue;    // Skip comments

      c = ArgParse(buffer, par, 4);

      if(!Stricmp(par[0],"PASSWORD") || !Stricmp(par[0],"PW"))
      {
         if(c < 2)
         {
            Close(fh);
            strcpy(pwstring,"");
            return(0);
         }

         if(CompareFidoAddress(par[1],ticto))
         {
            if(c < 3)
            {
               strcpy(pwstring,"");
               Close(fh);
               return(0);
            }

            StripQuote(par[2]);
            ASPrintf(NULL,pwstring,"PW %s",par[2]);
            Close(fh);
            return(1);
         }
      }
   }

   strcpy(pwstring,"");
   Close(fh);
   return(0);
}
//-
/// BuildMulti()
int BuildMulti(char *file,char *area,char *desc,char *msgarea,BOOL ticked,int size)
{
   BPTR fp;
   char tmp[100];
   char *s;
   char *who;
   BPTR fh;

   ASPrintf(NULL,logstring,"-> BuildMulti: routing %s %s",ticked ? "Ticked" : "Hatched", msgarea);
   Log(logstring);

   fh=CreateDir("T:TIC_MULTI");
   if(fh) UnLock(fh);

   s=NULL;
   s=strchr(msgarea,' ');

   if(!s) s=strchr(msgarea,'\t');

   if(s) s=stpblk(s);

   if(s)
   {
      who=s;

      while(s=strchr(s,' '))
         *s='_';

      ASPrintf(NULL,tmp,"T:TIC_MULTI/%s",who);

      fp = Open(tmp,MODE_READWRITE);

      if(fp)
      {
         char *tempy;
         char *tempytoo;

         Seek(fp,0,OFFSET_END);

         tempy = strdup(file);
         Upper(tempy);
         tempytoo = strdup(area);
         Upper(tempytoo);

         AFPrintf(NULL,fp,"File: %-30.30s (%d bytes)   File Echo: %.15s\n",tempy,size,tempytoo);
         AFPrintf(NULL,fp,"Desc: %s\n\n",desc);
         Close(fp);
      }
   }
   else
      Log("! No argument after 'ANNOUNCE MULTI' in config file");
   return 1;
}
//-
/// DoWeHaveATickDir()
DoWeHaveATickDir(char *area)
{
   BPTR fh;
   char pth[50];

   ASPrintf(NULL,pth,"TICK:%s",area);

   if(Exists(pth))
   {
      return(1);
   }
   else
   {
      fh=CreateDir(pth);

      if(fh)
      {
         UnLock(fh);
         return(1);
      }
      else
      {
         AFPrintf(NULL,sout,"Can't create passthru directory %s!!\n",pth);
         Log(" ! Can't create passthru directory!!");
      }
   }
   return(0);
}
//-
/// HashCodes  New 2.63.2.28
HashCodes(char *string,char *path,char *fname,char *Echo, ULONG fsize)
{
   char *workbuf;
   char *s;
   char token[200];
   char sw[10];

   s=strchr(string,'%');
   if(s==NULL) return(1); /* nothing to hash */

   workbuf=(void *)calloc(strlen(string)+200,1);

   if(workbuf)
   {
      *workbuf=NULL;

      s=string;

      while(1)
      {
         if(*s==NULL) break;

         s=stptok(s,token,sizeof(token),"%");
         strcat(workbuf,token);

         /* s is sitting on the % */

         if(*s)
         {
            s++;
         }  /*if*/
         else
         {
            break;
         }  /*else*/

         if(*s && strlen(s)>3)
         {
            strncpy(sw,s,5);
            sw[4]=NULL;
            s+=4;

            if(!Stricmp(sw,"NAME")) strcat(workbuf,fname);
            if(!Stricmp(sw,"PATH")) strcat(workbuf,path);
            if(!Stricmp(sw,"ECHO")) strcat(workbuf,Echo);

            if(!Stricmp(sw,"SIZE"))
            {
               char t[20];
               ASPrintf(NULL,t,"%ld",fsize);
               strcat(workbuf,t);
            }
         }
      }

      strcpy(string,workbuf);
      free(workbuf);
   }

   return(1);
}
//-
/// ReplaceFrom() New 1.27
void ReplaceFrom(char *FileName)
{
   BPTR fh  =  NULL;
   BPTR ofh =  NULL;
   char t[1024];
   char BaseAddr[64];
   char *p, *s;
   char  k[1024];


   if(0 == strlen(TicArea[AIndex].UseAddress))  return;

   ASPrintf(NULL,BaseAddr,"%ld:%ld/%ld",_MyZone,_MyNet,_MyNode);

   fh = Open(FileName,MODE_OLDFILE);

   if(!fh) return;

   ofh = Open("t:temp.dlgtick",MODE_NEWFILE);

   if(!ofh)
   {
      Close(fh);
      return;
   }

   AFPrintf(NULL,sout,"\t\tUsing address %s\n",TicArea[AIndex].UseAddress);
   ASPrintf(NULL,t," > Using address %s\n",TicArea[AIndex].UseAddress);
   Log(logstring);

   while(FGets(fh,t,1024))
   {
      strcpy(k,t);
      p = strdup(strtok(t," "));
      s = strdup(strtok(NULL,"\n\r\0"));

      if(!p)
      {
         FPuts(ofh,k);
         continue;
      }

      if(!s)
      {
         FPuts(ofh,k);
         continue;
      }

      if(!Stricmp(p,"FROM"))
      {
         if(!Stricmp(s,BaseAddr))
         {
            AFPrintf(NULL,ofh,"FROM %s\r\n",TicArea[AIndex].UseAddress);
            continue;
         }
      }

      if(!Stricmp(p,"ORIGIN"))
      {
         if(!Stricmp(s,BaseAddr))
         {
            AFPrintf(NULL,ofh,"ORIGIN %s\r\n",TicArea[AIndex].UseAddress);
            continue;
         }
      }

      if(!Stricmp(p,"SEENBY"))
      {
         if(!Stricmp(s,BaseAddr))
         {
            FPuts(ofh,k); Flush(ofh);
            AFPrintf(NULL,ofh,"SEENBY %s\r\n",TicArea[AIndex].UseAddress);
            continue;
         }
      }

      FPuts(ofh,k);
      Flush(ofh);
   }

   Close(ofh);
   Close(fh);

   Copy("t:temp.dlgtick",FileName);
   DeleteFile("t:temp.dlgtick");
   return;
}
//-
