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

#include <dos/dos.h>
#include <clib/dos_protos.h>

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


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

#include <HBBS/Defines.h>
#include <HBBS/types.h>
#include <HBBS/ansi_codes.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: FrontEnd "RELEASE_STR;
             
long __stack=16*1024; // increse this in 4k incrments if you suffer from
                      // random/suprious crashings after or during the running
                      // of your door.

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

struct BBSGlobalData *BBSGlobal=NULL;
struct NodeData *N_ND=NULL;
int N_NodeNum=-1;

char Username[LEN_HANDLE+1];
char Password[LEN_PASSWORD+1];
short MinUsernameLen=3;
char tmpstr[256];
char *HangupCommand=NULL;
struct UserData TmpUser;
char *HandleToShortMsg=NULL;
char *HandleNotAllowedMsg=NULL;
char *InvalidHandleMsg=NULL;
char *NameCorrectMsg=NULL;            
char *NameCorrectCancelMsg=NULL;            
char *AlreadyLoggedInMsg=NULL;
char *AltHandlePrompt=NULL;
char *AltPasswordPrompt=NULL;
char *WrongPasswordMsg=NULL; 
char *NodeReservedMsg=NULL;
char *TooManyAttemptsMsg=NULL;
char *HangupMsg=NULL;
char *DoneMsg=NULL;

V_BOOL NoCRLF=FALSE; // i.e. A CR+LF is added after each message
V_BOOL UseSpecialScreens=FALSE;
V_SMALLNUM PromptTimeout=30,MaxAttempts=10;
short AttemptCount=0;
struct List *NamesNotAllowed=NULL;
V_BIGNUM NameCount = 0;

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


/********************************* ACTUAL DOOR CODE **************************/

void DisplayErrMsg( char *msg )
{
  // msg contains either a filename or a string message, depending on UseSpecialScreens
  
  if (msg && N_ND->OnlineStatus == OS_ONLINE)
  {
    if (UseSpecialScreens)
    {
      if (!DOOR_DisplaySpecialScreen(msg))
      {
        DOOR_WriteText(msg);
      }                    
    }
    else
    {
      DOOR_WriteText(msg);
      if (!NoCRLF)
      {
        DOOR_WriteText("\r\n");
      }
    }
  }

}

int FE_GetUsername( void )
{
  struct Node *node;

  do
  {
    AttemptCount++;
    
    if (AltHandlePrompt)
    {
      DisplayErrMsg(AltHandlePrompt);
    }
    else                                                      
    {
      DOOR_MenuPrompt(N_ND->NodeSettings.UserNamePrompt,' ');
    }

    if (DOOR_GetLine(GL_NOOLM|GL_EDIT|GL_DISPLAY|GL_HISTORY| (AltHandlePrompt ? GL_NODISTURB : 0),'\0',LEN_HANDLE,PromptTimeout,NULL)==IN_LOSSCARRIER) // Lost Carrier ?
    {
      return(-1);
    }
    else
    {

      RemoveSpaces(N_ND->CurrentLine);

      // Copy input to our own variable.

      strNcpy(Username,N_ND->CurrentLine,LEN_HANDLE);

    }
                                                            
    if (stricmp(Username,HangupCommand)==0)
    {
      DisplayErrMsg(HangupMsg);
      DOOR_Goodbye();
      DOOR_HangUp();
      return(-1);
    }

    if (N_ND->ReservedNode)
    {
      if (stricmp(Username,N_ND->ReservedHandle)!=0)
      {
        if (!DOOR_DisplaySpecialScreen("RESERVED"))
        {
          DisplayErrMsg(NodeReservedMsg);
          DOOR_Goodbye();
          DOOR_HangUp();
          return(-1);
        }
      }
    }

    if (strlen(Username)<MinUsernameLen)
    {
      DisplayErrMsg(HandleToShortMsg);
    }
    else // ok, username is long enough
    {

      if (NameCount>0)
      {
        // compare handle to list of names that are not allowed

        for (node = NamesNotAllowed->lh_Head ; node->ln_Succ ; node = node->ln_Succ)
        {
          if (stricmp(node->ln_Name,Username) == 0)
          {
            DisplayErrMsg(HandleNotAllowedMsg);

            // To hangup or not to hangup..  user shouldn't be allowed to try
            // another name cos they're probably lame or trying to hack the bbs.

            DOOR_Goodbye();
            DOOR_HangUp();
            return(-1);
          }
        }
      }

      // handle is good, all systems are go for launch!

      return(1);
    }

    if (AttemptCount>=MaxAttempts)
    {
      DisplayErrMsg(TooManyAttemptsMsg);
      DOOR_Goodbye();
      DOOR_HangUp();
      return(-1);
    }
  } while (TRUE);
}

