/***************************************************************************
 *                                                                         *
 *   TELNET.C                                                              *
 *                                                                         *
 *   Copyright (c) 1994-1997 Galacticomm, Inc.    All Rights Reserved.     *
 *                                                                         *
 *   Telnet client for Worldgroup.                                         *
 *                                                                         *
 *                                        - RNStein  6/12/94               *
 *                                                                         *
 ***************************************************************************/

#include "gcomm.h"
#include "majorbbs.h"
#include "fsd.h"
#include "tcpip.h"
#include "tno.h"
#include "dns.h"
#include "telnet.h"
#include "ftscope.h"
#include "telnetd.h"
#include "galtnt.h"

#define FILREV "$Revision: 26 $"

static INT globaltnt(VOID);
static GBOOL tncinp(VOID);
static VOID hdlhst(VOID);
static VOID tnccbk(struct dns *dnsptr);
static INT tnccall(VOID);
static VOID bgncon(struct tncinf *tnc);
static VOID abtcon(VOID);
static VOID dectnt(VOID);
static VOID endcon(INT msgnum,...);
static VOID fincon(VOID);
static VOID tncsts(VOID);
static VOID tnchup(VOID);
static INT tncsbf(CHAR *bytes,INT nbytes);
static INT tncops(INT code,INT parm);
static VOID sndop3(CHAR c1,CHAR c2,CHAR c3);
static INT mrgpar(CHAR *parm);
static CHAR *mrgpfx(CHAR *prfx);

INT tncstt;                        /* Telnet client module state number    */
struct module tncmodule={          /* module interface block               */
     "",                           /*    name used to refer to this module */
     NULL,                         /*    user logon supplemental routine   */
     tncinp,                       /*    input routine if selected         */
     tncsts,                       /*    status-input routine if selected  */
     NULL,                         /*    "injoth" routine for this module  */
     NULL,                         /*    user logoff supplemental routine  */
     tnchup,                       /*    hangup (lost carrier) routine     */
     NULL,                         /*    midnight cleanup routine          */
     NULL,                         /*    delete-account routine            */
     NULL,                         /*    finish-up (sys shutdown) routine  */
};

#define TNCWAIT -1                 /* substate wait for output to finish   */
#define TNCEXIT -2                 /* substate to trigger Telnet exit      */

#define DSCTMO (5*16)              /* wait up to 5 sec for output to finish*/

static INT opovfl;                 /* overflows sending Telnet ops         */
static CHAR irupt;                 /* <IAC IP> received from server        */
static VOID (*olddec)(VOID);       /* for recording (*decusr)() vector     */
static INT rwconn;                 /* receive window for Telnet connection */
static INT rbempt;                 /* receive buffer empty (btuoba() val)  */
static INT tnthys;                 /* hysteresis for rcv window growth     */

HMCVFILE tncmb;                    /* --- options in GALTNT.MCV ---------- */
CHAR tntgo;                        /* can users type "/TELNET" command?    */
CHAR *portkey;                     /* key required to specify the port     */
INT contwt;                        /* dialing out patience, 1/16 sec units */
CHAR *abtkey;                      /* key required to use abort string     */
CHAR *abtstg;                      /* string to abort non-ASCII connection */
CHAR *bnckey;                      /* key required to prevent bouncing     */
INT abtsec;                        /* pause b4 abort stg (1/16 sec units)  */
CHAR *xitstg;                      /* string to abort ASCII connection     */
INT maxtnt;                        /* max simultaneous outgoing telnets    */
CHAR audcon;                       /* audit telnet connects                */
CHAR auddsc;                       /* audit telnet disconnects             */
INT tntchg;                        /* telnet client surcharge creds/min    */
LONG tntkchg;                      /* charge / 1K bytes traffic via Telnet */
static CHAR **endmsgs;             /* end connection messages              */

