/////////////////////////////////////////////////////////////////////////////
//
//   TLCCS.CPP
//
//   Copyright (c) 1997-1998  Galacticomm, Inc.    All Rights Reserved
//
//   Teleconference Client-Server Agent
//   Implentation
//                                           - Phil Henning 6/10/97
//
/////////////////////////////////////////////////////////////////////////////
#include "gcomm.h"
#include "majorbbs.h"
#include "gcspsrv.h"
#include "tlcapi.hpp"
#include "tlccs.h"
#include "tlccstra.h"
#include "galwebd.h"

#define FILREV "$Revision: 88 $"

#define tlcoff(x) (&tlccs[x])

/***************************************************************************
 * Variable Declarations                                                   *
 ***************************************************************************/
static CHAR BannerDirFull[GCMAXPTH]; //  Location of all banners
static CHAR BannerDir[GCMAXPTH];     //  Banner directory rel. to webpages

CHAR* pszTicker;                   //  Teleconference ticker string
CHAR* pszTickerNoAnsi;             //  Non-ANSI teleconference ticker
INT iTickerTime;                   //  Ticker timer

CHAR* cssndbuf=NULL;               //  Temporary sending buffer
LPBANRFIL banrfiles=NULL;          //  Linked list of banner files
INT banrval;                       //  Number of seconds banners show

CCSTransport* transCS=NULL;        //  Client Server Transport

static LPTLCCSINF tlcptr;          //  Teleconference C/S memory pointer
static LPTLCCSINF tlccs=NULL;      //  Teleconference C/S per user memory

CHAR tlcdpks[][DPKLEN]={           //  Teleconference dynapak list
     "sa=GALTELE;u:whisper",       //  FromUID TAB Message
     "sa=GALTELE;u:ent",           //  FromUID TAB Message (or \0)
     "sa=GALTELE;u:ext",           //  FromUID TAB Message (or \0)
     "sa=GALTELE;u:direct",        //  FromUID TAB ToUID TAB Message
     "sa=GALTELE;u:newtop",        //  FromUID TAB New Topic
     "sa=GALTELE;u:entedit",       //  FromUID         (Entered Edit Mode)
     "sa=GALTELE;u:action",        //  Action Text
     "sa=GALTELE;u:speak",         //  FromUID TAB Message (normal talk)
     "sa=GALTELE;u:misc",          //  Text  (to be displayed)
     "sa=GALTELE;u:uppals",        //  updated list of pals coming...
     "sa=GALTELE;u:dbtoggle",      //  user just toggled drawing board
     "sa=GALTELE;u:chanchg",       //  user just changed channel
     "sa=GALTELE;u:intro",         //  intro message
     "sa=GALTELE;u:ulist",         //  user list on channel enter (term)
     "sa=GALTELE;u:welcome",       //  welcome message
     "sa=GALTELE;u:userenter",     //  user entered tcon UID TAB Channel
     "sa=GALTELE;u:userexit",      //  user exited tcon  UID TAB Channel
     "sa=GALTELE;u:chat",          //  user getting chat data
     "sa=GALTELE;u:reqchat",       //  someone requesting chat
     "sa=GALTELE;u:rejchat",       //  someone left the chat
     "sa=GALTELE;u:startchat",     //  starting chat
     "sa=GALTELE;u:addchan",       //  a channel was added
     "sa=GALTELE;u:delchan",       //  a channel was deleted
     "sa=GALTELE;u:retedt",        //  someone returned from an editor
     "sa=GALTELE;u:userstat",
     "sa=GALTELE;u:addact",        //  an action was added
     "sa=GALTELE;u:delact",        //  an action was deleted
     "sa=GALTELE;u:chgact",        //  an action was changed
     "sa=GALTELE;u:addlst",        //  an action list was added
     "sa=GALTELE;u:dellst",        //  an action list was deleted
     "sa=GALTELE;u:chglst",        //  an action list was changed
};

/***************************************************************************
 * Function & Structure declarations                                       *
 ***************************************************************************/
static VOID
wt4rcvr(VOID);                     // wait 4 receiver to be ready for more

static VOID
notprep(                           /* prepare notify dynapak data          */
INT priort,
CHAR *parm1);

static VOID
aboarcvs(VOID);                    // abort all receives/receive requests

static GBOOL                       //  TRUE if transfer, FALSE if not
hdlxfr(                            // dynapak if it's file xfer stuff
CHAR *dpkstg,                      //  Current dynapak string
VOID *value,                       //  Pointer to information received
USHORT length);                    //  Length of the information received

static VOID
(*oldinvhook)(                     //  Invisible vector pointer
INT unum);                         //  usrnum of person going invisible

static VOID
abosnd(VOID);                      // abort send of file

static VOID
aborcv(                            // abort receive/receive request
INT sndchn);                       //   from this sender channel

static VOID
csInvHook(                         //  C/S Invisble hook replacement
INT unum);                         //  usrnum of person toggling invis

static VOID
banrDelList(VOID);                 //  Delete banner linked list

static VOID
banrLoadList(VOID);                //  Load linked list of banners

static CHAR *                      //  Pointer to fixed path
checkdir(                          //  modify path to proper format
CHAR *path);                       //  ... i.e., no trailing \ or /

static VOID
tlcread(                           //  Teleconference read handler
INT direction,                     //  Dynapak direction
struct saunam* dpknam);            //  pointer to structure

static VOID
tlcwrite(                          //  Teleconference write handler
struct saunam* dpknam,             //  Pointer to dynapak
USHORT length,                     //  Length of information received
VOID* value);                      //  Pointer to information received


