/***************************************************************************
 *                                                                         *
 *   REGSYN.CPP                                                            *
 *                                                                         *
 *   Copyright (c) 1998       Galacticomm, Inc.    All Rights Reserved.    *
 *                                                                         *
 *   Active HTML Registry                                                  *
 *   Implementation                                                        *
 *                                                                         *
 *                                                - Phil Henning 8/20/98   *
 *                                                                         *
 ***************************************************************************/

#pragma option -w-par
#define _RWSTDLL
#include <cstring.h>
#include <deque.h>
using namespace std;
#include "gcomm.h"
#include "majorbbs.h"
#include "galacth.h"
#include "dnf.h"
#include "dnfmgr.h"
#include "registry.h"
#include "regsyn.h"
#include "regtvb.h"
#include "cyctimer.h"
#include "ahusrxrf.h"
#include "ahutil.h"
#include "galregis.h"
#include "galregah.h"

#define FILREV "$Revision: 12 $"
#define PPFIX "galacth/galregah"

#define DFTSUCCESS "index.htm"          // default success page file name
#define DFTERROR   "error.htm"          // default error page file name

#define REGFRAMECOOKIE "regframe"       // frame cookie name

enum {RLISTITEM};

HMCVFILE regahmb;                       // Active HTML registry message file

// DynaFile Steps and Maps
dnfStep dnfFrameSteps[]={
     dnfStep(DNFMAPEND)
};
dnfMap regFrameMap(PPFIX "/indexf.htm","Frames Response",dnfFrameSteps);

dnfStep dnfNoFrameSteps[]={
     dnfStep(DNFMAPEND)
};
dnfMap regNoFrameMap(PPFIX "/indexn.htm","NoFrames Response",dnfNoFrameSteps);

dnfStep dnfFileSteps[]={
     dnfStep(DNFMAPEND)
};
dnfMap regFileMap(PPFIX "/file/index.htm","Generic File Response",dnfFileSteps);

dnfStep dnfFileErrorSteps[]={
     dnfStep(DNFMAPEND)
};
dnfMap regFileErrorMap(PPFIX "/file/error.htm","Generic File Error",dnfFileErrorSteps);

dnfStep dnfListSteps[]={
     dnfStep(DNFTABLE),
          dnfStep(DNFCOLUMN,RLISTITEM,"LISTITEM"),
     dnfStep(DNFTABLEEND),
     dnfStep(DNFMAPEND)
};
dnfMap regListMap(PPFIX "/list/index.htm","List Registries",dnfListSteps);

dnfStep dnfListErrorSteps[]={
     dnfStep(DNFMAPEND)
};
dnfMap regListErrorMap(PPFIX "/list/error.htm","List Registries Error",dnfListErrorSteps);

dnfStep dnfViewSteps[]={
     dnfStep(DNFTABLE),
          dnfStep(DNFCOLUMN,RLISTITEM,"LISTITEM"),
     dnfStep(DNFTABLEEND),
     dnfStep(DNFMAPEND)
};
dnfMap regViewMap(PPFIX "/view/index.htm","View Registry",dnfViewSteps);

dnfStep dnfViewNoTableSteps[]={
     dnfStep(DNFMAPEND)
};
dnfMap regViewNoTableMap(PPFIX "/list/index.htm","View Registry Error",dnfViewNoTableSteps);

dnfStep dnfViewErrorSteps[]={
     dnfStep(DNFMAPEND)
};
dnfMap regViewErrorMap(PPFIX "/list/error.htm","View Registry Error",dnfViewErrorSteps);

dnfStep dnfEditSteps[]={
     dnfStep(DNFMAPEND)
};
dnfMap regEditMap(PPFIX "/edit/index.htm","Edit Registry Error",dnfEditSteps);

dnfStep dnfEditErrorSteps[]={
     dnfStep(DNFMAPEND)
};
dnfMap regEditErrorMap(PPFIX "/edit/error.htm","Edit Registry Error",dnfEditErrorSteps);

dnfStep dnfDeleteSteps[]={
     dnfStep(DNFMAPEND)
};
dnfMap regDeleteMap(PPFIX "/delete/index.htm","Delete Registry Error",dnfDeleteSteps);

dnfStep dnfDeleteErrorSteps[]={
     dnfStep(DNFMAPEND)
};
dnfMap regDeleteErrorMap(PPFIX "/delete/error.htm","Delete Registry Error",dnfDeleteErrorSteps);


typedef deque<struct regrec> REGQUE;    // queue to store registries

class regAgent : public acthAgent {
public:
     regAgent() : acthAgent("Active HTML Registry","registry")
                , m_hregkey(NULL)
                , m_nousrinfo(NULL)
     {
          registerAgent(acthVersion);
          initAgent();
     }

     ~regAgent()
     {
          finishAgent();
     }

     acthSynthesis*
     newSynthesis(
     acthSession* ses);
public:
     bool m_anonl;                      // allow anon listings
     bool m_anonv;                      // allow anon viewings
     bool m_frameset;                   // use frames by default
     bool m_authdft;                    // require authentication for dft URL
     bool m_tbldft;                     // use tables by default for view
     CHAR* m_hregkey;                   // key to access HTML registry
     CHAR* m_nousrinfo;                 // string for Java tablist-"no usr info"
     INT m_maxlst;                      // max number of entries in list
     INT m_dftlst;                      // dft number of entries in list
     INT m_timeslice;                   // timeslice for generating resp

private:
     VOID
     initAgent();                       // initialize registry options

     VOID
     finishAgent();                     // cleanup agent for shutdown

     VOID
     initMcvOptions();                  // initialize MCV file options
};

regAgent* theRegAgent;

class regBaseSyn : public acthSynthesis {
public:
     regBaseSyn(acthSession* _ses) : acthSynthesis(_ses)
                                    , m_SynState(SYNSTATE_REQUEST)
                                    , m_timer(theRegAgent->m_timeslice)

     {
     }

     virtual
     ~regBaseSyn()
     {
     }

     ACTHCODE
     proceed();                         // acthSynthesis proceed implementation

protected:
     virtual
     ACTHCODE
     proceedRequest()=0;                // setup request context

     virtual
     ACTHCODE
     proceedSynth()=0;                  // synthesise the response

     virtual
     ACTHCODE
     proceedResponse()=0;               // output the response

