/***************************************************************************
 *                                                                         *
 *   SYNFDWN.CPP                                                           *
 *                                                                         *
 *   Copyright (c) 1998       Galacticomm, Inc.    All Rights Reserved.    *
 *                                                                         *
 *   Active HTML File Download Synthesis                                   *
 *   Implementation                                                        *
 *                                                                         *
 *                                                - Phil Henning 11/12/98  *
 *                                                                         *
 ***************************************************************************/

#include "gcomm.h"
#include "majorbbs.h"
#include "galacth.h"
#include <list>
#include "dnf.h"
#include "dnfmgr.h"
#include "ahutil.h"
#include "filtvb.h"
#include "synfdwn.h"
#include "filagent.h"
#include "galfilah.h"
using namespace std;

#define FILREV "$Revision: 9 $"

static
dnfStep dnfErrorSteps[]={
     dnfStep(DNFMAPEND)
};
static
dnfMap filErrorMap(PPFIX "/download/error.htm","Error Response",dnfErrorSteps);

//
//  List Libs Synthesis Member Functions
//

synFileDwn::synFileDwn(
acthSession* _ses,
bool anonok,
INT lvlcnt)   : synBaseAuth(_ses,anonok,theFilAgent->m_hfilkey,lvlcnt)
              , m_rinfo(NULL)
              , m_ResponseState(RESPSTATE_NORMAL)
              , m_price(0L)
              , m_filsize(0L)
{
     memset(&m_key,0,sizeof(union combo));
     memset(&m_Lib,0,sizeof(struct fllib));
     memset(&m_flfile,0,sizeof(struct flfile));
     m_ulby[0]='\0';
}

synFileDwn::~synFileDwn()
{
     if (m_rinfo != NULL) {
          delete m_rinfo;
     }
}

bool
synFileDwn::isFile()                    // is request file actually a file?
{
     bool result=false;
     bool canget;
     if (m_Lib.flags&FLGDOS) {
          m_ulby[0]='\0';
          canget=true;
     }
     else {
          dfaSetBlk(flfdat);
          stlcpy(m_key.key1.libname,m_strLib.c_str(),FLNAMESZ);
          stlcpy(m_key.key1.filname,m_strFile.c_str(),FLFILENM);
          canget=dfaAcqEQ(&m_flfile,&m_key,COMPLF)
              && (!notapped(m_flfile.udate)
               || ahIsFlop(m_usr,&m_Lib));
          stlcpy(m_ulby,m_flfile.ulby,UIDSIZ);
          dfaRstBlk();
     }
     if (canget) {
          ostrstream ost;
          ost << libpath(&m_Lib) << SLS << m_strFile.c_str() << ends;
          ffblk fb;
          if (!fndfile(&fb,ost.str(),0)) {
               result=false;
          }
          else {
               m_filsize=fb.ff_fsize;
               m_strPathToFile=ost.str();
               result=true;
          }
          ost.rdbuf()->freeze(0);
     }
     return(result);
}

bool
synFileDwn::canAfford()                 // can user afford to download?
{
     bool result=false;
     LONG m_price=dnlPrice(m_filsize,&m_Lib,m_usr);

     if (m_price > 0 && (m_usr == NULL || !m_usr->afford(m_price))) {
          m_err=(m_usr == NULL ? ERR_NOERROR : ERR_NOCREDITS);
     }
     else {
          result=true;
     }
     return(result);
}

