/***************************************************************************
 *                                                                         *
 *   GALSCWAH.CPP                                                          *
 *                                                                         *
 *   Copyright (c) 1997      Galacticomm, Inc.      All Rights Reserved.   *
 *                                                                         *
 *   Active HTML Secure Web.                                               *
 *                                                                         *
 *                                                  - N. C. Osterc 3/23/97 *
 *                                                                         *
 ***************************************************************************/

#include "gcomm.h"
#include "majorbbs.h"
#include "galacth.h"
#include "tcpip.h"
#include "dnf.h"
#include "webd.h"
#include "gcspsrv.h"
#include "galscwah.h"
#include "galscw.h"
#include "dns.h"

#define FILREV "$Revision: 16 $"
#define PPFIX "galacth/galscwah/"

DFAFILE *scwbb;                    // pointer to btrieve database for secureweb
struct secwebdat sw;               // secure web structure
static CHAR *secwebky;             // pointer to SECWEBKY .MSG option
static CHAR *pwordInvalid;         // pointer to PWINV .MSG option
static CHAR *linkNotFound;         // pointer to LINKNF .MSG option
static CHAR *linkNotSpecified;     // pointer to LINKNS .MSG option
static CHAR *invalidNumber;        // pointer to INVNUM .MSG option
static CHAR *recordRemoved;        // point to RECREM .MSG option
static CHAR *cannotRemove;         // pointer to CANTREM .MSG option
static CHAR *cannotBeBlank;        // pointer to NOBLANK .MSG option
static CHAR *specifyKeyOrPwd;      // pointer to SPECKP .MSG option
static CHAR *secureSecureWeb;      // pointer to SECSWEB .MSG option
static CHAR *recordUpdated;        // point to RECUPD .MSG option
static CHAR *recordAdded;          // pointer to RECADD .MSG option
static CHAR *cannotAdd;            // pointer to CANTADD .MSG option
static CHAR *noneFound;            // pointer to NONEFND .MSG option
static GBOOL authenticate;         // boolean AUTHENT .MSG option
static GBOOL enabled;              // boolean SWENABL .MSG option
static CHAR *mptr;                 // temporary pointer .MSG data
VOID closebb(VOID);                // close database
HMCVFILE scwmb;                    // message block pointer

GBOOL
IsDirSecure(                       // Is this a secured directory/page?
const CHAR *link);                 // The link in question

enum {SECNUM, SECDIR, SECKEY};
dnfStep scwlstSteps[]={
     dnfStep(DNFTABLE),
          dnfStep(DNFCOLUMN,SECNUM,"SECNUM"),
          dnfStep(DNFCOLUMN,SECDIR,"SECDIR"),
          dnfStep(DNFCOLUMN,SECKEY,"SECKEY"),
     dnfStep(DNFTABLEEND),
     dnfStep(DNFMAPEND)
};
dnfMap scwmainMap(PPFIX "scwmain.htm",
              "Secure Web Interface",scwlstSteps);

enum {DPNAME, KEYREQ, PSWORD, EDITDP};
dnfStep scwwatchSteps[]={
     dnfStep(DNFWATCH,DPNAME,"DPNAME"),
     dnfStep(DNFWATCH,KEYREQ,"KEYREQ"),
     dnfStep(DNFWATCH,PSWORD,"PWORD"),
     dnfStep(DNFWATCH,EDITDP,"EDITDP"),
     dnfStep(DNFMAPEND)
};
dnfMap scwEditMap(PPFIX "scwedit.htm",
              "Secure Web Interface",scwwatchSteps);

enum {DPANAME, KEYAREQ, PAWORD};
dnfStep scwwatchaSteps[]={
     dnfStep(DNFWATCH,DPANAME,"DPANAME"),
     dnfStep(DNFWATCH,KEYAREQ,"KEYAREQ"),
     dnfStep(DNFWATCH,PAWORD,"PAWORD"),
     dnfStep(DNFMAPEND)
};
dnfMap scwAddMap(PPFIX "scwadd.htm",
              "Secure Web Interface",scwwatchaSteps);

enum {HIDLINK};
dnfStep scwpswSteps[]={
     dnfStep(DNFWATCH,HIDLINK,"HIDLINK"),
     dnfStep(DNFMAPEND)
};
dnfMap scwpswMap(PPFIX "scwpsw.htm",
              "Secure Web Password",scwpswSteps);