     bool
     startProc();                       // cycle timer ok to start

     bool
     contProc(                          // cycle timer ok to continue
     ACTHCODE rc);                      //  return code so far


     enum synstate {SYNSTATE_REQUEST,SYNSTATE_SYNTH,SYNSTATE_RESPONSE};

     enum synstate m_SynState;          // request state
     cycleTimer m_timer;                // cycle timer object
};

class regInvalidURLSyn : public acthSynthesis {
public:
     regInvalidURLSyn(acthSession* _ses) : acthSynthesis(_ses)
     {
     }

     ~regInvalidURLSyn()
     {
     }

     ACTHCODE
     proceed();                         // acthSynthesis proceed implementation
};

class regOldSearchURLSyn : public acthSynthesis { // for backward compat
public:
     regOldSearchURLSyn(acthSession* _ses) : acthSynthesis(_ses)
     {
     }

     ~regOldSearchURLSyn()
     {
     }

     ACTHCODE
     proceed();                         // acthSynthesis proceed implementation
};

class regTeleTabListSyn : public acthSynthesis {  // for backward compat
public:
     regTeleTabListSyn(acthSession* _ses) : acthSynthesis(_ses)
     {
     }

     ~regTeleTabListSyn()
     {
     }

     ACTHCODE
     proceed();                         // acthSynthesis proceed implementation
};

class regBaseAuthSyn : public regBaseSyn {   // registry HTML base class
public:
     regBaseAuthSyn(
     acthSession* _ses,
     bool anonok,
     CHAR const * reqkey,
     INT lvlcnt=0 ) : regBaseSyn(_ses)
                    , m_anonok(anonok)
                    , m_lvlcnt(lvlcnt)
                    , m_succRedir(false)
                    , m_errRedir(false)
                    , m_onsuccessFile(NULL)
                    , m_onerrorFile(NULL)
                    , m_inframes(false)
                    , m_reqKey(reqkey)
                    , m_err(RERR_NOERROR)
                    , m_SynSubState(SYNSUB_BUILDMAP)
                    , m_dnf(NULL)


     {
          memset(&m_usaptr,0,sizeof(struct usracc));
     }

     virtual
     ~regBaseAuthSyn()
     {
          if (m_dnf != NULL) {
               delete m_dnf;
          }
          if (m_onsuccessFile != NULL) {
               delete [] m_onsuccessFile;
          }
          if (m_onerrorFile != NULL) {
               delete [] m_onerrorFile;
          }
     }

protected:
     virtual
     ACTHCODE
     proceedRequest();                  // setup request context

     virtual
     ACTHCODE
     proceedSynth()=0;                  // synthesise the response

     virtual
     ACTHCODE
     proceedDerivedResponse()=0;        // output response

     ACTHCODE                           //   continue synthesis?
     errorResponse(                     // an error was encountered
     dnfMap& map,                       //   map to use for error response
     CHAR const * path);                //   path to the template directory

     ACTHCODE                           //   continue synthesis?
     successResponse(                   // process onsuccess parameter
     dnfMap& map,                       //   map to use for error response
     CHAR const * path);                //   path to the template directory

     bool m_anonok;                     // anon access is ok
     INT  m_lvlcnt;                     // how deep is this url (/forums/view/)
     acthUserID* m_usr;                 // the current user
     struct usracc m_usaptr;            // user account structure
     bool m_succRedir;                  // redirect on success?
     bool m_errRedir;                   // redirect on error?
     CHAR* m_onsuccessFile;             // path to redirect onsuccess to
     CHAR* m_onerrorFile;               // path to redirect onerror to
     bool m_inframes;                   // is this request inframes?
     CHAR const * m_reqKey;             // key required to access URL
     errorcodes m_err;                  // error status
     enum synsubstate {SYNSUB_BUILDMAP,SYNSUB_LISTCONTEXT};
     enum synsubstate m_SynSubState;    // substate of request synthesis
     dnfHandler* m_dnf;                 // dnf Handler

private:
     virtual
     ACTHCODE
     proceedResponse();                 // output the response

     VOID
     initTemplates();                   // init onerror/onsuccess templates

     bool                               //   should we redirect?
     getOnParam(                        // get onsuccess/onerror parameter
     CHAR const * paramName,            //   name of param we're getting
     CHAR const * dftFileName,          //   default file name to use
     CHAR** pStorePtr);                 //   buffer to store param value

     VOID
     setusaptr();                       // set the current user acc pointer
};

class regInitialSyn : public regBaseAuthSyn {     // default URL
public:
     regInitialSyn(
     acthSession* _ses,
     bool anonok,
     INT lvlcnt=0) : regBaseAuthSyn(_ses,anonok,theRegAgent->m_hregkey,lvlcnt)
     {
     }

     ~regInitialSyn()
     {
     }

protected:
     ACTHCODE
     proceedRequest();

     ACTHCODE
     proceedSynth();

     ACTHCODE
     proceedDerivedResponse();
};

class regFileSyn : public regBaseAuthSyn {   // generic file output
public:
     regFileSyn(
     acthSession* _ses,
     bool anonok,
     INT lvlcnt=0) : regBaseAuthSyn(_ses,anonok,theRegAgent->m_hregkey,lvlcnt)
     {
     }

     ~regFileSyn()
     {
     }

protected:
     ACTHCODE
     proceedRequest();                  // setup request context

     ACTHCODE
     proceedSynth();                    // synthesise the response

     ACTHCODE
     proceedDerivedResponse();          // output the response
};

class regListSyn : public regBaseAuthSyn {   // registry list
public:
     regListSyn(
     acthSession* _ses,
     bool anonok,
     INT lvlcnt=0)  : regBaseAuthSyn(_ses,anonok,theRegAgent->m_hregkey
                                          ,lvlcnt)
                    , m_bAll(false)
                    , m_iCount(theRegAgent->m_dftlst)
                    , m_iOrder(ORDER_DESCENDING)
                    , m_iDirection(DIR_GT|DIR_EQ)
                    , m_cnt(0)
                    , m_bIsMoreLow(false)
                    , m_bIsMoreHigh(false)
                    , m_usrxrf(NULL)

     {
          *m_start='\0';
          *m_search='\0';
          *m_pszHighName='\0';
          *m_pszLowName='\0';
          memset(&m_userReg,0,sizeof(struct regrec));
     }

