/***************************************************************************
 *                                                                         *
 *   GALWEBHD.CPP                                                          *
 *                                                                         *
 *   Copyright (c) 1996 Galacticomm, Inc.    All Rights Reserved.          *
 *                                                                         *
 *   Web server interface for Active HTML kernel (GALACTH).                *
 *                                                                         *
 *                                              - R. Stein  8/6/96         *
 *                                                                         *
 ***************************************************************************/

#include "gcomm.h"
#include "galacth.h"
#include "majorbbs.h"
#include "tcpip.h"
#include "webd.h"
#include "dns.h"
#include "galscwah.h"
#include "galwebhd.h"
#include "galscw.h"
#include "ahutil.h"
#include <fstream.h>
#include <new.h>

#define FILREV "$Revision: 25 $"
                                   // authentication type flags
#define ATF_BROWSER 1              //   browser ("Authorization" header)
#define ATF_COOKIE  2              //   cookie ("Cookie" header)
#define ATF_EITHER  (ATF_BROWSER|ATF_COOKIE) // either is allowed

static VOID newfailure();
static SHORT acthwebhdr();
SHORT acthhdrlin(CHAR *hdrdlin);
static VOID  acthwebreq(CHAR *uri);
static GBOOL sesproceed();
static VOID sesend();
static SHORT webhinp();
static VOID webhsts();
static VOID webhdrst();

const CHAR * const EXPORT dftMediaType="text/html";


GBOOL swenabled=FALSE;

INT webacthstt;                    /* Web server dynamic HTML interface    */
struct module webacthmodule={      /* module interface block               */
     "",                           /*    name used to refer to this module */
     NULL,                         /*    user logon supplemental routine   */
     webhinp,                      /*    input routine if selected         */
     webhsts,                      /*    status-input routine if selected  */
     NULL,                         /*    "injoth" routine for this module  */
     NULL,                         /*    user logoff supplemental routine  */
     NULL,                         /*    hangup (lost carrier) routine     */
     NULL,                         /*    midnight cleanup routine          */
     NULL,                         /*    delete-account routine            */
     NULL                          /*    finish-up (sys shutdown) routine  */
};

class webRequest : public acthRequest {
public:
     webRequest(                   // constructor
          struct webdusr *_webdp,  //   Web server info, see WEBD.H
          INT _unum);              //   usrnum-style channel number

     ~webRequest();                // destructor

     const CHAR *
     method();                     // e.g. "GET"

     const CHAR *
     url();                        // e.g. "/dynamic/app/index.htm?parm=2"

     const CHAR *
     version();                    // e.g. "HTTP/1.0"

     const CHAR *
     hdr1st();                     // e.g. "Host: www.abc.com" (NULL=none)

     const CHAR *
     hdrnxt();                     // e.g. "Accept: */*" ... (NULL=no more)

     LONG
     lenBody();                    // length of request's entity-body

     istream&
     bodyin();                     // input stream for request body

     ULONG                         //   network order: 0x01020304 => 1.2.3.4
     clientIP();                   // IP address of remote client

     USHORT                        //   usually 1024 to 65535
     clientPort();                 // TCP port number of remote client

     INT
     age();                        // age of connection in seconds

     VOID freereq();               // free up request resources

private:                           // funcs and vars unique to webRequest:
     VOID seehdr(CHAR *lin);       // see header field line(s)

     friend
     SHORT
     acthhdrlin(                   // interception of (*prshdrlin)()
     CHAR *hdrlin);
private:
     struct webdusr *webdp;        // Web server info, see WEBD.H
     INT unum;                     // usrnum for the web channel
     CHAR *hdrstart;               // pointer to 1st header line (NULL=none)
     CHAR *hdrlast;                // pointer to last header line
     CHAR *hdrprev;                // previous return by hdr1st()/hdrnxt()
     INT hdrcnt;                   // header line counter
     istream *pin;                 // parameter stream (file or memory)
};

class webResponse : public acthResponse {
public:
     webResponse(                  // constructor
     struct webdusr *_webdp,       //   Web server info, see WEBD.H
     INT _unum);                   //   usrnum-style channel number

     ~webResponse();               // destructor

public:                            // call these only in the ACTHPREHDR stage:
     VOID
     headerFollows();              // agent makes header ("nph" in CGI lingo)