int FE_GetPassword(char *Prompt,char *ActualPassword)
{                                             
  short passwordattempts;
                    
  for (passwordattempts=1;passwordattempts<=BBSGlobal->MaxPasswordAttempts;passwordattempts++)
  { 
    if (AltPasswordPrompt)
    {
      DisplayErrMsg(AltPasswordPrompt);
    }
    else
    {
      DOOR_MenuPrompt(Prompt,' ');
    }

    if (DOOR_GetLine(GL_NOOLM|GL_EDIT|GL_DISPLAY| (AltHandlePrompt ? GL_NODISTURB : 0),'*',LEN_PASSWORD,PromptTimeout,NULL)==IN_LOSSCARRIER)
    {
      return(-1);
    }
    else
    {
      strNcpy(Password,N_ND->CurrentLine,79);

      // compare users actual password to the string (case insensitive) in Password

      if (stricmp(Password,ActualPassword)==0)
      {
        return(1);
      }
      else
      {
        sprintf(tmpstr,WrongPasswordMsg,BBSGlobal->MaxPasswordAttempts-passwordattempts);
        DisplayErrMsg(tmpstr);
      }
    }
  }
  return(0);
}

void DisplayBaudScreen( void )
{
  char filename[20];

  sprintf(filename,"Speed_%s",N_ND->ConnectBaud);
  DOOR_DisplaySpecialScreen(filename);
}

int AlreadyLoggedIn( void )
{
  LONG loop;
  struct NodeData *nd;
  BOOL Found=FALSE;

  for (loop=0;loop<BBSGlobal->BBSNodes;loop++)
  {
    if (loop!=N_ND->NodeNum) // don't check our own node!
    {
      if (nd=HBBS_NodeDataPtr(loop))
      {
        if (nd->User->Valid)
        {
          if (N_ND->User->CallData->UserID == nd->User->CallData->UserID)
          {
            Found=TRUE;
          }
        }
      }
    }
  }

  if (Found)
  {
    DisplayErrMsg(AlreadyLoggedInMsg);
  }

  return(Found);
}

#define HCK_NONE 0
#define HCK_TIME 1
#define HCK_LOCK 2
#define HCK_PSWD 3

