#include "DLGtick.h"
#include "ivn.h"

#include <link/io.h>

// ------- ======== ---------

BOOL  DoTick(char *, char *);
BOOL  ConfirmPassword(char *, char *);
int   BuildBadZMsg(char *, char *, char *, char *);
BOOL  BuildMasterTic(char *);

// ------- ======== ---------


///   Tick()
BOOL Tick(char *TicPath)
{
   /* Tick()
   **
   ** Input: the path to look for ????????.TIC files
   **
   ** Return: TRUE if one or more files processed correctly, FALSE if a fatal
   **         error was detected.
   **
   ** We have the TicPath element because we use this routine in two
   ** places: called from main() to process incoming files, and called
   ** elsewhere to process files in TICK:BAD/ during recovery operations.
   */

   char                   *FileName;
   char                   *Path;
   struct   SearchCookie  *sc = NULL;

   Log("-> Tick() -- process inbound directory for #?.TIC files");

   if(TicPath)
   {
      Path = strdup(TicPath);
   }
   else
   {
      Path = strdup("Inbound:");
   }

   AFPrintf(NULL,sout,"Searching %s for TIC files\n",Path);
   ASPrintf(NULL,logstring,"  Searching %s for TIC files",Path);
   Log(logstring);

   // Nice thing about Search*() is that they pay no attention to dirs!

   sc = SearchStart(Path,"????????.TIC");

   if(!sc)
   {
      ASPrintf(NULL,logstring," ! SearchStart() failed!");
      Log(logstring);
      return(FALSE);
   }

   while(FileName = SearchNext(sc))
   {
      if(CheckDLGMAILSTOP())
         break;

      Chk_Abort();

      AFPrintf(NULL,sout,"\n\tProcessing %s\n",FileName);
      ASPrintf(NULL,logstring,"   Processing %s",FileName);
      Log(logstring);

      if(!DoTick(Path,FileName))
      {
         ASPrintf(NULL,logstring," ! Something went wrong processing %s",FileName);
         Log(logstring);
      }
   }

   SearchEnd(sc);

   Log("<- Exiting Tick() -- done processing .TICs");
   return(TRUE);
}
//-

///   DoTick()
BOOL DoTick(char *Path, char *FileName)
{
   BOOL  rc = TRUE;
   BOOL  NoPW = FALSE;

   BPTR  fh = NULL;

   char FilePath[256] = "";
   char TargPath[256] = "";
   char  t[1000]      = "";
   char Echo[128]     = "";
   char Target[128]   = "";
   char Desc[500]     = "";
   char From[128]     = "";
   char ThierCRC[10]  = "";
   char PW[50]        = "";

   struct   TicAddr  *Addr = NULL;

   ULONG CRC = 0;

   Log("-> DoTick() -- process individual TIC files");

   strcpy(Desc,"");

   strmfn(FilePath,NULL,Path,FileName,NULL);

/// Does file exist?
   if(!Exists(FilePath))
   {
      ASPrintf(NULL,logstring," ! %s not found!",FilePath);
      Log(logstring);
      return(FALSE);
   }
//-

/// Parse incoming tic file
   fh = Open(FilePath,MODE_OLDFILE);

   if(!fh)
   {
      ASPrintf(NULL,logstring," ! Could not open [ %s ] !",FilePath);
      Log(logstring);
      return(FALSE);
   }

///   Loop through TIC file and parse it

   rc = TRUE;

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

      Chk_Abort();
      strcpy(t, stpblk(strtok(t,"\r\n")));
      if (strlen(t) == 0)  continue;         // Skip empty lines
      dup = strdup(t);
      c = ArgParse(t, par, 19);

///      AREA
      if(!Stricmp(par[0],"AREA"))
      {
         if(c < 2)
         {
            Log(" ! .TIC file syntax error: AREA!");
            rc = FALSE;
            break;
         }

         strcpy(Echo,par[1]);
         continue;
      }
//-

///      FILE
      if(!Stricmp(par[0],"FILE"))
      {
         if(c < 2)
         {
            Log(" ! .TIC file syntax error: FILE!");
            rc = FALSE;
            break;
         }

         strcpy(Target,par[1]);
         continue;
      }
//-

///      DESC
      if(!Stricmp(par[0], "DESC"))
      {
         BOOL indesc = FALSE;
         char *d;
         int k;
         int j = 0;

         if(c < 2)
         {
            Log(" ! .TIC file syntax error: DESC!");
            rc = FALSE;
            break;
         }

         d = calloc(1,strlen(dup));

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

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

         strcpy(Desc,d);
         continue;
      }
//-

///      FROM
      if(!Stricmp(par[0],"FROM"))
      {
         if(c < 2)
         {
            Log(" ! .TIC file syntax error: FROM!");
            rc = FALSE;
            break;
         }

         strcpy(From,par[1]);
         continue;
      }
//-

///      CRC
      if(!Stricmp(par[0],"CRC"))
      {
         if(c < 2)
         {
            Log(" ! .TIC file syntax error: CRC!");
            rc = FALSE;
            break;
         }

         strcpy(ThierCRC,par[1]);
         continue;
      }
//-

///      PW or PASSWORD
      if(!Stricmp(par[0],"PW")   || !Stricmp(par[0],"PASSWORD"))
      {
         if(c < 2)
         {
            Log(" ! .TIC file syntax error: PASSWORD (PW) is incomplete!");
            NoPW = TRUE;
            break;
         }

         strcpy(PW,par[1]);

         StripQuote(PW);

         continue;
      }
//-

      if(rc == FALSE) break;
   }
