/***************************************************************************
 *                                                                         *
 *   FINGERD.C                                                             *
 *                                                                         *
 *   Copyright (c) 1995-1997 Galacticomm, Inc.                             *
 *                                                                         *
 *   Finger server for access to user information as stored in the         *
 *   Registry of Users, and also for finding out who is online, per        *
 *   RFC 1288.                                                             *
 *                                                                         *
 *                               2/20/95 - Bob Stein & Mahesh Neelakanta   *
 *                                                                         *
 ***************************************************************************/

#include "gcomm.h"
#include "majorbbs.h"
#include "remote.h"
#include "tcpip.h"
#include "alias.h"
#include "fingerd.h"
#include "galfingd.h"

#define FILREV "$Revision: 17 $"

static VOID fngdincall(INT gotchn);
static GBOOL fngdinp(VOID);
static VOID dftfrq(VOID);
static VOID fngonl(VOID);
static INT fngusr(CHAR *usrnam);
static VOID dftrpt(CHAR *userid);
static VOID fngdsts(VOID);
static INT mrgpar(CHAR *parm);

INT fngdstt;                       /* Finger server module state number    */
struct module fngdmodule={         /* module interface block               */
     "",                           /*    name used to refer to this module */
     NULL,                         /*    user logon supplemental routine   */
     fngdinp,                      /*    input routine if selected         */
     fngdsts,                      /*    status-input routine if selected  */
     NULL,                         /*    "injoth" routine for this module  */
     NULL,                         /*    user logoff supplemental routine  */
     NULL,                         /*    hangup (lost carrier) routine     */
     NULL,                         /*    midnight cleanup routine          */
     NULL,                         /*    delete-account routine            */
     NULL                          /*    finish-up (sys shutdown) routine  */
};

VOID (*hdlfrq)(VOID)=dftfrq;       /* Finger request handler vector        */
                                   /* request: margv[0],...,margv[margc-1] */
VOID (*usrrpt)(                    /* Finger user report vector            */
     CHAR *usrnam)=dftrpt;         /* user name                            */

CHAR *fnguid;                      /* User-ID identified by fngusr()       */

HMCVFILE fngdmb;                   /* GALFINGD.MCV file                    */
INT fngdreg;                       /* display registry info for users?     */
CHAR *fngdkey;                     /* key required to be fingerable        */
CHAR *fngdhide;                    /* key required to hide from finger req */
INT fngdonl;                       /* Finger server online?                */
INT fngdrej;                       /* reject or ignore calls (fngdonl=0)   */
INT maxfngd;                       /* maximum finger requests at a time    */
INT audfngd;                       /* audit successful Finger requests?    */
INT audfngde;                      /* audit invalid Finger requests?       */
UINT fngdpatc;                     /* patience for activity, 1/16 sec units*/

#define FNGDBEG 0                  /* connected to remote client, await cmd*/
#define FNGDRPL 1                  /* command received, sending reply      */
#define FNGDDMY 2                  /* dummy state formatting registry info */
#define FNGDEND 3                  /* waiting for output buffers to empty  */

VOID EXPORT
init__galfingd(VOID)               /* Initialization                       */
{
     init__tcpip();
     fngdmb=opnmsg("galfingd.mcv");
     fngdonl=ynopt(FNGDONL);
     fngdrej=ynopt(FNGDREJ);
     if (fngdonl || fngdrej) {
          fngdreg=ynopt(FNGDREG);
          fngdkey=stgopt(FNGDKEY);
          fngdhide=stgopt(FNGDHIDE);
          maxfngd=numopt(MAXFNGD,0,250);
          audfngd=ynopt(AUDFNGD);
          audfngde=ynopt(AUDFNGDE);
          fngdpatc=numopt(FNGDPATC,0,2047)*16;
          stzcpy(fngdmodule.descrp,gmdnam("galfingd.mdf"),MNMSIZ);
          fngdstt=register_module(&fngdmodule);
          regtcpsvr(FNGNAME,FNGPORT,FNGBACKLOG,fngdincall);
          dclvda(sizeof(struct fngdusr));
     }
     else {
          clsmsg(fngdmb);
     }
}

VOID EXPORT
initwc__galfingd(VOID)
{
     init__galfingd();
}

