/*



  if you re-design the GUI don't forget to look at the changes made to the
  .c and .h files.  specificy, OpenStatWindow() has to have parameters passed
  to it and should not try and lock/unlock a named public screen.



  Todo
  ====


  listview for status messages instead of sysoptext()'ing them..

  cps

  percent

*/

/* **** 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 <devices/serial.h>
#include <devices/timer.h>

#include <dos/dosextens.h>
#include <intuition/screens.h>
#include <intuition/intuition.h>
#include <intuition/gadgetclass.h>
#include <libraries/gadtools.h>
#include <diskfont/diskfont.h>
#include <utility/utility.h>
#include <graphics/gfxbase.h>
#include <workbench/workbench.h>
#include <graphics/scale.h>
#include <clib/wb_protos.h>
#include <clib/intuition_protos.h>
#include <clib/gadtools_protos.h>
#include <clib/graphics_protos.h>
#include <clib/utility_protos.h>
#include <clib/diskfont_protos.h>

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

#define HBBS_NODE
#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>
#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: HYDRA "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];

struct List *MsgHistory=NULL; // used to stote status messages

BOOL StatusOpen=FALSE;

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

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

long AlreadyClosed=FALSE;
/*
struct FileBuffer
{
  struct Node node; // filename in ln_Name

  ULONG BytesTransferred;
  ULONG BlocksTransferred;
  ULONG ActualSize;
  BOOL Testing;
  BOOL ReadFile;

  BOOL UseAsync;

  struct AsyncFile *ASFH;
  BPTR DOSFH;

};

struct FileBuffer
{
  struct Node node; // filename in ln_Name

  ULONG BytesTransferred;
  ULONG ActualSize;
  ULONG StartOffset;
  ULONG CPS;
};

 see hydracom.h

 */

struct List *UL_FileList=NULL; // a list of FileBuffer nodes..
LONG UL_Files=0;

struct List *DL_FileList=NULL; // a list of FileBuffer nodes..
LONG DL_Files=0;

struct List *SKIP_FileList=NULL; // a list of Struct Nodes. name of file without path stored in ln_Name
                                 // ln_Pri is used as flags, see defines above
LONG SKIP_Files=0;

UBYTE *DL_Path=NULL;
BOOL DL_PathOK=FALSE;

UBYTE *UL_Path=NULL;
BOOL UL_PathOK=FALSE;

BOOL Flag_CheckConfPaths=FALSE;
BOOL Flag_TakeCredits=FALSE;
BOOL Flag_WindowOpen=FALSE;

BOOL SerialGrabbed;

struct FileBuffer *CurrentULFile=NULL;
LONG CurrentULFileNum=1;

struct FileBuffer *CurrentDLFile=NULL;
LONG CurrentDLFileNum=1;

struct MsgPort *OldWritePort,*OldReadPort;

struct IOExtSer *h_serio=NULL;

#define MAIN
#include "hydracom.h"

extern char *mon[12];

extern struct Screen      *Screen;

#include "Hydra_GUI.h"

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


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

void GrabSerial( void )
{
  Forbid();
  OldWritePort=N_ND->SerWrite->IOSer.io_Message.mn_ReplyPort;
  OldReadPort=N_ND->SerRead->IOSer.io_Message.mn_ReplyPort;
  Permit();
  SerialGrabbed=TRUE;
}

void ReleaseSerial( void )
{
  if (SerialGrabbed)
  {
    Forbid();
    N_ND->SerWrite->IOSer.io_Message.mn_ReplyPort=OldWritePort;
    N_ND->SerRead->IOSer.io_Message.mn_ReplyPort=OldReadPort;
    Permit();
  }
}

void endprog (int errcode)
{

  sys_reset();

  ClosebaseCatalog();

  CloseLibs();

  if (HBBSNodeBase)
  {
    HBBS_CleanUpDoor();
    CloseLibrary (HBBSNodeBase);
  }

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

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

  resultlog(false,NULL,0L,0L);
  if (logfp) fclose(logfp);
  exit (errcode);

}



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

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

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

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

  if (OpenLibs())
  {
    endprog(5);
  }

  OpenbaseCatalog(NULL,NULL);

  SetProgramName(name);

}


