/***************************************************************************
 *                                                                         *
 *   BBSRIP.C                                                              *
 *                                                                         *
 *   Copyright (c) 1993-1997 Galacticomm, Inc.       All Rights Reserved.  *
 *                                                                         *
 *   This is Worldgroup auto-sensor for the RIP protocol, sensing          *
 *   whether or not the connecting user supports RIP graphics.  Also,      *
 *   this is where any special RIP hooks will be placed for a smoother     *
 *   RIP integration.                                                      *
 *                                              - C. Robert  2/17/93       *
 *                                                                         *
 *   Logon and logoff routines added to check for the proper               *
 *   size/date/time of the icon library signature file on users' hard      *
 *   drives, and warn the user if his library is out of date.              *
 *                                              - Bill Hyatt 6/9/93        *
 *                                                                         *
 *   Enhance logon and logoff routines to enable downloading of custom     *
 *   libraries.                                                            *
 *                                              - Bill Hyatt 10/17/93      *
 *                                                       and 1/2/94        *
 *                                                                         *
 ***************************************************************************/

#include "gcomm.h"
#include "majorbbs.h"
#include "filexfer.h"
#include "wgsrip.h"

#define FILREV "$Revision: 9 $"

#define RIPMNM   "Custom RIP Lib. Download"
                               /* special non-.MDF-file name for this modl */
#define RIPVAL   5             /* if RIP, set RIP langs to this value      */
#define RIPNVL   1             /* if not RIP, set langs to this (dft)      */
#define SLOWAI   (3*16)        /* time to wait for rsp beg (X25/300 bd)    */
#define OTHWAI   (2*16)        /* time to wait for rsp beg (other chns)    */
#define TOTWAI   (6*16)        /* how long to wait for total response      */
#define ABTWAI   4             /* sec's to wait b4 prfing after abt d/l    */
#define B4VERS   8             /* # of chars b4 ver code in resp stg       */
#define DNMSIZ   (80+1)        /* maximum sixe of directory name           */
#define SFISIZ   (60+1)        /* maximum size of signature file info      */
#define TIMSIZ   (25+1)        /* maximum size of time-to-d/l desc's       */
#define ROFSIZ   (12+6+1)      /* len of info to append to sgnfiq          */
#define FORCE    1             /* force user to download custom library    */
#define CHOOSE   2             /* let user choose to  "    "       "       */
#define WARN     3             /* just warn user of out-of-date library    */
#define MULT     1             /* custom library is set of multiple files  */
#define ARCHVD   2             /*   "       "    "  single file archive    */
#define LOGON    1             /* gen purp indicator: "during logon"       */
#define LOGOFF   2             /*  "   "       "      "during logoff"      */
#define BOTH     3             /*  "   "       "      "both"               */

/* non-.MSG-file sub-states for riplon() and riplof()                      */
#define OUTDWARN -1            /* out of date: just warn user              */
#define OUTDFM   -2            /* out of date: force d/l, mult files       */
#define OUTDCM   -3            /* out of date: choose to d/l, mult files   */
#define OUTDFA   -4            /* out of date: force d/l, archive file     */
#define OUTDCA   -5            /* out of date: choose to d/l, archive file */
#define OUTDFC   -6            /* out of date: force d/l, mult or archive  */
#define DLCHCEXP -7            /* explain choice of download methods       */
#define OUTDCC   -8            /* out of date: choose to d/l, mult or archv*/
#define WAITPRF  -9            /* waiting to prf after aborted download    */
#define OUTDAFM  -10           /* aborted d/l: force restart, mult files   */
#define OUTDAFA  -11           /* aborted d/l: force restart, archive file */
#define OUTDACM  -12           /* aborted d/l: choose to restrt, mult files*/
#define OUTDACA  -13           /* aborted d/l: choose to restrt, archive   */
#define UNZIPREM -14           /* after d/l, remind user to unzip archive  */

#define dlabt() ((ftfscb->tryfil == 0 && ftfscb->actfil == 0) \
                 || (ftfscb->tryfil > ftfscb->actfil))
                               /* did usr try to abort icon lib download?  */
#define shdchk(when) (isripu() && hasclb && \
                      (chksgn == (when) || chksgn == BOTH))
                               /* should user's HD be checked now?         */

