/***************************************************************************
 *                                                                         *
 *   GALSUPAH.CPP                                                          *
 *                                                                         *
 *   Copyright (c) 1996-1997 Galacticomm, Inc.    All rights reserved.     *
 *                                                                         *
 *   Active HTML Agent for Worldgroup signup.                              *
 *                                                                         *
 *                                                - RNStein  9/4/96        *
 *                                                                         *
 ***************************************************************************/

#include "gcomm.h"
#include "galacth.h"
#include "dnfmgr.h"
#include "ahutil.h"
#include "wgssup.h"
#include "majorbbs.h"
#include "wgsuext.h"
#include "enctvb.h"
#include "cyctimer.h"
#include "gme.h"
#include "galsupah.h"
#include <cstring>

#define FILREV "$Revision: 12 $"
#define DFTERROR "error.htm"      // default error template
#define DFTSUCCESS "welcome.htm"  // default signup success template

#define PPFIX "galacth/galsupah"  // path prefix for DynaFiles and other media
#define ISPARAM(x) ses->paramIndex(x,m_index)

#define NMQSTS        10           // max number of additional sign-up questions
#define ANSSIZ        80           // answer size
#define QUES_NAME     0            // represents question type "name"
#define QUES_COMPANY  1            // represents question type "company"
#define QUES_ADDRESS  2            // represents question type "address"
#define QUES_PHONE    3            // represents question type "phone"
#define QUES_COMPUTER 4            // represents question type "computer"
#define QUES_DOB      5            // represents question type "date of birth"
#define QUES_SEX      6            // represents question type "sex"

enum {LISTITEM};

typedef enum {
     ERR_NOERROR=0,                // no error
     ERR_UNRECOGNIZED=0,           // unrecognized error
     ERR_BDAY=1,                   // invalid birthday
     ERR_INVUID=2,                 // invalid User-ID
     ERR_UNVUID=3,                 // unavailable User-ID
     ERR_ACCUID=4,                 // unacceptable User-ID
     ERR_NOUID=5,                  // no User-ID supplied
     ERR_TOOSHORT=6,               // answer too short
     ERR_TOOLONG=7,                // answer too long
     ERR_AIUUID=8,                 // User-ID already in use
     ERR_PASSNONE=9,               // no password supplied
     ERR_PASSSEC=10,               // insecure password
     ERR_PASSBOTH=11,              // need to confirm password
     ERR_PASSMATCH=12,             // passwords don't match
     ERR_PROFANE=13,               // profane input
     ERR_NOVAL=14,                 // no value submitted for required field
     ERR_NOMORE=15,                // seat count reached
     ERR_INVEMAIL=16,              // invalid alternate email address
     ERR_HIGHERROR            // this must be the highest err, insert new
                              // errors before and give them continuous values
} errorcodes;

// message options

bool g_bReqAltEmail;               // is alternate email address required?
bool g_bReqPwdHint;                // is password hint required?
bool g_bSendWelcome;               // send welcome message to alternate email?
CHAR g_msgFrom[MAXADR];            // sender of welcome message
CHAR *namehtml;
CHAR *comhtml;
CHAR *adr1html;
CHAR *adr2html;
CHAR *ctryhtml;
CHAR *phohtml;
CHAR *comphtml;
CHAR *bdayhtml;
CHAR *sexhtml;
CHAR *userhtml;
CHAR *userchk;
CHAR *altemlhtml;
CHAR *hinthtml;
INT timslc;

HMCVFILE supAHMsg;                 // signup message file pointer

INT errArray[ERR_HIGHERROR]={ERROOPS,ERR01,ERR02,ERR03,ERR04,ERR05,ERR06,ERR07
                            ,ERR08,ERR09,ERR10,ERR11,ERR12,ERR13,ERR14,ERR15
                            ,ERR16};

// text variables

basicTvb t_ErrorMessage("SUP_ERROR");
basicTvb t_Name("SUP_NAME");
basicTvb t_UserID("SUP_USERID");
basicTvb t_UserNameCheck("SUP_USERID_CHECK");
basicTvb t_Company("SUP_COMPANY");
basicTvb t_Address1("SUP_ADDRESS1");
basicTvb t_Address2("SUP_ADDRESS2");
basicTvb t_Country("SUP_COUNTRY");
basicTvb t_Computer("SUP_COMPUTER");
basicTvb t_Birthdate("SUP_BIRTHDATE");
basicTvb t_Phone("SUP_PHONE");
basicTvb t_Sex("SUP_SEX");
basicTvb t_PwdHint("SUP_PWDHINT");
basicTvb t_AltEmail("SUP_ALTEMAIL");
basicTvb t_OnSuccess("SUP_ONSUCCESS");
basicTvb t_OnError("SUP_ONERROR");
basicTvb t_SupEnd("SUP_ENDER");
basicTvb t_SuppHeader("SUP_HEADER");
basicTvb t_SuppQuestion("SUP_QUESTION");
basicTvb t_SuppQuesName("SUP_QUESNAME");

// array of boolean options for specific signup questions

static INT g_askarr[] = { ASKNAM, ASKCOM, ASKADR,
                          ASKPHO, ASKSYS, ASKBDY,
                          ASKSEX, SAVINF, SUPFRM,
                          -1};

