/* **** HBBS Door Code****************************************************** */

/*
  XPR Door
  ========

*/

/* **** 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 <libraries/asyncio.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 <clib/asyncio_protos.h>

#include "xpr_gui.h"

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <time.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>
#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: XPR "RELEASE_STR;

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


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

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

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;

struct TextFont *HBBSFont=NULL;

UBYTE ZModemCancel[] =
{
    24,24,24,24,24,24,24,24,24,24,
     8, 8, 8, 8, 8, 8, 8, 8, 8, 8
};

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;

  LONG CPS;
  BOOL WarezFile;
  ULONG ConferenceNum;
};

#include "xproto.h"  // XPR protocol stuff...

BOOL TransferAborted = FALSE;
ULONG TransferAbortCount = 0;
LONG TransferAbortState = 0;

struct timerequest *treq;
struct MsgPort *tport;
char timeropen;

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;

struct FileBuffer *CurrentFile=NULL;
LONG CurrentFileNum=1;

#define T_SEND 1
#define T_RECEIVE 2

ULONG TransferType=0;

#define LEN_ASYNCBUFSIZE 65535

UBYTE *xprlibname=NULL;
UBYTE *xprlibopts=NULL;
                    
struct Library *XProtocolBase=NULL;
struct XPR_IO *xio=NULL;

struct IOExtSer *xpr_serio=NULL;
struct MsgPort *NewSerPort=NULL;

double CPSTotal;
double CPSAmount;



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


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

static VOID cleanup(ULONG num)
{
  if (asynciobase)
  {
    CloseLibrary(asynciobase);
  }
  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);
  }

  if(!(asynciobase = OpenLibrary("asyncio.library",0)))
  {
    cleanup(5);
  }

  SetProgramName(name);
}

// Timer Routines..

long opentimer(void)
{
  if (!(treq=AllocVec(sizeof(struct timerequest),MEMF_CLEAR|MEMF_PUBLIC)))
  {
    return(-1);
  }
  if(!(tport=CreateMsgPort()))
  {
    FreeVec(treq);
    return(-1);
  }
  treq->tr_node.io_Message.mn_ReplyPort=tport;
  if (OpenDevice("timer.device",UNIT_VBLANK,(struct IORequest*)treq,0))
  {
    DeleteMsgPort(tport);
    FreeVec(treq);
    return(-1);
  }
  timeropen=1;
  return(0);
}

static void closetimer(void)
{
  if(treq)
  {
    if(timeropen) CloseDevice((struct IORequest*)treq);
    if(tport) DeleteMsgPort(tport);
    FreeVec(treq);
  }
}

void qtimer(LONG micros)
{

  long secs=0;
  if (micros > 1000000)
  {
    secs = micros / 1000000;
    micros = micros % 1000000;
  }
  treq->tr_time.tv_micro=micros;
  treq->tr_time.tv_secs=secs;

/*
  treq->tr_time.tv_micro=micros % 1000000;
  treq->tr_time.tv_secs=micros / 1000000;
*/

  treq->tr_node.io_Command=TR_ADDREQUEST;
  SendIO((struct IORequest*)treq);
}

void qtimer2( LONG Secs, LONG Micros)
{
  treq->tr_time.tv_micro=Micros;
  treq->tr_time.tv_secs=Secs;
  treq->tr_node.io_Command=TR_ADDREQUEST;
  SendIO((struct IORequest*)treq);
}

BOOL GrabSerial( void )
{
  //  AbortSerRead(); 
  
  if (xpr_serio = AllocVec(sizeof(struct IOExtSer),MEMF_PUBLIC))
  {
    CopyMem(N_ND->SerRead, xpr_serio, sizeof(struct IOExtSer));
    
    if(NewSerPort=CreatePort(0,0))
    { 
      xpr_serio -> IOSer.io_Message.mn_ReplyPort = NewSerPort;

      return(TRUE);
    }
    FreeVec(xpr_serio);
  }

  return(FALSE);
            
}