     ~regListSyn()
     {
          if (!m_rque.empty()) {
               emptyque();
          }
          if (m_usrxrf != NULL) {
               delete m_usrxrf;
          }
     }

protected:
     ACTHCODE
     proceedRequest();                  // setup request context

     ACTHCODE
     proceedSynth();                    // synthesise the response

     ACTHCODE
     proceedDerivedResponse();          // output the response

private:

     VOID
     proceedSearch();                   // proceed with search

     VOID
     proceedNoSearch();                 // proceed with list

     VOID
     addToQueue(                        //   add registry entry to queue
     const struct regrec& ptr);         // registry info to add

     bool                               //   there is another registry
     getRegInfo();                      // move next registry into m_userReg

     VOID
     setupVars();                       // setup variables for response

     VOID
     emptyque();                        // empty the registry queue

     enum {ORDER_ASCENDING,ORDER_DESCENDING};
     enum {DIR_GT=1,DIR_LT=2,DIR_EQ=4};

     bool m_bAll;                       // list all entries?
     bool m_bIsMoreLow;                 // more entries before start
     bool m_bIsMoreHigh;                // more entries after start
     INT m_iCount;                      // number of entries to output
     INT m_iDirection;                  // direction to list
     INT m_iOrder;                      // order to output
     INT m_cnt;                         // actual number of entries
     CHAR m_start[UIDSIZ];              // start at User-ID
     CHAR m_search[UIDSIZ];             // search on this User-ID
     CHAR m_pszHighName[UIDSIZ];        // high User-ID listed
     CHAR m_pszLowName[UIDSIZ];         // low User-ID listed
     REGQUE m_rque;                     // queue of registry entries
     struct regrec m_userReg;           // registry record
     acthUserXrf* m_usrxrf;             // user cross reference object
};

class regViewSyn : public regBaseAuthSyn {   // view registry
public:
     regViewSyn(
     acthSession* _ses,
     bool anonok,
     INT lvlcnt=0)  : regBaseAuthSyn(_ses,anonok,::viewkey,lvlcnt)
                    , m_iDirection(DIR_EQ)
                    , m_bNoTable(!(theRegAgent->m_tbldft))
                    , m_bEdit(false)
                    , m_bIsMoreLow(false)
                    , m_bIsMoreHigh(false)
     {
          *m_userid='\0';
          memset(&m_userReg,0,sizeof(struct regrec));
     }

     ~regViewSyn()
     {
     }

protected:
     ACTHCODE
     proceedRequest();                  // setup request context

     ACTHCODE
     proceedSynth();                    // synthesise the response

     ACTHCODE
     proceedDerivedResponse();          // output the response

private:

     VOID
     setupVars();                       // setup variables for response

     enum {DIR_GT=1,DIR_LT=2,DIR_EQ=4};

     bool m_bIsMoreLow;                 // more entries before
     bool m_bIsMoreHigh;                // more entries after
     bool m_bNoTable;                   // don't use dnftable for questions
     bool m_bEdit;                      // is this an edit request?
     INT m_iDirection;                  // direction to view from
     CHAR m_userid[UIDSIZ];             // use this userid
     struct regrec m_userReg;           // registry record
};

class regEditSyn : public regBaseAuthSyn {  // edit registry
public:
     regEditSyn(
     acthSession* _ses,
     bool anonok,
     INT lvlcnt=0)  : regBaseAuthSyn(_ses,anonok,::regkey,lvlcnt)
                    , m_answer(NULL)
                    , m_bIsUpdate(false)
     {
          memset(&m_userReg,0,sizeof(struct regrec));
          m_answer=new CHAR* [numfds];
          initAnswerArray();
          *m_summary='\0';
     }

     ~regEditSyn()
     {
          deleteAnswerArray();
     }

protected:
     ACTHCODE
     proceedRequest();                  // setup request context

     ACTHCODE
     proceedSynth();                    // synthesise the response

     ACTHCODE
     proceedDerivedResponse();          // output the response

private:

     VOID
     setupVars();                       // setup variables for response

     VOID
     deleteAnswerArray();               // delete the answer array

     VOID
     initAnswerArray();                 // initialize the answer array

     struct regrec m_userReg;           // registry record
     CHAR** m_answer;                   // array of answers
     CHAR m_summary[SUMSIZ];            // summary answer
     bool m_bIsUpdate;                  // is this an update or new registry?
     bool m_bHadSummary;                // was a summary question submitted
};

class regDeleteSyn : public regBaseAuthSyn {  // Delete a registry
public:
     regDeleteSyn(
     acthSession* _ses,
     bool anonok,
     INT lvlcnt=0)  : regBaseAuthSyn(_ses,anonok,"",lvlcnt)
     {
          *m_userid='\0';
     }

     ~regDeleteSyn()
     {
     }

protected:
     ACTHCODE
     proceedRequest();                  // setup request context

     ACTHCODE
     proceedSynth();                    // synthesise the response

     ACTHCODE
     proceedDerivedResponse();          // output the response

private:

     VOID
     setupVars();                       // setup variables for response

     CHAR m_userid[UIDSIZ];             // User-ID's registry to delete
};


MARKSOURCE(regsyn);


extern "C" VOID EXPORT
init__galregah(VOID)                    // standard init routine
{
     theRegAgent=new regAgent;
}

VOID
regAgent::initAgent()                   // registry agent initialization
{
     static bool init=false;
     if (init) {
          return;
     }
     init__galreg();
     ::regahmb=::opnmsg("GALREGAH.MCV");
     ::initMcvText();
     initMcvOptions();
     ::initQuestionTxv(numfds);
     ::setAllQuestions();
     init=true;
}

VOID
regAgent::finishAgent()                 // shutdown the agent
{
     if (m_hregkey != NULL) {
          free(m_hregkey);    // allocated with stgopt
     }
     if (m_nousrinfo != NULL) {
          free(m_nousrinfo);  // allocated with stgopt
     }
     if (g_pszAnchor != NULL) {
          free(g_pszAnchor);
     }
     ::finishMcvText();
     ::finishQuestionTxv();
     ::clsmsg(regahmb);
}

