
/*

   *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 <HBBS/release.h>
char *versionstr="$VER: Mail_Read "RELEASE_STR;

#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..
BOOL MailScanMode=FALSE;
BOOL NoQuit=FALSE;

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

/*

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 DisplayMessage(struct MailData *Msg,V_BIGNUM MsgNum,V_BIGNUM ConfNum)
{
  struct Node *node;
  struct ConfData *Conf;
  V_BIGNUM Lines=6; // message header lines
  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 " ,Msg->To,Msg->CreateDate);
    DOOR_WriteText(outstr);
    if (stricmp(Msg->ExpiryDate,"DD-MM-YYYY")==0)
    {
      DOOR_WriteText("\r\n");
    }
    else
    {
      sprintf(outstr,ANSI_FG_CYAN"Expires "ANSI_FG_BLUE": "ANSI_FG_WHITE"%s\r\n",(stricmp(Msg->ExpiryDate,"DD-MMM-YYYY") == 0) ? "Never" : 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++;
      Lines+=strlen(outstr) / 80 ;  // if the line is long it'll wrap around onto another line, so we have to count how many
                                   // times it wraps around and increment out linecount accordingly.
      if (Lines>=N_ND->User->CallData->LinesPerScreen)
      {
        // check the message flags for MUSTREAD, if so then don't let the user cancel reading the rest of
        // the message!.

        if ((Msg->Flags & MSGF_MUSTREAD) && MailScanMode)
        {
          DOOR_ContinuePrompt("Continue Reading [Return]",DEFAULT_NONE|DCP_CLEAR|DCP_NODISPLAY|DCP_COLOURIZE);
        }
        else
        {
          Cancel=!DOOR_ContinuePrompt("Continue Reading ",DEFAULT_YES|DCP_ADDYN|DCP_CLEAR); // Display Pause Prompt for long messages
        }

        // reset counter.
        Lines=1;
      }
    }
  }
}

BOOL DeleteMailMsg(struct MailData *Msg,V_BIGNUM MsgNum,V_BIGNUM ConfNum)
{
  BOOL retval=FALSE;
  BOOL DeleteOK=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')
      {

        // The owner can *ALWAYS* delete mail that they wrote!
        if (N_ND->User->CallData->UserID==Msg->FromID)
        {
          DeleteOK=TRUE;
        }
        else
        {
          // is the message deletable ?

          if (!(Msg->Flags & MSGF_NODELETE))
          {
            // message is deletable, lets see if the user has access to delete
            // messages from other users

            if (N_ND->User->Acs.Data[ACS_DELETEANYONE]=='Y')
            {
              // yes!
              DeleteOK=TRUE;
            }

            // was the message written by another user but written to the user
            // reading it now ?
            if (Msg->ToID==N_ND->User->CallData->UserID)
            {
              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=FALSE;
  char menuprompt[80];


  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 (iposition("MAILSCAN",outstr)>=0) MailScanMode=TRUE;
  if (iposition("NOQUIT",outstr)>=0) NoQuit=TRUE;


  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)))
          {
            sprintf(outstr,ANSI_RESET "Message Type "ANSI_FG_YELLOW"%s"ANSI_FG_WHITE", Number "ANSI_FG_YELLOW"%d"ANSI_FG_WHITE" no longer exists [Return]",MSGTypeName[CurrentMsg->Type],CurrentMsg->Number);
            DOOR_ContinuePrompt(outstr,DCP_NONE | DCP_NODISPLAY | DCP_COLOURIZE);
            CurrentMsg->ReadIt=TRUE; // so that it's removed from the list.
          }
          else
          {
            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,"DdRrFfAaNnPpKk");
              menuprompt[0]=0;

              // add the quit if we're not in mail scan mode

              // *C* we need to have a quit in mailscan mode if there are no more unread mustread messages

              if (! (MailScanMode && NoQuit))
              {
                strcat(N_ND->CharsAllowed,"Qq");
                strcpy(menuprompt,"[Q]uit, ");
              }

              strcat(menuprompt,"[K]eep, [D]elete, [R]eply, [F]orward, [A]gain, [N]ext, [P]rev : ");

              DOOR_MenuPrompt(menuprompt,(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("MailReply",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;

              // check for the must read flag.
              // note: we don't remove the mustread flag on the actual message, but instead
              // we remove it from the msglist, this is so that it will still appear as a
              // mustread message to other users if it's an EALL type..

              if (CurrentMsg->Flags & MSGF_MUSTREAD)
              {
                CurrentMsg->Flags -= MSGF_MUSTREAD;
              }


            }
            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("MailReply",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("KEEP");
  }
}

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