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

#include "gcomm.h"
#include "majorbbs.h"
#include "galacth.h"
#include "dnf.h"
#include "dnfmgr.h"
#include "ahutil.h"
#include "filtvb.h"
#include "synfup.h"
#include "filagent.h"
#include "galfilah.h"
using namespace std;

#define FILREV "$Revision: 10 $"

#define CPYCHKSIZ 4096

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

static
dnfStep dnfSuccessSteps[]={
     dnfStep(DNFMAPEND)
};
static
dnfMap filSuccessMap(PPFIX "/upload/indexu.htm","Success Response",dnfSuccessSteps);

static
dnfStep dnfCancelSteps[]={
     dnfStep(DNFMAPEND)
};
static
dnfMap filCancelMap(PPFIX "/upload/indexc.htm","Cancel Response",dnfCancelSteps);


//
//  List Libs Synthesis Member Functions
//

synFileUp::synFileUp(
acthSession* _ses,
bool anonok,
INT lvlcnt)   : synBaseAuth(_ses,anonok,theFilAgent->m_hfilkey,lvlcnt)
              , m_holderr(ERR_NOERROR)
              , m_ofstr(NULL)
              , m_ulistr(NULL)
              , m_bHold(false)
              , m_bRespToHold(false)
              , m_bOverWrite(false)
              , m_uploading(false)
              , m_pLib(NULL)
              , m_moving(false)
              , m_bCanceled(false)

{
     memset(&m_File,0,sizeof(struct flfile));
     memset(m_writeBuf,0,sizeof(m_writeBuf));
     memset(&m_fb,0,sizeof(struct ffblk));

}

synFileUp::~synFileUp()
{
}

