/***************************************************************************
 *                                                                         *
 *   SYNLOGON.CPP                                                          *
 *                                                                         *
 *   Copyright (c) 1999 Galacticomm, Inc.         All Rights Reserved.     *
 *                                                                         *
 *   Active HTML Log On/Off Module log on request synthesis class.         *
 *                                                                         *
 *                                            - J. Alvrus   02/03/1999     *
 *                                                                         *
 ***************************************************************************/

#include "synlogon.h"
#include "dnfmgr.h"
#include "sesutil.h"
#include "tvbutil.h"

#define FILREV "$Revision: 1 $"
MARKSOURCE(synlogon)

tvbBase tvbUserID("SES_USERID");   // User-ID text variable
tvbBase tvbOnSuccess("SES_ONSUCCESS"); // reproduce onsuccess param text var
tvbBase tvbTemplateMask("SES_TEMPLATE_MASK");
                                   // custom template text var

synLogOn::synLogOn(                // constructor
acthSession *ses,                  //   associated session object
INT UrlLevel,                      //   URL argument level
bool CheckAlready) :               //   check if already logged on?
     synBase(ses),
     m_onsuccess(NULL),
     m_onerror(NULL),
     m_mask(NULL),
     m_level(UrlLevel),
     m_state(ST_START),
     m_err(ERR_NONE),
     m_already(CheckAlready)
{
     *m_uid='\0';
     *m_pwd='\0';
}

ACTHCODE
synLogOn::proceed()
{
     ACTHCODE rc=ACTHMORE;         // function return code

     for (bool go=startProc() ; go ; go=contProc(rc)) {
          switch (m_state) {
          case ST_START:
               {
                    // check URL syntax
                    if (ses->urlargc() == m_level && ses->forceDir()) {
                         return(ACTHDONE);
                    }

                    // save optional parameters
                    size_t len;
                    if ((len=ses->paramRoom("onsuccess")) > 1) {
                         m_onsuccess=new CHAR [len];
                         ses->param("onsuccess",m_onsuccess,len);
                    }
                    if ((len=ses->paramRoom("onerror")) > 1) {
                         m_onerror=new CHAR [len];
                         ses->param("onerror",m_onerror,len);
                    }

                    // conditionally check if already logged in
                    if (m_already && ses->getUser() != NULL) {
                         ::stlcpy(m_uid,ses->getUser()->userid(),UIDSIZ);
                         m_state=ST_PREPRESP;
                    }
                    else {
                         m_state=ST_CHECKLOG;
                    }
               }
               break;

          case ST_CHECKLOG:        // check User-ID/password
               {
                    struct usracc acc;
                    dfaSetBlk(accbb);
                    if (!ses->param("uid",m_uid,UIDSIZ) || *m_uid == '\0') {
                         m_err=ERR_NOUID;
                    }
                    else if (!dfaAcqEQ(&acc,m_uid,0)) {
                         m_err=ERR_BADUID;
                    }
                    else if (!ses->param("pwd",m_pwd,PSWSIZ) || *m_pwd == '\0') {
                         m_err=ERR_NOPWD;
                    }
                    else if (!sameas(acc.psword,m_pwd)) {
                         m_err=ERR_BADPWD;
                    }
                    else {
                         ::stlcpy(m_uid,acc.userid,UIDSIZ);
                         ::stlcpy(m_pwd,acc.psword,PSWSIZ);
                    }
                    m_state=ST_PREPRESP;
                    dfaRstBlk();
               }
               break;

          case ST_PREPRESP:        // set up response
               if (m_err == ERR_NONE) {
                    rc=PrepLogOnSuccessResp();
               }
               else {
                    rc=PrepLogOnErrorResp();
               }
               m_state=ST_OUTPUT;
               break;
          case ST_OUTPUT:

               // set up text variables
               tvbUserID.set(m_uid);
               tvbOnSuccess.set(m_onsuccess);
               tvbTemplateMask.set(m_mask);
               dnfSetTemplateTvb(m_dnf);

               // generate some output
               if (m_dnf->process() == DNFEND) {
                    rc=ACTHDONE;
               }

               // clear text variables
               tvbUserID.clr();
               tvbOnSuccess.clr();
               tvbTemplateMask.clr();
               dnfClearTemplateTvb();
               break;
          }
     }
     return(rc);
}