// array of bools corresponding to option values

static bool g_askopts[NMQSTS];

// array of supplemental question message options

static INT g_supqarr[] = { SUPQS1, SUPQS2, SUPQS3,
                           SUPQS4, SUPQS5, SUPQS6,
                           SUPQS7, SUPQS8, SUPQS9,
                           SUPQ10, -1 };

// array of supplemental question strings

static CHAR* g_supstgs[NMQSTS];

static CHAR* g_suphdr=NULL;        // supplemental question header
static CHAR* g_supend=NULL;        // supplemental question trailer
bool b_supUsed=false;              // were supplemental questions used?

dnfStep supsteps[]={
     dnfStep(DNFTABLE),
          dnfStep(DNFCOLUMN,LISTITEM,"LISTITEM"),
     dnfStep(DNFTABLEEND),
     dnfStep(DNFMAPEND)
};

dnfStep welsteps[]={
     dnfStep(DNFMAPEND)
};

dnfStep errSteps[]={
     dnfStep(DNFMAPEND)
};

dnfMap errMap(PPFIX "/error.htm","Error in Signup",errSteps);
dnfMap supMap(PPFIX "/index.htm","Sign-up form",supsteps);
dnfMap welMap(PPFIX "/welcome.htm","Sign-up welcome",welsteps);

enum SUPDOING {                    // signup phases
     SUPBEGIN,                     // beginning, processing form
     SUPSTORE,                     // storing info into database
     SUPEXTING,                    // storing extended account info
     SUPADDING,                    // save addittional qs
     SUPMSGING,                    // sending welcome message
     SUPBEGRESP,                   // begin success response
     SUPRESP                       // welcoming aboard
};

class supAgent : public acthAgent { // Signup agent
public:
     supAgent() : acthAgent("Signup Agent","signup")
     {
          registerAgent(acthVersion);
          getMsgOptions();
          setVariables();
     }

     ~supAgent()
     {
          freeStgAllocs();
     }

     VOID
     getMsgOptions();              // get message options

     VOID
     freeStgAllocs();              // free up allocatoins

     acthSynthesis *               // my session info (NULL=can't)
     newSynthesis(                 // instantiate my Synthesis class
     acthSession *ses);            //   for passing to acthSynthesis's ctor

     VOID
     setVariables();               // setup text variables
};

class supSynthesis : public acthSynthesis { // Signup response synthesis

public:

     VOID
     nullifyArr();

     supSynthesis(acthSession *_ses) :
          acthSynthesis(_ses),
          m_dnfPtr(NULL),
          supdoing(SUPBEGIN),
          m_err(ERR_NOERROR),
          m_errparam(""),
          m_errlength(""),
          m_thisQuestion(""),
          m_thisName(""),
          m_succRedir(false),
          m_errRedir(false),
          m_onsuccessFile(NULL),
          m_errParam(NULL),
          m_onerrorFile(DFTERROR),
          m_WelMsg(NULL),
          m_templInit(false)
     {
          nullifyArr();
          m_cyc.setSlice(timslc);
     }

     ~supSynthesis()               // destructor
     {
          if (supdoing == SUPMSGING) {
               ::clsgmerq(m_gmeWork);
          }
          if (m_dnfPtr != NULL) {
               delete m_dnfPtr;
          }
          if (m_onsuccessFile != NULL) {
               delete [] m_onsuccessFile;
          }
          if (m_errParam != NULL) {
               delete [] m_errParam;
          }
          if (m_WelMsg != NULL) {
               delete [] m_WelMsg;
          }
     }

     ACTHCODE                      //   enumerated response (see galacth.h)
     proceed();                    // process request, synthesize response

     VOID
     abort();                      // kernel -> agent, request died
                                   //   (expect no more proceed() calls)

private:

     bool                          //   true=ok, false=error, explained
     okName();                     // check name form entry

     bool                          //   true=ok, false=error, explained
     okCompany();                  // check company form entry

     bool                          //   true=ok, false=error, explained
     okAddr1();                    // check address line 1 form entry

     bool                          //   true=ok, false=error, explained
     okAddr2();                    // check address line 2 form entry

     bool                          //   true=ok, false=error, explained
     okCountry();                  // check country form entry

     bool                          //   true=ok, false=error, explained
     okPhone();                    // check phone number form entry

     bool                          //   true=ok, false=error, explained
     okBday();                     // check birthday form entry

     bool
     okAltEmail();                 // check alternate email form entry

     bool
     isValidEmail(                 // is this a valid email address?
     CHAR const * pEmail);         //   address to check

     bool
     okPwdHint();                  // check password hint form entry

     bool                          //   true=ok, false=error, explained
     okUidPsw(                     // check User-ID form entry
     CHAR *psw2);                  //   second password

     bool                          //   true=ok, false=error, explained
     okField(                      // general purpose form field tester
     CHAR *data,                   //   entered data
     INT minlen,                   //   minimum length (0=no min)
     INT maxlen,                   //   maximum length
     CHAR *called);                //   what this field is called

     bool                          //   true if we found a parameter
     getParam(                     // get a session parameter, trimmed
     const CHAR *pname,            //   parameter to search for
     CHAR *pval,                   //   left/right trimmed value (returned)
     INT pbufLen);                 //   value buffer length