ACTHCODE
synFileUp::proceedRequest()             // setup request context
{
     ACTHCODE rc=synBaseAuth::proceedRequest();

     const CHAR* contype="multipart/form-data; boundary=";
     CHAR boundary[200];      // should be big enough

     struct fllib* pLib;

     setrsv(reserve);
     if (rc != ACTHMORE) {
          return(rc);
     }
     INT sz=ses->paramRoom("lib");
     CHAR* val=new CHAR[sz+1];
     ses->param("lib",val,sz);
     m_strLib=val;
     delete [] val;
     // check for presense and validity of lib parameter
     if (m_strLib.length() == 0) {
          m_err=ERR_PARAMMISSING;
          m_errtext="lib";
     }
     else if ((pLib=libfind(m_strLib.c_str())) == NULL) {
          m_err=ERR_INVALIDLIB;
          m_errtext=val;
     }
     // does user have access to the lib?
     else if (m_usr == NULL && !ahHasLibKey(m_usr,pLib,pLib->ulkey)) {
          return(ACTHNOANON);
     }
     else if (m_usr != NULL && !ahHasLibKey(m_usr,pLib,pLib->ulkey)) {
          m_err=ERR_NOACCESS;
     }
     // check for presence of file data
     if (m_err == ERR_NOERROR) {
          m_pLib=pLib;
          if (!ses->param("file") && !ses->param("heldname")) {
               m_err=ERR_PARAMMISSING;
               m_errtext="file";
          }
     }
     // check for proper encoding
     if (m_err == ERR_NOERROR && ses->param("file") && theFilAgent->m_bReqEncoding) {
          if (!ses->header("Content-Type",boundary,sizeof(boundary))) {
               ses->setStatus("400 Bad Request");
               m_err=ERR_UPENCODE;
          }
          else if (!sameto(contype,boundary)) {
               CHAR uagent[256];
               if (!ses->header("User-Agent",uagent,sizeof(uagent))) {
                    *uagent='\0';
               }
               if (getClientType(uagent) == BROWSER_IE
                && getClientVer(uagent) < 4) {
                    m_err=ERR_UPNOSUPIE;
               }
               else {
                    m_err=ERR_UPNOSUP;
               }
          }
     }
     // load description, if present
     if (m_err == ERR_NOERROR) {
          INT sz=ses->paramRoom("description");
          CHAR* val=new CHAR[sz+1];
          ses->param("description",val,sz);
          m_strDesc=val;
          delete [] val;
          // get filename and validate filename
          m_strRealFileName=getFileName();
          if (m_strRealFileName.length() == 0) {
               m_err=ERR_PARAMMISSING;
               m_errtext="filename";
          }
          else if (!isValidFileName(m_strRealFileName.c_str())) {
               m_err=ERR_INVALIDPARAM;
               m_errtext="filename";
          }
          // check if file already exists in library.
          else if (fileExists(m_strRealFileName.c_str())) {
               m_holderr=ERR_FILEEXISTSINLIB;
               m_errtext=m_strRealFileName;
               m_bHold=true;
          }
          else if (unifile && fileExistsGlobal(m_strRealFileName.c_str())) {
               m_holderr=ERR_FILEEXISTSGLOBAL;
               m_errtext=m_strRealFileName;
               m_bHold=true;
          }
          if (ses->param("origname")) {
               INT sz=ses->paramRoom("origname");
               val=new CHAR[sz];
               ses->param("origname",val,sz);
               m_strOrigFileName=val;
               delete [] val;
          }
          if (ses->param("heldname") && !ses->param("file")) {
               INT sz=ses->paramRoom("heldname");
               CHAR* val=new CHAR[sz];
               ses->param("heldname",val,sz);
               m_strFileName=val;
               delete [] val;
               m_strHeldName=m_strFileName;
               if (ses->param("cancel")) {
                    ostrstream ost;
                    ost << tempdir << SLS << m_strFileName << ends;
                    unlink(ost.str());
                    ost.rdbuf()->freeze(0);
                    m_bCanceled=true;
                    m_SynState=SYNSTATE_RESPONSE;
                    return(ACTHMORE);
               }
               else if (ok2overwrite()) {
                    if (m_strOrigFileName.length() > 0) {
                         m_strRealFileName=m_strOrigFileName;
                         m_bOverWrite=true;
                         m_bHold=false;
                         m_holderr=ERR_NOERROR;
                    }
                    else {
                         m_err=ERR_PARAMMISSING;
                         m_errtext="origname";
                    }
               }
               m_bRespToHold=true;
          }
          else if (m_bHold) {
               m_strHeldName=m_strRealFileName;
          }
          m_strRealFilePathName=libpath(m_pLib);
          m_strRealFilePathName+=SLS;
          m_strRealFilePathName+=m_strRealFileName;
     }
     if (m_err == ERR_NOERROR) {
          if (m_bRespToHold) {
               if (m_bHold) {
                    m_SynState=SYNSTATE_RESPONSE;
                    return(ACTHMORE);
               }
               else {
                    m_strFilePathName=tempdir;
                    m_strFilePathName+=SLS;
                    m_strFilePathName+=m_strFileName;
                    m_strRealFilePathName=libpath(m_pLib);
                    m_strRealFilePathName+=SLS;
                    m_strRealFilePathName+=m_strRealFileName;
                    if (w2writ(m_pLib->libname
                         ,const_cast<CHAR*>(m_strRealFileName.c_str()),0)
                       != NOCONFLICT) {
                         m_err=ERR_INUSE;
                         m_SynState=SYNSTATE_RESPONSE;
                    }
                    else {
                         m_SynSubState=SYNSUB_FINISHUPLOAD;
                    }
               }
          }
          else if (!getFileStream()) {
               m_SynState=SYNSTATE_RESPONSE;
          }
          else {
               m_SynSubState=SYNSUB_WRITEFSTREAM;
          }
     }
     m_SynState=SYNSTATE_SYNTH;
     return(rc);
}

bool
synFileUp::fileExists(                  // does file exist?
const CHAR* fname)                      // filename
{
     ostrstream ost;
     ost << libpath(m_pLib) << SLS << fname << ends;
     ffblk fb;
     if (fndfile(&fb,ost.str(),0)) {
          return(true);
     }
     ost.rdbuf()->freeze(0);
     return(false);
}


bool
synFileUp::fileExistsGlobal(            // does file exist in any lib?
const CHAR* fname)                      // filename
{
     union combo key;
     bool rc=false;
     memset(&key,0,sizeof(union combo));
     stlcpy(key.key4.filname,fname,FLFILENM);
     dfaSetBlk(flfdat);
     if (dfaQueryGE(&key,COMPFL)) {
          struct flfile ff;
          dfaAbsRec(&ff,COMPFL);
          if (sameas(ff.filname,fname)) {
               rc=true;
          }
     }
     dfaRstBlk();
     return(rc);
}