     VOID
     setStatus(                    // specify status (ala CGI "Status:" hdr)
     const CHAR *sts);             //   e.g. "200 OK"

     VOID
     redirect(                     // redirect request to another URL (sts 302)
     const CHAR *url);             //   full URL of alternate resource

     VOID
     notFound();                   // 404 not found error

public:                            // call these in ACTHPREHDR or ACTHHEADER stg:
     VOID
     contypeFext(                  // specify content type by file extension
     const CHAR *fext);            //   e.g. "htm"

     VOID
     contypeMIME(                  // specify Content-type header field value
     const CHAR *mimetype);        //   e.g. "text/html" (default: text/html)

     VOID
     addlVersion(                  // append product version to Server header
     const CHAR *prodver);         //   product and version, e.g. "Zhoots/6.0"

     VOID
     headerField(                  // inject custom header field into response
     const CHAR *field);           //   e.g. "Who-knows: what"

     VOID
     headerField(                  // inject custom header field into response
     const CHAR *fieldname,        //   e.g. "Who-knows"
     const CHAR *fieldvalue);      //   e.g. "what"

public:                            // call these at any stage:
     INT                           //   number of bytes actually sent
     sndrsp(                       // send reply (portion) to Web browser
     const CHAR *data,             //   data to send
     INT nbytes);                  //   number of bytes

     INT                           //   number of bytes actually sent
     sndrsp(                       // send reply (portion) to Web browser
     const CHAR *data);            //   string to send ('\0' terminated)

     GBOOL                         //   TRUE=begun FALSE="not found" reported
     sndfile(                      // send file as entity body
     const CHAR *fpath);           //   path of file (type inferred from ext)

     GBOOL                         //   TRUE=begun FALSE="not found" reported
     sndfile(                      // send file as entity body
     const CHAR *fpath,            //   path of file
     const CHAR *mimetype);        //   MIME type of file (e.g. "text/html")

     LONG
     sndfileCount();               // bytes sent by most recent sndfile()

     const CHAR *
     mainHost();                   // server main domain (eg "www.gcomm.com")

     const CHAR *
     serverVer();                  // server's version (eg "Worldgroup/2.1")

public:
     VOID
     sendingFile();                // take up what sndfile() started

     GBOOL proceedLast;            // last return value of ses->proceed()
     ifstream *sfin;               // reading file and sending to browser

private:
     struct webdusr *webdp;        // Web server info, see WEBD.H
     INT unum;                     // usrnum for the web channel
     GBOOL contyped;               // contypesomething() called
     CHAR *addlver;                // additional version info
     LONG _sndfileCount;           // count of sndfile() bytes

     VOID
     preHeader();                  // Prepare to output header lines

     VOID
     preBody();                    // Prepare to output body of response

     VOID
     sndstg(                       // low-level sender (no structure)
     const CHAR *stg);             //   string, '\0'-terminated

     VOID
     sfinclose();                  // close file opened by sndfile()
};

class webSecurity                  // Web Security Class
{
public:

     webSecurity(                  // constructor
     CHAR *uri);

     ~webSecurity();               // destrcutor

     VOID
     checkSecurity();              // do security-check work

     CHAR *
     fix2default(                  // make sure URI is formatted properly
     CHAR *uri);

     CHAR *
     fixupuri(                     // convert double-slash to single
     CHAR *uri);

private:

     webRequest *req;              // pointer to temp request object
     webResponse *rsp;             // pointer to temp response object
     acthSession *ses;             // pointer to temp session object
     CHAR *m_uri;                  // uri
     CHAR *newuri;                 // temporary uri
     CHAR *thepath;                // path of uri
     CHAR redirurl[URISIZ+21];     // buffer of url to redirect to
     CHAR moduri[URISIZ];          // buffer for uri
};


struct webhdusr {                  // channel-specific info
     webRequest *req;
     webResponse *rsp;
     acthSession *ses;
} *webhdusr;

static
SHORT (*oldwebhdr)();              // intercepting (*prswebhdr)()
static
SHORT (*oldhdrlin)(CHAR *hdrlin);  // intercepting (*prshdrlin)()
static
VOID (*oldwebreq)(CHAR *uri);      // intercepting (*hdlwebreq)()
static
VOID (*oldrst)(VOID);              // intercepting (*hdlrst)()


