/***************************************************************************
 *                                                                         *
 *   IRC.C                                                                 *
 *                                                                         *
 *   Copyright (c) 1995-1997 Galacticomm, Inc.    All Rights Reserved.     *
 *                                                                         *
 *   Internet Relay Chat client for Worldgroup.                            *
 *                                                                         *
 *                                        - Craig Yap 7/5/95               *
 *                                                                         *
 ***************************************************************************/

#include "gcomm.h"
#include "majorbbs.h"
#include "tcpip.h"
#include "dns.h"
#include "alias.h"
#include "galirc.h"
#include "irc.h"
#include "smtpexp.h"

#define FILREV "$Revision: 29 $"

#ifndef ICO2
#define tcpip_errno ips_errno
#define TNFACCP TNFRECV
#define TNFCONN TNFSEND
#endif

VOID (*numrply[MAXRPLY])(          /* array of pointers to functions that  */
                                   /*   are used for numeric replies from  */
                                   /*   the server                         */
CHAR *,                            /*   nick!userid@host                   */
CHAR *,                            /*   numeric reply as a string          */
CHAR *);                           /*   rest of line after the num. reply  */

VOID *ircblk=NULL;                 /* IRC session array block pointer      */

struct olnkcmd *ocmdhead;          /* head of list for outgoing commands   */
struct ilnkcmd *irplhead;          /* head of list for incoming replies    */
struct ircusr *ircptr;             /* pointer to current IRC user          */
HMCVFILE ircmb;                    /* message block file pointer           */
CHAR dftserv[SERVLEN];             /* default IRC server                   */
SHORT dftport;                     /* default port number for dftserv      */
CHAR *superusr;                    /* key to change defaults               */
CHAR *irckey;                      /* key required for IRC access          */
CHAR **banlst;                     /* list of "banned" channels            */
INT nbanned;                       /* number of "banned" channels          */
static INT maxirc;                 /* maximum number of IRC sessions       */
INT ircchg;                        /* IRC credit charge, per minute        */

static VOID buildban(VOID);
static INT globalirc(VOID);
static GBOOL ircinp(VOID);
static VOID ircrcv(struct ircusr *);
static VOID parsirc(CHAR *);
static GBOOL irccall(VOID);
static VOID ircsts(VOID);
CHAR *crpstr(const CHAR *string,CHAR cropch);
static VOID abtcon(VOID);
static VOID irchup(VOID);
static VOID irccon(struct ircusr *);
static VOID ircstrt(VOID);
static VOID ircfin(VOID);
INT ircstt;                        /* IRC module state number              */

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

VOID EXPORT
init__galirc(VOID)                 /* initializing function for IRC        */
{
     INT i;

     stzcpy(ircmodule.descrp,gmdnam("galirc.mdf"),MNMSIZ);
     ircstt=register_module(&ircmodule);
     setmem(numrply,sizeof(numrply),0);
     ircmb=opnmsg("galirc.mcv");
     ircinitcmd();
     stzcpy(dftserv,getmsg(DFTSERV),SERVLEN);
     dftport=numopt(DFTPORT,0,32767);
     ircchg=numopt(IRCCHG,0,32767);
     superusr=stgopt(SUPERUSR);
     irckey=stgopt(IRCKEY);
     maxirc=min(numopt(MAXIRC,1,256),nterms);
     ircblk=alcblok(maxirc,sizeof(struct ircusr));
     for (i=0 ; i < maxirc ; i++) {
          (ircptr=(struct ircusr *)ptrblok(ircblk,i))->chan=IRCVCT;
     }
     buildban();
     globalcmd(globalirc);
     init_ircagt();
}

VOID EXPORT
initwc__galirc(VOID)
{
     init__galirc();
}