     VOID
     initTemplates();              // init onsuccess/onerror

     ACTHCODE
     dynafile();                   // dynafile

     VOID
     saveAcctExt();                // save extend account info to database

     VOID
     saveAddSQtoFile();            // save additional questions to file

     VOID
     saveAddSQtoDb();              // save additional questions to database

     bool                          //   returns true when done
     sendWelcome();                // send welcome message to alternate email

     VOID
     getAddSQInfo();               // get additional signup question info

     INT
     translateError();             // translate error code to message file

     VOID
     setErrorVbs();                // set up error vbs

     bool
     handleSignupInfo();           // handle signup information

     VOID
     setVariables();               // set up our text variables

     VOID
     setVarsDefault();             // set variables to default

     bool                          //   is a redirct?
     getOnParam(                   // get onxxx parameter
     CHAR const * paramName,       //   parameter to get
     CHAR** pStorePtr);            //   store redir path here

     ACTHCODE                      //   continue synthesis?
     errorResponse(                // process an error
     dnfMap& map,                  //   map to use
     CHAR const * path);           //   path to template directory

     ACTHCODE                      //   continue synthesis?
     successResponse(              // process an onsuccess
     dnfMap& map,                  //   map to use
     CHAR const * path);           //   path to template directory

     dnfHandler *m_dnfPtr;         //   DynaFile handler
     usracc uacc;                  //   user account info
     INT supstt;                   //   morsup() state
     SUPDOING supdoing;            //   morsup() calling in progress
     CHAR tempBday[LDATSIZ];       //   temp buffer for birthdate
     CHAR addSupQ[NMQSTS][ANSSIZ]; //   array of additional signup questions
     CHAR saveUserIdBuf[UIDSIZ];   //   save user-id
     CHAR m_email[ALTEMLSIZ];      //   alternate email address
     CHAR m_hint[HINTSIZ];         //   password hint
     errorcodes m_err;             //   error code in effect
     string m_errparam;            //   saved error message
     string m_errlength;           //   additional error info
     cycleTimer m_cyc;             //   instance of cycle timer
     INT m_index;                  //   used in ISPARAM macro
     string m_thisQuestion;        //   store current supplemental question
     string m_thisName;            //   store current name
     bool m_templInit;             //   are templates initalized?
     bool m_succRedir;             //   redirect success response?
     bool m_errRedir;              //   redirect error response?
     CHAR* m_onsuccessFile;        //   ptr to store success redirect location
     CHAR* m_errParam;             //   contents of onerror parameter
     string m_onerrorFile;         //   error redirect location
     CHAR m_gmeWork[GMEWRKSZ];     //   GME work buffer
     struct message m_msg;         //   message header structure
     CHAR* m_WelMsg;               //   CNF WELMSG
};

supAgent thesupAgent;              // the one and only instance of supAgent

acthSynthesis *                    //   new session-specific structure
supAgent::newSynthesis(            // instantiate Synthesis class
acthSession *ses)                  //   passed to acthSynthesis's constructor
{
     return(new supSynthesis(ses));
}

ACTHCODE
supSynthesis::proceed()            // proceed with HTML synthesis
{
     ACTHCODE retval=ACTHMORE;

     if ((strchr(ses->urlsfx(),'.')) == NULL) {
          if (ses->forceDir()) {
               return(ACTHDONE);
          }
     }
     if (!m_templInit) {
          initTemplates();
          m_templInit=true;
     }
     setVarsDefault();
     if (m_err != ERR_NOERROR) {
          setErrorVbs();
     }
     if (!(*alwsup)()) {
          if (m_dnfPtr == NULL) {
               m_err=ERR_NOMORE;
               return(errorResponse(errMap,PPFIX "/"));
          }
          return(dynafile());
     }

     if (!m_cyc.start()) {
          return(retval);
     }

     struct usracc* savacc;  // NMH. maybe this should be under the Do { (like Joe had)
     do {
          switch (supdoing) {
          case SUPBEGIN:
               if (ISPARAM("signup")) {
                    if (handleSignupInfo()) {
                         supdoing=SUPSTORE;
                    }
                    else {
                         supdoing=SUPRESP;
                         return(errorResponse(errMap,PPFIX "/"));
                    }
               }
               else {
                    if (m_dnfPtr == NULL) {
                         m_dnfPtr=dnfCreateHandlerURL(ses,&supMap,PPFIX "/");
                    }
                    retval=dynafile();
               }
               break;
          case SUPSTORE:
               clingo=0;
               savacc=usaptr;
               usaptr=&uacc;
               supstt=morsup(supstt);
               //usaptr=savacc;                // NMH. Jack dropped, but may need this
               if (supstt == DUNINS) {
                    //savacc=usaptr;           // NMH
                    //usaptr=&uacc;            // NMH
                    endsup();
                    //usaptr=savacc;           // NMH
                    supdoing=SUPEXTING;
               }
               usaptr=savacc;                  // NMH. Jack added
               break;
          case SUPEXTING:
               saveAcctExt();
               supdoing=SUPADDING;
               break;
          case SUPADDING:
               savacc=usaptr;
               usaptr=&uacc;
               saveAddSQtoFile();
               saveAddSQtoDb();
               if (g_bSendWelcome && ::strlen(m_email) != 0) {
                    ::memset(m_gmeWork,0,GMEWRKSZ);
                    ::inigmerq(m_gmeWork);
                    ::memset(&m_msg,0,sizeof(struct message));
                    ::stlcpy(m_msg.from,g_msgFrom,MAXADR);
                    ::strcpy(m_msg.to,"IN:");
                    ::stlcat(m_msg.to,m_email,MAXADR);
                    ::setmbk(supAHMsg);
                    ::prfmsg(WELTPC);
                    ::stlcpy(m_msg.topic,::stpans(::prfbuf),TPCSIZ);
                    ::clrprf();
                    ::prfmsg(WELMSG,uacc.psword);
                    m_WelMsg = new CHAR[strlen(prfbuf)+1];
                    ::strcpy(m_WelMsg,::strrpl(::stpans(::prfbuf),'\n','\r'));
                    ::clrprf();
                    ::rstmbk();
                    supdoing=SUPMSGING;
               }
               else {
                    supdoing=SUPBEGRESP;
               }
               usaptr=savacc;
               break;
          case SUPMSGING:
               savacc=usaptr;
               usaptr=&uacc;
               if (sendWelcome()) {
                    supdoing=SUPBEGRESP;
               }
               usaptr=savacc;
               break;
          case SUPBEGRESP:
               {
                    supdoing=SUPRESP;
                    savacc=usaptr;
                    usaptr=&uacc;
                    ACTHCODE ahCode=successResponse(welMap,PPFIX "/");
                    usaptr=savacc;
                    return(ahCode);
               }
          case SUPRESP:
               if (m_err == ERR_NOERROR && b_supUsed && g_supend != NULL) {
                    t_SupEnd=stpans(g_supend);
               }
               if (m_err != ERR_NOERROR) {
                    setErrorVbs();
               }
               savacc=usaptr;
               usaptr=&uacc;
               retval=dynafile();
               usaptr=savacc;
               break;
          }
     } while (m_cyc.haveTime() && retval != ACTHDONE);

     return(retval);
}