// Worldgroup tie-ins

extern "C" VOID EXPORT
init__galwebhd(VOID)
{
     init__webd();
     stzcpy(webacthmodule.descrp,gmdnam("galwebhd.mdf"),MNMSIZ);
     webacthstt=register_module(&webacthmodule);
     bdymax=GCMAXLONG;             // HACK, should be configurable
     oldwebhdr=prswebhdr;
     prswebhdr=acthwebhdr;
     oldhdrlin=prshdrlin;
     prshdrlin=acthhdrlin;
     oldwebreq=hdlwebreq;
     hdlwebreq=acthwebreq;
     oldrst=hdlrst;
     hdlrst=webhdrst;
     webhdusr=(struct webhdusr *)alczer(nterms*sizeof(struct webhdusr));
     set_new_handler(newfailure);
     HMCVFILE swmb=opnmsg("GALSCWAH.MCV");
     setmbk(swmb);
     if (ynopt(SWENABL)) {
          swenabled = TRUE;
     }
     rstmbk();
     clsmsg(swmb);

     // configure Active HTML Kernel
     HMCVFILE ahmb=opnmsg("GALWEBHD.MCV");
     ACTHOPTCODE rc;

     INT atflg=tokopt(AUTHTYPE,"BROWSER","COOKIE","EITHER",NULL);
     // browser = 1, cookie = 2, either = 1|2 = 3
     if (atflg == 0) {
          catastro("ERROR IN ENUMERATED OPTION %d IN GALWEBHD.MCV",AUTHTYPE);
     }
     CHAR atstr[sizeof("Browser Cookie")]="";
     if (atflg&ATF_BROWSER) {
          strcpy(atstr,"Browser");
     }
     if (atflg&ATF_COOKIE) {
          if (*atstr != '\0') {
               strcat(atstr," ");
          }
          strcat(atstr,"Cookie");
     }
     rc=acthSetOption("Authentication Allowed Types",atstr);
     if (rc != AHOPT_SUCCESS) {
          catastro("Unable to set authentication type AUTHTYPE (%d)",rc);
     }

     if (atflg == ATF_EITHER) {
          INT dftnum=tokopt(AUTHDFT,"BROWSER","COOKIE",NULL);
          if (dftnum == 0) {
               catastro("ERROR IN ENUMERATED OPTION %d IN GALWEBHD.MCV",AUTHDFT);
          }
          CHAR dftstr[max(sizeof("Browser"),sizeof("Cookie"))];
          if (dftnum == ATF_BROWSER) {
               strcpy(dftstr,"Browser");
          }
          else {
               strcpy(dftstr,"Cookie");
          }
          rc=acthSetOption("Authentication Default Type",dftstr);
          if (rc != AHOPT_SUCCESS) {
               catastro("Unable to set default authentication type AUTHDFT (%d)",rc);
          }
     }

     if (atflg&ATF_BROWSER) {
          rc=acthSetOption("Authentication Realm",stpans(rawmsg(AUTHRELM)));
          if (rc != AHOPT_SUCCESS) {
               catastro("Unable to set authentication realm AUTHRELM (%d)",rc);
          }
     }

     if (atflg&ATF_COOKIE) {
          CHAR * cookie=unpad(skptwht(stpans(rawmsg(AUTHCNAM))));
          rc=acthSetOption("Authentication Cookie Name",cookie);
          switch (rc) {
          case AHOPT_SUCCESS:
               break;
          case AHOPT_INVALID:
               catastro("Invalid authentication cookie name in AUTHCNAM");
          case AHOPT_INVRESERVED:
               catastro("Reserved authentication cookie name in AUTHCNAM");
          default:
               catastro("Unable to set authentication cookie name AUTHCNAM (%d)",rc);
          }
     }

     INT slice=numopt(TIMSLC,1,1000);
     rc=acthSetOption("Parameter Parsing Time Slice",l2as(slice));
     if (rc != AHOPT_SUCCESS) {
          catastro("Unable to set parameter parsing time slice TIMSLC (%d)",rc);
     }
     clsmsg(ahmb);
}

extern "C" VOID EXPORT
initwc__galwebhd(VOID)
{
     init__galwebhd();
}