void ReleaseSerial( void )
{
  if (NewSerPort) DeletePort(NewSerPort);
  if (xpr_serio) FreeVec(xpr_serio);
}

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;

        N_ND->User->FilesDownloaded++;
        N_ND->User->BytesDownloaded+=node->ActualSize;

        if (node->CPS > N_ND->User->CallData->BestCPSDown)
        {
          N_ND->User->CallData->BestCPSDown=node->CPS;
          N_ND->User->NormalData->BestCPSDown=node->CPS;
        }
      }
      if (Tag=HBBS_FindTag(node->node.ln_Name,TRUE))
      {
        if (Flag_TakeCredits && node->BlocksTransferred && N_ND->User->Valid && node->ReadFile)
        {
        
          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 from the list.

        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);

      // rename the file by adding @<userid> to the end of the file name

      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 OpenTransferWindow( void )
{
  struct Screen *Scr=NULL;

  if (!Flag_WindowOpen)
  {
    if (HBBSFont=OpenDiskFont( &HBBS8066 ) )
    {
      if (N_ND->ConOK && N_ND->NodeSettings.UseOwnScreen)
      {
        sprintf(outstr,"NodeWatch_%d",N_ND->NodeNum);
        Scr = LockPubScreen(outstr);
      }
      if (!Scr)
      {
        Scr = LockPubScreen( "CtrlScrn" );
      }
      if (Scr)
      {
        if (OpenWindowXPRWin(Scr)==0)
        {
          // Opened OK
          Flag_WindowOpen=TRUE;
        }
        UnlockPubScreen( NULL, Scr);
      }
    }
  }
}
                                   
void CloseTransferWindow( void )
{
  if (HBBSFont) CloseFont(HBBSFont);
  if (Flag_WindowOpen)
  {
    CloseWindowXPRWin();
    Flag_WindowOpen=FALSE;
  }
}

LONG CheckAbort( void )
{
  struct IntuiMessage *im=0;
  ULONG class;
  UWORD code;

  LONG Result=0;
  
  /* Return error if carrier is lost. */
  
  if (CarrierLost()) Result=-1;

	/* Process all incoming messages. */

  if (Flag_WindowOpen)
  {
    while (im=GT_GetIMsg(XPRWin->UserPort))
    {
      class=im->Class;
      code=im->Code;
      GT_ReplyIMsg(im);

      switch (class)
      {
        case IDCMP_CLOSEWINDOW :
          TransferAborted = TRUE;
          Result=-1;
          break;

        case IDCMP_REFRESHWINDOW :
        case IDCMP_NEWSIZE :
          GT_BeginRefresh(XPRWin);
          GT_EndRefresh(XPRWin, TRUE);
          break;
      }
    }
  }

  return(Result);
} 
  
LONG __saveds __asm xpr_chk4abort(void)
{
	LONG Result;

	if(TransferAbortState != 0)
	{    
		Result = TransferAbortState;

//		TransferAbortState = 0;
	}
	else
		Result = CheckAbort();

	return(Result);
}


LONG __saveds __asm xpr_ffirst(register __a0 STRPTR Buffer,register __a1 STRPTR Pattern)
{

  if (UL_Files)
  {
    CurrentFile=(struct FileBuffer*)UL_FileList->lh_Head;
    CurrentFileNum=1;
    strcpy(Buffer,CurrentFile->node.ln_Name);
    return((LONG)1);
  }

  return((LONG)0);

}

LONG __saveds __asm xpr_fnext(register __d0 LONG OldState,register __a0 STRPTR Buffer,register __a1 STRPTR Pattern)
{
  if (CurrentFileNum<UL_Files)
  {
    CurrentFile=(struct FileBuffer*)CurrentFile->node.ln_Succ;
    CurrentFileNum++;
    strcpy(Buffer,CurrentFile->node.ln_Name);
    return((LONG)1);
  }

  return((LONG)0);
}

LONG __saveds __asm xpr_finfo(register __a0 STRPTR FileName,register __d0 LONG InfoType)
{
  struct FileInfoBlock FB;
  BPTR FL;
  LONG result=0;

  switch (InfoType)
  {
    case 1:  // file size

      if (TransferType==T_RECEIVE && DL_PathOK)
      {
        strcpy(outstr,DL_Path);
        strcat(outstr,FileName);
      }
      else
      {
        strcpy(outstr,FileName);
      }

      if(FL=Lock(outstr,SHARED_LOCK))
      {
        if (Examine(FL,&FB))
        {
          result=FB.fib_Size;
        }
        UnLock(FL);
      }
      return(result);
      break;

    case 2:
      return((LONG)1);   // reutn 1 for binary, 2 for ascii, we always do binary..!
      break;
  }
}

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;                  
            sprintf(outstr,"File: %s resumed!",FileName);
            HBBS_AddToCallersLog(outstr);
          }
        }
      }
    }
  }

  return(SkipReason);
}

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++;
  }
}                                