//-

   if(fh)
   {
      Close(fh);
      fh = NULL;
   }
//-

/// If rc is TRUE, one of the tags was incomplete.
   if(rc == FALSE)
   {
      Log("<- Incomplete tag in TIC file, cannot complete");
      return(FALSE);
   }
//-

/// Error checking for tic file
   if(strlen(Target) == 0)
   {
      Log("<- No object file specified in TIC file!");
      return(FALSE);
   }
//-

/// Does target file exist?  If not, leave tic file in place and exit
   strmfn(TargPath,NULL,Path,Target,NULL);

   if(!Exists(TargPath))
   {
      AFPrintf(NULL,sout,"Target file %s is missing!\n",TargPath);
      ASPrintf(NULL,logstring," ! Target file %s is missing!",TargPath);
      Log(logstring);
      return(FALSE);
   }
//-

/// DESC not specified
   if(strlen(Desc) == 0)
   {
      Log(" # No description provided, creating our own.");
      strcpy(Desc,"Error: No description available");
   }
//-

/// Check for echo name -- if not found, --> BAD
   if(strlen(Echo) == 0)
   {
      Log("<- Tic file mangled -- no AREA tag!");
      MoveToBad(FilePath,TargPath,Target,"NO_ECHO_NAME",Desc,"None");
      return(FALSE);
   }
//-

/// Check for FROM -- if not found, --> BAD
   if(strlen(From) == 0)
   {
      Log("<- Tic file mangled -- no FROM tag!");
      MoveToBad(FilePath,TargPath,Target,"NO_SOURCE_SPECIFIED",Desc,Echo);
      return(FALSE);
   }
//-

   AFPrintf(NULL,sout,"\t\tEcho: %s\n",Echo);
   ASPrintf(NULL,logstring,"   Echo: %s",Echo);
   Log(logstring);
   AFPrintf(NULL,sout,"\t\tTarget File: %s\n",Target);
   ASPrintf(NULL,logstring,"   Target File: %s",Target);
   Log(logstring);
   AFPrintf(NULL,sout,"\t\tFrom: %s\n",From);
   ASPrintf(NULL,logstring,"   From: %s",From);
   Log(logstring);

   if(strlen(ThierCRC) > 0)
   {
      AFPrintf(NULL,sout,"\t\tCRC: %s\n",ThierCRC);
      ASPrintf(NULL,logstring,"   CRC: %s",ThierCRC);
      Log(logstring);
   }
   else
   {
      AFPrintf(NULL,sout,"\t\tCRC: None\n");
      Log("   CRC: NONE");
   }

   AFPrintf(NULL,sout,"\t\tDescription: \"%s\"\n",Desc);
   ASPrintf(NULL,logstring,"   Description: %s",Desc);
   Log(logstring);