void CheckULList(void)
{
  struct FileBuffer *node;
  struct TaggedFile *Tag;

  char tmpstr[BIG_STR];

  for (node = (struct FileBuffer *)UL_FileList->lh_Head ; node->node.ln_Succ ; node =(struct FileBuffer *)node->node.ln_Succ)
  {

    if (node->WarezFile)
    {
      sprintf(tmpstr,"%d %s",node->ConferenceNum,FilePart(node->node.ln_Name));
    }
    else
    {
      sprintf(tmpstr,">%s",node->node.ln_Name);
    }

    DOOR_Add_Last_Download(tmpstr);

    if (node->ActualSize==node->BytesTransferred)
    {
      N_ND->Actions[ACTN_DOWNLOAD]=ACTC_DOWNLOAD;

      // transferred the file ok, remove creds (if applicable)
      // and write to callers log and ul/dl log..

      sprintf(outstr,"DOWNLOADED File:\"%s\" Size:%ld",node->node.ln_Name,node->ActualSize);
      HBBS_AddToCallersLog(outstr);

      if (N_ND->User->Valid)
      {
        N_ND->User->CallData->ActualDownloadFiles++;
        N_ND->User->NormalData->ActualDownloadFiles++;
        N_ND->User->CallData->ActualDownloadBytes+=node->ActualSize;
        N_ND->User->NormalData->ActualDownloadBytes+=node->ActualSize;

        if (node->CPS > N_ND->User->CallData->BestCPSDown)
        {
          N_ND->User->CallData->BestCPSDown=node->CPS;
          N_ND->User->NormalData->BestCPSDown=node->CPS;
        }

        N_ND->User->FilesDownloaded++;
        N_ND->User->BytesDownloaded+=node->ActualSize;
      }
      if (Tag=HBBS_FindTag(node->node.ln_Name,TRUE))
      {
        if (Flag_TakeCredits && N_ND->User->Valid)
        {
          if(Tag->WarezFile && !(Tag->Flags & TF_FREEDOWNLOAD) )
          {
            sprintf(outstr,ANSI_RESET ANSI_FG_CYAN "Removing "ANSI_FG_WHITE"%6ld"ANSI_FG_CYAN" credits for file: "ANSI_FG_WHITE"%s\r\n",node->ActualSize,FilePart(node->node.ln_Name));
            DOOR_WriteText(outstr);

            N_ND->User->CallData->DownloadFiles++;
            N_ND->User->NormalData->DownloadFiles++;

            N_ND->User->CallData->DownloadBytes+=Tag->FileSize;
            N_ND->User->NormalData->DownloadBytes+=Tag->FileSize;
          }

          if (Tag->Flags & TF_FREEDOWNLOAD)
          {
            sprintf(outstr,ANSI_RESET ANSI_FG_CYAN "%s is a "ANSI_FG_WHITE"free download"ANSI_FG_CYAN", no credit's removed",FilePart(node->node.ln_Name));
            DOOR_WriteText(outstr);
          }
        }

        // and remove the tagged file

        N_ND->TaggedFiles--; // first! :-)
        Remove((struct Node*)Tag);

        FreeStr(Tag->node.ln_Name);
        FreeVec(Tag);
      }


    }
    else
    {
      // *C* add U/D log too..
      // failed transferring that file, leave it tagged and write to the log..
      sprintf(outstr,"FAILED DOWNLOAD File:\"%s\" Size:%ld Transferred:%ld",node->node.ln_Name,node->ActualSize,node->BytesTransferred);
      HBBS_AddToCallersLog(outstr);
      N_ND->Actions[ACTN_FAILEDDOWNLOAD]=ACTC_FAILEDDOWNLOAD;

    }
  }
}

void CheckDLList(void)
{
  struct FileBuffer *node;

  for (node = (struct FileBuffer *)DL_FileList->lh_Head ; node->node.ln_Succ ; node =(struct FileBuffer *)node->node.ln_Succ)
  {
    if (node->ActualSize==node->BytesTransferred)
    {
      // transferred the file ok, add creds (if applicable)
      // and write to callers log and ul/dl log..

      N_ND->User->CallData->ActualUploadFiles++;
      N_ND->User->NormalData->ActualUploadFiles++;
      N_ND->User->CallData->ActualUploadBytes+=node->ActualSize;
      N_ND->User->NormalData->ActualUploadBytes+=node->ActualSize;

// AddCreds handles these now
//    N_ND->User->FilesUploaded++;
//    N_ND->User->BytesUploaded+=node->ActualSize;

      if (node->CPS > N_ND->User->CallData->BestCPSUp)
      {
        N_ND->User->CallData->BestCPSUp=node->CPS;
        N_ND->User->NormalData->BestCPSUp=node->CPS;
      }

      sprintf(outstr,"UPLOADED File:\"%s\" Size:%ld",node->node.ln_Name,node->ActualSize);
      HBBS_AddToCallersLog(outstr);
      N_ND->Actions[ACTN_UPLOAD]=ACTC_UPLOAD;


    }
    else
    {
      // *C* add U/D log too..
      // failed transferring that file, leave it tagged and write to the log..

      N_ND->Actions[ACTN_FAILEDUPLOAD]=ACTC_FAILEDUPLOAD;

      sprintf(outstr,"FAILED UPLOAD File:\"%s\" Size:%ld Transferred:%ld",node->node.ln_Name,node->ActualSize,node->BytesTransferred);
      HBBS_AddToCallersLog(outstr);


      sprintf(outstr,ANSI_RESET ANSI_FG_CYAN "Moving failed file"ANSI_FG_BLUE": "ANSI_FG_WHITE"%s"ANSI_FG_CYAN" Size"ANSI_FG_BLUE": "ANSI_FG_WHITE"%ld\r\n",node->node.ln_Name,node->BytesTransferred);
      DOOR_WriteText(outstr);

      sprintf(outstr,"%s@%ld",node->node.ln_Name,N_ND->User->CallData->UserID);

      if (Rename(node->node.ln_Name,outstr))
      {
        sprintf(outstr,"%sPARTUPLOAD %s@%ld",N_ND->OnlineStatus==OS_ONLINE ? "" : "NOUSER ",FilePart(node->node.ln_Name),N_ND->User->CallData->UserID); // move NOUSER to the door's system options!!!
        DOOR_SystemDoor("MoveFile",outstr);
      }
      else
      {
        DeleteFile(node->node.ln_Name);
      }

    }
  }
}