static VOID
csActionAddHook(                   /* cs action add hookfunc               */
LPACTION act);                     /*   action word                        */

static VOID
csActionChangeHook(                /* cs action change hookfunc            */
LPACTION actnew,                   /*   new action word                    */
LPACTION actold);                  /*   old action word                    */

static VOID
csActionDeleteHook(                /* cs action delete hookfunc            */
LPACTION act);                     /*   action word                        */

static VOID
csListAddHook(                     /* cs action list add hookfunc          */
LPACTLST list);                    /*   action list                        */

static VOID
csListDeleteHook(                  /* cs action list delete hookfunc       */
LPACTLST list);                    /*   action list                        */

static VOID
csListChangeHook(                  /* cs action list change hookfunc       */
LPACTLST listnew,                  /*   new action list                    */
LPACTLST listold);                 /*   old action list                    */

static VOID
csChanActionUpdateHook(            /* cs chan action list upd hookfunc     */
LPCHANINFO pChan);                 /*   pointer to channel info            */

static VOID
(*oldActionAddHook)(               /* cs action add saved vector           */
LPACTION act);                     /*   action word                        */

static VOID
(*oldActionChangeHook)(            /* cs action change saved vector        */
LPACTION actnew,                   /*   new action word                    */
LPACTION actold);                  /*   old action word                    */

static VOID
(*oldActionDeleteHook)(            /* cs action delete saved vector        */
LPACTION act);                     /*   action word                        */

static VOID
(*oldListAddHook)(                 /* cs action list add saved vector      */
LPACTLST list);                    /*   action list                        */

static VOID
(*oldListDeleteHook)(              /* cs action list delete saved vector   */
LPACTLST list);                    /*   action list                        */

static VOID
(*oldListChangeHook)(              /* cs action list change saved vector   */
LPACTLST listnew,                  /*   new action list                    */
LPACTLST listold);                 /*   old action list                    */

static VOID
(*oldChanActionUpdateHook)(         /* cs chan action list upd saved vector*/
LPCHANINFO pChan);                 /*   pointer to channel info            */

struct agent tlcagent={            //  Teleconference agent handler
     TLCAPID,                      //  Teleconference APPID
     tlcread,                      //  Read Handler
     tlcwrite,                     //  Write Handler
     NULL,                         //  Transfer Done Handler
     NULL                          //  Transfer Aborted handler
};

/***************************************************************************
 * Source Code                                                             *
 ***************************************************************************/
MARKSOURCE(tlccs);                 //  Source code marker

VOID
tlcInitCS(VOID)                    //  C/S Initialization
{
     HMCVFILE web;

     cssndbuf=static_cast<CHAR*>(alczer(CSSNDBUFSIZ));
     tlccs=reinterpret_cast<LPTLCCSINF>(alczer(sizeof(TLCCSINF)*nterms));
     register_agent(&tlcagent);
     register_dpks();
     pszTicker=alcdup(getmsg(ATICKER2));
     tlcreflow(pszTicker);
     pszTickerNoAnsi=alcdup(pszTicker);
     stpans(pszTickerNoAnsi);
     iTickerTime=numopt(ATICKERT,1,5);
     banrval=numopt(BANRVAL,30,600);
     transCS=new CCSTransport;
     tlcAPI->transRegister("C/S Mode",transCS);
     setmem(BannerDir,GCMAXPTH,0);

     stlcpy(BannerDir,checkdir(pthopt(BANRDIR2)),GCMAXPTH);

     web=opnmsg("galwebd.mcv");
     setmem(BannerDirFull,GCMAXPTH,0);
     stlcpy(BannerDirFull,checkdir(pthopt(WEBROOT)),GCMAXPTH);
     stlcat(BannerDirFull,SLS,GCMAXPTH);
     stlcat(BannerDirFull,BannerDir,GCMAXPTH);
     gmkdir(BannerDirFull);
     rstmbk();
     clsmsg(web);

     oldinvhook=tlcInvisRou;
     tlcInvisRou=csInvHook;

     oldActionAddHook=actionAddHook;
     oldActionDeleteHook=actionDeleteHook;
     oldActionChangeHook=actionChangeHook;
     oldListAddHook=listAddHook;
     oldListDeleteHook=listDeleteHook;
     oldListChangeHook=listChangeHook;
     oldChanActionUpdateHook=chanActionUpdateHook;

     actionAddHook=csActionAddHook;
     actionDeleteHook=csActionDeleteHook;
     actionChangeHook=csActionChangeHook;
     listAddHook=csListAddHook;
     listDeleteHook=csListDeleteHook;
     listChangeHook=csListChangeHook;
     chanActionUpdateHook=csChanActionUpdateHook;

     hook_startup(banrLoadList);
     hook_disconnect(tlcDisconnect);
     hook_connect(tlcConnect);
}

VOID
vb2tlcfil(                         //  Transfer file info (VB to C)
VOID* data,                        //  Pointer to data received
LPCTLCFIL fil)                     //  Pointer to buffer to fill
{
     LPVBTLCFIL vb=static_cast<LPVBTLCFIL>(data);

     b2ccpy(fil->userid,vb->userid,TLCUIDSIZ);
     b2ccpy(fil->name,vb->name,VBFILSIZ+1);
     fil->tnd=vb->tnd;
     fil->size=vb->size;
}