/* BBSRIP.C 03/01/94 10.43.10 */
VOID inirmdl (VOID);
static GBOOL ripsns(UINT snccon,CHAR *incbuf,INT nbytes);
static INT isamat(CHAR newc,CHAR rspc);
static INT chkdir(CHAR *dirnam);
static INT chkspc(CHAR *arcspc);
static GBOOL riplon(VOID);
static GBOOL ripinp(VOID);
static VOID ripsts(VOID);
static GBOOL riplof(VOID);
static INT outofdte(INT when);
static VOID outdmsg(INT when);
static CHAR *esttim(LONG nbytes);
static VOID dldclb(INT dldmod,INT whndl);
static INT tshrip(INT tshcod);
static INT act2tk(INT when);
VOID ripcls(VOID);

struct module ripmdl={             /* module interface block               */
     "",                           /*    name used to refer to this module */
     riplon,                       /*    user logon supplemental routine   */
     ripinp,                       /*    input routine if selected         */
     ripsts,                       /*    status-input routine if selected  */
     NULL,                         /*    "injoth" routine for this module  */
     riplof,                       /*    user logoff supplemental routine  */
     NULL,                         /*    hangup (lost carrier) routine     */
     NULL,                         /*    midnight cleanup routine          */
     NULL,                         /*    delete-account routine            */
     NULL                          /*    finish-up (sys shutdown) routine  */
};

extern HMCVFILE ripmb;             /* bbsrip.msg                           */

INT ripstt;                        /* BBSRIP auto-download state           */

static
CHAR *minver,                      /* minimum RIPterm version for this svr */
     *sgnnam,                      /* nm of svr icon lib "signature" file  */
     bbssfi[SFISIZ],               /* signature file info for server       */
     *sgnfiq,                      /* RIP cmd for sig file inq (w/ file nm)*/
     *clbpth,                      /* path for mult custom library files   */
     *arcspc,                      /* path and name of single file archive */
     *arcdir,                      /* dir path of single file archive      */
     arcnam[FNMSIZ],               /* name of single file archive          */
     *dlpath,                      /* d/l path for tshrip() (see dldclb()) */
     *bypsky,                      /* key required to bypass forced d/l's  */
     dltime[TIMSIZ],               /* d/l time phrase for MULT/ARCHVD lib  */
     dltim2[TIMSIZ],               /* 2nd d/l time phrase for library BOTH */
     *autrip="    [!\b\b\b\r",   /* auto-sensing RIP query string        */
     *begrsp="RI",                 /* beginning of RIP response string     */
     *riprsp="RIPSCRIP000000";     /* RIP response (0s could be any digit) */

static
INT hasclb,                        /* does this svr have a custom library? */
    chksgn,                        /* when to chk usrs HD for sgn file     */
    clbfrm,                        /* stored as archive or mult files?     */
    odonactn=0,                    /* action at logon  if user out-of-date */
    odofactn=0;                    /* action at logoff if user out of date */

static
LONG mverln,                       /* minimum RIPterm version (as a long)  */
     arcsiz,                       /* size of single file archive          */
     dirsiz;                       /* total bytes in cust lib directory    */

struct ripstf {                    /* RIP user information                 */
     INT matcnt;                   /*   count of matched chars so far      */
     CHAR vercod[RIPVSZ];          /*   version code string so far         */
     INT whndl;                    /*   d/l'ing cust lib in logon or -off  */
     INT dldmod;                   /*      "    as sep files or archive    */
     LONG riptck;                  /*   timer to delay prf's in abt'd d/l's*/
} *ripgys,                         /* array of RIP info for every user     */
  *ripusr;                         /* pointer to current guy's RIP info    */

struct ftfpsp ripzmd;              /* special form of ZMODEM protocol spec */

INT rzcopied=0;                    /* have we filled ripzmd yet?           */