string                                  // the filename
synFileUp::getFileName()                // get filename from parameter
{
     string hdr;
     string fname;
     CHAR pinpbuf[8192];
     size_t sz=sizeof(pinpbuf);

     if (ses->param("filename")) {
          INT sz=ses->paramRoom("filename");
          CHAR* val=new CHAR[sz+1];
          ses->param("filename",val,sz);
          fname=val;
          delete [] val;
     }
     else {
          ses->paramHeader("file","Content-Disposition",pinpbuf,sz);
          hdr=pinpbuf;
          acthGetHeaderParam(hdr.c_str(),"filename",&sz,pinpbuf);
          CHAR fbuf[GCMAXPTH];
          fname=fileparts(GCPART_FNAM,pinpbuf,fbuf,GCMAXPTH);
          fname=cvtfname(fname.c_str());
     }
     return(fname);
}

bool                                    // stream was opened ok
synFileUp::getFileStream()              // get file stream
{
     if (m_ofstr != NULL) {
          delete m_ofstr;
     }
     if (w2writ(m_pLib->libname,const_cast<CHAR*>(m_strFileName.c_str()),0) != NOCONFLICT) {
          m_err=ERR_INUSE;
          return(false);
     }
     {
          ostrstream ost;
          ost << tempdir << SLS << "AHXXXXXX" << ends;
          m_strFilePathName=mktemp(ost.str());
          CHAR fbuf[GCMAXFNM];
          m_strFileName=fileparts(GCPART_FNAM,ost.str(),fbuf,GCMAXFNM);
          m_strHeldName=m_strFileName;
          ost.rdbuf()->freeze(0);
     }
     m_ofstr=new ofstream(m_strFilePathName.c_str(),ios::binary);
     m_uploading=true;
     ses->paramStreamOpen("file",&m_ulistr);
     if (m_ulistr != NULL) {
          memset(m_writeBuf,0,sizeof(m_writeBuf));
          m_ulistr->read(m_writeBuf,sizeof(m_writeBuf));
          INT len=m_ulistr->gcount();
          if (len == 0) {
               ses->paramStreamClose(m_ulistr);
               m_err=ERR_FSTREAMDATA;
               return(false);
          }
          else {
               m_ofstr->write(m_writeBuf,len);
          }
     }
     return(true);
}

bool                                    // are we done?
synFileUp::writeFileStream()            // write file stream to disk
{
     ASSERT(m_ulistr != NULL);

     if (m_ulistr != NULL) {
          memset(m_writeBuf,0,sizeof(m_writeBuf));
          m_ulistr->read(m_writeBuf,sizeof(m_writeBuf));
          ASSERT(m_ofstr != NULL);
          m_ofstr->write(m_writeBuf,m_ulistr->gcount());
          preventTimeout();
          if (m_ulistr->eof() || m_ulistr->fail()) {
               delete m_ulistr;
               m_ulistr=NULL;
               delete m_ofstr;
               m_ofstr=NULL;
               return(true);
          }
     }
     else {
          return(true);
     }
     return(false);
}

VOID
synFileUp::preventTimeout()             // prevent browser timeout
{
     bout << "<!-- -->" << crlf;
}

VOID
synFileUp::abort()                      // abort handler
{
     if (m_strFilePathName.length() != 0 && m_uploading) {
          unlink(m_strFilePathName.c_str());
     }
     if (m_moving) {
          fluoff(usrnum);
          fclose(flo->fsrc);
          fclose(flo->fdst);
          m_moving=false;
          unlink(flo->destpath);
          unlink(flo->srcpath);
          delfile();
     }
     donewrit(-1);
}