VOID
tlcfil2vb(                         //  Transfer file info (C to VB)
LPCTLCFIL fil,                     //  Pointer to file information
LPVBTLCFIL vb)                     //  Pointer to VB information
{
     c2bcpy(vb->userid,fil->userid,TLCUIDSIZ-1);
     c2bcpy(vb->name,fil->name,VBFILSIZ);
     vb->tnd=fil->tnd;
     vb->size=fil->size;
}

VOID
tlcCSdwn(VOID)                     //  Client/Server shutdown routine
{
     banrDelList();
     if (tlccs != NULL) {
          aboarcvs();
          free(tlccs);
          tlccs=NULL;
     }
     if (cssndbuf != NULL) {
          free(cssndbuf);
          cssndbuf=NULL;
     }
     if (transCS != NULL) {
          delete transCS;
          transCS=NULL;
     }
}

static VOID
banrLoadList(VOID)                 //  Load linked list of banners
{
     struct ffblk fbk;
     LPBANRFIL bf;
     LPBANRFIL lst;
     CHAR CheckDir[GCMAXPTH];

     if (fnd1st(&fbk,makePath(CheckDir,BannerDirFull,"*.gif",GCMAXPTH),0)) {
          do {
               bf=new banrfil;
               bf->name=new CHAR[strlen(BannerDir)+strlen(fbk.ff_name)+2];
               sprintf(bf->name,"%s%s%s",BannerDir,SLS,fbk.ff_name);
               bf->next=NULL;
               if (banrfiles == NULL) {
                    lst=banrfiles=bf;
               }
               else {
                    lst->next=bf;
                    lst=bf;
               }
          } while (fndnxt(&fbk));
     }
}

static VOID
banrDelList(VOID)                  //  Delete banner linked list
{
     LPBANRFIL pr;
     LPBANRFIL bf;

     while (banrfiles != NULL) {
          pr=bf=banrfiles;
          while (bf->next != NULL) {
               pr=bf;
               bf=bf->next;
          }
          pr->next=NULL;
          delete [] bf->name;
          if (bf == banrfiles) {
               banrfiles=NULL;
          }
          delete bf;
     }
}

static CHAR *                      //  Pointer to fixed path
checkdir(                          //  modify path to proper format
CHAR *path)                        //  ... i.e., no trailing \ or /
{
     INT len;
     CHAR chr,*sptr;

     if ((sptr=strchr(path,':')) != NULL) {
          if (sptr-path != 1) {
               *path='\0';
          }
          else {
               if (!isalpha(*path)) {
                    *path='\0';
               }
               else if ((sptr=strchr(++sptr,':')) != NULL) {
                    *sptr='\0';
               }
          }
     }
     len=strlen(path);
     while (len > 0) {
          chr=path[len-1];
          if (chr == '\\' || chr == '/') {
               if (path[len-2] == ':') {
                    strcat(path,".");
                    break;
               }
               else {
                    path[--len]='\0';
               }
          }
          else {
               if (chr == ':') {
                    strcat(path,".");
               }
               break;
          }
     }
     return(path);
}

static VOID
tlcread(                           //  Teleconference read handler
INT direction,                     //  Dynapak direction
struct saunam* dpknam)             //  pointer to structure
{
     setmbk(msgTlc);

     if (!stdmchk(CSKEY)) {
          rejectreq();
          return;
     }

     dpkstg=cnvs2d(dpknam);
     curUser=dpkUser=tlcAPI->usrGetByName(usaptr->userid);
     dpkdirection=direction;

     if (curUser == NULL) {
          rejectreq();
          return;
     }
     *rsptmp='\0';

     if (direction > 0 && sameto(DPKS_CHANL,dpkstg)) {
          dpkr_chanl(dpknam,dpkstg,tlcoff(usrnum)->LastChannel);
     }
     else if (sameto(DPKS_CHANU,dpkstg)) {
          dpkr_chanu();
     }
     else if (sameto(DPKS_TICKER,dpkstg)) {
          dpkr_ticker();
     }
     else if (sameto(DPKS_GETPALS,dpkstg) && hasmkey(PALKEY)) {
          dpkr_getpals();
     }
     else if (sameto(DPKS_GETPROF,dpkstg)) {
          dpkr_getprof();
     }
     else if (sameto(DPKS_ACCESS,dpkstg)) {
          dpkr_getaccess();
     }
     else if (sameto(DPKS_ALISTS,dpkstg)) {
          dpkr_alists();
     }
     else if (sameto(DPKS_AWORDS,dpkstg)) {
          dpkr_awords();
     }
     else if (sameto(DPKS_GETSQUELCH,dpkstg)) {
          dpkr_getsquelch();
     }
     else if (sameto(DPKS_GETINVITE,dpkstg)) {
          dpkr_getinvite();
     }
     else if (sameto(DPKS_GETFORGET,dpkstg)) {
          dpkr_getforget();
     }
     else if (sameto(DPKS_GETIGNORE,dpkstg)) {
          dpkr_getignore();
     }
     else if (direction > 0 && sameto(DPKS_ALLU,dpkstg)) {
          dpkr_allu(dpknam,dpkstg,tlcoff(usrnum)->LastUser);
     }
     else if (samepato(DPKS_SYSEDIT,dpkstg) && hasmkey(MNUKEY)) {
          dpkr_sysedit();
     }
     else if (sameto(DPKS_SYSCHAN,dpkstg)
      && (hasmkey(TSYSKEY) || hasmkey(TLCOPKY))) {
          dpkr_syschan();
     }
     else if (sameto(DPKS_GETCHAN,dpkstg)) {
          dpkr_getchan();
     }
     else if (sameto(DPKS_SYSALIST,dpkstg)) {
          dpkr_sysalist();
     }
     else if (sameto(DPKS_GETALIST,dpkstg)
      && (hasmkey(AEDTKEY) || hasmkey(TSYSKEY))) {
          dpkr_getalist();
     }
     else if (sameto(DPKS_GETAWORD,dpkstg)
      && (hasmkey(AEDTKEY) || hasmkey(TSYSKEY))) {
          dpkr_getaword();
     }
     else if (sameto(DPKS_SYSAWORD,dpkstg)
      && (hasmkey(AEDTKEY) || hasmkey(TSYSKEY))) {
          dpkr_sysaword(TRUE);
     }
     else {
          rejectreq();
     }
     rstmbk();
}

