#include <CrashMail.h>

const UBYTE ver[]="\0$VER: CrashMail "VERSION" ("__COMMODORE_DATE__")";

UBYTE *config_version="CrashMail "VERSION;

UBYTE *MonthNames[13]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec","???"};

struct PktHeader     PktHeader;
struct PktMsgHeader  PktMsgHeader;
struct MemMessage    MemMessage;
struct Node4D        PktOrig;
struct Node4D        PktDest;

UBYTE *dupebuf;
struct NotifyRequest notifyrequest;
UBYTE notifysignal;
BOOL startnotify;
BOOL initlogtask;

struct jbList PktList;
struct jbList DeleteList;

struct Area *LastArea;
struct ConfigNode *LastNode;

BOOL nomem;
BOOL diskfull;
BOOL exitprg;
BOOL pkt_pw;
BOOL pkt_fake;
BOOL pkt_4d;
BOOL pkt_5d;

BOOL no_security;

ULONG toss_total;
ULONG toss_bad;
ULONG toss_route;
ULONG toss_import;
ULONG toss_dupes;
ULONG toss_written;

ULONG scan_num;

BOOL isscanning;
BOOL hasstarted;
BOOL saveconfig;
BOOL isrescanning;

BOOL UMS=FALSE;
BOOL JAM=FALSE;

BOOL scannedumsmail;

ULONG DayStatsWritten;

struct ConfigNode *RescanNode;

UMSUserAccount UMSHandle;
struct Library *UMSBase;

struct RxsLib *RexxSysBase;
struct MsgPort *rexxport;

UBYTE copybuf[COPYBUFSIZE];

struct Process *process;
APTR oldwin;
struct RDArgs *rdargs;

BOOL nlopen;