static VOID
newfailure(VOID)                   // instead of new returning NULL...
{
     memcata();
}

static SHORT
webhinp()                          // input handler, never to be used
{
     return(1);
}

static VOID
webhsts()                          // status handler, for CYCLEs
{
     if (status == RING) {
          rstchn();
          return;
     }
     if (status != CYCLE || (usrptr->flags&BYEBYE)) {
          return;
     }
     switch (usrptr->substt) {
     case 0:
          if (sesproceed()) {
               btuinj(usrnum,CYCLE);
          }
          else {
               sesend();
               endwebd(1);
               byendl(NO_MESSAGE);
          }
          break;
     }
}

VOID
webhdrst()                         // (*hdlrst)() reset interception
{
     if (webhdusr[usrnum].ses != NULL) {
          webhdusr[usrnum].ses->abort();
     }
     sesend();
     (*oldrst)();
}


// intercepted Web Server vectors

static SHORT
acthwebhdr()                       // interception of (*prswebhdr)()
{
     webhdusr[usrnum].req=new webRequest(webdptr,usrnum);
     return((*oldwebhdr)());
}

SHORT
acthhdrlin(                        // interception of (*prshdrlin)()
CHAR *hdrlin)
{
     webhdusr[usrnum].req->seehdr(hdrlin);
     return((*oldhdrlin)(hdrlin));
}

static VOID
acthwebreq(                        // interception of (*hdlwebreq)()
CHAR *uri)
{
     webRequest *req;
     webResponse *rsp;
     acthSession *ses;

     if (swenabled) {

          webSecurity *scw = new webSecurity ( uri );

          scw->checkSecurity();

          delete scw;

     }
     req=webhdusr[usrnum].req;
     rsp=webhdusr[usrnum].rsp=new webResponse(webdptr,usrnum);
     ses=webhdusr[usrnum].ses=new acthSession(req,rsp);
     if (!ses->claim()) {
          sesend();
          (*oldwebreq)(uri);
          return;
     }
     rsp->addlVersion(rsp->serverVer());
     sesproceed();                 // chance to get some work done this cycle
     usrptr->state=webacthstt;
     usrptr->substt=0;
     btuinj(usrnum,CYCLE);
}

static GBOOL                       //   TRUE=more to go, FALSE=session done
sesproceed()                       // proceed with session
{                                  //   (harmless-persistent-FALSE at end)
     GBOOL retval;

     webResponse *rsp=webhdusr[usrnum].rsp;
     if (rsp->sfin != NULL) {
          rsp->sendingFile();      // no more proceedings until file is done
          retval=TRUE;
     }
     else if (btuoba(usrnum) < OUTSIZ/2-1) {
          retval=TRUE;             // hang around until output buffer empties
     }
     else if (rsp->proceedLast) {
          clrprf();
          rsp->proceedLast=webhdusr[usrnum].ses->proceed();
          retval=TRUE;             // not done yet, in case sndfile() initiated
     }
     else {
          retval=FALSE;            // finally ready to be done with session
     }
     return(retval);
}

static VOID
sesend()                           // session comes to an end
{
     if (webhdusr[usrnum].ses != NULL) {
          delete webhdusr[usrnum].ses;
          webhdusr[usrnum].ses=NULL;
     }
     if (webhdusr[usrnum].rsp != NULL) {
          delete webhdusr[usrnum].rsp;
          webhdusr[usrnum].rsp=NULL;
     }
     if (webhdusr[usrnum].req != NULL) {
          delete webhdusr[usrnum].req;
          webhdusr[usrnum].req=NULL;
     }
}

////////////////
// webRequest //
////////////////

webRequest::webRequest(            // constructor
struct webdusr *_webdp,            //   Web server info, see WEBD.H
INT _unum) :                       //   usrnum-style channel number
     webdp(_webdp),
     unum(_unum),
     pin(NULL)
{
     hdrstart=NULL;
     hdrprev=NULL;
     hdrcnt=0;
}

webRequest::~webRequest()          // destructor
{
     if (pin != NULL) {
          delete pin;
          pin=NULL;
     }
}

const CHAR *
webRequest::method()               // e.g. "GET"
{
     return(webdp->method);
}

