
#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/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[80];
char Password[80];
short MinUsernameLen=3;
char tmpstr[256];
char *HangupCommand=NULL;
struct UserData User;
char *HandleToShortMsg=NULL;
char *HandleNotAllowedMsg=NULL;
char *InvalidHandleMsg=NULL;
char *NameCorrectMsg=NULL;
char *AlreadyLoggedInMsg=NULL;
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 (UseSpecialScreens)
  {
    if (!DOOR_DisplaySpecialScreen(msg))
    {
      DOOR_SysopText("Could not find special screen: ");
      DOOR_SysopText(msg);
      DOOR_SysopText("\r\n");
    }
  }
  else
  {
    DOOR_WriteText(msg);
    DOOR_WriteText("\r\n");
  }

}

int FE_GetUsername( void )
{
  struct Node *node;

  do
  {
    AttemptCount++;

    DOOR_WriteText(N_ND->NodeSettings.UserNamePrompt);
    if (DOOR_GetLine(GL_NOOLM|GL_EDIT|GL_DISPLAY|GL_HISTORY,'\0',0,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)
    {
      DOOR_Goodbye();
      DOOR_HangUp();
      return(-1);
    }

    if (N_ND->ReservedNode)
    {
      if (stricmp(Username,N_ND->ReservedHandle)!=0)
      {
        if (!DOOR_DisplaySpecialScreen("RESERVED"))
        {
          DOOR_WriteText("The node is reserved! try again later!\r\n");
          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)
    {
      DOOR_WriteText("Too many attempts, bye!\r\n");
      DOOR_Goodbye();
      DOOR_HangUp();
      return(-1);
    }
  } while (TRUE);
}

int FE_GetPassword(char *Prompt,char *ActualPassword)
{
  short passwordattempts;

  for (passwordattempts=1;passwordattempts<=BBSGlobal->MaxPasswordAttempts;passwordattempts++)
  {
    DOOR_WriteText(Prompt);
    if (DOOR_GetLine(GL_NOOLM|GL_EDIT|GL_DISPLAY,'*',0,PromptTimeout,NULL)==IN_LOSSCARRIER) // *C* make the 30 a configurable item
    {
      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,"Wrong Password, you have %d tr%s left\r\n",BBSGlobal->MaxPasswordAttempts-passwordattempts,BBSGlobal->MaxPasswordAttempts-passwordattempts!=1 ? "ies" : "y");
        DOOR_WriteText(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;
  short LoginAttempts=0;
  BOOL OK,ReTry=TRUE,VarSet=FALSE;
  struct CfgFileData *CfgFile;
  struct Node *newnode=NULL;

  HandleToShortMsg=DupStr("That name/handle is too short. Please try again, or type BYE to hangup.");
  HandleNotAllowedMsg=DupStr("That name/handle is not allowed.");
  InvalidHandleMsg=DupStr("That name/handle contains invalid characters, try again, or type BYE to hangup");
  NameCorrectMsg=DupStr("Did you enter your name correctly ? ");
  HangupCommand=DupStr("BYE");
  AlreadyLoggedInMsg=DupStr("You're already logged in on another node!");

  if (CfgFile=HBBS_LoadConfig("ProgDir:FrontEnd.CFG",LCFG_NONE))
  {

    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 *)&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 *)&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);
    NameCount = HBBS_GetSetting(CfgFile,(void *)&NamesNotAllowed,VTYPE_STRINGLIST,"NamesNotAllowed",OPT_MULTI);
    HBBS_FlushConfig(CfgFile);
  }

  if (NameCount == 0)
  {
    if (NamesNotAllowed = HBBS_CreateList())
    {
      if (newnode = HBBS_CreateNode("SYSOP",0))
      {
        AddTail(NamesNotAllowed,newnode);
        NameCount=1;
      }
    }
  }

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

  if (OK && N_ND->NodeSettings.UseNodePW)
  {
    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,&User))
              {
                // user does not exist

  //              if (ReTry)

                ReTry=FALSE;
                if (DOOR_ContinuePrompt(NameCorrectMsg,DCP_ADDYN|DEFAULT_NO))
                {
                  if (!HBBS_HandleNameOK(Username))
                  {
                    DisplayErrMsg(InvalidHandleMsg); // display comments about ;'s and ,'s being in the file..
                  }
                  else
                  {
                    DOOR_WriteText("Calling New User Door\r\n");
                    DOOR_SystemDoor("NEWUSER",Username);
                    if (iposition("USERADDED",N_ND->DoorReturn)!=-1)
                    {
                      ReTry=TRUE;
                      SkipPW=TRUE;
                      LoginAttempts--;
                    }
                  }
                }
              }
              else
              {
                ReTry=FALSE;
                // ok, copy the users data to the node's user pointer..

                CopyMem(&User,&N_ND->User.NormalData,sizeof(struct UserData));
                CopyMem(&User,&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,User.Password))
                    {
                      case -1:
                        DOOR_WriteText("Lost Carrier!\r\n");
                        LostCarrier=TRUE;
                        break;
                      case 0:
                        DOOR_WriteText("Hack Attempt\r\n");
                        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("No logins are allowed at the moment, sysop has restricted access\r\n");// *C* make configurable, add to BBSStrings.h
    }
    if (LoggedIn)
    {
      if (!AlreadyLoggedIn())
      {
        HBBS_SetAccess();
        DOOR_Return("LOGGEDIN");
        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);
      DOOR_SystemDoor("HACK",tmpstr);
      DOOR_Return("HACK");
      VarSet=TRUE;;
    }
  }

  FreeStr(HandleToShortMsg);
  FreeStr(InvalidHandleMsg);
  FreeStr(HangupCommand);
  FreeStr(NameCorrectMsg);
  FreeStr(AlreadyLoggedInMsg);
  FreeStrList(NamesNotAllowed);
  FreeStr(HandleNotAllowedMsg);
  if (!VarSet)
  {
    DOOR_Return("FAILED");
  }
}

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