VOID
regAgent::initMcvOptions()              // init MCV file options
{
     ::setmbk(::regahmb);
     m_anonl=::ynopt(REGANONL);
     m_anonv=::ynopt(REGANONV);
     m_authdft=::ynopt(AUTHDFT);
     m_tbldft=::ynopt(TBLDFT);
     m_hregkey=::stgopt(HREGKEY);
     m_nousrinfo=::stpans(::stgopt(NOUSRIN));
     m_maxlst=::numopt(MAXLSTC,1,32767);
     m_dftlst=::numopt(DFTLSTC,1,32767);
     m_timeslice=(USHORT)numopt(TIMSLC,1,1000);
     ::g_pszAnchor=::stpans(::stgopt(ANCHOR));
     m_frameset=::tokopt(RPRFFRM,"NO-FRAMES","FRAMES",NULL);
     ::rstmbk();
}

acthSynthesis*                          //   pointer to new synthesis obj
regAgent::newSynthesis(                 // create a new registry synthesis
acthSession* ses)                       //   acthSession ptr
{
     INT narg=ses->urlargc();

     if (narg > 0) {
          if (sameas(ses->urlargv(0),"file")) {
               return(new regFileSyn(ses,true,1));
          }
          if (sameas(ses->urlargv(0),"list")) {
               return(new regListSyn(ses,theRegAgent->m_anonl,1));
          }
          if (sameas(ses->urlargv(0),"view")) {
               return(new regViewSyn(ses,theRegAgent->m_anonv,1));
          }
          if (sameas(ses->urlargv(0),"edit")) {
               return(new regEditSyn(ses,false,1));
          }
          if (sameas(ses->urlargv(0),"delete")) {
               return(new regDeleteSyn(ses,false,1));
          }
          if (sameas(ses->urlargv(0),"search") && ses->param("userid")) {
               return(new regOldSearchURLSyn(ses)); // compatability
          }
          if (sameas(ses->urlargv(0),"tablist") && ses->urlargc() > 1) {
               return(new regTeleTabListSyn(ses));   // compatability
          }
     }
     if (narg == 0 || (narg == 1 && strchr(ses->urlargv(0),'.') != 0)) {
          return(new regInitialSyn(ses,!m_authdft,0));
     }
     return(new regInvalidURLSyn(ses));
}

//
// Base Synthesis Member Functions
//

ACTHCODE
regBaseSyn::proceed()              // proceed with synthesis
{
     ACTHCODE rc=ACTHMORE;
     if (startProc()) {
          do {
               switch (m_SynState) {
               case SYNSTATE_REQUEST:
                    rc=proceedRequest();
                    break;
               case SYNSTATE_SYNTH:
                    rc=proceedSynth();
                    break;
               case SYNSTATE_RESPONSE:
                    rc=proceedResponse();
                    break;
               }
          } while (contProc(rc));
     }
     return(rc);
}

bool
regBaseSyn::startProc()                 // cycle timer start
{
     return(m_timer.start());
}

bool
regBaseSyn::contProc(                   // cycle timer ok to continue
ACTHCODE rc)
{
     return(rc == ACTHMORE && m_timer.haveTime());
}

ACTHCODE
regBaseAuthSyn::proceedRequest()        // get common params, check authentication
{
     ACTHCODE rc=ACTHMORE;
     if (ses->urlargc() == m_lvlcnt && ses->forceDir()) {
          rc=ACTHDONE;
     }
     else if ((m_usr=ses->getUser()) == NULL && !m_anonok) {
          rc=ACTHNOANON;
     }
     else if (m_usr != NULL && !m_usr->hasKey(m_reqKey)) {
          m_err=RERR_NOACCESS;
     }
     initTemplates();
     const CHAR* value=getCookie(REGFRAMECOOKIE,ses);
     m_inframes=(value != NULL && sameas(value,"1"));
     return(rc);
}

ACTHCODE
regBaseAuthSyn::proceedResponse()  // proceed with response
{
     ACTHCODE rc;
     clearAllTVars();
     if (m_usr != NULL) {
          setusaptr();
          setHasCompletedTvb(m_usr->userid());
     }
     regIsAuth=(INT)((m_usr != NULL));
     setTarget(m_inframes);
     setErrorVars(m_err);
     dnfSetTemplateTvb(m_dnf);
     rc=proceedDerivedResponse();
     usaptr=uacoff(usrnum);
     clearAllTVars();
     return(rc);
}

VOID
regBaseAuthSyn::setusaptr()             // set usaptr for use with global Tvars
{
     if (::onsysn(m_usr->userid(),TRUE)) {
          usaptr=othuap;
     }
     if (*(m_usaptr.userid) == '\0') {
          ::dfaSetBlk(::accbb);
          dfaAcqEQ(&m_usaptr,m_usr->userid(),0);
          ::dfaRstBlk();
     }
     usaptr=&m_usaptr;
}

VOID
regBaseAuthSyn::initTemplates()         // get onxxx parameter values
{
     m_succRedir=getOnParam("onsuccess",DFTSUCCESS,&m_onsuccessFile);
     m_errRedir=getOnParam("onerror",DFTERROR,&m_onerrorFile);
}

bool                                    //   is a redirct?
regBaseAuthSyn::getOnParam(             // get onxxx parameter
CHAR const * paramName,                 //   parameter to get
CHAR const * dftFileName,               //   dft file name to use
CHAR** pStorePtr)                       //   store redir path here
{
     if (ses->param(paramName)) {
          if (*pStorePtr != NULL) {
               delete [] *pStorePtr;
          }
          INT sz=ses->paramRoom(paramName);
          *pStorePtr=new CHAR[sz];
          ses->param(paramName,*pStorePtr,sz);
          if (sameto("http://",*pStorePtr) || **pStorePtr == '/') {
               return(true);
          }
          if (!isvalfn(*pStorePtr)) {
               if (sz < strlen(dftFileName)+1) {
                    delete [] *pStorePtr;
                    *pStorePtr=NULL;
               }
          }
          return(false);
     }
     else if (*pStorePtr != NULL) {
          delete [] *pStorePtr;
          *pStorePtr=NULL;
     }
     return(false);
}

ACTHCODE                                //   continue synthesis?
regBaseAuthSyn::errorResponse(          // process an error
dnfMap& map,                            //   map to use
CHAR const * path)                      //   path to template directory
{
     if (m_errRedir) {
          ses->redirect(m_onerrorFile);
          return(ACTHDONE);
     }
     ASSERT(path != NULL);
     string dest=path;

     dest+=(m_onerrorFile == NULL ? DFTERROR : m_onerrorFile);
     m_dnf=dnfCreateHandler(&bout,&map,dest.c_str());
     return(ACTHMORE);
}