ACTHCODE
synFileUp::proceedSynth()               // proceed with synthesis
{
     if (m_err != ERR_NOERROR) {
          m_SynState=SYNSTATE_RESPONSE;
          return(ACTHMORE);
     }
     switch (m_SynSubState) {
     case SYNSUB_DELKWDS:
          delkwds();
          break;
     case SYNSUB_FINISHUPLOAD:
          if (!FileSizeOk()) {
               unlink(m_strFilePathName.c_str());
               m_err=ERR_UPTOOBIG;
          }
          if (!m_bHold && m_bOverWrite) {
               if (m_pLib->flags&FLGDOS || longsrch) {
                    delfile();
               }
               else {
                    m_SynSubState=SYNSUB_DELKWDS;
                    delkwds();
                    break;
               }
          }
     case SYNSUB_FINALFINISHUPLOAD:
          if (!m_bHold) {
               {
                    ostrstream ost;
                    ost << tempdir << SLS << m_strRealFileName.c_str() << ends;
                    rename(m_strFilePathName.c_str(),ost.str());
                    ost.rdbuf()->freeze(0);
                    GBOOL found = fndfile(&m_fb,ost.str(),0);
                    if (!found)
  					     {
      						m_err = ERR_NOSPACE;
		      				abort();
				      		break;
                    }
               }
               CHAR desc[DESCSIZ];
               stlcpy(desc,m_strDesc.c_str(),DESCSIZ);
               stlcat(desc,"\r",DESCSIZ);
               formatDesc(desc,DESCSIZ);
               fluoff(usrnum);
               INT inscode=insfile(m_pLib,tempdir,&m_fb,approval(m_pLib)
                ,m_usr == NULL ? g_pszAnonul : m_usr->userid(),desc);
               switch(inscode) {
               case INS_MOVE:
                    // we need to move the file
                    m_SynSubState=SYNSUB_MOVEFILE;
                    m_moving=true;
                    break;
               case INS_DONE:
                    m_SynSubState=SYNSUB_UPDONE;
                    break;
               case INS_ERROR:
                    m_err=ERR_FILEIO;
                    abort();
                    break;
               }
          }
          else {
                donewrit(-1);
                m_SynState=SYNSTATE_RESPONSE;
          }
          break;
     case SYNSUB_MOVEFILE:
          fluoff(usrnum);
          if (xfrfil(flo->fsrc,flo->fdst,CPYCHKSIZ) < CPYCHKSIZ) {
              m_moving=false;
              fclose(flo->fsrc);
              fclose(flo->fdst);
              unlink(flo->srcpath);
              m_SynSubState=SYNSUB_UPDONE;
          }
          break;
     case SYNSUB_UPDONE:
          {
               CHAR desc[DESCSIZ];
               stlcpy(desc,m_strDesc.c_str(),DESCSIZ);
               stlcat(desc,"\r",DESCSIZ);
               formatDesc(desc,DESCSIZ);

               shocst("LIBRARY FILE UPLOAD","User %s uploaded %s"SLS"%s",
                     m_usr->userid(),m_pLib->libname,m_fb.ff_name);
               addKeys(m_pLib->libname,m_fb.ff_name,desc);
               union combo key;
               memset(&key,0,sizeof(union combo));
               stlcpy(key.key4.filname,m_fb.ff_name,FLFILENM);
               stlcpy(key.key4.libkey,m_pLib->libname,FLNAMESZ);
               dfaSetBlk(flfdat);
               dfaAcqEQ(&m_File,&key,COMPFL);
               dfaRstBlk();
               donewrit(-1);
               m_SynState=SYNSTATE_RESPONSE;
               break;
          }
     case SYNSUB_WRITEFSTREAM:
          if (writeFileStream()) {
               m_SynSubState=SYNSUB_FINISHUPLOAD;
          }
          break;
     }
     return(ACTHMORE);
}