static VOID
tlcwrite(                          //  Teleconference write handler
struct saunam* dpknam,             //  Pointer to dynapak
USHORT length,                     //  Length of information received
VOID* value)                       //  Pointer to information received
{
     setmbk(msgTlc);
     if (!stdmchk(CSKEY)) {
          rejectreq();
          return;
     }

     dpkstg=cnvs2d(dpknam);
     dpklength=length;
     dpkvalue=value;
     setmbk(msgTlc);
     curUser=dpkUser=tlcAPI->usrGetByName(usaptr->userid);
     if (dpkUser != NULL) {
          curChannel=dpkChannel=tlcAPI->chanGetByName(dpkUser->GetChannelName());
     }

     if (sameto(DPKS_NEWU,dpkstg)) {
          dpkw_newu();
     }
     else if (dpkUser == NULL) {
          rejectreq();
     }
     else if (sameto(DPKS_EXIT,dpkstg)) {
          dpkw_exit();
     }
     else if (sameto(DPKS_TALK,dpkstg)) {
          dpkw_talk();
     }
     else if (sameto(DPKS_JOIN,dpkstg)) {
          dpkw_join();
     }
     else if (sameto(DPKS_ADDPAL,dpkstg) && hasmkey(PALKEY)) {
          dpkw_addpal();
     }
     else if (sameto(DPKS_REMPAL,dpkstg) && hasmkey(PALKEY)) {
          dpkw_rempal();
     }
     else if (sameto(DPKS_SETPROF,dpkstg)) {
          dpkw_setprof();
     }
     else if (sameas(dpkstg,DPKS_DBSTART)) {
          dpkw_dbstart();
     }
     else if (sameas(dpkstg,DPKS_DBSTOP)) {
          dpkw_dbstop();
     }
     else if (sameas(dpkstg,DPKS_DBDATA)) {
          dpkw_dbdata();
     }
     else if (sameas(dpkstg,DPKS_SQUEL) && dpkUser->IsTypeOf(USR_MODERATOR)) {
          dpkw_squel();
     }
     else if (sameas(dpkstg,DPKS_UNSQUEL) && dpkUser->IsTypeOf(USR_MODERATOR)) {
          dpkw_unsquel();
     }
     else if (sameas(dpkstg,DPKS_INVITE) && hasmkey(INVKEY)) {
          dpkw_invite();
     }
     else if (sameas(dpkstg,DPKS_UNINVITE)) {
          dpkw_uninvite();
     }
     else if (sameas(dpkstg,DPKS_FORGET) && hasmkey(FGTKEY)) {
          dpkw_forget();
     }
     else if (sameas(dpkstg,DPKS_IGNORE) && hasmkey(IGNKEY)) {
          dpkw_ignore();
     }
     else if (sameas(dpkstg,DPKS_REMEMBER)) {
          dpkw_remember();
     }
     else if (sameas(dpkstg,DPKS_NOTICE)) {
          dpkw_notice();
     }
     else if (sameas(dpkstg,DPKS_REQCHAT) && hasmkey(CHTKEY)) {
          dpkw_reqchat();
     }
     else if (sameas(dpkstg,DPKS_CHAT)) {
          dpkw_chat();
     }
#if 0
     else if (sameas(dpkstg,DPKS_REJCHAT)) {
          dpkw_rejchat();
     }
#endif // 0
     else if (samepato(DPKS_SYSEDIT,dpkstg) && hasmkey(MNUKEY)) {
          dpkw_sysedit();
     }
     else if (sameto(DPKS_SETCHAN,dpkstg)) {
          dpkw_setchan();
     }
     else if (sameto(DPKS_DELCHAN,dpkstg)  && hasmkey(TSYSKEY)) {
          dpkw_delchan();
     }
     else if (sameto(DPKS_DELALIST,dpkstg) && hasmkey(AEDTKEY)) {
          dpkw_delalist();
     }
     else if (sameto(DPKS_SETALIST,dpkstg) && hasmkey(AEDTKEY)) {
          dpkw_setalist();
     }
     else if (sameto(DPKS_SETAWORD,dpkstg) && hasmkey(AEDTKEY)) {
          dpkw_setaword();
     }
     else if (sameto(DPKS_DELAWORD,dpkstg) && hasmkey(AEDTKEY)) {
          dpkw_delaword();
     }
     else if (!hdlxfr(dpkstg,value,length)) {
          rejectreq();
     }
     rstmbk();
     tlcAPI->OutputFinished();
     return;
}