void Add_Msg_To_List( char *buf )
{
  struct Node *newnode;

  if (StatusOpen)
  {
    if (MsgHistory == NULL)
    {
      MsgHistory=HBBS_CreateList();
    }

    if (MsgHistory)
    {
      if (newnode=HBBS_CreateNode(buf,0))
      {
        AddHead(MsgHistory,newnode);
        GT_SetGadgetAttrs(StatGadgets[Stat_MsgHistory],Stat,NULL,GTLV_Labels,MsgHistory,GTLV_Top,0,TAG_DONE);
      }
    }
  }
}

void message (int level, char *fmt,...)
{
        char       buf[255];
        long       tim;
        struct tm *t;
        va_list    arg_ptr;

        tim = time(NULL);
        t = localtime(&tim);

        va_start(arg_ptr,fmt);
        sprintf(buf, "MSG: %c %02d %03s %02d:%02d:%02d %-4s ",
                     *fmt, t->tm_mday, mon[t->tm_mon],
                     t->tm_hour, t->tm_min, t->tm_sec, LOGID);
        vsprintf(&buf[23], &fmt[1], arg_ptr);
        va_end(arg_ptr);

        if (level >= loglevel && logfp)
           fprintf(logfp, "%s\n", buf);

        Add_Msg_To_List(buf);

}


void cprint (char *fmt, ...)
{
        char    buf[255];
        va_list arg_ptr;

        strcpy(buf,"CP :");

        va_start(arg_ptr,fmt);
        vsprintf(buf+4, fmt, arg_ptr);
        va_end(arg_ptr);

        Add_Msg_To_List(buf);

}

/*
#define MSGM_NONE 0
#define MSGM_XMIT 1
#define MSGM_POS 2
#define MSGM_FSIZE 4
#define MSGM_CPS 8
#define MSGM_TIME 16
#define MSGM_MSG 32
#define MSGM_FNAME 64
*/

