/*
  Check_Corrupt
  =============

    Checks uploaded file for corruption

  Version
  =======

    Source code last changed for release V1.04

  Docs
  ====

    Not written yet

  Notes
  =====

    Source code based on ExampleDoor code from HydraBBS V1.0

  ToDo
  ====

    fix the overlapping buffer error message problem (see below)

  Params
  ======

    FileName (no path)
    FileName (with no .extension)
    File Extension (with no leading .)
    [NOUSER] (to enable quiet mode!)

*/

/* **** Includes *********************************************************** */

#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 <HBBS/ANSI_Codes.h>
#include <HBBS/Defines.h>
#include <HBBS/Access.h>
#include <HBBS/types.h>
#include <HBBS/structures.h>

#include <HBBS/Hbbscommon_protos.h>
#include <HBBS/hbbscommon_pragmas_sas.h>

#include <HBBS/Hbbsnode_protos.h>
#include <HBBS/Hbbsnode_pragmas_sas.h>

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

/* **** Variables ********************************************************** */


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

struct BBSGlobalData *BBSGlobal = NULL;
struct NodeData *N_ND           = NULL;
int    N_NodeNum                = -1;
char   outstr[1024];

long __stack=16*1024; // increse this in 4k incrments if you suffer from
                      // random/suprious crashings after or during the running
                      // of your door. (16k default)

int    gargc;         // these are just copies of main()'s argc and argv..
char   **gargv;

struct CheckerData
{
  V_STRING Extension;
  V_STRING TypeDescription;
  V_STRING Command;
  V_STRING Params;
  struct List *Errors;
};

/* **** Functions ********************************************************** */


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

#define INITERR_NOERROR 0
#define INITERR_NOPARAMS 1
#define INITERR_NOHBBSCOMMON 2
#define INITERR_INITCOMMONFAILED 3
#define INITERR_NOHBBSNODE 4
#define INITERR_INITDOORFAILED 5

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

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

  switch(num)
  {
    case INITERR_NOERROR:
      exit(0);

    case INITERR_NOPARAMS:
      puts("Bad or no paramaters specified, check your config files\n"
           "(Don't try starting doors from the CLI!)");
      break;

    case INITERR_NOHBBSCOMMON:
      puts("Could not open HBBSCommon.library, check your assign's");
      break;

    case INITERR_INITCOMMONFAILED:
      puts("Could not initialise HBBSCommon.library functions\n"
           "Is HBBS Running ?");
      break;

    case INITERR_NOHBBSNODE:
      puts("Could not open HBBSNode.library, check your assign's");
      break;

    case INITERR_INITDOORFAILED:
      puts("The node specified is either not running or did not want to\n"
           "start this door now. (Don't run doors from the CLI!)");
      break;
  }
  exit(20);
}

static VOID Init(char *name)
{
  if(!(HBBSCommonBase = OpenLibrary("HBBSCommon.library",0)))
  {
    CleanUp(INITERR_NOHBBSCOMMON);
  }

  if (!(HBBS_InitCommon()))
  {
    CleanUp(INITERR_INITCOMMONFAILED);
  }

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

  if (!(HBBS_InitDoor(N_NodeNum,name)))
  {
    CleanUp(INITERR_INITDOORFAILED);
  }
  SetProgramName(name);
}

/* **** DoorMain *********************************************************** */

BOOL ChangeDir( char *path)
{
  BPTR FL,NFL;

  if (PathOK(path))
  {
    if (FL = Lock(path,ACCESS_READ))
    {
      NFL=CurrentDir(FL);
      UnLock(NFL);
      return(TRUE);
    }
  }
  return(FALSE);
}

#define FILE_BUFFER_SIZE 1024*32 // *C* make smaller (see below)