static GBOOL                       //  TRUE if transfer, FALSE if not
hdlxfr(                            // dynapak if it's file xfer stuff
CHAR *dpkstg,                      //  Current dynapak string
VOID *value,                       //  Pointer to information received
USHORT length)                     //  Length of the information received
{
     CTlcUser* pOthUser;

     CTLCFIL finfc;
     VBTLCFIL finfvb;
     LPTLCCSINF otptr;
     CHAR uid[TLCUIDSIZ];

     tlcptr=tlcoff(usrnum);
     if (sameas(dpkstg,DPKS_BGNXFR)) {
          if (!(hasmkey(FILKEY) && length == sizeof(VBTLCFIL)
             && tlcptr->sndstt == NOSEND)) {
               rejectreq();
               return(TRUE);
          }
          vb2tlcfil(value,&finfc);
          if (finfc.size < 1L) {
               prfmsg(SNDLN02);
               notprep(CSNOPOP,prfbuf);
               rsp2write(FALSE,STGLEN,cssndbuf,rtextFDA);
               return(TRUE);
          }
          if (usrptr->flags&INVISB) {
               notprep(CSNOPOP,prfbuf);
               rsp2write(FALSE,STGLEN,cssndbuf,rtextFDA);
               return(TRUE);
          }
          stlcpy(uid,finfc.userid,TLCUIDSIZ-1);
          switch (tlcAPI->usrFind(uid,&pOthUser)) {
          case 0:
               prfmsg(SNDNHR2,uid);
               notprep(CSNOPOP,prfbuf);
               rsp2write(FALSE,STGLEN,cssndbuf,rtextFDA);
               return(TRUE);
          case 1:
               othusn=pOthUser->GetUsrnum();
               if (pOthUser->m_fFlags&USR_INVISB) {
                    prfmsg(SNDNHR2,uid);
                    notprep(CSNOPOP,prfbuf);
                    rsp2write(FALSE,STGLEN,cssndbuf,rtextFDA);
                    return(TRUE);
               }
               if (othusn == usrnum) {
                    prfmsg(SNDSLF2);
                    notprep(CSNOPOP,prfbuf);
                    rsp2write(FALSE,STGLEN,cssndbuf,rtextFDA);
                    return(TRUE);
               }
               if (othusn < 0 || !(usroff(othusn)->flags&ISGCSU)) {
                    prfmsg(SNDNCS2,uid);
                    notprep(CSNOPOP,prfbuf);
                    rsp2write(FALSE,STGLEN,cssndbuf,rtextFDA);
                    return(TRUE);
               }
               if (tlcoff(othusn)->csflags&TLCRCV) {
                    prfmsg(SNDRCV2,uid);
                    notprep(CSNOPOP,prfbuf);
                    rsp2write(FALSE,STGLEN,cssndbuf,rtextFDA);
                    return(TRUE);
               }
               if (!(pOthUser->m_fFlags&USR_INCHAN) || !qroom(othusn,NORMAL)) {
                    prfmsg(SNDNNW2,uid);
                    notprep(CSNOPOP,prfbuf);
                    rsp2write(FALSE,STGLEN,cssndbuf,rtextFDA);
                    return(TRUE);
               }
               othuap=uacoff(othusn);
               cnvd2s(spr("sau=%s;:xfrreq",othuap->userid),namtmp);
               strcpy(finfc.userid,curUser->GetName());
               tlcfil2vb(&finfc,&finfvb);
               senddpk(othusn,TLCAPID,NORMAL,namtmp,length,&finfvb,vbtlcfilFDA);
               tlcptr->sndstt=WT4ROK;
               tlcptr->rcvchn=othusn;
               tlcptr->sndbyt=finfc.size;
               rsp2write(TRUE,STGLEN,othuap->userid,rtextFDA);
               return(TRUE);
          default:
               prfmsg(AMBIG2,"send file");
               notprep(CSNOPOP,prfbuf);
               rsp2write(FALSE,STGLEN,cssndbuf,rtextFDA);
               return(TRUE);
          }
     }
     if (sameas(dpkstg,DPKS_OK2XFR) && !(tlcptr->csflags&TLCRCV)) {
          vb2tlcfil(value,&finfc);
          if (onsys(finfc.userid)) {
               otptr=tlcoff(othusn);
               if (otptr->sndstt == WT4ROK && otptr->rcvchn == usrnum
                && qroom(othusn,NORMAL)) {
                    otptr->sndstt=WT4DAT;
                    cnvd2s(spr("sau=%s;:ok2xfr",othuap->userid),namtmp);
                    senddpk(othusn,TLCAPID,NORMAL,namtmp,STGLEN,usaptr->userid,
                            rtextFDA);
                    tlcptr->csflags|=TLCRCV;
                    tlcptr->sndchn=othusn;
                    tlcptr->rcvopc=0;
                    rsp2write(TRUE,0,NULL,NULL);
                    return(TRUE);
               }
          }
     }
     if (sameas(dpkstg,DPKS_FILDAT) && tlcptr->sndstt == WT4DAT
      && length <= tlcptr->sndbyt) {
          cnvd2s(spr("sau=%s;:fildat",uacoff(tlcptr->rcvchn)->userid),namtmp);
          senddpk(tlcptr->rcvchn,TLCAPID,BACKGND,namtmp,length,value,NULL);
          otptr=tlcoff(tlcptr->rcvchn);
          if ((tlcptr->sndbyt-=length) == 0L) {
               tlcptr->sndstt=NOSEND;
               otptr->csflags&=~TLCRCV;
               rsp2write(FALSE,0,NULL,NULL);
          }
          else if (++otptr->rcvopc >= MAXOPC
                || !qroom(tlcptr->rcvchn,BACKGND)) {
               tlcptr->sndstt=WT4RRM;
               cycleme(wt4rcvr);
          }
          else {
               rsp2write(TRUE,0,NULL,NULL);
          }
          return(TRUE);
     }
     if (sameas(dpkstg,DPKS_GOTDAT)) {
          if (!(tlcptr->csflags&TLCRCV)) {
               rejectreq();
               return(TRUE);
          }
          if (tlcptr->rcvopc <= 0) {
               rejectreq();
               return(TRUE);
          }
          tlcptr->rcvopc--;
          rsp2write(TRUE,0,NULL,NULL);
          return(TRUE);
     }
     if (sameas(dpkstg,DPKS_ABOSND) && tlcptr->sndstt != NOSEND) {
          abosnd();
          rsp2write(TRUE,0,NULL,NULL);
          return(TRUE);
     }
     if (sameas(dpkstg,DPKS_ABORCV)) {
          if (tlcptr->csflags&TLCRCV)  {
               aborcv(tlcptr->sndchn);
          }
          else {
               vb2tlcfil(value,&finfc);
               if (onsysn(finfc.userid,1)) {
                    aborcv(othusn);
               }
          }
          rsp2write(TRUE,0,NULL,NULL);
          return(TRUE);
     }
     return(FALSE);
}

