
/*

   *C* take node of Msg->AccessLevel

   Mail_Read.HBBS
   ===============

   Door for reading mail.

*/

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

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

#include <HBBS/ANSI_Codes.h>
#include <HBBS/Defines.h>
#include <HBBS/types.h>
#include <HBBS/Access.h>
#include <HBBS/structures.h>
#include <HBBS/hbbscommon_protos.h>
#include <HBBS/hbbscommon_pragmas.h>
#include <HBBS/Hbbsnode_protos.h>
#include <HBBS/Hbbsnode_pragmas.h>

#include "/Mail_Common.h"


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

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

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

int startparam;

LONG __stack=16384;

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

#include "/maillib.i"

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

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

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

  exit(0);
}

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

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

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

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


#define CHCKID_OK     0
#define CHCKID_CANCEL 1      // ordered in this way for easier checking..
#define CHCKID_NC     2      // ie, 0 is OK, >=1 is Not OK

V_BIGNUM CheckUserIDs(char *userids, char *outputhandles, char *outputids, BOOL Interactive)
{
  V_BIGNUM retval=CHCKID_CANCEL;
  char tmpstr[1024],verytmpstr[1024];
  char oneid[1024];
  short where;
  BOOL More,Cancel=FALSE;
  struct UserData User;

/*

   the following routine checks userids for valid user handles, if they are not
   valid and interactive move is enabled the user will be asked to re-type
   the invalid handle which is then checked again

   the (upper/lower case corrected) handles are then written out to a string
   (outputhandles) with a command (,) seperating each one
   the UserID's are then written out to a string called outputids also with a comma
   seperating each one.

   if one or more valid handles were passed to this function it will return
   CHCKID_OK, if it was in interactive mode and the user lost carrier it will
   return CHCKID_NC.  If no valid user handles were found it will return CHCKID_CANCEL

   I do hope you agree that this routine is rather handy tho...  it took the best
   part of 2 hours to create (and bug-tet) :-)


   *C* It will be moved to HBBSNode.C anytime soon.. (not hbbscommon as it uses DOOR_#?
   functions..


*/


  strcpy(tmpstr,userids); // take a copy of the input string, incase the output
                          // string is the same as the inout string..

  // null terminate the output strings.

  outputhandles[0]=0;
  outputids[0]=0;

  // the error checking in this must be able to handle all sorts of incorrectly entered
  // strings, e.g,  ",,,  hydra , fred, cruise & co,  "
  // with a string like that it should only check for the following users:
  // "hydra", "fed" and "cruise & co"

  while(tmpstr[strlen(tmpstr)-1]==' ' || tmpstr[strlen(tmpstr)-1]==',')
  {
    tmpstr[strlen(tmpstr)-1]=0;
  }

  do
  {
    More=FALSE;
    where=position(",",tmpstr);

    if (where>=0)
    {
      More=TRUE;
      strNcpy(oneid,tmpstr,where);
      strfcpy(tmpstr,tmpstr,where+1);
    }
    else
    {
      strcpy(oneid,tmpstr);
    }


    while (oneid[0])
    {
      // now to remove spurious spaces from oneid

      for (where=0;oneid[where]==' ';where++);

      if (where) strfcpy(oneid,oneid,where);

      while(oneid[strlen(oneid)-1]==' ' || oneid[strlen(oneid)-1]==',')
      {
        oneid[strlen(oneid)-1]=0;
      }

      if (oneid[0]) // got anything left after we remove the spaces ?
      {
        if (HBBS_ValidUserHandle(oneid,&User))
        {
          if (outputhandles[0]!=0)
          {
            strcat(outputhandles,",");
            strcat(outputids,",");
          }
          strcat(outputhandles,User.Handle); // append the users handle with the correct case letters!
          sprintf(verytmpstr,"%ld",User.UserID);
          strcat(outputids,verytmpstr);
          oneid[0]=0;
          retval=CHCKID_OK;
        }
        else
        {
          if (Interactive)
          {
            sprintf(outstr,"The Handle \"%s\" is not valid, please re-enter: ",oneid);
            DOOR_WriteText(outstr);
            oneid[0]=0;
            DOOR_GetLine(GL_EDIT|GL_DISPLAY,'\0',0,0,NULL);
            if (N_ND->OnlineStatus==OS_ONLINE)
            {
              strcpy(oneid,N_ND->CurrentLine);
            }
            else
            {
              retval=CHCKID_NC; // lost carrier
              Cancel=TRUE;
            }
          }
          else oneid[0]=0;

        }
      }
    }

  } while (More && !Cancel);
  return(retval);
}