bool
supSynthesis::handleSignupInfo()   // handle signup post
{
     setmem(&uacc,sizeof(uacc),0);

     if (namdft == 1 || (namdft == 2 && ISPARAM("realname"))) { // Use user names as userids
          getParam("NAME",uacc.userid,sizeof(uacc.userid));
     }
     else {
          getParam("USERID",uacc.userid,sizeof(uacc.userid));
     }
     stzcpy(saveUserIdBuf,uacc.userid,UIDSIZ);
     getParam("PASSWORD1",uacc.psword,sizeof(uacc.psword));
     CHAR psw2[PSWSIZ];
     getParam("PASSWORD2",psw2,sizeof(psw2));
     CHAR compbuf[20];
     getParam("COMPUTER",compbuf,sizeof(compbuf));
     istrstream sbuf(compbuf);
     UINT systpi;
     sbuf >> systpi;
     if (sbuf.fail() || systpi > 3) {
          uacc.systyp=0;
     }
     else {
          uacc.systyp=static_cast<CHAR>(systpi);
     }
     CHAR sexbuf[2];
     getParam("SEX",sexbuf,sizeof(sexbuf));
     uacc.sex=sexbuf[0];
     if (getParam("NAME",    uacc.usrnam,sizeof(uacc.usrnam)) && !okName()
      || getParam("COMPANY", uacc.usrad1,sizeof(uacc.usrad1)) && !okCompany()
      || getParam("ADDRESS1",uacc.usrad2,sizeof(uacc.usrad2)) && !okAddr1()
      || getParam("ADDRESS2",uacc.usrad3,sizeof(uacc.usrad3)) && !okAddr2()
      || getParam("COUNTRY", uacc.usrad4,sizeof(uacc.usrad4)) && !okCountry()
      || getParam("PHONE",   uacc.usrpho,sizeof(uacc.usrpho)) && !okPhone()
      || getParam("BIRTHDAY",tempBday,sizeof(tempBday)) && !okBday()
      || getParam("EMAIL",m_email,ALTEMLSIZ) && !okAltEmail()
      || getParam("HINT",m_hint,HINTSIZ) && !okPwdHint()
      || !okUidPsw(psw2)) {
          return(false);
     }
     else {
          uacc.scnwid=80;
          uacc.scnfse=uacc.scnbrk=24;
          if (ISPARAM("ANSION")) {
               uacc.ansifl|=ANSON;
          }
          supstt=1;
          getAddSQInfo();
     }
     return(true);
}

bool                               // true=ok, false=error, explained
supSynthesis::okName()             // check name form entry
{
     return(okField(uacc.usrnam,(namdft == 1 || namdft == 2 ? minuid : 5),
                    NADSIZ,"name"));
}

bool                               //   true=ok, false=error, explained
supSynthesis::okCompany()          // check company form entry
{
     return(okField(uacc.usrad1,0,NADSIZ,"company name"));
}

bool                               //   true=ok, false=error, explained
supSynthesis::okAddr1()            // check address line 1 form entry
{
     return(okField(uacc.usrad2,5,NADSIZ,"address line 1"));
}

