/***************************************************************************
 *                                                                         *
 *   LOCKNKEY.C                                                            *
 *                                                                         *
 *   Copyright (c) 1991-1997 Galacticomm, Inc.    All Rights Reserved.     *
 *                                                                         *
 *   This is the module and suite of routines for handling locks & keys.   *
 *                                                                         *
 *                                   - S. Brinker and C. Robert  10/3/91   *
 *                                                                         *
 ***************************************************************************/

#include "gcomm.h"
#include "majorbbs.h"
#include "gcspsrv.h"

#define FILREV "$Revision: 11 $"

#define bitint(x)   ((x)>>4)  /* index into array of ints for this bit x   */

CHAR **locks;                 /* dynamically allocated array of lock names */
INT nlocks=0;                 /* total number of locks defined             */
INT nlalcd=0;                 /* total number of locks allocated for       */
INT npkeys=0;                 /* current number of pseudokeys in memory    */
INT npalcd=0;                 /* total number of pseudokeys alloc'd for    */
INT nkalcd=1;                 /* total number of keys alc'd 4 (ints 4 bits)*/

static INT npkeys2=0;         /* Number of 2nd generation pseudokeys       */
static INT npalcd2=0;         /* Current number allocated for              */
static struct pskeys2* pkeys2;/* Second generation pseudokeys              */

struct pskeys *pkeys;         /* in-memory array of registered pseudokeys  */

CHAR *kysbuf;                 /* pointer to one-shot internal keys buffer  */

DFAFILE *keysbb;              /* Btrieve data block ptr for "keys" file    */

static VOID setkeys(CHAR *keylist);
static GBOOL valkorl(const CHAR *korl,GBOOL islock);
static VOID edtonl(const CHAR *uid,const CHAR *keynam,GBOOL rmv);
static GBOOL evlexp(GBOOL val1,GBOOL val2,CHAR op);

VOID
inikys(VOID)                       /* initialize the locks & keys interface*/
{
     keysbb=dfaOpen("wgskey2.dat",RINGSZ,NULL);
     kysbuf=alcmem(RINGSZ);
}

INT
register_pseudok(                  /* register a new pseudokey             */
const CHAR *pskbeg,                /*   beginning of pseudokey             */
pseudoKeyFunc pskrou)              /*   routine for validating pseudokey   */
{
     if (npalcd < npkeys+1) {
          if (npalcd == 0) {
               pkeys=(struct pskeys *)alcmem(sizeof(struct pskeys)*RSZBLK);
          }
          else {
               pkeys=(struct pskeys *)alcrsz(pkeys,
                              sizeof(struct pskeys)*npalcd,
                       sizeof(struct pskeys)*(npalcd+RSZBLK));
          }
          npalcd+=RSZBLK;
     }
     stzcpy(pkeys[npkeys].pskbeg,pskbeg,KEYSIZ);
     pkeys[npkeys].pskrou=pskrou;
     return(npkeys++);
}

INT
register_pseudok2(                 /* register a new generation pseudokey  */
const CHAR *pskbeg,                /*   beginning of pseudokey             */
pseudoKeyFunc2 pskrou)             /*   routine for validating pseudokey   */
{
     if (npalcd2 < npkeys2+1) {
          if (npalcd2 == 0) {
               pkeys2=(struct pskeys2 *)alcmem(sizeof(struct pskeys2)*RSZBLK);
          }
          else {
               pkeys2=(struct pskeys2 *)alcrsz(pkeys2,
                              sizeof(struct pskeys2)*npalcd2,
                       sizeof(struct pskeys2)*(npalcd2+RSZBLK));
          }
          npalcd2+=RSZBLK;
     }
     stzcpy(pkeys2[npkeys2].pskbeg,pskbeg,KEYSIZ);
     pkeys2[npkeys2].pskrou=pskrou;
     return(npkeys2++);
}

UINT                               /*  Amount of 2nd generation pseudokeys */
numPseudokey2(VOID)                /* Get amount of pseudokeys             */
{
     return(npkeys2);
}

GBOOL                              /*  TRUE/FALSE if pseudokey is found    */
getPseudokey2(                     /* Get a pseudokey by number            */
UINT uKey,                         /*  Pseudokey number to get             */
struct pskeys2* ppskey,            /*  Structure to be filled in           */
size_t nSize)                      /*  Size of the structure to be filled  */
{
     ASSERT(ppskey != NULL);
     ASSERT(uKey < npkeys2);
     ASSERT(nSize <= sizeof(struct pskeys2));

     memset(ppskey,0,nSize);
     if (uKey < npkeys2 && nSize <= sizeof(struct pskeys2)) {
          memcpy(ppskey,&pkeys2[uKey],nSize);
          return(TRUE);
     }
     return(FALSE);
}