ACTHCODE                                //   continue synthesis?
regBaseAuthSyn::successResponse(        // process an onsuccess
dnfMap& map,                            //   map to use
CHAR const * path)                      //   path to template directory
{
     if (m_succRedir) {
          ses->redirect(m_onsuccessFile);
          return(ACTHDONE);
     }
     ASSERT(path != NULL);
     string dest=path;

     dest+=(m_onsuccessFile == NULL ? DFTSUCCESS : m_onsuccessFile);
     m_dnf=dnfCreateHandler(&bout,&map,dest.c_str());
     return(ACTHMORE);
}

//
// Inalid URL Synthesis Member Functions
//

ACTHCODE
regInvalidURLSyn::proceed()             // proceed with synthesis
{
     return(ACTHNOTFND);
}

//
//  Default URL Synthesis Member Functions
//

ACTHCODE
regInitialSyn::proceedRequest()         // setup request context
{
     ACTHCODE rc=regBaseAuthSyn::proceedRequest();

     if (rc != ACTHMORE) {
          return(rc);
     }
     m_inframes=theRegAgent->m_frameset;
     if (ses->param("useframes")) {
          INT sz=ses->paramRoom("useframes")+1;
          CHAR* buf=new CHAR[sz];
          ses->param("useframes",buf,sz);
          switch (*buf) {
          case '1':
               m_inframes=true;
               break;
          case '0':
               m_inframes=false;
               break;
          case '~':
               m_inframes=(!m_inframes);
               break;
          default:
               break;
          }
          delete [] buf;
     }
     m_SynState=SYNSTATE_SYNTH;
     return(rc);
}

ACTHCODE
regInitialSyn::proceedSynth()           // proceed with synthesis
{
     m_SynState=SYNSTATE_RESPONSE;
     return(ACTHMORE);
}

ACTHCODE
regInitialSyn::proceedDerivedResponse() // proceed with response
{
     ACTHCODE rc=ACTHMORE;

     if (m_dnf == NULL) {
          const CHAR* pCookieVal=(m_inframes ? "1" : "0");
          dnfMap* pMap=(m_inframes ? &regFrameMap : &regNoFrameMap);
          m_dnf=dnfCreateHandlerURL(ses,pMap,PPFIX);
          setCookie(REGFRAMECOOKIE,pCookieVal,NULL,NULL,NULL,false,ses);
     }
     else if (m_dnf->process() == DNFEND) {
          rc=ACTHDONE;
     }
     return(rc);
}

//
//  Generic File URL Synthesis Member Functions
//

ACTHCODE
regFileSyn::proceedRequest()            // setup request context
{
     ACTHCODE rc=regBaseAuthSyn::proceedRequest();
     m_SynState=SYNSTATE_SYNTH;
     return(rc);
}

ACTHCODE
regFileSyn::proceedSynth()              // proceed with synthesis
{
     m_SynState=SYNSTATE_RESPONSE;
     return(ACTHMORE);
}

ACTHCODE
regFileSyn::proceedDerivedResponse()    // proceed with response
{
     ACTHCODE rc=ACTHMORE;

     if (m_dnf == NULL) {
          if (m_err) {
               return(errorResponse(regFileErrorMap,PPFIX "/file/"));
          }
          m_dnf=dnfCreateHandlerURL(ses,&regFileMap,PPFIX);
          if (m_dnf != NULL) {
               CHAR fileExtension[GCMAXEXT+1];
               const CHAR* fileName=m_dnf->getMap().getFile();
               fileparts(GCPART_EXTN,fileName,fileExtension,GCMAXEXT+1);
               ses->contypeFext(fileExtension);
          }
     }
     else if (m_dnf->process() == DNFEND) {
          rc=ACTHDONE;
     }
     return(rc);
}

//
//  List URL Synthesis Member Functions
//

ACTHCODE
regListSyn::proceedRequest()            // setup request context
{
     INT sz=0;
     CHAR* buf=NULL;
     ACTHCODE rc=regBaseAuthSyn::proceedRequest();

     // backward compatability with old style /list/userid
     if (ses->urlargc() == 2) {
          string argv1=ses->urlargv(1);
          string decodedStr=urlDecode(argv1);
          if (uidxst(decodedStr.c_str())) {
               ostrstream ost;
               ost << PPFIX << "/list/" << ses->urlargv(1) << ends;
               if (!isfile(ost.str())) {
                    ostrstream ost2;
                    ost2 << "/registry/view/?userid=" << ses->urlargv(1) << ends;
                    ses->redirect(ost2.str());
                    ost2.rdbuf()->freeze(0);
                    rc=ACTHDONE;
               }
               ost.rdbuf()->freeze(0);
          }
     }
     if (rc != ACTHMORE) {
          return(rc);
     }
     if (ses->param("order")) {
          sz=ses->paramRoom("order");
          buf=new CHAR[sz];
          ses->param("order",buf,sz);
          if (sameas(buf,"ascending")) {
               m_iOrder=ORDER_ASCENDING;
          }
     }
     if (!(m_bAll=ses->param("all"))) {
          if (ses->param("start") && !ses->param("search")) {
               ses->param("start",m_start,UIDSIZ);
          }
          if (ses->param("count")) {
               INT newsz=ses->paramRoom("count")+1;
               if (newsz > sz) {
                    if (sz != 0) {
                         delete [] buf;
                    }
                    sz=newsz;
                    buf=new CHAR[sz];
               }
               ses->param("count",buf,sz);
               INT tmpcnt=atoi(buf);
               if (tmpcnt > 0) {
                    m_iCount=min(theRegAgent->m_maxlst,tmpcnt);
               }
          }
          if (ses->param("direction") && !ses->param("search")) {
               INT newsz=ses->paramRoom("direction")+1;
               if (newsz > sz) {
                    if (sz != 0) {
                         delete [] buf;
                    }
                    sz=newsz;
                    buf=new CHAR[sz];
               }
               ses->param("direction",buf,sz);
               if (sameas(buf,"gt")) {
                    m_iDirection=DIR_GT;
               }
               else if (sameas(buf,"lt")) {
                    m_iDirection=DIR_LT;
               }
               else if (sameas(buf,"le")) {
                    m_iDirection=(DIR_LT|DIR_EQ);
               }
          }
     }
     if (buf != NULL) {
          delete [] buf;
     }
     if (ses->param("search")) {
          ses->param("search",m_search,UIDSIZ);
     }
     m_SynState=SYNSTATE_SYNTH;
     return(rc);
}