bool
synFileUp::FileSizeOk()                 // is file size ok?
{
     if (fndfile(&m_fb,m_strFilePathName.c_str(),0)) {
          if (m_fb.ff_fsize > m_pLib->maxbup
            || m_fb.ff_fsize+m_pLib->totbytes > m_pLib->maxbyt) {
               return(false);
          }
          else {
               return(true);
          }
     }
     return(false);
}


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

     if (m_err != ERR_NOERROR || m_holderr != ERR_NOERROR) {
          if (m_dnf == NULL) {
               if (m_err == ERR_NOERROR) {
                    m_err=m_holderr;
               }
               rc=errorResponse(filErrorMap,PPFIX "/upload/");
          }
          else {
               if (m_err == ERR_FILEEXISTSINLIB || m_err == ERR_FILEEXISTSGLOBAL) {
                    tvbFileHeldName=m_strHeldName;
                    tvbFileHeldNameUrl=m_strHeldName;
                    if (m_strOrigFileName.length() > 0) {
                         tvbFileName=m_strOrigFileName;
                         tvbFileNameUrl=m_strOrigFileName;
                    }
                    else {
                         tvbFileName=m_strRealFileName;
                         tvbFileNameUrl=m_strRealFileName;
                    }
                    tvbFileDetailRaw=htmlEncode(m_strDesc);
                    tvbFileDetailJS=jsEncode(m_strDesc.c_str());
                    ::setLibVars(m_pLib,m_usr);
                    if (m_err == ERR_FILEEXISTSINLIB) {
                         tvbLibAccessOverwrite=canUserOverwrite() ? "1" : "0";
                    }
                    else {
                         tvbLibAccessOverwrite="0";
                    }
               }
               if (m_dnf->process() == DNFEND) {
                    rc=ACTHDONE;
               }
          }
          return(rc);
     }
     ::setLibVars(m_pLib,m_usr);
     if (m_pLib->flags&FLGDOS) {
          ::setOSFileVars(&m_fb,m_pLib,m_usr);
     }
     else {
          ::setFileVars(&m_File,m_usr);
     }
     if (m_dnf == NULL)  {
          dnfMap* pMap=(m_bCanceled ? &filCancelMap : &filSuccessMap);
          CHAR ch=m_bCanceled ? 'C' : 'U';
          CHAR* fnbuf;

          if (m_onsuccessFile != NULL) {
               INT sz=strlen(m_onsuccessFile)+1;
               fnbuf=new CHAR[sz];
               stlcpy(fnbuf,m_onsuccessFile,sz);
          }
          else if (ses->urlargc() > m_lvlcnt) {
               INT sz=strlen(ses->urlargv(m_lvlcnt))+1;
               fnbuf=new CHAR[sz];
               stlcpy(fnbuf,ses->urlargv(m_lvlcnt),sz);
          }
          else {
               fnbuf=new CHAR[sizeof("INDEX*.HTM")+1];
               strcpy(fnbuf,"INDEX*.HTM");
          }
          if (isProtocol(fnbuf) || fnbuf[0] == '/') {
               ses->setStatus("302 Redirect");
               ostrstream ost;
               if (fnbuf[0] == '/') {
                    ost << "http://" << ses->host() << fnbuf << ends;
               }
               else {
                    ost << fnbuf << ends;
               }
               ses->headerField("Location",ost.str());
               ses->contypeMIME("text/html");
               bout << "Request redirected to: <A HREF=\"" << ost.str();
               bout << "\">" << ost.str() << "</A>" << crlf;
               ost.rdbuf()->freeze(0);
               rc=ACTHDONE;
          }
          else {
               strrpl(fnbuf,'*',ch);
               string strfname=PPFIX "/upload/";
               strfname+=fnbuf;
               m_dnf=dnfCreateHandler(&bout,pMap,strfname.c_str());
          }
          delete [] fnbuf;
     }
     else if (m_dnf->process() == DNFEND) {
          rc=ACTHDONE;
     }
     return(rc);
}

VOID
synFileUp::formatDesc(                  // format incoming file desc
CHAR *desc,                             //   raw description text
INT descBufSize)                        //   max desc buffer size
{
     //   step one -- LF or CR-LF --> CR
     //
     CHAR *dst=desc;
     const CHAR *src=desc;

     while (*src) {
          if (*src == '\n') {
               *dst++='\r';
               src++;
          }
          else if ((*src == '\r') && (src[1] == '\n')) {
               *dst++='\r';
               src+=2;
          }
          else {
               *dst++=*src++;
          }
     }
     *dst = '\0';
     //   step two -- no more than 15 lines, no more than 49 char/line
     //
     CHAR outBuf[DESCSIZ];

     int lineCount=0;
     const CHAR *nextIn=desc;
     CHAR *nextOut=outBuf;

     while ((lineCount < 15) && (*nextIn != '\0')) {
          int lineLen=0;
          const CHAR *lineStart=nextOut;

          while (*nextIn != '\0') {
               if (*nextIn == '\r') {
                    *nextOut++=*nextIn++;
                    lineCount++;
                    break;
               }
               else {
                    *nextOut++=*nextIn++;
                    lineLen++;
               }
               if (lineLen >= 50) {
                    *nextOut='\0';
                    CHAR *space=strrchr((CHAR *)lineStart,' ');

                    if (space == NULL) {
                         --nextIn;
                         --nextOut;
                         *nextOut++='\r';
                    }
                    else {
                         *space='\r';
                         nextOut=space+1;
                         nextIn-=lineStart+strlen(lineStart)-space-1;
                    }
                    ++lineCount;
                    break;
               }
          }
     }
     *nextOut=0;
     stlcpy(desc,outBuf,descBufSize);
     //   step three -- make sure we end with '\r'
     //
     CHAR lastCh='\0';

     if (strlen(desc) > 0) {
          lastCh=desc[strlen(desc)-1];
     }
     if (lastCh != '\r') {
          if (strlen(desc) >= (descBufSize-1)) {
               desc[descBufSize-2]='\0';
          }
          stlcat(desc,"\r",descBufSize);
     }
     return;
}

