/***************************************************************************
 *                                                                         *
 *   VDIRBBS.C                                                             *
 *                                                                         *
 *   Copyright (c) 1994-1997 Galacticomm, Inc.    All Rights Reserved.     *
 *                                                                         *
 *   Virtual Directory API Server for File Libraries, etc.                 *
 *                                                                         *
 *   Glue for getting:  FTP Server Daemon / Virtual Directory API          *
 *   to live with:  File Libraries / File Reservations Kernel.             *
 *   This is the only source file with references to both systems,         *
 *   except that donewrit() is called in FTPD.C.                           *
 *                                                                         *
 *                                        - RNStein  7/11/94               *
 *                                                                         *
 ***************************************************************************/

#include "gcomm.h"
#include "vdir.h"
#include "vdiros.h"
#include "vdirbbs.h"
#include "majorbbs.h"
#include "filexfer.h"
#include "galfilh.h"

#define FILREV "$Revision: 13 $"

#define FFNAMESIZ (sizeof(((struct ffblk *)0)->ff_name))

static INT libdir(VOID);
static INT lib1st(CHAR *fspec);
static INT libnxt(VOID);
static CHAR *libinf(INT type);
static CHAR *pthutl(CHAR *fspec);
static INT libwrap(INT isdos);
static CHAR *libpth(VOID);
static VOID librcd(INT nml,LONG numbyt);
static INT liblog(CHAR *desc);
static INT libdel(VOID);
static INT librnm(CHAR *newnam);
static VOID libprep(VOID);
static INT libfile(VOID);
static LONG libsroom(void);
static GBOOL delkwds(void);
static VOID delfile(void);

CHAR *osvkey="SYSOP";              /* key required to view OS sub-tree     */
CHAR *osgkey="SYSOP";              /* key required to read from OS sub-tree*/
CHAR *ospkey="SYSOP";              /* key required to write to OS sub-tree */
CHAR *libkey="";                   /* extra key req for Library sub-tree   */

struct vdirsvc vdirlib={           /* VDIRserver for File Library sub-tree */
     NULL,                         /* next link in list of VDIRservers     */
     SLS"LIBRARY",                 /* directory prefix                     */
     libdir,                       /* select new subdir (vdirscb->trgdir)  */
     lib1st,                       /* find one, or first of a set, of files*/
     libnxt,                       /* find next files in a set             */
     libinf,                       /* information (VDFXXX/VDDXXX in VDIR.H)*/
     libpth,                       /* DOS path for file from vds1st/nxt=1  */
     librcd,                       /* record reading a file fm vds1st/nxt=1*/
     liblog,                       /* log in new file after vds1st=0/1     */
     libdel,                       /* delete file from vds1st/nxt=1        */
     librnm,                       /* rename file from vds1st/nxt=1        */
     NULL,                         /* make dir from recent vds1st=0        */
     NULL,                         /* remove dir from recent vdsdir=1      */
     0,                            /* creation date                        */
     0                             /* creation time                        */
};

#define LIBROOT 0L                 /* indicates "master" Library directory */

struct fllib *vlib;                /* global Library pointer               */

static
LONG bytesroom;                    /* bytes free for uploading to a Library*/
                                   /* (set by libsroom()) - if over limit  */

VOID EXPORT
init__galvdir(VOID)                /* Initialize BBS's basic VDIRservers  */
{
     struct ffblk fb;

     if (fnd1st(&fb,"MAIN",FAMDIR)) {
          vdirlib.credat=fb.ff_fdate;
          vdirlib.cretim=fb.ff_ftime;
     }
     if (fnd1st(&fb,SLS".",FAMDIR)) {
          vdiros.credat=fb.ff_fdate;
          vdiros.cretim=fb.ff_ftime;
     }
     regvds(&vdiros);
     regvds(&vdirlib);
}

VOID EXPORT
initwc__galvdir(VOID)
{
     init__galvdir();
}

GBOOL
canosvis(VOID)                     /* Can user view OS file names?         */
{
     return(haskey(osvkey));
}

GBOOL
canosget(VOID)                     /* Can user read OS files?              */
{
     return(haskey(osgkey));
}

GBOOL
canosput(VOID)                     /* Can user write OS files?             */
{
     return(haskey(ospkey));
}

static INT
libdir(VOID)                       /* Library VDIRserver: select directory */
{                                  /* returns 1=valid, 0=not               */
     CHAR *ptr;

     if (!haskey(libkey)) {
          return(0);
     }
     ptr=vdirscb->trgdir;
     if (ptr[0] == '\0') {
          vdirscb->trgnum=LIBROOT;
          return(1);
     }
     if ((vlib=libfind(ptr)) == NULL) {
          return(0);
     }
     vdirscb->trgnum=(LONG)vlib;
     return(haslibkey(vlib,vlib->keyreq)
         && (vlib->flags&(FLGCBD|FLGHID)) == 0);
}

