/***************************************************************************
 *                                                                         *
 *   IRCAGT.C                                                              *
 *                                                                         *
 *   Copyright (c) 1995-1997 Galacticomm, Inc.    All Rights Reserved.     *
 *                                                                         *
 *   Internet Relay Chat client agent for Worldgroup client/server.        *
 *                                                                         *
 *                                        - Scott Brinker 7/24/95          *
 *                                          walhf Charles Dunn & Craig Yap *
 *                                                                         *
 ***************************************************************************/

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

#define FILREV "$Revision: 23 $"

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

static VOID ircread();
static VOID ircwrite();
static VOID nulirc(VOID);
static VOID aboirc(VOID);

#define IRCAID "GALIRC"            /* Internet Relay Chat C/S app-id       */

static
struct agent ircagt={
     "GALIRC",                     /* app-id                               */
     ircread,                      /* dynapak read handler                 */
     ircwrite,                     /* dynapak write handler                */
     nulirc,                       /* file transfer done handler           */
     aboirc                        /* read or write abort handler          */
};

#define IDBFSZ 512                 /* size of buf for data sent w/senddpk()*/

static
struct saunam *rspdpk;             /* modifiable copy of req. dynapak hdr. */

static VOID circbk(struct dns *dnsptr);
static GBOOL circcall(VOID);
static VOID circcon(struct ircusr *irc);
static VOID circrcv(struct ircusr *irc);
static VOID cparsirc(CHAR *inlin);
static VOID circsts(VOID);
static VOID ircagt_bye(VOID);
static VOID circsnd(VOID);


VOID
init_ircagt(VOID)                  /* initialize client/server IRC         */
{
     register_agent(&ircagt);
     hook_disconnect(ircagt_bye);
     rspdpk=(struct saunam *)alcmem(sizeof(struct saunam));
}

static VOID
ircread(                           /* IRC dynapak read handler             */
INT direction,
struct saunam *dpknam)
{
     struct savirc sirc;
     CHAR *dpkstg;
     INT i;

     (VOID)direction;
     *rspdpk=*dpknam;
     dpkstg=cnvs2d(rspdpk);
     setmbk(ircmb);
     if (!stdchk(irckey)) {
          rejectreq();
          return;
     }
     if (!ircoff(usrnum)) {
          if ((ircptr=ircavl()) == NULL) {
               rsp2read(NULL,STGLEN,"noroom",NULL);
               return;
          }
          setmem(ircptr,sizeof(struct ircusr),0);
          ircptr->chan=usrnum;
          ircptr->sockt=-1;
          ircptr->psubstt=ENTRNCE;
          stzcpy(ircptr->alias,(*alsofusr)(usaptr->userid),NICKLEN);
          for (i=0 ; i < strlen(ircptr->alias) ; i++) {
               if (ircptr->alias[i] == '.') {
                    ircptr->alias[i]='\0';
               }
          }
     }
     if (samepat("sau:irccfg",dpkstg)) {
          dfaSetBlk(genbb);
          stzcpy(sirc.modnam,ircmodule.descrp,MNMSIZ);
          stzcpy(sirc.userid,usaptr->userid,UIDSIZ);
          if (dfaAcqEQ(&sirc,&sirc,0)) {
               sprintf(rsptmp,"%s\t%s\t%s\t%d",
                    sirc.nick,sirc.server,sirc.ircnam,sirc.port);
          }
          else {
               sprintf(rsptmp,"%s\t%s\t%s\t%d",
                    ircptr->alias,dftserv,usaptr->usrnam,dftport);
          }
          rsp2read(NULL,STGLEN,rsptmp,NULL);
     }
     else if (samepat("sa:banned",dpkstg)) {
          for (i=0,*rsptmp='\0' ; i < nbanned ; i++) {
               strcat(rsptmp,banlst[i]);
               strcat(rsptmp,"\t");
          }
          rsp2read(NULL,STGLEN,rsptmp,NULL);
     }
     else {
          rejectreq();
     }
}