static VOID
buildban(VOID)                     /* build list of "banned" channels      */
{
     CHAR *omsgpt,*msgptr,*chnptr;
     INT i;

     nbanned=0;
     omsgpt=msgptr=getmsg(BANNED);
     while (*(msgptr=skpwht(msgptr)) != '\0') {
          msgptr=skpwrd(msgptr);
          nbanned++;
     }
     if (nbanned != 0) {
          banlst=(CHAR **)alczer(sizeof(CHAR *)*nbanned);
          for (i=0,msgptr=omsgpt ; i < nbanned ; i++,msgptr++) {
               chnptr=msgptr=skpwht(msgptr);
               msgptr=skpwrd(msgptr);
               *msgptr='\0';
               banlst[i]=alcdup(chnptr);
          }
     }
}

GBOOL
ircoff(                            /* get pointer to user's IRC session    */
INT unum)                          /*   user's channel number to look up   */
{
     INT i;

     for (i=0 ; i < maxirc ; i++) {
          if ((ircptr=(struct ircusr *)ptrblok(ircblk,i))->chan == unum) {
               return(TRUE);
          }
     }
     ircptr=NULL;
     return(FALSE);
}

struct ircusr *
ircavl(VOID)                       /* get pointer to available IRC session */
{
     INT i;

     for (i=0 ; i < maxirc ; i++) {
          if ((ircptr=(struct ircusr *)ptrblok(ircblk,i))->chan == IRCVCT) {
               return(ircptr);
          }
     }
     return(NULL);
}

static INT
globalirc(VOID)                    /* global "/IRC" command                */
{
     CHAR *cp;

     if (margc >= 1 && sameas(margv[0],"/IRC")) {
          cp=margv[0];
          rstrin();
          movmem(cp,cp+3,(UINT)(INPSIZ-(cp-input)-3));
          input[INPSIZ-1]='\0';
          movmem("/go ",cp,4);
          parsin();
          return(globalgo());
     }
     return(0);
}