ACTHCODE
synFileDwn::proceedRequest()            // setup request context
{
     ACTHCODE rc=synBaseAuth::proceedRequest();
     struct fllib* pLib;

     if (rc != ACTHMORE) {
          return(rc);
     }
     m_strLib=ses->urlargv(1);
     m_strFile=ses->urlargv(2);
     m_strFile=urlDecode(m_strFile);
     if ((pLib=libfind(m_strLib.c_str())) == NULL) {
          m_err=ERR_INVALIDLIB;
          m_errtext=m_strLib;
     }
     else if (m_usr == NULL && !ahHasLibKey(m_usr,pLib,pLib->dlkey)) {
          return(ACTHNOANON);
     }
     else if (m_usr != NULL && !ahHasLibKey(m_usr,pLib,pLib->dlkey)) {
          m_err=ERR_NOACCESS;
     }
     if (m_err == ERR_NOERROR) {
          m_Lib=*pLib;
          if (!isFile()) {
               m_err=ERR_INVALIDFILE;
               m_errtext=m_strFile;
          }
          else if (m_usr == NULL && !canAfford()) {
               return(ACTHNOANON);
          }
          else if (m_usr != NULL && !canAfford()) {
               // error for some reason.
               // canAfford already set it.
          }
          else {
               // we're ok to send the file
               m_rinfo=new readingInfo(m_strLib.c_str(),m_strFile.c_str());
          }
     }
     m_SynState=(m_err == ERR_NOERROR ? SYNSTATE_SYNTH : SYNSTATE_RESPONSE);
     return(rc);
}

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


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

     switch (m_ResponseState) {
     case RESPSTATE_NORMAL:
          if (m_err != ERR_NOERROR) {
               if (m_dnf == NULL) {
                    // need to make sure we're not redirecting before setting the status
                    if ((rc=errorResponse(filErrorMap,PPFIX "/download/")) == ACTHMORE) {
                         setStatus();
                    }
               }
               else if (m_dnf->process() == DNFEND) {
                    rc=ACTHDONE;
               }
               return(rc);
          }
          setContentLengthAndType();
          if (!ses->sndfile(m_strPathToFile.c_str())) {
               m_err=ERR_INVALIDFILE;
               m_errtext=m_strFile;
          }
          else {
               m_ResponseState=RESPSTATE_FILSENDING;
          }
          break;
     case RESPSTATE_FILSENDING:
          finDownload(true);
          m_ResponseState=RESPSTATE_NORMAL;
          rc=ACTHDONE;
          break;
     }
     return(rc);
}

VOID
synFileDwn::setContentLengthAndType()   // set content length and mime-type
{
     ostrstream ost;
     ost << "Content-Length: " << m_filsize << ends;
     ses->headerField(ost.str());
     ost.rdbuf()->freeze(0);
     CHAR fileExtension[GCMAXEXT+1];
     fileparts(GCPART_EXTN,m_strPathToFile.c_str(),fileExtension,GCMAXEXT+1);
     ses->contypeFext(fileExtension);
}

VOID
synFileDwn::finDownload(                // finish up download
bool bCompleted)                        // did we complete the download?
{
     if ((bCompleted || chgabt)
      && m_usr != NULL
      && m_price != 0L
      && m_usr->charge(m_price,1)
      && !(m_Lib.flags&FLGDOS)
      && m_Lib.royal > 0
      && !sameas(m_ulby,"Sysop")
      && *m_ulby != '\0') {
          crdusr(m_ulby,l2as(m_price*(LONG)m_Lib.royal/100L),0,0);
     }
     if (bCompleted) {
          dnlcount(const_cast<CHAR*>(m_strLib.c_str())
                  ,const_cast<CHAR*>(m_strFile.c_str())
                  ,-1L);
          sv.dwnlds++;
     }
     if (auditall || (m_Lib.flags&FLGADL)) {
          shocst(bCompleted ? "LIBRARY FILE WEB RETRIEVAL"
                            : "LIBRARY FILE WEB RETRIEVAL ABORT",
                 bCompleted ? "User %s download %s"SLS"%s"
                            : "User %s aborted dnld of %s"SLS"%s",
                 m_usr == NULL ? "" : m_usr->userid(),m_strLib.c_str()
                                        ,m_strFile.c_str());
     }
}



VOID
synFileDwn::abort()                     // synthesis abort
{
     if (m_ResponseState == RESPSTATE_FILSENDING) {
          finDownload(false);
          m_ResponseState=RESPSTATE_NORMAL;
     }
}

INT
synFileDwn::translateError()            // translate err to mcv file op
{
     switch (m_err) {
     case ERR_INVALIDLIB:
          return(ERRDL03);
     case ERR_INVALIDFILE:
          return(ERRDL04);
     case ERR_FILEIO:
          return(ERRDL07);
     case ERR_NOACCESS:
          return(ERRDL05);
     case ERR_NOCREDITS:
          return(ERRDL08);
     default:
          ASSERTM(FALSE,"Error in synFileDwnDetail::translateError()");
     }
     m_errtext=spr("%d",m_err);
     return(ERROOPS);
}

VOID
synFileDwn::setStatus()                 // set http status after error
{
     string status;
     switch (m_err) {
     case ERR_INVALIDLIB:
     case ERR_INVALIDFILE:
          status="404 Not Found";
          break;
     case ERR_FILEIO:
          status="500 Internal Server Error";
          break;
     case ERR_NOACCESS:
          status="403 Forbidden";
          break;
     case ERR_NOCREDITS:
          status="402 Payment Required";
          break;
     default:
          ASSERTM(FALSE,"Error in synFileDwnDetail::setStatus");
     }
     ses->setStatus(status.c_str());
}