VOID                               /* note: this is called from majorbbs.c */
inirmdl(VOID)                      /* initialize the RIP module            */
{
     CHAR *cp,*rawstr;
     INT pthok,spcok;
     struct ffblk sgnfbptr;

     setmbk(ripmb);
     stzcpy(ripmdl.descrp,RIPMNM,MNMSIZ);
     ripstt=register_module(&ripmdl);
     mverln=atol(minver=stgopt(MINVER));
     if (strlen(minver) != 6) {
          catastro("RIP MINIMUM VERSION MUST BE 6 CHARACTERS!");
     }
     ripgys=(struct ripstf *)alczer(nterms*sizeof(struct ripstf));
     regautsns(ripsns);
     if ((hasclb=ynopt(HASCLB)) != 0) {
          if (*(sgnnam=stgopt(SGNNAM)) == '\0') {
               shocst("WGSRIP: No signature file used.",
                      "No signature file name specified for icons check");
               hasclb=0;
               return;
          }
          if (!fndfile(&sgnfbptr,sgnnam,0)) {
               shocst("WGSRIP: Signature file error!",
                      "Can't find icon signature file %s",sgnnam);
               hasclb=0;
               return;
          }
          sgnfiq=alcmem(strlen(rawstr=rawmsg(SGNFIQ))+ROFSIZ);
          stzcpy(sgnfiq,rawstr,strlen(rawstr)+1);
          strcat(sgnfiq,(cp=strrchr(sgnnam,SL)) != NULL ? cp+1
           : (cp=strrchr(sgnnam,':')) != NULL ? cp+1 : sgnnam);
       strcat(sgnfiq,RNMOR3);
          sprintf(bbssfi,"1.%s.%02d/%02d/%02d.%02d:%02d:%02d",
            l2as(sgnfbptr.ff_fsize),ddmon(sgnfbptr.ff_fdate),
            ddday(sgnfbptr.ff_fdate),ddyear(sgnfbptr.ff_fdate)%100,
            dthour(sgnfbptr.ff_ftime),dtmin(sgnfbptr.ff_ftime),
            dtsec(sgnfbptr.ff_ftime));
          chksgn=tokopt(CHKSGN,"LOGON","LOGOFF","BOTH",NULL);
          if (chksgn == LOGON || chksgn == BOTH) {
               odonactn=tokopt(ODONACTN,"FORCE","CHOOSE","WARN",NULL);
          }
          if (chksgn == LOGOFF || chksgn == BOTH) {
               odofactn=tokopt(ODOFACTN,"FORCE","CHOOSE","WARN",NULL);
          }
          bypsky=stgopt(BYPSKY);
          if (odonactn != WARN || odofactn != WARN) {
               switch (clbfrm=tokopt(CLBFRM,"MULT","ARCHVD","BOTH",NULL)) {
            case MULT:
                    if (!chkdir(clbpth=pthopt(CLBPTH))) {
                         odonactn=odofactn=WARN;
                         return;
                    }
                    else {
                         dlpath=alcmem(strlen(clbpth)+1);
                    }
                    break;
               case ARCHVD:
                    if (!chkspc(arcspc=stgopt(ARCSPC))) {
                         odonactn=odofactn=WARN;
                         return;
                    }
                    else {
                         dlpath=alcmem(strlen(arcdir)+1);
                    }
                    break;
               case BOTH:
                    pthok=chkdir(clbpth=pthopt(CLBPTH));
                    spcok=chkspc(arcspc=stgopt(ARCSPC));
                    if (!pthok && !spcok) {
                         odonactn=odofactn=WARN;
                         return;
                    }
                    else if (pthok && !spcok) {
                         clbfrm=MULT;
                         dlpath=alcmem(strlen(clbpth)+1);
                    }
                    else if (!pthok && spcok) {
                         clbfrm=ARCHVD;
                         dlpath=alcmem(strlen(arcdir)+1);
                    }
                    else {
                         dlpath=alcmem(max(strlen(clbpth)+1,strlen(arcdir)+1));
                    }
               }
          }
     }
}