static GBOOL
ircinp(VOID)                       /* input routine                        */
{
     GBOOL rc=TRUE;
     CHAR *cp,inpchr;
     struct olnkcmd *ocurr;
     struct savirc sirc;

     setmbk(ircmb);
     if (usrptr->substt == ENTRNCE) {
          if ((ircptr=ircavl()) == NULL) {
               prfmsg(IRCFULL,maxirc);
               outprf(usrnum);
               return(0);
          }
          if (!haskey(irckey)) {
               prfmsg(IRCNOA);
               outprf(usrnum);
               return(0);
          }
          setmem(ircptr,sizeof(struct ircusr),0);
          ircptr->chan=usrnum;
          ircptr->sockt=-1;
          stzcpy(ircptr->alias,(*alsofusr)(usaptr->userid),NICKLEN);
          ircstrt();
          usrptr->substt=(haskey(superusr) ? CNTRLMN2 : NRMLMNU);
          ircptr->psubstt=usrptr->substt;
          if (margc > 1) {
               stzcpy(ircptr->nick,margv[1],NICKLEN);
               if (margc > 2) {
                    stzcpy(ircptr->serv,margv[2],SERVLEN);
               }
               usrptr->substt=IRCSTART;
               btuinj(usrnum,CRSTG);
          }
          else {
               prfmsg(usrptr->substt);
               outprf(usrnum);
          }
          return(TRUE);
     }
     if (!ircoff(usrnum)) {
          shocst("IRC ERROR","Cannot locate IRC info for %s",usaptr->userid);
          usrptr->flags&=~NOGLOB;
          return(FALSE);
     }
     ircptr->lstinp=time(NULL);
     if (margc == 1 && sameas(input,"X")) {
          switch (usrptr->substt) {
          case NRMLMNU:
          case CNTRLMN2:
               ircptr->chan=IRCVCT;
               return(FALSE);
          case CONNECTD:
               break;
          case LOOKING:
          case CONNING:
               abtcon();
          default:
               prfmsg(usrptr->substt=ircptr->psubstt);
               outprf(usrnum);
               return(TRUE);
          }
     }
     switch (usrptr->substt) {
     case CNTRLMN2:
          if ((inpchr=toupper(*input)) != '\0') {
               switch (inpchr) {
               case 'I':
                    if (!tstcrd(ircchg)) {
                         prfmsg(NOCREDS);
                         prfmsg(usrptr->substt);
                    }
                    else {
                         usrptr->substt=IRCSTART;
                         btuinj(usrnum,CRSTG);
                    }
                    break;
               case 'C':
                    prfmsg(usrptr->substt=NICKINP,ircptr->nick);
                    break;
               case 'S':
                    prfmsg(usrptr->substt=DSRVINP,dftserv);
                    break;
               case 'X':
                    rc=FALSE;
                    break;
               case '?':
                    prfmsg(usrptr->substt);
                    break;
               default:
                    prfmsg(BADCMD);
                    prfmsg(usrptr->substt);
                    break;
               }
          }
          else {
               prfmsg(usrptr->substt);
          }
          break;
     case DSRVINP:
          if (margc > 0) {
               stzcpy(dftserv,input,SERVLEN);
          }
          prfmsg(usrptr->substt=CNTRLMN2);
          break;
     case NRMLMNU:
          if ((inpchr=toupper(*input)) != '\0') {
               switch (inpchr) {
               case 'I':
                    if (!tstcrd(ircchg)) {
                         prfmsg(NOCREDS);
                         prfmsg(usrptr->substt);
                    }
                    else {
                         usrptr->substt=IRCSTART;
                         btuinj(usrnum,CRSTG);
                    }
                    break;
               case 'C':
                    prfmsg(usrptr->substt=NICKINP,ircptr->nick);
                    break;
               case 'X':
                    rc=FALSE;
                    break;
               case '?':
                    prfmsg(usrptr->substt);
                    break;
               default:
                    prfmsg(BADCMD);
                    prfmsg(usrptr->substt);
                    break;
               }
          }
          else {
               prfmsg(usrptr->substt);
          }
          break;
     case NICKINP:
          if (margc > 0) {
               stzcpy(ircptr->nick,margv[0],NICKLEN);
          }
          prfmsg(usrptr->substt=SERVINP,ircptr->serv);
          break;
     case SERVINP:
          if (margc > 0) {
               stzcpy(ircptr->serv,margv[0],SERVLEN);
          }
          prfmsg(usrptr->substt=IRCNINP,ircptr->ircnam);
          break;
     case IRCNINP:
          if (margc > 0) {
               rstrin();
               stzcpy(ircptr->ircnam,input,IRCNAMELEN);
          }
          prfmsg(usrptr->substt=PORTINP,ircptr->port);
          break;
     case PORTINP:
          if (margc > 0) {
               ircptr->port=atoi(margv[0]);
          }
          stzcpy(sirc.userid,usaptr->userid,UIDSIZ);
          stzcpy(sirc.nick,ircptr->nick,NICKLEN);
          stzcpy(sirc.server,ircptr->serv,SERVLEN);
          stzcpy(sirc.ircnam,ircptr->ircnam,IRCNAMELEN);
          sirc.port=ircptr->port;
          ssavirc(&sirc);
          prfmsg(usrptr->substt=ircptr->psubstt);
          break;
     case IRCSTART:
          stzcpy(ircptr->dns.name,ircptr->serv,DNSNSZ);
          ircptr->dns.numaddr=1;
          ircptr->dns.callbk=ircbk;
          if (!dnsn2a(&ircptr->dns)) {
               prfmsg(usrptr->substt=LOOKING,ircptr->serv);
          }
          usrptr->flags|=NOGLOB;
          break;
     case CONNECTD:
          rstrin();
          cp=input;
          if (*cp == '/' && *(cp+1) != '\0') {
               if (ocmdhead != NULL) {
                    clrprf();
                    cp=firstwd(input);
                    cp++;
                    for (ocurr=ocmdhead ; ocurr != NULL ; ocurr=ocurr->next) {
                         if (sameto(cp,ocurr->comstruct.cmdname)
                          && haskey(ocurr->comstruct.key)) {
                              depad(input);
                              ocurr->comstruct.cmdfunc(input+1,
                               &ocurr->comstruct);
                              break;
                         }
                    }
                    if (ocurr == NULL) {
                         prfmsg(CMDNOFND,cp);
                    }
               }
          }
          else if (*cp != '\0') {
               if (*ircptr->curchan == '\0') {
                    prfmsg(INLIMBO);
               }
               else {
                    sprintf(vdatmp,"PRIVMSG %s :",ircptr->curchan);
                    stzcat(vdatmp,cp,vdasiz);
                    stzcat(vdatmp,"\r\n",vdasiz);
                    ircsbf(vdatmp,strlen(vdatmp));
                    ircsnd();
               }
          }
          *input='\0';
          break;
     case LOOKING:
          abtcon();
          prfmsg(usrptr->substt=ircptr->psubstt);
          break;
     case CONNING:
          usrptr->substt=IRCEXIT;
     default:
          break;
     }
     if (usrptr->substt == CONNECTD) {
          prfmsg(ATTRIB);
     }
     outprf(usrnum);
     rstmbk();
     if (!rc) {
          ircptr->chan=IRCVCT;
     }
     return(rc);
}