static INT
lib1st(                            /* First file in multi-file group       */
CHAR *fspec)                       /* file name or wildcard specification  */
{                                  /* returns 1=got, 0=none, 2=call again  */
     INT isdos;
     INT rc;

     if (vdirscb->trgnum == LIBROOT) {
          if (fspec[0] == '\0' || fspec[0] == '*' && (vdflags&VDSWLD)) {
               vdirscb->j.misc=0;
               return(2);
          }
          return(0);
     }
     libprep();
     isdos=fnd1st(&vdirscb->fb,pthutl(autwex(fspec)),0);
     if (!isdos) {
          stlcpy(vdirscb->fb.ff_name,fspec,FFNAMESIZ);
     }
     rc=libwrap(isdos);
     if ((vdirscb->flags&VDPWRIT)
      && (vdflags&VDSITW)
      && w2writ(vlib->libname,vdirscb->fb.ff_name,0) != NOCONFLICT) {
          vdirscb->flags&=~VDPWRIT;
     }
     return(rc);
}

static INT
libnxt(VOID)                       /* get next file in multi-file spec     */
{
     INT libn;
     struct ffblk fb;

     if (vdirscb->trgnum == LIBROOT) {
          if ((libn=vdirscb->j.misc) < numoflib) {
               vdirscb->j.misc++;
               vlib=liboff(libn);
               if (haslibkey(vlib,vlib->keyreq)
                && (vlib->flags&(FLGCBD|FLGHID)) == 0) {
                    vdirscb->flags|=VDPVSBL;
                    if (haslibkey(vlib,vlib->ulkey)) {
                         vdirscb->flags|=VDPWRIT;
                    }
                    if (haslibkey(vlib,vlib->dlkey)) {
                         vdirscb->flags|=VDPREAD;
                    }
               }
               vdirscb->fb.ff_attrib=FAMDIR;
               stlcpy(vdirscb->fb.ff_name,vlib->libname,FFNAMESIZ);
               if (fndfile(&fb,vlib->path[0] != '\0' ? vlib->path
                                                     : vlib->libname,FAMDIR)) {
                    vdirscb->fb.ff_ftime=fb.ff_ftime;
                    vdirscb->fb.ff_fdate=fb.ff_fdate;
               }
               return(1);
          }
          else {
               return(0);
          }
     }
     libprep();
     if (fndnxt(&vdirscb->fb)) {
          return(libwrap(1));
     }
     setmem(&vdirscb->fb,sizeof(struct ffblk),0);
     return(0);
}

static CHAR *
pthutl(                            /* construct path prefix to Library file*/
CHAR *fspec)                       /* vlib implicit input                  */
{                                  /* use with caution, static return val  */
     static CHAR buffer[GCMAXPTH];

     stlcpy(buffer,vlib->path[0] != '\0' ? vlib->path : vlib->libname,GCMAXPTH);
     stlcat(buffer,SLS,GCMAXPTH);
     stlcat(buffer,fspec,GCMAXPTH);
     return(buffer);
}

static INT
libwrap(                           /* wrap up Library file permissions, etc*/
INT isdos)                         /* 1=DOS file exists (see vdirscb->fb)  */
{                                  /* vlib implicit input, curusr in effect*/
     INT islib;

     libprep();
     islib=libfile();
     /* check for file exists */
     if ((vlib->flags&FLGDOS) ? isdos : islib) {
          vdirscb->flags|=VDPEXIS;
     }
     /* check for user can see library */
     if (haslibkey(vlib,vlib->keyreq)
      && !(vlib->flags&(FLGCBD|FLGHID))) {
          /* check if user can view file */
          if (isdos
           && ((vlib->flags&FLGDOS) || islib)
           && ((vlib->flags&FLGDOS) || !notapped(flf->udate) || isflop(vlib))) {
               vdirscb->flags|=VDPVSBL;
               /* check if user can read file */
               if (haslibkey(vlib,vlib->dlkey)
                && tstcrd(dnlprc(vdirscb->fb.ff_fsize,vlib))
                && w2read(vlib->libname,vdirscb->fb.ff_name) == NOCONFLICT) {
                    vdirscb->flags|=VDPREAD;
               }
          }
          /* check if user can write file */
          if (!(vlib->flags&FLGRDO)
           && haslibkey(vlib,vlib->ulkey)
           && ((libsroom() >= 1L && bytesroom >= vlib->cluster)
            || isdos        /* no advance size restrictions if file exists */
            || haskey(flsysop))) {
               if (!(vlib->flags&FLGDOS)
                && islib
                && !haslibkey(vlib,vlib->overw)
                && (!sameas(usaptr->userid,flf->ulby)
                 || (!notapped(flf->udate)
                  && !haslibkey(vlib,vlib->autoap)))) {
                    /* overwrite rejected -- see parallel */
                    /* logic in isok2ul() in GALFILCS.C */
               }
               else {
                    vdirscb->flags|=VDPWRIT;
                    /* check disk space available */
                    if (haskey(flsysop)) {
                         vdirroom=MAXROOM;
                    }
                    else {
                         vdirroom=bytesroom;
                         if (isdos) {
                              vdirroom+=clfit(vdirscb->fb.ff_fsize,
                                              vlib->cluster);
                         }
                    }
               }
          }
     }
     /* if the file exists on disk, but not in the library, and it is not  */
     /* a DOS-only library, try again (return 2), else just return whether */
     /* it exists on disk or not.                                          */
     return(isdos && !islib && !(vlib->flags&FLGDOS) ? 2 : isdos);
}