VOID EXPORT
init__galtnt(VOID)                 /* Telnet client initialization         */
{
     init__tcpip();
     init__galtntd();
     stzcpy(tncmodule.descrp,gmdnam("galtnt.mdf"),MNMSIZ);
     tncstt=register_module(&tncmodule);
     tncmb=opnmsg("galtnt.mcv");
     endmsgs=(CHAR **)alczer(nterms*sizeof(CHAR *));
     maxtnt=numopt(MAXTNT,0,250);
     tntgo=ynopt(TNTGO);
     tntchg=numopt(TNTCHG,-32767,32767);
     tntkchg=lngopt(TNTKCHG,-2000000000L,2000000000L);
     portkey=stgopt(PORTKEY);
     contwt=numopt(CONTWT,-1,2047);
     if (contwt != -1) {
          contwt*=16;
     }
     abtkey=stgopt(ABTKEY);
     abtstg=stgopt(ABTSTG);
     bnckey=stgopt(BNCKEY);
     abtsec=numopt(ABTSEC,-1,2047);
     if (abtsec != -1) {
          abtsec*=16;
     }
     xitstg=stgopt(XITSTG);
     audcon=ynopt(AUDCON);
     auddsc=ynopt(AUDDSC);
     olddec=decusr;
     decusr=dectnt;
     globalcmd(globaltnt);
     dclvda(sizeof(struct tncinf));
     rwconn=OUTSIZ/2-1;
     rbempt=OUTSIZ/2-1;
     tnthys=OUTSIZ/4;
}

VOID EXPORT
initwc__galtnt(VOID)
{
     init__galtnt();
}

static INT
globaltnt(VOID)                    /* global "/telnet <host>" command      */
{
     CHAR *cp;
     INT rc;

     if (margc >= 1 && sameas(margv[0],"/telnet") && tntgo) {
          if (usrptr->state == tncstt
           && usrptr->substt == CONNED
           && !sameas(xitstg,"X")) {
               setmbk(tncmb);
               prfmsg(TNTCONFL,xitstg);
               outprf(usrnum);
               rstmbk();
               return(1);
          }
          cp=margv[0];
          rstrin();
          movmem(cp,cp+3,(UINT)(INPSIZ-(cp-input)-3));
          input[INPSIZ-1]='\0';
          movmem("/go ",cp,4);
          parsin();
          rc=globalgo();
          if (usrptr->state != tncstt && usrptr->flags&MASTER) {
               setmbk(tncmb);
               prfmsg(TNTNOPAG);
               outprf(usrnum);
               rstmbk();
          }
          return(rc);
     }
     return(0);
}

static GBOOL
tncinp(VOID)                       /* Telnet client user input routine     */
{
     GBOOL rc=TRUE;
     CHAR *cp;

     setmbk(tncmb);
     if (margc == 1 && sameas(margv[0],"X") && usrptr->substt != CONNED) {
          dnsabt();
          abtcon();
          return(0);
     }
     do {
          bgncnc();
          switch (usrptr->substt) {
          case 0:
               cncchr();
               if (endmsgs[usrnum] != NULL) {
                    free(endmsgs[usrnum]);
                    endmsgs[usrnum]=NULL;
               }
               setmem(tncptr,sizeof(struct tncinf),0);
               tncptr->socket=-1;
               if (tntinf != NULL  /* in case TNTONL and TNTREJ are NO */
                && (tntinf[usrnum].flags&TNTCHN) != 0
                && !haskey(bnckey)) {
                    prfmsg(CANTBNC);
                    rc=FALSE;
               }
               else if (numonl(tncstt) > maxtnt) {
                    prfmsg(maxtnt == 0 ? TNTZMANY : TNTMANY,maxtnt);
                    rc=FALSE;
               }
               else {
                    prfmsg(usrptr->substt=TNTHOST);
               }
               break;
          case TNTHOST:
               hdlhst();
               cncall();
               break;
          case CONNED:
               if (sameas(cp=cncall(),xitstg)) {
                    abtcon();
                    fincon();
                    rc=FALSE;
               }
               else {
                    if (usrptr->flags&INJOIP) {
                         tncptr->flags|=TFLINJ;
                    }
                    tncsbf(cp,strlen(cp));
                    tncsbf("\r\n",2);
                    tncsnd(tncptr);
               }
               break;
          case LOOKING:
          case CONNING:
               cncall();
               if ((usrptr->flags&(INJOIP+ABOIP)) == INJOIP) {
                    break;
               }
               dnsabt();
               abtcon();
          case TNCEXIT:
               cncall();
               rc=FALSE;
               break;
          }
     } while (!endcnc());
     outprf(usrnum);
     return(rc);
}