VOID
nkyrec(                            /* create a new key record              */
const CHAR *uid)                   /*   for this user-id or keyring        */
{
     struct keyrec temp;

     dfaSetBlk(keysbb);
     setmem(&temp,sizeof(struct keyrec),0);
     strcpy(temp.userid,uid);
     if (dfaQueryEQ(temp.userid,0)) {
          dfaAbsRec(NULL,0);
          dfaUpdateV(&temp,sizeof(struct keyrec));
     }
     else {
          dfaInsertV(&temp,sizeof(struct keyrec));
     }
     dfaRstBlk();
}


VOID
loadkeys(                          /* load a user's keys into memory       */
const CHAR *keyring)               /*   keyring name for this user         */
{
     CHAR stg[KEYSIZ+1];

     if (usrptr->keys != NULL) {
          axschg(usaptr->userid);
          free(usrptr->keys);
     }
     usrptr->keys=(INT *)alcmem(nkalcd*sizeof(INT));
     setmem(usrptr->keys,nkalcd*sizeof(INT),0);
     dfaSetBlk(keysbb);
     if (isuidc(usaptr->userid[0])) {
          if (!dfaAcqEQ(kysbuf,usaptr->userid,0)) {
               nkyrec(usaptr->userid);
               shocst("MISSING A USER'S KEYRING RECORD",
                      "(%s has been given a blank keyring record.)",
                      usaptr->userid);
          }
          else {
               setkeys(&kysbuf[KLSTOF]);
          }
     }
     if (keyring != NULL) {
          setmem(stg,KEYSIZ+1,0);
          stg[0]=RINGID;
          strncat(stg,keyring,KEYSIZ-1);
          if (!dfaAcqEQ(kysbuf,stg,0)) {
               nkyrec(stg);
               shocst("MISSING A CLASS KEYRING RECORD",
                      "(%s class has been given a blank keyring record.)",
                      stg+1);
          }
          else {
               setkeys(&kysbuf[KLSTOF]);
          }
     }
     dfaRstBlk();
}

VOID
usetkey(                           /* turn cur user's bit on for this key  */
const CHAR *lock)                  /*   key name to turn on                */
{
     INT bit;

     bit=lockbit(lock,1);
     usrptr->keys[bit>>4]|=(1<<(bit&15));
}

static VOID
setkeys(                           /* turn user's bits on list of locks    */
CHAR *keylist)                     /*   pointer to list of locks           */
{
     CHAR *keyptr,*ptr;

     keyptr=ptr=keylist;
     while (*ptr != '\0') {
          if (*ptr == ' ') {
               *ptr='\0';
               usetkey(keyptr);
               keyptr=ptr+1;
          }
          ptr++;
     }
     usetkey(keyptr);
}

INT
haskno(                            /* does this user have this key number  */
INT bitno)                         /*   bit number for this key            */
{
     if (bitno >= 0) {
          return((usrptr->keys[bitno>>4]&(1<<(bitno&15))) != 0);
     }
     return(FALSE);
}

INT
gen_haskey(                        /* general haskey() for any user routine*/
const CHAR *lock,                  /*   lock name the key is for           */
INT unum,                          /*   user number we're dealing with     */
struct user *uptr)                 /*   user pointer we're dealing with    */
{
     CHAR op='\0',*cp,savchr;
     GBOOL vsofar=FALSE;

     ASSERTM(lock != NULL,"gen_haskey(): lock value NULL");
     while ((cp=strpbrk(lock,"&|")) != NULL) {
          savchr=*cp;
          *cp='\0';
          vsofar=evlexp(vsofar,low_haskey(lock,unum,uptr),op);
          op=savchr;
          *cp=savchr;
          lock=cp+1;
     }
     return(evlexp(vsofar,low_haskey(lock,unum,uptr),op));
}

INT
low_haskey(                        /* low-level haskey() hdlr (no ands/ors)*/
const CHAR *lock,                  /*   lock name the key is for           */
INT unum,                          /*   user number we're dealing with     */
struct user *uptr)                 /*   user pointer we're dealing with    */
{
     INT bit;

     if (uptr->keys == NULL) {
          return(uptr->usrcls == BBSPRV);
     }
     if (lock[0] == '\0') {
          return(1);
     }
     if ((bit=scnpsk(unum,lock)) != -1) {
          return(bit);
     }
     if ((bit=scnpsk2(uacoff(unum),unum,lock)) != -1) {
          return(bit);
     }
     if (uptr->flags&MASTER) {
          return(1);
     }
     if ((bit=lockbit(lock,0)) == -1) {
          return(0);
     }
     return((uptr->keys[bit>>4]&(1<<(bit&15))) != 0);
}