VOID
ssavirc(                           /* save user preferred IRC info         */
struct savirc *sirc)
{
     dfaSetBlk(genbb);
     stzcpy(sirc->modnam,ircmodule.descrp,MNMSIZ);
     stzcpy(sirc->userid,usaptr->userid,UIDSIZ);
     if (dfaAcqEQ(NULL,sirc,0)) {
          dfaUpdateV(sirc,sizeof(struct savirc));
     }
     else {
          dfaInsertV(sirc,sizeof(struct savirc));
     }
     dfaRstBlk();
}

static VOID
ircstrt(VOID)                      /* get info before starting IRC con.    */
{
     struct savirc sirc;

     dfaSetBlk(genbb);
     stzcpy(sirc.modnam,ircmodule.descrp,MNMSIZ);
     stzcpy(sirc.userid,usaptr->userid,UIDSIZ);
     if (dfaAcqEQ(&sirc,&sirc,0)) {
          stzcpy(ircptr->nick,sirc.nick,NICKLEN);
          stzcpy(ircptr->serv,sirc.server,SERVLEN);
          stzcpy(ircptr->ircnam,sirc.ircnam,IRCNAMELEN);
          ircptr->port=sirc.port;
     }
     else {
          stzcpy(ircptr->nick,ircptr->alias,NICKLEN);
          stzcpy(ircptr->serv,dftserv,SERVLEN);
          stzcpy(ircptr->ircnam,usaptr->usrnam,IRCNAMELEN);
          ircptr->port=dftport;
     }
     dfaRstBlk();
}

VOID
ircbk(                             /* DNS callback routine for IRC         */
struct dns *dnsptr)                /*   DNS structure                      */
{
     GBOOL gahead;

     if (!ircoff(usrnum)) {
          return;
     }
     setmbk(ircmb);
     if (dnsptr->status >= 0) {
          if (!(gahead=irccall())) {
               prfmsg(CNTCON,ircptr->serv);
          }
     }
     else {
          prfmsg(LUPERR,dnsemg);
          gahead=FALSE;
     }
     outprf(usrnum);
     clrprf();
     if (!gahead) {
          abtcon();
          prfmsg(usrptr->substt=ircptr->psubstt);
          outprf(usrnum);
          clrprf();
     }
     rstmbk();
}

static GBOOL
irccall(VOID)                      /* routine that connects to the server  */
{
     INT rc,rwsav;

     if (!ircoff(usrnum)) {
          return(FALSE);
     }
     rwsav=rcvwin;
     rcvwin=ircptr->rcvwin=min(MAXBYT,btuoba(usrnum));
     rc=tcpdial(ircptr->dns.inaddr[0],htons(ircptr->port),0,&ircptr->sockt);
     rcvwin=rwsav;
     switch (rc) {
     case DLCNOW:
     case DLCING:
          sktnfy(TNFCONN,ircptr->sockt,irccon,ircptr,usrnum);
          prfmsg(usrptr->substt=CONNING,inet_ntoa(ircptr->dns.inaddr[0]));
          btuinj(usrnum,CYCLE);
          return(TRUE);
     default:
          prfmsg(CONNERR,rc,tcpip_errno,tcpErrStg(tcpip_errno));
          break;
     }
     ircptr->sockt=-1;
     return(FALSE);
}