/// Does area exist in our tic file?  If not, -->BAD
   AIndex = GetEcho(Echo);

   if(AIndex == -1)
   {
      MoveToBad(FilePath,TargPath,Target,"AREA_NOT_CONFIGURED",Desc,Echo);
      AFPrintf(NULL,sout,"Echo %s is not configured, moving to TICK:BAD\n",Echo);
      ASPrintf(NULL,logstring," ! Echo %s is not configured!",Echo);
      Log(logstring);
      return(FALSE);
   }
//-

/// Is area set to ignore?  If so, delete TIC file
   if(TicArea[AIndex].Ignore == TRUE)
   {
      ASPrintf(NULL,logstring," . Echo %s set to delete TIC files",TicArea[AIndex].AreaName);
      Log(logstring);
      DeleteFile(FilePath);
      return(TRUE);
   }
//-

/// If they supplied a CRC, check it.  If it fails, --> BAD
   if(strlen(ThierCRC) > 0)
   {
      long thcrc;

      FileCRC(TargPath,&CRC);

      stcl_h(t,CRC);
      Upper(t);

      stch_l(ThierCRC,&thcrc);

      if(thcrc != CRC)
      {
         AFPrintf(NULL,sout,"CRC failure! Them: %s -- Us: %s\n",ThierCRC,t);
         ASPrintf(NULL,logstring," ! CRC failure! Them: %s -- Us: %s",ThierCRC,t);
         Log(logstring);
         MoveToBad(FilePath,TargPath,Target,"CRC_FAILURE",Desc,Echo);
         return(FALSE);
      }
   }
//-

/// Check password.  If mismatch or missing, --> BAD
   if(NoPW)
   {
      if(!ConfirmPassword(From,NULL))
      {
         Log(" ! Password missing!");
         AFPrintf(NULL,sout,"\t*** Missing password! ***\n");
         MoveToBad(FilePath,TargPath,Target,"MISSING_PASSWORD",Desc,Echo);
         return(FALSE);
      }
   }
   else
   {
      if(strlen(PW) > 0)
      {
         if(!ConfirmPassword(From,PW))
         {
            Log(" ! Password mismatch!");
            AFPrintf(NULL,sout,"\t*** Bad password! ***\n");
            MoveToBad(FilePath,TargPath,Target,"BAD_PASSWORD",Desc,Echo);
            return(FALSE);
         }
      }
      else
      {
         Log(" # Password not supplied");
      }
   }
//-

/// Check to see if node is authorized to hatch to us. If not, --> BAD

   Addr = calloc(1,sizeof(struct TicAddr));

   if(!Addr)
   {
      Log(" ! Memory allocation error!");
      AFPrintf(NULL,sout,"\t*** Memory Allocation Error! ***\n");
      return(FALSE);
   }

   Addr = GetAddr(From,Echo);

   if(Addr == NULL)
   {
      Log(" ! Node not configured for this area!");
      MoveToBad(FilePath,TargPath,Target,"NODE_NOT_IN_AREA_LIST",Desc,Echo);
      free(Addr);
      return(FALSE);
   }

   if(Addr->Hatch == FALSE)
   {
      Log(" ! Node not authorized to hatch to us!");
      MoveToBad(FilePath,TargPath,Target,"NODE_NOT_AUTH_TO_FEED",Desc,Echo);
      free(Addr);
      return(FALSE);
   }
//-

   /* If we're here, everything is OK.  We will either ignore the tic file
   ** (delete it) or process the tic.
   */

/// Create master TIC file template
   if(!BuildMasterTic(FilePath))
   {
      Log(" ! Error building master TIC file!");
      if(Addr) free(Addr);
      return(FALSE);
   }
//-

/// If all went well, let's tick the file
   if(!SendLoop(Target,TargPath,Echo,Desc,TRUE,FilePath,Addr))
   {
      Log(" ! Error TICKing file!");
      if(Addr) free(Addr);
      DeleteFile(MASTER);
      return(FALSE);
   }