BOOL PostNewMail( struct MailData *NewMail,V_BIGNUM ConfNum, char *TxtFilename)
{
  // maildata must be filled in, if TxtFilename is NOT NULL then a text file will
  // be loaded from there into NewMail->Data,  confnum should be specified if you
  // are posting a conference message.


  BOOL retval=FALSE;
  V_BIGNUM loop;
  struct CfgFileData *CfgFile,*StatCfgFile;
  char maildir[1024]="";
  char mailcfg[1024];
  char paramstr[1024];
  V_BIGNUM LastMsg=0;
  struct Node *node;
  struct ConfData *Conf;


  if (NewMail)
  {
    switch (NewMail->Type)
    {
      case MSGT_SYSOP:
        strcpy(maildir,MAILDIR_SYSOP);
        break;
      case MSGT_PRIVATE:
        sprintf(maildir,MAILDIR_USER "%d/",NewMail->ToID);
        if (!PathOK(maildir)) CreatePath(maildir);
        break;
      case MSGT_PUBLIC:
        strcpy(maildir,MAILDIR_PUBLIC);
        break;
      case MSGT_CONFERENCE:
        Conf=(struct ConfData *)GetNode(BBSGlobal->ConfList,ConfNum-1);
        sprintf(maildir,"%sMail/",Conf->ConfPath);
        break;
    }
    if (maildir[0])
    {
      strcpy(mailcfg,maildir);
      strcat(mailcfg,MAILSTATFILE);

      if (!(StatCfgFile=HBBS_LoadConfig(mailcfg,LCFG_NONE)))
      {
        if (StatCfgFile=HBBS_CreateConfig(mailcfg))
        {
          HBBS_AddCfgItem(StatCfgFile,"FirstMessage","0");
          // the "lastmessage" item is removed anway, so we don't need to ad it here..

        }
      }
      if (StatCfgFile)
      {
        HBBS_GetSetting(StatCfgFile,(void *)&LastMsg,VTYPE_BIGNUM,"LastMessage",OPT_SINGLE);

        LastMsg++;

        // update the pointer to the last message

        HBBS_RemoveCfgItem(StatCfgFile,"LastMessage");
        sprintf(paramstr,"%d",LastMsg);
        HBBS_AddCfgItem(StatCfgFile,"LastMessage",paramstr);
        HBBS_SaveConfig(StatCfgFile);
        HBBS_FlushConfig(StatCfgFile);


        sprintf(mailcfg,"%sMessage_%d.CFG",maildir,LastMsg);

        if (TxtFilename)
        {
          NewMail->Data=HBBS_LoadFile(TxtFilename);
        }
        if (NewMail->Data)
        {
          // got some message data ?? (must have at least some...)
          if (CfgFile=HBBS_CreateConfig(mailcfg))
          {
            // build the configuration file

            sprintf(paramstr,"%d",NewMail->Type);
            HBBS_AddCfgItemQuick(CfgFile,"Type",paramstr);
            HBBS_AddCfgItemQuick(CfgFile,"From",NewMail->From);
            sprintf(paramstr,"%d",NewMail->FromID);
            HBBS_AddCfgItemQuick(CfgFile,"FromID",paramstr);
            HBBS_AddCfgItemQuick(CfgFile,"To",NewMail->To);
            sprintf(paramstr,"%d",NewMail->ToID);
            HBBS_AddCfgItemQuick(CfgFile,"ToID",paramstr);
            HBBS_AddCfgItemQuick(CfgFile,"Subject",NewMail->Subject);
            HBBS_AddCfgItemQuick(CfgFile,"CreateDate",NewMail->CreateDate);
            HBBS_AddCfgItemQuick(CfgFile,"ExpiryDate",NewMail->ExpiryDate);
            sprintf(paramstr,"%d",NewMail->Flags);
            HBBS_AddCfgItemQuick(CfgFile,"Flags",paramstr);
            if (NewMail->AttachedFileList)
            {
              for (loop=1,node=NewMail->AttachedFileList->lh_Head;node->ln_Succ;node=node->ln_Succ,loop++)
              {
                sprintf(paramstr,"AttacedFile_%d",loop);
                HBBS_AddCfgItemQuick(CfgFile,paramstr,node->ln_Name);
              }
            }

            // ok, now save it.

            if (HBBS_SaveConfig(CfgFile))
            {
              // if we saved it then save the data file too..

              sprintf(mailcfg,"%sMessage_%d.TXT",maildir,LastMsg);
              if (HBBS_SaveFile(mailcfg,NewMail->Data))
              {
                // and if that worked then all is ok!! whaay!
                retval=TRUE;
              }
            }
            HBBS_FlushConfig(CfgFile);

          }

        }
      }
    }
  }
  return(retval);
}