static VOID
ircwrite(                          /* IRC dynapak write handler            */
struct saunam *dpknam,
UINT length,
VOID *value)
{
     CHAR *cp;
     CHAR *dpkstg;
     struct savirc sirc;
     struct olnkcmd *ocurr;

     *rspdpk=*dpknam;
     dpkstg=cnvs2d(rspdpk);
     setmbk(ircmb);
     if (!stdchk(irckey)) {
          rejectreq();
          return;
     }
     if (!ircoff(usrnum)) {
          rejectreq();
          return;
     }
     if (samepat("sau:irc",dpkstg)
      && ircptr->psubstt == CONNECTD) {
          if (length > IDBFSZ/2) {
               rejectreq();
               return;
          }
          ircptr->lstinp=time(NULL);
          cp=value;
          if (*cp == '/' && *(cp+1) != '\0') {
               if (ocmdhead != NULL) {
                    cp=firstwd(value);
                    cp++;
                    for (ocurr=ocmdhead ; ocurr != NULL ; ocurr=ocurr->next) {
                         if (sameto(cp,ocurr->comstruct.cmdname)
                          && haskey(ocurr->comstruct.key)) {
                              depad(value);
                              ocurr->comstruct.cmdfunc((CHAR *)value+1,
                               &ocurr->comstruct);
                              /* command handler responsible for rsp2wri   */
                              break;
                         }
                    }
                    if (ocurr == NULL) {
                         rsp2write(FALSE,STGLEN,spr("nocmd\t%s",cp),NULL);
                    }
               }
          }
          else if (*cp != '\0') {
               if (*ircptr->curchan == '\0') {
                    rsp2write(FALSE,STGLEN,"nochan",NULL);
               }
               else {
                    sprintf(vdatmp,"PRIVMSG %s :",ircptr->curchan);
                    stzcat(vdatmp,cp,vdasiz);
                    stzcat(vdatmp,"\r\n",vdasiz);
                    ircsbf(vdatmp,strlen(vdatmp));
                    circsnd();
                    rsp2write(TRUE,0,NULL,NULL);
               }
          }
          else {
               rejectreq();
          }
     }
     else if (samepato("sau:irccfg",dpkstg)) {
          if (itemcnt(value) != 4 ) {
               rejectreq();
               return;
          }
          if (!tstcrd(ircchg)) {
               prfmsg(NOCREDS);
               ircdyna("nocreds",stp4cs(prfbuf),NULL,NULL,NULL);
               rejectreq();
               return;
          }
          stzcpy(ircptr->nick,itemidx(value,0),NICKLEN);
          stzcpy(ircptr->serv,itemidx(value,1),SERVLEN);
          stzcpy(ircptr->ircnam,itemidx(value,2),IRCNAMELEN);
          ircptr->port=atoi(itemidx(value,3));
          depad(ircptr->nick);
          depad(ircptr->serv);
          depad(ircptr->ircnam);
          if (sameas(dpknam->suffix,"irccfgw")) {
               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);
          }
          stzcpy(ircptr->dns.name,ircptr->serv,DNSNSZ);
          ircptr->dns.numaddr=1;
          ircptr->dns.callbk=circbk;
          if (!dnsn2a(&ircptr->dns)) {
               ircdyna("looking",ircptr->serv,NULL,NULL,NULL);
               ircptr->psubstt=LOOKING;
          }
          cycleme(circsts);
     }
     else if (samepat("sau:exit",dpkstg)) {
          aboirc();
          rsp2write(TRUE,0,NULL,NULL);
     }
     else {
          rejectreq();
     }
}

static VOID
circbk(                            /* DNS callback routine for client IRC  */
struct dns *dnsptr)                /*   DNS structure                      */
{
     CHAR stg[8];

     if (!ircoff(usrnum)) {
          return;
     }
     if (dnsptr->status >= 0 && circcall()) {
          ircdyna("conning",inet_ntoa(ircptr->dns.inaddr[0]),NULL,NULL,NULL);
     }
     else {
          aboirc();
          ircdyna("connerr",itoa(tcpip_errno,stg,10),tcpErrStg(tcpip_errno),
                  NULL,NULL);
     }
}