ACTHCODE
synLogOn::PrepLogOnSuccessResp()   // prepare success response to log on URL
{
     ACTHCODE rc=ACTHMORE;

     // get response template
     CHAR const * TemplateFile="welcome.htm";
     bool DelTemplate=false;

     // check for parameter override
     if (!NULSTR(m_onsuccess)) {
          TemplateFile=m_onsuccess;
     }

     // redirect or create DynaFile
     if (::isRedirectLocation(TemplateFile)) {

          // make sure location is fully qualified
          if (TemplateFile[0] == '/') {
               ostrstream ost;
               ost << "http://" << ses->host() << TemplateFile << ends;
               TemplateFile=ost.str();
               DelTemplate=true;
          }

          // generate redirection response
          ses->setStatus("302 Redirect");
          ses->headerField("Location",TemplateFile);
          ses->setUser(m_uid,m_pwd);
          bout << "<html>" << crlf
               << "<head><title>Request Redirected</title></head>" << crlf
               << "<body>" << crlf
               << "Request redirected to: <a href=\""
               << TemplateFile << "\">"
               << TemplateFile << "</a>" << crlf
               << "</body></html>";

          rc=ACTHDONE;
     }
     else {

          // create DynaFile handler
          CHAR TemplatePath[GCMAXPTH];
          makePath(TemplatePath,PPFIX "login",TemplateFile,GCMAXPTH);
          m_dnf=dnfCreateHandler(&bout,&baseMap,TemplatePath);

          // set authentication cookie
          ses->setUser(m_uid,m_pwd);
     }

     if (DelTemplate) {
          delete [] TemplateFile;
     }
     return(rc);
}

ACTHCODE
synLogOn::PrepLogOnErrorResp()     // prepare response to log off URL
{
     ACTHCODE rc=ACTHMORE;

     // set up template
     CHAR const * pTempMask=FindOnErrorTemplate();
     if (NULSTR(pTempMask)) {
          pTempMask=(ses->urlargc() == m_level+1
                   ? ses->urlargv(m_level) : "login*.htm");
     }
     CHAR * TemplateFile=new CHAR [::strlen(pTempMask)+1];
     ::strcpy(TemplateFile,pTempMask);
     ::strrpl(TemplateFile,'*','0'+m_err);


     // redirect or create DynaFile
     if (::isRedirectLocation(TemplateFile)) {

          // make sure location is fully qualified
          if (TemplateFile[0] == '/') {
               ostrstream ost;
               ost << "http://" << ses->host() << TemplateFile << ends;
               delete [] TemplateFile;
               TemplateFile=ost.str();
          }

          // generate redirection response
          ses->setStatus("302 Redirect");
          ses->headerField("Location",TemplateFile);
          bout << "<html>" << crlf
               << "<head><title>Request Redirected</title></head>" << crlf
               << "<body>" << crlf
               << "Request redirected to: <a href=\""
               << TemplateFile << "\">"
               << TemplateFile << "</a>" << crlf
               << "</body></html>";

          rc=ACTHDONE;
     }
     else {

          // save mask
          m_mask=new CHAR [::strlen(pTempMask)+1];
          ::strcpy(m_mask,pTempMask);

          // create DynaFile handler
          CHAR TemplatePath[GCMAXPTH];
          makePath(TemplatePath,PPFIX "login",TemplateFile,GCMAXPTH);
          m_dnf=dnfCreateHandler(&bout,&baseMap,TemplatePath);
     }

     delete [] TemplateFile;
     return(rc);
}

CHAR *                             //   resp in parsed m_onerror; NULL if none
synLogOn::FindOnErrorTemplate()    // find error-specific response in onerror
{
     // handle trivial case
     if (NULSTR(m_onerror)) {
          return(NULL);
     }

     // onerror is a comma-delimited list of space-delimited entries
     // each list item consists of the error code, followed by a space,
     // followed by the corresponding response
     CHAR * pDefault=NULL;         // default response
     CHAR * pErr=m_onerror;        // pointer to error number
     while (true) {

          // find end of current item
          CHAR * pEnd=::strchr(pErr,',');
          if (pEnd != NULL) {
               *pEnd='\0';
          }

          // evalute item
          CHAR * pResp=::strchr(pErr,' ');
          if (pResp == NULL) {
               pDefault=::unpad(::skpwht(pErr));
          }
          else {
               *pResp='\0';
               if (::atoi(pErr) == m_err) {
                    return(::unpad(::skpwht(pResp+1)));
               }
          }

          // update parsing position
          if (pEnd == NULL) {
               break;
          }
          pErr=pEnd+1;
     }
     return(pDefault);
}

synLogOn::~synLogOn()
{
     if (m_onsuccess != NULL) {
          delete [] m_onsuccess;
          m_onsuccess=NULL;
     }
     if (m_onerror != NULL) {
          delete [] m_onerror;
          m_onerror=NULL;
     }
     if (m_mask != NULL) {
          delete [] m_mask;
          m_mask=NULL;
     }
}