ULONG argarray[] = {FALSE,FALSE,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
UBYTE argstr[]   = "SCAN/S,TOSS/S,TOSSFILE/K,SCANAREA/K,SCANLIST/K,SETTINGS/K,WAIT/S,QUIT/S,RESCAN/K,RESCANNODE/K,RESCANMAX/N/K,SENDQUERY=NOTIFY/K,SENDLIST/K,SENDUNLINKED/K,SENDHELP/K,SENDINFO/K,REMOVE/K";

__asm void (*own_StackSwap)(register __A0 struct StackSwapStruct *newstack,register __A6 struct Library *library);

struct StackSwapStruct stackswapstruct;

extern struct ExecBase *SysBase;

myputs(UBYTE *str)
{
   Write(Output(),str,strlen(str));
}

_main(int argc, char **argv)
{
   UBYTE res;
   UBYTE rexxbuf[200];

   if(SysBase->LibNode.lib_Version<37)
   {
      myputs("Sorry! This program requires Kickstart 2.04 or higher\n");
      _exit(0);
   }

   process=(APTR)FindTask(NULL);
   oldwin=(APTR)process->pr_WindowPtr;

   hasstarted=FALSE;

   if(!(rdargs=(struct RDArgs *)ReadArgs(argstr,argarray,NULL)))
   {
      PrintFault(IoErr(),NULL);
      exitprg=TRUE;
      CleanUp();
   }

   if(!(RexxSysBase=(struct RxsLib *)OpenLibrary(RXSNAME,0)))
   {
      PutStr("Unable to open "RXSNAME"\n");
      exitprg=TRUE;
      CleanUp();
   }

   Forbid();
   if(rexxport=(struct MsgPort *)FindPort("CRASHMAIL"))
   {
      res=0;
      Permit();
      PutStr("Another copy of CrashMail is already running, passing on commands...\n");

      if(argarray[ARG_TOSS])
      {
         res=RexxPassOn(rexxport,"TOSS");
      }
      if(argarray[ARG_SCAN])
      {
         res=RexxPassOn(rexxport,"SCAN");
      }
      if(argarray[ARG_TOSSFILE])
      {
         sprintf(rexxbuf,"TOSSFILE %s",argarray[ARG_TOSSFILE]);
         res=RexxPassOn(rexxport,rexxbuf);
      }
      if(argarray[ARG_SCANAREA])
      {
         sprintf(rexxbuf,"SCANAREA %s",argarray[ARG_SCANAREA]);
         res=RexxPassOn(rexxport,rexxbuf);
      }
      if(argarray[ARG_SCANLIST])
      {
         sprintf(rexxbuf,"SCANLIST %s",argarray[ARG_SCANLIST]);
         res=RexxPassOn(rexxport,rexxbuf);
      }
      if(argarray[ARG_SENDQUERY])
      {
         sprintf(rexxbuf,"SENDQUERY %s",argarray[ARG_SENDQUERY]);
         res=RexxPassOn(rexxport,rexxbuf);
      }
      if(argarray[ARG_SENDLIST])
      {
         sprintf(rexxbuf,"SENDLIST %s",argarray[ARG_SENDLIST]);
         res=RexxPassOn(rexxport,rexxbuf);
      }
      if(argarray[ARG_SENDUNLINKED])
      {
         sprintf(rexxbuf,"SENDUNLINKED %s",argarray[ARG_SENDUNLINKED]);
         res=RexxPassOn(rexxport,rexxbuf);
      }
      if(argarray[ARG_SENDHELP])
      {
         sprintf(rexxbuf,"SENDHELP %s",argarray[ARG_SENDHELP]);
         res=RexxPassOn(rexxport,rexxbuf);
      }
      if(argarray[ARG_SENDINFO])
      {
         sprintf(rexxbuf,"SENDINFO %s",argarray[ARG_SENDINFO]);
         res=RexxPassOn(rexxport,rexxbuf);
      }
      if(argarray[ARG_REMOVE])
      {
         sprintf(rexxbuf,"REMOVE %s",argarray[ARG_REMOVE]);
         res=RexxPassOn(rexxport,rexxbuf);
      }
      if(argarray[ARG_RESCAN] && argarray[ARG_RESCANNODE])
      {
         if(argarray[ARG_RESCANMAX])
            sprintf(rexxbuf,"RESCAN %s %s %lu",argarray[ARG_RESCAN],argarray[ARG_RESCANNODE],*(ULONG *)argarray[ARG_RESCANMAX]);

         else
            sprintf(rexxbuf,"RESCAN %s %s",argarray[ARG_RESCAN],argarray[ARG_RESCANNODE]);

         res=RexxPassOn(rexxport,rexxbuf);
      }
      if(argarray[ARG_QUIT])
      {
         res=RexxPassOn(rexxport,"QUIT");
      }

      rexxport=NULL;
      CleanUpARexx(res);
   }

   if(argarray[ARG_QUIT])
   {
      PutStr("No other copy of CrashMail is running.\n");
      CleanUpARexx(0);
   }

   if(!(rexxport=(struct MsgPort *)CreatePort("CRASHMAIL",0)))
   {
      Permit();
      nomem=TRUE;
      CleanUp();
   }

   Permit();

   /* The local variables aren't used after this point anyway... */

   if(!(stackswapstruct.stk_Lower = AllocMem(STACKSIZE,MEMF_PUBLIC)))
   {
      nomem=TRUE;
      CleanUp();
   }

   stackswapstruct.stk_Upper   = (ULONG)stackswapstruct.stk_Lower + STACKSIZE;
   stackswapstruct.stk_Pointer = (APTR)stackswapstruct.stk_Upper;

   /* This trick is needed, otherwise DICE will use stack when swapping... */

   own_StackSwap=(void *)((ULONG)SysBase-(ULONG)732);
   (*own_StackSwap)(&stackswapstruct,(struct Library *)SysBase);

   ReadKey();

/*   if(!(memorypool=CreatePool(MEMF_ANY,20000,5000)))
   {
      nomem=TRUE;
      CleanUp();
   } */

   ResetConfig();

   if(argarray[ARG_SETTINGS]==NULL)
   {
      if(ReadConfig("PROGDIR:CrashMail.prefs",NULL))
         argarray[ARG_SETTINGS]="PROGDIR:CrashMail.prefs";

      else if(ReadConfig("MAIL:CrashMail.prefs",NULL))
         argarray[ARG_SETTINGS]="MAIL:CrashMail.prefs";

      if(argarray[ARG_SETTINGS]==NULL)
      {
         Printf("Couldn't read config file PROGDIR:CrashMail.prefs or MAIL:CrashMail.prefs\n");
         CleanUpConfig();
      }
   }
   else
   {
      if(!ReadConfig((UBYTE *)argarray[ARG_SETTINGS],NULL))
      {
         Printf("Couldn't read config file %s\n",(long)argarray[ARG_SETTINGS]);
         CleanUpConfig();
      }
   }

   CheckConfig();
   Init();

   LogWrite(2,SYSTEMINFO,"CrashMail %s started successfully!",VERSION);
   hasstarted=TRUE;

   if(Checksum9() == checksums[9])
      LogWrite(2,SYSTEMINFO,"Registered to %s",key.Name);

   else
      LogWrite(2,SYSTEMINFO,"Unregistered version, only two downlinks allowed");

   if(argarray[ARG_TOSS])
      Toss();

   if(argarray[ARG_SCAN])
      Scan();

   if(argarray[ARG_TOSSFILE])
      TossFile((UBYTE *)argarray[ARG_TOSSFILE]);

   if(argarray[ARG_SCANAREA])
      ScanArea((UBYTE *)argarray[ARG_SCANAREA]);

   if(argarray[ARG_SCANLIST])
      ScanList((UBYTE *)argarray[ARG_SCANLIST]);

   if(argarray[ARG_SENDQUERY])
      SendAFList(SENDLIST_QUERY,(UBYTE *)argarray[ARG_SENDQUERY]);

   if(argarray[ARG_SENDLIST])
      SendAFList(SENDLIST_FULL,(UBYTE *)argarray[ARG_SENDLIST]);

   if(argarray[ARG_SENDUNLINKED])
      SendAFList(SENDLIST_UNLINKED,(UBYTE *)argarray[ARG_SENDUNLINKED]);

   if(argarray[ARG_SENDHELP])
      SendAFList(SENDLIST_HELP,(UBYTE *)argarray[ARG_SENDHELP]);

   if(argarray[ARG_SENDINFO])
      SendAFList(SENDLIST_INFO,(UBYTE *)argarray[ARG_SENDINFO]);

   if(argarray[ARG_REMOVE])
      RemoveArea((UBYTE *)argarray[ARG_REMOVE]);

   if(argarray[ARG_RESCAN] && argarray[ARG_RESCANNODE])
   {
      if(argarray[ARG_RESCANMAX])
         ParseRescan((UBYTE *)argarray[ARG_RESCAN],(UBYTE *)argarray[ARG_RESCANNODE],*(ULONG *)argarray[ARG_RESCANMAX]);

      else
         ParseRescan((UBYTE *)argarray[ARG_RESCAN],(UBYTE *)argarray[ARG_RESCANNODE],0);
   }

   /* Om några har hunnit komma redan... */

   HandleRexxMessages();

   if(argarray[ARG_WAIT])
      RexxLoop();

   HandleRexxMessages();
   Forbid();
   DeletePort(rexxport);
   Permit();
   rexxport=NULL;

   CleanUp();
}

BOOL FinishSession(void)
{
   struct Area *area;
   struct ConfigNode *cnode;
   struct FileToProcess *ftp;
   ULONG exp,toexp,last,d,e;
   BOOL kg;
   struct DateStamp ds;

   DateStamp(&ds);

   ClosePackets();

   if(!nomem && !exitprg && !diskfull && !(SetSignal(0L,0L) & SIGBREAKF_CTRL_C))
   {
      ArchiveOutbound();

      if(UMS && isscanning==TRUE)
      {
         toexp=UMSSelectTags(UMSHandle,UMSTAG_SelReadLocal,  TRUE,
                                       UMSTAG_SelMask,       UMSFLAG_TOEXPORT,
                                       UMSTAG_SelMatch,      UMSFLAG_TOEXPORT,
                                       TAG_DONE);

         last=0;
         kg=TRUE;

         while(kg)
         {
            last=UMSSearchTags(UMSHandle,UMSTAG_SearchLast,  last,
                                         UMSTAG_SearchLocal, TRUE,
                                         UMSTAG_SearchMask,  UMSFLAG_EXPORTED,
                                         UMSTAG_SearchMatch, UMSFLAG_EXPORTED,
                                         TAG_DONE);

            if(last!=0)
               UMSExportedMsg(UMSHandle,last);

            else
               kg=FALSE;
         }

         exp=UMSSelectTags(UMSHandle,UMSTAG_SelReadLocal,  TRUE,
                                     UMSTAG_SelMask,       UMSFLAG_EXPORTED,
                                     UMSTAG_SelMatch,      UMSFLAG_EXPORTED,
                                     UMSTAG_SelSet,        UMSUSTATF_Old,
                                     UMSTAG_SelUnset,      0,
                                     TAG_DONE);

         if(toexp-exp != 0)
            LogWrite(1,TOSSINGINFO,"Warning: %lu messages in UMS messagebase not exported",toexp-exp);
      }

      if(!(SetSignal(0L,0L) & SIGBREAKF_CTRL_C))
         for(ftp=DeleteList.First;ftp;ftp=ftp->Next)
            DeleteFile(ftp->Name);

      if(DayStatsWritten == 0)
         DayStatsWritten = ds.ds_Days;

      for(area=AreaList.First;area;area=area->Next)
      {
         if(area->AreaType == AREATYPE_MSG && area->HighWater != area->OldHighWater && cfg_Flags & CFG_MSGHIGHWATER)
         {
            if(isscanning || !(cfg_Flags & CFG_NOTOUCHHWTOSSING))
               WriteHighWater(area);
         }
         if(ds.ds_Days > DayStatsWritten)
         {
            for(d=DayStatsWritten;d<ds.ds_Days;d++)
            {
               for(e=0;e<7;e++)
                  area->Last8Days[7-e]=area->Last8Days[7-e-1];

               area->Last8Days[0]=0;
            }
         }

         if(area->NewTexts || area->NewDupes)
         {
            area->Texts+=area->NewTexts;
            area->Last8Days[0]+=area->NewTexts;
            area->Dupes+=area->NewDupes;

            if(area->NewTexts)
               area->LastDay=ds.ds_Days;

            if(area->NewTexts && area->FirstTime.ds_Days==0)
               DateStamp(&area->FirstTime);
         }
      }

      DayStatsWritten=ds.ds_Days;

      for(cnode=CNodeList.First;cnode;cnode=cnode->Next)
      {
         if(cnode->FirstDay==0 && (cnode->GotEchomails!=0 || cnode->GotNetmails!=0 || cnode->SentEchomails!=0 || cnode->SentNetmails)!=0)
            cnode->FirstDay=ds.ds_Days;
      }

      WriteStats();

      if(cfg_DupeMode!=DUPE_IGNORE)
         WriteDupeBuf();

      if(saveconfig)
         WriteConfig((UBYTE *)argarray[ARG_SETTINGS]);
   }

   jbFreeList(&PktList,PktList.First,sizeof(struct Pkt));
   jbFreeList(&DeleteList,DeleteList.First,sizeof(struct FileToProcess));

   /* Nodelist */

   if(nlopen)
   {
      nlEnd();
      nlopen=FALSE;
   }
}

BOOL InitSession(void)
{
   struct Area *area;
   struct ConfigNode *cnode;
   struct jbList NewPktList;
   struct FileToProcess *ftp;
   UBYTE delbuf[200];

   if(cfg_NodeList[0]!=0)
   {
      UBYTE errbuf[100];

      if(!(nlopen=nlStart(errbuf)))
      {
         LogWrite(1,SYSTEMERR,"%s",errbuf);
         return(FALSE);
      }
   }

   toss_total=0;
   toss_bad=0;
   toss_route=0;
   toss_import=0;
   toss_dupes=0;
   toss_written=0;

   scannedumsmail=FALSE;

   scan_num=0;

   no_security=FALSE;

   LastArea=NULL;
   LastNode=NULL;

   UMS_MsgidConvertCache_Zone=0;
   UMS_MsgidConvertCache_Domain[0]=0;

   for(area=AreaList.First;area;area=area->Next)
   {
      area->NewDupes=0;
      area->NewTexts=0;
      area->HighMsg=0;
      area->LowMsg=0;
      area->HighWater=0;
      area->OldHighWater=0;
   }

   for(cnode=CNodeList.First;cnode;cnode=cnode->Next)
      cnode->Pkt=NULL;

   jbNewList(&PktList);
   jbNewList(&DeleteList);

   ScanForNewPkt(cfg_PacketCreate,&NewPktList);

   for(ftp=NewPktList.First;ftp;ftp=ftp->Next)
   {
      LogWrite(1,SYSTEMINFO,"Deleting orphan tempfile %s",ftp->Name);

      strcpy(delbuf,cfg_PacketCreate);
      AddPart(delbuf,ftp->Name,200);
      DeleteFile(delbuf);
   }

   jbFreeList(&NewPktList,NewPktList.First,sizeof(struct FileToProcess));

   return(TRUE);
}

Init()
{
   struct Area *area;

   if(!(initlogtask=InitLogWrite()))
   {
      Printf("Failed to start task for logwrites\n");
      exitprg=TRUE;
      CleanUp();
   }

   if(cfg_Flags & CFG_UNATTENDED)
      process->pr_WindowPtr=(APTR)-1;

   saveconfig=FALSE;

   jbNewList(&MemMessage.TextChunks);
   jbNewList(&MemMessage.SeenBy);
   jbNewList(&MemMessage.Path);

   MemMessage.TextChunks.First = (struct TextChunk *)AllocMem(sizeof(struct TextChunk),MEMF_ANY);
   MemMessage.SeenBy.First     = (struct Nodes2D *)AllocMem(sizeof(struct Nodes2D),MEMF_ANY);
   MemMessage.Path.First       = (struct Nodes2D *)AllocMem(sizeof(struct Path),MEMF_ANY);

   MemMessage.TextChunks.Last  = MemMessage.TextChunks.First;
   MemMessage.SeenBy.Last      = MemMessage.SeenBy.First;
   MemMessage.Path.Last        = MemMessage.Path.First;

   MemMessage.TextChunks.First->Next=NULL;
   MemMessage.SeenBy.First->Next=NULL;
   MemMessage.Path.First->Next=NULL;

   if(!MemMessage.TextChunks.First || !MemMessage.SeenBy.First || !MemMessage.Path.First)
   {
      LogWrite(1,SYSTEMERR,"Out of memory");
      exitprg=TRUE;
      CleanUp();
   }

   if(cfg_DupeMode!=DUPE_IGNORE)
   {
      dupebuf = (UBYTE *)AllocMem(cfg_DupeSize,MEMF_ANY);

      if(!dupebuf)
      {
         LogWrite(1,SYSTEMERR,"No memory for dupe-check buffer");
         exitprg=TRUE;
         CleanUp();
      }

      InitDupe();
      ReadDupeBuf();

      if(exitprg)
         CleanUp();
   }

   nomem=FALSE;
   diskfull=FALSE;
   exitprg=FALSE;

   UMS=FALSE;

   for(area=AreaList.First;area;area=area->Next)
      if(area->AreaType==AREATYPE_UMS) UMS=TRUE;

   if(UMS) UMSStart();

   ReadStats();

   if(exitprg)
      CleanUp();
}