void DoorMain( void )
{
  struct CfgFileData *Cfg;
  BOOL ConfigOK = TRUE,
       FoundError = FALSE,
       UserAround = TRUE,
       EndOfFile = FALSE;
  struct CheckerData *Checker;
  V_STRING FileName;
  V_STRING FileNameNoExt;
  char commandline[1024];
  char optionname[1024];
  char tempfilename[20];
  char tmpstr[1024];
  char filebuffer[FILE_BUFFER_SIZE];
  BPTR FH;
  LONG loop;
  struct Node *node;
  ULONG BytesRead;

  if (Checker = AllocVec(sizeof (struct CheckerData), MEMF_CLEAR))
  {

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

    if (gargc >= 2) FileName = gargv[2];
    if (gargc >= 3) FileNameNoExt = gargv[3];
    if (gargc >= 4) Checker -> Extension = gargv[4];

    // tmpstr is used twice below, then overwritten later.
    sprintf(tmpstr,"File type unknown for file \"%s\"",FileName);

    if ( ! Checker -> Extension)
    {
      if (UserAround) DOOR_WriteText(ANSI_FG_YELLOW "Unknown file type, file not checked!\r\n");
      HBBS_AddToCallersLog(tmpstr);
    }
    else
    {
      // check extension length (255 max filename length on most systems)
      if (strlen (Checker -> Extension) > 255) Checker -> Extension[255] = 0; // truncate the string.

      if (Cfg = HBBS_LoadConfig( "HBBS:System/FCheck",LCFG_NONE))
      {

        sprintf(optionname,"FileTypeDesc_%s",Checker->Extension);
        if (!HBBS_GetSetting(Cfg,(void *)&Checker -> TypeDescription,VTYPE_STRING,optionname,OPT_SINGLE))
          ConfigOK=FALSE;
        else
        {
          sprintf(optionname,"CheckerCMD_%s",Checker->Extension);
          if (!HBBS_GetSetting(Cfg,(void *)&Checker -> Command,VTYPE_STRING,optionname,OPT_SINGLE))
            ConfigOK=FALSE;
          else
          {
            sprintf(optionname,"CheckerParams_%s",Checker->Extension);
            if (!HBBS_GetSetting(Cfg,(void *)&Checker -> Params,VTYPE_STRING,optionname,OPT_SINGLE))
              ConfigOK=FALSE;
            else
            {
              sprintf(optionname,"Error_%s",Checker->Extension);
              if (!HBBS_GetSetting(Cfg,(void *)&Checker -> Errors,VTYPE_STRINGLIST,optionname,OPT_MULTI))
                ConfigOK=FALSE;
            }
          }
        }

        HBBS_FlushConfig(Cfg);
      }
      else
      {
        // add an extra bit to the error message
        sprintf(tmpstr+strlen(tmpstr)," extension unknown \"%s\"",Checker -> Extension);
        HBBS_AddToCallersLog(tmpstr);

        HBBS_LogError(BBSGlobal->ErrorLogFile,ERR_GENERAL,"Could not load HBBS:System/FCheck",TYPE_IMPORTANT);
      }

      if (ConfigOK)
      {
        if (ChangeDir(N_ND->NodeSettings.NodePlayPen))
        {
          if (UserAround)
          {
            sprintf(tmpstr,ANSI_FG_YELLOW "Checking: "ANSI_FG_CYAN"%s "ANSI_FG_WHITE"- "ANSI_FG_CYAN"%s"ANSI_FG_YELLOW" ... ",
                    FileName,Checker -> TypeDescription);
            DOOR_WriteText(tmpstr);
          }

          sprintf(tempfilename,"T:HBBS/FC%ld.TMP",N_ND->NodeNum);

          // build the command line to run.
//          sprintf(commandline,"%s <NIL: >%s %s",Checker -> Command, tempfilename,Checker -> Params);
          sprintf(commandline,"%s %s",Checker -> Command, Checker -> Params);

          replace(commandline,commandline,"@F@",FileName);
          replace(commandline,commandline,"@N@",FileNameNoExt);
          replace(commandline,commandline,"@E@",Checker -> Extension);
          replace(commandline,commandline,"@P@",N_ND->NodeSettings.NodePlayPen);

          // run the command

          DeleteFile(tempfilename); // remove the file, in case one exists and our checker doesn't run..

          HBBS_DosCommand(commandline,RDC_NONE,tempfilename);

//        DOOR_DisplayScreen(tempfilename);

          // now we check to see if the command we ran returned an error (by searching it's output)
          // for user-specified strings.

          if (FH = Open(tempfilename,MODE_OLDFILE))
          {

            filebuffer[FILE_BUFFER_SIZE-1] = 0; // null terminate our buffer

            // *C* this routine is bugged, if the output file is bigger
            // than FILE_BUFFER_SIZE and the error message overlaps this
            // offset in the output file then it won't be detected.
            // temporarily using a LARGE file buffer to workaround this problem!

            do
            {
              BytesRead = Read(FH,filebuffer,FILE_BUFFER_SIZE-1);

              if (BytesRead == 0)
                EndOfFile = TRUE;
              else
              {
                // convert null terminators to \n's

                for (loop = 0; loop < BytesRead ; loop ++)
                {
                  if (filebuffer[loop] == 0 ) filebuffer[loop] = '\n';
                }

                // check our error message strings, stopping at the first one we find.

                for (node=Checker -> Errors -> lh_Head; !FoundError && node -> ln_Succ; node = node -> ln_Succ)
                {
                  if (iposition(node->ln_Name,filebuffer) >=0 ) //returns <0 for failure!
                  {
                    FoundError=TRUE;
                    DOOR_Return("CORRUPT"); // tell checkfiles that the file is corrupted!
                    sprintf(tmpstr,"While checking \"%s\", the file checker (%s) reported \"%s\"",FileName, Checker -> Command, node->ln_Name);
                    HBBS_AddToCallersLog(tmpstr);
                  }
                }
              }

            } while (!EndOfFile && !FoundError);

            Close(FH);

          } // tempfile opened ok

          if (UserAround)
          {
            if (FoundError)
              DOOR_WriteText(ANSI_FG_RED"FAILED!\r\n");
            else
              DOOR_WriteText(ANSI_FG_GREEN"OK!\r\n");
          }
          DeleteFile(tempfilename);


        } // changedir OK
      } // configOK

      FreeStr(Checker -> TypeDescription);
      FreeStr(Checker -> Command);
      FreeStr(Checker -> Params);
      FreeStrList(Checker -> Errors);
    }
    FreeVec(Checker);
  }
}

int main(int argc,char **argv)
{

  /* set these so that we can access the paramaters in the rest of the program */

  gargc=argc;
  gargv=argv;

  /* Check to see if we've got any paramaters */

  if (argc <= 1)
  {
    CleanUp(INITERR_NOPARAMS);
  }

  /* Get the node number from the paramaters */

  if (sscanf(argv[1],"%d",&N_NodeNum)==0)
  {
    CleanUp(INITERR_NOPARAMS);
  }

  /* Initialise and set the door/program name */

  Init("Check Corrupt");

  /* if the init() didn't fail then we need to ask HBBS for the pointer to
     it's data structures */

  if (BBSGlobal=HBBS_GimmeBBS())
  {
    /* Then we need to get the node's data structures for the node this door is going to run on */

    if (N_ND=HBBS_NodeDataPtr(N_NodeNum))
    {

      /* Now that's done, you can put the rest of your code here, or in the DoorMain() function */

      DoorMain();
    }
  }

  /* Cleanup and close libraries */

  CleanUp(INITERR_NOERROR);
}

/* **** End Of File ******************************************************** */
