/*


  CheckFiles
  ==========

  Called by Protocols after a file has been uploaded if the protocol is receiving
  more than one file at a time, or by the UPLOAD or DOWNLOAD door after a transfer
  has completed.

  Options
  =======

    N_ND->ActiveDoor->SystemOptions
    -------------------------------

    No options used at this time


    Door Options (Command Line)
    ---------------------------

    ALL|SINGLE

    [FILENAME]


    Example
    -------

    CheckFiles ALL

    CheckFiles SINGLE BANANA.LHA


    Specifiying the ALL parameter causes CheckFiles to get a list of FILES in the
    playpen (directories are purposely ignored!) and check each one individually.

  todo (*C*)
  ====

  add tonnes of errorchecking to the allocation of memory, esp. in the direcotry
  listing of the playpen dir..

  Structure
  =========

  Extract_DIZ

  copy .DIZ to .ADD, .ADD is added back into the file after all the doors have finished
  playing around with it,  .DIZ is added to the file lists.

  Modify_DIZ

  Check_Corrupt

  if "CORRUPT"
    MoveFile BAD
    AddDIZToList BAD
    Update_DIZ
  else
    Confirm_UL
    DupeCheck

    if Not "INCORRECT" and Not "DUPLICATE"
      Ask_Conf
      AddDIZToList <confnum>
      MoveFile <cofnum>
      Update_DIZ
      AddCreds <confnum> <filename>
    else
      AddDIZToList "SYSOP" or "HOLD"
      MoveFile "HOLD"
      Update_DIZ
    endif
  endif

*/
#include <exec/types.h>
#include <exec/memory.h>
#include <dos/dos.h>
#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#include <clib/alib_protos.h>

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <time.h>


#ifdef __SASC
int CXBRK(void) { return(0); }
int _CXBRK(void) { return(0); }
void chkabort(void) {}
#endif

#include <HBBS/ANSI_Codes.h>
#include <HBBS/Defines.h>
#include <HBBS/types.h>
#include <HBBS/structures.h>

#include <HBBS/Hbbscommon_protos.h>
#ifdef __SASC
#include <HBBS/hbbscommon_pragmas_sas.h>
#else
#include <HBBS/hbbscommon_pragmas_stc.h>
#endif

#include <HBBS/Hbbsnode_protos.h>
#ifdef __SASC
#include <HBBS/Hbbsnode_pragmas_sas.h>
#else
#include <HBBS/Hbbsnode_pragmas_stc.h>
#endif


#include <HBBS/release.h>
char *versionstr="$VER: CheckFiles "RELEASE_STR;

struct Library *HBBSCommonBase=NULL;
struct Library *HBBSNodeBase=NULL;

long __stack=16384;

struct BBSGlobalData *BBSGlobal=NULL;
struct NodeData *N_ND=NULL;
int N_NodeNum=-1;
char outstr[BIG_STR]; // temp string for displaying text..

BOOL checkall=FALSE;
struct List *FileList=NULL;
LONG Files=0;

char *file_name=NULL;
LONG file_size=0;
BOOL file_corrupt;
BOOL file_sysop;
BOOL file_incorrect;
LONG file_conf;
BOOL UserAround=TRUE;

static VOID cleanup(ULONG num)
{
  if (HBBSNodeBase)
  {
    HBBS_CleanUpDoor();
    CloseLibrary (HBBSNodeBase);
  }

  if (HBBSCommonBase)
  {
    HBBS_CleanUpCommon();
    CloseLibrary (HBBSCommonBase);
  }

  if (num) printf("Door Error = %d\n",num);

  exit(0);
}

static VOID init(char *name)
{
  if(!(HBBSCommonBase = OpenLibrary("HBBSCommon.library",0)))
  {
    cleanup(1);
  }

  if (!(HBBS_InitCommon()))
  {
    cleanup(2);
  }

  if(!(HBBSNodeBase = OpenLibrary("HBBSNode.library",0)))
  {
    cleanup(3);
  }

  if (!(HBBS_InitDoor(N_NodeNum,name)))
  {
    cleanup(4);
  }
  SetProgramName(name);
}