LONG __saveds __asm xpr_fopen(register __a0 STRPTR FileName,register __a1 STRPTR AccessMode)
{
  BPTR FL;

  UBYTE tmpfilename[1024];
  ULONG SkipReason;
  
  if ( toupper(AccessMode[0]) =='R')
  {
    if (TransferType==T_RECEIVE)
    {

      if (SkipReason = FileInPaths(FileName))  // the file is already present on the system
      {
        AddSkip(FileName,SkipReason);

        return((LONG)0); // causes the file to be skipped
      }
      
      if (!DL_PathOK)  // We have NO valid download path for the user system to place the files in
      {
        return((LONG)0); // causes the file to be skipped
      }

      CurrentFile=NULL;
      
      // we must set this AFTER we call HBBS_OkToUpload() or it will check the node we're uploading on!
      
      strNcpy(N_ND->TransferReceiveName,FileName,LEN_XFERFILENAME);
      
      strcpy(tmpfilename,DL_Path);
      AddPart(tmpfilename,FileName,1024);

      if (CurrentFile=(struct FileBuffer*)HBBS_CreateNode(tmpfilename,sizeof(struct FileBuffer)))
      {
        // we set this flag if for some reason the protcol is just testing for existance of files...
        // we have to allocate the buffer, and when fclose is called we free it.
        // or if the Open() below fails we free it.

        CurrentFile->Testing=TRUE;
      }
    }
  }
  else
  {
    // the user is UPLOADING to the BBS, create the file in the current download path.

    if (DL_PathOK && !HBBS_FindNode(SKIP_FileList,FileName))
    {
      strcpy(tmpfilename,DL_Path);
      strcat(tmpfilename,FileName);

      if (CurrentFile=(struct FileBuffer*)HBBS_CreateNode(tmpfilename,sizeof(struct FileBuffer)))
      {
        CurrentFile->CPS=0;
        AddTail(DL_FileList,(struct Node*)CurrentFile);
        DL_Files++;

        CPSTotal=0;
        CPSAmount=0;

      }
    }
    else return(NULL);
  }

  if (!CurrentFile) // *R* needed ?
  {
    return(0);
  }


  switch(AccessMode[0])
  {
    case 'r':
      if (CurrentFile)
      {
        if (AccessMode[1]=='+')
        {
          CurrentFile->UseAsync=FALSE;
          CurrentFile->DOSFH=Open(CurrentFile->node.ln_Name,MODE_READWRITE); // *C* should this be read ? rather than readwrite ?
        }
        else
        {
          CurrentFile->UseAsync=TRUE;
          CurrentFile->ASFH=OpenAsync(CurrentFile->node.ln_Name,MODE_READ,LEN_ASYNCBUFSIZE);
        }
      }
      break;
    case 'w':

      if (AccessMode[1]=='+')  // truncate file (well, delete it anyway)
      {

        // if the file exists, delete it.
        if (FL = Lock(CurrentFile->node.ln_Name,ACCESS_WRITE))
        {
          UnLock(FL);
          DeleteFile(CurrentFile->node.ln_Name);
        }
        CurrentFile->UseAsync=FALSE;
        CurrentFile->DOSFH=Open(CurrentFile->node.ln_Name,MODE_READWRITE);
      }
      else
      {
        CurrentFile->UseAsync=TRUE;
        CurrentFile->ASFH=OpenAsync(CurrentFile->node.ln_Name,MODE_WRITE,LEN_ASYNCBUFSIZE);
      }
      break;
    case 'a':
      // the user is resuming an UPLOAD to the BBS, add to the file in the current download path.

      // open file, seek to end, create new file if none exists.

      if (FL=Lock(CurrentFile->node.ln_Name,ACCESS_WRITE))
      {
        UnLock(FL);

        // old file exists.

        CurrentFile->UseAsync=FALSE;
        if(CurrentFile->DOSFH=Open(CurrentFile->node.ln_Name,MODE_READWRITE))
        {
          if (Seek(CurrentFile->DOSFH,0,OFFSET_END) == -1) // error seeking ?
          {
            Close(CurrentFile->DOSFH);
            CurrentFile->DOSFH = NULL;
            CurrentFile->Testing=TRUE; // so that everything gets freed up.
          }
        }
      }
      else // old file does not exist, create new file
      {
        CurrentFile->UseAsync=TRUE;
        CurrentFile->ASFH=OpenAsync(CurrentFile->node.ln_Name,MODE_WRITE,LEN_ASYNCBUFSIZE);
      }
      break;
  }

  if (CurrentFile)
  {
    if ((CurrentFile->UseAsync==FALSE && CurrentFile->DOSFH == NULL) || (CurrentFile->UseAsync && CurrentFile->ASFH == NULL))
    {
      if (CurrentFile->Testing)
      {
        HBBS_FreeNode((struct Node*)CurrentFile,FALSE);
      }
      CurrentFile=NULL;
    }
  }
  return((LONG)CurrentFile);
}