INT                                /*   -1 = not found, 1 = pass, 0 = fail */
scnpsk(                            /* scan through pseudo-keys for a lock  */
INT unum,                          /*   user number trying to use key      */
const CHAR *lock)                  /*   lock name the key is for           */
{
     INT i;

     for (i=0 ; i < npkeys ; i++) {
          if (sameto(pkeys[i].pskbeg,lock)) {
               return((*(pkeys[i].pskrou))(unum,lock) ? 1 : 0);
          }
     }
     return(-1);
}

INT                                /*   -1 = not found, 1 = pass, 0 = fail */
scnpsk2(                           /* scan through pseudo-keys for a lock  */
struct usracc* pacc,               /*   user account structure using key   */
INT unum,                          /*   user number trying (-1 if none)    */
const CHAR* lock)                  /*   lock name the key is for           */
{
     INT i;

     ASSERT(pacc != NULL);
     ASSERT(unum >= -1 && unum < nterms);
     for (i=0; i < npkeys2; i++) {
          if (sameto(pkeys2[i].pskbeg,lock)) {
               return((*(pkeys2[i].pskrou))(pacc,unum,lock) ? 1 : 0);
          }
     }
     return(-1);
}

INT
uhsmkey(                           /* passed user have this key (in msg)?  */
const CHAR *uid,                   /*   user-id (online or offline)        */
INT mnum)                          /*   message number with key name       */
{
     CHAR *lock;

     lock=rawmsg(mnum);
     return(onsysn(uid,1) ? othkey(lock) : uidkey(uid,lock));
}

INT
hasmkey(                           /* does cur usr have key in message?    */
INT mnum)                          /*   message number with key name       */
{
     return(gen_haskey(rawmsg(mnum),usrnum,usrptr));
}

INT
uhskey(                            /* does passed user have this key?      */
const CHAR *uid,                   /*   user-id (online or offline)        */
const CHAR *lock)                  /*   key name to check for              */
{
     return(onsysn(uid,1) ? othkey(lock) : uidkey(uid,lock));
}

INT
haskey(                            /* does the current user have this key? */
const CHAR *lock)                  /*   lock name to check for             */
{
     return(gen_haskey(lock,usrnum,usrptr));
}

INT
othkey(                            /* does other user have this key(othusn)*/
const CHAR *lock)                  /*   lock name to check for             */
{
     return(gen_haskey(lock,othusn,othusp));
}

INT
uidkey(                            /* does passed user have this key?      */
const CHAR *uid,                   /*   user-id to look up (not online)    */
const CHAR *lock)                  /*   lock name to check for             */
{
     GBOOL rv;
     CHAR stg[KEYSIZ+1];

     if (lock[0] == '\0') {
          return(1);
     }
     dfaSetBlk(accbb);
     if (!dfaAcqEQ(&acctmp,uid,0)) {         /* User not found             */
          dfaRstBlk();
          return(0);
     }
     dfaRstBlk();
     if ((rv=scnpsk2(&acctmp,-1,lock)) != -1) {
          return(rv);
     }
     rv=(getlst(uid,NULL) && findkey(lock,&kysbuf[KLSTOF],0));
     if (!rv) {
          if (acctmp.flags&HASMST) {
               rv=1;
          }
          else {
               setmem(stg,KEYSIZ+1,0);
               stg[0]=RINGID;
               strncat(stg,acctmp.curcls,KEYSIZ-1);
               rv=(getlst(stg,NULL) && findkey(lock,&kysbuf[KLSTOF],0));
          }
     }
     return(rv);
}

INT
getlst(                            /* get a list of keys from BTRIEVE      */
const CHAR *uid,                   /*   user-id or keyring name to get     */
CHAR *buffer)                      /*   buffer to put end result           */
{
     GBOOL rv;

     dfaSetBlk(keysbb);
     if ((rv=dfaAcqEQ(kysbuf,uid,0)) != FALSE && buffer != NULL) {
          strcpy(buffer,&kysbuf[KLSTOF]);
     }
     dfaRstBlk();
     return(rv);
}