void GetFileList( void )
{
  BPTR FL;
  struct FileInfoBlock *FB;
  LONG noerror;


  if (FileList=AllocVec(sizeof(struct List),MEMF_PUBLIC))
  {
    NewList(FileList);

    if (FL=Lock(N_ND->NodeSettings.NodePlayPen,ACCESS_READ))
    {
      if (FB=AllocVec(sizeof(struct FileInfoBlock),MEMF_PUBLIC))
      {
        if (Examine(FL,FB))
        {
          do
          {
            if (noerror=ExNext(FL,FB))
            {
              if (FB->fib_DirEntryType<0) // File ? or DIR
              {
                Files++;
                NewStrNode(FB->fib_FileName,FileList);
              }
            }
          }
          while (noerror);
        }
        FreeVec(FB);
      }

      UnLock(FL);
    }
  }
}

void GetFile( char *fname )
{
  BPTR FL;
  struct FileInfoBlock *FB;

  strcpy(outstr,N_ND->NodeSettings.NodePlayPen);
  strcat(outstr,fname);
  if (FL=Lock(outstr,ACCESS_READ))
  {
    if (FB=AllocVec(sizeof(struct FileInfoBlock),MEMF_PUBLIC))
    {
      if (Examine(FL,FB))
      {
        file_name=AllocVec(50,MEMF_PUBLIC);
        strcpy(file_name,FB->fib_FileName);
        file_size=FB->fib_Size;
        file_corrupt=FALSE;
        file_sysop=FALSE;
        file_incorrect=FALSE;
        file_conf=0;
        if (N_ND->CurrentConf) //are we in a conf ?
        {
          file_conf=N_ND->CurrentConf->ConfNum;
        }
      }
      FreeVec(FB);
    }
    UnLock(FL);
  }
}

void DoorCleanup( void )
{
  if (FileList)
  {
    FreeStrList(FileList); // love those helpful library routines! :-)
  }
}


BOOL CheckChars(char *charsallowed, char *checkstr)
{

  // weird function...  you pass it a list of chars that are allowed in your string, if
  // a char apprears in your string that's not in your list of chars then we return FALSE

  int loop,len;
  BOOL foundbadchar=FALSE;

  len=strlen(checkstr);

  for (loop=0;loop<len && !foundbadchar;loop++)
  {
    foundbadchar=(strchr(charsallowed,checkstr[loop]) == 0);
  }

  return(foundbadchar);

}

BOOL CheckFileName ( UBYTE *filename,BOOL PromptUser)
{
  char *allowedchars="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqurstuvwxyz1234567890^+-_@!.";
  UBYTE fromfull[BIG_STR];

  BOOL nameok=TRUE,FileExists=FALSE,NameChanged=FALSE,DisplayedMessage=FALSE;

  do
  {
    if (strlen(filename)>12 || filename[0]=='.' || CheckChars(allowedchars,filename))
    {
      if (filename[0]=='.') filename[0]='_';
      nameok=FALSE;
    }


    if (!nameok || FileExists)
    {
      if (PromptUser && UserAround)
      {
        if (!DisplayedMessage)
        {
          sprintf(outstr,ANSI_RESET ANSI_FG_WHITE "\""ANSI_FG_YELLOW"%s"ANSI_FG_WHITE"\" "ANSI_FG_CYAN"is an invalid name for this system\r\n",filename);
          DOOR_WriteText(outstr);
          DisplayedMessage=TRUE;
        }
        filename[12]=0; //max 12 chars..

        strcpy(N_ND->CharsAllowed,allowedchars);

        DOOR_WriteText(ANSI_FG_YELLOW "Please enter a new name "ANSI_FG_BLUE": "ANSI_FG_WHITE);
        DOOR_GetLine(GL_EDIT|GL_DISPLAY|GL_HISTORY|GL_USECHARS,'\0',12,30,filename);
        RemoveSpaces(N_ND->CurrentLine);

        if (N_ND->OnlineStatus==OS_OFFLINE)
        {
          return(FALSE);
        }

        if (N_ND->CurrentLine[0])
        {
          strcpy(filename,N_ND->CurrentLine);
          NameChanged=TRUE;
        }
      }
      else
      {
        filename[12]=0; //max 12 chars..
        NameChanged=TRUE;
      }
    }

    if (NameChanged)
    {
      strcpy(fromfull,N_ND->NodeSettings.NodePlayPen);
      strcat(fromfull,filename);
      if (PathOK(fromfull))
      {
        if (PromptUser && UserAround)
        {
          nameok=FALSE;
          FileExists=TRUE;
          DOOR_WriteText(ANSI_FG_WHITE"There is a file with the same name as that in the playpen already!\r\n");
        }
        else
        {
          // if the user is not around the file will be renamed by the dorename() function below!
          nameok=TRUE;
        }
      }
      else
      {
        nameok=TRUE;
      }

    }


  } while (nameok==FALSE && N_ND->OnlineStatus==OS_ONLINE);

  return(TRUE);
}