void updatestatus(ULONG msgmask,int xmit,long pos,long fsize, long cps,long mins, long secs,char *fname,char *fmt, ...)
{
  char    buf[255],timestr[10];
  va_list arg_ptr;

  if (fmt!=NULL && (msgmask & MSGM_MSG))
  {
    va_start(arg_ptr,fmt);
    vsprintf(buf, fmt, arg_ptr);
    va_end(arg_ptr);
  }
  else
  {
    buf[0]=0;
  }

  if (StatusOpen)
  {
    if (msgmask & MSGM_XMIT)
    {
      if (xmit)
      {

        if (msgmask & MSGM_CPS)
        {
          GT_SetGadgetAttrs(StatGadgets[Stat_txCPS],Stat,NULL,GTNM_Number,(LONG) cps ,TAG_DONE);
          if (!N_ND->NodeSettings.Iconified)
          {
            sprintf(N_ND->LastCPS,"%ld",cps);
            DOOR_UpdateNodeStatus(UPD_CPSBAUD);
          }
        }


        if (msgmask & MSGM_POS)
        {
          GT_SetGadgetAttrs(StatGadgets[Stat_txDone],Stat,NULL,GTNM_Number,(LONG) pos ,TAG_DONE);
        }
        if (msgmask & MSGM_FSIZE)
        {
          GT_SetGadgetAttrs(StatGadgets[Stat_txSize],Stat,NULL,GTNM_Number,(LONG) fsize ,TAG_DONE);
        }
        if (msgmask & MSGM_TIME)
        {
          sprintf(timestr,"%ld:%02ld",mins,secs);
          GT_SetGadgetAttrs(StatGadgets[Stat_txTime],Stat,NULL,GTTX_Text,timestr ,TAG_DONE);
        }
        if (msgmask & MSGM_FNAME)
        {
          GT_SetGadgetAttrs(StatGadgets[Stat_txFile],Stat,NULL,GTTX_Text,fname ,TAG_DONE);
        }
        if (msgmask & MSGM_MSG && buf[0])
        {
          GT_SetGadgetAttrs(StatGadgets[Stat_txStatus],Stat,NULL,GTTX_Text,buf ,TAG_DONE);
        }
      }
      else
      {
        if (msgmask & MSGM_CPS)
        {
          GT_SetGadgetAttrs(StatGadgets[Stat_rxCPS],Stat,NULL,GTNM_Number,(LONG) cps ,TAG_DONE);
        }

        if (msgmask & MSGM_POS)
        {
          GT_SetGadgetAttrs(StatGadgets[Stat_rxDone],Stat,NULL,GTNM_Number,(LONG) pos ,TAG_DONE);
        }
        if (msgmask & MSGM_FSIZE)
        {
          GT_SetGadgetAttrs(StatGadgets[Stat_rxSize],Stat,NULL,GTNM_Number,(LONG) fsize ,TAG_DONE);
        }
        if (msgmask & MSGM_TIME)
        {
          sprintf(timestr,"%ld:%02ld",mins,secs);
          GT_SetGadgetAttrs(StatGadgets[Stat_rxTime],Stat,NULL,GTTX_Text,timestr ,TAG_DONE);
        }
        if (msgmask & MSGM_FNAME)
        {
          GT_SetGadgetAttrs(StatGadgets[Stat_rxFile],Stat,NULL,GTTX_Text,fname ,TAG_DONE);
        }
        if (msgmask & MSGM_MSG && buf[0])
        {
          GT_SetGadgetAttrs(StatGadgets[Stat_rxStatus],Stat,NULL,GTTX_Text,buf ,TAG_DONE);
        }
      }
    }
  }

  if (msgmask & MSGM_XMIT)
  {
    if (msgmask & MSGM_POS)
    {
      if (xmit)
      {
        CurrentULFile->BytesTransferred=pos;
      }
      else
      {
        CurrentDLFile->BytesTransferred=pos;
      }
    }

    if (msgmask & MSGM_FSIZE)
    {
      if (xmit)
      {
        CurrentULFile->ActualSize=fsize;
      }
      else
      {
        CurrentDLFile->ActualSize=fsize;
      }
    }                                                             
  }


}

char *ffirst(void)

{
  CurrentULFile=(struct FileBuffer*)UL_FileList->lh_Head;
  CurrentULFileNum=1;
  return(CurrentULFile->node.ln_Name);

}

char *fnext(void)
{
  if (CurrentULFileNum<UL_Files)
  {
    CurrentULFile=(struct FileBuffer*)CurrentULFile->node.ln_Succ;
    CurrentULFileNum++;
    return(CurrentULFile->node.ln_Name);
  }
  return(NULL);
}

ULONG FileInPaths(STRPTR FileName)
{
  ULONG SkipReason = SKIPF_NONE;
  UBYTE tmpfilename[1024];
  UBYTE newname[1024];
  struct ConfData *Conf=NULL;
  struct Node *node;
  BOOL Found;

  // we have been instructed to open a file for reading and the user is UPLOADING a file to the BBS
  // so we have to check for the following:
  // 1) The file is not being uploaded by anyone else
  // 2) the file does not exist in any of the download paths for the current conference
  // 3) the file does not exist in a partupload directory
  //    3.a) if it does and was started to upload by the user who is logged on now then
  //         copy the file to the playpen directory, delete ot from the partupload dir
  //         and resume uploading


  // If not found it yet, then check the other nodes playpen directories

  if (SkipReason == SKIPF_NONE)
  {
    SkipReason = HBBS_OkToUpload(FileName);
  }

  if ((Flag_CheckConfPaths) && (SkipReason == SKIPF_NONE))
  {
    if (Conf=FindConf())  // are we in a conference ?
    {
      // Search Download paths in current conference.

      for (node=Conf->Download->lh_Head;(SkipReason == SKIPF_NONE) && (node->ln_Succ);node=node->ln_Succ)
      {
        strcpy(tmpfilename,node->ln_Name);
        strcat(tmpfilename,FileName);
        if (PathOK(tmpfilename)) SkipReason=SKIPF_INADLPATH;
      }

      // check in partupload directory and if present copy to playpen so user can resume the u/l..

      if (SkipReason == SKIPF_NONE)
      {
        Found=FALSE;
        for (node=Conf->PartUpload->lh_Head;!Found && node->ln_Succ;node=node->ln_Succ)
        {
          sprintf(tmpfilename,"%s%s@%ld",node->ln_Name,FileName,N_ND->User->CallData->UserID);
          if (PathOK(tmpfilename))
          {
            strcpy(newname,DL_Path);
            AddPart(newname,FileName,1024);
            Rename(tmpfilename,newname);    // *C* change and add a copy here if rename fails!!
                                            // *C* at the moment playpen and confs MUST be on same amiga volume
            Found=TRUE;
          }
        }
      }
    }
  }

  return(SkipReason);
}