static CHAR *
libinf(                            /* Library directory & file information */
INT type)                          /* VDFXXX and VDDXXX in VDIR.H          */
{
     static CHAR fildesc[SDESCLN];
     CHAR *cp;

     if (vdirscb->trgnum == LIBROOT) {
          switch (type) {
          case VDDNAM:
               return("Library 'master' directory.");
          case VDDDSC:
               return("Type \"DIR\" for a list of Libraries.");
          default:
               return("");
          }
     }
     libprep();
     libfile();
     switch (type) {
     case VDFDSC:
          if ((cp=strchr(stzcpy(fildesc,flf->desc,SDESCLN),'\r')) != NULL) {
               *cp='\0';
          }
          return(fildesc);
     case VDFWHO:
          return(flf->ulby);
     case VDDNAM:
          return(spr("%s Library",vlib->libname));
     case VDDDSC:
          return(vlib->libdesc);
     default:
          return("");
     }
}

static CHAR *                      /*   short-life return (til next pthutl)*/
libpth(VOID)                       /* DOS path for current Library file    */
{
     if (vdirscb->trgnum == LIBROOT) {
          return("");
     }
     libprep();
     return(pthutl(vdirscb->fb.ff_name));
}

static VOID
librcd(                            /* record reading of file               */
INT nml,                           /* 1=normal 0=aborted                   */
LONG numbyt)                       /* number of bytes actually read        */
{
     LONG charge;

     if (vdirscb->trgnum == LIBROOT) {
          return;
     }
     libprep();
     libfile();
     if ((nml || chgabt)
      && (charge=dnlprc(numbyt,vlib)) != 0L
      && (charge=hdedcrd(usaptr,charge,0,1)) != 0L
      && !(vlib->flags&FLGDOS)
      && vlib->royal > 0
      && !sameas(flf->ulby,"Sysop")) {
          crdusr(flf->ulby,l2as(charge*(LONG)vlib->royal/100L),0,0);
     }
     if (nml) {
          dnlcount(vlib->libname,vdirscb->fb.ff_name,-1L);
          sv.dwnlds++;
     }
}