static VOID
irccon(                            /* logs the user onto IRC               */
struct ircusr *irc)                /*   ircusr structure                   */
{
     CHAR onechar;
     static CHAR tbuf[BUFSIZ];

     if (irc->chan != usrnum) {
          return;
     }
     setmbk(ircmb);
     if (btuoba(usrnum) != OUTSIZ-1) {
          return;
     }
     if (recv(irc->sockt,&onechar,0,0) < 0 && tcpip_errno != EWOULDBLOCK) {
          prfmsg(IMMERR,irc->dns.name,tcpErrStg(tcpip_errno));
          outprf(usrnum);
          clrprf();
          usrptr->substt=IRCEXIT;
          return;
     }
     sprintf(tbuf,"NICK %s\r\n",irc->nick);
     ircsbf(tbuf,strlen(tbuf));
     ircsnd();
     sprintf(tbuf,"USER %s %s %s :%s\r\n",irc->alias,hstdom,irc->serv,
                  irc->ircnam);
     ircsbf(tbuf,strlen(tbuf));
     ircsnd();
     usrptr->substt=CONNECTD;
     irc->crdtim=btuTicker();
     sktnfy(TNFRECV,irc->sockt,ircrcv,irc,usrnum);
     sktcnc(TNFCONN,irc->sockt);
}

static VOID
ircrcv(                            /* receive info from socket             */
struct ircusr *irc)                /*   ircusr structure                   */
{
     CHAR *ptr;
     INT nread,nroom;

     if (irc->chan != usrnum) {
          return;
     }
     if ((nroom=MAXBYT-irc->rcvcnt-1) == 0 || btuoba(usrnum) < 512) {
          return;
     }
#ifdef GCDOS
     rwnmgr(irc->sockt,nroom,&irc->rcvwin);
#endif // GCDOS
     setmbk(ircmb);
     if ((nread=recv(irc->sockt,irc->ibuf+irc->rcvcnt,nroom,0)) <= 0) {
          usrptr->substt=IRCEXIT;
          prfmsg(CONDROP,tcpip_errno,tcpErrStg(tcpip_errno));
          outprf(usrnum);
          clrprf();
     }
     else {
          irc->rcvcnt=memstp(irc->ibuf,irc->rcvcnt+nread,'\0');
          irc->ibuf[irc->rcvcnt]='\0';
          irc->rcvcnt=strlen(strstp(irc->ibuf,'\r'));
          while ((ptr=strchr(irc->ibuf,'\n')) != NULL) {
               *ptr='\0';
               parsirc(irc->ibuf);
               strcpy(irc->ibuf,ptr+1);
               irc->rcvcnt=strlen(irc->ibuf);
          }
          if (irc->rcvcnt >= MAXBYT-1) {
               irc->rcvcnt=0;
          }
     }
}

static VOID
parsirc(                           /* parses input received from socket    */
CHAR *inlin)                       /*   input from the server              */
{
     CHAR *from,*cmd,*rest,*cp;
     INT numrpl;
     struct ilnkcmd *curr;
     static CHAR errbuf[BUFSIZ];

     if (!ircoff(usrnum)) {
          return;
     }
     setmbk(ircmb);
     stzcpy(errbuf,inlin,BUFSIZ);
     if ((from=strtok(inlin," ")) == NULL || (cmd=strtok(NULL," ")) == NULL) {
          return;
     }
     while (*from == ':') {
          from++;
     }
     if (sameto("ERROR",from) && from[5] != '!') {
          usrptr->substt=IRCEXIT;
          prfmsg(CONDED,crpstr(errbuf,':'));
          outprf(usrnum);
          clrprf();
     }
     else if (sameto("PING",from)) {
          onping(cmd);
     }
     else if ((rest=strtok(NULL,"")) != NULL) {
          while (*rest == ':' || isspace(*rest)) {
               rest++;
          }
          depad(rest);
          if ((numrpl=atoi(cmd)) > 0 && numrpl < MAXRPLY) {
               if (numrply[numrpl] != NULL) {
                    numrply[numrpl](from,cmd,rest);
               }
               else {
                    if ((cp=strchr(rest,' ')) != NULL) {
                         while (*cp == ':' || *cp == ' ') {
                              cp++;
                         }
                         prfmsg(DEFNUM,cp);
                    }
               }
          }
          else if (irplhead != NULL) {
               for (curr=irplhead ; curr != NULL ; curr=curr->next) {
                    if (sameas(cmd,curr->comstruct.rplname)) {
                         curr->comstruct.rplfunc(from,cmd,rest);
                         break;
                    }
               }
          }
          prfmsg(ATTRIB);
          outprf(usrnum);
          clrprf();
     }
}