static GBOOL
circcall(VOID)                     /* connects to the server for client IRC*/
{
     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:
          ircptr->psubstt=CONNING;
          sktnfy(TNFCONN,ircptr->sockt,circcon,ircptr,usrnum);
          return(TRUE);
     }
     ircptr->sockt=-1;
     return(FALSE);
}

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

     if (irc->chan != usrnum) {
          return;
     }
     setmbk(ircmb);
     if (!qroom(usrnum,NORMAL)) {
          return;
     }
     if (recv(irc->sockt,&onechar,0,0) < 0 && tcpip_errno != EWOULDBLOCK) {
          ircdyna("immerr",irc->dns.name,tcpErrStg(tcpip_errno),NULL,NULL);
          irc->psubstt=IRCEXIT;
          return;
     }
     sprintf(tbuf,"NICK %s\r\n",irc->nick);
     ircsbf(tbuf,strlen(tbuf));
     circsnd();
     sprintf(tbuf,"USER %s %s %s :%s\r\n",(*alsofusr)(usaptr->userid),
                  hstdom,irc->serv,irc->ircnam);
     ircsbf(tbuf,strlen(tbuf));
     circsnd();
     irc->psubstt=CONNECTD;
     irc->crdtim=btuTicker();
     sktnfy(TNFRECV,irc->sockt,circrcv,irc,usrnum);
     sktcnc(TNFCONN,irc->sockt);
}

static VOID
circrcv(                           /* receive info from socket for client  */
struct ircusr *irc)                /*   ircusr structure                   */
{
     CHAR *ptr,stg[8];
     INT nread,nroom;

     if (irc->chan != usrnum) {
          return;
     }
     if ((nroom=MAXBYT-irc->rcvcnt-1) == 0 || !qroom(usrnum,NORMAL)) {
          return;
     }
#ifdef GCDOS
     rwnmgr(irc->sockt,nroom,&irc->rcvwin);
#endif // GCDOS
     if ((nread=recv(irc->sockt,irc->ibuf+irc->rcvcnt,nroom,0)) <= 0) {
          irc->psubstt=IRCEXIT;
          ircdyna("condrop",itoa(tcpip_errno,stg,10),tcpErrStg(tcpip_errno),
                  NULL,NULL);
     }
     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';
               cparsirc(irc->ibuf);
               strcpy(irc->ibuf,ptr+1);
               irc->rcvcnt=strlen(irc->ibuf);
          }
          if (irc->rcvcnt >= MAXBYT-1) {
               irc->rcvcnt=0;
          }
     }
}

static VOID
cparsirc(                          /* parses input from socket for client  */
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] != '!') {
          ircptr->psubstt=IRCEXIT;
          ircdyna("condead",crpstr(errbuf,':'),NULL,NULL,NULL);
     }
     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++;
                         }
                         ircdyna("defnum",cp,NULL,NULL,NULL);
                    }
               }
          }
          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;
                    }
               }
          }
     }
}

VOID
ircdyna(                           /* send unsolicited IRC dynapak         */
CHAR *ircsfx,                      /*   type: public msg, private msg, etc.*/
CHAR *parm1,                       /*   variable parameters (as in spr())  */
CHAR *parm2,                       /*        caution: total size of all    */
CHAR *parm3,                       /*        parms must be < 508 bytes     */
CHAR *parm4)
{
     static CHAR datbuf[IDBFSZ];

     if ((usrptr->flags&ISGCSU) && qroom(usrnum,NORMAL)) {
          stzcpy(namtmp->sysid,msysid,SIDSIZ);
          stzcpy(namtmp->appid,IRCAID,AIDSIZ);
          stzcpy(namtmp->usrid,usaptr->userid,UIDSIZ);
          namtmp->flags=0x00;
          stzcpy(namtmp->suffix,ircsfx,SFXSIZ);
          datbuf[0]='\0';
          if (parm1 != NULL) {
               strcpy(datbuf,parm1);
               if (parm2 != NULL) {
                    strcat(datbuf,"\t");
                    strcat(datbuf,parm2);
                    if (parm3 != NULL) {
                         strcat(datbuf,"\t");
                         strcat(datbuf,parm3);
                         if (parm4 != NULL) {
                              strcat(datbuf,"\t");
                              strcat(datbuf,parm4);
                         }
                    }
               }
          }
          senddpk(usrnum,IRCAID,NORMAL,namtmp,STGLEN,datbuf,NULL);
     }
}