static VOID
hdlhst(VOID)                       /* handle host name & other options     */
{
     INT canport;
     CHAR *parm;

     parsin();
     tncptr->flags=0;
     tncptr->bntmax=0;
     if (mrgpar("-a") || mrgpar("/a")) {
          tncptr->flags|=TFLASC;
     }
     if (mrgpar("-bs") || mrgpar("/bs")) {
          tncptr->flags|=TFLBKS;
     }
     if (mrgpar("-rk") || mrgpar("/rk")) {
          tncptr->flags|=TFLKYR;
     }
     if (mrgpar("-rs") || mrgpar("/rs")) {
          tncptr->flags|=TFLSCR;
     }
     if (mrgpar("-nb") || mrgpar("/nb")) {
          tncptr->flags|=TFLNOB;
     }
     if (((parm=mrgpfx("-b")) != NULL
       || (parm=mrgpfx("/b")) != NULL) && alldgs(parm)) {
          tncptr->bntmax=atoi(parm);
     }
     canport=haskey(portkey);
     stzcpy(tncptr->dns.name,margv[0],DNSNSZ);
     if (margc == 0 || sameas(margv[0],"?")) {
          prfmsg(canport ? TNTHLPP1 : TNTHLP1);
          dnshelp();
          prfmsg(canport ? TNTHLPP4 : TNTHLP4);
          outprf(usrnum);
          condex();
          prfmsg(TNTSHOST);
     }
     else if (margc > 1 && !canport) {
          rstrin();
          prfmsg(CANTPRT,input);
          outprf(usrnum);
          condex();
          prfmsg(TNTHOST);
     }
     else if (margc > 2 || margc == 2 && !alldgs(margv[1])) {
          rstrin();
          prfmsg(BADPRT,margv[1]);
          prfmsg(TNTHLPP4);
          outprf(usrnum);
          condex();
          prfmsg(TNTSHOST);
     }
     else {
          tncptr->port=margc == 2 ? atoi(margv[1]) : TNTPORT;
          tncptr->dns.numaddr=1;
          tncptr->dns.callbk=tnccbk;
          if (!dnsn2a(&tncptr->dns)) {
               prfmsg(usrptr->substt=LOOKING,margv[0]);
          }
     }
}

static VOID
tnccbk(                            /* Telnet address lookup callback vector*/
struct dns *dnsptr)
{
     INT goahead;

     setmbk(tncmb);
     if (dnsptr->status >= 0) {
          prfmsg(CONNING,inet_ntoa(dnsptr->inaddr[0]));
          goahead=tnccall();
     }
     else {
          prfmsg(LUPERR,dnsemg);
          goahead=0;
     }
     outprf(usrnum);
     clrprf();
     if (!goahead) {
          usrptr->substt=TNCEXIT;
          btuinj(usrnum,CRSTG);
     }
}

static INT
tnccall(VOID)                      /* try to connect to Telnet server      */
                                   /* returns 1=working 0=problem reported */
                                   /* tncptr->dns.inaddr[0], tncptr->port, */
{                                  /* implicit inputs, expects curusr() too*/
     INT rc;

     switch (rc=tcpdial(tncptr->dns.inaddr[0],htons(tncptr->port),0,
                        &tncptr->socket)) {
     case DLERRS:
          prfmsg(SOCKERR,tcpip_errno,tcpErrStg(tcpip_errno));
          break;
     case DLERRI:
          prfmsg(IOCLERR,tcpip_errno,tcpErrStg(tcpip_errno));
          break;
     default:
          prfmsg(CONNERR,rc,tcpip_errno,tcpErrStg(tcpip_errno));
          break;
     case DLCNOW:
     case DLCING:
          setrcvwin(tncptr->socket,tncptr->rcvwin=rwconn);
          sktnfy(TNFCONN,tncptr->socket,bgncon,tncptr,usrnum);
          usrptr->substt=CONNING;
          btuinj(usrnum,CYCLE);
          tncptr->sttime=(USHORT)(hrtval()>>12);
          tncptr->tckonl=lngtck;
          tncptr->byttfc=0L;
          return(1);
     }
     tncptr->socket=-1;
     return(0);
}

static VOID
bgncon(                            /* Connection has begun                 */
struct tncinf *tnc)                /* Telnet client info structure (vda)   */
{                                  /* expects curusr() to be in effect     */
     CHAR onechar;