void WriteMail(char mailtype)
{
  char FromHandle[1024];
  char ToHandles[1024];
  char ToIDS[1024];
  BOOL Cancel,Done;
  V_BIGNUM tempval;
  LONG loop;
  char tmphandle[50];
  char tmpmailfile[1024];
  char tmpid[50];
  char *NewMailSubject=NULL;
  int where;
  V_SMALLNUM NewMailType=MSGT_VOID;

  struct MailData *NewMail;

  // this bit handles the asking of the username.

  Cancel=FALSE;
  Done=FALSE;

  ToHandles[0]=0;

  strcpy(FromHandle,N_ND->User.CallData.Handle); // the user on-line..


  switch(mailtype)
  {
    case 'S':
      NewMailType=MSGT_SYSOP;
      DOOR_WriteText("Sysop Comment\r\n");
      break;
    case 'U':
      NewMailType=MSGT_PRIVATE;
      DOOR_WriteText("Private User Mail\r\n");
      break;
    case 'C':
      NewMailType=MSGT_CONFERENCE;
      DOOR_WriteText("Conference Mail\r\n");
      break;
    case 'P':
      NewMailType=MSGT_PUBLIC;
      DOOR_WriteText("Public Mail\r\n");
      break;
    case 'I':
      NewMailType=MSGT_INTERNET;
      DOOR_WriteText("Internet Mail\r\n");
      break;
    case 'F':
      NewMailType=MSGT_FIDONET;
      DOOR_WriteText("Fidonet Mail\r\n");
      break;
  }

  switch(mailtype)
  {
    case 'S':
      strcpy(ToHandles,BBSGlobal->SysopAccount);
      break;
    case 'U':
    case 'C':
    case 'P':
    case 'I':
    case 'F':
      if (gargc>=startparam)
      {
        for (loop=startparam-1;loop<gargc;loop++)
        {
          if (ToHandles[0]!=0) strcat(ToHandles," ");
          strcat(ToHandles,gargv[loop]);
        }
      }
      else
      {
        DOOR_WriteText(ANSI_RESET ANSI_FG_YELLOW "To"ANSI_FG_GREEN" : "ANSI_FG_WHITE);
        DOOR_GetLine(GL_EDIT|GL_DISPLAY,'\0',0,0,NULL);
        if (N_ND->OnlineStatus==OS_ONLINE)
        {
          if (N_ND->CurrentLine[0])
          {
            strcpy(ToHandles,N_ND->CurrentLine);
          } else Cancel=TRUE; // No User ID Entered.
        }
        else Cancel=TRUE; // User Lost Carrier.
      }
      break;
  }

  if (!Cancel && ToHandles[0])
  {
    switch(mailtype)
    {
      case 'I': //internet
        // check for the format #?@#?.#?
        // also place the user's return internet address in FromHandle at this point *C*
        ToIDS[0]=0;
        break;
      case 'F': //fidonet
        // check for a correct fido address ( n/n:n.n )
        // also a return fido address needs to be placed in FromHandle *C*
        ToIDS[0]=0;
        break;
      case 'S':
      case 'U':
      case 'C':
      case 'P':
        // Got a user id, now lets check it..
        tempval=CheckUserIDs(ToHandles,ToHandles,ToIDS,TRUE);
        switch (tempval)
        {
          case CHCKID_OK:      // all valid
            Done=TRUE;
            break;
          case CHCKID_CANCEL:  // cancelled
          case CHCKID_NC:      // lost carrier
            Cancel=TRUE;
            break;
        }
        break;
    }
  }

  if (!Cancel && Done)
  {
    // got some valid user names (in ToHandles) , a valid fromname (in FromHandle)
    // and all the user id numbers (in ToIDS) (if applicable)

    DOOR_WriteText(ANSI_RESET ANSI_FG_YELLOW "Subject" ANSI_FG_GREEN" : "ANSI_FG_WHITE);
    DOOR_GetLine(GL_EDIT|GL_DISPLAY,'\0',40,0,NULL);
    if (N_ND->OnlineStatus==OS_ONLINE)
    {
      RemoveSpaces(N_ND->CurrentLine);
      if (N_ND->CurrentLine[0]==0)
      {
        DOOR_WriteText("Cancelled!\r\n");
      }
      else
      {
        NewMailSubject=DupStr(N_ND->CurrentLine);





        sprintf(tmpmailfile,"%sWork/NewMail.TXT",N_ND->NodeLocation);

        strcpy(outstr,tmpmailfile);
        strcat(outstr," MAIL");

        DOOR_SystemDoor("EDITOR",outstr);

        if (N_ND->OnlineStatus==OS_ONLINE)
        {
          if (iposition("CANCEL",N_ND->DoorReturn)>=0)
          {
            DOOR_WriteText("Cancelled!\r\n");
          }
          else
          {
            Done=FALSE;
            do
            {
              where=position(",",ToHandles);
              if (where>0)
              {
                strNcpy(tmphandle,ToHandles,where);
                strftcpy(ToHandles,ToHandles,where+1,strlen(ToHandles));
              }
              else
              {
                strcpy(tmphandle,ToHandles);
                Done=TRUE;
              }

              where=position(",",ToIDS);
              if (where>0)
              {
                strNcpy(tmpid,ToIDS,where);
                strftcpy(ToIDS,ToIDS,where+1,strlen(ToIDS));
              }
              else
              {
                strcpy(tmpid,ToIDS);
              }

              sprintf(outstr,"Saving Message to %s ",tmphandle);
              DOOR_WriteText(outstr);

              if (NewMail=AllocVec(sizeof(struct MailData ),MEMF_PUBLIC|MEMF_CLEAR))
              {
                NewMail->Type=NewMailType;
                NewMail->Subject=DupStr(NewMailSubject);

                NewMail->From=DupStr(FromHandle);
                NewMail->FromID=N_ND->User.CallData.UserID;

                NewMail->To=DupStr(tmphandle);
                sscanf(tmpid,"%d",&NewMail->ToID);

                NewMail->CreateDate=DupStr("DD/MMM/YYYY");
                NewMail->ExpiryDate=DupStr("DD/MMM/YYYY");

                NewMail->Flags=0; // *C* for now!
                NewMail->AttachedFiles=0;
                                         // confnum or 0
                if (PostNewMail(NewMail,N_ND->CurrentConf ? N_ND->CurrentConf->ConfNum : 0,tmpmailfile))
                {
                  N_ND->User.CallData.MessagesWritten++;
                  N_ND->User.NormalData.MessagesWritten++;
                }

                FreeMailMessage(NewMail);
              }
              DOOR_WriteText("\r[K");
            } while (!Done);
            DOOR_WriteText("Bish Bosh....... Sorted!\r\n");
          }
        }
        FreeStr(NewMailSubject);
      }
    }
  }
}