/*
BOOL OldFileInPaths(STRPTR FileName)
{
  BOOL FileExists;
  UBYTE tmpfilename[1024];
  UBYTE newname[1024];
  struct ConfData *Conf=NULL;
  struct NodeData *nd;
  struct Node *node;

  // we have been instructed to open a file for reading and the user is UPLOADING a file to the BBS
  // so we have to check for the following:
  // 1) The file is not being uploaded by anyone else
  //    1.a) count nodes
  //    1.b) scan nodes PlayPen directory for same file
  // 2) the file does not exist in any of the download paths for the current conference
  //    2.a) are we in a conference ?
  // 3) the file does not exist in a partupload directory
  //    3.a) if it does and was started to upload by the user who is logged on now then
  //         copy the file to the playpen directory, delete ot from the partupload dir
  //         and resume uploading

  FileExists=FALSE;

  if (Flag_CheckConfPaths)
  {
    if (Conf=FindConf())  // are we in a conference ?
    {
      // Search Download paths in current conference.

      for (node=Conf->Download->lh_Head;!FileExists && node->ln_Succ;node=node->ln_Succ)
      {
        strcpy(tmpfilename,node->ln_Name);
        strcat(tmpfilename,FileName);
        if (PathOK(tmpfilename)) FileExists=TRUE;
      }
    }

    // If not found it yet, then check the other nodes playpen directories

    if (!FileExists)
    {
      for (nd=(struct NodeData *)BBSGlobal->NodeList->lh_Head;!FileExists && nd->node.ln_Succ;nd=(struct NodeData*)nd->node.ln_Succ)
      {
        strcpy(tmpfilename,nd->NodeSettings.NodePlayPen);
        AddPart(tmpfilename,FileName,1024);
        if (PathOK(tmpfilename)) FileExists=TRUE;
      }
    }

    // check in partupload directory and if present copy to playpen so user can resume the u/l..

    if (!FileExists)
    {

      for (node=Conf->PartUpload->lh_Head;!FileExists && node->ln_Succ;node=node->ln_Succ)
      {
        sprintf(tmpfilename,"%s%s@%ld",node->ln_Name,FileName,N_ND->User->CallData->UserID);
        if (PathOK(tmpfilename))
        {
          strcpy(newname,DL_Path);
          AddPart(newname,FileName,1024);
          Rename(tmpfilename,newname);    // *C* change and add a copy here if rename fails!!
                                          // *C* at the moment playpen and confs MUST be on same amiga volume
          FileExists=TRUE; // just so we exit this loop..
        }
      }

      // This part will never set file exists to be TRUE because if the file does exist
      // then it will be moved so the user can resume.  If we set FileExists then the file would be skipped
      // and not resumed.

      FileExists=FALSE;
    }
  }

  return(FileExists);
}
*/

void AddSkip(STRPTR FileName,ULONG SkipFlags)
{
  struct Node *SkipNode;

  sprintf(outstr,"SKIPPED File:\"%s\"%s",FileName,SkipFlags & SKIPF_INADLPATH ? "Found in a DL path" : SkipFlags & SKIPF_ONANOTHERNODE ? "User on another node is uploading this file" : "No reason given");
  HBBS_AddToCallersLog(outstr);

  if (SkipNode=HBBS_CreateNode(FileName,0))
  {
    SkipNode->ln_Pri = SkipFlags;
    AddTail(SKIP_FileList,SkipNode);
    SKIP_Files++;
  }
}

#define NAMELEN  64
#define LINELEN  128


static char xfer_log[PATHLEN];
static boolean xfer_logged;
static char xfer_pathname[PATHLEN];
static char xfer_real[NAMELEN],
            xfer_temp[NAMELEN];
static long xfer_fsize,
            xfer_ftime;


void unique_name (char *pathname)
{
        static char *suffix = ".000";
        register char *p;
        register int   n;

        if (fexist(pathname)) {
           p = pathname;
           while (*p && *p!='.') p++;
           for (n=0; n<4; n++) {
               if (!*p) {
                  *p     = suffix[n];
                  *(++p) = '\0';
               }
               else
                  p++;
           }

           while (fexist(pathname)) {
                 p = pathname + ((int) strlen(pathname)) - 1;
                 if (!isdigit(*p))
                    *p = '0';
                 else {
                    for (n=3; n--;) {
                        if (!isdigit(*p)) *p = '0';
                        if (++(*p) <= '9') break;
                        else               *p-- = '0';
                    }/*for*/
                 }
           }/*while(exist)*/
        }/*if(exist)*/
}/*unique_name()*/