//-

   DeleteFile(FilePath);
   AFPrintf(NULL,sout,"\t\tFile %s successfully processed\n",Target);

   if(Addr) free(Addr);
   DeleteFile(MASTER);

   Log("<- DoTick() exiting normally");

   return(TRUE);
}
//-

///   MoveToBad
void MoveToBad(char *tickfile, char *path, char *file, char *reason,char *desc,char *area)
{
   /*
   ** MoveToBad() -- move a file from its source to TICK:BAD/
   **
   ** Inputs:  tickfile = the path + name of the *.TIC file
   **          path     = the path + name of the target file
   **          file     = the name of the target file
   **          reason   = reason for rejection
   **          desc     = description of file
   **          area     = the echo the file was destined for
   */

   char buf[256] = "";

   BuildBadZMsg(file,area,desc,reason);

   ASPrintf(NULL,buf,"TICK:BAD/%s",file);

   if(Copy(path,buf) == 0L)
   {
      SetComment(buf,reason);
      DeleteFile(path);
   }
   else
   {
      Log("!   MoveToBad(): Error copying file to TICK:BAD/ !");
   }

   ASPrintf(NULL,buf,"TICK:BAD/%s_TK000000.TIC",file);

   if(Copy(tickfile,buf) == 0L)
   {
      SetComment(buf,reason);
      DeleteFile(tickfile);
   }
   else
   {
      Log("!   MoveToBad(): Error copying file to TICK:BAD/ !");
   }

   return;
}
//-

///   BuildBAdZMsg
BuildBadZMsg(char *file, char *area, char *desc, char *reason)
{
   /* msg area might be a name for a private message :-) */

   BPTR fp;

   DeleteFile(TICKREPLY);

   fp = Open(TICKREPLY,MODE_NEWFILE);

   if(fp)
   {
      AFPrintf(NULL,fp,"FROM: DLGTick\n");      
      AFPrintf(NULL,fp,"TO: %s\n",_OPERATOR);
      AFPrintf(NULL,fp,"TYPE: P\n");
      AFPrintf(NULL,fp,"SUBJECT: Bad file processed by DLGTick\n");     

      Upper(area);

      AFPrintf(NULL,fp,"BODY:\n");     
      AFPrintf(NULL,fp,"      The following file: ");    
      AFPrintf(NULL,fp,"%s\n\n",file);    
      AFPrintf(NULL,fp,"       in file echo area: ");    
      AFPrintf(NULL,fp,"%s\n\n\n",area);     
      AFPrintf(NULL,fp,"    Was rejected because: ");
      AFPrintf(NULL,fp,"%s\n\n\n",reason);
      AFPrintf(NULL,fp,"It was supplied with the following description:\n\n");      
      AFPrintf(NULL,fp,"%-75.75s\n\n\n",desc);     

      if(fp) Close(fp);

      SendMsg(TICKREPLY);        
      DeleteFile(TICKREPLY);
   }
   return(1);
}
//-

///   ConfirmPassword
BOOL ConfirmPassword(char *ticfrom, char *ticpw)
{
   BPTR fh = NULL;

   char buffer[100];

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

   if(!fh) return(FALSE);

   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(CompareFidoAddress(par[1],ticfrom))  // is this for the node of interest?
         {
            if(ticpw == NULL)
            {
               Close(fh);
               return(FALSE);
            }

            StripQuote(par[2]);

            if(!Stricmp(par[2],ticpw))
            {
               Close(fh);
               return(TRUE);
            }
            else
            {
               Close(fh);
               return(FALSE);
            }
         }
      }
      continue;
   }

   if(ticpw == NULL) return(TRUE);
   return(FALSE);
}
//-