     if (btuoba(usrnum) != OUTSIZ-1) {
          return;
     }
     tnc->tckonl=lngtck;
     tnc->byttfc=0L;
     if (recv(tnc->socket,&onechar,0,0) < 0 && tcpip_errno != EWOULDBLOCK) {
          endcon(IMMERR,tnc->dns.name,tcpErrStg(tcpip_errno));
          return;
     }
     if (audcon) {
          shocst("TELNET CLIENT CONNECT","to %s",tnc->dns.name);
     }
     oobinline(tnc->socket);
     btubsz(usrnum,OUTSIZ/2,OUTSIZ/2);
     btuhwh(usrnum,OUTSIZ/2-40);
     setmbk(tncmb);
     prfasc(usrptr->substt=CONNED,tnc->dns.name);
     if (tncptr->flags&TFLASC) {
          if (xitstg[0] != '\0') {
               prfasc(DISCAHLP,xitstg);
          }
     }
     else {
          usrptr->flags|=NOINJO;
          btutrg(usrnum,OUTSIZ/2);
          btuech(usrnum,0);
          if (abtsec != -1 && haskey(abtkey)) {
               prfasc(DISCHELP,abtsec/16,abtstg);
          }
     }
     if (!(usaptr->ansifl&ANSON)) {
          stpans(prfbuf);
     }
     powprf();
     rstmbk();
     sktnfy(TNFRECV,tnc->socket,tncrcv,tnc,usrnum);
     sktcnc(TNFCONN,tnc->socket);
     tnoscb=&tnc->tnoscb;
     tnoini(TNCTSIZ,tncops);
     tnc->bntccnt=0;
     tnc->bntscnt=0;
     if (!(tnc->flags&TFLASC)) {
          sndop3(IAC,DO,TNTOPT_SGA);
          sndop3(IAC,DO,TNTOPT_ECHO);
          tnoscb->flags|=TNCECH;
          if (!(tnc->flags&TFLNOB)) {
               sndop3(IAC,DO,TNTOPT_BINARY);
               sndop3(IAC,WILL,TNTOPT_BINARY);
               tnc->bntccnt++;
               tnc->bntscnt++;
               tnoscb->flags|=TNCSBN+TNCCBN;
          }
          else {
               tnc->bntmax=0;
          }
          tncsnd(tnc);
     }
     else {
          tnc->bntmax=0;
     }
}

static VOID
abtcon(VOID)                       /* Abort connection to the server       */
{
     if (tncptr->socket != -1) {
          tfcchg(tncptr->byttfc,tntkchg);
          if (auddsc) {
               shocst("TELNET CLIENT DISCONNECT",
                      "from %s, %s seconds, %s bytes traffic",
                      inet_ntoa(tncptr->dns.inaddr[0]),
                      ul2as(lngtck-tncptr->tckonl),
                      ul2as(tncptr->byttfc));
          }
          clsskt(tncptr->socket);
          tncptr->socket=-1;
     }
}

static VOID
dectnt(VOID)                       /* Telnet client connect time accting   */
{
     if (decusp->usrcls == ACTUSR && decusp->state == tncstt) {
          deccst+=(tntchg>>2)+((decusp->minut4&3) < (tntchg&3));
     }
     (*olddec)();
}

static VOID
endcon(                            /* End of connection                    */
INT msgnum,                        /*   notice to user                     */
...)                               /*   parameters                         */
{                                  /* curusr() expected to be in effect    */
     va_list args;
     CHAR *stg;

     abtcon();
     tncptr->bufcnt=0;
     setmbk(tncmb);
     stg=getmsg(msgnum);
     rstmbk();
     va_start(args,msgnum);
     vsprintf(vdatmp,stg,args);
     va_end(args);
     endmsgs[usrnum]=alcdup(vdatmp);
     usrptr->substt=TNCWAIT;
     tncptr->sttime=(USHORT)(hrtval()>>12);
}

static VOID
fincon(VOID)                       /* Final restoration of channel         */
{                                  /* curusr() expected to be in effect    */
     btubsz(usrnum,INPSIZ,OUTSIZ);
     btuhwh(usrnum,OUTSIZ-40);
     btutrg(usrnum,0);
     echon();
     usrptr->flags&=~NOINJO;
}