char *xfer_init (char *fname, long fsize, long ftime)
{
        char  linebuf[LINELEN + 1];
        char  bad_real[NAMELEN],
              bad_temp[NAMELEN];
        long  bad_fsize,
              bad_ftime;
        char *p;
        FILE *fp;
        ULONG SkipReason=SKIPF_NONE;

        if (single_done)
           return (NULL);
                                                              
        strcpy(xfer_real,fname);
        xfer_fsize = fsize;
        xfer_ftime = ftime;

        mergepath(xfer_pathname,download,xfer_real);
        if (fexist(xfer_pathname) || (SkipReason = FileInPaths(xfer_real)))
        {
          if (SkipReason)
            AddSkip(xfer_real,SkipReason);
          else
            AddSkip(xfer_real,SKIPF_INADLPATH);
          return (NULL); // SKIP FILE!
        }

        if (CurrentDLFile=(struct FileBuffer*)HBBS_CreateNode(fname,sizeof(struct FileBuffer)))
        {
          CurrentDLFile->StartOffset=0;
          CurrentDLFile->CPS=0;

          AddTail(DL_FileList,(struct Node*)CurrentDLFile);
          DL_Files++;
        }
        else return(NULL);

        mergepath(xfer_log,download,"BAD-XFER.LOG"); // *C*

        if ((fp = sfopen(xfer_log,"rt",DENY_WRITE)) != NULL) {
           while (fgets(linebuf,LINELEN,fp)) {
                 sscanf(linebuf,"%s %s %ld %lo",
                                bad_real, bad_temp, &bad_fsize, &bad_ftime);
                 if (!strcmp(xfer_real,bad_real) &&
                     xfer_fsize == bad_fsize && xfer_ftime == bad_ftime) {
                    mergepath(xfer_pathname,download,bad_temp);
                    if (fexist(xfer_pathname)) {
                       fclose(fp);
                       strcpy(xfer_temp,bad_temp);

                       xfer_logged = true;
                       return (xfer_pathname);
                    }
                 }
           }

           fclose(fp);
        }

        strcpy(xfer_pathname,download);
        p = xfer_pathname + ((int) strlen(xfer_pathname));
        strcat(xfer_pathname,"BAD-XFER.000");
        unique_name(xfer_pathname);
        strcpy(xfer_temp,p);

        xfer_logged = false;
        return (xfer_pathname);
}/*xfer_init()*/


boolean xfer_bad (void)
{
        struct stat f;
        FILE *fp;
        int n;

        if (single_file[0])
           single_done = true;

        if (xfer_logged)                        /* Already a logged bad-xfer */
           return (true);

        n = ((int) strlen(xfer_real)) - 1;
        if (n > 3 &&
            (!strcmp(&xfer_real[n-3],".PKT") || !strcmp(&xfer_real[n-3],".REQ"))) {
           xfer_del();
           return (false);                      /* don't recover .PKT / .REQ */
        }

        stat(xfer_pathname,&f);
        if (noresume || f.st_size < 1024L) {     /* not allowed/worth saving */
           xfer_del();
           return (false);
        }

        if ((fp = sfopen(xfer_log,"at",DENY_WRITE)) != NULL) {
           fprintf(fp,"%s %s %ld %lo\n",
                     xfer_real, xfer_temp, xfer_fsize, xfer_ftime);
           fclose(fp);

           return (true);                             /* bad-xfer logged now */
        }

        xfer_del();
        return (false);                             /* Couldn't log bad-xfer */
}/*xfer_bad()*/


char *xfer_okay (void)
{
        static char new_pathname[PATHLEN];
        char *p;

        strcpy(new_pathname,download);
        p = new_pathname + ((int) strlen(new_pathname));   /* start of fname */
        if (single_file[0]) {
           strcat(new_pathname,single_file);           /* add override fname */
           single_done = true;
        }
        else {
           strcat(new_pathname,xfer_real);                 /* add real fname */
           unique_name(new_pathname);                      /* make it unique */
        }
        rename(xfer_pathname,new_pathname);           /* rename temp to real */
        if (!nostamp && xfer_ftime)
           setstamp(new_pathname,xfer_ftime);               /* set timestamp */

        if (xfer_logged)                         /* delete from bad-xfer log */
           xfer_del();




        return (strcmp(p,xfer_real) ? p : NULL);              /* dup rename? */
}