static VOID
circsts(VOID)                      /* client status handler                */
{
     setmbk(ircmb);
     if (!ircoff(usrnum)) {
          rsp2write(TRUE,0,NULL,NULL);
          dnsabt();
          return;
     }
     switch (ircptr->psubstt) {
     case CONNECTD:
          if (!ircdcrd(ircptr)) {
               prfmsg(ROCREDS);
               ircdyna("rocreds",stp4cs(prfbuf),NULL,NULL,NULL);
               ircptr->psubstt=IRCEXIT;
          }
#ifdef GCDOS
          rwnadj(ircptr->sockt,MAXBYT,min(MAXBYT,btuoba(usrnum)),MAXBYT/2,&ircptr->rcvwin);
#endif // GCDOS
          break;
     case IRCEXIT:
          dnsabt();
          if (ircptr->sockt != -1) {
               clsskt(ircptr->sockt);
               ircptr->sockt=-1;
          }
          ircptr->curchan[0]='\0';
          ircptr->flags&=~IRCACTIVE;
          ircptr->chan=IRCVCT;
          ircptr->psubstt=ENTRNCE;
          rsp2write(TRUE,0,NULL,NULL);  // kills cycleme()
          return;
     default:
          break;
     }
     cycleme(circsts);
}

static VOID
ircagt_bye(VOID)                   /* automatic QUIT if disconnect         */
{
     CHAR *ptr;

     if (ircoff(usrnum)) {
          if (ircptr->psubstt == CONNECTD) {
               ptr=spr("QUIT :Hangup\r\n");
               ircsbf(ptr,strlen(ptr));
               circsnd();
          }
          if (ircptr->sockt != -1) {
               clsskt(ircptr->sockt);
               ircptr->sockt=-1;
          }
          ircptr->chan=IRCVCT;
     }
     dnsabt();
}

static VOID
circsnd(VOID)                      /* sends buffer to socket for client    */
{
     if (!ircoff(usrnum)) {
          return;
     }
     switch (sndmgr(ircptr->obuf,&ircptr->bufcnt,ircptr->sockt)) {
     case -1:
          ircptr->psubstt=IRCEXIT;
          break;
     case 0:
          if (ircptr->sockt != -1) {
               sktcnc(TNFSEND,ircptr->sockt);
          }
          break;
     case 1:
          sktnfy(TNFSEND,ircptr->sockt,(VOID (*)())circsnd,ircptr,usrnum);
          break;
     }
}

VOID
pfordy(                            /* sends out a prf or dynapak           */
INT msg,
CHAR *dyna,
CHAR *parm1,
CHAR *parm2,
CHAR *parm3,
CHAR *parm4)
{
     if (usrptr->flags&ISGCSU) {
          ircdyna(dyna,parm1,parm2,parm3,parm4);
     }
     else {
          prfmsg(msg,parm1,parm2,parm3,parm4);
     }
}

VOID
pfor2w(                            /* sends out a prf or rsp2wri           */
INT msg,
CHAR *dyna,
CHAR *parm1,
CHAR *parm2,
CHAR *parm3,
CHAR *parm4)
{
     if (usrptr->flags&ISGCSU) {
          strcpy(vdatmp,dyna);
          if (parm1 != NULL) {
               strcat(vdatmp,"\t");
               strcat(vdatmp,parm1);
               if (parm2 != NULL) {
                    strcat(vdatmp,"\t");
                    strcat(vdatmp,parm2);
                    if (parm3 != NULL) {
                         strcat(vdatmp,"\t");
                         strcat(vdatmp,parm3);
                         if (parm4 != NULL) {
                              strcat(vdatmp,"\t");
                              strcat(vdatmp,parm4);
                         }
                    }
               }
          }
          rsp2write(TRUE,STGLEN,vdatmp,NULL);
     }
     else {
          prfmsg(msg,parm1,parm2,parm3,parm4);
     }
}

VOID
condis(VOID)                       /* conditional ircsnd()/circsnd()       */
{
     if (usrptr->flags&ISGCSU) {
          circsnd();
     }
     else {
          ircsnd();
     }
}

VOID
ifcs2w(VOID)                       /* if client/server user, rsp2wri()     */
{
     if (usrptr->flags&ISGCSU) {
          rsp2write(TRUE,0,NULL,NULL);  // cbklen() of 0 indicates no resp
     }
}

static VOID
aboirc(VOID)                       /* abort IRC reads & writes             */
{
     if (ircoff(usrnum)) {
          ircptr->curchan[0]='\0';
          ircptr->flags&=~IRCACTIVE;
          ircptr->chan=IRCVCT;
          ircptr->psubstt=ENTRNCE;
          if (ircptr->sockt != -1) {
               clsskt(ircptr->sockt);
               ircptr->sockt=-1;
          }
     }
     dnsabt();
     usrptr->flags&=~NOGLOB;
}

static VOID
nulirc(VOID)
{
}