static INT
liblog(                            /* Log newly written file into Library  */
CHAR *desc)                        /* one-line description for file        */
                                   /*   VDIR-client is expected to enforce */
{                                  /*   size restrictions, per vdirroom    */
     struct ffblk fb;
     INT approve;
     INT islib;
     CHAR *cp,fptmp[GCMAXFNM];

     if (vdirscb->trgnum == LIBROOT) {
          return(0);
     }
     libprep();
     if (!fnd1st(&fb,libpth(),0)) {
          return(0);
     }
     if (!(vlib->flags&FLGDOS)) {
          if (!okfname(vdirscb->fb.ff_name)) {
               return(0);
          }
          islib=libfile();
          approve=haslibkey(vlib,vlib->autoap);
          if (islib) {
               if (notapped(flf->udate)) {
                    vlib->appwait--;
               }
               chuldate(vlib,flf->udate,0);
               vlib->totbytes-=clfit(flf->siz,vlib->cluster);
               vlib->numfiles--;
          }
          else {
               stzcpy(flf->libname,vlib->libname,FLNAMESZ);
               stzcpy(flf->filname,fb.ff_name,FLFILENM);
          }
          stzcpy(flf->udate,(approve ? ddat2srt(today()) : NOTAPPED),DATESZ);
          flf->utime=now();
          flf->siz=fb.ff_fsize;
          stzcpy(flf->fdate,ddat2srt(fb.ff_fdate),DATESZ);
          flf->tim=fb.ff_ftime;
          stzcpy(flf->numdls,spr(NUMSZP,0L),NUMSZ);
          stzcpy(flf->ulby,usaptr->userid,UIDSIZ);
          if (!islib || desc[0] != '\0') {
               stzcpy(flf->desc,desc,DESCSIZ);
          }
          dfaSetBlk(flfdat);
          if (islib) {
               dfaUpdateV(NULL,FLFILREC+strlen(flf->desc)+1);
          }
          else {
               dfaInsertV(NULL,FLFILREC+strlen(flf->desc)+1);
          }
          if (notapped(flf->udate)) {
               vlib->appwait++;
          }
          chuldate(vlib,flf->udate,1);
          vlib->totbytes+=clfit(fb.ff_fsize,vlib->cluster);
          vlib->numfiles++;
          vlib->flags|=LIBCHN;
          if (!longsrch) {
               addkw(fileparts(GCPART_FILE,flf->filname,fptmp,GCMAXFNM),
                     flf->filname,flf->libname);
               if (*(cp=fileparts(GCPART_EXTN,flf->filname,fptmp,
                                  GCMAXFNM)) != '\0') {
                    addkw(cp,flf->filname,flf->libname);
               }
          }
     }
     sv.uplds++;
     return(1);
}

static INT
libdel(VOID)                       /* delete a Library file                */
{                                  /* returns 1=could, 0=couldn't, 2=trying*/
     INT islib;

     if (vdirscb->trgnum == LIBROOT) {
          return(0);
     }
     libprep();
     islib=libfile();
     if (isflop(vlib) || (islib && sameas(usaptr->userid,flf->ulby))) {
          if (islib) {
               return(delkwds() ? 1 : 2);
          }
          else {
               delfile();
               return(1);
          }
     }
     return(0);
}

static INT
librnm(CHAR *newnam)               /* rename a Library file (not supported)*/
{
     (VOID)newnam;
     return(0);
}

static VOID
libprep(VOID)                      /* prepare for working with Library     */
{
     vlib=(struct fllib *)vdirscb->trgnum;
     setrsv(librsv);
}

static INT                         /*   ret 1=exists already, 0=not        */
libfile(VOID)                      /* read in info about current file      */
                                   /*   if exists, flf has file's data     */
                                   /*   which can be used for updbtv()     */
{                                  /*   vlib (library info) implicit input */
     dfaSetBlk(flfdat);
     if (!(vlib->flags&FLGDOS)
      && dfaAcqEQ(NULL,compkey(vlib->libname,vdirscb->fb.ff_name),COMPLF)) {
          return(1);
     }
     setmem(flf,FLFILREC+DESCSIZ,0);
     return(0);
}

/*--- Following similar to libroom() in GALFILU.C, which isn't exported ---*/

static LONG                        /*   returns file room, sets bytesroom  */
libsroom(VOID)                     /* how much room for an upload?         */
{                                  /*   either output - if limit exceeded  */
     if (vlib->flags&FLGDOS) {
          cntdir(pthutl(STAR));
     }
     else {
          numfils=vlib->numfiles;
          numbytp=vlib->totbytes;
     }
     bytesroom=min(vlib->maxbup,vlib->maxbyt-numbytp);
     return(vlib->maxfil-numfils);
}

/*--- Following routines are mutated clones of those in GALFILCS.C ---*/

static GBOOL                       /*   returns TRUE if done               */
delkwds(void)                      /* delete keyword records               */
{                                  /*   vlib, flf implicit inputs          */
     GBOOL rc;

     dfaSetBlk(flkdat);
     if (dfaAcqEQ(NULL,
                  rcompkey(vlib->libname,vdirscb->fb.ff_name),
                  COMPFL_K)) {
          dfaDelete();
          rc=FALSE;
     }
     else {
          delfile();
          rc=TRUE;
     }
     return(rc);
}

static VOID
delfile(void)                      /* delete btv record and DOS file       */
                                   /*   vlib, flf implicit inputs          */
{                                  /*   expects libfile() was just called  */
     if (!(vlib->flags&FLGCBD)) {
          unlink(libpth());
     }
     if (!(vlib->flags&FLGDOS)) {
          vlib->numfiles--;
          vlib->totbytes-=clfit(flf->siz,vlib->cluster);
          if (notapped(flf->udate)) {
               vlib->appwait--;
          }
          chuldate(vlib,flf->udate,0);
          vlib->flags|=LIBCHN;
          dfaSetBlk(flfdat);
          dfaDelete();
     }
}