INT
findkey(                           /* find a key (or remove) in list       */
const CHAR *lock,                  /*   lock name to search for            */
CHAR *keylist,                     /*   pointer to list of keys            */
GBOOL remove)                      /*   remove? (1=yes 0=no)               */
{
     GBOOL found=FALSE;
     CHAR *keyptr,*ptr;

     keyptr=ptr=keylist;
     while (*ptr != '\0' && !found) {
          if (*ptr == ' ') {
               *ptr='\0';
               found=sameas(lock,keyptr);
               *ptr=' ';
               if (found && remove) {
                    movmem(ptr+1,keyptr,strlen(ptr+1)+1);
               }
               keyptr=ptr+1;
          }
          ptr++;
     }
     if (!found && (found=sameas(lock,keyptr)) != 0 && remove) {
          if (keyptr == keylist) {
               *keyptr='\0';
          }
          else {
               *(keyptr-1)='\0';
          }
     }
     return(found);
}

INT
givkey(                            /* give user or keyring list of keys    */
const CHAR *uid,                   /*   uid or keyring name to update      */
CHAR *keylist)                     /*   list of keys to give               */
{
     CHAR *keyptr,*ptr;

     keyptr=ptr=keylist;
     dfaSetBlk(keysbb);
     if (!dfaAcqEQ(kysbuf,uid,0)) {
          dfaRstBlk();
          return(0);
     }
     while (*ptr != '\0') {
          if (*ptr == ' ') {
               *ptr='\0';
               if (!addkyu(keyptr,0)) {
                    dfaRstBlk();
                    return(0);
               }
               edtonl(uid,keyptr,0);
               keyptr=ptr+1;
          }
          ptr++;
     }
     if (!addkyu(keyptr,1)) {
          dfaRstBlk();
          return(0);
     }
     edtonl(uid,keyptr,0);
     dfaRstBlk();
     return(1);
}

INT
addkyu(                            /* add a key to the kysbuf buffer       */
const CHAR *keyptr,                /*   pointer to the key name            */
GBOOL update)                      /*   update the BTRIEVE record?         */
{
     if (!findkey(keyptr,&kysbuf[KLSTOF],0)) {
          if (strlen(&kysbuf[KLSTOF])+32+strlen(keyptr) >= RINGSZ) {
               return(0);
          }
          if (kysbuf[KLSTOF] != '\0') {
               strcat(&kysbuf[KLSTOF]," ");
          }
          strcat(&kysbuf[KLSTOF],keyptr);
     }
     if (update) {
          dfaSetBlk(keysbb);
          dfaUpdateV((struct keyrec *)kysbuf,KLSTOF+strlen(&kysbuf[KLSTOF])+1);
          dfaRstBlk();
     }
     return(1);
}

VOID
ustkey(                            /* turn off user's bit for this key     */
const CHAR *lock)                  /*   key name to turn off               */
{
     INT bit;

     if ((bit=lockbit(lock,0)) != -1) {
          usrptr->keys[bit>>4]&=~(1<<(bit&15));
     }
}

VOID
rmvkey(                            /* remove a list of keys from user      */
const CHAR *uid,                   /*   user (or keyring) to remove from   */
CHAR *keylist)                     /*   list of keys to remove             */
{
     CHAR *keyptr,*ptr;

     keyptr=ptr=keylist;
     dfaSetBlk(keysbb);
     if (dfaAcqEQ(kysbuf,uid,0)) {
          while (*ptr != '\0') {
               if (*ptr == ' ') {
                    *ptr='\0';
                    findkey(keyptr,&kysbuf[KLSTOF],1);
                    edtonl(uid,keyptr,1);
                    keyptr=ptr+1;
               }
               ptr++;
          }
          findkey(keyptr,&kysbuf[KLSTOF],1);
          edtonl(uid,keyptr,1);
          dfaUpdateV((struct keyrec *)kysbuf,KLSTOF+strlen(&kysbuf[KLSTOF])+1);
     }
     dfaRstBlk();
}