const CHAR *
webRequest::url()                  // e.g. "/dynamic/app/index.htm?parm=2"
{
     CHAR *urlp=webdp->uri;
     CHAR *newp;

     if (sameto("http://",urlp) && (newp=strchr(urlp+7,'/')) != NULL) {
          urlp=newp;
     }
     return(urlp);
}

const CHAR *
webRequest::version()              // e.g. "HTTP/1.0"
{
     return(webdp->vertxt);
}

const CHAR *
webRequest::hdr1st()               // get first header line
{
     return(hdrprev=hdrstart);
}

const CHAR *
webRequest::hdrnxt()               // get next header line
{
     if (hdrprev == NULL) {
     }                                       // no (more) header lines
     else if (hdrprev == hdrlast) {
          hdrprev=NULL;                      // last header line just passed
     }
     else {
          hdrprev+=strlen(hdrprev)+1;        // skip to next header line
          while (isspace(*hdrprev)) {
               hdrprev++;
          }
     }
     return(hdrprev);
}

LONG
webRequest::lenBody()              // length of request's entity-body
{
     return(webdp->conlen);
}

istream&
webRequest::bodyin()               // request entity body input stream
{
     if (pin == NULL) {
          if (webdp->reqfp == NULL) {                  // still in memory
               pin=new istrstream(bodyareau(unum),lenBody());
          }
          else {                                       // resides on disk
               pin=new ifstream(webdp->reqfp->fd);
          }
     }
     return(*pin);
}

ULONG                              //   network order: 0x01020304 => 1.2.3.4
webRequest::clientIP()             // IP address of remote client
{
     return(tcpipinf[unum].inaddr.s_addr);
}

USHORT                             //   usually 1024 to 65535
webRequest::clientPort()           // TCP port number of remote client
{
     return(static_cast<USHORT>(tcpipinf[unum].port));
}

INT
webRequest::age()                  // age of connection in seconds
{
     return(tcpipinf[unum].tckcon-lngtck);
}

VOID
webRequest::freereq()              // free up request resources
{
}

VOID                               //   (unique to webRequest)
webRequest::seehdr(                // review header lines 1 at a time
CHAR *lin)                         //   header field lines from server
{
     if (hdrcnt == 0) {
          hdrstart=lin;            // remember 1st header line
     }
     hdrlast=lin;                  // remember last header line
     hdrcnt++;
}


/////////////////
// webResponse //
/////////////////

webResponse::webResponse(          // constructor
struct webdusr *_webdp,            //   Web server info, see WEBD.H
INT _unum) :                       //   usrnum-style channel number
     webdp(_webdp),
     unum(_unum),
     sfin(NULL),
     proceedLast(TRUE),
     contyped(FALSE),
     addlver(NULL),
     _sndfileCount(0L)
{
}

webResponse::~webResponse()        // destructor
{
     sfinclose();
     if (addlver != NULL) {
          delete[] addlver;
          addlver=NULL;
     }
}

VOID
webResponse::headerFollows()       // agent makes header ("nph" in CGI lingo)
{
     stage=ACTHBODY;
}

VOID
webResponse::setStatus(            // status (ala CGI "Status:" hdr)
const CHAR *sts)                   //   e.g. "200 OK"
{
     sndstg(httpVersion);
     sndstg(" ");
     sndstg(sts);
     sndstg("\r\n");
     if (stage < ACTHHEADER) {
          stage=ACTHHEADER;
     }
}

VOID
webResponse::addlVersion(          // append product version to Server header
const CHAR *prodver)               //   product and version, e.g. "Zhoots/6.0"
{
     if (addlver == NULL) {
          addlver=strcpy(new CHAR[strlen(prodver)+1],prodver);
     }
     else {
          CHAR *newp=new CHAR[strlen(addlver)+1+strlen(prodver)+1];
          strcpy(newp,addlver);
          strcat(newp," ");
          strcat(newp,prodver);
          delete[] addlver;
          addlver=newp;
     }
}

VOID
webResponse::redirect(             // redirect request to another URL (sts 302)
const CHAR *url)                   //   full URL of alternate resource
{
     setStatus("302 Redirect");
     sndstg("Location: ");
     sndstg(url);
     sndstg("\r\n"
            "Content-type: text/plain\r\n"
            "Server: ");
     sndstg(serverVer());
     sndstg("\r\n"
            "\r\n"
            "Request redirected to: ");
     sndstg(url);
     sndstg("\r\n");
}