void DisplayMessage(struct MailData *Msg,V_BIGNUM MsgNum,V_BIGNUM ConfNum)
{
  struct Node *node;
  struct ConfData *Conf;
  V_BIGNUM Lines=5;
  V_BIGNUM LineNum;
  V_BOOL Cancel=FALSE;

  if (Msg)
  {
    N_ND->Actions[ACTN_READMAIL]=ACTC_READMAIL;
    DOOR_WriteText(ANSI_RESET ANSI_CLS ANSI_FG_BLUE"---"ANSI_FG_CYAN"-"ANSI_FG_WHITE"-"ANSI_FG_CYAN"-"ANSI_FG_BLUE"-------------------------------------------------------------------------\r\n");

    sprintf(outstr,ANSI_RESET ANSI_FG_CYAN "From "ANSI_FG_BLUE"   : " ANSI_FG_WHITE "%-25s " ANSI_FG_CYAN "Msg # "ANSI_FG_BLUE": " ANSI_FG_WHITE "%d " ANSI_FG_CYAN "Type "ANSI_FG_BLUE": "ANSI_FG_WHITE,Msg->From,MsgNum);
    DOOR_WriteText(outstr);

    switch(Msg->Type)
    {
      case MSGT_CONFERENCE:
        Conf=(struct ConfData *)GetNode(BBSGlobal->ConfList,ConfNum-1);
//        sprintf(outstr,"Conference "ANSI_FG_BLUE":" ANSI_FG_WHITE " %s ",Conf->node.ln_Name);
        strcpy(outstr,Conf->node.ln_Name);
        DOOR_WriteText(outstr);
        break;
      case MSGT_PUBLIC:
        DOOR_WriteText("Public Mail ");
        break;
      case MSGT_SYSOP:
        DOOR_WriteText("Sysop Mail ");
        break;
      case MSGT_PRIVATE:
        DOOR_WriteText("Private Mail ");
        break;
    }
    sprintf(outstr,ANSI_RESET ANSI_FG_CYAN "\r\n"
                                           "To     "ANSI_FG_BLUE" : " ANSI_FG_WHITE "%-25s " ANSI_FG_CYAN "Date "ANSI_FG_BLUE": " ANSI_FG_WHITE"%s " ANSI_FG_CYAN"Expires "ANSI_FG_BLUE": "ANSI_FG_WHITE"%s\r\n",Msg->To,Msg->CreateDate,Msg->ExpiryDate);
    DOOR_WriteText(outstr);
    sprintf(outstr,ANSI_RESET ANSI_FG_CYAN "Subject"ANSI_FG_BLUE" : " ANSI_FG_WHITE "%-60s "ANSI_FG_CYAN"%s\r\n",Msg->Subject,(Msg->Flags & MSGF_PRIVATE) ? "Private!" : "");
    DOOR_WriteText(outstr);
    DOOR_WriteText(ANSI_RESET ANSI_FG_BLUE"------------------------------------------------------------------"ANSI_FG_BLACK"-"ANSI_FG_BLUE"--"ANSI_FG_CYAN"-"ANSI_FG_WHITE"+"ANSI_FG_CYAN"-"ANSI_FG_BLUE"--"ANSI_FG_BLACK"-"ANSI_FG_BLUE"----\r\n");
    for (LineNum=0,node=Msg->Data->lh_Head;!Cancel && node->ln_Succ;node=node->ln_Succ,LineNum++)
    {
//      sprintf(outstr,ANSI_FG_CYAN"%2d"ANSI_FG_WHITE") %s\r\n",LineNum,node->ln_Name);
      sprintf(outstr,ANSI_RESET "%s\r\n",node->ln_Name);
      DOOR_WriteText(outstr);
      Lines++;
      if (Lines>=N_ND->User.CallData.LinesPerScreen)
      {
        Cancel=!DOOR_ContinuePrompt(NULL,DEFAULT_YES|DCP_ADDYN); // Display Pause Prompt for long messages
        Lines=0;
      }
    }
  }
}