static VOID
fngdincall(                        /* begin incoming Finger session        */
INT gotchn)                        /* 1=chan (curusr) assigned, 0=not avail*/
{                                  /* implicit:  clskt=socket to client    */
     struct tcpipinf *tip;

     setmbk(fngdmb);
     clrprf();
     if (!gotchn) {
          sprintf(prfbuf,xlttxv(stpans(getasc(kilipg || errcod != 1 ?
                                BBSSHUT : BBSFULL)),mxmssz),numcdi("TCP/IP"));
          send(clskt,prfbuf,strlen(prfbuf),0);
     }
     else {
          setmem(fngdptr,sizeof(struct fngdusr),0);
          tip=&tcpipinf[usrnum];
          usrptr->usrcls=BBSPRV;
          usrptr->state=fngdstt;
          usrptr->substt=FNGDBEG;
          usrptr->flags|=NOGLOB+NOINJO;
          btuech(usrnum,0);
          stansi();
          if (!fngdonl) {
               byetcp(FNGNOTON);
               rejinc(FNGNAME,"Finger disabled");
          }
          else if (numonl(fngdstt) > maxfngd) {
               byetcp(FNGDMANY,maxfngd);
               rejinc(FNGNAME,spr("%d Finger connections",maxfngd));
          }
          else {
               shochl("Incoming Finger contact",'Ÿ',0x1F);
               sktnfy(TNFRECV,clskt,tcpinc,tip,usrnum);
               sprintf(usaptr->userid,"(%s) finger",inet_ntoa(tip->inaddr));
               btuinj(usrnum,CYCLE);
               fngdptr->sttime=(USHORT)(hrtval()/4096U);
          }
     }
}

static GBOOL
fngdinp(VOID)                      /* Finger server input line handler     */
{
     setmbk(fngdmb);
     switch (usrptr->substt) {
     case FNGDBEG:
          (*hdlfrq)();
          break;
     case FNGDDMY:
          break;
     }
     return(TRUE);
}

VOID
dftfrq(VOID)                       /* default (*hdlfrq)() finger handler   */
{                                  /* request: margv[0],...,margv[margc-1] */
     INT savusr;
     CHAR *request;

     mrgpar("/W");
     rstrin();
     request=margv[0];
     if (strchr(request,'@') != NULL) {
          byetcp(FNGDFOR);
     }
     else if (request[0] == '\0') {
          shochl("Fingering online list",'Ÿ',0x1F);
          fngonl();
     }
     else {
          if (fngusr(request)) {
               prfmsg(FNGDUHDR,fnguid,(*alsofusr)(fnguid));
               outprf(usrnum);
               if (fngdreg) {
                    (*usrrpt)(fnguid);
               }
               if (onsys(fnguid)) {
                    savusr=usrnum;
                    curusr(othusn);
                    prfmsg(FNGDUONL);
                    curusr(savusr);
               }
               prfmsg(FNGDUFTR);
               if (audfngd) {
                    shocst("FINGER SERVER REQUEST",
                           "User-ID: %0.50s, Alias: %0.50s, from %s",
                           fnguid,(*alsofusr)(fnguid),
                           inet_ntoa(tcpipinf[usrnum].inaddr));
               }
               shochl("Fingering user",'Ÿ',0x1F);
          }
          else {
               prfmsg(FNGDUNOT,request);
               if (audfngde) {
                    shocst("FINGER SERVER REQUEST INVALID",
                           "User name: %0.50s, from %s",request,
                           inet_ntoa(tcpipinf[usrnum].inaddr));
               }
          }
          outprf(usrnum);
          usrptr->substt=FNGDEND;
          fngdptr->sttime=(USHORT)(hrtval()/4096U);
     }
}

static VOID
fngonl(VOID)                       /* handle fingering all users online    */
{
     usrptr->substt=FNGDRPL;
     fngdptr->unum=0;
     fngdptr->nusers=0;
     prfmsg(FNGDHDR);
     outprf(usrnum);
}