VOID
synFileUp::addKeys(                     // add to keyword database
const CHAR *libname,                    //   library for file
const CHAR *filename,                   //   file name
const CHAR *desc)                       //   file description
{
     INT loop;

     if (!longsrch) {
          fllib *libptr=libfind(libname);

          if ((libptr != NULL) && (!(libptr->flags&FLGDOS))) {
               fluoff(usrnum);
               if (!delkw("",filename,libname) && kwbuf[0].keyword[0] == '\0') {
                    if (makwdlst(desc,filename)) {
                         for (loop=0 ; loop < dargc ; loop++) {
                              addkw(dargv[loop],filename,libname);
                         }
                    }
               }
          }
     }
}

const CHAR *                            //   approval date string or NOTAPPED
synFileUp::approval(                    // get approval date for new upload
fllib *libptr)                          //   libary info
{
     ASSERT(libptr != NULL);

     const CHAR *result = NOTAPPED;

     if (ahHasLibKey(m_usr,libptr,libptr->autoap)) {
          result=ddat2srt(today());
     }
     if (notapped(result)) {
          libptr->appwait++;
     }
     return(result);
}

VOID
synFileUp::delfile()                    // delete file from lib
{
     if (!(m_pLib->flags&FLGCBD)) {
          unlink(spr("%s"SLS"%s",libpath(m_pLib),m_strRealFileName.c_str()));
     }
     if (!(m_pLib->flags&FLGDOS)) {
          dfaSetBlk(flfdat);
          union combo key;

          memset(&key,0,sizeof(union combo));
          stlcpy(key.key4.filname,m_strRealFileName.c_str(),FLFILENM);
          stlcpy(key.key4.libkey,m_pLib->libname,FLNAMESZ);
          struct fllib* libptr=libfind(m_pLib->libname);
          if (dfaAcqEQ(NULL,&key,COMPLF)) {
               libptr->numfiles--;
               libptr->totbytes-=flclfit(flf->siz,libptr->cluster);
               if (notapped(flf->udate)) {
                    libptr->appwait--;
               }
               chuldate(libptr,flf->udate,0);
               libptr->flags|=LIBCHN;
               dfaDelete();
          }
          dfaRstBlk();
     }
     m_SynSubState=SYNSUB_FINALFINISHUPLOAD;
}


VOID
synFileUp::delkwds()                    // delete keywords
{
     fluoff(usrnum);
     dfaSetBlk(flkdat);
     if (dfaAcqEQ(NULL,rcompkey(m_pLib->libname,m_strRealFileName.c_str()),COMPFL_K)) {
          dfaDelete();
     }
     else {
          delfile();
     }
     dfaRstBlk();
}

bool
synFileUp::ok2overwrite()               // can this file be overwritten?
{
     bool rc=false;
     if (ses->param("overwrite") && m_holderr != ERR_FILEEXISTSGLOBAL) {
          rc=canUserOverwrite();
     }
     return(rc);
}

bool
synFileUp::canUserOverwrite()           // can current user overwrite?
{
     bool rc=false;
     if (ahHasLibKey(m_usr,m_pLib,m_pLib->overw)) {
          rc=true;
     }
     else if (!(m_pLib->flags&FLGDOS)) {
          dfaSetBlk(flfdat);
          union combo key;
          memset(&key,0,sizeof(union combo));
          stlcpy(key.key4.filname,m_strRealFileName.c_str(),FLFILENM);
          stlcpy(key.key4.libkey,m_pLib->libname,FLNAMESZ);
          if (dfaAcqEQ(NULL,&key,COMPLF)) {
               if (sameas(flf->ulby,m_usr->userid())
               && notapped(flf->udate)) {
                    rc=true;
               }
          }
          dfaRstBlk();
     }
     return(rc);
}