void xfer_del (void)
{
        char  new_log[PATHLEN];
        char  linebuf[LINELEN + 1];
        char  bad_real[NAMELEN],
              bad_temp[NAMELEN];
        long  bad_fsize,
              bad_ftime;
        FILE *fp, *new_fp;
        boolean left;

        if (fexist(xfer_pathname))
           unlink(xfer_pathname);

        if ((fp = sfopen(xfer_log, "rt", DENY_WRITE)) != NULL) {
           mergepath(new_log,download,"BAD-XFER.$$$");
           if ((new_fp = sfopen(new_log, "wt", DENY_ALL)) != NULL) {
              left = false;
              while (fgets(linebuf,LINELEN,fp)) {
                    sscanf(linebuf,"%s %s %ld %lo",
                                   bad_real, bad_temp, &bad_fsize, &bad_ftime);
                    if (strcmp(xfer_real,bad_real) ||
                        strcmp(xfer_temp,bad_temp) ||
                        xfer_fsize != bad_fsize || xfer_ftime != bad_ftime) {
                       fputs(linebuf,new_fp);
                       left = true;
                    }
              }
              fclose(fp);
              fclose(new_fp);
              unlink(xfer_log);
              if (left) rename(new_log,xfer_log);
              else      unlink(new_log);
           }
           else
              fclose(fp);
        }
}/*xfer_del()*/

void init(void)
{
        sys_init();

        dtr_out(1);

        if (com_speed)
           com_setspeed(com_speed);
        if (!com_speed)
           com_speed = N_ND->NodeDevice.SerialBaud;
        if (!cur_speed)
           cur_speed = com_speed;
}

int batch_hydra (char *filespec, char *alias)
{
        char *p;
        char  doafter;
        int   fd;

        if (!filespec) return(0);

        switch (*filespec)
        {
               case '^':
               case '#': doafter = *filespec++;
                         break;
               default:  doafter = 0;
                         break;
        }

        for (p = ffirst(); p; p = fnext()) {
            strcpy(work,p);
            switch (hydra(work,alias)) {
                   case XFER_ABORT:
                        return (5);

                   case XFER_SKIP:
                        break;

                   case XFER_OK:
                        switch (doafter) {
                               case '^':  /* Delete */
                                    if (unlink(work))
                                       message(6,"!HSEND: Could not delete %s",work);
                                    else
                                       message(1,"+Sent-H deleted %s",work);
                                    break;

                               case '#':  /* Truncate */
                                    if ((fd = dos_sopen(work,2)) < 0) {
                                       message(6,"!HSEND: Error truncating %s",work);
                                       break;
                                    }
                                    dos_close(fd);
                                    message(1,"+Sent-H truncated %s",work);
                                    break;

                               default:
                                    message(1,"+Sent-H %s",work);
                                    break;
                        }

                        break;
            }
        }

        return (0);
}

void BeginHYDRATransfer( void )
{
  int err=0;

  N_ND->TransferringFile = TRUE;

  GrabSerial();
        cur_speed = com_speed = 0U;
        parity = false;
        noinit = nocarrier = dropdtr = false;
        nobell = false;
        mailer = false;
        hdxsession = nooriginator = false;
        hydra_txwindow = hydra_rxwindow = 0L;
        flowflags = 2;
        dcdmask = 0x80;
        noresume = nostamp = false;
        hydra_options = 0x0L;
        result = opuslog = NULL;
        download = "";
        single_file[0] = '\0';
        single_done = false;
        logfp = NULL;
        loglevel = 2;
        opustask = -1;
        didsome = false;

        if (N_ND->NodeDevice.NullModemCable) nocarrier=TRUE;

        strcpy(device,N_ND->NodeDevice.SerialDevice);
        port=N_ND->NodeDevice.SerialUnit;
        com_speed=N_ND->NodeDevice.SerialBaud;
        if (atoi(N_ND->ConnectBaud)>0) cur_speed=atoi(N_ND->ConnectBaud);


//        signal(SIGINT,SIG_IGN);                            /* disable Ctrl-C */


        download=strdup(DL_Path);

        init();

        if (OpenStatWindow(Screen)==0)
        {
          StatusOpen=TRUE;
        }

        hydra_init(hydra_options);

        if (UL_Files)
        {
          err = batch_hydra("",NULL);
        }
        if (!err) // continue transferring in one direction if there's anything left to do
        {
          hydra(NULL,NULL);
        }
        hydra_deinit();

        if (StatusOpen)
        {
          CloseStatWindow();
        }

  ReleaseSerial();

  // flush the serial buffer, and keep flushing until no more
  // data is received.
  
  Delay(10);
  
  while (SerQueryData())
  { 
    SerClear();                      
    
    Delay(50);
  }

  N_ND->TransferringFile = FALSE;
  DOOR_UpdateNodeStatus(UPD_CPSBAUD);
}


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