static VOID
hdlonl(VOID)
{
     INT savusr;
     INT usrrpt;

     savusr=usrnum;
     while (usrptr->substt == FNGDRPL && btuoba(usrnum) > 1000) {
          usrrpt=0;
          curusr(fngdptr->unum);
          switch (usrptr->usrcls) {
          case VACANT:
          case ONLINE:
          case SUPIPG:
               break;
          default:
               if (!(usrptr->flags&INVISB)
                && (isuidc(usaptr->userid[0]) || usaptr->userid[0] == '(')
                && haskey(fngdkey)
                && (fngdhide[0] == '\0' || !haskey(fngdhide))) {
                    prfmsg(FNGDLIN);
                    outprf(savusr);
                    usrrpt=1;
               }
               break;
          }
          curusr(savusr);
          if (usrrpt) {
               fngdptr->nusers++;
          }
          if (++fngdptr->unum >= nterms) {
               if (fngdptr->nusers == 0) {
                    prfmsg(FNGDNONE);
               }
               prfmsg(FNGDFTR);
               outprf(usrnum);
               usrptr->substt=FNGDEND;
          }
     }
}

static INT
fngusr(                            /* check specific user to be fingered   */
CHAR *usrnam)                      /* e-mail alias or User-ID to be fingerd*/
{                                  /* returns 1=OK (see fnguid), 0=bad     */
     CHAR buff[80];

     if ((fnguid=usrofals(usrnam)) == NULL) {
          if (hdluid(usrnam) != UIDFND) {
               clrxrf();
               clrprf();
               strrpl(stzcpy(buff,usrnam,sizeof(buff)),'.',' ');
               if (hdluid(buff) != UIDFND) {
                    clrxrf();
                    clrprf();
                    return(0);
               }
               else {
                    fnguid=uidxrf.userid;
               }
          }
          else {
               fnguid=uidxrf.userid;
          }
     }
     if (!uhskey(fnguid,fngdkey)
      || (fngdhide[0] != '\0' && uhskey(fnguid,fngdhide))) {
          return(0);
     }
     return(1);
}

static VOID                        /* (default (*usrrpt)() value)          */
dftrpt(                            /* format and send registry info        */
CHAR *userid)                      /* validated User-ID                    */
{                                  /* (unorthodox methodology)             */
     strcpy(input,spr("/r %-0.29s",userid));
     parsin();
     usrptr->flags&=~NOGLOB;
     usrptr->substt=FNGDDMY;
     hdlinp();
     usrptr->flags|=NOGLOB;
     setmbk(fngdmb);
}

static VOID
fngdsts(VOID)                      /* status handling                      */
{
     INT more=1;
     USHORT stnow;

     setmbk(fngdmb);
     if (status == RING) {
          rstchn();
          return;
     }
     if (status != CYCLE) {
          return;
     }
     stnow=(USHORT)(hrtval()/4096U);
     switch (usrptr->substt) {
     case FNGDBEG:
          if (stnow-fngdptr->sttime > fngdpatc && fngdpatc > 0) {
               byenow(0);
               more=0;
          }
          break;
     case FNGDRPL:
          if (stnow-fngdptr->sttime > fngdpatc && fngdpatc > 0) {
               byenow(0);
               more=0;
          }
          else {
               hdlonl();
          }
          break;
     case FNGDEND:
          if ((btuoba(usrnum) == OUTSIZ-1
            && tcpipinf[usrnum].outsnk.bufcnt == 0)
           || (stnow-fngdptr->sttime > fngdpatc && fngdpatc > 0)) {
               byenow(0);
               more=0;
          }
          break;
     case FNGDDMY:
          break;
     default:
          more=0;
          break;
     }
     if (more) {
          btuinj(usrnum,CYCLE);
     }
}

/*--- The following routine may one day be moved to more general   ---*/
/*--- purpose location, such as PHGCOMM.LIB.                       ---*/
/*--- The DLL created by this source will work after that point,   ---*/
/*--- but these routines might need to be removed from the source. ---*/

static INT
mrgpar(                            /* detects and removes margv[] parameter*/
CHAR *parm)                        /* parameter, e.g. "-a", case ignored   */
                                   /* returns number of occurances         */
                                   /* expects parsin() to be in effect     */
{                                  /* (general purpose)                    */
     INT i;
     INT cnt=0;

     for (i=0 ; i < margc ; i++) {
          if (sameas(margv[i],parm)) {
               cnt++;
               rstrin();
               if (i+1 < margc) {
                    strcpy(margv[i],margv[i+1]);
               }
               else {
                    margv[i][0]='\0';
               }
               parsin();
          }
     }
     return(cnt);
}