LONG __saveds __asm xpr_fclose(register __a0 struct FileBuffer *FileBuf)
{
  N_ND->TransferReceiveName[0]=0;
  if (FileBuf)
  {
    if (FileBuf->UseAsync==FALSE && FileBuf->DOSFH)
    {
      Close(FileBuf->DOSFH);
    }
    if (FileBuf->UseAsync && FileBuf->ASFH)
    {
      CloseAsync(FileBuf->ASFH);
    }

    if (FileBuf->Testing)
    {
      HBBS_FreeNode((struct Node*)FileBuf,FALSE);
    }
  }

  // kick off BG file checking here... *C*

  return((LONG)0);
}

LONG __saveds __asm xpr_fread(register __a0 APTR Buffer,register __d0 LONG Size,register __d1 long Count,register __a1 struct FileBuffer *FileBuf)
{
  LONG retval;

  // check params are ok..
  if (!Size)
  {
    return(0);
  }

  if (FileBuf->UseAsync)
  {
    retval=ReadAsync(FileBuf->ASFH,Buffer,Size*Count);
  }
  else
  {
    retval=Read(FileBuf->DOSFH,Buffer,Size*Count);
  }

  if (retval) FileBuf->ReadFile=TRUE; // so we can determine if a file was skipped if it was
                                      // already present on the user's machine (not the sysop's)

  return(retval);
}

LONG __saveds __asm xpr_fwrite(register __a0 APTR Buffer,register __d0 LONG Size,register __d1 LONG Count,register __a1 struct FileBuffer *FileBuf)
{
  // check params are ok..

  if (!Size)
  {
    return(0);
  }

  if (FileBuf->UseAsync)
  {
    return((LONG)WriteAsync(FileBuf->ASFH,Buffer,Size*Count));
  }
  else
  {
    return((LONG)Write(FileBuf->DOSFH,Buffer,Size*Count));
  }
}

LONG __saveds __asm xpr_fseek(register __a0 struct FileBuffer *FileBuf,register __d0 LONG Offset,register __d1 LONG Origin)
{
  LONG Mode=-1;
  LONG tmpval;

  // check params are ok..
  if (FileBuf==NULL) return((LONG)-1);

  if ((FileBuf->UseAsync && !FileBuf->ASFH) || (!FileBuf->UseAsync && !FileBuf->DOSFH)) return((LONG)-1);

  switch(Origin)
  {
    case 0: Mode=OFFSET_BEGINNING; break;
    case 1: Mode=OFFSET_CURRENT; break;
    case 2: Mode=OFFSET_END; break;
    default: return((LONG)-1);
  }
  if (FileBuf->UseAsync)
  {
    tmpval=SeekAsync(FileBuf->ASFH,Offset,Mode);
  }
  else
  {
    tmpval=Seek(FileBuf->DOSFH,Offset,Mode);
  }
  return((tmpval==-1) ? (LONG)-1 : (LONG)0);

}

LONG __saveds __asm xpr_gets(register __a0 STRPTR Prompt,register __a1 STRPTR Buffer)
{
  // Do nothing..  This is a BBS!
  return((LONG)0);
}

LONG __saveds __asm xpr_unlink(register __a0 STRPTR FileName)
{
  // unlink is another name for delete...

  return(DeleteFile(FileName));
}

LONG __saveds __asm xpr_swrite(register __a0 APTR Buffer,register __d0 LONG Size)
{
  if (Size==0)
  {
    return((LONG)0);
  }

  xpr_serio->IOSer.io_Length=Size;
  xpr_serio->IOSer.io_Data=Buffer;
  xpr_serio->IOSer.io_Command=CMD_WRITE;

  DoIO((struct IORequest *)xpr_serio);
  return((LONG)xpr_serio->IOSer.io_Error);
}

void CancelZModem( void )
{
  // cancel the transfer!  *C* only send if using zmodem...
  DOOR_SysopText("\r\nSending ZModem Cancel request\r\n");

  xpr_serio->IOSer.io_Command=SDCMD_BREAK;
  DoIO((struct IORequest *)xpr_serio);

  xpr_swrite(ZModemCancel,20);
}

// Complex xpr_sread(), handles things better...