static GBOOL
ripsns(                            /* RIP protocol auto-sense routine      */
UINT snccon,                       /* time since connect, 1/16 second units*/
CHAR *incbuf,                      /* bytes coming in (not NUL terminated) */
INT nbytes)                        /* number of bytes coming in            */
{
     CHAR c;
     INT begwait;

     ripusr=&ripgys[usrnum];
     if (usrptr->flags&NOHDWE) {
          return(TRUE);
     }
     if (snccon == 0) {
          setmem(ripusr,sizeof(struct ripstf),0);
          btuxct(usrnum,strlen(autrip),autrip);
          return(FALSE);
     }
     while (nbytes-- > 0) {
          if (isamat(c=((*incbuf++)&0x7F),riprsp[ripusr->matcnt])) {
               if (ripusr->matcnt >= B4VERS) {
                    ripusr->vercod[ripusr->matcnt-B4VERS]=c;
               }
               if (++ripusr->matcnt == strlen(begrsp)) {
                    setbyprot(RIPSFX,RIPVAL);
               }
               else if (ripusr->matcnt == strlen(riprsp)) {
                    if (atol(ripusr->vercod) < mverln) {
                         setmbk(ripmb);
                         prfmsg(OLDVER,spr("%.2s.%.2s.%.2s",minver,minver+2,
                                           minver+4));
                         rstmbk();
                         setbyprot(RIPSFX,RIPNVL);
                    }
                    return(TRUE);
               }
          }
     }
     begwait=((usrptr->flags&ISX25) || usrptr->baud == 300 ? SLOWAI : OTHWAI);
     if (snccon >= TOTWAI || (ripusr->matcnt < strlen(begrsp) &&
                     snccon >= begwait)) {
          if (ripusr->matcnt >= strlen(begrsp)) {
               setmbk(ripmb);
               prfmsg(NOVERS,spr("%.2s.%.2s.%.2s",minver,minver+2,minver+4));
               rstmbk();
               setbyprot(RIPSFX,RIPNVL);
          }
          return(TRUE);
     }
     return(FALSE);
}

static INT
isamat(                            /* does latest char match response char?*/
CHAR newc,                         /* new character received               */
CHAR rspc)                         /* response character to match w/       */
{
     return(newc == rspc || (isdigit(newc) && isdigit(rspc)));
}

static INT
chkdir(                            /* see if dir xsts & has files, get size*/
CHAR *dirnam)                      /*    directory to check on             */
{
     CHAR *spec;
     INT rc=1;

     stzcpy(spec=alczer(strlen(dirnam)+3+1),dirnam,strlen(dirnam)+1);
     strcat(spec,STAR);
     cntdir(spec);
     if (numfils == 0) {
          shocst("WGSRIP: invalid directory spec!",
                 "Unable to read files from %s",dirnam);
          rc=0;
     }
     else {
          dirsiz=numbyts;
     }
     free(spec);
     return(rc);
}

static INT
chkspc(                            /* see if archive file exists, get size */
CHAR *arcspc)                      /*    file to check on                  */
{
     struct ffblk fb;
     CHAR *cp;
     INT len;

     if (!fndfile(&fb,arcspc,0)) {
          shocst("WGSRIP: Invalid archive name!",
                 "Unable to find file %s",arcspc);
          return(0);
     }
     if ((cp=strrchr(arcspc,SL)) != NULL) {
          arcdir=alcmem(len=((INT)(cp-arcspc))+1+1);
          stzcpy(arcdir,arcspc,len);
          stzcpy(arcnam,cp+1,FNMSIZ);
     }
     else {
          arcdir=alczer(1);
          stzcpy(arcnam,arcspc,FNMSIZ);
     }
     arcsiz=fb.ff_fsize;
     return(1);
}

static GBOOL
riplon(VOID)                       /* check sgn file at logon, if need to  */
{
     return(shdchk(LOGON) ? outofdte(LOGON) : FALSE);
}

static GBOOL
ripinp(VOID)                       /* RIP stub input routine               */
{
     return(FALSE);
}