static VOID
tncsts(VOID)                       /* Telnet client status handler routine */
{
     INT nroom;
     INT nactual;
     CHAR *newbytes;
     USHORT now;
     INT timout;
     INT i;
     INT abt=0;
     INT more=1;

     if (status != CYCLE) {
          dfsthn();
          return;
     }
     now=(USHORT)(hrtval()>>12);
     switch(usrptr->substt) {
     case CONNING:
          if (contwt != -1 && now-tncptr->sttime > contwt) {
               endcon(CONTMO,contwt/16);
          }
          break;
     case CONNED:
          if (!(tncptr->flags&TFLASC)
           && (nroom=TNCBSIZ/2-tncptr->bufcnt) > 0
           && (nactual=btuica(usrnum,newbytes=tncptr->buffer+
                                              tncptr->bufcnt,nroom)) > 0) {
               timout=(abtsec != -1 && now-tncptr->abtclk >= abtsec);
               if (timout || tncptr->abtcnt > 0) {
                    if (timout) {
                         tncptr->abtcnt=0;
                    }
                    for (i=0 ; i < nactual ; i++) {
                         if (newbytes[i] == abtstg[tncptr->abtcnt]) {
                              if (++tncptr->abtcnt == strlen(abtstg)) {
                                   abt=haskey(abtkey);
                                   break;
                              }
                         }
                         else {
                              tncptr->abtcnt=0;
                              break;
                         }
                    }
               }
               tncptr->abtclk=now;
               if (tncptr->flags&TFLKYR) {
                    while ((i=scanch(newbytes,'\r',nactual)) != nactual) {
                         newbytes[i]='\n';
                    }
               }
               if (tncptr->flags&TFLBKS) {
                    while ((i=scanch(newbytes,'\b',nactual)) != nactual) {
                         newbytes[i]=0x7F;
                    }
               }
               tncptr->bufcnt+=iacenc(newbytes,nactual,TNCBSIZ-tncptr->bufcnt);
               tncsnd(tncptr);
               if (abt) {
                    endcon(ABORTED,abtstg);
               }
          }
          else {
               actdet=0;
          }
          if (tncptr->socket != -1) {
               rwnadj(tncptr->socket,
                      rwconn,btuoba(usrnum),tnthys,&tncptr->rcvwin);
          }
          break;
     case TNCWAIT:
          if (btuoba(usrnum) >= rbempt || now-tncptr->sttime > DSCTMO) {
               fincon();
               if (endmsgs[usrnum] != NULL) {
                    prf("%s",endmsgs[usrnum]);
                    free(endmsgs[usrnum]);
                    endmsgs[usrnum]=NULL;
               }
               else {
                    prf("");
               }
               outprf(usrnum);
               usrptr->substt=TNCEXIT;
               btuinj(usrnum,CRSTG);
               more=0;
          }
          break;
     default:
          more=0;
          break;
     }
     if (more) {
          btuinj(usrnum,CYCLE);
     }
}

static VOID
tnchup(VOID)                       /* Telnet client user hangup routine    */
{
     if (usrptr->state == tncstt) {
          abtcon();
     }
     if (endmsgs[usrnum] != NULL) {
          free(endmsgs[usrnum]);
          endmsgs[usrnum]=NULL;
     }
}

VOID
tncrcv(                            /* Telnet client receive-from-server    */
struct tncinf *tnc)                /* user's telnet client info            */
{
     INT nroom,nactual;
     INT crbfor;
     INT i;