bool                               //   true=ok, false=error, explained
supSynthesis::okAddr2()            // check address line 2 form entry
{
     return(okField(uacc.usrad3,0,NADSIZ,"address line 2"));
}

bool                               //   true=ok, false=error, explained
supSynthesis::okCountry()          // check country form entry
{
     return(okField(uacc.usrad4,0,NADSIZ,"country"));
}

bool                               //   true=ok, false=error, explained
supSynthesis::okPhone()            // check phone number form entry
{
     return(okField(uacc.usrpho,7,PHOSIZ,"phone number"));
}

bool                               //   true=ok, false=error, explained
supSynthesis::okBday()             // check birthday form entry
{
     stripb(tempBday);
     if (!okbday(tempBday)) {
          m_err=ERR_BDAY;
          return(false);
     }
     stlcpy(uacc.birthd,tempBday,DATSIZ);
     uacc.age=calcage(uacc.birthd);
     return(true);
}

bool
supSynthesis::okAltEmail()         // check alternate email form entry
{
     m_errparam="alternate email";
     bool bSupplied=(::strlen(m_email) != 0);
     if (g_bReqAltEmail && !bSupplied) {
          m_err=ERR_NOVAL;
          return(false);
     }
     if (bSupplied && !isValidEmail(m_email)) {
          m_err=ERR_INVEMAIL;
          return(false);
     }
     return(true);
}

bool
supSynthesis::isValidEmail(
CHAR const * pEmail)
{
     CHAR const * pAt=::strchr(pEmail,'@');
     CHAR const * pDot=::strrchr(pEmail,'.');
     CHAR const * pEnd=::strchr(pEmail,'\0');
     return(pAt >= (pEmail+1) && pDot > (pAt+1) && pEnd > (pDot+1));
}

bool
supSynthesis::okPwdHint()          // check password hint form entry
{
     bool bGood=(!g_bReqPwdHint || ::strlen(m_hint) != 0);
     if (!bGood) {
          m_err=ERR_NOVAL;
          m_errparam="password hint";
     }
     return(bGood);
}


bool                               //   true=ok, false=error, explained
supSynthesis::okUidPsw(            // check User-ID and password form entries
CHAR *psw2)                        //   second password
{
     bool  retval=false;

     if ((pfnlvl=profan(uacc.userid)) != 0) {
          if (pfnlvl > pfceil) {
               pfnlvl=pfceil;      // (can't call setpfn, it calls haskey())
          }
     }
     switch (valuid(uacc.userid)) {
     case 1:
          m_err=ERR_INVUID;
          break;
     default:
     case 2:
          m_err=ERR_UNVUID;
          break;
     case 3:
     case 4:
          m_err=ERR_ACCUID;
          break;
     case 5:
          if (strlen(uacc.userid) == 0) {
               m_err=ERR_NOUID;
          }
          else {
               m_err=ERR_TOOSHORT;
               m_errparam="User-ID";
               m_errlength=ltoa(MINUID);
          }
          break;
     case 6:
          m_err=ERR_TOOLONG;
          m_errparam="User-ID";
          m_errlength=ltoa(UIDSIZ-1);
          break;
     case 7:
          m_err=ERR_AIUUID;
          break;
     case 0:
          if (strlen(uacc.psword) == 0) {
               m_err=ERR_PASSNONE;
          }
          else if (strlen(uacc.psword) < minpwd) {
               m_err=ERR_TOOSHORT;
               m_errparam="password";
               m_errlength=ltoa(minpwd);
          }
          else if (strlen(uacc.psword) > PSWSIZ-1) {
               m_err=ERR_TOOLONG;
               m_errparam="password";
               m_errlength=ltoa(PSWSIZ-1);
          }
          else if (!safpsw && (sameas(uacc.userid,uacc.psword)
                            || sameas("PASSWORD",uacc.psword))) {
               m_err=ERR_PASSSEC;
          }
          else if (!sameas(uacc.psword,psw2)) {
               if (strlen(psw2) == 0) {
                    m_err=ERR_PASSBOTH;
               }
               else {
                    m_err=ERR_PASSMATCH;
               }
          }
          else {
               retval=true;
          }
          break;
     }
     return(retval);
}

bool                               //   true=ok, false=error, explained
supSynthesis::okField(             // general purpose form field tester
CHAR *data,                        //   entered data
INT minlen,                        //   minimum length (0=no min)
INT maxlen,                        //   maximum length
CHAR *called)                      //   what this field is called
{
     (*setpfn)(data);
     if (pfnlvl > 1) {
          m_err=ERR_PROFANE;
     }
     else if (strlen(data) < minlen) {
          if (strlen(data) == 0) {
               m_err=ERR_NOVAL;
               m_errparam=called;
          }
          else {
               m_err=ERR_TOOSHORT;
               m_errparam=called;
               m_errlength=ltoa(minlen);
          }
     }
     else if (strlen(data) >= maxlen) {
          m_err=ERR_TOOLONG;
          m_errparam=called;
          m_errlength=ltoa(maxlen);
     }
     else {
          return(true);
     }
     return(false);
}


VOID
supSynthesis::abort()              // kernel -> agent, request died
{                                  //   (expect no more proceed() calls)
     if (supdoing == SUPSTORE) {
          usaptr=&uacc;
          abosup(supstt);
          curusr(usrnum);
     }
}