VOID
webResponse::notFound()            // 404 not found error
{
     setStatus("404 Not Found");
     contypeMIME("text/plain");
     preBody();
     sndstg("404 Not Found\r\n");
}

VOID
webResponse::contypeFext(          // specify content type by file extension
const CHAR *fext)                  //   e.g. "htm"
{
     contypeMIME(acthMediaType(fext));
}

VOID
webResponse::contypeMIME(          // specify Content-type header field value
const CHAR *mimetype)              //   e.g. "text/html" (default: text/html)
{
     if (mimetype != NULL && !contyped) {
          contyped=TRUE;
          preHeader();
          headerField("Content-type",mimetype);
     }
}

VOID
webResponse::headerField(          // inject custom header field into response
const CHAR *field)                 //   e.g. "Who-knows: what"
{
     preHeader();
     sndstg(field);
     sndstg("\r\n");
}

VOID
webResponse::headerField(          // inject custom header field into response
const CHAR *fieldname,             //   e.g. "Who-knows"
const CHAR *fieldvalue)            //   e.g. "what"
{
     preHeader();
     sndstg(fieldname);
     sndstg(": ");
     sndstg(fieldvalue);
     sndstg("\r\n");
}

INT                                //   number of bytes actually sent
webResponse::sndrsp(               // send reply (portion) to Web browser
const CHAR *data,                  //   data to send
INT nbytes)                        //   number of bytes
{
     INT oba;
     INT retval;

     preBody();
     if ((oba=btuoba(unum)) == 0) {
          retval=0;
     }
     else if (oba < nbytes) {
          btuxct(unum,oba,const_cast<CHAR *>(data));
          retval=oba;
     }
     else {
          btuxct(unum,nbytes,const_cast<CHAR *>(data));
          retval=nbytes;
     }
     webdp->bytcnt+=retval;
     return(retval);
}

INT                                //   number of bytes actually sent
webResponse::sndrsp(               // send response (portion) to Web browser
const CHAR *data)                  //   string to send ('\0' terminated)
{
     return(sndrsp(data,strlen(data)));
}                                  // pass the buck to our brother, above...

GBOOL                              //   TRUE=begun FALSE="not found" reported
webResponse::sndfile(              // send file as entity body
const CHAR *fpath)                 //   path of file (type inferred from ext)
{
     CHAR fnext[MAXEXT];

     return(sndfile(fpath,acthMediaType(fileparts(GCPART_EXTN,
                                                  fpath,fnext,MAXEXT))));
}

GBOOL                              //   TRUE=begun FALSE="not found" reported
webResponse::sndfile(              // send file as entity body
const CHAR *fpath,                 //   path of file
const CHAR *mimetype)              //   MIME type of file (e.g. "text/html")
{
     if (sfin != NULL) {
          catastro("GALWEBHD:  Cannot send multiple files "
                   "in one proceed() cycle");
     }
     _sndfileCount=0L;
     if (strstr(fpath,"..") != NULL
      || strstr(fpath,"//") != NULL
      || strstr(fpath,"\\/") != NULL
      || strstr(fpath,"/\\") != NULL
      || strstr(fpath,"\\\\") != NULL
      || rsvnam(fpath)) {
          notFound();
          return(FALSE);
     }
     sfin=new ifstream(fpath,ios::in|ios::binary);
     if (sfin->fail()) {
          notFound();
          sfinclose();
          return(FALSE);
     }
     contypeMIME(mimetype);
     preBody();
     return(TRUE);
}

LONG
webResponse::sndfileCount()        // bytes sent by most recent sndfile()
{
     return(_sndfileCount);
}

const CHAR *
webResponse::mainHost()            // server main domain (eg "www.gcomm.com")
{
     return(hstdom);
}

const CHAR *
webResponse::serverVer()           // svr brand&version (eg "Worldgroup/3.0")
{
     static CHAR raw[]="Worldgroup/\x01N+BBS_VERSION\x01";
     static CHAR *cnv=NULL;

     if (cnv == NULL) {

          CHAR buff[128];
          stlcpy(buff,raw,sizeof(buff));
          xlttxv(buff,sizeof(buff));
          stlcat(buff," ",sizeof(buff));
          stlcat(buff,acthResponse::serverVer(),sizeof(buff));
          cnv=strcpy(new CHAR[strlen(buff)+1],buff);
     }
     return(cnv);
}