     nroom=btuoba(usrnum);
     if (tnc->flags&TFLINJ) {
          if (nroom < rbempt) {    /* local reprompt must be non-binary    */
               actdet=0;
               return;
          }
          tnc->flags&=~TFLINJ;
     }
     if (nroom > vdasiz) {
          nroom=vdasiz;
     }
     rwnmgr(tnc->socket,nroom,&tnc->rcvwin);
     if (nroom == 0) {
          actdet=0;
          return;
     }
     if ((nactual=recv(tnc->socket,vdatmp,nroom,0)) < 0) {
          if (tcpip_errno == ECONNRESET) {
               endcon(SVRDISC);
          }
          else {
               endcon(RCVERR,tcpip_errno,tcpErrStg(tcpip_errno));
          }
     }
     else if (nactual == 0) {
          endcon(SVREND);
     }
     else {
          tncptr->byttfc+=nactual;
          irupt=0;
          opovfl=0;
          tnoscb=&tnc->tnoscb;
          tntunm=usrnum;
          nactual=iacdec(vdatmp,nactual);
          if (irupt) {
               endcon(IRUPT);
               return;
          }
          if (tncptr->flags&TFLSCR) {
               for (i=0 ; (i+=scanch(vdatmp+i,'\n',nactual-i)) < nactual ; i++) {
                    if (i > 0) {
                         crbfor=vdatmp[i-1] == '\r';
                    }
                    else {
                         crbfor=(tncptr->flags&TFLCRB) != 0;
                    }
                    if (!crbfor && nactual < vdasiz) {
                         movmem(vdatmp+i,vdatmp+i+1,nactual-i);
                         vdatmp[i]='\r';
                         nactual++;
                         i++;
                    }
               }
               if (nactual > 0) {
                    if (vdatmp[nactual-1] == '\r') {
                         tncptr->flags|=TFLCRB;
                    }
                    else {
                         tncptr->flags&=~TFLCRB;
                    }
               }
          }
          btuxct(usrnum,nactual,vdatmp);
          if (opovfl > 0) {
               setmbk(tncmb);
               prfasc(OPOVFL,opovfl);
               if (!(usaptr->ansifl&ANSON)) {
                    stpans(prfbuf);
               }
               powprf();
               rstmbk();
          }
          if (tnc->bufcnt > 0) {
               tncsnd(tnc);
          }
     }
}

static INT
tncsbf(                            /* put bytes into the send buffer       */
CHAR *bytes,
INT nbytes)
{
     INT nroom;
     INT nbuf;

     nroom=TNCBSIZ-tncptr->bufcnt;
     nbuf=min(nroom,nbytes);
     if (nbuf > 0) {
          movmem(bytes,tncptr->buffer+tncptr->bufcnt,nbuf);
          tncptr->bufcnt+=nbuf;
     }
     else {
          nbuf=0;
     }
     return(nbuf);
}

VOID
tncsnd(                            /* try to send data in buffer to server */
struct tncinf *tnc)                /* Telnet client session info           */
{
     if (tnfclosed) {
          endcon(SVREND);
          return;
     }
     switch (sndmgr(tnc->buffer,&tnc->bufcnt,tnc->socket)) {
     case -1:
          endcon(SNDERR,tcpip_errno,tcpErrStg(tcpip_errno));
          break;
     case 0:
          sktcnc(TNFSEND,tnc->socket);
          break;
     case 1:
          sktnfy(TNFSEND,tnc->socket,tncsnd,tnc,usrnum);
          break;
     }
     tncptr->byttfc+=sndact;
}