VOID
regListSyn::proceedNoSearch()           // proceed with list synthesis
{
     bool found=false;

     switch (m_SynSubState) {
     case SYNSUB_BUILDMAP:
          if (!m_bAll && m_cnt >= m_iCount) {
               m_SynSubState=SYNSUB_LISTCONTEXT;
               break;
          }
          if (m_iDirection&DIR_EQ && m_cnt == 0) {
               found=((m_iDirection&DIR_LT) ? dfaQueryLE(m_start,0)
                                             : dfaQueryGE(m_start,0));
          }
          else if (m_iDirection&DIR_LT) {
               found=dfaQueryLT(m_start,0);
          }
          else if (m_iDirection&DIR_GT) {
               found=dfaQueryGT(m_start,0);
          }
          if (found) {
               dfaAbsRec(&m_userReg,0);
               if (m_bAll) {
                    m_SynState=SYNSTATE_RESPONSE;
               }
               else {
                    addToQueue(m_userReg);
                    ++m_cnt;
               }
               stlcpy(m_start,m_userReg.userid,UIDSIZ);
          }
          else {
               memset(&m_userReg,0,sizeof(struct regrec));
               if (m_bAll) {
                    m_SynState=SYNSTATE_RESPONSE;
               }
               else {
                    m_SynSubState=SYNSUB_LISTCONTEXT;
               }
          }
          break;
     case SYNSUB_LISTCONTEXT:
          if (!m_rque.empty()) {
               stlcpy(m_pszHighName,((struct regrec)m_rque.back()).userid,UIDSIZ);
               stlcpy(m_pszLowName,((struct regrec)m_rque.front()).userid,UIDSIZ);
               m_bIsMoreHigh=dfaQueryGT(m_pszHighName,0);
               m_bIsMoreLow=dfaQueryLT(m_pszLowName,0);
          }
          m_SynState=SYNSTATE_RESPONSE;
          break;
     }
}

VOID
regListSyn::proceedSearch()             // proceed with search synthesis
{
     bool found=false;
     CHAR const * uid;

     if (m_usrxrf == NULL) {
          m_usrxrf=new acthUserXrf(m_search,(m_bAll ? 0 : m_iCount));
     }
     if (!m_bAll && m_cnt >= m_iCount) {
          memset(&m_userReg,0,sizeof(struct regrec));
          m_SynState=SYNSTATE_RESPONSE;
          return;
     }
     if ((uid=m_usrxrf->getmatch()) != NULL) {
          found=dfaQueryEQ(uid,0);
     }
     else {
          memset(&m_userReg,0,sizeof(struct regrec));
          m_SynState=SYNSTATE_RESPONSE;
     }
     if (found) {
          dfaAbsRec(&m_userReg,0);
          if (m_bAll) {
               m_SynState=SYNSTATE_RESPONSE;
          }
          else {
               addToQueue(m_userReg);
               m_cnt++;
          }
     }
}

ACTHCODE
regListSyn::proceedSynth()              // proceed with synthesis
{
     dfaSetBlk(regbb);
     if (*m_search == '\0') {
          proceedNoSearch();
     }
     else {
          proceedSearch();
     }
     dfaRstBlk();
     return(ACTHMORE);
}

ACTHCODE
regListSyn::proceedDerivedResponse()    // proceed with response
{
     ACTHCODE rc=ACTHMORE;

     if (m_dnf == NULL) {
          if (m_err != RERR_NOERROR) {
               return(errorResponse(regListErrorMap,PPFIX "/list/"));
          }
          m_dnf=dnfCreateHandlerURL(ses,&regListMap,PPFIX);
     }
     else {
          setupVars();
          switch (m_dnf->process()) {
          case DNFROWBEGIN:
               if ((m_bAll && *m_userReg.userid == '\0')
               || (!m_bAll && !getRegInfo())) {
                    m_dnf->tableDone();
               }
               break;
          case RLISTITEM:
               break;
          case DNFROWEND:
               if (m_bAll) {
                    m_SynState=SYNSTATE_SYNTH;
               }
               break;
          case DNFEND:
               rc=ACTHDONE;
               break;
          }
     }
     return(rc);
}

VOID
regListSyn::addToQueue(                 // add to registry queue
const struct regrec& ptr)               //   registry record to add
{
     if (m_iDirection&DIR_LT) {
          m_rque.push_front(ptr);
     }
     else {
          m_rque.push_back(ptr);
     }
}

bool                                    // got a new one?
regListSyn::getRegInfo()                // get next registry from queue
{
     if (m_rque.empty()) {
          memset(&m_userReg,0,sizeof(struct regrec));
          return(false);
     }
     m_userReg=((m_iOrder == ORDER_DESCENDING) ? m_rque.front()
                                               : m_rque.back());
     if (m_iOrder == ORDER_DESCENDING) {
          m_rque.pop_front();
     }
     else {
          m_rque.pop_back();
     }
     return(true);
}

VOID
regListSyn::setupVars()                 // setup text variables for response
{
     setNoList(m_cnt == 0);
     if (m_userReg.userid != '\0') {
          setupUserContext(m_userReg);
          if (m_usr == NULL) {
               setCanDelete(false);
          }
          else {
               setCanDelete(sameas(m_usr->userid(),m_userReg.userid) || m_usr->hasKey(delkey));
          }
          if (!m_bAll) {
               regListMoreHigh=(INT)m_bIsMoreHigh;
               regListMoreLow=(INT)m_bIsMoreLow;
               regListUserHigh=m_pszHighName;
               regListUserLow=m_pszLowName;
               regListCount=m_iCount;
               regListOrder=(m_iOrder == ORDER_ASCENDING ? "ascending"
                                                         : "descending");
          }
     }
}

VOID
regListSyn::emptyque()                  // empty the registry queue
{
     while (!m_rque.empty()) {
          m_rque.pop_back();
     }
}

//
// View URL Synthesis Member Functions
//

