/*

  Functions of the frontend door are thus:

  - Ask for system and node password and display PrivateSystem and PrivateNode
    screens is appropriate.
  - To Ask User for Username
  - Check the user name is in the userdatabase
    - if not then call new user door
  - ask user for password.
  - Check password is correct.
    - if not then call hack door
  - fill in N_ND->User with the correct data.
  - check if user is allowed to logon
    - Access Level >= N_ND->AccessRequired ?
    - Allowed at this time ?
  - reset user->TimeUsed to 0 if last called date is not the same as today..
  - return one of the following strings.
    - LOGGEDIN
    - HACK        // if HACK then run "HACK" system door..
    - FAILED
  - check to see if there is an access level the same as the user, if not
    flag the sysop and downgrade the user's access level to the next lowest one.

  todo:

  put max password and username attempts in config file.
  put min/max username/password lengths in config file.


  NamesNotAllowedFile, with an option for allowretry.. eg.

  Name_1=ALL
  Retry_1=YES
  Name_2=SUNNY
  Retry_2=NO    ;-)


*/

#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>
#include <time.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>
#include <HBBS/hbbscommon_pragmas.h>
#include <HBBS/Hbbsnode_protos.h>
#include <HBBS/Hbbsnode_pragmas.h>

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 *InvalidHandleMsg=NULL;

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 **************************/

int FE_GetUsername( void )
{
  do
  {
    DOOR_WriteText(N_ND->NodeSettings.UserNamePrompt);
    if (DOOR_GetLine(GL_EDIT|GL_DISPLAY|GL_HISTORY,'\0',0,0,NULL)==IN_LOSSCARRIER) // Lost Carrier ?
    {
      return(-1);
    }
    else
    {
      // Copy input to our own variable.

      strNcpy(Username,N_ND->CurrentLine,79); // 79 == Max Username Len..
      RemoveSpaces(Username);
    }

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

    if (strlen(Username)<MinUsernameLen)
    {
      DOOR_WriteText(HandleToShortMsg);
      DOOR_WriteText("\r\n");
    }
    else // ok, username is long enough
    {
      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_EDIT|GL_DISPLAY,'*',0,0,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,"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);
}

BOOL HBBS_HandleNameOK(char *checkstr)
{
  if (strchr(checkstr,',')) return(FALSE);
  if (strchr(checkstr,';')) return(FALSE);
  if (strchr(checkstr,'?')) return(FALSE);
  if (strchr(checkstr,'*')) return(FALSE);
  if (strchr(checkstr,'#')) return(FALSE);       // *C* optimze me..
  if (strchr(checkstr,'(')) return(FALSE);
  if (strchr(checkstr,')')) return(FALSE);
  return(TRUE);
}

void DoorMain( void )
{
  BOOL LostCarrier=FALSE;
  BOOL LoggedIn=FALSE;
  BOOL Hack=FALSE;
  BOOL SkipPW;
  short LoginAttempts=0;
  BOOL OK,ReTry=TRUE;
  struct CfgFileData *CfgFile;


  HandleToShortMsg=DupStr("That name/handle is too short. Please try again, or type BYE to hangup.");
  InvalidHandleMsg=DupStr("That name/handle contains invalid characters, try again, or type BYE to hangup");
  HangupCommand=DupStr("BYE");

  if (CfgFile=HBBS_LoadConfig("ProgDir:FrontEnd.CFG",LCFG_NONE))
  {
    HBBS_GetSetting(CfgFile,(void *)&HandleToShortMsg,VTYPE_STRING,"HandleToShortMsg",OPT_SINGLE);
    HBBS_GetSetting(CfgFile,(void *)&InvalidHandleMsg,VTYPE_STRING,"InvalidHandleMsg",OPT_SINGLE);
    HBBS_GetSetting(CfgFile,(void *)&HangupCommand,VTYPE_STRING,"HangupCommand",OPT_SINGLE);

    HBBS_FlushConfig(CfgFile);
  }

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

    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("Did you enter your name correctly ? ",DCP_ADDYN|DEFAULT_NO))
              {
                if (!HBBS_HandleNameOK(Username))
                {
                  DOOR_WriteText(InvalidHandleMsg); // display comments about ;'s and ,'s being in the file..
                  DOOR_WriteText("\r\n");
                }
                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!
              // cool idea, call HACK door with a parameter, e.g. HACK PASSWORD, HACK NOTIME etc..

              if (N_ND->User.CallData.Status!=USER_LOGINSDENIED)
              {


                // 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=TRUE;
                      break;
                    case 1:
                      LoggedIn=TRUE;
                      break;
                  }



                } else LoggedIn=TRUE; //skip password and log user in..
              }
              else
              {
                // Logins are denied for this user!
                Hack=TRUE;
              }
            }
          } while (ReTry);
          break;
      }
      if (N_ND->OnlineStatus==OS_OFFLINE) LostCarrier=TRUE;
    } while (!LostCarrier && !LoggedIn && !Hack && LoginAttempts<BBSGlobal->MaxUsernameAttempts);

    if (LoggedIn)
    {
      HBBS_SetAccess();
      DOOR_Return("LOGGEDIN");
      return;
    }
    if (Hack)
    {
      // Call Hack Attempt Door (so that the user has a chance to mail the sysop)
      DOOR_SystemDoor("HACK",Username);
      DOOR_Return("HACK");
      return;
    }
  }
  DOOR_Return("FAILED");
  FreeStr(HandleToShortMsg);
  FreeStr(InvalidHandleMsg);
  FreeStr(HangupCommand);
}

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