CHAR *
crpstr(                            /* crops string after passed character  */
const CHAR *string,                /*   string to crop                     */
CHAR cropch)                       /*   crop character                     */
{
     CHAR *ptr;

     if ((ptr=strchr(string,cropch)) != NULL) {
          do {
               ptr++;
          } while (isspace(*ptr));
          return(ptr);
     }
     return((CHAR *)string);
}

static VOID
abtcon(VOID)                       /* abort connection and restore channel */
{
     if (!ircoff(usrnum)) {
          return;
     }
     usrptr->flags&=~NOGLOB;
     rstrxf();
     dnsabt();
     clrprf();
     if (ircptr->sockt != -1) {
          clsskt(ircptr->sockt);
          ircptr->sockt=-1;
     }
     ircptr->flags&=~IRCACTIVE;
}

static VOID
irchup(VOID)                       /* logs user off IRC if active          */
{
     CHAR *ptr;

     if (usrptr->state == ircstt && ircoff(usrnum)) {
          if (ircptr->sockt != -1) {
               if (usrptr->substt == CONNECTD) {
                    ptr=spr("QUIT :Hangup\r\n");
                    ircsbf(ptr,strlen(ptr));
                    ircsnd();
               }
               clsskt(ircptr->sockt);
               ircptr->sockt=-1;
          }
          ircptr->chan=IRCVCT;
          ircptr->flags&=~IRCACTIVE;
     }
     dnsabt();
}

static VOID
ircsts(VOID)                       /* status handler                       */
{
     GBOOL more=TRUE;

     setmbk(ircmb);
     if (status != CYCLE) {
          dfsthn();
          return;
     }
     if (!ircoff(usrnum)) {
          usrptr->substt=(haskey(superusr) ? CNTRLMN2 : NRMLMNU);
          btuinj(usrnum,CRSTG);
          return;
     }
     switch (usrptr->substt) {
     case CONNECTD:
          if (!ircdcrd(ircptr)) {
               prfmsg(ROCREDS);
               usrptr->substt=IRCEXIT;
          }
#ifdef GCDOS
          rwnadj(ircptr->sockt,MAXBYT,min(MAXBYT,btuoba(usrnum)),MAXBYT/2,&ircptr->rcvwin);
#endif // GCDOS
          break;
     case IRCEXIT:
          abtcon();
          ircptr->curchan[0]='\0';
          usrptr->substt=ircptr->psubstt;
          btuinj(usrnum,CRSTG);
          more=FALSE;
          break;
     case LOOKING:
     case CONNING:
          break;
     default:
          more=FALSE;
          break;
     }
     if (more) {
          btuinj(usrnum,CYCLE);
     }
     outprf(usrnum);
     rstmbk();
}

VOID
ircsbf(                            /* put bytes in an array for output     */
CHAR *bytes,                       /*   bytes to put in array              */
SHORT bytcnt)                      /*   number of bytes to put in array    */
{
     INT nroom;
     INT nbuf;

     if (!ircoff(usrnum)) {
          return;
     }
     nroom=IRCSIZ-ircptr->bufcnt;
     nbuf=min(nroom,bytcnt);
     if (nbuf > 0) {
          movmem(bytes,ircptr->obuf+ircptr->bufcnt,nbuf);
          ircptr->bufcnt+=nbuf;
     }
}