LONG __saveds __asm xpr_sread(register __a0 APTR Buffer,register __d0 LONG Size,register __d1 ULONG Timeout)
{
  ULONG WaitSigs,ReturnedSigs;
  LONG AbortResult;
  
  LONG BytesWaiting,AmountToRead,BytesRead;

  if (Size==0)
  {
    return((LONG)0);
  }

  // Carrier Lost ?

  if (CarrierLost()) return((LONG)-1);

  // See how many bytes there are

  xpr_serio->IOSer.io_Command=SDCMD_QUERY;
  DoIO((struct IORequest *)xpr_serio);
  BytesWaiting=xpr_serio->IOSer.io_Actual;

  if(!Timeout || BytesWaiting >= Size)
  {
    if(!BytesWaiting)
    {
      // no data to be read..
      return(0);
    }
    else
    {
      // some data is waiting
      if(BytesWaiting>Size) // is more data waiting than we need ?
      {
        AmountToRead=Size;  // yup, so just read the amount that we want
      }
      else
      {
        AmountToRead=BytesWaiting; // nope, so read all that there is..
      }
      xpr_serio->IOSer.io_Command=CMD_READ;          
      xpr_serio->IOSer.io_Data=Buffer;
      xpr_serio->IOSer.io_Length=AmountToRead;
      SetSignal(0,1L<<NewSerPort->mp_SigBit);
      DoIO((struct IORequest *)xpr_serio);

      BytesRead=xpr_serio->IOSer.io_Actual;

      // return amount of bytes read
      return((LONG)BytesRead);
    }
  }
  else
  {
    BytesRead=Size;
    xpr_serio->IOSer.io_Command=CMD_READ;
    xpr_serio->IOSer.io_Data=Buffer;
    xpr_serio->IOSer.io_Length=Size;
    SendIO((struct IORequest *)xpr_serio);


    WaitSigs=(1L<<tport->mp_SigBit) | (1L<<NewSerPort->mp_SigBit);
    if (Flag_WindowOpen) WaitSigs|=(1L << XPRWin->UserPort->mp_SigBit);

    SetSignal(0L,WaitSigs);
    qtimer(Timeout);

    while (TRUE)
    {
      ReturnedSigs=Wait(WaitSigs);

      if (ReturnedSigs & (1L << XPRWin->UserPort->mp_SigBit))
      {
        AbortResult = CheckAbort();
        
        if (AbortResult !=0)
        {
          TransferAbortState = AbortResult;
                 
          // abort everything, tidy up and return -1
                                                                           
          if (!CheckIO((struct IORequest *)xpr_serio))
          {
            AbortIO((struct IORequest *)xpr_serio);
          }
          WaitIO((struct IORequest *)xpr_serio);

          if (!CheckIO((struct IORequest *)treq))
          {
            AbortIO((struct IORequest *)treq);
          }
          WaitIO((struct IORequest *)treq);
          
          if (AbortResult <0)
          {
//            if (TransferAbortCount++)
//            {
              CancelZModem();
//            }
            return(AbortResult);
          }
          else
          {
            if (xpr_serio->IOSer.io_Actual)
            {
              BytesRead=xpr_serio->IOSer.io_Actual;
              // return the amount of bytes read

              return((LONG)BytesRead);
            }
            else
            {
              xpr_serio->IOSer.io_Command=SDCMD_QUERY;
              DoIO((struct IORequest *)xpr_serio);


              BytesWaiting=xpr_serio->IOSer.io_Actual;

              if(BytesWaiting>Size) // is more data waiting than we need ?
              {
                AmountToRead=Size;  // yup, so just read the amount that we want
              }
                else
              {
                AmountToRead=BytesWaiting; // nope, so read all that there is..
              }
              BytesRead=AmountToRead;


              if (AmountToRead)
              {
                xpr_serio->IOSer.io_Command=CMD_READ;
                xpr_serio->IOSer.io_Data=Buffer;
                xpr_serio->IOSer.io_Length=AmountToRead;
                SetSignal(0,1L<<NewSerPort->mp_SigBit);
                DoIO((struct IORequest *)xpr_serio);

                BytesRead = xpr_serio->IOSer.io_Actual;
              }
              else
              {
                if (CarrierLost()) return((LONG)-1);
              }
              // return amount of bytes read
              return((LONG)BytesRead);

            } 
            
          }
        }
      }

      if (ReturnedSigs & (1L<<NewSerPort->mp_SigBit))
      {

        // and cleanup the timer request
        if (!CheckIO((struct IORequest *)treq))
        {
          AbortIO((struct IORequest *)treq);
        }

        if (!CheckIO((struct IORequest *)xpr_serio))
        {
          AbortIO((struct IORequest *)xpr_serio);
        }

        // Wait for the IO to complete..
        if (WaitIO((struct IORequest *)xpr_serio))
        {
          BytesRead=xpr_serio->IOSer.io_Actual;
        }

        WaitIO((struct IORequest *)treq);

        // return the amount of bytes read

        return((LONG)BytesRead);
      }

      if (ReturnedSigs&(1L<<tport->mp_SigBit))
      {

        if (!CheckIO((struct IORequest *)xpr_serio))
        {
          AbortIO((struct IORequest *)xpr_serio);
        }
        WaitIO((struct IORequest *)xpr_serio);

        WaitIO((struct IORequest *)treq);

        if (xpr_serio->IOSer.io_Actual)
        {
          return((LONG)xpr_serio->IOSer.io_Actual);
        }
        else
        {
          xpr_serio->IOSer.io_Command=SDCMD_QUERY;
          DoIO((struct IORequest *)xpr_serio);


          BytesWaiting=xpr_serio->IOSer.io_Actual;

          if(BytesWaiting>Size) // is more data waiting than we need ?
          {
            AmountToRead=Size;  // yup, so just read the amount that we want
          }
            else
          {
            AmountToRead=BytesWaiting; // nope, so read all that there is..
          }
          BytesRead=AmountToRead;


          if (AmountToRead)
          {
            xpr_serio->IOSer.io_Command=CMD_READ;
            xpr_serio->IOSer.io_Data=Buffer;
            xpr_serio->IOSer.io_Length=AmountToRead;
            SetSignal(0,1L<<NewSerPort->mp_SigBit);
            DoIO((struct IORequest *)xpr_serio);

            BytesRead = xpr_serio->IOSer.io_Actual;
          }
          else
          {
            if (CarrierLost()) return((LONG)-1);
          }
          // return amount of bytes read
          return((LONG)BytesRead);
        }
      }
    }
  }

  // Should Never Get Here, if it does a false signal arrived!
  // DOOR_SysopText("XPR Error - FALSE SIGNAL\r\n");
  // return(0);
}