INT
lockbit(                           /* find bit number for a certain lock   */
const CHAR *lock,                  /*   lock name to look for              */
GBOOL ins)                         /*   insert if not found? (1 or 0)      */
{
     INT cond;
     INT low,mid,high;
     INT i,j;
     INT newalc;
     struct user *usp;

     low=0;
     mid=0;
     high=nlocks-1;
     while (low <= high) {
          mid=low+(high-low)/2;
          if ((cond=stricmp(lock,locks[mid])) < 0) {
               if (mid == low) {
                    break;
               }
               high=mid-1;
          }
          else if (cond > 0) {
               if (mid == high) {
                    mid++;
                    break;
               }
               low=mid+1;
          }
          else {
               return(mid);
          }
     }
     if (ins) {
          if (nlocks == 0) {
               locks=(CHAR **)alcmem(sizeof(CHAR *)*(nlalcd=RSZBLK));
          }
          else if (nlocks == nlalcd) {
               locks=(CHAR **)alcrsz(locks,nlocks*sizeof(CHAR *),
                          (nlocks+RSZBLK)*sizeof(CHAR *));
               nlalcd+=RSZBLK;
          }
          if (mid != nlocks) {
               movmem(&locks[mid],&locks[mid+1],(nlocks-mid)*sizeof(CHAR *));
          }
          locks[mid]=strupr(alcdup(lock));
          if (nlocks != 0 && nliniu()) {
               newalc=nkalcd;
               for (i=0 ; i < nterms ; i++) {
                    usp=usroff(i);
                    if (usp->usrcls >= ONLINE && usp->keys != NULL) {
                         if (bitint(nlocks)+1 == nkalcd) {
                              usp->keys=(INT *)alcrsz(usp->keys,
                                                  nkalcd*sizeof(INT),
                                                  (nkalcd+RSZBLK)*sizeof(INT));
                              if (newalc == nkalcd) {
                                   newalc+=RSZBLK;
                              }
                         }
                         for (j=nlocks-1 ; j >= mid ; j--) {
                              if (usp->keys[j>>4]&(1<<(j&15))) {
                                   usp->keys[(j+1)>>4]|=(1<<((j+1)&15));
                              }
                              else {
                                   usp->keys[(j+1)>>4]&=~(1<<((j+1)&15));
                              }
                         }
                         usp->keys[mid>>4]&=~(1<<(mid&15));
                    }
               }
               nkalcd=newalc;
               redolocks(mid);
          }
          nlocks++;
          return(mid);
     }
     return(-1);
}

INT
keynam(                            /* validate a proposed keyname          */
const CHAR *keyname)               /*   name to validate, 0=bad 1=ok       */
{
     return(valkorl(keyname,0));
}

INT
loknam(                            /* validate a proposed lock name        */
const CHAR *lokname)               /*   name to validate, 0=bad 1=ok       */
{
     return(valkorl(lokname,1));
}

static GBOOL
valkorl(                           /* validate a key or lock name          */
const CHAR *korl,                  /*   pointer to key or lock name        */
GBOOL islock)                      /*   validate as 1=lock, 0=key          */
{
     INT len,maxlen;

     len=strlen(korl);
     maxlen=(islock ? LOKSIZ-1 : KEYSIZ-1);
     if (((!islock || len > 0) && len < 3) || len > maxlen) {
          return(0);
     }
     for ( ; *korl != '\0' ; korl++) {
          switch (*korl) {
          case '#':
          case '=':
               break;
          case '&':
          case '|':
               if (!islock) {
                    return(0);
               }
               break;
          default:
               if (!istxvc(*korl)) {
                    return(0);
               }
          }
     }
     return(1);
}

VOID
dlkeys(                            /* locks & keys delete account routine  */
const CHAR *uid)                   /*   user's account that was deleted    */
{
     dfaSetBlk(keysbb);
     if (dfaAcqEQ(NULL,uid,0)) {
          dfaDelete();
     }
     dfaRstBlk();
}

VOID
clslnk(VOID)                       /* locks & keys system shutdown routine */
{
     dfaClose(keysbb);
}

static VOID
edtonl(                            /* edit key for possibly online user    */
const CHAR *uid,                   /*   user-id to edit key for            */
const CHAR *keynam,                /*   key name being edited              */
GBOOL rmv)                         /*   remove this key? (1=rmv, 0=add)    */
{
     INT savusn;

     if (*uid != RINGID) {
          othusn=usrnum;
          if (sameas(uid,usaptr->userid) || onsysn(uid,1)) {
               savusn=usrnum;
               curusr(othusn);
               if (rmv) {
                    ustkey(keynam);
               }
               else {
                    usetkey(keynam);
               }
               curusr(savusn);
          }
     }
}

VOID
axschg(                            /* access changed for passed User-ID    */
const CHAR *uid)
{
     INT cn;
     struct user *uptr;

     for (cn=0 ; cn < nterms ; cn++) {
          uptr=usroff(cn);
          if (uptr->usrcls > SUPIPG && sameas(uid,uacoff(cn)->userid)) {
               if (uptr->flags&ISGCSU) {
                    senddpk(cn,"",NORMAL,saunmu("secure"),0,"",NULL);
               }
               return;
          }
     }
}

static GBOOL
evlexp(                            /* help evaluate epression in lock stg  */
GBOOL val1,                        /*   value for first operand            */
GBOOL val2,                        /*   value for second operand           */
CHAR op)                           /*   operator to use                    */
{
     GBOOL retval;

     switch (op) {
     case '&':
          retval=(val1 && val2);
          break;
     case '|':
          retval=(val1 || val2);
          break;
     default:
          retval=val2;
     }
     return(retval);
}