VOID
ircsnd(VOID)                       /* sends the buffer to the socket       */
{
     if (!ircoff(usrnum)) {
          return;
     }
     setmbk(ircmb);
     switch (sndmgr(ircptr->obuf,&ircptr->bufcnt,ircptr->sockt)) {
     case -1:
          usrptr->substt=IRCEXIT;
          prfmsg(CONDED,tcpErrStg(tcpip_errno));
          outprf(usrnum);
          clrprf();
          break;
     case 0:
          if (ircptr->sockt != -1) {
               sktcnc(TNFSEND,ircptr->sockt);
          }
          break;
     case 1:
          sktnfy(TNFSEND,ircptr->sockt,(VOID (*)())ircsnd,ircptr,usrnum);
          break;
     }
}

/***************************************************************************
 *  for additions or add-on options                                        *
 ***************************************************************************/

VOID
regoircmd(                         /* add outgoing command structure       */
struct ocmdstrt *newcmd)           /*   command structure to add           */
{
     struct olnkcmd *curr=NULL,*prev,*tmpcmd;

     if (ocmdhead != NULL) {
          for (prev=curr=ocmdhead ; curr != NULL ; prev=curr,curr=curr->next) {
               if (sameas(curr->comstruct.cmdname,newcmd->cmdname)) {
                    break;
               }
          }
     }
     if (curr == NULL) {
          tmpcmd=(struct olnkcmd *)alcmem(sizeof(struct olnkcmd));
          memcpy(&tmpcmd->comstruct,newcmd,sizeof(struct ocmdstrt));
          if (ocmdhead == NULL) {
               ocmdhead=tmpcmd;
          }
          else {
               prev->next=tmpcmd;
          }
          tmpcmd->next=NULL;
     }
     else {
          memcpy(&curr->comstruct,newcmd,sizeof(struct ocmdstrt));
     }
}

VOID
regiircmd(                         /* add incoming command structure       */
struct irplstrt *newcmd,           /*   incoming command structure         */
INT rply)                          /*   positive if numeric command        */
{
     struct ilnkcmd *curr=NULL,*prev,*tmpcmd;

     if (rply > 0) {
          if (rply >= MAXRPLY) {
               catastro("Server reply %d is over the maximum of %d",rply,
                        MAXRPLY);
          }
          numrply[rply]=newcmd->rplfunc;
     }
     else {
          if (irplhead != NULL) {
               for (prev=curr=irplhead ; curr != NULL ; prev=curr,
                                                        curr=curr->next) {
                    if (sameas(curr->comstruct.rplname,newcmd->rplname)) {
                         break;
                    }
               }
          }
          if (curr == NULL) {
               tmpcmd=(struct ilnkcmd *)alcmem(sizeof(struct ilnkcmd));
               memcpy(&tmpcmd->comstruct,newcmd,sizeof(struct irplstrt));
               if (irplhead == NULL) {
                    irplhead=tmpcmd;
               }
               else {
                    prev->next=tmpcmd;
               }
               tmpcmd->next=NULL;
          }
          else {
               memcpy(&curr->comstruct,newcmd,sizeof(struct irplstrt));
          }
     }
}

/***************************************************************************
 *  some default registered commands from server that shouldn't be put in  *
 *  ircfnc.c                                                               *
 ***************************************************************************/

VOID
onping(                            /* sends back a PONG to the server      */
CHAR *line)                        /*   line to send back to the server    */
{
     sprintf(vdatmp,"PONG %s\r\n",line);
     ircsbf(vdatmp,strlen(vdatmp));
     ircsnd();
}

static VOID
ircfin(VOID)                       /* finish-up (system shutdown) routine  */
{
     clsmsg(ircmb);
}

GBOOL                              /*   return FALSE if insufficient creds */
ircdcrd(                           /* IRC deduct credit facility           */
struct ircusr *irc)                /*   IRC user information               */
{
     if (btuTicker()-irc->crdtim >= 60) {
          if (!dedcrd(ircchg,1)) {
               return(FALSE);
          }
          irc->crdtim=btuTicker();
     }
     return(TRUE);
}