LONG __saveds __asm xpr_sflush(void)
{
  xpr_serio->IOSer.io_Command=CMD_FLUSH;
  DoIO((struct IORequest *)xpr_serio);

  return((LONG)xpr_serio->IOSer.io_Error);
}

#define ST_PARTYON  (1 << 0)
#define ST_PARTYODD (1 << 1)
#define ST_7WIRE  (1 << 2)
#define ST_QBREAK (1 << 3)
#define ST_RADBOOGIE  (1 << 4)
#define ST_SHARED (1 << 5)
#define ST_EOFMODE  (1 << 6)
#define ST_XDISABLED  (1 << 7)
#define ST_PARTYMARKON  (1 << 8)
#define ST_PARTYMARK  (1 << 9)
#define ST_2BITS  (1 << 10)
#define ST_READ7  (1 << 11)
#define ST_WRITE7 (1 << 12)

LONG __saveds __asm xpr_setserial(register __d0 LONG Status)
{
  STATIC LONG XprBauds[12] =
  {
    110,
    300,
    1200,
    2400,
    4800,
    9600,
    19200,
    31250,
    38400,
    57600,
    76800,
    115200
  };

  LONG Return,i;

  xpr_serio -> IOSer . io_Command = SDCMD_QUERY;
  DoIO((struct IORequest *)xpr_serio);

  Return = xpr_serio -> io_SerFlags & 0xFF;

  if(xpr_serio -> io_ExtFlags & SEXTF_MSPON)
    Return |= ST_PARTYMARKON;

  if(xpr_serio -> io_ExtFlags & SEXTF_MARK)
    Return |= ST_PARTYMARK;

  if(xpr_serio -> io_StopBits == 2)
    Return |= ST_2BITS;

  if(xpr_serio -> io_ReadLen == 7)
    Return |= ST_READ7;

  if(xpr_serio -> io_WriteLen == 7)
    Return |= ST_WRITE7;

  for(i = 0 ; i < 12 ; i++)
  {
    if(xpr_serio -> io_Baud == XprBauds[i])
    {
      Return |= (i << 16);   // uses 16MSB's of the LONGWORD for the speed ?

      break;
    }
  }

  if(Status != -1)
  {

    xpr_serio -> IOSer . io_Command = SDCMD_SETPARAMS;

    xpr_serio -> io_SerFlags    = Status & 0xFF;
    xpr_serio -> io_ExtFlags    = 0;

    if(Status & ST_PARTYMARKON)
      xpr_serio -> io_ExtFlags |= SEXTF_MSPON;

    if(Status & ST_PARTYMARK)
      xpr_serio -> io_ExtFlags |= SEXTF_MARK;

    if(Status & ST_2BITS)
      xpr_serio -> io_StopBits = 2;
    else
      xpr_serio -> io_StopBits = 1;

    if(Status & ST_READ7)
      xpr_serio -> io_ReadLen = 7;
    else
      xpr_serio -> io_ReadLen = 8;

    if(Status & ST_WRITE7)
      xpr_serio -> io_WriteLen = 7;
    else
      xpr_serio -> io_WriteLen = 8;

    DoIO((struct IORequest *)xpr_serio);
  }

  return(Return);

}