bool                               //   true if we found a parameter
supSynthesis::getParam(            // get a session parameter, trimmed
const CHAR *pname,                 //   parameter to search for
CHAR *pval,                        //   left/right trimmed value (returned)
INT pbufLen)                       //   value buffer length
{
     bool  gotOne=ses->param( pname, pval, pbufLen );
     if (gotOne) {
          strmove(pval,unpad(skpwht(pval)));
     }
     return( gotOne );
}

VOID
supSynthesis::setVarsDefault()     // set text variables to default values
{
     t_SuppQuestion="";
     t_SuppQuesName="";
     t_ErrorMessage="";
}


VOID
supSynthesis::setVariables()       // set text variables up as appropriate
{
     t_SuppQuesName=m_thisName;
     t_SuppQuestion=m_thisQuestion;
     t_OnSuccess=(m_onsuccessFile == NULL ? "" : m_onsuccessFile);
     t_OnError=(m_errParam == NULL ? "" : m_errParam);
}


ACTHCODE
supSynthesis::dynafile()           // do dynafile processing
{
     ACTHCODE retval=ACTHMORE;

     setVariables();
     switch (m_dnfPtr->process()) {
     case DNFROWBEGIN:
          if (g_supstgs[m_dnfPtr->rowNumber()][0] != '\0') {
               m_thisName="ADDSUP";
               m_thisName+=spr("%d",(m_dnfPtr->rowNumber()+1));
               t_SuppQuesName=m_thisName;
               t_SuppQuestion=m_thisQuestion=g_supstgs[m_dnfPtr->rowNumber()];
          }
          else {
               t_SuppQuesName="";
               t_SuppQuestion="";
               m_thisName="";
               m_thisQuestion="";
               m_dnfPtr->tableDone();
          }
          break;
     case DNFEND:
          retval=ACTHDONE;
          break;
     }
     return(retval);
}


VOID
supSynthesis::saveAcctExt()        // save extend account info to database
{
     struct uaccext buf;
     ::stzcpy(buf.userid,uacc.userid,UIDSIZ);
     ::stzcpy(buf.alteml,m_email,ALTEMLSIZ);
     ::stzcpy(buf.pwdhint,m_hint,HINTSIZ);
     ::dfaSetBlk(::dfaUserExt);
     ::dfaInsertDup(&buf);
     ::dfaRstBlk();
}

VOID
supSynthesis::saveAddSQtoFile()    // save signup info to file
{
     INT i;
     FILE *suptxt;

     if ((suptxt=fopen("wgssup.txt",FOPAA)) == NULL)
     {
          shocst("CAN'T EXPORT SIGNUPS TO TEXT!",
                 "(ERROR: Can't open \"WGSSUP.TXT\" for updating!)");
          return;
     }
     if (g_askopts[8]) {
          fprintf(suptxt,"%-11s",ncdatel(today()));
          fprintf(suptxt,"%-9s",nctime(now()));
          fprintf(suptxt,"%-29s",usaptr->userid);
          if (g_askopts[7]) {
               fprintf(suptxt,"%-29s",usaptr->usrnam);
               fprintf(suptxt,"%-29s",usaptr->usrad1);
               fprintf(suptxt,"%-29s",usaptr->usrad2);
               fprintf(suptxt,"%-29s",usaptr->usrad3);
               fprintf(suptxt,"%-29s",usaptr->usrad4);
               fprintf(suptxt,"%-15s",usaptr->usrpho);
               fprintf(suptxt,"%-3d",usaptr->systyp);
               fprintf(suptxt,"%-3d",usaptr->scnwid);
               fprintf(suptxt,"%-3d",usaptr->scnbrk);
               fprintf(suptxt,"%-11s",strBirthdate(usaptr->birthd));
               fprintf(suptxt,"%c",usaptr->sex == '\0' ? ' ' : usaptr->sex);
          }
          for (i=0 ; i < NMQSTS && addSupQ[i][0] != '\0' ; i++) {
               fprintf(suptxt,"%-79s",addSupQ[i]);
          }
     }
     else {
          fprintf(suptxt,"------------------------------------------"
                         "-------------------------------------\n\n");
          fprintf(suptxt,"User-ID:  %-29s   Date:  %-10s  Time:  %-8s\n\n",
                         usaptr->userid,ncdatel(today()),nctime(now()));
          if (g_askopts[7]) {
               fprintf(suptxt,"Address:  %-29s   Name:  %-29s\n",
                       usaptr->usrad1,usaptr->usrnam);
               fprintf(suptxt,"          %-29s  Phone:  %-15s\n",
                       usaptr->usrad2,usaptr->usrpho);
               fprintf(suptxt,"          %-29s   Born:  %-11s\n",
                       usaptr->usrad3,strBirthdate(usaptr->birthd));
               fprintf(suptxt,"          %-29s\n",usaptr->usrad4);
               fprintf(suptxt," System type:  %-24s    Sex:  %c\n",
                       sysstg[(SHORT)usaptr->systyp],
                       usaptr->sex == '\0' ? ' ' : usaptr->sex);
               fprintf(suptxt,"Screen width:  %-24d Length:  %-3d\n",
                       usaptr->scnwid,usaptr->scnbrk);
          }

          fprintf(suptxt,"\n");
          fprintf(suptxt,"Answers to additional signup questions:\n\n");

          for (i=0 ; i < NMQSTS && addSupQ[i][0] != '\0' ; i++)
          {
               fprintf(suptxt,"%s\n",addSupQ[i]);
          }
     }
     fprintf(suptxt,"\n");
     fclose(suptxt);
}