INT
synFileUp::translateError()             // translate err to mcv file op
{
     switch (m_err) {
     case ERR_INVALIDLIB:
          return(ERRUL03);
     case ERR_PARAMMISSING:
          return(ERRUL01);
     case ERR_FILEIO:
          return(ERRUL07);
     case ERR_NOSPACE:
	       return(ERRUL18);
     case ERR_NOACCESS:
          return(ERRUL05);
     case ERR_UPENCODE:
          return(ERRUL10);
     case ERR_UPNOSUP:
          return(ERRUL11);
     case ERR_UPNOSUPIE:
          return(ERRUL12);
     case ERR_FILEEXISTSINLIB:
          return(ERRUL13);
     case ERR_FILEEXISTSGLOBAL:
          return(ERRUL14);
     case ERR_INUSE:
          return(ERRUL17);
     case ERR_FSTREAMDATA:
          return(ERRUL15);
     case ERR_UPTOOBIG:
          return(ERRUL16);
     default:
          ASSERTM(FALSE,"Error in synFileUp::translateError()");
     }
     m_errtext=spr("%d",m_err);
     return(ERROOPS);
}

const CHAR *                            //   return pointer to "good" file name
cvtfname(                               // convert file name to DOS convention
const CHAR *badfname)
{
     CHAR *cp;
     CHAR *ext;
     static CHAR fname[GCMAXFNM];

     if (isValidFileName(badfname)) {
          stlcpy(fname,badfname,GCMAXFNM);
     }
     else {
          /* copy name into return buffer, removing any path info */
          if ((cp=strrchr((CHAR*)badfname,'\\')) != NULL
           || (cp=strrchr((CHAR*)badfname,'/')) != NULL
           || (cp=strrchr((CHAR*)badfname,':')) != NULL) {
               stlcpy(fname,cp+1,GCMAXFILE+1);
          }
          else {
               stlcpy(fname,badfname,GCMAXFILE+1);
          }
          /* find and remove any partial extension */
          if ((cp=strchr(fname,'.')) == NULL) {
               cp=&fname[strlen(fname)];
          }
          else {
               *cp='\0';
          }
          /* make sure file name isn't blank */
          if (*fname == '\0') {
               cp=stpcpy(fname,"FILE");
          }
          /* find and append actual extension */
          if ((ext=strrchr((CHAR*)badfname,'.')) != NULL) {
               stlcpy(cp,ext,CSTRLEN(".")+GCMAXEXT+1);
          }
          ASSERT(strlen(fname) < GCMAXFNM);
          /* replace any characters that aren't allowed in file names */
          for (cp=fname ; *cp != '\0' ; ++cp) {
               if (*cp != '.' && !isValidFileNameChar(*cp)) {
                    *cp='_';
               }
          }
          /* replace entire name if it's reserved */
          if (rsvnam(fname)) {
               strcpy(fname,"FILE.EXT");
          }
          ASSERT(isValidFileName(fname));
     }
     return(fnmcse(fname));
}

bool
isValidFileName(                        // is this a valid DOS file name?
const CHAR *fileName)                   // file name to check
{
     const CHAR *cp;
     INT i;

     for (i=1,cp=fileName ; *cp != '\0' && *cp != '.' ; ++i,++cp) {
          if (i > GCMAXFILE || !isValidFileNameChar(*cp)) {
               return(false);
          }
     }
     if (*cp++ == '.') {
          for (i=1 ; *cp != '\0' ; ++i,++cp) {
               if (i > GCMAXEXT || !isValidFileNameChar(*cp)) {
                    return(false);
               }
          }
     }
     return(!rsvnam(fileName));
}

bool
isValidFileNameChar(                    // is this a valid DOS file name char?
CHAR c)                                 //   character to test
{
     return(c >  ' ' && c != '.' && c != '"' && c != '/' && c != '\\'
         && c != '[' && c != ']' && c != ':' && c != ';' && c != '|'
         && c != '<' && c != '>' && c != '+' && c != '=' && c != ',');
}