BOOL DeleteMailMsg(struct MailData *Msg,V_BIGNUM MsgNum,V_BIGNUM ConfNum)
{
  BOOL retval=FALSE;
  BOOL DeleteOK=FALSE;
  BOOL Owner=FALSE;
  char tmpstr[1024];
  char filename[1024];
  struct Node *node;

  if (Msg)
  {

    // is the user a co-sysop ?

    if (N_ND->User.CallData.Access>=250)
    {
      DeleteOK=TRUE;
    }
    else
    {
      // is user allowed to delete mail ?

      if (N_ND->User.Acs.Data[ACS_DELETEMAIL]=='Y')
      {

        // was the message written by the current user ?

        if (N_ND->User.CallData.UserID==Msg->FromID) Owner=TRUE;

        // is the message deletable ?

        if (!(Msg->Flags & MSGF_NODELETE))
        {
          // is message an eall/all ?

          if (stricmp(Msg->To,"ALL")==0 || stricmp(Msg->To,"EVERYBODY")==0)
          {
            // yes, was it written by the user, or do they have access to delete
            // anyones mail
            if ((Owner) || (N_ND->User.Acs.Data[ACS_DELETEANYONE]=='Y'))
            {
              // yes!
              DeleteOK=TRUE;
            }
          }
          else
          {
            // message is not an eall/all.  was it addressed to the user
            if (Msg->ToID==N_ND->User.CallData.UserID)
            {
              // yes!
              DeleteOK=TRUE;
            }
          }
        }
      }
    }
    if (DeleteOK)
    {
      DOOR_WriteText("Removing Message ");
      GetMailPath(tmpstr,Msg->Type,ConfNum,N_ND->User.CallData.UserID);
      sprintf(filename,"%sMessage_%d.CFG",tmpstr,MsgNum);
      DeleteFile(filename);
      sprintf(filename,"%sMessage_%d.TXT",tmpstr,MsgNum);
      DeleteFile(filename);

      if (Msg->AttachedFiles>0)
      {
        if (Msg->Flags & MSGF_REMOVEFILES)
        {
          DOOR_WriteText("& files ");
          for (node=Msg->AttachedFileList->lh_Head;node->ln_Succ;node=node->ln_Succ)
          {
            DeleteFile(node->ln_Name);
          }
        }
        // remove dir that files MIGHT be in..
        sprintf(filename,"%sMessage_%d_FILES",tmpstr,MsgNum);
        DeleteFile(filename);
      }
      DOOR_WriteText("Done\r\n");
      retval=TRUE;

    }
    else
    {
      DOOR_WriteText("Access Denied!\r\n");
    }

  }
  return(retval);
}