void dorename(UBYTE *fromname,UBYTE *toname)
{
  UBYTE fromfull[BIG_STR],tofull[BIG_STR];

  ULONG Num=0;

  if (stricmp(fromname,toname)!=0)
  {

    strcpy(fromfull,N_ND->NodeSettings.NodePlayPen);
    strcat(fromfull,fromname);

    strcpy(tofull,N_ND->NodeSettings.NodePlayPen);
    strcat(tofull,toname);

    if (PathOK(tofull))
    {
      // file exists!, so find a new name for the file!

      do
      {
        toname[9]=0;
        sprintf(toname+strlen(toname),"%ld",Num++);

        strcpy(tofull,N_ND->NodeSettings.NodePlayPen);
        strcat(tofull,toname);

      } while (PathOK(tofull) && Num<256);

      Rename(fromfull,tofull);

      // there's hardly likely to be 255 files in the pp

    }
    else
    {
      Rename(fromfull,tofull);
    }
  }
}

void DoorMain(int argc,char *argv[])
{
  LONG loop,loop2; // *C* remove all references to loop2 and tmpname!
  char tmpname[20],extname[20],oldname[255];
  struct List *FileID=NULL;
  struct ConfData *conf;

  if (UserAround) DOOR_DisplaySpecialScreen("FCHECK_START");

  if (stricmp("ALL",argv[2])==0)
  {
    checkall=TRUE;
    GetFileList();
  }
  else Files=1;  //now we can for/next loop on the files variable..

  sprintf(outstr,"T:HBBS/MultipleFiles_%d",N_ND->NodeNum);
  DeleteFile(outstr);

  if (Files>1)
  {
    sprintf(tmpname,"%d",Files);
    HBBS_AppendStrToFile(outstr,tmpname);
  }

  DOOR_SystemDoor("PreCheck",NULL);

  for (loop=1;loop<=Files;loop++)
  {
    if (N_ND->OnlineStatus!=OS_ONLINE) UserAround=FALSE;
    if (checkall)
    {
      GetFile(HBBS_ListName(FileList,loop-1));
    }
    else GetFile(argv[3]);

    // ok, file_name and file_size contain the right details about the file that
    // has been uploaded into the playpen, so let's check em!



    // Create tmpname and extname strings..

    strcpy(oldname,file_name);

    // check to see if the file has no name (E.G. a file called ".BANANA" would be invalid)
    if (!CheckFileName(file_name,TRUE))
    {
      dorename(oldname,file_name);
      if (UserAround) DOOR_DisplaySpecialScreen("FCHECK_INVALIDNAME");

      sprintf(outstr,"BAD %s%s",file_name,UserAround ? "" : " NOUSER"); // move NOUSER to the door's system options!!!
      DOOR_SystemDoor("MoveFile",outstr);

    }
    else
    {
      dorename(oldname,file_name);
      for (loop2=strlen(file_name)-1;loop2>=0 && file_name[loop2]!='.';loop2--);
      strNcpy(tmpname,file_name,loop2);  // just the <filename> without the .ext
      strfcpy(extname,file_name,loop2+1); // the .extension name without the .


      DOOR_Add_Last_Upload(file_name);

      DOOR_SysopText("Extracting Diz...\r\n");

      sprintf(outstr,"%s %sWork/ %s %s %s%s",file_name,N_ND->NodeLocation,N_ND->NodeSettings.NodePlayPen,tmpname,extname,UserAround ? "" : " NOUSER");
      DOOR_SystemDoor("Extract_DIZ",outstr);

      DOOR_SysopText("Copying Diz...\r\n");

      // Make a copy of the .DIZ and call it .ADD

      sprintf(outstr,"%sWork/%s.DIZ",N_ND->NodeLocation,file_name);
      if (FileID=HBBS_LoadFile(outstr))
      {
        DOOR_SysopText("Modify Diz...\r\n");

        sprintf(outstr,"%s %sWork/ %s %s %s%s",file_name,N_ND->NodeLocation,N_ND->NodeSettings.NodePlayPen,tmpname,extname,UserAround ? "" : " NOUSER");
        DOOR_SystemDoor("Modify_DIZ",outstr);
      }
      else
      {
        DOOR_SysopText("New Diz...\r\n");

        sprintf(outstr,"%s %sWork/ %s %s %s%s",file_name,N_ND->NodeLocation,N_ND->NodeSettings.NodePlayPen,tmpname,extname,UserAround ? "" : " NOUSER");
        DOOR_SystemDoor("New_DIZ",outstr);

        sprintf(outstr,"%sWork/%s.DIZ",N_ND->NodeLocation,file_name);
        FileID=HBBS_LoadFile(outstr);
      }

      if (!FileID)
      {
        // No file id for some reason (maybe user uploaded a file with no diz and then
        // lost carrier. so we have to move the file to the conference's
        // "needdiz" directory..

        sprintf(outstr,"NEEDDIZ %s%s",file_name,UserAround ? "" : " NOUSER");
        DOOR_SystemDoor("MoveFile",outstr);

      }
      else
      {
        sprintf(outstr,"%sWork/%s.ADD",N_ND->NodeLocation,file_name);
        HBBS_SaveFile(outstr,FileID); // *C* error checking..?

        FreeStrList(FileID);
        FileID=NULL;


        sprintf(outstr,"%sBAD %s",UserAround ? "" : "NOUSER ",file_name);
        DOOR_SystemDoor("Check_Corrupt",outstr);
        if (stricmp("CORRUPT",N_ND->DoorReturn)==0)
        {
          file_corrupt=TRUE;
          if (UserAround) DOOR_DisplaySpecialScreen("FCHECK_BADFILE");

          sprintf(outstr,"BAD %sWork/%s.DIZ %s%s",N_ND->NodeLocation,file_name, file_name,UserAround ? "" : " NOUSER");
          DOOR_SystemDoor("AddDIZToList",outstr);

          sprintf(outstr,"%s %sWork/ %s %s %s%s",file_name,N_ND->NodeLocation,N_ND->NodeSettings.NodePlayPen,tmpname,extname,UserAround ? "" : " NOUSER");
          DOOR_SystemDoor("Update_DIZ",outstr);

          sprintf(outstr,"BAD %s%s",file_name,UserAround ? "" : " NOUSER");
          DOOR_SystemDoor("MoveFile",outstr);

        }
        else
        {

          sprintf(outstr,"%s%s",file_name,UserAround ? "" : " NOUSER");
          DOOR_SystemDoor("Confirm_UL",outstr);

          if (iposition("SYSOP",N_ND->DoorReturn)>=0) file_sysop=TRUE;
          if (iposition("INCORRECT",N_ND->DoorReturn)>=0) file_incorrect=TRUE;

          if (!file_sysop && !file_incorrect)
          {

            sprintf(outstr,"%s%s",file_name,UserAround ? "" : " NOUSER");
            if (DOOR_SystemDoor("AskConf",outstr))
            {
              file_conf=atoi(N_ND->DoorReturn);
            }

            if (!(file_conf>=1 && file_conf<=BBSGlobal->Conferences))
            {
              file_conf = N_ND->CurrentConf->ConfNum;
            }


            sprintf(outstr,"%s %d%s",file_name,file_conf,UserAround ? "" : " NOUSER");
            DOOR_SystemDoor("DupeCheck",outstr);
            if (iposition("DUPLICATE",N_ND->DoorReturn)>=0) file_incorrect=TRUE;

            if (!file_incorrect)
            {
              if (UserAround)
              {
                DOOR_DisplaySpecialScreen("Banner_TOP");
                //DOOR_WriteText(ANSI_RESET ANSI_FG_RED "[=---------------------------------------------------------------------------=]\r\n");

                DOOR_WriteText(ANSI_FG_WHITE "      Placing File in the \"" ANSI_FG_BLUE);
                DOOR_WriteText(HBBS_ListName(BBSGlobal->ConfList,file_conf-1));
                DOOR_WriteText(ANSI_FG_WHITE "\" conference.\r\n");

                DOOR_DisplaySpecialScreen("Banner_BOT");
                //DOOR_WriteText(ANSI_RESET ANSI_FG_RED "[=---------------------------------------------------------------------------=]\r\n" ANSI_RESET);
              }

              DOOR_SysopText("Adding To List...\r\n");

              sprintf(outstr,"%d %sWork/%s.DIZ %s%s",file_conf,N_ND->NodeLocation,file_name, file_name,UserAround ? "" : " NOUSER");
              DOOR_SystemDoor("AddDIZToList",outstr);
              //AddDIZToList <confnum> <diz_file> <file_name>

              DOOR_SysopText("Adding Creds...\r\n");
              sprintf(outstr,"%d %s%s",file_conf,file_name,UserAround ? "" : " NOUSER");
              DOOR_SystemDoor("AddCreds",outstr);
              //AddCreds <conf> <full_filename>

              DOOR_SysopText("Adding Time...\r\n");
              sprintf(outstr,"%d %s%s",file_conf,file_name,UserAround ? "" : " NOUSER");
              DOOR_SystemDoor("AddTime",outstr);
              //AddTime <conf> <full_filename>

              DOOR_SysopText("Updating DIZ...\r\n");
              sprintf(outstr,"%s %sWork/ %s %s %s%s",file_name,N_ND->NodeLocation,N_ND->NodeSettings.NodePlayPen,tmpname,extname,UserAround ? "" : " NOUSER");
              DOOR_SystemDoor("Update_DIZ",outstr);
              //UpdateDIZ <filename> <workdir> <playpen> <name> <ext> [NOUSER]


              DOOR_SysopText("Conf Info...\r\n");
              conf=(struct ConfData *)GetNode(BBSGlobal->ConfList,file_conf-1); // list index=0

              sprintf(outstr,"%d %s",file_conf,file_name);
              if (conf->KeepDIZWithFile)
              {
                DOOR_SysopText("Moving file and DIZ...\r\n");
                sprintf(outstr+strlen(outstr)," MOVEDIZ %s",file_name);
              }
              else
              {
                DOOR_SysopText("Moving file...\r\n");
              }
              if (!UserAround)
              {
                strcat(outstr," NOUSER");
              }
              DOOR_SystemDoor("MoveFile",outstr);
              //MoveFile <confnum> <full_filename> [MOVEDIZ filename] [NOUSER]

              N_ND->User.CallData.LastUploadDate=time(NULL);
              N_ND->User.NormalData.LastUploadDate=time(NULL);
            }
          }
          if (file_sysop || file_incorrect)
          {
            if (UserAround) DOOR_WriteText("Moving File to SYSOP's HOLD dir!\r\n");

            sprintf(outstr,"%s %sWork/%s.DIZ %s%s",file_sysop ? "SYSOP" : "HOLD",N_ND->NodeLocation,file_name, file_name,UserAround ? "" : " NOUSER");
            DOOR_SystemDoor("AddDIZToList",outstr);

            sprintf(outstr,"%s %sWork/ %s %s %s%s",file_name,N_ND->NodeLocation,N_ND->NodeSettings.NodePlayPen,tmpname,extname,UserAround ? "" : " NOUSER");
            DOOR_SystemDoor("Update_DIZ",outstr);

            sprintf(outstr,"HOLD %s%s",file_name,UserAround ? "" : " NOUSER");
            DOOR_SystemDoor("MoveFile",outstr);
          }

          DOOR_SysopText("Removing temp files...\r\n");

          sprintf(outstr,"%sWork/%s.DIZ",N_ND->NodeLocation,file_name);
          DeleteFile(outstr);
          sprintf(outstr,"%sWork/%s.ADD",N_ND->NodeLocation,file_name);
          DeleteFile(outstr);
          DOOR_SysopText("File Processed...\r\n");
        }
      }
    }
    FreeStr(file_name);
    file_name=NULL;
  }

  DoorCleanup();
}

int main(int argc,char *argv[])
{
  if (sscanf(argv[1],"%d",&N_NodeNum)==0)
  {
    printf("Invalid/No Paramaters for door!\n");
    exit (20);
  }

  if (stricmp("NOUSER",argv[argc-1])==0) UserAround=FALSE;

  init("Check_Files");

  if (BBSGlobal=HBBS_GimmeBBS())
  {
    if (N_ND=HBBS_NodeDataPtr(N_NodeNum)) // this should not fail in normal circumstances..
    {
      DoorMain(argc,argv);
    }
  }
  cleanup(0);
}