VOID
supSynthesis::saveAddSQtoDb()      // save additional questions to database
{
     struct {
          CHAR userid[UIDSIZ];     // User-ID
          CHAR modnam[MNMSIZ];     // Module Name ("WGSSUP - AddlQues")
          CHAR answer[NMQSTS][ANSSIZ]; // answers to questions
     } buf;
     stzcpy(buf.userid,uacc.userid,UIDSIZ);
     stzcpy(buf.modnam,"WGSSUP - AddlQues",MNMSIZ);
     memcpy(buf.answer,addSupQ,sizeof(addSupQ));
     ::dfaSetBlk(::genbb);
     ::dfaInsertV(&buf,sizeof(buf));
     ::dfaRstBlk();
}

bool                               //   returns true when done
supSynthesis::sendWelcome()        // send welcome message to alternate email
{
     INT msgStatus=gsndmsg(m_gmeWork,&m_msg,m_WelMsg,NULL);
     if (msgStatus != GMEAGAIN) {
          ::clsgmerq(m_gmeWork);
          return(true);
     }
     return(false);
}

VOID
supSynthesis::nullifyArr()         // clear array
{
     for (INT i=0; i < NMQSTS; i++) {
         addSupQ[i][0] = '\0';
     }
}

VOID
supSynthesis::getAddSQInfo()       // get additional signup question answers
{
     CHAR qbuf[sizeof("ADDSUP10")];     //  NUMQSTS=10

     for (INT i=0; i < NMQSTS; i++) {
          sprintf (qbuf, "ADDSUP%d", i+1 );
          ses->param (qbuf , addSupQ[i], ANSSIZ );
     }
}

INT
supSynthesis::translateError()
{
     if (m_err > 0 && m_err < ERR_HIGHERROR) {
          return(errArray[m_err]);
     }
     m_errparam=spr("%d",(INT)m_err);
     return(errArray[ERR_UNRECOGNIZED]);
}

VOID
supSynthesis::setErrorVbs()        // set up error text variables
{
     setmbk(supAHMsg);
     clingo=0;
     prfmsg(translateError(),m_errparam.c_str(),m_errlength.c_str());
     t_ErrorMessage=prfbuf;
     clrprf();
     rstmbk();
}

VOID
supSynthesis::initTemplates()      //  init onsuccess/onerrror
{
     m_succRedir=getOnParam("onsuccess",&m_onsuccessFile);
     if (ses->param("onerror")) {
          INT sz=ses->paramRoom("onerror");
          if (sz > sizeof("")) {
               m_errParam=new CHAR[sz];
               ses->param("onerror",m_errParam,sz);
          }
     }
     m_onerrorFile=DFTERROR;
}

bool                               //   is a redirct?
supSynthesis::getOnParam(          // get onxxx parameter
CHAR const * paramName,            //   parameter to get
CHAR** pStorePtr)                  //   store redir path here
{
     if (ses->param(paramName)) {
          if (*pStorePtr != NULL) {
               delete [] *pStorePtr;
          }
          INT sz=ses->paramRoom(paramName);
          if (sz > sizeof("")) {
               *pStorePtr=new CHAR[sz];
               ses->param(paramName,*pStorePtr,sz);
               if (isProtocol(*pStorePtr) || **pStorePtr == '/') {
                    return(true);
               }
          }
     }
     else if (*pStorePtr != NULL) {
          delete [] *pStorePtr;
          *pStorePtr=NULL;
     }
     return(false);
}

ACTHCODE                           //   continue synthesis?
supSynthesis::errorResponse(       // process an error
dnfMap& map,                       //   map to use
CHAR const * path)                 //   path to template directory
{
     if (m_errParam != NULL) {
          for (int i=0 ; i < itemcntd(m_errParam,",") ; i++) {
               CHAR* errcs=itemidxd(m_errParam,i,",");
               CHAR* restr=strchr(errcs,' ');
               if (atoi(errcs) == 0 && restr == NULL) {
                    // found default redir
                    strmove(errcs,unpad(skpwht(errcs)));
                    if (strlen(errcs) > 0) {
                         m_onerrorFile=errcs;
                    }
               }
               else if (atoi(errcs) == m_err && restr != NULL) {
                    strmove(restr,unpad(skpwht(restr)));
                    if (strlen(restr) > 0) {
                         m_onerrorFile=restr;
                         break;
                    }
               }
          }

     }
     m_errRedir=(isProtocol(m_onerrorFile.c_str()) || m_onerrorFile[0] == '/');
     if (m_errRedir) {
          ses->redirect(m_onerrorFile.c_str());
          return(ACTHDONE);
     }
     ASSERT(path != NULL);
     string dest=path;
     dest+=m_onerrorFile;
     m_dnfPtr=dnfCreateHandler(&bout,&map,dest.c_str());
     return(ACTHMORE);
}