VOID
webResponse::sendingFile()         // take up what sndfile() started
{
     INT ntry=btuoba(unum);
     if (ntry > 0) {
          INT nread=sfin->read(prfbuf,ntry).gcount();
          if (nread > 0) {
               btuxct(unum,nread,prfbuf);
               _sndfileCount+=nread;
          }
          else if (ntry == OUTSIZ/2-1) {
               sfinclose();        // file done, output buffer empty
          }                        // job well done
          clrprf();
     }
}

VOID
webResponse::preHeader()           // Prepare to output header lines
{
     if (stage < ACTHHEADER) {
          setStatus("200 OK");
     }
}

VOID
webResponse::preBody()             // Prepare to output body of response
{
     preHeader();
     if (stage < ACTHBODY) {
          if (addlver != NULL) {
               headerField("Server",addlver);
               delete[] addlver;
               addlver=NULL;
          }
          if (!contyped) {
               contypeMIME(dftMediaType);
          }
          sndstg("\r\n");          // blank line between header and body
          stage=ACTHBODY;
     }
}

VOID
webResponse::sndstg(               // low-level sender (no structure)
const CHAR *stg)                   //   string, '\0'-terminated
{
     btuxct(unum,strlen(stg),const_cast<CHAR *>(stg));
}

VOID
webResponse::sfinclose()           // close file opened by sndfile()
{
     if (sfin != NULL) {
          delete sfin;
          sfin=NULL;
     }
}


////////////////////
//  Web Security  //
////////////////////


webSecurity::webSecurity(          // constructor
CHAR *uri)
{
     m_uri = uri;

     req=webhdusr[usrnum].req;
     rsp=webhdusr[usrnum].rsp=new webResponse(webdptr,usrnum);
     ses=webhdusr[usrnum].ses=new acthSession(req,rsp);

     dfaSetBlk(scwbb);

}

VOID
webSecurity::checkSecurity()       // do security-checking work
{

     stlcpy(moduri,m_uri,URISIZ);

     newuri=fixupuri(moduri);

     if (!ses->claim()) {
          thepath=fix2default(newuri);
     }
     else {
          thepath=NULL;
     }
     if (thepath != NULL) {
          if (IsDirSecure(thepath)) {
               stlcpy(redirurl,"/secureweb/",URISIZ);
               stlcat(redirurl,"auth?link=",URISIZ);
               stlcat(redirurl,thepath,URISIZ);
               stlcpy(webdptr->uri,redirurl,URISIZ);
          }
          else {
               webdptr->flags&=~WFLIMS;
          }
     }

}

webSecurity::~webSecurity()        // destructor
{

     dfaRstBlk();
     delete rsp;
     delete ses;

}

CHAR *
webSecurity::fixupuri(             // make double slash, 1 slash
CHAR *uri)
{

     CHAR *tmptr, *retptr;

     tmptr=retptr=uri;
     while (*tmptr != '\0') {
          if ((*tmptr == '/' && *(tmptr+1) != '/') ||
               *tmptr != '/') {
               *uri++=*tmptr;
          }
          tmptr++;
     }
     *uri='\0';
     return(retptr);
}

CHAR *                             // return the "proper" path for the URI
webSecurity::fix2default(          // Find the URI's path
CHAR *uri)                         // the URI in question
{
     CHAR *pth;
     struct ffblk fb;

     if (samend(uri,"/")) {             // dftfil ("index.htm")
          stlcat(uri,dftfil+1,URISIZ);
          return(uri);
     }
     if (samend(uri,".html")) {         // cut off to .htm
          uri[strlen(uri)-1]='\0';
          return(uri);
     }
     pth=webpth(uri);
     if (fndfile(&fb,pth,FAMDIR) && fb.ff_attrib&FAMDIR) {
          if (!samend(uri,"/")) {
               stlcat(uri,"/",URISIZ);
          }
          stlcat(uri,dftfil+1,URISIZ);
     }
     else if (uri[strlen(uri)-1] != '/' && !strchr(uri,'.')) {
          // replace "dumb file" with .htm
          stlcat(uri,".htm",URISIZ);
     }
     return(uri);
}