#define DIR_FORWARD 1
#define DIR_BACKWARD 2

void DoorMain( void )
{
  struct MailData *Msg=NULL;
  char maildir[1024];
  V_SMALLNUM MsgType;
  V_BIGNUM MsgNum,ConfNum,loop;
  BOOL Done=FALSE,Deleted=FALSE;
  V_SMALLNUM Direction=DIR_FORWARD;
  struct List *MsgList=NULL;
  V_BIGNUM Msgs;
  struct MsgNode *CurrentMsg;
  BOOL ReTry,UpdateScan=FALSE,AutoDelete=TRUE,DeleteFailed,SaveList=FALSE,Keep;



  for (loop=3;loop<gargc;loop++)
  {
    strcat(outstr,gargv[loop]);
    if (loop<gargc-1) strcat(outstr," ");
  }

  if (iposition("UPDATESCAN",outstr)>=0) UpdateScan=TRUE;
  if (iposition("SAVELIST",outstr)>=0) SaveList=TRUE;
  if (iposition("NOAUTODELETE",outstr)>=0) AutoDelete=FALSE;


  if (stricmp(gargv[2],"MSGLIST")==0)
  {
    if (Msgs=LoadMsgList(&MsgList,gargv[3]))
    {
      // start looping

      CurrentMsg=(struct MsgNode *)MsgList->lh_Head;

      while (!Done)
      {
        // get the message

        if (GetMailPath(maildir,CurrentMsg->Type,CurrentMsg->ConfNum,CurrentMsg->ToID))
        {
          if (Msg=GetMailMessage(maildir,CurrentMsg->Number))
          {
            N_ND->User.MsgsRead++;
            Deleted=FALSE;
            DeleteFailed=FALSE;
            Keep=FALSE;
            do
            {
              // display it
              ReTry=FALSE;
              DisplayMessage(Msg,CurrentMsg->Number,CurrentMsg->ConfNum);

              if (UpdateScan) SaveLastScanned(CurrentMsg->Type,CurrentMsg->ConfNum,CurrentMsg->Number);

              strcpy(N_ND->CharsAllowed,"QqDdRrFfAaNnPpKk");
              DOOR_MenuPrompt("[K]eep, [Q]uit, [D]elete, [R]eply, [F]orward, [A]gain, [N]ext, [P]rev : ",(Direction == DIR_FORWARD) ? 'N' : 'P');
              DOOR_GetLine(GL_EDIT|GL_DISPLAY|GL_USECHARS|GL_IMMEDIATE,'\0',1,0,NULL);
              if (N_ND->OnlineStatus==OS_ONLINE)
              {
                if (N_ND->CurrentLine[0]==0)
                {
                  if (Direction==DIR_FORWARD) N_ND->CurrentLine[0]='N'; else N_ND->CurrentLine[0]='P';
                }
                switch (toupper(N_ND->CurrentLine[0]))
                {
                  case 'N':
                    Direction=DIR_FORWARD;
                    break;
                  case 'P':
                    Direction=DIR_BACKWARD;
                    break;
                  case 'Q':
                    Done=TRUE;
                    break;
                  case 'K':
                    Keep=TRUE;
                    break;
                  case 'A':
                    ReTry=TRUE;
                    break;
                  case 'D':
                    if (!Deleted)
                    {
                      if (DeleteMailMsg(Msg,CurrentMsg->Number,CurrentMsg->ConfNum))
                      {
                        Deleted=TRUE;
                      } else DeleteFailed=TRUE;
                    }
                    break;
                  case 'R':
                    sprintf(outstr,"REPLY %ld %ld %ld",CurrentMsg->Number,CurrentMsg->Type,CurrentMsg->ConfNum);
                    DOOR_SystemDoor("Mail_Reply",outstr);
                    if (stricmp("OK",N_ND->DoorReturn)==0)
                    {
                      if (DOOR_ContinuePrompt("Delete Original Message ",DEFAULT_YES|DCP_ADDYN))
                      {
                        if (DeleteMailMsg(Msg,CurrentMsg->Number,CurrentMsg->ConfNum))
                        {
                          Deleted=TRUE;
                          Keep=FALSE;
                        }
                      }
                    }
                    break;
                  default:  // anything else that's not a blank line
                    ReTry=TRUE;
                    break;
                }

              }
              else
              {
                Done=TRUE;
              }
            } while (ReTry && !Done);
            if (Keep)
            {
              CurrentMsg->ReadIt=FALSE;
            }
            else
            {
              CurrentMsg->ReadIt=TRUE;
              if (!Deleted && !DeleteFailed && Msg->Flags & MSGF_AUTODELETE && N_ND->OnlineStatus==OS_ONLINE && AutoDelete)
              {
                // auto delete message
                if (DeleteMailMsg(Msg,CurrentMsg->Number,CurrentMsg->ConfNum))
                {
                  Deleted=TRUE;
                }
              }
            }
            FreeMailMessage(Msg);
          }
        }
        if (!((Direction==DIR_FORWARD && CurrentMsg->node.ln_Succ) ||
              (Direction==DIR_BACKWARD && CurrentMsg->node.ln_Pred)))
        {
          DOOR_WriteText(ANSI_RESET ANSI_FG_CYAN "No More Messages!\r\n");
          Done=TRUE;
        }
        else
        {
          if (Direction==DIR_FORWARD) CurrentMsg=(struct MsgNode *)CurrentMsg->node.ln_Succ;
          if (Direction==DIR_BACKWARD) CurrentMsg=(struct MsgNode *)CurrentMsg->node.ln_Pred;
        }
        if (N_ND->OnlineStatus==OS_OFFLINE) Done=TRUE;
      }
      if (SaveList)
      {
        SaveMsgList(MsgList,gargv[3],SML_ALL);
      }
      FreeMsgList(MsgList);
    }
  }
  else
  if (stricmp(gargv[2],"MSG")==0)
  {
    // msgnum, type, confnum
    if (sscanf(outstr,"%d %d %d",&MsgNum,&MsgType,&ConfNum)==3)
    {
      if (GetMailPath(maildir,MsgType,ConfNum,N_ND->User.CallData.UserID))
      {
        if (Msg=GetMailMessage(maildir,MsgNum))
        {
          N_ND->User.MsgsRead++;
          DisplayMessage(Msg,MsgNum,ConfNum);

          if (UpdateScan) SaveLastScanned(MsgType,ConfNum,MsgNum);

          Keep=FALSE;
          do
          {
            strcpy(N_ND->CharsAllowed,"DdRrFfAaCcKk");
            DOOR_MenuPrompt("[K]eep, [D]elete, [R]eply, [F]orward, [A]gain, [C]ontinue : ",'C');
            DOOR_GetLine(GL_EDIT|GL_DISPLAY|GL_USECHARS|GL_IMMEDIATE,'\0',1,0,NULL);
            if (N_ND->OnlineStatus==OS_ONLINE)
            {
              switch (toupper(N_ND->CurrentLine[0]))
              {
                case '\0': //blank line
                case 'C':
                  Done=TRUE;
                  break;
                case 'K':
                  Keep=TRUE;
                  break;
                case 'A':
                  DisplayMessage(Msg,MsgNum,ConfNum);
                  break;
                case 'D':
                  if (DeleteMailMsg(Msg,MsgNum,ConfNum))
                  {
                    Done=TRUE;
                    Deleted=TRUE;
                  }
                  break;
                case 'R':
                  sprintf(outstr,"REPLY %ld %ld %ld",MsgNum,MsgType,ConfNum);
                  DOOR_SystemDoor("Mail_Reply",outstr);
                  if (stricmp("OK",N_ND->DoorReturn)==0)
                  {
                    Done=TRUE;
                    if (DOOR_ContinuePrompt("Delete Original Message ",DEFAULT_YES|DCP_ADDYN))
                    {
                      DeleteMailMsg(Msg,MsgNum,ConfNum);
                      Deleted=TRUE;
                      Keep=FALSE;
                    }
                  }
              }
            }
          } while (!Done);


          if (!Keep && !Deleted && Msg->Flags & MSGF_AUTODELETE && N_ND->OnlineStatus==OS_ONLINE)
          {
            // auto delete message
            if (DeleteMailMsg(Msg,MsgNum,ConfNum))
            {
              Deleted=TRUE;
            }
          }
          FreeMailMessage(Msg);
        }
        else
        {
          DOOR_WriteText(ANSI_RESET "That message does not exist!\r\n");
        }
      }
    }
    if (Deleted) DOOR_Return("DELETED");
    if (Keep) DOOR_Return("DELETED");
  }
}

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("Reading Mail");

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