ACTHCODE                           //   continue synthesis?
supSynthesis::successResponse(     // process an onsuccess
dnfMap& map,                       //   map to use
CHAR const * path)                 //   path to template directory
{
     if (m_succRedir) {
          ses->setStatus("302 Redirect");
          ses->headerField("Location",m_onsuccessFile);
          ses->setUser(uacc.userid,uacc.psword);
          bout << "<html>" << crlf
               << "<head><title>Request Redirected</title></head>" << crlf
               << "<body>" << crlf
               << "Request redirected to: <a href=\""
               << m_onsuccessFile << "\">"
               << m_onsuccessFile << "</a>" << crlf
               << "</body></html>";
          return(ACTHDONE);
     }
     ASSERT(path != NULL);
     string dest=path;

     dest+=(m_onsuccessFile == NULL ? DFTSUCCESS : m_onsuccessFile);
     m_dnfPtr=dnfCreateHandler(&bout,&map,dest.c_str());
     ses->setUser(uacc.userid,uacc.psword);
     return(ACTHMORE);
}

VOID
supAgent::getMsgOptions()          // get message options
{
     HMCVFILE supmsg=opnmsg("WGSSUP.MCV");
     setmbk(supmsg);
     for (int i = 0 ; g_askarr[i] != -1 ; i++) {
          g_askopts[i] = ynopt(g_askarr[i]);
     }
     for ( int j = 0 ; g_supqarr[j] != -1 ; j++ ) {
          g_supstgs[j] = stpans(stgopt(g_supqarr[j]));
          if (strlen(g_supstgs[j]) == 0) {
               g_supstgs[j][0]='\0';
               break;
          }
          else {
               b_supUsed = true;
          }
     }
     if (b_supUsed) {
          g_suphdr = stpans(stgopt(SUPHDR));
          g_supend = stpans(stgopt(SUPEND));
     }
     rstmbk();
     clsmsg(supmsg);

     supAHMsg=opnmsg("GALSUPAH.MCV");
     setmbk(supAHMsg);
     g_bReqAltEmail=static_cast<bool>(ynopt(RQALTEML));
     g_bReqPwdHint=static_cast<bool>(ynopt(RQPWHINT));
     g_bSendWelcome=static_cast<bool>(ynopt(SENDWEL));
     if (g_bSendWelcome) {
          ::clrprf();
          ::prfmsg(WELFROM);
          ::stlcpy(g_msgFrom,::stpans(::prfbuf),MAXADR);
     }
     namehtml=stpans(stgopt(NAMEHTML));
     comhtml=stpans(stgopt(COMHTML));
     adr1html=stpans(stgopt(ADR1HTML));
     adr2html=stpans(stgopt(ADR2HTML));
     ctryhtml=stpans(stgopt(CTRYHTML));
     phohtml=stpans(stgopt(PHOHTML));
     sexhtml=stpans(stgopt(SEXHTML));
     phohtml=stpans(stgopt(PHOHTML));
     comphtml=stpans(stgopt(COMPHTML));
     bdayhtml=stpans(stgopt(BDAYHTML));
     userhtml=stpans(stgopt(USERHTML));
     userchk=stpans(stgopt(USERCHK));
     altemlhtml=stpans(stgopt(ALTEHTML));
     hinthtml=stpans(stgopt(HINTHTML));
     timslc=numopt(TIMSLC,1,1000);
     rstmbk();
}

VOID
supAgent::setVariables()           // set text variables up as appropriate
{
     if (g_askopts[QUES_NAME]) {
          t_Name=namehtml;
     }
     if (g_askopts[QUES_COMPANY]) {
          t_Company=comhtml;
     }
     if (g_askopts[QUES_ADDRESS]) {
          t_Address1=adr1html;
          t_Address2=adr2html;
          t_Country=ctryhtml;
     }
     if (g_askopts[QUES_PHONE]) {
          t_Phone=phohtml;
     }
     if (g_askopts[QUES_COMPUTER]) {
          t_Computer=comphtml;
     }
     if (g_askopts[QUES_DOB]) {
          t_Birthdate=bdayhtml;
     }
     if (g_askopts[QUES_SEX]) {
          t_Sex=sexhtml;
     }
     if (g_suphdr != NULL) {
          t_SuppHeader=g_suphdr;
     }
     if (namdft == 2) {
          t_UserID=userhtml;
          t_UserNameCheck=userchk;
     }
     else if (namdft == 3) {
          t_UserID=userhtml;
     }
     t_PwdHint=hinthtml;
     t_AltEmail=altemlhtml;
}

VOID
supAgent::freeStgAllocs()          // free allocated memory
{
     for ( int j = 0 ; g_supqarr[j] != -1 && g_supstgs[j][0] != '\0' ; j++ ) {
          free ( g_supstgs[j] );
     }
     if ( g_suphdr != NULL ) {
          free ( g_suphdr );
     }
     if ( g_supend != NULL ) {
          free ( g_supend );
     }
     free(namehtml);
     free(comhtml);
     free(adr1html);
     free(adr2html);
     free(ctryhtml);
     free(sexhtml);
     free(phohtml);
     free(comphtml);
     free(bdayhtml);
     free(userhtml);
     free(userchk);
}