ACTHCODE
regViewSyn::proceedRequest()            // setup request context
{
     INT sz=0;
     CHAR* buf=NULL;
     ACTHCODE rc=regBaseAuthSyn::proceedRequest();

     if (rc != ACTHMORE) {
          return(rc);
     }
     if (m_err != RERR_NOERROR) {
          m_SynState=SYNSTATE_RESPONSE;
          return(ACTHMORE);
     }
     m_bEdit=ses->param("edit");
     if (m_bEdit) {
          if (m_usr == NULL) {
               return(ACTHNOANON);
          }
          else if (!(m_usr->hasKey(::regkey))) {
               m_err=RERR_NOACCESS;
               m_SynState=SYNSTATE_RESPONSE;
               return(ACTHMORE);
          }
     }
     if (ses->param("userid")) {
          ses->param("userid",m_userid,UIDSIZ);
     }
     else if (m_bEdit) {
          stlcpy(m_userid,m_usr->userid(),UIDSIZ);
     }
     else {
          m_err=RERR_MUSTSPECIFYUSER;
          m_SynState=SYNSTATE_RESPONSE;
          return(ACTHMORE);
     }
     if (ses->param("direction")) {
          sz=ses->paramRoom("direction")+1;
          buf=new CHAR[sz];
          INT newsz=ses->paramRoom("direction")+1;
          if (newsz > sz) {
               if (sz != 0) {
                    delete [] buf;
               }
               sz=newsz;
               buf=new CHAR[sz];
          }
          ses->param("direction",buf,sz);
          if (sameas(buf,"gt")) {
               m_iDirection=DIR_GT;
          }
          else if (sameas(buf,"lt")) {
               m_iDirection=DIR_LT;
          }
          else if (sameas(buf,"le")) {
               m_iDirection=(DIR_LT|DIR_EQ);
          }
          else if (sameas(buf,"ge")) {
               m_iDirection=(DIR_GT|DIR_EQ);
          }
     }
     if (ses->param("notable")) {
          INT newsz=ses->paramRoom("notable")+1;
          if (newsz > sz) {
               if (sz != 0) {
                    delete [] buf;
               }
               sz=newsz;
               buf=new CHAR[sz];
          }
          ses->param("notable",buf,sz);
          if (sameas(buf,"1")) {
               m_bNoTable=true;
          }
          else if (sameas(buf,"0")) {
               m_bNoTable=false;
          }
     }
     if (buf != NULL) {
          delete [] buf;
     }
     m_SynState=SYNSTATE_SYNTH;
     return(rc);
}

ACTHCODE
regViewSyn::proceedSynth()              // proceed with synthesis
{
     bool found;
     dfaSetBlk(regbb);

     if (m_iDirection&DIR_GT) {
          found=(m_iDirection&DIR_EQ    ? dfaQueryGE(m_userid,0)
                                        : dfaQueryGT(m_userid,0));
     }
     else if (m_iDirection&DIR_LT) {
          found=(m_iDirection&DIR_EQ    ? dfaQueryLE(m_userid,0)
                                        : dfaQueryLT(m_userid,0));
     }
     else {
          found=dfaQueryEQ(m_userid,0);
     }
     if (found) {
          dfaAbsRec(&m_userReg,0);
          m_bIsMoreHigh=dfaQueryGT(m_userReg.userid,0);
          m_bIsMoreLow=dfaQueryLT(m_userReg.userid,0);
     }
     else if (m_bEdit) {
          stlcpy(m_userReg.userid,m_usr->userid(),UIDSIZ);
     }
     else {
          m_err=RERR_NOTFOUND;
     }
     m_SynState=SYNSTATE_RESPONSE;
     return(ACTHMORE);
}

ACTHCODE
regViewSyn::proceedDerivedResponse()    // proceed with response
{
     ACTHCODE rc=ACTHMORE;

     if (m_dnf == NULL) {
          if (m_err != RERR_NOERROR) {
               return(errorResponse(regViewErrorMap,PPFIX "/view/"));
          }
          dnfMap* pMap=(m_bNoTable ? &regViewNoTableMap : &regViewMap);
          m_dnf=dnfCreateHandlerURL(ses,pMap,PPFIX);
     }
     else {
          setupVars();
          switch (m_dnf->process()) {
          case DNFROWBEGIN:
               {
                    INT row=m_dnf->rowNumber();
                    if (row >= numfds || sameas("",regQuestionN[row]->get())) {
                         m_dnf->tableDone();
                    }
                    else {
                         setCurrentQuestion(row);
                         setCurrentAnswer(row);
                    }
               }
               break;
          case RLISTITEM:
               break;
          case DNFROWEND:
               break;
          case DNFEND:
               rc=ACTHDONE;
               break;
          }
          linkAnswers(true);
     }
     return(rc);
}

VOID
regViewSyn::setupVars()                 // setup text variables for response
{
     linkAnswers(!m_bEdit);
     if (m_userReg.userid != '\0') {
          setupUserContext(m_userReg);
          if (m_usr == NULL) {
               setCanDelete(false);
          }
          else {
               setCanDelete(sameas(m_usr->userid(),m_userReg.userid) || m_usr->hasKey(delkey));
          }
          regListMoreHigh=(INT)m_bIsMoreHigh;
          regListMoreLow=(INT)m_bIsMoreLow;
          regListUserHigh=m_userReg.userid;
          regListUserLow=m_userReg.userid;
     }
     INT row=m_dnf->rowNumber();
     if (!m_bNoTable
        && row >= 0
        && row < numfds
        && !sameas("",regQuestionN[row]->get())) {
          setCurrentQuestion(row);
          setCurrentAnswer(row);
     }
}

//
// Edit URL Synthesis Memeber Functions
//

ACTHCODE
regEditSyn::proceedRequest()            // setup request context, read params
{
     ACTHCODE rc=regBaseAuthSyn::proceedRequest();

     if (rc != ACTHMORE) {
          return(rc);
     }
     if (m_err != RERR_NOERROR) {
          m_SynState=SYNSTATE_RESPONSE;
          return(ACTHMORE);
     }
     if (m_usr == NULL) {
          return(ACTHNOANON);
     }
     else if (!(m_usr->hasKey(::regkey))) {
          m_err=RERR_NOACCESS;
          m_SynState=SYNSTATE_RESPONSE;
          return(ACTHMORE);
     }
     for (int i=0 ; i < numfds ; i++) {
          ostrstream ost;
          ost << "answer" << i+1 << ends;
          if (ses->param(ost.str())) {
               INT sz=ses->paramRoom(ost.str())+1;
               m_answer[i]=new CHAR[sz];
               ses->param(ost.str(),m_answer[i],sz);
          }
          ost.rdbuf()->freeze(0);
     }
     m_bHadSummary=ses->param("summary",m_summary,SUMSIZ);
     m_SynState=SYNSTATE_SYNTH;
     return(rc);
}