void DoorMain( void )
{
  BOOL LostCarrier=FALSE;
  BOOL LoggedIn=FALSE;
  int Hack=HCK_NONE;
  BOOL SkipPW;
  BOOL NameOK;
  short LoginAttempts=0;
  BOOL OK=TRUE,ReTry=TRUE,VarSet=FALSE;
  struct CfgFileData *CfgFile;
  struct Node *newnode=NULL;
  char tmpstr[BIG_STR];

  HandleToShortMsg=DupStr(ANSI_FG_WHITE"That name/handle is too short. Please try again, or type BYE to hangup.");
  HandleNotAllowedMsg=DupStr(ANSI_FG_WHITE"That name/handle is not allowed.");
  InvalidHandleMsg=DupStr(ANSI_FG_WHITE"That name/handle contains invalid characters, try again, or type BYE to hangup");
  NameCorrectMsg=DupStr(ANSI_FG_WHITE"Did you enter your name correctly :");
  HangupCommand=DupStr("BYE");
  AlreadyLoggedInMsg=DupStr(ANSI_FG_WHITE"You're already logged in on another node!");
  WrongPasswordMsg=DupStr(ANSI_FG_CYAN"Wrong Password, you have "ANSI_FG_YELLOW"%d"ANSI_FG_CYAN" attempt(s) left");
  NodeReservedMsg=DupStr(ANSI_FG_WHITE"The node is reserved! try again later!");
  TooManyAttemptsMsg=DupStr("Too many attempts, bye!");
  HangupMsg=DupStr("Ok, bye!\r\n");
  // no default for DoneMsg;
  
  if (CfgFile=HBBS_LoadConfig("ProgDir:FrontEnd.CFG",LCFG_NOSTRIPCOMMENTS))
  {

    HBBS_GetSetting(CfgFile,(void *)&AltHandlePrompt,VTYPE_STRING,"AlternateHandlePrompt",OPT_SINGLE);
    HBBS_GetSetting(CfgFile,(void *)&AltPasswordPrompt,VTYPE_STRING,"AlternatePasswordPrompt",OPT_SINGLE);
    HBBS_GetSetting(CfgFile,(void *)&WrongPasswordMsg,VTYPE_STRING,"WrongPasswordMsg",OPT_SINGLE);
    HBBS_GetSetting(CfgFile,(void *)&NodeReservedMsg,VTYPE_STRING,"NodeReservedMsg",OPT_SINGLE);
    HBBS_GetSetting(CfgFile,(void *)&TooManyAttemptsMsg,VTYPE_STRING,"TooManyAttemptsMsg",OPT_SINGLE);
    HBBS_GetSetting(CfgFile,(void *)&HangupMsg,VTYPE_STRING,"HangupMsg",OPT_SINGLE);

    HBBS_GetSetting(CfgFile,(void *)&HandleToShortMsg,VTYPE_STRING,"HandleToShortMsg",OPT_SINGLE);
    HBBS_GetSetting(CfgFile,(void *)&HandleNotAllowedMsg,VTYPE_STRING,"HandleNotAllowedMsg",OPT_SINGLE);
    HBBS_GetSetting(CfgFile,(void *)&NameCorrectMsg,VTYPE_STRING,"NameCorrectMsg",OPT_SINGLE);
    HBBS_GetSetting(CfgFile,(void *)&NameCorrectCancelMsg,VTYPE_STRING,"NameCorrectCancelMsg",OPT_SINGLE);
    HBBS_GetSetting(CfgFile,(void *)&InvalidHandleMsg,VTYPE_STRING,"InvalidHandleMsg",OPT_SINGLE);
    HBBS_GetSetting(CfgFile,(void *)&HangupCommand,VTYPE_STRING,"HangupCommand",OPT_SINGLE);
    HBBS_GetSetting(CfgFile,(void *)&AlreadyLoggedInMsg,VTYPE_STRING,"AlreadyLoggedInMsg",OPT_SINGLE);
    HBBS_GetSetting(CfgFile,(void *)&DoneMsg,VTYPE_STRING,"DoneMsg",OPT_SINGLE);
    HBBS_GetSetting(CfgFile,(void *)&PromptTimeout,VTYPE_SMALLNUM,"PromptTimeout",OPT_SINGLE);
    HBBS_GetSetting(CfgFile,(void *)&MaxAttempts,VTYPE_SMALLNUM,"MaxAttempts",OPT_SINGLE);
    HBBS_GetSetting(CfgFile,(void *)&UseSpecialScreens,VTYPE_BOOL,"UseSpecialScreens",OPT_SINGLE);
    HBBS_GetSetting(CfgFile,(void *)&NoCRLF,VTYPE_BOOL,"NoCRLF",OPT_SINGLE);
    NameCount = HBBS_GetSetting(CfgFile,(void *)&NamesNotAllowed,VTYPE_STRINGLIST,"NamesNotAllowed",OPT_MULTI);
    HBBS_FlushConfig(CfgFile);
  }
  
  // replace all occurences of {LF} in Msg variables with a CR+LF.
   
  replace(DoneMsg,DoneMsg,"{LF}","\r\n"); 
  replace(AltHandlePrompt,AltHandlePrompt,"{LF}","\r\n");
  replace(AltPasswordPrompt,AltPasswordPrompt,"{LF}","\r\n");
  replace(WrongPasswordMsg,WrongPasswordMsg,"{LF}","\r\n");
  replace(NodeReservedMsg,NodeReservedMsg,"{LF}","\r\n");
  replace(TooManyAttemptsMsg,TooManyAttemptsMsg,"{LF}","\r\n");
  replace(HangupMsg,HangupMsg,"{LF}","\r\n");
  replace(HandleToShortMsg,HandleToShortMsg,"{LF}","\r\n");
  replace(HandleNotAllowedMsg,HandleNotAllowedMsg,"{LF}","\r\n");
  replace(NameCorrectMsg,NameCorrectMsg,"{LF}","\r\n");
  replace(NameCorrectCancelMsg,NameCorrectCancelMsg,"{LF}","\r\n");
  replace(InvalidHandleMsg,InvalidHandleMsg,"{LF}","\r\n");
  replace(AlreadyLoggedInMsg,AlreadyLoggedInMsg,"{LF}","\r\n");
  replace(DoneMsg,DoneMsg,"{LF}","\r\n");
   
  if (NameCount == 0)
  {
    if (NamesNotAllowed = HBBS_CreateList())
    {
      if (newnode = HBBS_CreateNode("SYSOP",0))
      {
        AddTail(NamesNotAllowed,newnode);
        NameCount=1;
      }
    }
  }
                         
  // system password

  if (N_ND->NodeSettings.UseSysPW)
  {

    // run the scripts if they exist.
    if (PathOK(N_ND->NodeSettings.SysPWScript))
    {
      strcpy(N_ND->Action,"SysPWScript");
      DOOR_UpdateNodeStatus(UPD_ACTION);

      sprintf(tmpstr,"%s %ld %s",N_ND->NodeSettings.SysPWScript,N_ND->NodeNum,N_ND->ConnectBaud);
      HBBS_RunDOSCMD(tmpstr,FALSE);
    }

    DOOR_DisplaySpecialScreen(SSCREEN_PRIVATESYSTEM);
    OK=(FE_GetPassword(N_ND->NodeSettings.SysPWPrompt,N_ND->NodeSettings.SysPW)==1);
  }

  if (OK && N_ND->NodeSettings.UseNodePW)
  {

    // run the scripts if they exist.
    if (PathOK(N_ND->NodeSettings.NodePWScript))
    {
      strcpy(N_ND->Action,"NodePWScript");
      DOOR_UpdateNodeStatus(UPD_ACTION);

      sprintf(tmpstr,"%s %ld %s",N_ND->NodeSettings.NodePWScript,N_ND->NodeNum,N_ND->ConnectBaud);
      HBBS_RunDOSCMD(tmpstr,FALSE);
    }

    DOOR_DisplaySpecialScreen(SSCREEN_PRIVATENODE);
    OK=(FE_GetPassword(N_ND->NodeSettings.NodePWPrompt,N_ND->NodeSettings.NodePW)==1);
  }

  if (OK)
  {
    DisplayBaudScreen();
  }

  if (OK && N_ND->OnlineStatus==OS_ONLINE)
  {
    DOOR_DisplaySpecialScreen("BBSTitle");

    if (N_ND->AllowLogins || N_ND->LoginType==LOGIN_LOCAL)
    {

      do
      {
        switch(FE_GetUsername())
        {
          case -1:
            LostCarrier=TRUE;
            break;
          case 1:
            LoginAttempts++;
            // ok, we got a sensible username lets see if it exists

            SkipPW=FALSE;
            do
            {


              if (!HBBS_ValidUserHandle(Username,&TmpUser))
              {
                // user does not exist

  //              if (ReTry)

                ReTry=FALSE;
                NameOK=FALSE;
                
                if (NameCorrectMsg[strlen(NameCorrectMsg)-1] == ':')
                { 
                  strNcpy(tmpstr,NameCorrectMsg,strlen(NameCorrectMsg)-1);
                  NameOK = DOOR_ContinuePrompt(tmpstr,DCP_ADDYN|DCP_DEFAULT_NO);
                }
                else
                {                                                         
                  DisplayErrMsg(NameCorrectMsg);
                  NameOK = DOOR_ContinuePrompt("",DCP_DEFAULT_NO);
                }                                       
                
                if (NameOK)                                             
                {
                  if (!HBBS_HandleNameOK(Username))
                  {
                    DisplayErrMsg(InvalidHandleMsg); // display comments about ;'s and ,'s being in the file..
                  }
                  else
                  { 
                    DisplayErrMsg(DoneMsg);
                    DOOR_SystemDoor("NEWUSER",Username);
                    if (iposition("USERADDED",N_ND->DoorReturn)!=-1)
                    {
                      ReTry=TRUE;
                      SkipPW=TRUE;        
                      LoginAttempts--;
                    }
                  }
                }
                else
                {
                  DisplayErrMsg(NameCorrectCancelMsg);
                }
              }
              else
              {
                ReTry=FALSE;
                // ok, copy the users data to the node's user pointer..

                CopyMem(&TmpUser,N_ND->User->NormalData,sizeof(struct UserData));
                CopyMem(&TmpUser,N_ND->User->CallData,sizeof(struct UserData));
                N_ND->User->Valid=TRUE; // that's not to say they've logged in ok yet tho... :-)

                // lets see if they are allowed to login..

                // *C* add timecheck here! (and set Hack=HCK_TIME)

                if (N_ND->User->CallData->Status==USER_NEW ||
                    N_ND->User->CallData->Status==USER_VALIDATED ||
                    N_ND->User->CallData->Status==USER_INACTIVE)
                {
                  // user exists, now lets try getting a password.

                  if (N_ND->NodeSettings.AskUserPW && !SkipPW)
                  {
                    // but only if we're supposed to...
                    switch(FE_GetPassword(N_ND->NodeSettings.UserPWPrompt,TmpUser.Password))
                    {
                      case -1:
                        LostCarrier=TRUE;
                        break;
                      case 0:
                        Hack=HCK_PSWD;
                        break;
                      case 1:
                        LoggedIn=TRUE;
                        break;
                    }



                  } else LoggedIn=TRUE; //skip password and log user in..
                }
                else
                {
                  // Logins are denied for this user!
                  Hack=HCK_LOCK;
                }                                           
              }
            } while (ReTry);
            break;
        }
        if (N_ND->OnlineStatus==OS_OFFLINE) LostCarrier=TRUE;
      } while (!LostCarrier && !LoggedIn && !Hack && LoginAttempts<BBSGlobal->MaxUsernameAttempts);
    }
    else
    {
      DOOR_WriteText(N_ND->BBSStrings->NoLoginsAllowed);
    }
    
    if (LoggedIn)
    {
      if (!AlreadyLoggedIn())
      {
        HBBS_SetAccess();
        DOOR_Return("LOGGEDIN");
        if (!SkipPW) DisplayErrMsg(DoneMsg); //(don't display DoneMsg twice!)
        VarSet=TRUE;
      }
    }
    if (Hack && !VarSet)
    {
      // Call Hack Attempt Door (so that the user has a chance to mail the sysop)
      tmpstr[0]=0;
      switch (Hack)
      {
        case HCK_TIME:
          strcpy(tmpstr,"TIME");
          break;
        case HCK_PSWD:
          strcpy(tmpstr,"PSWD");
          break;
        case HCK_LOCK:
          strcpy(tmpstr,"LOCK");
          break;
      }
      strcat(tmpstr," ");
      strcat(tmpstr,Username);
      DisplayErrMsg(DoneMsg);
      DOOR_SystemDoor("HACK",tmpstr);
      DOOR_Return("HACK");
      VarSet=TRUE;;
    }
  }

                                                       
  FreeStr(HandleToShortMsg);
  FreeStr(InvalidHandleMsg);
  FreeStr(HangupCommand);
  FreeStr(NameCorrectMsg);
  FreeStr(NameCorrectCancelMsg);
  FreeStr(AlreadyLoggedInMsg);
  FreeStrList(NamesNotAllowed);
  FreeStr(HandleNotAllowedMsg);

  FreeStr(AltHandlePrompt);
  FreeStr(AltPasswordPrompt);          
  FreeStr(WrongPasswordMsg);
  FreeStr(NodeReservedMsg);
  FreeStr(TooManyAttemptsMsg);
  FreeStr(HangupMsg);        
  FreeStr(DoneMsg);        

  if (!VarSet)
  {
    DOOR_Return("FAILED");
    DisplayErrMsg(DoneMsg);
  } 
}

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

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