static VOID
ripsts(VOID)                       /* RIP status hdlr - waiting to prf     */
{
     if (status == CYCLE) {
          switch (usrptr->substt) {
          case WAITPRF:
               ripusr=&ripgys[usrnum];
               if ((btuTicker()-ripusr->riptck) < ABTWAI) {
                    btuinj(usrnum,CYCLE);
               }
               else {
                    prf(" ");
                    setmbk(ripmb);
                    switch (act2tk(ripusr->whndl)) {
                    case FORCE:
                         switch (ripusr->dldmod) {
                         case MULT:
                              usrptr->substt=OUTDAFM;
                              prfmsg(ripusr->whndl == LOGON ? ODONAFM
                         : ODOFAFM);
                              break;
                         case ARCHVD:
                              usrptr->substt=OUTDAFA;
                              prfmsg((ripusr->whndl == LOGON ? ODONAFA
                          : ODOFAFA),arcnam);
                         }
                         break;
                    case CHOOSE:
                         switch (ripusr->dldmod) {
                         case MULT:
                              usrptr->substt=OUTDACM;
                              prfmsg(ripusr->whndl == LOGON ? ODONACM
                         : ODOFACM);
                              prfmsg(DLRSPMT);
                              break;
                         case ARCHVD:
                              usrptr->substt=OUTDACA;
                              prfmsg((ripusr->whndl == LOGON ? ODONACA
                          : ODOFACA),arcnam);
                              prfmsg(DLRSPMTA,arcnam);
                         }
                    }
                    outprf(usrnum);
               }
               break;
          default:
               dfsthn();
          }
     }
     else {
          dfsthn();
     }
}

static GBOOL
riplof(VOID)                       /* check sgn file at logoff, if need to */
{
     return(shdchk(LOGOFF) ? outofdte(LOGOFF) : FALSE);
}

static INT                         /* common sub-state handler for         */
outofdte(                          /*    riplon() and riplof()             */
INT when)                          /*      handling at logon or logoff?    */
{
     if ((margc == 1 && sameas(margv[0],"x")) &&
      (usrptr->substt == OUTDCM  || usrptr->substt == OUTDCA
       || usrptr->substt == OUTDCC  || usrptr->substt == DLCHCEXP
       || usrptr->substt == OUTDACM || usrptr->substt == OUTDACA)) {
          return(0);
     }
     ripusr=&ripgys[usrnum];
     setmbk(ripmb);
     switch (usrptr->substt) {
     case 0:
          clrprf();
          prf("\r!%s",sgnfiq);
          usrptr->substt=SGNFIQ;
          break;
     case SGNFIQ:
          if (sameas(margv[0],bbssfi)) {
               return(0);
          }
          if (when == LOGOFF) {
               prfmsg(ODOFHDR);
          }
          outdmsg(when);
          break;
     case OUTDWARN:
          return(0);
     case OUTDFM:
          dldclb(MULT,when);
          break;
     case OUTDCM:
          switch (lingyn(margv[0][0])) {
          case 'Y':
               dldclb(MULT,when);
               break;
          case 'N':
               return(0);
          default:
               prfmsg(DLPMT);
          }
          break;
     case OUTDFA:
          dldclb(ARCHVD,when);
          break;
     case OUTDCA:
          switch (lingyn(margv[0][0])) {
          case 'Y':
               dldclb(ARCHVD,when);
               break;
          case 'N':
               return(0);
          default:
               prfmsg(DLPMTA,arcnam);
          }
          break;
     case OUTDFC:
     case DLCHCEXP:
          switch (toupper(margv[0][0])) {
          case 'B':
               dldclb(MULT,when);
               break;
          case 'S':
               dldclb(ARCHVD,when);
               break;
          default:
               prfmsg(DLPMTC);
          }
          break;
     case OUTDCC:
          switch (lingyn(margv[0][0])) {
          case 'Y':
               usrptr->substt=DLCHCEXP;
               prfmsg((when == LOGON ? DLCEXPON : DLCEXPOF),arcnam,arcnam);
               prfmsg(DLTMBTH,dltime,arcnam,dltim2);
               prfmsg(DLPMTC);
               break;
          case 'N':
               return(0);
          default:
               prfmsg(DLPMT);
          }
          break;
     case WAITPRF:
          return(1);
     case OUTDAFM:
          dldclb(MULT,when);
          break;
     case OUTDAFA:
          dldclb(ARCHVD,when);
          break;
     case OUTDACM:
          switch (lingyn(margv[0][0])) {
          case 'Y':
               dldclb(MULT,when);
               break;
          case 'N':
               return(0);
          default:
               prfmsg(DLRSPMT);
          }
          break;
     case OUTDACA:
          switch (lingyn(margv[0][0])) {
          case 'Y':
               dldclb(ARCHVD,when);
               break;
          case 'N':
               return(0);
          default:
               prfmsg(DLRSPMTA,arcnam);
          }
          break;
     case UNZIPREM:
     case DLDDUN:
          if (when == LOGOFF) {
               prfmsg(ODOFTRL);
          }
          return(0);
     case CANTDL:
          return(0);
     }
     outprf(usrnum);
     return(1);
}