static VOID
wt4rcvr(VOID)                      // wait 4 receiver to be ready for more
{
     tlcptr=tlcoff(usrnum);
     if (tlcptr->sndstt != WT4RRM) {
          rejectreq();
     }
     else if (tlcoff(tlcptr->rcvchn)->rcvopc < MAXOPC
           && qroom(tlcptr->rcvchn,BACKGND)) {
          tlcptr->sndstt=WT4DAT;
          rsp2write(TRUE,0,NULL,NULL);
     }
}

static VOID
abosnd(VOID)                       // abort send of file
{
     switch (tlcptr->sndstt) {
     case NOSEND:
          break;
     case WT4ROK:
     case WT4DAT:
     case WT4RRM:
          if (qroom(tlcptr->rcvchn,NORMAL)) {
               cnvd2s(spr("sa=%s;u=%s;:aborcv",TLCAPID,
                          uacoff(tlcptr->rcvchn)->userid),namtmp);
               senddpk(tlcptr->rcvchn,TLCAPID,NORMAL,namtmp,STGLEN,
                       usaptr->userid,rtextFDA);
          }
          if ((tlcoff(tlcptr->rcvchn)->csflags&TLCRCV)
           && tlcoff(tlcptr->rcvchn)->sndchn == usrnum) {
               tlcoff(tlcptr->rcvchn)->csflags&=~TLCRCV;
          }
          tlcptr->sndstt=NOSEND;
          break;
     }
}

static VOID
aborcv(                            // abort receive/receive request
INT sndchn)                        //   from this sender channel
{
     LPTLCCSINF stptr;

     stptr=tlcoff(sndchn);
     switch (stptr->sndstt) {
     case NOSEND:
          break;
     case WT4ROK:
          if (qroom(sndchn,NORMAL)) {
               cnvd2s(spr("sa=%s;u=%s;:abosnd",TLCAPID,uacoff(sndchn)->userid),
                      namtmp);
               senddpk(sndchn,TLCAPID,NORMAL,namtmp,STGLEN,usaptr->userid,
                       rtextFDA);
          }
     case WT4DAT:
     case WT4RRM:
          stptr->sndstt=NOSEND;
          break;
     }
     if ((tlcptr->csflags&TLCRCV) && tlcptr->sndchn == sndchn) {
          tlcptr->csflags&=~TLCRCV;
     }
}

static VOID
aboarcvs(VOID)                     // abort all receives/receive requests
{
     INT i;

     for (i=0 ; i < nterms ; i++) {
          if (tlcoff(i)->sndstt != NOSEND && tlcoff(i)->rcvchn == usrnum) {
               aborcv(i);
          }
     }
}


VOID
CreateCSUser(VOID)                 //  Create C/S User
{
     ULONG unum;
     CTlcUser User;
     CTlcUser* pUser;

     setmbk(msgTlc);
     User.SetName(usaptr->userid);
     User.SetTransport(transCS);
     User.SetUsrnum(usrnum);
     if ((unum=tlcAPI->FindIDByUser(usaptr->userid)) == 0L) {
          unum=tlcAPI->CreateUniqueID(usaptr->userid);
     }
     User.SetTlcUnum(unum);
     User.SetTlcBoardNum(0);
     User.SetSex(usaptr->sex == 'F' ? 'F' : 'M');
     User.SetAge(usaptr->age);
     User.LoadRecord();
     if (!hasmkey(UNLKEY)) {
          User.m_iTimesSpoken+=teltimes[usrnum];
     }
     if (usrptr->flags&INVISB) {
          User.m_fFlags|=USR_INVISB;
     }
     pUser=tlcAPI->usrAdd(&User);
     transCS->AddUser(pUser);
     rstmbk();
}

VOID
tlcConnect(VOID)                   //  Hooked connect routine
{
     CTlcUser* pUser;

     setmbk(msgTlc);
     pUser=tlcAPI->usrGetByName(usaptr->userid);
     if (pUser == NULL) {
          if (tlcAPI->FindIDByUser(usaptr->userid) == 0L) {
               tlcAPI->CreateUniqueID(usaptr->userid);
          }
          tlcChannelCreatePrivate(usaptr->userid);
     }
     teltimes[usrnum]=0;
     msgchanged[usrnum]=FALSE;
     memset(&aachat[usrnum],0,sizeof(CHATINFO));
     rstmbk();
}