class scwAgent : public acthAgent {              // scw agent
public:
     scwAgent() : acthAgent("Galacticomm Web Security","secureweb")
     {
          registerAgent(acthVersion);
          scwmb=opnmsg("GALSCWAH.MCV");
          setmbk(scwmb);
          secwebky=stgopt(SECWEBKY);
          authenticate=ynopt(AUTHENT);
          enabled=ynopt(SWENABL);
          mptr=stpans(getmsg(PWINV));
          pwordInvalid=strcpy(new CHAR[strlen(mptr)+1],mptr);
          mptr=stpans(getmsg(LINKNF));
          linkNotFound=strcpy(new CHAR[strlen(mptr)+1],mptr);
          mptr=stpans(getmsg(LINKNS));
          linkNotSpecified=strcpy(new CHAR[strlen(mptr)+1],mptr);
          mptr=stpans(getmsg(INVNUM));
          invalidNumber=strcpy(new CHAR[strlen(mptr)+1],mptr);
          mptr=stpans(getmsg(RECREM));
          recordRemoved=strcpy(new CHAR[strlen(mptr)+1],mptr);
          mptr=stpans(getmsg(CANTREM));
          cannotRemove=strcpy(new CHAR[strlen(mptr)+1],mptr);
          mptr=stpans(getmsg(NOBLANK));
          cannotBeBlank=strcpy(new CHAR[strlen(mptr)+1],mptr);
          mptr=stpans(getmsg(SPECKP));
          specifyKeyOrPwd=strcpy(new CHAR[strlen(mptr)+1],mptr);
          mptr=stpans(getmsg(SECSWEB));
          secureSecureWeb=strcpy(new CHAR[strlen(mptr)+1],mptr);
          mptr=stpans(getmsg(RECADD));
          recordAdded=strcpy(new CHAR[strlen(mptr)+1],mptr);
          mptr=stpans(getmsg(RECUPD));
          recordUpdated=strcpy(new CHAR[strlen(mptr)+1],mptr);
          mptr=stpans(getmsg(CANTADD));
          cannotAdd=strcpy(new CHAR[strlen(mptr)+1],mptr);
          mptr=stpans(getmsg(NONEFND));
          noneFound=strcpy(new CHAR[strlen(mptr)+1],mptr);
          rstmbk();
          clsmsg(scwmb);
          scwbb=dfaOpen("GALSCW2.DAT",sizeof(struct secwebdat),NULL);
          hook_shutdown(closebb);
     }

     ~scwAgent()
     {
          free(secwebky);
          delete [] pwordInvalid;
          delete [] linkNotFound;
          delete [] linkNotSpecified;
          delete [] invalidNumber;
          delete [] recordRemoved;
          delete [] cannotRemove;
          delete [] cannotBeBlank;
          delete [] specifyKeyOrPwd;
          delete [] secureSecureWeb;
          delete [] recordAdded;
          delete [] recordUpdated;
          delete [] cannotAdd;
          delete [] noneFound;
     }

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

     private:

};

class scwSynthesis : public acthSynthesis {      // scwah response synthesis
public:
     scwSynthesis(acthSession *_ses) :
          acthSynthesis(_ses),
          dnf(NULL),
          abspos(0)
     {

          urlptr = NULL;
     }

     ~scwSynthesis()
     {
          if (dnf != NULL) {
               delete dnf;
               dnf = NULL;
          }
     }

     private:

     ACTHCODE
     dnf_main();                   // Do the initial screen

     ACTHCODE
     dnf_add();                    // Add Page

     ACTHCODE
     dnf_edit(                     // Edit page
     INT rec);

     ACTHCODE
     dnf_psword();                 // send password page

     ACTHCODE
     proceed();                    // proceed with session

     ACTHCODE
     proceedPost();                // proceed with session

     ACTHCODE
     proceedGet();                 // proceed with session

     ACTHCODE
     proceedAED();                 // Determine what to do

     VOID
     proceedAddEd();               // Add/Edit Secure stuff

     GBOOL
     proceedPsw();                 // see if password is good

     GBOOL
     notsecure(                    // Can it be secured?
     CHAR *name);                  // the secure "name"

     VOID
     sendSecuredFile();