LONG __saveds __asm xpr_displayupdate(register __a0 struct XPR_UPDATE *xu)
{
  long ud=xu->xpru_updatemask;
  LONG tempnum;


  // ** we shouldn't need this bit, but xprftp does not use XPRU_FILESIZE
  if (ud==-1)
  {
    if (CurrentFile)
    {
      CurrentFile->ActualSize=xu->xpru_filesize;
    }
  }

  if(ud&XPRU_BYTES)
  {
    if (CurrentFile)
    {
      CurrentFile->BytesTransferred=xu->xpru_bytes;

      if (Flag_WindowOpen)
      {
        GT_SetGadgetAttrs(XPRWinGadgets[XPRWin_Transferred],XPRWin,NULL,GTNM_Number,(LONG)xu->xpru_bytes,TAG_DONE);

        if (CurrentFile->ActualSize)
        {
          tempnum=(LONG)((CurrentFile->BytesTransferred * 100) / CurrentFile->ActualSize);
          GT_SetGadgetAttrs(XPRWinGadgets[XPRWin_Percent],XPRWin,NULL,GTNM_Number,(LONG)tempnum,TAG_DONE);

          tempnum=CurrentFile->ActualSize - CurrentFile->BytesTransferred;
          GT_SetGadgetAttrs(XPRWinGadgets[XPRWin_Remaining],XPRWin,NULL,GTNM_Number,(LONG)tempnum,TAG_DONE);
        }
      }
    }

  }

  if(ud&XPRU_PROTOCOL)
  {
    if (Flag_WindowOpen)
    {
      GT_SetGadgetAttrs(XPRWinGadgets[XPRWin_Library],XPRWin,NULL,GTTX_Text,xu->xpru_protocol,TAG_DONE);
    }
  }
  if(ud&XPRU_FILENAME)
  {
    if (Flag_WindowOpen)
    {
      GT_SetGadgetAttrs(XPRWinGadgets[XPRWin_File],XPRWin,NULL,GTTX_Text,xu->xpru_filename,TAG_DONE);
    }
  }
  if(ud&XPRU_FILESIZE)
  {
    if (Flag_WindowOpen)
    {
      GT_SetGadgetAttrs(XPRWinGadgets[XPRWin_Size],XPRWin,NULL,GTNM_Number,(LONG)xu->xpru_filesize,TAG_DONE);
    }

    if (CurrentFile)
    {
      CurrentFile->ActualSize=xu->xpru_filesize;
    }

  }
  if(ud&XPRU_MSG)
  {
    if (Flag_WindowOpen)
    {                                                                  
      GT_SetGadgetAttrs(XPRWinGadgets[XPRWin_Message],XPRWin,NULL,GTTX_Text,xu->xpru_msg,TAG_DONE);
    }

  }
  if(ud&XPRU_BLOCKS)
  {
    if (CurrentFile)
    {
      CurrentFile->BlocksTransferred=xu->xpru_blocks;
    }
    if (Flag_WindowOpen)
    {
      GT_SetGadgetAttrs(XPRWinGadgets[XPRWin_Blocks],XPRWin,NULL,GTNM_Number,(LONG)xu->xpru_blocks,TAG_DONE);
    }
  }
  if(ud&XPRU_ERRORMSG)
  {
    if (Flag_WindowOpen)
    {
      GT_SetGadgetAttrs(XPRWinGadgets[XPRWin_Message],XPRWin,NULL,GTTX_Text,xu->xpru_errormsg,TAG_DONE);
    }
  }

  if(ud&XPRU_DATARATE)
  {
    if (Flag_WindowOpen)
    {
      GT_SetGadgetAttrs(XPRWinGadgets[XPRWin_CPS],XPRWin,NULL,GTNM_Number,(LONG)xu->xpru_datarate,TAG_DONE);
    }

    if (!N_ND->NodeSettings.Iconified)
    {
      sprintf(N_ND->LastCPS,"%ld",xu->xpru_datarate);
      DOOR_UpdateNodeStatus(UPD_CPSBAUD);
    }

    CPSTotal+=xu->xpru_datarate;
    CPSAmount++;

    if (CurrentFile) CurrentFile->CPS=CPSTotal / CPSAmount;

  }

  if(ud&XPRU_BLOCKSIZE)
  {
    if (Flag_WindowOpen)
    {
      GT_SetGadgetAttrs(XPRWinGadgets[XPRWin_BlockSize],XPRWin,NULL,GTNM_Number,(LONG)xu->xpru_blocksize,TAG_DONE);
    }

  }
  if(ud&XPRU_ERRORS)
  {                                                            
    if (Flag_WindowOpen)
    {
      GT_SetGadgetAttrs(XPRWinGadgets[XPRWin_Errors],XPRWin,NULL,GTNM_Number,(LONG)xu->xpru_errors,TAG_DONE);
    }
  }

  /*
  if(ud&XPRU_ELAPSEDTIME)printf("%d Elapsed Time\n",xu->xpru_elapsedtime);
  if(ud&XPRU_EXPECTTIME) printf("%d ExpectTime\n",xu->xpru_expecttime);
  if(ud&XPRU_BLOCKCHECK) printf("%d Block Check\n",xu->xpru_blockcheck);
  */

  return(0);
}