ACTHCODE
regEditSyn::proceedSynth()              // proceed with synthesis
{
     dfaSetBlk(regbb);
     m_bIsUpdate=dfaAcqEQ(&m_userReg,m_usr->userid(),0);
     for (int i=0 ; i < numfds ; i++) {
          if (m_answer[i] != NULL) {
               stlcpy((&m_userReg.profil[0])+reginf[i].ansidx
                      ,m_answer[i]
                      ,reginf[i].anssiz+1);
          }
     }
     if (m_bHadSummary) {
          stlcpy(m_userReg.sumlin,m_summary,SUMSIZ);
     }
     if (m_bIsUpdate) {
          dfaUpdate(&m_userReg);
     }
     else {
          stzcpy(m_userReg.userid,m_usr->userid(),UIDSIZ);
          dfaInsert(&m_userReg);
     }
     dfaRstBlk();
     m_SynState=SYNSTATE_RESPONSE;
     return(ACTHMORE);
}

ACTHCODE
regEditSyn::proceedDerivedResponse()    // proceed with response
{
     ACTHCODE rc=ACTHMORE;

     if (m_dnf == NULL) {
          if (m_err != RERR_NOERROR) {
               return(errorResponse(regEditErrorMap,PPFIX "/edit/"));
          }
          return(successResponse(regEditMap,PPFIX "/edit/"));
     }
     else {
          setupVars();
          if (m_dnf->process() == DNFEND) {
               return(ACTHDONE);
          }
     }
     return(rc);
}

VOID
regEditSyn::setupVars()                 // setup text variables for output
{
     setNewTvb(!m_bIsUpdate);
}

VOID
regEditSyn::deleteAnswerArray()         // delete the answer array
{
     for (int i=0 ; i < numfds ; i++) {
          if (m_answer[i] != NULL) {
               delete [] m_answer[i];
          }
     }
     delete [] m_answer;
}

VOID
regEditSyn::initAnswerArray()
{
     for (int i=0 ; i < numfds ; i++) {
          m_answer[i]=NULL;
     }
}


//
// Delete URL Synthesis Member Functions
//

ACTHCODE
regDeleteSyn::proceedRequest()          // setup request context
{
     ACTHCODE rc=regBaseAuthSyn::proceedRequest();

     if (rc != ACTHMORE) {
          return(rc);
     }
     if (m_usr == NULL) {
          return(ACTHNOANON);
     }
     if (!ses->param("userid",m_userid,UIDSIZ)) {
          m_err=RERR_MUSTSPECIFYUSER;
     }
     else if (!(m_usr->hasKey(::delkey))
       && !(sameas(m_userid,m_usr->userid()))) {
          m_err=RERR_NOACCESS;
     }
     if (m_err != RERR_NOERROR) {
          m_SynState=SYNSTATE_RESPONSE;
          return(ACTHMORE);
     }
     m_SynState=SYNSTATE_SYNTH;
     return(rc);
}

ACTHCODE
regDeleteSyn::proceedSynth()            // proceed with synthesis
{
     dfaSetBlk(regbb);
     if (dfaQueryEQ(m_userid,0)) {
          dfaAbsRec(NULL,0);
          dfaDelete();
     }
     else {
          m_err=RERR_NOTFOUND;
     }
     dfaRstBlk();
     m_SynState=SYNSTATE_RESPONSE;
     return(ACTHMORE);
}

ACTHCODE
regDeleteSyn::proceedDerivedResponse()  // proceed with response
{
     ACTHCODE rc=ACTHMORE;

     if (m_dnf == NULL) {
          if (m_err != RERR_NOERROR) {
               return(errorResponse(regDeleteErrorMap,PPFIX "/delete/"));
          }
          return(successResponse(regDeleteMap,PPFIX "/delete/"));
     }
     else {
          setupVars();
          if (m_dnf->process() == DNFEND) {
               return(ACTHDONE);
          }
     }
     return(rc);
}

VOID
regDeleteSyn::setupVars()               // setup text variables for response
{
     regUserid=m_userid;
     regUseridURL=m_userid;
}

// Backwards compatability classes
//

ACTHCODE
regOldSearchURLSyn::proceed()           // old style search search?userid=
{
     string sear("");

     if (ses->param("userid")) {
          INT sz=ses->paramRoom("userid")+1;
          CHAR* para=new CHAR[sz];
          ses->param("userid",para,sz);
          sear=para;
          delete [] para;
     }
     ostrstream ost;
     ost << "/registry/list/?search=" << sear << ends;
     ses->redirect(ost.str());
     ost.rdbuf()->freeze(0);
     return(ACTHDONE);
}


ACTHCODE
regTeleTabListSyn::proceed()            // Java Teleconference registry view
{
     acthUserID *usr=ses->getUser();
     if (usr == NULL && !theRegAgent->m_anonv) {
          return(ACTHNOANON);
     }
     if (usr != NULL && !usr->hasKey(viewkey) && !theRegAgent->m_anonv) {
          return(ACTHFORBID);
     }
     dfaSetBlk(regbb);
     setmbk(regmb);
     CHAR * userid=pls2spc((CHAR *)ses->urlargv(1));
     struct regrec regdf;
     if (!dfaAcqEQ(&regdf,userid,0)) {
          bout << theRegAgent->m_nousrinfo << endl;
     }
     else {
          for (int row=0; row < numfds; row++) {
               if (*(rawmsg(FLDLBL1+(4*row))) == '\0') {
                    break;
               }
               bout << rawmsg(FLDLBL1+(4*row)) << "\t";
               CHAR* dataptr=&(regdf.profil[0]);
               bout << dataptr+reginf[row].ansidx;
               bout << "\t";
               bout << endl;
          }
          bout << rawmsg(SUMLBL) << "\t";
          bout << regdf.sumlin << endl;
     }
     dfaRstBlk();
     rstmbk();
     return(ACTHDONE);
}