     dnfHandler *dnf;              // Instance of DNF handler
     struct secwebdat secw;        // instance of Secure Web Struct
     INT reccount;                 // Total records in data file
     acthUserID *usr;              // points to user data
     LONG abspos;                  // Absolute btrieve ptr
     CHAR linkbuf[LINKSIZ];        // buffer for hidden link
     CHAR *urlptr;
};

scwAgent thescwAgent;              // the one and only instance of scwAgent

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

ACTHCODE
scwSynthesis::proceed()            // proceed with HTML synthesis
{
     ACTHCODE retval;

     if (!enabled) {
          return (ACTHNOTFND);
     }
     else if (authenticate && (usr=ses->getUser()) == NULL) {
          retval=ACTHNOANON;
     }
     else if (sameas(ses->method(),"GET")) {
          retval=proceedGet();
     }
     else if (sameas(ses->method(),"POST")) {
          retval=proceedPost();
     }
     else {
          ses->setStatus("501 Not implemented");
          retval=ACTHDONE;
     }
     return(retval);
}

ACTHCODE
scwSynthesis::proceedGet()         // proceed synthesizing response to GET
{
     INT narg;
     ACTHCODE retval=ACTHMORE;

     if ((narg=ses->urlargc()) == 0) {
          if ((usr=ses->getUser()) == NULL) {
               return(ACTHNOANON);
          }
          if (!usr->hasKey(secwebky)) {
               return(ACTHFORBID);
          }
          else if (ses->forceDir()) {
               retval=ACTHDONE;
          }
          else {
               if (dnf == NULL) {
                    dnf = new dnfHandler(scwmainMap,bout);
               }
               else {
                    retval=dnf_main();
               }
          }
     }
     else if (narg == 1 && sameas(ses->urlsfx(),"auth")) {
          ses->param("link",linkbuf,LINKSIZ);
          if (linkbuf[0] != '\0') {
               dfaSetBlk(scwbb);
               setmem(&secw,sizeof(struct secwebdat),0);
               if (IsDirSecure(linkbuf)) {          // verify secure
                    if (strlen(sw.keynam) > 0) {  // need to verify key
                         if ((usr=ses->getUser()) == NULL) {
                               return(ACTHNOANON);
                         }
                         if (!usr->hasKey(sw.keynam)) {
                              dfaRstBlk();
                              return(ACTHFORBID);
                         }
                    }
                    if (strlen(sw.psword) > 0) { // verify password
                         if (dnf == NULL) {
                              dnf = new dnfHandler(scwpswMap,bout);
                         }
                         else {
                              retval=dnf_psword();
                         }
                    }
                    else {
                        sendSecuredFile();
                        retval=ACTHDONE;

                    }
               }
               else {
                    bout << linkNotFound;
                    retval=ACTHDONE;
               }
               dfaRstBlk();
          }
          else {
                bout << linkNotSpecified;
                retval = ACTHDONE;
          }
     }
     else {
          retval=ACTHDONE;
     }
     return(retval);
}


ACTHCODE
scwSynthesis::proceedPost()        // proceed synthesizing response to POST
{
     ACTHCODE retval=ACTHMORE;

     if (ses->urlargc() == 1) {
          if (sameas(ses->urlargv(0),"AED")) {
               if ((usr=ses->getUser()) == NULL) {
                    return(ACTHNOANON);
               }
               if (usr->hasKey(secwebky)) {
                    retval=proceedAED();
               }
               else {
                    retval=ACTHDONE;
               }
          }
          else if (sameas(ses->urlargv(0),"ADDED")) {
               if ((usr=ses->getUser()) == NULL) {
                    return(ACTHNOANON);
               }
               if (usr->hasKey(secwebky)) {
                    proceedAddEd();
               }
               retval=ACTHDONE;
          }
          else if (sameas(ses->urlargv(0),"PSW")) {
               if (!proceedPsw()) {
                    bout << pwordInvalid;
                    retval=ACTHDONE;
               }
               else {
                    sendSecuredFile();
                    retval=ACTHDONE;
               }
          }
     }
     else {
          retval=ACTHNOTFND;
     }
     return(retval);
}


VOID
scwSynthesis::sendSecuredFile()
{

    CHAR fpath[GCMAXPTH];
    strrpl (linkbuf, '/', '\\');
    stlcpy (fpath, webroot, GCMAXPTH );
    stlcat (fpath, linkbuf, GCMAXPTH );
    ses->sndfile ( fpath );

}