void BeginXPRTransfer( void )
{
  N_ND->TransferringFile=TRUE;

  if (XProtocolBase=OpenLibrary(xprlibname,0))
  {
    if (xio=(struct XPR_IO*)AllocVec(sizeof (struct XPR_IO),MEMF_CLEAR))
    {
      xio->xpr_swrite    = xpr_swrite;
      xio->xpr_sread     = xpr_sread;
      xio->xpr_sflush    = xpr_sflush;
      xio->xpr_setserial = xpr_setserial;

      xio->xpr_update    = xpr_displayupdate;
      xio->xpr_chkabort  = xpr_chk4abort;

      xio->xpr_ffirst    = xpr_ffirst;
      xio->xpr_fnext     = xpr_fnext;
      xio->xpr_finfo     = xpr_finfo;

      xio->xpr_fopen     = xpr_fopen;
      xio->xpr_fclose    = xpr_fclose;

      xio->xpr_fread     = xpr_fread;
      xio->xpr_fwrite    = xpr_fwrite;
      xio->xpr_fseek     = xpr_fseek;

      xio->xpr_gets      = xpr_gets;
      xio->xpr_unlink    = xpr_unlink;

      if (opentimer()==0)
      {
        xio->xpr_filename=xprlibopts;


        if (GrabSerial())
        {
          // From this point on you MUST NOT use any functions that write to the serial port
          // unless they are written in this program!

          OpenTransferWindow();

          if(XProtocolSetup(xio))
          {

            if (UL_Files) // if this is set we must be uploading files (i.e. the user is downloading.)
            {
              // init filename with finename of first the first..

              xio->xpr_filename=HBBS_ListName(UL_FileList,0);
              TransferType=T_SEND;
              XProtocolSend(xio);
            }
            else // nope, Downloading ?
            {
              if (DL_PathOK)
              {
                // Yup...

                // init filename with path to download files..

                xio->xpr_filename=DL_Path;

                TransferType=T_RECEIVE;
                XProtocolReceive(xio);
              }
            }
          }

          CloseTransferWindow();

          if (TransferAborted)
          {
            CancelZModem(); 
          }
          ReleaseSerial();
          
          // normal serial io can now be used again.
          
          if (TransferAborted)
          { 

            DOOR_WriteText("\r\n\r\n** Transfer was aborted! Flushing SerialIO ...");
            
            // flush the serial buffer, and keep flushing until no more
            // data is received.
            
            while (SerQueryData())
            { 
              SerClear();                      
              
              qtimer2(1,0);
              WaitIO((struct IORequest *)treq);
            }
            
            DOOR_WriteText("Done!\r\n\r\n");
          }

          closetimer();
        }
        else
        {
          DOOR_SysopText("Failed To Open Timer!\r\n");
        }
        
      }
    }
    CloseLibrary(XProtocolBase);
  }
  else
  {            
    DOOR_WriteText("Failed to open \"");
    DOOR_WriteText(xprlibname);
    DOOR_WriteText("\" Exiting!\r\n");
  }

  N_ND->TransferringFile=FALSE;
  DOOR_UpdateNodeStatus(UPD_CPSBAUD); // return display to BAUD rate

}


/* **** 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())
        {
        xprlibname=gargv[2];

        // add list of tagged files to our list of files to transfer..


        // then check the parameters to see if we have any other options..
        // remembering to miss out the arguments that we've already checked
        // (hence starting the loop from startparam)

        for(loop=3;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 'O' :
              xprlibopts=&gargv[loop][2];
              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 (PathOK(tmpfilename))
              {

                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->ActualSize=FB.fib_Size;
                      NewFileNode->CPS=0;

                      CPSTotal=0;
                      CPSAmount=0;

                      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 (PathOK(Tag->node.ln_Name))
                {
                  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)
        {
          CurrentFile=(struct FileBuffer*)UL_FileList->lh_Head;
          CurrentFileNum=1;
        }

        if ((xprlibname) && ((UL_Files || DL_PathOK)) )
        {

          BeginXPRTransfer();
        }

        // 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
        {
          // delete the file if it exists.
          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.
  }
}

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

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

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

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