void DoorMain( void )
{

  long loop;

  struct FileBuffer *NewFileNode;
  char tmpfilename[1024];

  BPTR FL;
  struct FileInfoBlock FB;

  BOOL Flag_AddTaggedFiles=FALSE;
  BOOL Flag_ProcessSystemFiles=TRUE;
  BOOL Flag_ProcessWarezFiles=TRUE;
  struct TaggedFile *Tag;

  if (UL_FileList=HBBS_CreateList())
  {
    if (DL_FileList=HBBS_CreateList())
    {
      if (SKIP_FileList=HBBS_CreateList())
      {

        for(loop=2;loop<gargc;loop++) // if there are any parameters then display them
        {
          switch(toupper(gargv[loop][0]))
          {
            case 'S' :
              Flag_CheckConfPaths=TRUE;
              break;

            case 'C' :
              Flag_TakeCredits=TRUE;
              break;

            case 'D' :
              if (!DL_PathOK)
              {
                DL_Path=&gargv[loop][2];
                if (PathOK(DL_Path)) DL_PathOK=TRUE;
              }
              break;

            case 'W' :  // the working directory for files
              if (!UL_PathOK)
              {
                UL_Path=&gargv[loop][2];
                if (PathOK(UL_Path)) UL_PathOK=TRUE;
              }
              break;

            case 'U' :
              // does the file exist ?

              strcpy(tmpfilename,&gargv[loop][2]);
              if (!(FL=Lock(tmpfilename,SHARED_LOCK)))
              {
                // if the file does not exist then add the working directory to the name and try again.

                if (UL_PathOK)
                {
                  strcpy(tmpfilename,UL_Path);
                  strcat(tmpfilename,&gargv[loop][2]); // same as argv[loop]+2, start at 3rd char..
                  FL=Lock(tmpfilename,SHARED_LOCK);
                }
              }

              if (FL) // file exists.
              {
                if (Examine(FL,&FB)) // get the size of the file
                {
                  if (NewFileNode=(struct FileBuffer*)HBBS_CreateNode(tmpfilename,sizeof(struct FileBuffer)))
                  {
                     // all othe rparams are set to null by HBBS_CreateNode()
                    NewFileNode->StartOffset=0;
                    NewFileNode->ActualSize=FB.fib_Size;
                    AddTail(UL_FileList,(struct Node *)NewFileNode);
                    UL_Files++;
                  }

                }
                UnLock(FL);
              }

              break;
            default:
              if (stricmp(gargv[loop],"TAGS")==0)
              {
                Flag_AddTaggedFiles=TRUE;
              }

              if (stricmp(gargv[loop],"NOSYSTEM")==0)
              {
                Flag_ProcessSystemFiles=FALSE;
              }

              if (stricmp(gargv[loop],"NOWAREZ")==0)
              {
                Flag_ProcessWarezFiles=FALSE;
              }

              break;

          }
        }

        if (Flag_AddTaggedFiles)
        {
          // has the user got any tagged files ?
          if (N_ND->TaggedFiles)
          {
            for (Tag=(struct TaggedFile *)N_ND->TaggedFileList->lh_Head;Tag->node.ln_Succ;Tag=(struct TaggedFile *)Tag->node.ln_Succ)
            {
              if ( ((Tag->WarezFile==TRUE) && (Flag_ProcessWarezFiles)) ||
                   ((Tag->WarezFile==FALSE) && (Flag_ProcessSystemFiles))  )
              {
                if (NewFileNode=(struct FileBuffer*)HBBS_CreateNode(Tag->node.ln_Name,sizeof(struct FileBuffer)))
                {
                  NewFileNode->ActualSize=Tag->FileSize;
                  NewFileNode->CPS=0;
                  NewFileNode->WarezFile=Tag->WarezFile;
                  NewFileNode->ConferenceNum=Tag->ConferenceNum;
                  AddTail(UL_FileList,(struct Node*)NewFileNode);
                  UL_Files++;
                }
              }
            }
          }
        }

        if (UL_Files || DL_PathOK)
        {

          BeginHYDRATransfer();
        }

        // if any uploaded files were skipped then save the filenames to a file.
        // otherwise delete the file if it already exists.

        strcpy(tmpfilename,N_ND->NodeLocation);
        strcat(tmpfilename,"Work/SkippedFiles.TXT");

        if (SKIP_Files)
        {
          HBBS_SaveFile(tmpfilename,SKIP_FileList);
        }
        else
        {
          if (PathOK(tmpfilename)) DeleteFile(tmpfilename);
        }
        FreeStrList(SKIP_FileList);
      }
      CheckDLList();
      FreeStrList(DL_FileList);
    }
    CheckULList();
    FreeStrList(UL_FileList);


    HBBS_SaveUserData(N_ND->User->NormalData);
    // just in case the user decided to loose carrier! :-)
    // or if the bbs crashed (heaven forbid..) before the data was saved normally.
  }

  if (MsgHistory)
  {
    FreeStrList(MsgHistory);
  }
}

int main(int argc,char **argv)
{
  gargc=argc;
  gargv=argv;

  pri=1000;

  if (sscanf(argv[1],"%d",&N_NodeNum)==0)
  {
    printf("Invalid/No Paramaters for door!\n");
    exit (20);
  }
  inithydra("HYDRA X-Fer!");

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

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