ACTHCODE
scwSynthesis::proceedAED()        // Interpret Add/Edit info
{
     ACTHCODE retval=ACTHMORE;
     CHAR aednum[5];
     CHAR aedsel[2];
     LONG num;
     USHORT n=0;
     GBOOL badnumflg=FALSE;

     ses->param("AEDSEL",aedsel,sizeof(aedsel)+1);
     ses->param("AEDNUM",aednum,5);
     dfaSetBlk(scwbb);
     if (alldgs(aednum)) {
          num=(strtol(aednum,'\0',10))-1;
          if (num > dfaCountRec()-1 || num < 0) {
               badnumflg=TRUE;
          }
     }
     else {
          badnumflg=TRUE;
     }
     dfaRstBlk();
     switch (atoi(aedsel)) {
     case 0:
          if (dnf == NULL) {
               dnf = new dnfHandler(scwAddMap,bout);
          }
          else {
               retval=dnf_add();
          }
          break;
     case 1:
          if (!badnumflg) {
               if (dnf == NULL) {
                    dnf = new dnfHandler(scwEditMap,bout);
               }
               else {
                    retval=dnf_edit(num);
               }
          }
          else {
               bout << invalidNumber;
               retval=ACTHDONE;
          }
          break;
     case 2:
          if (!badnumflg) {
               dfaSetBlk(scwbb);
               if (dfaQueryLO(0)) {
                    do {
                         if (n == num) {
                              dfaAbsRec(NULL,0);
                              dfaDelete();
                              bout << recordRemoved;
                              break;
                         }
                         n++;
                    }
                    while (dfaQueryNX());
               }
               dfaRstBlk();
          }
          else {
               bout << cannotRemove;
          }
          retval=ACTHDONE;
          break;
     default:
          retval=ACTHDONE;
     }
     return(retval);
}

VOID
scwSynthesis::proceedAddEd()      // proceed to interpret Add/Edit info
{
     CHAR dpname[LINKSIZ];
     CHAR keynam[KEYLEN];
     CHAR psword[PWDSIZ];
     GBOOL addflg=FALSE;

     if (ses->param("EDIT",dpname,LINKSIZ)) {
          ses->param("KEYREQ",keynam,KEYLEN);
          ses->param("PWORD",psword,PWDSIZ);
     }
     else {
          ses->param("DPANAME",dpname,LINKSIZ);
          ses->param("KEYAREQ",keynam,KEYLEN);
          ses->param("PAWORD",psword,PWDSIZ);
          addflg=TRUE;
     }
     if (addflg == TRUE && dpname[0] == '\0') {
          bout << cannotBeBlank;
     }
     else if (keynam[0] == '\0' && psword[0] == '\0') {
          bout << specifyKeyOrPwd;
     }
     else if (notsecure(dpname)) {
          bout << secureSecureWeb;
     }
     else {
          dfaSetBlk(scwbb);
          setmem(&secw,sizeof(struct secwebdat),0);
          if (dfaAcqEQ(&secw,dpname,0)) {
               if (!addflg) {
                    stzcpy(secw.keynam,keynam,KEYLEN);
                    stzcpy(secw.psword,psword,PWDSIZ);
                    dfaUpdate(&secw);
                    bout << recordUpdated;
               }
               else {
                    bout << cannotAdd;
               }
          }
          else {
               if (addflg) {
                   stzcpy(secw.dpname,dpname,LINKSIZ);
                   stzcpy(secw.keynam,keynam,KEYLEN);
                   stzcpy(secw.psword,psword,PWDSIZ);
                   dfaInsert(&secw);
                   bout << recordAdded;
                }
          }
          dfaRstBlk();
     }
}

GBOOL
scwSynthesis::proceedPsw()        // proceed with response to password input
{
     CHAR psbuf[PWDSIZ];

     ses->param("HIDDENLINK",linkbuf,LINKSIZ);
     ses->param("PSWORD",psbuf,PWDSIZ);
     dfaSetBlk(scwbb);
     if (IsDirSecure(linkbuf)) {
          dfaRstBlk();
          if (sameas(sw.psword,psbuf)) {
               return(TRUE);
          }
          else {
               return(FALSE);
          }
     }
     dfaRstBlk();
     return(FALSE);
}