///   BuildMasterTic()
BOOL BuildMasterTic(char *TicPath)
{
   /*
   ** BuildMasterTic() -- Build a master tic file template that we will fill
   **                     in as needed
   **
   ** Inputs:  TicPath     Path + filename of tic file we're processing
   **
   ** Result:  TRUE if all went well, FALSE if not
   */

   BOOL InPath;

   BPTR ifh = NULL;
   BPTR ofh = NULL;

   char t[1000];

/// Prepare files for parsing and writing
   if(Exists(MASTER))
   {
      if(!DeleteFile(MASTER))
      {
         Log(" ! BuildMasterTick: Error deleting old master tic file -- reboot!!!");
         return(FALSE);
      }
   }

   ifh = Open(TicPath,MODE_OLDFILE);

   if(ifh == NULL)
   {
      Log(" ! BuildMasterTic: Error opening .tic file!");
      return(FALSE);
   }

   ofh = Open(MASTER,MODE_NEWFILE);

   if(ofh == NULL)
   {
      Log(" ! BuildMasterTic: Error opening master tic file template!");
      Close(ifh);
      return(FALSE);
   }
//-

/// Loop through TIC file and copy stuff over to master tic file template
   while (FGets(ifh, t, 999))
   {
      char *par[20];
      char *dup;
      int   c;

      Chk_Abort();
      strcpy(t, stpblk(strtok(t,"\r\n")));
      if (strlen(t) == 0)  continue;         // Skip empty lines
      dup = strdup(t);
      c = ArgParse(t, par, 19);

///      pass through stuff that we are supposed to pass through
      if(Stricmp(par[0],"PW")       &&
         Stricmp(par[0],"PASSWORD") &&
         Stricmp(par[0],"FROM")     &&
         Stricmp(par[0],"PATH")     &&
         Stricmp(par[0],"VIA")      &&
         Stricmp(par[0],"FULLNAME") &&
         Stricmp(par[0],"MAGIC")    &&
         Stricmp(par[0],"APP")      &&
         Stricmp(par[0],"SEENBY")   &&
         Stricmp(par[0],"TO")       &&
         Stricmp(par[0],"RECEIPTREQUEST"))
      {
         AFPrintf(NULL,ofh,"%s\r\n",dup);
         continue;
      }
//-

///      Put ourselves in as FROM
      if(!Stricmp(par[0],"FROM"))
      {
         AFPrintf(NULL,ofh,"FROM %ld:%ld/%ld\r\n",_MyZone,_MyNet,_MyNode);
         continue;
      }
//-
   }
//-

/// Now rewind and just add the PATH stuff
   Seek(ifh,0,OFFSET_BEGINNING);
   InPath = FALSE;

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

      Chk_Abort();
      strcpy(t, stpblk(strtok(t,"\r\n")));
      if (strlen(t) == 0)  continue;         // Skip empty lines
      dup = strdup(t);
      c = ArgParse(t, par, 19);

///      PATH
      if(!Stricmp(par[0],"PATH"))
      {
         if(!InPath)
            InPath = TRUE;

         AFPrintf(NULL,ofh,"%s\r\n",dup);
         continue;
      }
//-

///      !PATH
      if(Stricmp(par[0],"PATH"))
      {
         if(!InPath)
            continue;

         AFPrintf(NULL,ofh,"PATH %ld:%ld/%ld\r\n",_MyZone,_MyNet,_MyNode);
         break;
      }
//-

   }
//-

/// Now rewind and just add the SeenBy stuff -- hijacking InPath for efficiency
   Seek(ifh,0,OFFSET_BEGINNING);
   InPath = FALSE;
   while (FGets(ifh, t, 999))
   {
      char *par[20];
      char *dup;
      int   c;

      Chk_Abort();
      strcpy(t, stpblk(strtok(t,"\r\n")));
      if (strlen(t) == 0)  continue;         // Skip empty lines
      dup = strdup(t);
      c = ArgParse(t, par, 19);

///      SeenBy
      if(!Stricmp(par[0],"SeenBy"))
      {
         if(!InPath)
            InPath = TRUE;

         AFPrintf(NULL,ofh,"%s\r\n",dup);
         continue;
      }
//-

///      !SeenBy
      if(Stricmp(par[0],"SeenBy"))
      {
         if(!InPath)
            continue;

         break;
      }
//-

   }
//-

   Close(ifh);
   Close(ofh);

   return(TRUE);
}
//-