VOID
tlcDisconnect(VOID)                //  Disconnect routine
{
     setmbk(msgTlc);
     if (usrptr->flags&ISGCSU) {
          CTlcChannel* pChannel;
          CTlcUser* pUser;

          // remove c/s user form API /// refine this when c/s gets impl.
          curUser=pUser=tlcAPI->usrGetByName(usaptr->userid);
          while (pUser != NULL) {
               if (sameas(pUser->GetName(),usaptr->userid)) {
                    if (pUser->GetType() != USR_TYPE_AHTML) {
                         if (pUser->m_fFlags&USR_CHATTING) {
                              ExitChat(TRUE,usaptr->userid,TRUE);
                         }
                         // remove c/s user from all channels
                         pUser->SaveRecord();
                         transCS->RemoveUser(pUser);
                         RemoveInvite(pUser);
                         RemoveForget(pUser);

                         if ((pChannel=tlcAPI->chanGetByName(pUser->GetChannelName())) != NULL) {
                              pChannel->RemoveUser(pUser,LEAVE_LOGOFF);
                         }
                         else {
                              tlcAPI->usrDelete(pUser);
                         }
                         break;
                    }
                    else {
                         pUser=tlcAPI->usrGetNext();
                    }
               }
               else {
                    break;
               }
          }
     }
     if (tlcAPI->usrGetByName(usaptr->userid) == NULL) {
          CHAR* chanName=tlcPrivateFromUser(usaptr->userid);
          tlcAPI->chanMoveAllUsers(chanName,dftchan,CHAN_SWITCH_USER_LOGOFF,
           SCMSG_USER_LOGOFF);
          tlcAPI->chanRemoveByName(chanName);
     }
     rstmbk();
}

static VOID
notprep(                           // prepare notify dynapak data
INT priort,
CHAR *parm1)
{
     INT hedlen;

     hedlen=sprintf(cssndbuf,"%d\t",priort);
     stzcpy(cssndbuf+hedlen,parm1,MAXDPKV-hedlen);
     stp4cs(cssndbuf+hedlen);
}

static VOID
csInvHook(                         //  C/S Invisble hook replacement
INT unum)                          //  usrnum of person toggling invis
{
     GBOOL invis=FALSE;

     if (usroff(unum)->flags&INVISB) {
          invis=TRUE;
     }
     CTlcUser* pUser;
     if ((pUser=tlcAPI->usrGetByName(uacoff(unum)->userid)) != NULL
      && pUser->GetUsrnum() >= 0) {
          CTlcChannel* savchn;
          savchn=curChannel;
          curChannel=tlcAPI->chanGetByName(pUser->GetChannelName());
          transCS->SendAll(pUser->GetName(),NULL,NULL,
             (invis ? RECV_TYPE_USEREXIT : RECV_TYPE_USERENTER),NULL,0);
          curChannel=savchn;
     }
     oldinvhook(unum);
}

VOID
tlcreflow(                         /* re-flow buffer for C/S output        */
CHAR *buf)                         /*   buffer to re-flow                  */
{
     GBOOL hardcr=FALSE;
     CHAR *bufptr;

     /* assumes buf is in format created by WGSMSX.C, */
     /* where \n = soft CR, \r = hard CR */
     for (bufptr=buf ; *bufptr != '\0' ; bufptr++) {
          if (*bufptr == '\r') {
               *bufptr='\n';
               hardcr=TRUE;
          }
          else {
               if (*bufptr == '\n') {
                    if (!hardcr) {
                         *bufptr=' ';
                    }
               }
               hardcr=FALSE;
          }
     }
}

static GBOOL
isCSUserInTele(
INT unum)
{
     if (usroff(unum)->usrcls == ACTUSR && usroff(unum)->flags&ISGCSU) {
          CTlcUser* pUser;
          if ((pUser=tlcAPI->usrGetByName(uacoff(unum)->userid)) != NULL
           && pUser->GetUsrnum() == unum) {
               return(TRUE);
          }
     }
     return(FALSE);
}

static VOID
MakeActionDpk(
LPACTION actnew)
{
     strcpy(cssndbuf,actnew->name);
     strcat(cssndbuf,"\t");
     strcat(cssndbuf,(actnew->isyell ? "Y" : "N"));
     strcat(cssndbuf,"\t");
     strcat(cssndbuf,(isempty(actnew->complx) ? "N" : "Y"));
     strcat(cssndbuf,"\t");
     strcat(cssndbuf,(isempty(actnew->simple) ? "N" : "Y" ));
     strcat(cssndbuf,"\t");
     strcat(cssndbuf,actnew->list);
}

static VOID
csActionAddHook(                   /* cs action add hookfunc               */
LPACTION act)                      /*   action word                        */
{
     oldActionAddHook(act);

     INT savusn=usrnum;
     CTlcAList* pList=tlcAPI->actGetByName(act->list);
     ASSERT(pList != NULL);
     LPACTLST listcfg=pList->GetConfigPtr();

     MakeActionDpk(act);
     for (int i=0; i < nterms; i++) {
          if (isCSUserInTele(i)) {
               curusr(i);
               if (haskey(listcfg->usekey) && haskey(act->actkey)) {
                    if (qroom(i,NORMAL)) {
                         cnvd2s(tlcdpks[DPK_ACTIONADD],namtmp);
                         senddpk(i,TLCAPID,NORMAL,namtmp,STGLEN,cssndbuf,NULL);
                    }
               }
          }
     }
     curusr(savusn);
}