static VOID
outdmsg(                           /* tell usr lib out of date, set substt */
INT when)                          /*    telling them at logon or logoff?  */
{
     INT action;

     if ((action=act2tk(when)) != WARN) {
          switch (clbfrm) {
          case MULT:
               strcpy(dltime,esttim(dirsiz));
               break;
          case ARCHVD:
               strcpy(dltime,esttim(arcsiz));
               break;
          case BOTH:
               strcpy(dltime,esttim(dirsiz));
               strcpy(dltim2,esttim(arcsiz));
          }
     }
     switch (action) {
     case FORCE:
          switch (clbfrm) {
          case MULT:
               usrptr->substt=OUTDFM;
               prfmsg(when == LOGON ? ODONFM : ODOFFM);
               prfmsg(DLTIME,dltime);
               prfmsg(RET2CN);
               break;
          case ARCHVD:
               usrptr->substt=OUTDFA;
               prfmsg((when == LOGON ? ODONFA : ODOFFA),arcnam,arcnam);
               prfmsg(DLTIME,dltime);
               prfmsg(RET2CN);
               break;
          case BOTH:
               usrptr->substt=OUTDFC;
               prfmsg(when == LOGON ? ODONFC : ODOFFC);
               prfmsg((when == LOGON ? DLCEXPON : DLCEXPOF),arcnam,arcnam);
               prfmsg(DLTMBTH,dltime,arcnam,dltim2);
               prfmsg(DLPMTC);
          }
          break;
     case CHOOSE:
          switch (clbfrm) {
          case MULT:
               usrptr->substt=OUTDCM;
               prfmsg(when == LOGON ? ODONCM : ODOFCM);
               prfmsg(DLTIME,dltime);
               prfmsg(DLPMT);
               break;
          case ARCHVD:
               usrptr->substt=OUTDCA;
               prfmsg((when == LOGON ? ODONCA : ODOFCA),arcnam,arcnam);
               prfmsg(DLTIME,dltime);
               prfmsg(DLPMTA,arcnam);
               break;
          case BOTH:
               usrptr->substt=OUTDCC;
               prfmsg(when == LOGON ? ODONCC : ODOFCC);
               prfmsg(DLPMT);
          }
          break;
     case WARN:
          usrptr->substt=OUTDWARN;
          prfmsg(when == LOGON ? ODONWRN : ODOFWRN);
          break;
     }
}

static CHAR *
esttim(                            /* estimate download time               */
LONG nbytes)                       /*    total number of bytes to download */
{
     LONG est;

     est=atol(xlttxv(spr("N'COMEFF"),5));
     if (est == 0L) {
          est=70L;
     }
     est*=(LONG)usrptr->baud;
     if (nbytes < 2000000L) {
          est=(nbytes*1000+(est>>1))/est;
     }
     else {
          est=est/1000;
          est=(nbytes+(est>>1))/est;
     }
     return(est < 60L ? "less than one minute"
         : spr("approximately %s minutes",l2as(est/60L)));
}

static VOID
dldclb(                            /* d/l custom library                   */
INT dldmod,                        /*    mult files or single archive?     */
INT whndl)                         /*    d/l'ing during logon or logoff?   */
{
     if (ftgnew()) {
          switch (dldmod) {
          case MULT:
               strcpy(ftgptr->tagspc,STAR);
               stzcpy(dlpath,clbpth,strlen(clbpth)+1);
               ftgptr->flags=FTGWLD;
               break;
          case ARCHVD:
               strcpy(ftgptr->tagspc,arcnam);
               stzcpy(dlpath,arcdir,strlen(arcdir)+1);
               ftgptr->flags=0;
          }
          ripusr->whndl=whndl;
          ripusr->dldmod=dldmod;
          ftgptr->tshndl=tshrip;
     }
     if (ftgsbm("Z") && ftuptr->ftfpsp != NULL) {
          clrprf();
          if (!rzcopied) {
               movmem(ftuptr->ftfpsp,&ripzmd,sizeof(struct ftfpsp));
               ripzmd.flags|=FTFAFN;
               rzcopied=1;
          }
          ftuptr->ftfpsp=&ripzmd;
          if (dldmod == MULT) {
               prf("\r!%s\r",RIPEBM);
          }
     }
     else {
          clrprf();
          setmbk(ripmb);
          usrptr->state=ripstt;
          prfmsg(usrptr->substt=CANTDL,maxtags);
     }
     outprf(usrnum);
     clrprf();
}