static INT
tncops(                            /* Telnet client option handler         */
INT code,                          /* see TNCXXXX codes in TNO.H           */
INT parm)                          /* see TNCXXXX codes in TNO.H           */
{                                  /* usrnum & tncptr implicit inputs      */
     INT rc=-1;
     static CHAR synch1[]={IAC,IP,IAC};
     static CHAR synch2[]={DM};
     CHAR *cp;
     INT n1,n2;
     SHORT contra;

     ftsops(code,parm);
     switch (code) {
     case TNCWIWO:                 /* WILL (parm=1) or WONT (parm=0) recd  */
          switch (tnoscb->opid) {
          case TNTOPT_SGA:
               if (!parm && tncptr->port == TNTPORT) {
                    shocst("TELNET CLIENT - NON-SGA SERVER",
                           "Server at %s does not support SGA",
                           cp=inet_ntoa(tncptr->dns.inaddr[0]));
                    setmbk(tncmb);
                    prfmsg(NOSGA,cp);
                    outprf(usrnum);
                    rstmbk();
               }
               break;
          case TNTOPT_ECHO:
               if (tncptr->flags&TFLASC) {
                    tnono(TNCECH);
               }
               else {
                    tnoyes(TNCECH);
               }
               break;
          case TNTOPT_BINARY:
               if (tncptr->flags&TFLASC) {
                    tnono(TNCSBN);
               }
               else {
                    contra=(tnoscb->flags&TNCSBN) != 0
                        && !parm
                        && tncptr->bntscnt < tncptr->bntmax;
                    tnoyes(TNCSBN);
                    if (contra) {
                         tncops(TNCSEND,DO);
                         tnoscb->flags|=TNCSBN;
                         tncptr->bntscnt++;
                    }
               }
               break;
          default:
               tnono(0);
               break;
          }
          break;
     case TNCDODO:                 /* DO (parm=1) or DONT (parm=0) recd    */
          switch (tnoscb->opid) {
          case TNTOPT_SGA:
               tnoyes(TNCCSG);
               break;
          case TNTOPT_BINARY:
               if (tncptr->flags&TFLASC) {
                    tnono(TNCCBN);
               }
               else {
                    contra=(tnoscb->flags&TNCCBN) != 0
                        && !parm
                        && tncptr->bntccnt < tncptr->bntmax;
                    tnoyes(TNCCBN);
                    if (contra) {
                         tncops(TNCSEND,WILL);
                         tnoscb->flags|=TNCCBN;
                         tncptr->bntccnt++;
                    }
               }
               break;
          default:
               tnono(0);
               break;
          }
          break;
     case TNCOTHR:                 /* IAC <other> recd (parm=<other>)      */
          switch (parm) {
          case AYT:
               setmbk(tncmb);
               cp=stpans(getasc(AYTREPL));
               sndopc(cp,iacenc(cp,strlen(cp),mxmssz));
               rstmbk();
               break;
          case AO:
               btucli(usrnum);
               n2=0;
               if ((n1=send(tncptr->socket,synch1,sizeof(synch1),MSG_OOB)) !=
                                                  sizeof(synch1)
                || (n2=send(tncptr->socket,synch2,sizeof(synch2),0)) !=
                                                  sizeof(synch2)) {

                    opovfl++;
               }
               if (whomon == usrnum) {
                    ftscope(FTSCMT,"XOOB  <IAC IP> <IAC DM>");
               }
               tncptr->byttfc+=n1+n2;
               break;
          case IP:
               irupt=1;
               break;
          }
          break;
     case TNCSEND:
          sndop3(IAC,parm,tnoscb->opid);
          break;
     }
     return(rc);
}

static VOID
sndop3(                            /* send a 3-character option string     */
CHAR c1,                           /* 3 characters                         */
CHAR c2,                           /* (tncptr (vdaptr) is implicit input)  */
CHAR c3)                           /* (opovfl may be incremented)          */
{
     CHAR buffer[3];

     buffer[0]=c1;
     buffer[1]=c2;
     buffer[2]=c3;
     if (!sndopc(buffer,3)) {
          opovfl++;
     }
     if (whomon == usrnum) {
          ftscope(FTSCMT,spr("XTNO  %s %s",cmdname(c2),optname(c3)));
     }
}

INT
sndopc(                            /* Telnet client sends an option string */
CHAR *bytes,                       /* binary string of bytes (may incl \0) */
INT nbytes)                        /* number of bytes                      */
{                                  /* returns 0=no room, 1=ok              */
                                   /* (tncptr (vdaptr) is implicit input)  */
     if (tncptr->bufcnt+nbytes > TNCBSIZ) {
          return(0);
     }
     if (nbytes > 0) {
          movmem(bytes,tncptr->buffer+tncptr->bufcnt,nbytes);
          tncptr->bufcnt+=nbytes;
     }
     return(1);
}

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

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

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

/*--- The following routine may one day be moved to more general  ---*/
/*--- purpose location, such as PHGCOMM.LIB.  (See also SCP.C.)   ---*/
/*--- The DLL created by this source will work after that point,  ---*/
/*--- but this routine might need to be removed from the source   ---*/
/*--- before it can be compiled again.                            ---*/

static CHAR *                      /*   returns parm after pfix (max 20 ch)*/
mrgpfx(                            /* detects and removes margv[] prefix   */
CHAR *prfx)                        /*   prefix, e.g. "-b", case ignored    */
{                                  /*   expects parsin() to be in effect   */
                                   /*   caution: return value short lived  */
     INT i;
     static CHAR parm[20];

     for (i=0 ; i < margc ; i++) {
          if (sameto(prfx,margv[i])) {
               stlcpy(parm,margv[i]+strlen(prfx),sizeof(parm));
               rstrin();
               if (i+1 < margc) {
                    strcpy(margv[i],margv[i+1]);
               }
               else {
                    margv[i][0]='\0';
               }
               parsin();
               return(parm);
          }
     }
     return(NULL);
}