static VOID
csActionChangeHook(                /* cs action change hookfunc            */
LPACTION actnew,                   /*   new action word                    */
LPACTION actold)                   /*   old action word                    */
{
     oldActionChangeHook(actnew,actold);

     CTlcAList* pList=tlcAPI->actGetByName(actnew->list);
     ASSERT(pList != NULL);
     LPACTLST listcfg=pList->GetConfigPtr();

     INT savusn=usrnum;
     for (int i=0; i < nterms; i++) {
          if (isCSUserInTele(i)) {
               curusr(i);
               // has access to list
               if (haskey(listcfg->usekey)) {
                    // had access before, doesn't have access now
                    if (haskey(actold->actkey) && !haskey(actnew->actkey)) {
                         strcpy(cssndbuf,actnew->name);
                         strcat(cssndbuf,"\t");
                         strcat(cssndbuf,actnew->list);
                         if (qroom(i,NORMAL)) {
                              cnvd2s(tlcdpks[DPK_ACTIONDEL],namtmp);
                              senddpk(i,TLCAPID,NORMAL,namtmp,STGLEN,cssndbuf,NULL);
                         }
                    }
                    // had access before, still has access
                    else if (haskey(actold->actkey)
                     && haskey(actnew->actkey)) {
                         MakeActionDpk(actnew);
                         if (qroom(i,NORMAL)) {
                              cnvd2s(tlcdpks[DPK_ACTIONCHG],namtmp);
                              senddpk(i,TLCAPID,NORMAL,namtmp,STGLEN
                                     ,cssndbuf,NULL);
                         }
                    }
                    // didn't have access before, but has now
                    else if (!haskey(actold->actkey)
                     && haskey(actnew->actkey)) {
                         MakeActionDpk(actnew);
                         if (qroom(i,NORMAL)) {
                              cnvd2s(tlcdpks[DPK_ACTIONADD],namtmp);
                              senddpk(i,TLCAPID,NORMAL,namtmp,STGLEN
                                     ,cssndbuf,NULL);
                         }
                    }
               }
          }
     }
     curusr(savusn);
}

static VOID
csActionDeleteHook(                /* cs action delete hookfunc            */
LPACTION act)                      /*   action word                        */
{
     oldActionDeleteHook(act);

     INT savusn=usrnum;
     strcpy(cssndbuf,act->name);
     strcat(cssndbuf,"\t");
     strcat(cssndbuf,act->list);
     for (int i=0; i < nterms; i++) {
          if (isCSUserInTele(i)) {
               curusr(i);
               if (qroom(i,NORMAL)) {
                    cnvd2s(tlcdpks[DPK_ACTIONDEL],namtmp);
                    senddpk(i,TLCAPID,NORMAL,namtmp,STGLEN,cssndbuf,NULL);
               }
          }
     }
     curusr(savusn);
}

static VOID
csListAddHook(                     /* cs action list add hookfunc          */
LPACTLST list)                     /*   action list                        */
{
     oldListAddHook(list);
     INT savusn=usrnum;
     strcpy(cssndbuf,list->name);
     for (int i=0; i < nterms; i++) {
          if (isCSUserInTele(i)) {
               curusr(i);
               if (qroom(i,NORMAL)) {
                    cnvd2s(tlcdpks[DPK_LISTADD],namtmp);
                    senddpk(i,TLCAPID,NORMAL,namtmp,STGLEN,cssndbuf,NULL);
               }
          }
     }
     curusr(savusn);

}

static VOID
csListDeleteHook(                  /* cs action list delete hookfunc       */
LPACTLST list)                     /*   action list                        */
{
     oldListDeleteHook(list);
     INT savusn=usrnum;
     strcpy(cssndbuf,list->name);
     for (int i=0; i < nterms; i++) {
          if (isCSUserInTele(i)) {
               curusr(i);
               if (qroom(i,NORMAL)) {
                    cnvd2s(tlcdpks[DPK_LISTDEL],namtmp);
                    senddpk(i,TLCAPID,NORMAL,namtmp,STGLEN,cssndbuf,NULL);
               }
          }
     }
     curusr(savusn);
}

static VOID
csListChangeHook(                  /* cs action list change hookfunc       */
LPACTLST listnew,                  /*   new action list                    */
LPACTLST listold)                  /*   old action list                    */
{
     oldListChangeHook(listnew,listold);

     if (!sameas(listnew->usekey,listold->usekey)) {
          INT savusn=usrnum;
          strcpy(cssndbuf,listnew->name);
          for (int i=0; i < nterms; i++) {
               curusr(i);
               if (qroom(i,NORMAL)) {
                    cnvd2s(tlcdpks[DPK_LISTCHG],namtmp);
                    senddpk(i,TLCAPID,NORMAL,namtmp,STGLEN,cssndbuf,NULL);
               }
          }
          curusr(savusn);
     }
}

static VOID
csChanActionUpdateHook(            /* cs chan action list upd hookfunc     */
LPCHANINFO pCInfo)                 /*   pointer to channel info            */
{
     oldChanActionUpdateHook(pCInfo);
     CTlcUser* pUser;
     CTlcChannel* pChannel=tlcAPI->chanGetByName(pCInfo->m_strName);

     if (pChannel == NULL) {
          return;
     }
     for (int i=0; i < nterms; i++) {
          if (isCSUserInTele(i)) {
               pUser=tlcAPI->usrGetByName(uacoff(i)->userid);
               ASSERT(pUser != NULL);
               if (qroom(i,NORMAL)) {
                    if (pChannel->CanAccess(pUser,CHAN_AXS_SEE)) {
                         csnotaddchan(pChannel,i);
                    }
                    else {
                         csnotdelchan(pChannel,i);
                    }
               }
          }
     }
}