static INT
tshrip(                           /* tagspec hdlr for d/l of custom lib    */
INT tshcod)                       /*    exit point                         */
{
     INT rc=0;                    /*  usrnum,usrptr,usaptr,vdaptr return   */
     FILE *fp;                    /*  value meaning depends on tshcod      */
                                  /*  implicit input/output in many cases: */
                                  /*  tshmsg expect caller to do outprf()  */
                                  /*  if any                               */

     ripusr=&ripgys[usrnum];
     setmbk(ripmb);
     switch (tshcod) {
     case TSHDSC:                 /*  describe tagspec in English          */
          sprintf(tshmsg,"file(s) \"%s\"",ftgptr->tagspc);
          break;
     case TSHVIS:                 /* visible to this user?                 */
          if (ftgptr->flags&FTGWLD) {
               rc=1;
          }
          else {
               strcpy(tshmsg,dlpath);
               strcat(tshmsg,ftgptr->tagspc);
               if ((fp=fopen(tshmsg,FOPRB)) != NULL) {
                    fread(tshmsg,1,TSHLEN,fp);
                    fclose(fp);
                    rc=1;
               }
          }
          break;
     case TSHSCN:                 /*  scan of multi-file wildcard tagspec  */
          strcpy(tshmsg,dlpath);
          strcat(tshmsg,ftgptr->tagspc);
          if (fnd1st(&ftuptr->fb,tshmsg,0)) {
               strcpy(tshmsg,ftuptr->fb.ff_name);
               rc=1;
          }
          else {
               tshmsg[0]='\0';    /*  clean up after myself                */
          }
          break;
     case TSHNXT:                 /*  next file in wildcard scan           */
          if (fndnxt(&ftuptr->fb)) {
               strcpy(tshmsg,ftuptr->fb.ff_name);
               rc=1;
          }
          break;
     case TSHBEG:                 /*  begin dwnld, chk permission, reserve */
          strcpy(tshmsg,dlpath);
          strcat(tshmsg,ftgptr->tagspc);
          strcpy(ftfscb->fname,ftgptr->tagspc);
          rc=1;
          break;
     case TSHEND:                 /*  end download of a file, unreserve    */
     case TSHSKP:                 /*  skip incomplete download of a file   */
          break;
     case TSHFIN:                 /*  finish file tranfer session          */
          usrptr->state=ripstt;
          clrprf();
          if (!dlabt()) {
               prf(" ");
               switch (ripusr->dldmod) {
               case MULT:
                    prfmsg(usrptr->substt=DLDDUN);
                    break;
               case ARCHVD:
                    usrptr->substt=UNZIPREM;
                    prfmsg((ripusr->whndl == LOGON ? UZREMON : UZREMOF),
                           arcnam);
               }
          }
          else {
               ripusr->riptck=btuTicker();
               btuinj(usrnum,CYCLE);
               usrptr->substt=WAITPRF;
          }
          rc=1;
          break;
     case TSHHUP:                 /*  end session because user logging off */
          break;
     }
     return(rc);
}

static INT
act2tk(                           /* determine action to take for a user   */
INT when)                         /*      action at logon or logoff?       */
{
     INT action;

     action=(when == LOGON ? odonactn : odofactn);
     if (action == FORCE && (haskey(bypsky) || haskey(syskey))) {
          action=CHOOSE;
     }
     return(action);
}

VOID                              /* note: this is called from MAJORBBS.C  */
ripcls(VOID)                      /* close down the RIP module             */
{
     clsmsg(ripmb);
}
