telnet.c revision 57416
157416Smarkm/*
257416Smarkm * Copyright (c) 1988, 1990, 1993
357416Smarkm *	The Regents of the University of California.  All rights reserved.
457416Smarkm *
557416Smarkm * Redistribution and use in source and binary forms, with or without
657416Smarkm * modification, are permitted provided that the following conditions
757416Smarkm * are met:
857416Smarkm * 1. Redistributions of source code must retain the above copyright
957416Smarkm *    notice, this list of conditions and the following disclaimer.
1057416Smarkm * 2. Redistributions in binary form must reproduce the above copyright
1157416Smarkm *    notice, this list of conditions and the following disclaimer in the
1257416Smarkm *    documentation and/or other materials provided with the distribution.
1357416Smarkm * 3. All advertising materials mentioning features or use of this software
1457416Smarkm *    must display the following acknowledgement:
1557416Smarkm *	This product includes software developed by the University of
1657416Smarkm *	California, Berkeley and its contributors.
1757416Smarkm * 4. Neither the name of the University nor the names of its contributors
1857416Smarkm *    may be used to endorse or promote products derived from this software
1957416Smarkm *    without specific prior written permission.
2057416Smarkm *
2157416Smarkm * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2257416Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2357416Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2457416Smarkm * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2557416Smarkm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2657416Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2757416Smarkm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2857416Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2957416Smarkm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3057416Smarkm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3157416Smarkm * SUCH DAMAGE.
3257416Smarkm */
3357416Smarkm
3457416Smarkm#include "telnet_locl.h"
3557416Smarkm#ifdef HAVE_TERMCAP_H
3657416Smarkm#include <termcap.h>
3757416Smarkm#endif
3857416Smarkm
3957416SmarkmRCSID("$Id: telnet.c,v 1.27 2000/01/01 11:53:24 assar Exp $");
4057416Smarkm
4157416Smarkm#define	strip(x) (eight ? (x) : ((x) & 0x7f))
4257416Smarkm
4357416Smarkmstatic unsigned char	subbuffer[SUBBUFSIZE],
4457416Smarkm			*subpointer, *subend;	 /* buffer for sub-options */
4557416Smarkm#define	SB_CLEAR()	subpointer = subbuffer;
4657416Smarkm#define	SB_TERM()	{ subend = subpointer; SB_CLEAR(); }
4757416Smarkm#define	SB_ACCUM(c)	if (subpointer < (subbuffer+sizeof subbuffer)) { \
4857416Smarkm				*subpointer++ = (c); \
4957416Smarkm			}
5057416Smarkm
5157416Smarkm#define	SB_GET()	((*subpointer++)&0xff)
5257416Smarkm#define	SB_PEEK()	((*subpointer)&0xff)
5357416Smarkm#define	SB_EOF()	(subpointer >= subend)
5457416Smarkm#define	SB_LEN()	(subend - subpointer)
5557416Smarkm
5657416Smarkmchar	options[256];		/* The combined options */
5757416Smarkmchar	do_dont_resp[256];
5857416Smarkmchar	will_wont_resp[256];
5957416Smarkm
6057416Smarkmint
6157416Smarkm	eight = 3,
6257416Smarkm	binary = 0,
6357416Smarkm	autologin = 0,	/* Autologin anyone? */
6457416Smarkm	skiprc = 0,
6557416Smarkm	connected,
6657416Smarkm	showoptions,
6757416Smarkm	ISend,		/* trying to send network data in */
6857416Smarkm	debug = 0,
6957416Smarkm	crmod,
7057416Smarkm	netdata,	/* Print out network data flow */
7157416Smarkm	crlf,		/* Should '\r' be mapped to <CR><LF> (or <CR><NUL>)? */
7257416Smarkm	telnetport,
7357416Smarkm	SYNCHing,	/* we are in TELNET SYNCH mode */
7457416Smarkm	flushout,	/* flush output */
7557416Smarkm	autoflush = 0,	/* flush output when interrupting? */
7657416Smarkm	autosynch,	/* send interrupt characters with SYNCH? */
7757416Smarkm	localflow,	/* we handle flow control locally */
7857416Smarkm	restartany,	/* if flow control enabled, restart on any character */
7957416Smarkm	localchars,	/* we recognize interrupt/quit */
8057416Smarkm	donelclchars,	/* the user has set "localchars" */
8157416Smarkm	donebinarytoggle,	/* the user has put us in binary */
8257416Smarkm	dontlecho,	/* do we suppress local echoing right now? */
8357416Smarkm	globalmode;
8457416Smarkm
8557416Smarkmchar *prompt = 0;
8657416Smarkm
8757416Smarkmcc_t escape;
8857416Smarkmcc_t rlogin;
8957416Smarkm#ifdef	KLUDGELINEMODE
9057416Smarkmcc_t echoc;
9157416Smarkm#endif
9257416Smarkm
9357416Smarkm/*
9457416Smarkm * Telnet receiver states for fsm
9557416Smarkm */
9657416Smarkm#define	TS_DATA		0
9757416Smarkm#define	TS_IAC		1
9857416Smarkm#define	TS_WILL		2
9957416Smarkm#define	TS_WONT		3
10057416Smarkm#define	TS_DO		4
10157416Smarkm#define	TS_DONT		5
10257416Smarkm#define	TS_CR		6
10357416Smarkm#define	TS_SB		7		/* sub-option collection */
10457416Smarkm#define	TS_SE		8		/* looking for sub-option end */
10557416Smarkm
10657416Smarkmstatic int	telrcv_state;
10757416Smarkm#ifdef	OLD_ENVIRON
10857416Smarkmunsigned char telopt_environ = TELOPT_NEW_ENVIRON;
10957416Smarkm#else
11057416Smarkm# define telopt_environ TELOPT_NEW_ENVIRON
11157416Smarkm#endif
11257416Smarkm
11357416Smarkmjmp_buf	toplevel;
11457416Smarkmjmp_buf	peerdied;
11557416Smarkm
11657416Smarkmint	flushline;
11757416Smarkmint	linemode;
11857416Smarkm
11957416Smarkm#ifdef	KLUDGELINEMODE
12057416Smarkmint	kludgelinemode = 1;
12157416Smarkm#endif
12257416Smarkm
12357416Smarkm/*
12457416Smarkm * The following are some clocks used to decide how to interpret
12557416Smarkm * the relationship between various variables.
12657416Smarkm */
12757416Smarkm
12857416SmarkmClocks clocks;
12957416Smarkm
13057416Smarkmstatic int is_unique(char *name, char **as, char **ae);
13157416Smarkm
13257416Smarkm
13357416Smarkm/*
13457416Smarkm * Initialize telnet environment.
13557416Smarkm */
13657416Smarkm
13757416Smarkmvoid
13857416Smarkminit_telnet(void)
13957416Smarkm{
14057416Smarkm    env_init();
14157416Smarkm
14257416Smarkm    SB_CLEAR();
14357416Smarkm    memset(options, 0, sizeof options);
14457416Smarkm
14557416Smarkm    connected = ISend = localflow = donebinarytoggle = 0;
14657416Smarkm#if	defined(AUTHENTICATION) || defined(ENCRYPTION)
14757416Smarkm    auth_encrypt_connect(connected);
14857416Smarkm#endif	/* defined(AUTHENTICATION) || defined(ENCRYPTION)  */
14957416Smarkm    restartany = -1;
15057416Smarkm
15157416Smarkm    SYNCHing = 0;
15257416Smarkm
15357416Smarkm    /* Don't change NetTrace */
15457416Smarkm
15557416Smarkm    escape = CONTROL(']');
15657416Smarkm    rlogin = _POSIX_VDISABLE;
15757416Smarkm#ifdef	KLUDGELINEMODE
15857416Smarkm    echoc = CONTROL('E');
15957416Smarkm#endif
16057416Smarkm
16157416Smarkm    flushline = 1;
16257416Smarkm    telrcv_state = TS_DATA;
16357416Smarkm}
16457416Smarkm
16557416Smarkm
16657416Smarkm/*
16757416Smarkm * These routines are in charge of sending option negotiations
16857416Smarkm * to the other side.
16957416Smarkm *
17057416Smarkm * The basic idea is that we send the negotiation if either side
17157416Smarkm * is in disagreement as to what the current state should be.
17257416Smarkm */
17357416Smarkm
17457416Smarkmvoid
17557416Smarkmsend_do(int c, int init)
17657416Smarkm{
17757416Smarkm    if (init) {
17857416Smarkm	if (((do_dont_resp[c] == 0) && my_state_is_do(c)) ||
17957416Smarkm				my_want_state_is_do(c))
18057416Smarkm	    return;
18157416Smarkm	set_my_want_state_do(c);
18257416Smarkm	do_dont_resp[c]++;
18357416Smarkm    }
18457416Smarkm    NET2ADD(IAC, DO);
18557416Smarkm    NETADD(c);
18657416Smarkm    printoption("SENT", DO, c);
18757416Smarkm}
18857416Smarkm
18957416Smarkmvoid
19057416Smarkmsend_dont(int c, int init)
19157416Smarkm{
19257416Smarkm    if (init) {
19357416Smarkm	if (((do_dont_resp[c] == 0) && my_state_is_dont(c)) ||
19457416Smarkm				my_want_state_is_dont(c))
19557416Smarkm	    return;
19657416Smarkm	set_my_want_state_dont(c);
19757416Smarkm	do_dont_resp[c]++;
19857416Smarkm    }
19957416Smarkm    NET2ADD(IAC, DONT);
20057416Smarkm    NETADD(c);
20157416Smarkm    printoption("SENT", DONT, c);
20257416Smarkm}
20357416Smarkm
20457416Smarkmvoid
20557416Smarkmsend_will(int c, int init)
20657416Smarkm{
20757416Smarkm    if (init) {
20857416Smarkm	if (((will_wont_resp[c] == 0) && my_state_is_will(c)) ||
20957416Smarkm				my_want_state_is_will(c))
21057416Smarkm	    return;
21157416Smarkm	set_my_want_state_will(c);
21257416Smarkm	will_wont_resp[c]++;
21357416Smarkm    }
21457416Smarkm    NET2ADD(IAC, WILL);
21557416Smarkm    NETADD(c);
21657416Smarkm    printoption("SENT", WILL, c);
21757416Smarkm}
21857416Smarkm
21957416Smarkmvoid
22057416Smarkmsend_wont(int c, int init)
22157416Smarkm{
22257416Smarkm    if (init) {
22357416Smarkm	if (((will_wont_resp[c] == 0) && my_state_is_wont(c)) ||
22457416Smarkm				my_want_state_is_wont(c))
22557416Smarkm	    return;
22657416Smarkm	set_my_want_state_wont(c);
22757416Smarkm	will_wont_resp[c]++;
22857416Smarkm    }
22957416Smarkm    NET2ADD(IAC, WONT);
23057416Smarkm    NETADD(c);
23157416Smarkm    printoption("SENT", WONT, c);
23257416Smarkm}
23357416Smarkm
23457416Smarkm
23557416Smarkmvoid
23657416Smarkmwilloption(int option)
23757416Smarkm{
23857416Smarkm	int new_state_ok = 0;
23957416Smarkm
24057416Smarkm	if (do_dont_resp[option]) {
24157416Smarkm	    --do_dont_resp[option];
24257416Smarkm	    if (do_dont_resp[option] && my_state_is_do(option))
24357416Smarkm		--do_dont_resp[option];
24457416Smarkm	}
24557416Smarkm
24657416Smarkm	if ((do_dont_resp[option] == 0) && my_want_state_is_dont(option)) {
24757416Smarkm
24857416Smarkm	    switch (option) {
24957416Smarkm
25057416Smarkm	    case TELOPT_ECHO:
25157416Smarkm	    case TELOPT_BINARY:
25257416Smarkm	    case TELOPT_SGA:
25357416Smarkm		settimer(modenegotiated);
25457416Smarkm		/* FALL THROUGH */
25557416Smarkm	    case TELOPT_STATUS:
25657416Smarkm#if	defined(AUTHENTICATION)
25757416Smarkm	    case TELOPT_AUTHENTICATION:
25857416Smarkm#endif
25957416Smarkm#if	defined(ENCRYPTION)
26057416Smarkm	    case TELOPT_ENCRYPT:
26157416Smarkm#endif
26257416Smarkm		new_state_ok = 1;
26357416Smarkm		break;
26457416Smarkm
26557416Smarkm	    case TELOPT_TM:
26657416Smarkm		if (flushout)
26757416Smarkm		    flushout = 0;
26857416Smarkm		/*
26957416Smarkm		 * Special case for TM.  If we get back a WILL,
27057416Smarkm		 * pretend we got back a WONT.
27157416Smarkm		 */
27257416Smarkm		set_my_want_state_dont(option);
27357416Smarkm		set_my_state_dont(option);
27457416Smarkm		return;			/* Never reply to TM will's/wont's */
27557416Smarkm
27657416Smarkm	    case TELOPT_LINEMODE:
27757416Smarkm	    default:
27857416Smarkm		break;
27957416Smarkm	    }
28057416Smarkm
28157416Smarkm	    if (new_state_ok) {
28257416Smarkm		set_my_want_state_do(option);
28357416Smarkm		send_do(option, 0);
28457416Smarkm		setconnmode(0);		/* possibly set new tty mode */
28557416Smarkm	    } else {
28657416Smarkm		do_dont_resp[option]++;
28757416Smarkm		send_dont(option, 0);
28857416Smarkm	    }
28957416Smarkm	}
29057416Smarkm	set_my_state_do(option);
29157416Smarkm#if	defined(ENCRYPTION)
29257416Smarkm	if (option == TELOPT_ENCRYPT)
29357416Smarkm		encrypt_send_support();
29457416Smarkm#endif
29557416Smarkm}
29657416Smarkm
29757416Smarkmvoid
29857416Smarkmwontoption(int option)
29957416Smarkm{
30057416Smarkm	if (do_dont_resp[option]) {
30157416Smarkm	    --do_dont_resp[option];
30257416Smarkm	    if (do_dont_resp[option] && my_state_is_dont(option))
30357416Smarkm		--do_dont_resp[option];
30457416Smarkm	}
30557416Smarkm
30657416Smarkm	if ((do_dont_resp[option] == 0) && my_want_state_is_do(option)) {
30757416Smarkm
30857416Smarkm	    switch (option) {
30957416Smarkm
31057416Smarkm#ifdef	KLUDGELINEMODE
31157416Smarkm	    case TELOPT_SGA:
31257416Smarkm		if (!kludgelinemode)
31357416Smarkm		    break;
31457416Smarkm		/* FALL THROUGH */
31557416Smarkm#endif
31657416Smarkm	    case TELOPT_ECHO:
31757416Smarkm		settimer(modenegotiated);
31857416Smarkm		break;
31957416Smarkm
32057416Smarkm	    case TELOPT_TM:
32157416Smarkm		if (flushout)
32257416Smarkm		    flushout = 0;
32357416Smarkm		set_my_want_state_dont(option);
32457416Smarkm		set_my_state_dont(option);
32557416Smarkm		return;		/* Never reply to TM will's/wont's */
32657416Smarkm
32757416Smarkm#ifdef ENCRYPTION
32857416Smarkm  	    case TELOPT_ENCRYPT:
32957416Smarkm	      encrypt_not();
33057416Smarkm	      break;
33157416Smarkm#endif
33257416Smarkm	    default:
33357416Smarkm		break;
33457416Smarkm	    }
33557416Smarkm	    set_my_want_state_dont(option);
33657416Smarkm	    if (my_state_is_do(option))
33757416Smarkm		send_dont(option, 0);
33857416Smarkm	    setconnmode(0);			/* Set new tty mode */
33957416Smarkm	} else if (option == TELOPT_TM) {
34057416Smarkm	    /*
34157416Smarkm	     * Special case for TM.
34257416Smarkm	     */
34357416Smarkm	    if (flushout)
34457416Smarkm		flushout = 0;
34557416Smarkm	    set_my_want_state_dont(option);
34657416Smarkm	}
34757416Smarkm	set_my_state_dont(option);
34857416Smarkm}
34957416Smarkm
35057416Smarkmstatic void
35157416Smarkmdooption(int option)
35257416Smarkm{
35357416Smarkm	int new_state_ok = 0;
35457416Smarkm
35557416Smarkm	if (will_wont_resp[option]) {
35657416Smarkm	    --will_wont_resp[option];
35757416Smarkm	    if (will_wont_resp[option] && my_state_is_will(option))
35857416Smarkm		--will_wont_resp[option];
35957416Smarkm	}
36057416Smarkm
36157416Smarkm	if (will_wont_resp[option] == 0) {
36257416Smarkm	  if (my_want_state_is_wont(option)) {
36357416Smarkm
36457416Smarkm	    switch (option) {
36557416Smarkm
36657416Smarkm	    case TELOPT_TM:
36757416Smarkm		/*
36857416Smarkm		 * Special case for TM.  We send a WILL, but pretend
36957416Smarkm		 * we sent WONT.
37057416Smarkm		 */
37157416Smarkm		send_will(option, 0);
37257416Smarkm		set_my_want_state_wont(TELOPT_TM);
37357416Smarkm		set_my_state_wont(TELOPT_TM);
37457416Smarkm		return;
37557416Smarkm
37657416Smarkm	    case TELOPT_BINARY:		/* binary mode */
37757416Smarkm	    case TELOPT_NAWS:		/* window size */
37857416Smarkm	    case TELOPT_TSPEED:		/* terminal speed */
37957416Smarkm	    case TELOPT_LFLOW:		/* local flow control */
38057416Smarkm	    case TELOPT_TTYPE:		/* terminal type option */
38157416Smarkm	    case TELOPT_SGA:		/* no big deal */
38257416Smarkm#if	defined(ENCRYPTION)
38357416Smarkm	    case TELOPT_ENCRYPT:	/* encryption variable option */
38457416Smarkm#endif
38557416Smarkm		new_state_ok = 1;
38657416Smarkm		break;
38757416Smarkm
38857416Smarkm	    case TELOPT_NEW_ENVIRON:	/* New environment variable option */
38957416Smarkm#ifdef	OLD_ENVIRON
39057416Smarkm		if (my_state_is_will(TELOPT_OLD_ENVIRON))
39157416Smarkm			send_wont(TELOPT_OLD_ENVIRON, 1); /* turn off the old */
39257416Smarkm		goto env_common;
39357416Smarkm	    case TELOPT_OLD_ENVIRON:	/* Old environment variable option */
39457416Smarkm		if (my_state_is_will(TELOPT_NEW_ENVIRON))
39557416Smarkm			break;		/* Don't enable if new one is in use! */
39657416Smarkm	    env_common:
39757416Smarkm		telopt_environ = option;
39857416Smarkm#endif
39957416Smarkm		new_state_ok = 1;
40057416Smarkm		break;
40157416Smarkm
40257416Smarkm#if	defined(AUTHENTICATION)
40357416Smarkm	    case TELOPT_AUTHENTICATION:
40457416Smarkm		if (autologin)
40557416Smarkm			new_state_ok = 1;
40657416Smarkm		break;
40757416Smarkm#endif
40857416Smarkm
40957416Smarkm	    case TELOPT_XDISPLOC:	/* X Display location */
41057416Smarkm		if (env_getvalue((unsigned char *)"DISPLAY"))
41157416Smarkm		    new_state_ok = 1;
41257416Smarkm		break;
41357416Smarkm
41457416Smarkm	    case TELOPT_LINEMODE:
41557416Smarkm#ifdef	KLUDGELINEMODE
41657416Smarkm		kludgelinemode = 0;
41757416Smarkm		send_do(TELOPT_SGA, 1);
41857416Smarkm#endif
41957416Smarkm		set_my_want_state_will(TELOPT_LINEMODE);
42057416Smarkm		send_will(option, 0);
42157416Smarkm		set_my_state_will(TELOPT_LINEMODE);
42257416Smarkm		slc_init();
42357416Smarkm		return;
42457416Smarkm
42557416Smarkm	    case TELOPT_ECHO:		/* We're never going to echo... */
42657416Smarkm	    default:
42757416Smarkm		break;
42857416Smarkm	    }
42957416Smarkm
43057416Smarkm	    if (new_state_ok) {
43157416Smarkm		set_my_want_state_will(option);
43257416Smarkm		send_will(option, 0);
43357416Smarkm		setconnmode(0);			/* Set new tty mode */
43457416Smarkm	    } else {
43557416Smarkm		will_wont_resp[option]++;
43657416Smarkm		send_wont(option, 0);
43757416Smarkm	    }
43857416Smarkm	  } else {
43957416Smarkm	    /*
44057416Smarkm	     * Handle options that need more things done after the
44157416Smarkm	     * other side has acknowledged the option.
44257416Smarkm	     */
44357416Smarkm	    switch (option) {
44457416Smarkm	    case TELOPT_LINEMODE:
44557416Smarkm#ifdef	KLUDGELINEMODE
44657416Smarkm		kludgelinemode = 0;
44757416Smarkm		send_do(TELOPT_SGA, 1);
44857416Smarkm#endif
44957416Smarkm		set_my_state_will(option);
45057416Smarkm		slc_init();
45157416Smarkm		send_do(TELOPT_SGA, 0);
45257416Smarkm		return;
45357416Smarkm	    }
45457416Smarkm	  }
45557416Smarkm	}
45657416Smarkm	set_my_state_will(option);
45757416Smarkm}
45857416Smarkm
45957416Smarkmstatic void
46057416Smarkmdontoption(int option)
46157416Smarkm{
46257416Smarkm
46357416Smarkm	if (will_wont_resp[option]) {
46457416Smarkm	    --will_wont_resp[option];
46557416Smarkm	    if (will_wont_resp[option] && my_state_is_wont(option))
46657416Smarkm		--will_wont_resp[option];
46757416Smarkm	}
46857416Smarkm
46957416Smarkm	if ((will_wont_resp[option] == 0) && my_want_state_is_will(option)) {
47057416Smarkm	    switch (option) {
47157416Smarkm	    case TELOPT_LINEMODE:
47257416Smarkm		linemode = 0;	/* put us back to the default state */
47357416Smarkm		break;
47457416Smarkm#ifdef	OLD_ENVIRON
47557416Smarkm	    case TELOPT_NEW_ENVIRON:
47657416Smarkm		/*
47757416Smarkm		 * The new environ option wasn't recognized, try
47857416Smarkm		 * the old one.
47957416Smarkm		 */
48057416Smarkm		send_will(TELOPT_OLD_ENVIRON, 1);
48157416Smarkm		telopt_environ = TELOPT_OLD_ENVIRON;
48257416Smarkm		break;
48357416Smarkm#endif
48457416Smarkm#if 0
48557416Smarkm#ifdef ENCRYPTION
48657416Smarkm	    case TELOPT_ENCRYPT:
48757416Smarkm	      encrypt_not();
48857416Smarkm	      break;
48957416Smarkm#endif
49057416Smarkm#endif
49157416Smarkm	    }
49257416Smarkm	    /* we always accept a DONT */
49357416Smarkm	    set_my_want_state_wont(option);
49457416Smarkm	    if (my_state_is_will(option))
49557416Smarkm		send_wont(option, 0);
49657416Smarkm	    setconnmode(0);			/* Set new tty mode */
49757416Smarkm	}
49857416Smarkm	set_my_state_wont(option);
49957416Smarkm}
50057416Smarkm
50157416Smarkm/*
50257416Smarkm * Given a buffer returned by tgetent(), this routine will turn
50357416Smarkm * the pipe seperated list of names in the buffer into an array
50457416Smarkm * of pointers to null terminated names.  We toss out any bad,
50557416Smarkm * duplicate, or verbose names (names with spaces).
50657416Smarkm */
50757416Smarkm
50857416Smarkmstatic char *name_unknown = "UNKNOWN";
50957416Smarkmstatic char *unknown[] = { 0, 0 };
51057416Smarkm
51157416Smarkmstatic char **
51257416Smarkmmklist(char *buf, char *name)
51357416Smarkm{
51457416Smarkm	int n;
51557416Smarkm	char c, *cp, **argvp, *cp2, **argv, **avt;
51657416Smarkm
51757416Smarkm	if (name) {
51857416Smarkm		if ((int)strlen(name) > 40) {
51957416Smarkm			name = 0;
52057416Smarkm			unknown[0] = name_unknown;
52157416Smarkm		} else {
52257416Smarkm			unknown[0] = name;
52357416Smarkm			strupr(name);
52457416Smarkm		}
52557416Smarkm	} else
52657416Smarkm		unknown[0] = name_unknown;
52757416Smarkm	/*
52857416Smarkm	 * Count up the number of names.
52957416Smarkm	 */
53057416Smarkm	for (n = 1, cp = buf; *cp && *cp != ':'; cp++) {
53157416Smarkm		if (*cp == '|')
53257416Smarkm			n++;
53357416Smarkm	}
53457416Smarkm	/*
53557416Smarkm	 * Allocate an array to put the name pointers into
53657416Smarkm	 */
53757416Smarkm	argv = (char **)malloc((n+3)*sizeof(char *));
53857416Smarkm	if (argv == 0)
53957416Smarkm		return(unknown);
54057416Smarkm
54157416Smarkm	/*
54257416Smarkm	 * Fill up the array of pointers to names.
54357416Smarkm	 */
54457416Smarkm	*argv = 0;
54557416Smarkm	argvp = argv+1;
54657416Smarkm	n = 0;
54757416Smarkm	for (cp = cp2 = buf; (c = *cp);  cp++) {
54857416Smarkm		if (c == '|' || c == ':') {
54957416Smarkm			*cp++ = '\0';
55057416Smarkm			/*
55157416Smarkm			 * Skip entries that have spaces or are over 40
55257416Smarkm			 * characters long.  If this is our environment
55357416Smarkm			 * name, then put it up front.  Otherwise, as
55457416Smarkm			 * long as this is not a duplicate name (case
55557416Smarkm			 * insensitive) add it to the list.
55657416Smarkm			 */
55757416Smarkm			if (n || (cp - cp2 > 41))
55857416Smarkm				;
55957416Smarkm			else if (name && (strncasecmp(name, cp2, cp-cp2) == 0))
56057416Smarkm				*argv = cp2;
56157416Smarkm			else if (is_unique(cp2, argv+1, argvp))
56257416Smarkm				*argvp++ = cp2;
56357416Smarkm			if (c == ':')
56457416Smarkm				break;
56557416Smarkm			/*
56657416Smarkm			 * Skip multiple delimiters. Reset cp2 to
56757416Smarkm			 * the beginning of the next name. Reset n,
56857416Smarkm			 * the flag for names with spaces.
56957416Smarkm			 */
57057416Smarkm			while ((c = *cp) == '|')
57157416Smarkm				cp++;
57257416Smarkm			cp2 = cp;
57357416Smarkm			n = 0;
57457416Smarkm		}
57557416Smarkm		/*
57657416Smarkm		 * Skip entries with spaces or non-ascii values.
57757416Smarkm		 * Convert lower case letters to upper case.
57857416Smarkm		 */
57957416Smarkm#define ISASCII(c) (!((c)&0x80))
58057416Smarkm		if ((c == ' ') || !ISASCII(c))
58157416Smarkm			n = 1;
58257416Smarkm		else if (islower(c))
58357416Smarkm			*cp = toupper(c);
58457416Smarkm	}
58557416Smarkm
58657416Smarkm	/*
58757416Smarkm	 * Check for an old V6 2 character name.  If the second
58857416Smarkm	 * name points to the beginning of the buffer, and is
58957416Smarkm	 * only 2 characters long, move it to the end of the array.
59057416Smarkm	 */
59157416Smarkm	if ((argv[1] == buf) && (strlen(argv[1]) == 2)) {
59257416Smarkm		--argvp;
59357416Smarkm		for (avt = &argv[1]; avt < argvp; avt++)
59457416Smarkm			*avt = *(avt+1);
59557416Smarkm		*argvp++ = buf;
59657416Smarkm	}
59757416Smarkm
59857416Smarkm	/*
59957416Smarkm	 * Duplicate last name, for TTYPE option, and null
60057416Smarkm	 * terminate the array.  If we didn't find a match on
60157416Smarkm	 * our terminal name, put that name at the beginning.
60257416Smarkm	 */
60357416Smarkm	cp = *(argvp-1);
60457416Smarkm	*argvp++ = cp;
60557416Smarkm	*argvp = 0;
60657416Smarkm
60757416Smarkm	if (*argv == 0) {
60857416Smarkm		if (name)
60957416Smarkm			*argv = name;
61057416Smarkm		else {
61157416Smarkm			--argvp;
61257416Smarkm			for (avt = argv; avt < argvp; avt++)
61357416Smarkm				*avt = *(avt+1);
61457416Smarkm		}
61557416Smarkm	}
61657416Smarkm	if (*argv)
61757416Smarkm		return(argv);
61857416Smarkm	else
61957416Smarkm		return(unknown);
62057416Smarkm}
62157416Smarkm
62257416Smarkmstatic int
62357416Smarkmis_unique(char *name, char **as, char **ae)
62457416Smarkm{
62557416Smarkm	char **ap;
62657416Smarkm	int n;
62757416Smarkm
62857416Smarkm	n = strlen(name) + 1;
62957416Smarkm	for (ap = as; ap < ae; ap++)
63057416Smarkm		if (strncasecmp(*ap, name, n) == 0)
63157416Smarkm			return(0);
63257416Smarkm	return (1);
63357416Smarkm}
63457416Smarkm
63557416Smarkmstatic char termbuf[1024];
63657416Smarkm
63757416Smarkmstatic int
63857416Smarkmtelnet_setupterm(const char *tname, int fd, int *errp)
63957416Smarkm{
64057416Smarkm	if (tgetent(termbuf, tname) == 1) {
64157416Smarkm		termbuf[1023] = '\0';
64257416Smarkm		if (errp)
64357416Smarkm			*errp = 1;
64457416Smarkm		return(0);
64557416Smarkm	}
64657416Smarkm	if (errp)
64757416Smarkm		*errp = 0;
64857416Smarkm	return(-1);
64957416Smarkm}
65057416Smarkm
65157416Smarkmint resettermname = 1;
65257416Smarkm
65357416Smarkmstatic char *
65457416Smarkmgettermname()
65557416Smarkm{
65657416Smarkm	char *tname;
65757416Smarkm	static char **tnamep = 0;
65857416Smarkm	static char **next;
65957416Smarkm	int err;
66057416Smarkm
66157416Smarkm	if (resettermname) {
66257416Smarkm		resettermname = 0;
66357416Smarkm		if (tnamep && tnamep != unknown)
66457416Smarkm			free(tnamep);
66557416Smarkm		if ((tname = (char *)env_getvalue((unsigned char *)"TERM")) &&
66657416Smarkm				telnet_setupterm(tname, 1, &err) == 0) {
66757416Smarkm			tnamep = mklist(termbuf, tname);
66857416Smarkm		} else {
66957416Smarkm			if (tname && ((int)strlen(tname) <= 40)) {
67057416Smarkm				unknown[0] = tname;
67157416Smarkm				strupr(tname);
67257416Smarkm			} else
67357416Smarkm				unknown[0] = name_unknown;
67457416Smarkm			tnamep = unknown;
67557416Smarkm		}
67657416Smarkm		next = tnamep;
67757416Smarkm	}
67857416Smarkm	if (*next == 0)
67957416Smarkm		next = tnamep;
68057416Smarkm	return(*next++);
68157416Smarkm}
68257416Smarkm/*
68357416Smarkm * suboption()
68457416Smarkm *
68557416Smarkm *	Look at the sub-option buffer, and try to be helpful to the other
68657416Smarkm * side.
68757416Smarkm *
68857416Smarkm *	Currently we recognize:
68957416Smarkm *
69057416Smarkm *		Terminal type, send request.
69157416Smarkm *		Terminal speed (send request).
69257416Smarkm *		Local flow control (is request).
69357416Smarkm *		Linemode
69457416Smarkm */
69557416Smarkm
69657416Smarkmstatic void
69757416Smarkmsuboption()
69857416Smarkm{
69957416Smarkm    unsigned char subchar;
70057416Smarkm
70157416Smarkm    printsub('<', subbuffer, SB_LEN()+2);
70257416Smarkm    switch (subchar = SB_GET()) {
70357416Smarkm    case TELOPT_TTYPE:
70457416Smarkm	if (my_want_state_is_wont(TELOPT_TTYPE))
70557416Smarkm	    return;
70657416Smarkm	if (SB_EOF() || SB_GET() != TELQUAL_SEND) {
70757416Smarkm	    return;
70857416Smarkm	} else {
70957416Smarkm	    char *name;
71057416Smarkm	    unsigned char temp[50];
71157416Smarkm	    int len;
71257416Smarkm
71357416Smarkm	    name = gettermname();
71457416Smarkm	    len = strlen(name) + 4 + 2;
71557416Smarkm	    if (len < NETROOM()) {
71657416Smarkm		snprintf((char *)temp, sizeof(temp),
71757416Smarkm			 "%c%c%c%c%s%c%c", IAC, SB, TELOPT_TTYPE,
71857416Smarkm			 TELQUAL_IS, name, IAC, SE);
71957416Smarkm		ring_supply_data(&netoring, temp, len);
72057416Smarkm		printsub('>', &temp[2], len-2);
72157416Smarkm	    } else {
72257416Smarkm		ExitString("No room in buffer for terminal type.\n", 1);
72357416Smarkm		/*NOTREACHED*/
72457416Smarkm	    }
72557416Smarkm	}
72657416Smarkm	break;
72757416Smarkm    case TELOPT_TSPEED:
72857416Smarkm	if (my_want_state_is_wont(TELOPT_TSPEED))
72957416Smarkm	    return;
73057416Smarkm	if (SB_EOF())
73157416Smarkm	    return;
73257416Smarkm	if (SB_GET() == TELQUAL_SEND) {
73357416Smarkm	    long output_speed, input_speed;
73457416Smarkm	    unsigned char temp[50];
73557416Smarkm	    int len;
73657416Smarkm
73757416Smarkm	    TerminalSpeeds(&input_speed, &output_speed);
73857416Smarkm
73957416Smarkm	    snprintf((char *)temp, sizeof(temp),
74057416Smarkm		     "%c%c%c%c%u,%u%c%c", IAC, SB, TELOPT_TSPEED,
74157416Smarkm		     TELQUAL_IS,
74257416Smarkm		     (unsigned)output_speed,
74357416Smarkm		     (unsigned)input_speed, IAC, SE);
74457416Smarkm	    len = strlen((char *)temp+4) + 4;	/* temp[3] is 0 ... */
74557416Smarkm
74657416Smarkm	    if (len < NETROOM()) {
74757416Smarkm		ring_supply_data(&netoring, temp, len);
74857416Smarkm		printsub('>', temp+2, len - 2);
74957416Smarkm	    }
75057416Smarkm/*@*/	    else printf("lm_will: not enough room in buffer\n");
75157416Smarkm	}
75257416Smarkm	break;
75357416Smarkm    case TELOPT_LFLOW:
75457416Smarkm	if (my_want_state_is_wont(TELOPT_LFLOW))
75557416Smarkm	    return;
75657416Smarkm	if (SB_EOF())
75757416Smarkm	    return;
75857416Smarkm	switch(SB_GET()) {
75957416Smarkm	case LFLOW_RESTART_ANY:
76057416Smarkm	    restartany = 1;
76157416Smarkm	    break;
76257416Smarkm	case LFLOW_RESTART_XON:
76357416Smarkm	    restartany = 0;
76457416Smarkm	    break;
76557416Smarkm	case LFLOW_ON:
76657416Smarkm	    localflow = 1;
76757416Smarkm	    break;
76857416Smarkm	case LFLOW_OFF:
76957416Smarkm	    localflow = 0;
77057416Smarkm	    break;
77157416Smarkm	default:
77257416Smarkm	    return;
77357416Smarkm	}
77457416Smarkm	setcommandmode();
77557416Smarkm	setconnmode(0);
77657416Smarkm	break;
77757416Smarkm
77857416Smarkm    case TELOPT_LINEMODE:
77957416Smarkm	if (my_want_state_is_wont(TELOPT_LINEMODE))
78057416Smarkm	    return;
78157416Smarkm	if (SB_EOF())
78257416Smarkm	    return;
78357416Smarkm	switch (SB_GET()) {
78457416Smarkm	case WILL:
78557416Smarkm	    lm_will(subpointer, SB_LEN());
78657416Smarkm	    break;
78757416Smarkm	case WONT:
78857416Smarkm	    lm_wont(subpointer, SB_LEN());
78957416Smarkm	    break;
79057416Smarkm	case DO:
79157416Smarkm	    lm_do(subpointer, SB_LEN());
79257416Smarkm	    break;
79357416Smarkm	case DONT:
79457416Smarkm	    lm_dont(subpointer, SB_LEN());
79557416Smarkm	    break;
79657416Smarkm	case LM_SLC:
79757416Smarkm	    slc(subpointer, SB_LEN());
79857416Smarkm	    break;
79957416Smarkm	case LM_MODE:
80057416Smarkm	    lm_mode(subpointer, SB_LEN(), 0);
80157416Smarkm	    break;
80257416Smarkm	default:
80357416Smarkm	    break;
80457416Smarkm	}
80557416Smarkm	break;
80657416Smarkm
80757416Smarkm#ifdef	OLD_ENVIRON
80857416Smarkm    case TELOPT_OLD_ENVIRON:
80957416Smarkm#endif
81057416Smarkm    case TELOPT_NEW_ENVIRON:
81157416Smarkm	if (SB_EOF())
81257416Smarkm	    return;
81357416Smarkm	switch(SB_PEEK()) {
81457416Smarkm	case TELQUAL_IS:
81557416Smarkm	case TELQUAL_INFO:
81657416Smarkm	    if (my_want_state_is_dont(subchar))
81757416Smarkm		return;
81857416Smarkm	    break;
81957416Smarkm	case TELQUAL_SEND:
82057416Smarkm	    if (my_want_state_is_wont(subchar)) {
82157416Smarkm		return;
82257416Smarkm	    }
82357416Smarkm	    break;
82457416Smarkm	default:
82557416Smarkm	    return;
82657416Smarkm	}
82757416Smarkm	env_opt(subpointer, SB_LEN());
82857416Smarkm	break;
82957416Smarkm
83057416Smarkm    case TELOPT_XDISPLOC:
83157416Smarkm	if (my_want_state_is_wont(TELOPT_XDISPLOC))
83257416Smarkm	    return;
83357416Smarkm	if (SB_EOF())
83457416Smarkm	    return;
83557416Smarkm	if (SB_GET() == TELQUAL_SEND) {
83657416Smarkm	    unsigned char temp[50], *dp;
83757416Smarkm	    int len;
83857416Smarkm
83957416Smarkm	    if ((dp = env_getvalue((unsigned char *)"DISPLAY")) == NULL) {
84057416Smarkm		/*
84157416Smarkm		 * Something happened, we no longer have a DISPLAY
84257416Smarkm		 * variable.  So, turn off the option.
84357416Smarkm		 */
84457416Smarkm		send_wont(TELOPT_XDISPLOC, 1);
84557416Smarkm		break;
84657416Smarkm	    }
84757416Smarkm	    snprintf((char *)temp, sizeof(temp),
84857416Smarkm		     "%c%c%c%c%s%c%c", IAC, SB, TELOPT_XDISPLOC,
84957416Smarkm		     TELQUAL_IS, dp, IAC, SE);
85057416Smarkm	    len = strlen((char *)temp+4) + 4;	/* temp[3] is 0 ... */
85157416Smarkm
85257416Smarkm	    if (len < NETROOM()) {
85357416Smarkm		ring_supply_data(&netoring, temp, len);
85457416Smarkm		printsub('>', temp+2, len - 2);
85557416Smarkm	    }
85657416Smarkm/*@*/	    else printf("lm_will: not enough room in buffer\n");
85757416Smarkm	}
85857416Smarkm	break;
85957416Smarkm
86057416Smarkm#if	defined(AUTHENTICATION)
86157416Smarkm	case TELOPT_AUTHENTICATION: {
86257416Smarkm		if (!autologin)
86357416Smarkm			break;
86457416Smarkm		if (SB_EOF())
86557416Smarkm			return;
86657416Smarkm		switch(SB_GET()) {
86757416Smarkm		case TELQUAL_IS:
86857416Smarkm			if (my_want_state_is_dont(TELOPT_AUTHENTICATION))
86957416Smarkm				return;
87057416Smarkm			auth_is(subpointer, SB_LEN());
87157416Smarkm			break;
87257416Smarkm		case TELQUAL_SEND:
87357416Smarkm			if (my_want_state_is_wont(TELOPT_AUTHENTICATION))
87457416Smarkm				return;
87557416Smarkm			auth_send(subpointer, SB_LEN());
87657416Smarkm			break;
87757416Smarkm		case TELQUAL_REPLY:
87857416Smarkm			if (my_want_state_is_wont(TELOPT_AUTHENTICATION))
87957416Smarkm				return;
88057416Smarkm			auth_reply(subpointer, SB_LEN());
88157416Smarkm			break;
88257416Smarkm		case TELQUAL_NAME:
88357416Smarkm			if (my_want_state_is_dont(TELOPT_AUTHENTICATION))
88457416Smarkm				return;
88557416Smarkm			auth_name(subpointer, SB_LEN());
88657416Smarkm			break;
88757416Smarkm		}
88857416Smarkm	}
88957416Smarkm	break;
89057416Smarkm#endif
89157416Smarkm#if	defined(ENCRYPTION)
89257416Smarkm	case TELOPT_ENCRYPT:
89357416Smarkm		if (SB_EOF())
89457416Smarkm			return;
89557416Smarkm		switch(SB_GET()) {
89657416Smarkm		case ENCRYPT_START:
89757416Smarkm			if (my_want_state_is_dont(TELOPT_ENCRYPT))
89857416Smarkm				return;
89957416Smarkm			encrypt_start(subpointer, SB_LEN());
90057416Smarkm			break;
90157416Smarkm		case ENCRYPT_END:
90257416Smarkm			if (my_want_state_is_dont(TELOPT_ENCRYPT))
90357416Smarkm				return;
90457416Smarkm			encrypt_end();
90557416Smarkm			break;
90657416Smarkm		case ENCRYPT_SUPPORT:
90757416Smarkm			if (my_want_state_is_wont(TELOPT_ENCRYPT))
90857416Smarkm				return;
90957416Smarkm			encrypt_support(subpointer, SB_LEN());
91057416Smarkm			break;
91157416Smarkm		case ENCRYPT_REQSTART:
91257416Smarkm			if (my_want_state_is_wont(TELOPT_ENCRYPT))
91357416Smarkm				return;
91457416Smarkm			encrypt_request_start(subpointer, SB_LEN());
91557416Smarkm			break;
91657416Smarkm		case ENCRYPT_REQEND:
91757416Smarkm			if (my_want_state_is_wont(TELOPT_ENCRYPT))
91857416Smarkm				return;
91957416Smarkm			/*
92057416Smarkm			 * We can always send an REQEND so that we cannot
92157416Smarkm			 * get stuck encrypting.  We should only get this
92257416Smarkm			 * if we have been able to get in the correct mode
92357416Smarkm			 * anyhow.
92457416Smarkm			 */
92557416Smarkm			encrypt_request_end();
92657416Smarkm			break;
92757416Smarkm		case ENCRYPT_IS:
92857416Smarkm			if (my_want_state_is_dont(TELOPT_ENCRYPT))
92957416Smarkm				return;
93057416Smarkm			encrypt_is(subpointer, SB_LEN());
93157416Smarkm			break;
93257416Smarkm		case ENCRYPT_REPLY:
93357416Smarkm			if (my_want_state_is_wont(TELOPT_ENCRYPT))
93457416Smarkm				return;
93557416Smarkm			encrypt_reply(subpointer, SB_LEN());
93657416Smarkm			break;
93757416Smarkm		case ENCRYPT_ENC_KEYID:
93857416Smarkm			if (my_want_state_is_dont(TELOPT_ENCRYPT))
93957416Smarkm				return;
94057416Smarkm			encrypt_enc_keyid(subpointer, SB_LEN());
94157416Smarkm			break;
94257416Smarkm		case ENCRYPT_DEC_KEYID:
94357416Smarkm			if (my_want_state_is_wont(TELOPT_ENCRYPT))
94457416Smarkm				return;
94557416Smarkm			encrypt_dec_keyid(subpointer, SB_LEN());
94657416Smarkm			break;
94757416Smarkm		default:
94857416Smarkm			break;
94957416Smarkm		}
95057416Smarkm		break;
95157416Smarkm#endif
95257416Smarkm    default:
95357416Smarkm	break;
95457416Smarkm    }
95557416Smarkm}
95657416Smarkm
95757416Smarkmstatic unsigned char str_lm[] = { IAC, SB, TELOPT_LINEMODE, 0, 0, IAC, SE };
95857416Smarkm
95957416Smarkmvoid
96057416Smarkmlm_will(unsigned char *cmd, int len)
96157416Smarkm{
96257416Smarkm    if (len < 1) {
96357416Smarkm/*@*/	printf("lm_will: no command!!!\n");	/* Should not happen... */
96457416Smarkm	return;
96557416Smarkm    }
96657416Smarkm    switch(cmd[0]) {
96757416Smarkm    case LM_FORWARDMASK:	/* We shouldn't ever get this... */
96857416Smarkm    default:
96957416Smarkm	str_lm[3] = DONT;
97057416Smarkm	str_lm[4] = cmd[0];
97157416Smarkm	if (NETROOM() > sizeof(str_lm)) {
97257416Smarkm	    ring_supply_data(&netoring, str_lm, sizeof(str_lm));
97357416Smarkm	    printsub('>', &str_lm[2], sizeof(str_lm)-2);
97457416Smarkm	}
97557416Smarkm/*@*/	else printf("lm_will: not enough room in buffer\n");
97657416Smarkm	break;
97757416Smarkm    }
97857416Smarkm}
97957416Smarkm
98057416Smarkmvoid
98157416Smarkmlm_wont(unsigned char *cmd, int len)
98257416Smarkm{
98357416Smarkm    if (len < 1) {
98457416Smarkm/*@*/	printf("lm_wont: no command!!!\n");	/* Should not happen... */
98557416Smarkm	return;
98657416Smarkm    }
98757416Smarkm    switch(cmd[0]) {
98857416Smarkm    case LM_FORWARDMASK:	/* We shouldn't ever get this... */
98957416Smarkm    default:
99057416Smarkm	/* We are always DONT, so don't respond */
99157416Smarkm	return;
99257416Smarkm    }
99357416Smarkm}
99457416Smarkm
99557416Smarkmvoid
99657416Smarkmlm_do(unsigned char *cmd, int len)
99757416Smarkm{
99857416Smarkm    if (len < 1) {
99957416Smarkm/*@*/	printf("lm_do: no command!!!\n");	/* Should not happen... */
100057416Smarkm	return;
100157416Smarkm    }
100257416Smarkm    switch(cmd[0]) {
100357416Smarkm    case LM_FORWARDMASK:
100457416Smarkm    default:
100557416Smarkm	str_lm[3] = WONT;
100657416Smarkm	str_lm[4] = cmd[0];
100757416Smarkm	if (NETROOM() > sizeof(str_lm)) {
100857416Smarkm	    ring_supply_data(&netoring, str_lm, sizeof(str_lm));
100957416Smarkm	    printsub('>', &str_lm[2], sizeof(str_lm)-2);
101057416Smarkm	}
101157416Smarkm/*@*/	else printf("lm_do: not enough room in buffer\n");
101257416Smarkm	break;
101357416Smarkm    }
101457416Smarkm}
101557416Smarkm
101657416Smarkmvoid
101757416Smarkmlm_dont(unsigned char *cmd, int len)
101857416Smarkm{
101957416Smarkm    if (len < 1) {
102057416Smarkm/*@*/	printf("lm_dont: no command!!!\n");	/* Should not happen... */
102157416Smarkm	return;
102257416Smarkm    }
102357416Smarkm    switch(cmd[0]) {
102457416Smarkm    case LM_FORWARDMASK:
102557416Smarkm    default:
102657416Smarkm	/* we are always WONT, so don't respond */
102757416Smarkm	break;
102857416Smarkm    }
102957416Smarkm}
103057416Smarkm
103157416Smarkmstatic unsigned char str_lm_mode[] = {
103257416Smarkm	IAC, SB, TELOPT_LINEMODE, LM_MODE, 0, IAC, SE
103357416Smarkm};
103457416Smarkm
103557416Smarkmvoid
103657416Smarkmlm_mode(unsigned char *cmd, int len, int init)
103757416Smarkm{
103857416Smarkm	if (len != 1)
103957416Smarkm		return;
104057416Smarkm	if ((linemode&MODE_MASK&~MODE_ACK) == *cmd)
104157416Smarkm		return;
104257416Smarkm	if (*cmd&MODE_ACK)
104357416Smarkm		return;
104457416Smarkm	linemode = *cmd&(MODE_MASK&~MODE_ACK);
104557416Smarkm	str_lm_mode[4] = linemode;
104657416Smarkm	if (!init)
104757416Smarkm	    str_lm_mode[4] |= MODE_ACK;
104857416Smarkm	if (NETROOM() > sizeof(str_lm_mode)) {
104957416Smarkm	    ring_supply_data(&netoring, str_lm_mode, sizeof(str_lm_mode));
105057416Smarkm	    printsub('>', &str_lm_mode[2], sizeof(str_lm_mode)-2);
105157416Smarkm	}
105257416Smarkm/*@*/	else printf("lm_mode: not enough room in buffer\n");
105357416Smarkm	setconnmode(0);	/* set changed mode */
105457416Smarkm}
105557416Smarkm
105657416Smarkm
105757416Smarkm
105857416Smarkm/*
105957416Smarkm * slc()
106057416Smarkm * Handle special character suboption of LINEMODE.
106157416Smarkm */
106257416Smarkm
106357416Smarkmstruct spc {
106457416Smarkm	cc_t val;
106557416Smarkm	cc_t *valp;
106657416Smarkm	char flags;	/* Current flags & level */
106757416Smarkm	char mylevel;	/* Maximum level & flags */
106857416Smarkm} spc_data[NSLC+1];
106957416Smarkm
107057416Smarkm#define SLC_IMPORT	0
107157416Smarkm#define	SLC_EXPORT	1
107257416Smarkm#define SLC_RVALUE	2
107357416Smarkmstatic int slc_mode = SLC_EXPORT;
107457416Smarkm
107557416Smarkmvoid
107657416Smarkmslc_init()
107757416Smarkm{
107857416Smarkm	struct spc *spcp;
107957416Smarkm
108057416Smarkm	localchars = 1;
108157416Smarkm	for (spcp = spc_data; spcp < &spc_data[NSLC+1]; spcp++) {
108257416Smarkm		spcp->val = 0;
108357416Smarkm		spcp->valp = 0;
108457416Smarkm		spcp->flags = spcp->mylevel = SLC_NOSUPPORT;
108557416Smarkm	}
108657416Smarkm
108757416Smarkm#define	initfunc(func, flags) { \
108857416Smarkm					spcp = &spc_data[func]; \
108957416Smarkm					if ((spcp->valp = tcval(func))) { \
109057416Smarkm					    spcp->val = *spcp->valp; \
109157416Smarkm					    spcp->mylevel = SLC_VARIABLE|flags; \
109257416Smarkm					} else { \
109357416Smarkm					    spcp->val = 0; \
109457416Smarkm					    spcp->mylevel = SLC_DEFAULT; \
109557416Smarkm					} \
109657416Smarkm				    }
109757416Smarkm
109857416Smarkm	initfunc(SLC_SYNCH, 0);
109957416Smarkm	/* No BRK */
110057416Smarkm	initfunc(SLC_AO, 0);
110157416Smarkm	initfunc(SLC_AYT, 0);
110257416Smarkm	/* No EOR */
110357416Smarkm	initfunc(SLC_ABORT, SLC_FLUSHIN|SLC_FLUSHOUT);
110457416Smarkm	initfunc(SLC_EOF, 0);
110557416Smarkm	initfunc(SLC_SUSP, SLC_FLUSHIN);
110657416Smarkm	initfunc(SLC_EC, 0);
110757416Smarkm	initfunc(SLC_EL, 0);
110857416Smarkm	initfunc(SLC_EW, 0);
110957416Smarkm	initfunc(SLC_RP, 0);
111057416Smarkm	initfunc(SLC_LNEXT, 0);
111157416Smarkm	initfunc(SLC_XON, 0);
111257416Smarkm	initfunc(SLC_XOFF, 0);
111357416Smarkm	initfunc(SLC_FORW1, 0);
111457416Smarkm	initfunc(SLC_FORW2, 0);
111557416Smarkm	/* No FORW2 */
111657416Smarkm
111757416Smarkm	initfunc(SLC_IP, SLC_FLUSHIN|SLC_FLUSHOUT);
111857416Smarkm#undef	initfunc
111957416Smarkm
112057416Smarkm	if (slc_mode == SLC_EXPORT)
112157416Smarkm		slc_export();
112257416Smarkm	else
112357416Smarkm		slc_import(1);
112457416Smarkm
112557416Smarkm}
112657416Smarkm
112757416Smarkmvoid
112857416Smarkmslcstate()
112957416Smarkm{
113057416Smarkm    printf("Special characters are %s values\n",
113157416Smarkm		slc_mode == SLC_IMPORT ? "remote default" :
113257416Smarkm		slc_mode == SLC_EXPORT ? "local" :
113357416Smarkm					 "remote");
113457416Smarkm}
113557416Smarkm
113657416Smarkmvoid
113757416Smarkmslc_mode_export()
113857416Smarkm{
113957416Smarkm    slc_mode = SLC_EXPORT;
114057416Smarkm    if (my_state_is_will(TELOPT_LINEMODE))
114157416Smarkm	slc_export();
114257416Smarkm}
114357416Smarkm
114457416Smarkmvoid
114557416Smarkmslc_mode_import(int def)
114657416Smarkm{
114757416Smarkm    slc_mode = def ? SLC_IMPORT : SLC_RVALUE;
114857416Smarkm    if (my_state_is_will(TELOPT_LINEMODE))
114957416Smarkm	slc_import(def);
115057416Smarkm}
115157416Smarkm
115257416Smarkmunsigned char slc_import_val[] = {
115357416Smarkm	IAC, SB, TELOPT_LINEMODE, LM_SLC, 0, SLC_VARIABLE, 0, IAC, SE
115457416Smarkm};
115557416Smarkmunsigned char slc_import_def[] = {
115657416Smarkm	IAC, SB, TELOPT_LINEMODE, LM_SLC, 0, SLC_DEFAULT, 0, IAC, SE
115757416Smarkm};
115857416Smarkm
115957416Smarkmvoid
116057416Smarkmslc_import(int def)
116157416Smarkm{
116257416Smarkm    if (NETROOM() > sizeof(slc_import_val)) {
116357416Smarkm	if (def) {
116457416Smarkm	    ring_supply_data(&netoring, slc_import_def, sizeof(slc_import_def));
116557416Smarkm	    printsub('>', &slc_import_def[2], sizeof(slc_import_def)-2);
116657416Smarkm	} else {
116757416Smarkm	    ring_supply_data(&netoring, slc_import_val, sizeof(slc_import_val));
116857416Smarkm	    printsub('>', &slc_import_val[2], sizeof(slc_import_val)-2);
116957416Smarkm	}
117057416Smarkm    }
117157416Smarkm/*@*/ else printf("slc_import: not enough room\n");
117257416Smarkm}
117357416Smarkm
117457416Smarkmvoid
117557416Smarkmslc_export()
117657416Smarkm{
117757416Smarkm    struct spc *spcp;
117857416Smarkm
117957416Smarkm    TerminalDefaultChars();
118057416Smarkm
118157416Smarkm    slc_start_reply();
118257416Smarkm    for (spcp = &spc_data[1]; spcp < &spc_data[NSLC+1]; spcp++) {
118357416Smarkm	if (spcp->mylevel != SLC_NOSUPPORT) {
118457416Smarkm	    if (spcp->val == (cc_t)(_POSIX_VDISABLE))
118557416Smarkm		spcp->flags = SLC_NOSUPPORT;
118657416Smarkm	    else
118757416Smarkm		spcp->flags = spcp->mylevel;
118857416Smarkm	    if (spcp->valp)
118957416Smarkm		spcp->val = *spcp->valp;
119057416Smarkm	    slc_add_reply(spcp - spc_data, spcp->flags, spcp->val);
119157416Smarkm	}
119257416Smarkm    }
119357416Smarkm    slc_end_reply();
119457416Smarkm    slc_update();
119557416Smarkm    setconnmode(1);	/* Make sure the character values are set */
119657416Smarkm}
119757416Smarkm
119857416Smarkmvoid
119957416Smarkmslc(unsigned char *cp, int len)
120057416Smarkm{
120157416Smarkm	struct spc *spcp;
120257416Smarkm	int func,level;
120357416Smarkm
120457416Smarkm	slc_start_reply();
120557416Smarkm
120657416Smarkm	for (; len >= 3; len -=3, cp +=3) {
120757416Smarkm
120857416Smarkm		func = cp[SLC_FUNC];
120957416Smarkm
121057416Smarkm		if (func == 0) {
121157416Smarkm			/*
121257416Smarkm			 * Client side: always ignore 0 function.
121357416Smarkm			 */
121457416Smarkm			continue;
121557416Smarkm		}
121657416Smarkm		if (func > NSLC) {
121757416Smarkm			if ((cp[SLC_FLAGS] & SLC_LEVELBITS) != SLC_NOSUPPORT)
121857416Smarkm				slc_add_reply(func, SLC_NOSUPPORT, 0);
121957416Smarkm			continue;
122057416Smarkm		}
122157416Smarkm
122257416Smarkm		spcp = &spc_data[func];
122357416Smarkm
122457416Smarkm		level = cp[SLC_FLAGS]&(SLC_LEVELBITS|SLC_ACK);
122557416Smarkm
122657416Smarkm		if ((cp[SLC_VALUE] == (unsigned char)spcp->val) &&
122757416Smarkm		    ((level&SLC_LEVELBITS) == (spcp->flags&SLC_LEVELBITS))) {
122857416Smarkm			continue;
122957416Smarkm		}
123057416Smarkm
123157416Smarkm		if (level == (SLC_DEFAULT|SLC_ACK)) {
123257416Smarkm			/*
123357416Smarkm			 * This is an error condition, the SLC_ACK
123457416Smarkm			 * bit should never be set for the SLC_DEFAULT
123557416Smarkm			 * level.  Our best guess to recover is to
123657416Smarkm			 * ignore the SLC_ACK bit.
123757416Smarkm			 */
123857416Smarkm			cp[SLC_FLAGS] &= ~SLC_ACK;
123957416Smarkm		}
124057416Smarkm
124157416Smarkm		if (level == ((spcp->flags&SLC_LEVELBITS)|SLC_ACK)) {
124257416Smarkm			spcp->val = (cc_t)cp[SLC_VALUE];
124357416Smarkm			spcp->flags = cp[SLC_FLAGS];	/* include SLC_ACK */
124457416Smarkm			continue;
124557416Smarkm		}
124657416Smarkm
124757416Smarkm		level &= ~SLC_ACK;
124857416Smarkm
124957416Smarkm		if (level <= (spcp->mylevel&SLC_LEVELBITS)) {
125057416Smarkm			spcp->flags = cp[SLC_FLAGS]|SLC_ACK;
125157416Smarkm			spcp->val = (cc_t)cp[SLC_VALUE];
125257416Smarkm		}
125357416Smarkm		if (level == SLC_DEFAULT) {
125457416Smarkm			if ((spcp->mylevel&SLC_LEVELBITS) != SLC_DEFAULT)
125557416Smarkm				spcp->flags = spcp->mylevel;
125657416Smarkm			else
125757416Smarkm				spcp->flags = SLC_NOSUPPORT;
125857416Smarkm		}
125957416Smarkm		slc_add_reply(func, spcp->flags, spcp->val);
126057416Smarkm	}
126157416Smarkm	slc_end_reply();
126257416Smarkm	if (slc_update())
126357416Smarkm		setconnmode(1);	/* set the  new character values */
126457416Smarkm}
126557416Smarkm
126657416Smarkmvoid
126757416Smarkmslc_check()
126857416Smarkm{
126957416Smarkm    struct spc *spcp;
127057416Smarkm
127157416Smarkm    slc_start_reply();
127257416Smarkm    for (spcp = &spc_data[1]; spcp < &spc_data[NSLC+1]; spcp++) {
127357416Smarkm	if (spcp->valp && spcp->val != *spcp->valp) {
127457416Smarkm	    spcp->val = *spcp->valp;
127557416Smarkm	    if (spcp->val == (cc_t)(_POSIX_VDISABLE))
127657416Smarkm		spcp->flags = SLC_NOSUPPORT;
127757416Smarkm	    else
127857416Smarkm		spcp->flags = spcp->mylevel;
127957416Smarkm	    slc_add_reply(spcp - spc_data, spcp->flags, spcp->val);
128057416Smarkm	}
128157416Smarkm    }
128257416Smarkm    slc_end_reply();
128357416Smarkm    setconnmode(1);
128457416Smarkm}
128557416Smarkm
128657416Smarkm
128757416Smarkmunsigned char slc_reply[128];
128857416Smarkmunsigned char *slc_replyp;
128957416Smarkm
129057416Smarkmvoid
129157416Smarkmslc_start_reply()
129257416Smarkm{
129357416Smarkm	slc_replyp = slc_reply;
129457416Smarkm	*slc_replyp++ = IAC;
129557416Smarkm	*slc_replyp++ = SB;
129657416Smarkm	*slc_replyp++ = TELOPT_LINEMODE;
129757416Smarkm	*slc_replyp++ = LM_SLC;
129857416Smarkm}
129957416Smarkm
130057416Smarkmvoid
130157416Smarkmslc_add_reply(unsigned char func, unsigned char flags, cc_t value)
130257416Smarkm{
130357416Smarkm	if ((*slc_replyp++ = func) == IAC)
130457416Smarkm		*slc_replyp++ = IAC;
130557416Smarkm	if ((*slc_replyp++ = flags) == IAC)
130657416Smarkm		*slc_replyp++ = IAC;
130757416Smarkm	if ((*slc_replyp++ = (unsigned char)value) == IAC)
130857416Smarkm		*slc_replyp++ = IAC;
130957416Smarkm}
131057416Smarkm
131157416Smarkmvoid
131257416Smarkmslc_end_reply()
131357416Smarkm{
131457416Smarkm    int len;
131557416Smarkm
131657416Smarkm    *slc_replyp++ = IAC;
131757416Smarkm    *slc_replyp++ = SE;
131857416Smarkm    len = slc_replyp - slc_reply;
131957416Smarkm    if (len <= 6)
132057416Smarkm	return;
132157416Smarkm    if (NETROOM() > len) {
132257416Smarkm	ring_supply_data(&netoring, slc_reply, slc_replyp - slc_reply);
132357416Smarkm	printsub('>', &slc_reply[2], slc_replyp - slc_reply - 2);
132457416Smarkm    }
132557416Smarkm/*@*/else printf("slc_end_reply: not enough room\n");
132657416Smarkm}
132757416Smarkm
132857416Smarkmint
132957416Smarkmslc_update()
133057416Smarkm{
133157416Smarkm	struct spc *spcp;
133257416Smarkm	int need_update = 0;
133357416Smarkm
133457416Smarkm	for (spcp = &spc_data[1]; spcp < &spc_data[NSLC+1]; spcp++) {
133557416Smarkm		if (!(spcp->flags&SLC_ACK))
133657416Smarkm			continue;
133757416Smarkm		spcp->flags &= ~SLC_ACK;
133857416Smarkm		if (spcp->valp && (*spcp->valp != spcp->val)) {
133957416Smarkm			*spcp->valp = spcp->val;
134057416Smarkm			need_update = 1;
134157416Smarkm		}
134257416Smarkm	}
134357416Smarkm	return(need_update);
134457416Smarkm}
134557416Smarkm
134657416Smarkm#ifdef	OLD_ENVIRON
134757416Smarkm#  define old_env_var OLD_ENV_VAR
134857416Smarkm#  define old_env_value OLD_ENV_VALUE
134957416Smarkm#endif
135057416Smarkm
135157416Smarkmvoid
135257416Smarkmenv_opt(unsigned char *buf, int len)
135357416Smarkm{
135457416Smarkm	unsigned char *ep = 0, *epc = 0;
135557416Smarkm	int i;
135657416Smarkm
135757416Smarkm	switch(buf[0]&0xff) {
135857416Smarkm	case TELQUAL_SEND:
135957416Smarkm		env_opt_start();
136057416Smarkm		if (len == 1) {
136157416Smarkm			env_opt_add(NULL);
136257416Smarkm		} else for (i = 1; i < len; i++) {
136357416Smarkm			switch (buf[i]&0xff) {
136457416Smarkm#ifdef	OLD_ENVIRON
136557416Smarkm			case OLD_ENV_VAR:
136657416Smarkm			case OLD_ENV_VALUE:
136757416Smarkm				/*
136857416Smarkm				 * Although OLD_ENV_VALUE is not legal, we will
136957416Smarkm				 * still recognize it, just in case it is an
137057416Smarkm				 * old server that has VAR & VALUE mixed up...
137157416Smarkm				 */
137257416Smarkm				/* FALL THROUGH */
137357416Smarkm#else
137457416Smarkm			case NEW_ENV_VAR:
137557416Smarkm#endif
137657416Smarkm			case ENV_USERVAR:
137757416Smarkm				if (ep) {
137857416Smarkm					*epc = 0;
137957416Smarkm					env_opt_add(ep);
138057416Smarkm				}
138157416Smarkm				ep = epc = &buf[i+1];
138257416Smarkm				break;
138357416Smarkm			case ENV_ESC:
138457416Smarkm				i++;
138557416Smarkm				/*FALL THROUGH*/
138657416Smarkm			default:
138757416Smarkm				if (epc)
138857416Smarkm					*epc++ = buf[i];
138957416Smarkm				break;
139057416Smarkm			}
139157416Smarkm		}
139257416Smarkm		if (ep) {
139357416Smarkm			*epc = 0;
139457416Smarkm			env_opt_add(ep);
139557416Smarkm		}
139657416Smarkm		env_opt_end(1);
139757416Smarkm		break;
139857416Smarkm
139957416Smarkm	case TELQUAL_IS:
140057416Smarkm	case TELQUAL_INFO:
140157416Smarkm		/* Ignore for now.  We shouldn't get it anyway. */
140257416Smarkm		break;
140357416Smarkm
140457416Smarkm	default:
140557416Smarkm		break;
140657416Smarkm	}
140757416Smarkm}
140857416Smarkm
140957416Smarkm#define	OPT_REPLY_SIZE	256
141057416Smarkmunsigned char *opt_reply;
141157416Smarkmunsigned char *opt_replyp;
141257416Smarkmunsigned char *opt_replyend;
141357416Smarkm
141457416Smarkmvoid
141557416Smarkmenv_opt_start()
141657416Smarkm{
141757416Smarkm	if (opt_reply) {
141857416Smarkm		void *tmp = realloc (opt_reply, OPT_REPLY_SIZE);
141957416Smarkm		if (tmp != NULL) {
142057416Smarkm			opt_reply = tmp;
142157416Smarkm		} else {
142257416Smarkm			free (opt_reply);
142357416Smarkm			opt_reply = NULL;
142457416Smarkm		}
142557416Smarkm	} else
142657416Smarkm		opt_reply = (unsigned char *)malloc(OPT_REPLY_SIZE);
142757416Smarkm	if (opt_reply == NULL) {
142857416Smarkm/*@*/		printf("env_opt_start: malloc()/realloc() failed!!!\n");
142957416Smarkm		opt_reply = opt_replyp = opt_replyend = NULL;
143057416Smarkm		return;
143157416Smarkm	}
143257416Smarkm	opt_replyp = opt_reply;
143357416Smarkm	opt_replyend = opt_reply + OPT_REPLY_SIZE;
143457416Smarkm	*opt_replyp++ = IAC;
143557416Smarkm	*opt_replyp++ = SB;
143657416Smarkm	*opt_replyp++ = telopt_environ;
143757416Smarkm	*opt_replyp++ = TELQUAL_IS;
143857416Smarkm}
143957416Smarkm
144057416Smarkmvoid
144157416Smarkmenv_opt_start_info()
144257416Smarkm{
144357416Smarkm	env_opt_start();
144457416Smarkm	if (opt_replyp)
144557416Smarkm	    opt_replyp[-1] = TELQUAL_INFO;
144657416Smarkm}
144757416Smarkm
144857416Smarkmvoid
144957416Smarkmenv_opt_add(unsigned char *ep)
145057416Smarkm{
145157416Smarkm	unsigned char *vp, c;
145257416Smarkm
145357416Smarkm	if (opt_reply == NULL)		/*XXX*/
145457416Smarkm		return;			/*XXX*/
145557416Smarkm
145657416Smarkm	if (ep == NULL || *ep == '\0') {
145757416Smarkm		/* Send user defined variables first. */
145857416Smarkm		env_default(1, 0);
145957416Smarkm		while ((ep = env_default(0, 0)))
146057416Smarkm			env_opt_add(ep);
146157416Smarkm
146257416Smarkm		/* Now add the list of well know variables.  */
146357416Smarkm		env_default(1, 1);
146457416Smarkm		while ((ep = env_default(0, 1)))
146557416Smarkm			env_opt_add(ep);
146657416Smarkm		return;
146757416Smarkm	}
146857416Smarkm	vp = env_getvalue(ep);
146957416Smarkm	if (opt_replyp + (vp ? strlen((char *)vp) : 0) +
147057416Smarkm				strlen((char *)ep) + 6 > opt_replyend)
147157416Smarkm	{
147257416Smarkm		int len;
147357416Smarkm		void *tmp;
147457416Smarkm		opt_replyend += OPT_REPLY_SIZE;
147557416Smarkm		len = opt_replyend - opt_reply;
147657416Smarkm		tmp = realloc(opt_reply, len);
147757416Smarkm		if (tmp == NULL) {
147857416Smarkm/*@*/			printf("env_opt_add: realloc() failed!!!\n");
147957416Smarkm			opt_reply = opt_replyp = opt_replyend = NULL;
148057416Smarkm			return;
148157416Smarkm		}
148257416Smarkm		opt_reply = tmp;
148357416Smarkm		opt_replyp = opt_reply + len - (opt_replyend - opt_replyp);
148457416Smarkm		opt_replyend = opt_reply + len;
148557416Smarkm	}
148657416Smarkm	if (opt_welldefined((char *)ep)) {
148757416Smarkm#ifdef	OLD_ENVIRON
148857416Smarkm		if (telopt_environ == TELOPT_OLD_ENVIRON)
148957416Smarkm			*opt_replyp++ = old_env_var;
149057416Smarkm		else
149157416Smarkm#endif
149257416Smarkm			*opt_replyp++ = NEW_ENV_VAR;
149357416Smarkm	} else
149457416Smarkm		*opt_replyp++ = ENV_USERVAR;
149557416Smarkm	for (;;) {
149657416Smarkm		while ((c = *ep++)) {
149757416Smarkm			switch(c&0xff) {
149857416Smarkm			case IAC:
149957416Smarkm				*opt_replyp++ = IAC;
150057416Smarkm				break;
150157416Smarkm			case NEW_ENV_VAR:
150257416Smarkm			case NEW_ENV_VALUE:
150357416Smarkm			case ENV_ESC:
150457416Smarkm			case ENV_USERVAR:
150557416Smarkm				*opt_replyp++ = ENV_ESC;
150657416Smarkm				break;
150757416Smarkm			}
150857416Smarkm			*opt_replyp++ = c;
150957416Smarkm		}
151057416Smarkm		if ((ep = vp)) {
151157416Smarkm#ifdef	OLD_ENVIRON
151257416Smarkm			if (telopt_environ == TELOPT_OLD_ENVIRON)
151357416Smarkm				*opt_replyp++ = old_env_value;
151457416Smarkm			else
151557416Smarkm#endif
151657416Smarkm				*opt_replyp++ = NEW_ENV_VALUE;
151757416Smarkm			vp = NULL;
151857416Smarkm		} else
151957416Smarkm			break;
152057416Smarkm	}
152157416Smarkm}
152257416Smarkm
152357416Smarkmint
152457416Smarkmopt_welldefined(char *ep)
152557416Smarkm{
152657416Smarkm	if ((strcmp(ep, "USER") == 0) ||
152757416Smarkm	    (strcmp(ep, "DISPLAY") == 0) ||
152857416Smarkm	    (strcmp(ep, "PRINTER") == 0) ||
152957416Smarkm	    (strcmp(ep, "SYSTEMTYPE") == 0) ||
153057416Smarkm	    (strcmp(ep, "JOB") == 0) ||
153157416Smarkm	    (strcmp(ep, "ACCT") == 0))
153257416Smarkm		return(1);
153357416Smarkm	return(0);
153457416Smarkm}
153557416Smarkm
153657416Smarkmvoid
153757416Smarkmenv_opt_end(int emptyok)
153857416Smarkm{
153957416Smarkm	int len;
154057416Smarkm
154157416Smarkm	len = opt_replyp - opt_reply + 2;
154257416Smarkm	if (emptyok || len > 6) {
154357416Smarkm		*opt_replyp++ = IAC;
154457416Smarkm		*opt_replyp++ = SE;
154557416Smarkm		if (NETROOM() > len) {
154657416Smarkm			ring_supply_data(&netoring, opt_reply, len);
154757416Smarkm			printsub('>', &opt_reply[2], len - 2);
154857416Smarkm		}
154957416Smarkm/*@*/		else printf("slc_end_reply: not enough room\n");
155057416Smarkm	}
155157416Smarkm	if (opt_reply) {
155257416Smarkm		free(opt_reply);
155357416Smarkm		opt_reply = opt_replyp = opt_replyend = NULL;
155457416Smarkm	}
155557416Smarkm}
155657416Smarkm
155757416Smarkm
155857416Smarkm
155957416Smarkmint
156057416Smarkmtelrcv(void)
156157416Smarkm{
156257416Smarkm    int c;
156357416Smarkm    int scc;
156457416Smarkm    unsigned char *sbp = NULL;
156557416Smarkm    int count;
156657416Smarkm    int returnValue = 0;
156757416Smarkm
156857416Smarkm    scc = 0;
156957416Smarkm    count = 0;
157057416Smarkm    while (TTYROOM() > 2) {
157157416Smarkm	if (scc == 0) {
157257416Smarkm	    if (count) {
157357416Smarkm		ring_consumed(&netiring, count);
157457416Smarkm		returnValue = 1;
157557416Smarkm		count = 0;
157657416Smarkm	    }
157757416Smarkm	    sbp = netiring.consume;
157857416Smarkm	    scc = ring_full_consecutive(&netiring);
157957416Smarkm	    if (scc == 0) {
158057416Smarkm		/* No more data coming in */
158157416Smarkm		break;
158257416Smarkm	    }
158357416Smarkm	}
158457416Smarkm
158557416Smarkm	c = *sbp++ & 0xff, scc--; count++;
158657416Smarkm#if	defined(ENCRYPTION)
158757416Smarkm	if (decrypt_input)
158857416Smarkm		c = (*decrypt_input)(c);
158957416Smarkm#endif
159057416Smarkm
159157416Smarkm	switch (telrcv_state) {
159257416Smarkm
159357416Smarkm	case TS_CR:
159457416Smarkm	    telrcv_state = TS_DATA;
159557416Smarkm	    if (c == '\0') {
159657416Smarkm		break;	/* Ignore \0 after CR */
159757416Smarkm	    }
159857416Smarkm	    else if ((c == '\n') && my_want_state_is_dont(TELOPT_ECHO) && !crmod) {
159957416Smarkm		TTYADD(c);
160057416Smarkm		break;
160157416Smarkm	    }
160257416Smarkm	    /* Else, fall through */
160357416Smarkm
160457416Smarkm	case TS_DATA:
160557416Smarkm	    if (c == IAC) {
160657416Smarkm		telrcv_state = TS_IAC;
160757416Smarkm		break;
160857416Smarkm	    }
160957416Smarkm		    /*
161057416Smarkm		     * The 'crmod' hack (see following) is needed
161157416Smarkm		     * since we can't set CRMOD on output only.
161257416Smarkm		     * Machines like MULTICS like to send \r without
161357416Smarkm		     * \n; since we must turn off CRMOD to get proper
161457416Smarkm		     * input, the mapping is done here (sigh).
161557416Smarkm		     */
161657416Smarkm	    if ((c == '\r') && my_want_state_is_dont(TELOPT_BINARY)) {
161757416Smarkm		if (scc > 0) {
161857416Smarkm		    c = *sbp&0xff;
161957416Smarkm#if	defined(ENCRYPTION)
162057416Smarkm		    if (decrypt_input)
162157416Smarkm			c = (*decrypt_input)(c);
162257416Smarkm#endif
162357416Smarkm		    if (c == 0) {
162457416Smarkm			sbp++, scc--; count++;
162557416Smarkm			/* a "true" CR */
162657416Smarkm			TTYADD('\r');
162757416Smarkm		    } else if (my_want_state_is_dont(TELOPT_ECHO) &&
162857416Smarkm					(c == '\n')) {
162957416Smarkm			sbp++, scc--; count++;
163057416Smarkm			TTYADD('\n');
163157416Smarkm		    } else {
163257416Smarkm#if	defined(ENCRYPTION)
163357416Smarkm		        if (decrypt_input)
163457416Smarkm			    (*decrypt_input)(-1);
163557416Smarkm#endif
163657416Smarkm
163757416Smarkm			TTYADD('\r');
163857416Smarkm			if (crmod) {
163957416Smarkm				TTYADD('\n');
164057416Smarkm			}
164157416Smarkm		    }
164257416Smarkm		} else {
164357416Smarkm		    telrcv_state = TS_CR;
164457416Smarkm		    TTYADD('\r');
164557416Smarkm		    if (crmod) {
164657416Smarkm			    TTYADD('\n');
164757416Smarkm		    }
164857416Smarkm		}
164957416Smarkm	    } else {
165057416Smarkm		TTYADD(c);
165157416Smarkm	    }
165257416Smarkm	    continue;
165357416Smarkm
165457416Smarkm	case TS_IAC:
165557416Smarkmprocess_iac:
165657416Smarkm	    switch (c) {
165757416Smarkm
165857416Smarkm	    case WILL:
165957416Smarkm		telrcv_state = TS_WILL;
166057416Smarkm		continue;
166157416Smarkm
166257416Smarkm	    case WONT:
166357416Smarkm		telrcv_state = TS_WONT;
166457416Smarkm		continue;
166557416Smarkm
166657416Smarkm	    case DO:
166757416Smarkm		telrcv_state = TS_DO;
166857416Smarkm		continue;
166957416Smarkm
167057416Smarkm	    case DONT:
167157416Smarkm		telrcv_state = TS_DONT;
167257416Smarkm		continue;
167357416Smarkm
167457416Smarkm	    case DM:
167557416Smarkm		    /*
167657416Smarkm		     * We may have missed an urgent notification,
167757416Smarkm		     * so make sure we flush whatever is in the
167857416Smarkm		     * buffer currently.
167957416Smarkm		     */
168057416Smarkm		printoption("RCVD", IAC, DM);
168157416Smarkm		SYNCHing = 1;
168257416Smarkm		ttyflush(1);
168357416Smarkm		SYNCHing = stilloob();
168457416Smarkm		settimer(gotDM);
168557416Smarkm		break;
168657416Smarkm
168757416Smarkm	    case SB:
168857416Smarkm		SB_CLEAR();
168957416Smarkm		telrcv_state = TS_SB;
169057416Smarkm		continue;
169157416Smarkm
169257416Smarkm
169357416Smarkm	    case IAC:
169457416Smarkm		TTYADD(IAC);
169557416Smarkm		break;
169657416Smarkm
169757416Smarkm	    case NOP:
169857416Smarkm	    case GA:
169957416Smarkm	    default:
170057416Smarkm		printoption("RCVD", IAC, c);
170157416Smarkm		break;
170257416Smarkm	    }
170357416Smarkm	    telrcv_state = TS_DATA;
170457416Smarkm	    continue;
170557416Smarkm
170657416Smarkm	case TS_WILL:
170757416Smarkm	    printoption("RCVD", WILL, c);
170857416Smarkm	    willoption(c);
170957416Smarkm	    telrcv_state = TS_DATA;
171057416Smarkm	    continue;
171157416Smarkm
171257416Smarkm	case TS_WONT:
171357416Smarkm	    printoption("RCVD", WONT, c);
171457416Smarkm	    wontoption(c);
171557416Smarkm	    telrcv_state = TS_DATA;
171657416Smarkm	    continue;
171757416Smarkm
171857416Smarkm	case TS_DO:
171957416Smarkm	    printoption("RCVD", DO, c);
172057416Smarkm	    dooption(c);
172157416Smarkm	    if (c == TELOPT_NAWS) {
172257416Smarkm		sendnaws();
172357416Smarkm	    } else if (c == TELOPT_LFLOW) {
172457416Smarkm		localflow = 1;
172557416Smarkm		setcommandmode();
172657416Smarkm		setconnmode(0);
172757416Smarkm	    }
172857416Smarkm	    telrcv_state = TS_DATA;
172957416Smarkm	    continue;
173057416Smarkm
173157416Smarkm	case TS_DONT:
173257416Smarkm	    printoption("RCVD", DONT, c);
173357416Smarkm	    dontoption(c);
173457416Smarkm	    flushline = 1;
173557416Smarkm	    setconnmode(0);	/* set new tty mode (maybe) */
173657416Smarkm	    telrcv_state = TS_DATA;
173757416Smarkm	    continue;
173857416Smarkm
173957416Smarkm	case TS_SB:
174057416Smarkm	    if (c == IAC) {
174157416Smarkm		telrcv_state = TS_SE;
174257416Smarkm	    } else {
174357416Smarkm		SB_ACCUM(c);
174457416Smarkm	    }
174557416Smarkm	    continue;
174657416Smarkm
174757416Smarkm	case TS_SE:
174857416Smarkm	    if (c != SE) {
174957416Smarkm		if (c != IAC) {
175057416Smarkm		    /*
175157416Smarkm		     * This is an error.  We only expect to get
175257416Smarkm		     * "IAC IAC" or "IAC SE".  Several things may
175357416Smarkm		     * have happend.  An IAC was not doubled, the
175457416Smarkm		     * IAC SE was left off, or another option got
175557416Smarkm		     * inserted into the suboption are all possibilities.
175657416Smarkm		     * If we assume that the IAC was not doubled,
175757416Smarkm		     * and really the IAC SE was left off, we could
175857416Smarkm		     * get into an infinate loop here.  So, instead,
175957416Smarkm		     * we terminate the suboption, and process the
176057416Smarkm		     * partial suboption if we can.
176157416Smarkm		     */
176257416Smarkm		    SB_ACCUM(IAC);
176357416Smarkm		    SB_ACCUM(c);
176457416Smarkm		    subpointer -= 2;
176557416Smarkm		    SB_TERM();
176657416Smarkm
176757416Smarkm		    printoption("In SUBOPTION processing, RCVD", IAC, c);
176857416Smarkm		    suboption();	/* handle sub-option */
176957416Smarkm		    telrcv_state = TS_IAC;
177057416Smarkm		    goto process_iac;
177157416Smarkm		}
177257416Smarkm		SB_ACCUM(c);
177357416Smarkm		telrcv_state = TS_SB;
177457416Smarkm	    } else {
177557416Smarkm		SB_ACCUM(IAC);
177657416Smarkm		SB_ACCUM(SE);
177757416Smarkm		subpointer -= 2;
177857416Smarkm		SB_TERM();
177957416Smarkm		suboption();	/* handle sub-option */
178057416Smarkm		telrcv_state = TS_DATA;
178157416Smarkm	    }
178257416Smarkm	}
178357416Smarkm    }
178457416Smarkm    if (count)
178557416Smarkm	ring_consumed(&netiring, count);
178657416Smarkm    return returnValue||count;
178757416Smarkm}
178857416Smarkm
178957416Smarkmstatic int bol = 1, local = 0;
179057416Smarkm
179157416Smarkmint
179257416Smarkmrlogin_susp(void)
179357416Smarkm{
179457416Smarkm    if (local) {
179557416Smarkm	local = 0;
179657416Smarkm	bol = 1;
179757416Smarkm	command(0, "z\n", 2);
179857416Smarkm	return(1);
179957416Smarkm    }
180057416Smarkm    return(0);
180157416Smarkm}
180257416Smarkm
180357416Smarkmstatic int
180457416Smarkmtelsnd()
180557416Smarkm{
180657416Smarkm    int tcc;
180757416Smarkm    int count;
180857416Smarkm    int returnValue = 0;
180957416Smarkm    unsigned char *tbp = NULL;
181057416Smarkm
181157416Smarkm    tcc = 0;
181257416Smarkm    count = 0;
181357416Smarkm    while (NETROOM() > 2) {
181457416Smarkm	int sc;
181557416Smarkm	int c;
181657416Smarkm
181757416Smarkm	if (tcc == 0) {
181857416Smarkm	    if (count) {
181957416Smarkm		ring_consumed(&ttyiring, count);
182057416Smarkm		returnValue = 1;
182157416Smarkm		count = 0;
182257416Smarkm	    }
182357416Smarkm	    tbp = ttyiring.consume;
182457416Smarkm	    tcc = ring_full_consecutive(&ttyiring);
182557416Smarkm	    if (tcc == 0) {
182657416Smarkm		break;
182757416Smarkm	    }
182857416Smarkm	}
182957416Smarkm	c = *tbp++ & 0xff, sc = strip(c), tcc--; count++;
183057416Smarkm	if (rlogin != _POSIX_VDISABLE) {
183157416Smarkm		if (bol) {
183257416Smarkm			bol = 0;
183357416Smarkm			if (sc == rlogin) {
183457416Smarkm				local = 1;
183557416Smarkm				continue;
183657416Smarkm			}
183757416Smarkm		} else if (local) {
183857416Smarkm			local = 0;
183957416Smarkm			if (sc == '.' || c == termEofChar) {
184057416Smarkm				bol = 1;
184157416Smarkm				command(0, "close\n", 6);
184257416Smarkm				continue;
184357416Smarkm			}
184457416Smarkm			if (sc == termSuspChar) {
184557416Smarkm				bol = 1;
184657416Smarkm				command(0, "z\n", 2);
184757416Smarkm				continue;
184857416Smarkm			}
184957416Smarkm			if (sc == escape) {
185057416Smarkm				command(0, (char *)tbp, tcc);
185157416Smarkm				bol = 1;
185257416Smarkm				count += tcc;
185357416Smarkm				tcc = 0;
185457416Smarkm				flushline = 1;
185557416Smarkm				break;
185657416Smarkm			}
185757416Smarkm			if (sc != rlogin) {
185857416Smarkm				++tcc;
185957416Smarkm				--tbp;
186057416Smarkm				--count;
186157416Smarkm				c = sc = rlogin;
186257416Smarkm			}
186357416Smarkm		}
186457416Smarkm		if ((sc == '\n') || (sc == '\r'))
186557416Smarkm			bol = 1;
186657416Smarkm	} else if (sc == escape) {
186757416Smarkm	    /*
186857416Smarkm	     * Double escape is a pass through of a single escape character.
186957416Smarkm	     */
187057416Smarkm	    if (tcc && strip(*tbp) == escape) {
187157416Smarkm		tbp++;
187257416Smarkm		tcc--;
187357416Smarkm		count++;
187457416Smarkm		bol = 0;
187557416Smarkm	    } else {
187657416Smarkm		command(0, (char *)tbp, tcc);
187757416Smarkm		bol = 1;
187857416Smarkm		count += tcc;
187957416Smarkm		tcc = 0;
188057416Smarkm		flushline = 1;
188157416Smarkm		break;
188257416Smarkm	    }
188357416Smarkm	} else
188457416Smarkm	    bol = 0;
188557416Smarkm#ifdef	KLUDGELINEMODE
188657416Smarkm	if (kludgelinemode && (globalmode&MODE_EDIT) && (sc == echoc)) {
188757416Smarkm	    if (tcc > 0 && strip(*tbp) == echoc) {
188857416Smarkm		tcc--; tbp++; count++;
188957416Smarkm	    } else {
189057416Smarkm		dontlecho = !dontlecho;
189157416Smarkm		settimer(echotoggle);
189257416Smarkm		setconnmode(0);
189357416Smarkm		flushline = 1;
189457416Smarkm		break;
189557416Smarkm	    }
189657416Smarkm	}
189757416Smarkm#endif
189857416Smarkm	if (MODE_LOCAL_CHARS(globalmode)) {
189957416Smarkm	    if (TerminalSpecialChars(sc) == 0) {
190057416Smarkm		bol = 1;
190157416Smarkm		break;
190257416Smarkm	    }
190357416Smarkm	}
190457416Smarkm	if (my_want_state_is_wont(TELOPT_BINARY)) {
190557416Smarkm	    switch (c) {
190657416Smarkm	    case '\n':
190757416Smarkm		    /*
190857416Smarkm		     * If we are in CRMOD mode (\r ==> \n)
190957416Smarkm		     * on our local machine, then probably
191057416Smarkm		     * a newline (unix) is CRLF (TELNET).
191157416Smarkm		     */
191257416Smarkm		if (MODE_LOCAL_CHARS(globalmode)) {
191357416Smarkm		    NETADD('\r');
191457416Smarkm		}
191557416Smarkm		NETADD('\n');
191657416Smarkm		bol = flushline = 1;
191757416Smarkm		break;
191857416Smarkm	    case '\r':
191957416Smarkm		if (!crlf) {
192057416Smarkm		    NET2ADD('\r', '\0');
192157416Smarkm		} else {
192257416Smarkm		    NET2ADD('\r', '\n');
192357416Smarkm		}
192457416Smarkm		bol = flushline = 1;
192557416Smarkm		break;
192657416Smarkm	    case IAC:
192757416Smarkm		NET2ADD(IAC, IAC);
192857416Smarkm		break;
192957416Smarkm	    default:
193057416Smarkm		NETADD(c);
193157416Smarkm		break;
193257416Smarkm	    }
193357416Smarkm	} else if (c == IAC) {
193457416Smarkm	    NET2ADD(IAC, IAC);
193557416Smarkm	} else {
193657416Smarkm	    NETADD(c);
193757416Smarkm	}
193857416Smarkm    }
193957416Smarkm    if (count)
194057416Smarkm	ring_consumed(&ttyiring, count);
194157416Smarkm    return returnValue||count;		/* Non-zero if we did anything */
194257416Smarkm}
194357416Smarkm
194457416Smarkm/*
194557416Smarkm * Scheduler()
194657416Smarkm *
194757416Smarkm * Try to do something.
194857416Smarkm *
194957416Smarkm * If we do something useful, return 1; else return 0.
195057416Smarkm *
195157416Smarkm */
195257416Smarkm
195357416Smarkm
195457416Smarkmstatic int
195557416SmarkmScheduler(int block) /* should we block in the select ? */
195657416Smarkm{
195757416Smarkm		/* One wants to be a bit careful about setting returnValue
195857416Smarkm		 * to one, since a one implies we did some useful work,
195957416Smarkm		 * and therefore probably won't be called to block next
196057416Smarkm		 * time (TN3270 mode only).
196157416Smarkm		 */
196257416Smarkm    int returnValue;
196357416Smarkm    int netin, netout, netex, ttyin, ttyout;
196457416Smarkm
196557416Smarkm    /* Decide which rings should be processed */
196657416Smarkm
196757416Smarkm    netout = ring_full_count(&netoring) &&
196857416Smarkm	    (flushline ||
196957416Smarkm		(my_want_state_is_wont(TELOPT_LINEMODE)
197057416Smarkm#ifdef	KLUDGELINEMODE
197157416Smarkm			&& (!kludgelinemode || my_want_state_is_do(TELOPT_SGA))
197257416Smarkm#endif
197357416Smarkm		) ||
197457416Smarkm			my_want_state_is_will(TELOPT_BINARY));
197557416Smarkm    ttyout = ring_full_count(&ttyoring);
197657416Smarkm
197757416Smarkm    ttyin = ring_empty_count(&ttyiring);
197857416Smarkm
197957416Smarkm    netin = !ISend && ring_empty_count(&netiring);
198057416Smarkm
198157416Smarkm    netex = !SYNCHing;
198257416Smarkm
198357416Smarkm    /* If we have seen a signal recently, reset things */
198457416Smarkm
198557416Smarkm    /* Call to system code to process rings */
198657416Smarkm
198757416Smarkm    returnValue = process_rings(netin, netout, netex, ttyin, ttyout, !block);
198857416Smarkm
198957416Smarkm    /* Now, look at the input rings, looking for work to do. */
199057416Smarkm
199157416Smarkm    if (ring_full_count(&ttyiring)) {
199257416Smarkm	    returnValue |= telsnd();
199357416Smarkm    }
199457416Smarkm
199557416Smarkm    if (ring_full_count(&netiring)) {
199657416Smarkm	returnValue |= telrcv();
199757416Smarkm    }
199857416Smarkm    return returnValue;
199957416Smarkm}
200057416Smarkm
200157416Smarkm/*
200257416Smarkm * Select from tty and network...
200357416Smarkm */
200457416Smarkmvoid
200557416Smarkmmy_telnet(char *user)
200657416Smarkm{
200757416Smarkm    sys_telnet_init();
200857416Smarkm
200957416Smarkm#if	defined(AUTHENTICATION) || defined(ENCRYPTION)
201057416Smarkm    {
201157416Smarkm	static char local_host[256] = { 0 };
201257416Smarkm
201357416Smarkm	if (!local_host[0]) {
201457416Smarkm		/* XXX - should be k_gethostname? */
201557416Smarkm		gethostname(local_host, sizeof(local_host));
201657416Smarkm		local_host[sizeof(local_host)-1] = 0;
201757416Smarkm	}
201857416Smarkm	auth_encrypt_init(local_host, hostname, "TELNET", 0);
201957416Smarkm	auth_encrypt_user(user);
202057416Smarkm    }
202157416Smarkm#endif
202257416Smarkm    if (telnetport) {
202357416Smarkm#if	defined(AUTHENTICATION)
202457416Smarkm	if (autologin)
202557416Smarkm		send_will(TELOPT_AUTHENTICATION, 1);
202657416Smarkm#endif
202757416Smarkm#if	defined(ENCRYPTION)
202857416Smarkm	send_do(TELOPT_ENCRYPT, 1);
202957416Smarkm	send_will(TELOPT_ENCRYPT, 1);
203057416Smarkm#endif
203157416Smarkm	send_do(TELOPT_SGA, 1);
203257416Smarkm	send_will(TELOPT_TTYPE, 1);
203357416Smarkm	send_will(TELOPT_NAWS, 1);
203457416Smarkm	send_will(TELOPT_TSPEED, 1);
203557416Smarkm	send_will(TELOPT_LFLOW, 1);
203657416Smarkm	send_will(TELOPT_LINEMODE, 1);
203757416Smarkm	send_will(TELOPT_NEW_ENVIRON, 1);
203857416Smarkm	send_do(TELOPT_STATUS, 1);
203957416Smarkm	if (env_getvalue((unsigned char *)"DISPLAY"))
204057416Smarkm	    send_will(TELOPT_XDISPLOC, 1);
204157416Smarkm	if (binary)
204257416Smarkm	    tel_enter_binary(binary);
204357416Smarkm    }
204457416Smarkm
204557416Smarkm    for (;;) {
204657416Smarkm	int schedValue;
204757416Smarkm
204857416Smarkm	while ((schedValue = Scheduler(0)) != 0) {
204957416Smarkm	    if (schedValue == -1) {
205057416Smarkm		setcommandmode();
205157416Smarkm		return;
205257416Smarkm	    }
205357416Smarkm	}
205457416Smarkm
205557416Smarkm	if (Scheduler(1) == -1) {
205657416Smarkm	    setcommandmode();
205757416Smarkm	    return;
205857416Smarkm	}
205957416Smarkm    }
206057416Smarkm}
206157416Smarkm
206257416Smarkm/*
206357416Smarkm * netclear()
206457416Smarkm *
206557416Smarkm *	We are about to do a TELNET SYNCH operation.  Clear
206657416Smarkm * the path to the network.
206757416Smarkm *
206857416Smarkm *	Things are a bit tricky since we may have sent the first
206957416Smarkm * byte or so of a previous TELNET command into the network.
207057416Smarkm * So, we have to scan the network buffer from the beginning
207157416Smarkm * until we are up to where we want to be.
207257416Smarkm *
207357416Smarkm *	A side effect of what we do, just to keep things
207457416Smarkm * simple, is to clear the urgent data pointer.  The principal
207557416Smarkm * caller should be setting the urgent data pointer AFTER calling
207657416Smarkm * us in any case.
207757416Smarkm */
207857416Smarkm
207957416Smarkmstatic void
208057416Smarkmnetclear()
208157416Smarkm{
208257416Smarkm#if	0	/* XXX */
208357416Smarkm    char *thisitem, *next;
208457416Smarkm    char *good;
208557416Smarkm#define	wewant(p)	((nfrontp > p) && ((*p&0xff) == IAC) && \
208657416Smarkm				((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
208757416Smarkm
208857416Smarkm    thisitem = netobuf;
208957416Smarkm
209057416Smarkm    while ((next = nextitem(thisitem)) <= netobuf.send) {
209157416Smarkm	thisitem = next;
209257416Smarkm    }
209357416Smarkm
209457416Smarkm    /* Now, thisitem is first before/at boundary. */
209557416Smarkm
209657416Smarkm    good = netobuf;	/* where the good bytes go */
209757416Smarkm
209857416Smarkm    while (netoring.add > thisitem) {
209957416Smarkm	if (wewant(thisitem)) {
210057416Smarkm	    int length;
210157416Smarkm
210257416Smarkm	    next = thisitem;
210357416Smarkm	    do {
210457416Smarkm		next = nextitem(next);
210557416Smarkm	    } while (wewant(next) && (nfrontp > next));
210657416Smarkm	    length = next-thisitem;
210757416Smarkm	    memmove(good, thisitem, length);
210857416Smarkm	    good += length;
210957416Smarkm	    thisitem = next;
211057416Smarkm	} else {
211157416Smarkm	    thisitem = nextitem(thisitem);
211257416Smarkm	}
211357416Smarkm    }
211457416Smarkm
211557416Smarkm#endif	/* 0 */
211657416Smarkm}
211757416Smarkm
211857416Smarkm/*
211957416Smarkm * These routines add various telnet commands to the data stream.
212057416Smarkm */
212157416Smarkm
212257416Smarkmstatic void
212357416Smarkmdoflush()
212457416Smarkm{
212557416Smarkm    NET2ADD(IAC, DO);
212657416Smarkm    NETADD(TELOPT_TM);
212757416Smarkm    flushline = 1;
212857416Smarkm    flushout = 1;
212957416Smarkm    ttyflush(1);			/* Flush/drop output */
213057416Smarkm    /* do printoption AFTER flush, otherwise the output gets tossed... */
213157416Smarkm    printoption("SENT", DO, TELOPT_TM);
213257416Smarkm}
213357416Smarkm
213457416Smarkmvoid
213557416SmarkmxmitAO(void)
213657416Smarkm{
213757416Smarkm    NET2ADD(IAC, AO);
213857416Smarkm    printoption("SENT", IAC, AO);
213957416Smarkm    if (autoflush) {
214057416Smarkm	doflush();
214157416Smarkm    }
214257416Smarkm}
214357416Smarkm
214457416Smarkm
214557416Smarkmvoid
214657416SmarkmxmitEL(void)
214757416Smarkm{
214857416Smarkm    NET2ADD(IAC, EL);
214957416Smarkm    printoption("SENT", IAC, EL);
215057416Smarkm}
215157416Smarkm
215257416Smarkmvoid
215357416SmarkmxmitEC(void)
215457416Smarkm{
215557416Smarkm    NET2ADD(IAC, EC);
215657416Smarkm    printoption("SENT", IAC, EC);
215757416Smarkm}
215857416Smarkm
215957416Smarkm
216057416Smarkmint
216157416Smarkmdosynch()
216257416Smarkm{
216357416Smarkm    netclear();			/* clear the path to the network */
216457416Smarkm    NETADD(IAC);
216557416Smarkm    setneturg();
216657416Smarkm    NETADD(DM);
216757416Smarkm    printoption("SENT", IAC, DM);
216857416Smarkm    return 1;
216957416Smarkm}
217057416Smarkm
217157416Smarkmint want_status_response = 0;
217257416Smarkm
217357416Smarkmint
217457416Smarkmget_status()
217557416Smarkm{
217657416Smarkm    unsigned char tmp[16];
217757416Smarkm    unsigned char *cp;
217857416Smarkm
217957416Smarkm    if (my_want_state_is_dont(TELOPT_STATUS)) {
218057416Smarkm	printf("Remote side does not support STATUS option\n");
218157416Smarkm	return 0;
218257416Smarkm    }
218357416Smarkm    cp = tmp;
218457416Smarkm
218557416Smarkm    *cp++ = IAC;
218657416Smarkm    *cp++ = SB;
218757416Smarkm    *cp++ = TELOPT_STATUS;
218857416Smarkm    *cp++ = TELQUAL_SEND;
218957416Smarkm    *cp++ = IAC;
219057416Smarkm    *cp++ = SE;
219157416Smarkm    if (NETROOM() >= cp - tmp) {
219257416Smarkm	ring_supply_data(&netoring, tmp, cp-tmp);
219357416Smarkm	printsub('>', tmp+2, cp - tmp - 2);
219457416Smarkm    }
219557416Smarkm    ++want_status_response;
219657416Smarkm    return 1;
219757416Smarkm}
219857416Smarkm
219957416Smarkmvoid
220057416Smarkmintp(void)
220157416Smarkm{
220257416Smarkm    NET2ADD(IAC, IP);
220357416Smarkm    printoption("SENT", IAC, IP);
220457416Smarkm    flushline = 1;
220557416Smarkm    if (autoflush) {
220657416Smarkm	doflush();
220757416Smarkm    }
220857416Smarkm    if (autosynch) {
220957416Smarkm	dosynch();
221057416Smarkm    }
221157416Smarkm}
221257416Smarkm
221357416Smarkmvoid
221457416Smarkmsendbrk(void)
221557416Smarkm{
221657416Smarkm    NET2ADD(IAC, BREAK);
221757416Smarkm    printoption("SENT", IAC, BREAK);
221857416Smarkm    flushline = 1;
221957416Smarkm    if (autoflush) {
222057416Smarkm	doflush();
222157416Smarkm    }
222257416Smarkm    if (autosynch) {
222357416Smarkm	dosynch();
222457416Smarkm    }
222557416Smarkm}
222657416Smarkm
222757416Smarkmvoid
222857416Smarkmsendabort(void)
222957416Smarkm{
223057416Smarkm    NET2ADD(IAC, ABORT);
223157416Smarkm    printoption("SENT", IAC, ABORT);
223257416Smarkm    flushline = 1;
223357416Smarkm    if (autoflush) {
223457416Smarkm	doflush();
223557416Smarkm    }
223657416Smarkm    if (autosynch) {
223757416Smarkm	dosynch();
223857416Smarkm    }
223957416Smarkm}
224057416Smarkm
224157416Smarkmvoid
224257416Smarkmsendsusp(void)
224357416Smarkm{
224457416Smarkm    NET2ADD(IAC, SUSP);
224557416Smarkm    printoption("SENT", IAC, SUSP);
224657416Smarkm    flushline = 1;
224757416Smarkm    if (autoflush) {
224857416Smarkm	doflush();
224957416Smarkm    }
225057416Smarkm    if (autosynch) {
225157416Smarkm	dosynch();
225257416Smarkm    }
225357416Smarkm}
225457416Smarkm
225557416Smarkmvoid
225657416Smarkmsendeof(void)
225757416Smarkm{
225857416Smarkm    NET2ADD(IAC, xEOF);
225957416Smarkm    printoption("SENT", IAC, xEOF);
226057416Smarkm}
226157416Smarkm
226257416Smarkmvoid
226357416Smarkmsendayt(void)
226457416Smarkm{
226557416Smarkm    NET2ADD(IAC, AYT);
226657416Smarkm    printoption("SENT", IAC, AYT);
226757416Smarkm}
226857416Smarkm
226957416Smarkm/*
227057416Smarkm * Send a window size update to the remote system.
227157416Smarkm */
227257416Smarkm
227357416Smarkmvoid
227457416Smarkmsendnaws()
227557416Smarkm{
227657416Smarkm    long rows, cols;
227757416Smarkm    unsigned char tmp[16];
227857416Smarkm    unsigned char *cp;
227957416Smarkm
228057416Smarkm    if (my_state_is_wont(TELOPT_NAWS))
228157416Smarkm	return;
228257416Smarkm
228357416Smarkm#define	PUTSHORT(cp, x) { if ((*cp++ = ((x)>>8)&0xff) == IAC) *cp++ = IAC; \
228457416Smarkm			    if ((*cp++ = ((x))&0xff) == IAC) *cp++ = IAC; }
228557416Smarkm
228657416Smarkm    if (TerminalWindowSize(&rows, &cols) == 0) {	/* Failed */
228757416Smarkm	return;
228857416Smarkm    }
228957416Smarkm
229057416Smarkm    cp = tmp;
229157416Smarkm
229257416Smarkm    *cp++ = IAC;
229357416Smarkm    *cp++ = SB;
229457416Smarkm    *cp++ = TELOPT_NAWS;
229557416Smarkm    PUTSHORT(cp, cols);
229657416Smarkm    PUTSHORT(cp, rows);
229757416Smarkm    *cp++ = IAC;
229857416Smarkm    *cp++ = SE;
229957416Smarkm    if (NETROOM() >= cp - tmp) {
230057416Smarkm	ring_supply_data(&netoring, tmp, cp-tmp);
230157416Smarkm	printsub('>', tmp+2, cp - tmp - 2);
230257416Smarkm    }
230357416Smarkm}
230457416Smarkm
230557416Smarkmvoid
230657416Smarkmtel_enter_binary(int rw)
230757416Smarkm{
230857416Smarkm    if (rw&1)
230957416Smarkm	send_do(TELOPT_BINARY, 1);
231057416Smarkm    if (rw&2)
231157416Smarkm	send_will(TELOPT_BINARY, 1);
231257416Smarkm}
231357416Smarkm
231457416Smarkmvoid
231557416Smarkmtel_leave_binary(int rw)
231657416Smarkm{
231757416Smarkm    if (rw&1)
231857416Smarkm	send_dont(TELOPT_BINARY, 1);
231957416Smarkm    if (rw&2)
232057416Smarkm	send_wont(TELOPT_BINARY, 1);
232157416Smarkm}
2322