ACTHCODE
scwSynthesis::dnf_main()          // outputs main secure management HTML
{
     ACTHCODE retval;

     dfaSetBlk(scwbb);
     switch (dnf->process()) {
     case DNFBEGIN:
          setmem(&secw,sizeof(struct secwebdat),0);
          if ((reccount=dfaCountRec()) > 0) {
               dfaGetLO(&secw,0);
          }
          break;
     case DNFROWBEGIN:
          if (!dfaAcqGE(&secw,secw.dpname,0)) {
               dnf->tableDone();
          }
          break;
     case SECNUM:
          bout << dnf->rowNumber()+1;
          break;
     case SECDIR:
          bout << secw.dpname;
          break;
     case SECKEY:
          bout << secw.keynam;
          break;
     case DNFROWEND:
          if (!dfaAcqGT(&secw,secw.dpname,0)) {
               dnf->tableDone();
          }
          break;
     case DNFEND:
          if (!reccount) {
               bout << noneFound;
          }
          retval=ACTHDONE;
          break;
     }
     dfaRstBlk();
     return(retval);
}

ACTHCODE
scwSynthesis::dnf_edit(                        // Edit info
INT rec)
{
     ACTHCODE retval=ACTHMORE;
     INT i=0;

     switch (dnf->process()) {
     case DNFBEGIN:
          dfaSetBlk(scwbb);
          if (dfaQueryLO(0)) {
              do {
                    if (i == rec) {
                        dfaAbsRec(&secw,0);
                        break;
                    }
                    i++;
              }
              while (dfaQueryNX());
          }
          dfaRstBlk();
          break;
     case DPNAME:
          bout << secw.dpname;
          break;
     case KEYREQ:
          bout << secw.keynam;
          break;
     case PSWORD:
          bout << secw.psword;
          dnf->tableDone();
          break;
     case EDITDP:
          bout << secw.dpname;
          break;
     case DNFEND:
          retval=ACTHDONE;
          break;
     }
     return(retval);
}

ACTHCODE
scwSynthesis::dnf_add()                       // Add
{
     ACTHCODE retval=ACTHMORE;

     switch (dnf->process()) {
     case DPANAME:
          bout << "";
          break;
     case KEYAREQ:
          bout << "";
          break;
     case PAWORD:
          bout << "";
          dnf->tableDone();
          break;
     case DNFEND:
          retval=ACTHDONE;
          break;
     }
     return(retval);
}

ACTHCODE
scwSynthesis::dnf_psword()        // Password dnf (hidden link)
{
     ACTHCODE retval=ACTHMORE;

     switch(dnf->process()) {
     case HIDLINK:
          bout << linkbuf;
          dnf->tableDone();
          break;
     case DNFEND:
          retval=ACTHDONE;
          break;
     }
     return(retval);
}

GBOOL
scwSynthesis::notsecure(          // Can't secure "secureweb"
CHAR *name)
{
     CHAR *fndptr;

     if ((fndptr=strstr(name,"/secureweb")) != NULL) {
          if (fndptr == name) {
               return (TRUE);
          }
     }
     return (FALSE);
}

GBOOL
IsDirSecure(                       // Find out if directory is secured
const CHAR *link)                  // The link in question
{
     CHAR *tmptr, *newlink;
     GBOOL retval=FALSE;
     CHAR tempuri[URISIZ];

     setmem(&sw,sizeof(struct secwebdat),0);
     stlcpy(tempuri,link,URISIZ);
     tmptr=newlink=tempuri;
     dfaSetBlk(scwbb);
     if (dfaAcqEQ(&sw,link,0)) {
          return(TRUE);
     }

     while (*newlink++ != '\0') {
          if (*(newlink-1) == '/') {
               tmptr=newlink;
          }
     }
     if (tmptr != tempuri) {
          *tmptr='\0';
          tmptr--;
          if (tmptr != tempuri) {
               *tmptr='\0';
          }
          if (dfaAcqGE(&sw,tempuri,0)) {
               if (sameas(sw.dpname,tempuri)) {
                    retval=TRUE;
               }
               else {
                    *tmptr='/';
                    if (sameas(sw.dpname,tempuri)) {
                         retval=TRUE;
                    }
               }
          }
     }
     dfaRstBlk();
     return(retval);
}

VOID
closebb(VOID)                     // Close data file on hook_shutdown
{
     dfaClose(scwbb);
}

