Initial import from FreeBSD RELENG_4:
[dragonfly.git] / crypto / heimdal / appl / telnet / telnet / commands.c
1 /*
2  * Copyright (c) 1988, 1990, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by the University of
16  *      California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33
34 #include "telnet_locl.h"
35
36 RCSID("$Id: commands.c,v 1.72 2002/08/28 21:04:59 joda Exp $");
37
38 #if     defined(IPPROTO_IP) && defined(IP_TOS)
39 int tos = -1;
40 #endif  /* defined(IPPROTO_IP) && defined(IP_TOS) */
41
42 char    *hostname;
43 static char _hostname[MaxHostNameLen];
44
45 typedef int (*intrtn_t)(int, char**);
46 static int call(intrtn_t, ...);
47
48 typedef struct {
49         char    *name;          /* command name */
50         char    *help;          /* help string (NULL for no help) */
51         int     (*handler)();   /* routine which executes command */
52         int     needconnect;    /* Do we need to be connected to execute? */
53 } Command;
54
55 static char line[256];
56 static char saveline[256];
57 static int margc;
58 static char *margv[20];
59
60 static void
61 makeargv()
62 {
63     char *cp, *cp2, c;
64     char **argp = margv;
65
66     margc = 0;
67     cp = line;
68     if (*cp == '!') {           /* Special case shell escape */
69         /* save for shell command */
70         strlcpy(saveline, line, sizeof(saveline));
71         *argp++ = "!";          /* No room in string to get this */
72         margc++;
73         cp++;
74     }
75     while ((c = *cp)) {
76         int inquote = 0;
77         while (isspace(c))
78             c = *++cp;
79         if (c == '\0')
80             break;
81         *argp++ = cp;
82         margc += 1;
83         for (cp2 = cp; c != '\0'; c = *++cp) {
84             if (inquote) {
85                 if (c == inquote) {
86                     inquote = 0;
87                     continue;
88                 }
89             } else {
90                 if (c == '\\') {
91                     if ((c = *++cp) == '\0')
92                         break;
93                 } else if (c == '"') {
94                     inquote = '"';
95                     continue;
96                 } else if (c == '\'') {
97                     inquote = '\'';
98                     continue;
99                 } else if (isspace(c))
100                     break;
101             }
102             *cp2++ = c;
103         }
104         *cp2 = '\0';
105         if (c == '\0')
106             break;
107         cp++;
108     }
109     *argp++ = 0;
110 }
111
112 /*
113  * Make a character string into a number.
114  *
115  * Todo:  1.  Could take random integers (12, 0x12, 012, 0b1).
116  */
117
118 static char
119 special(char *s)
120 {
121         char c;
122         char b;
123
124         switch (*s) {
125         case '^':
126                 b = *++s;
127                 if (b == '?') {
128                     c = b | 0x40;               /* DEL */
129                 } else {
130                     c = b & 0x1f;
131                 }
132                 break;
133         default:
134                 c = *s;
135                 break;
136         }
137         return c;
138 }
139
140 /*
141  * Construct a control character sequence
142  * for a special character.
143  */
144 static char *
145 control(cc_t c)
146 {
147         static char buf[5];
148         /*
149          * The only way I could get the Sun 3.5 compiler
150          * to shut up about
151          *      if ((unsigned int)c >= 0x80)
152          * was to assign "c" to an unsigned int variable...
153          * Arggg....
154          */
155         unsigned int uic = (unsigned int)c;
156
157         if (uic == 0x7f)
158                 return ("^?");
159         if (c == (cc_t)_POSIX_VDISABLE) {
160                 return "off";
161         }
162         if (uic >= 0x80) {
163                 buf[0] = '\\';
164                 buf[1] = ((c>>6)&07) + '0';
165                 buf[2] = ((c>>3)&07) + '0';
166                 buf[3] = (c&07) + '0';
167                 buf[4] = 0;
168         } else if (uic >= 0x20) {
169                 buf[0] = c;
170                 buf[1] = 0;
171         } else {
172                 buf[0] = '^';
173                 buf[1] = '@'+c;
174                 buf[2] = 0;
175         }
176         return (buf);
177 }
178
179
180
181 /*
182  *      The following are data structures and routines for
183  *      the "send" command.
184  *
185  */
186
187 struct sendlist {
188     char        *name;          /* How user refers to it (case independent) */
189     char        *help;          /* Help information (0 ==> no help) */
190     int         needconnect;    /* Need to be connected */
191     int         narg;           /* Number of arguments */
192     int         (*handler)();   /* Routine to perform (for special ops) */
193     int         nbyte;          /* Number of bytes to send this command */
194     int         what;           /* Character to be sent (<0 ==> special) */
195 };
196 \f
197
198 static int
199         send_esc (void),
200         send_help (void),
201         send_docmd (char *),
202         send_dontcmd (char *),
203         send_willcmd (char *),
204         send_wontcmd (char *);
205
206 static struct sendlist Sendlist[] = {
207     { "ao",     "Send Telnet Abort output",             1, 0, 0, 2, AO },
208     { "ayt",    "Send Telnet 'Are You There'",          1, 0, 0, 2, AYT },
209     { "brk",    "Send Telnet Break",                    1, 0, 0, 2, BREAK },
210     { "break",  0,                                      1, 0, 0, 2, BREAK },
211     { "ec",     "Send Telnet Erase Character",          1, 0, 0, 2, EC },
212     { "el",     "Send Telnet Erase Line",               1, 0, 0, 2, EL },
213     { "escape", "Send current escape character",        1, 0, send_esc, 1, 0 },
214     { "ga",     "Send Telnet 'Go Ahead' sequence",      1, 0, 0, 2, GA },
215     { "ip",     "Send Telnet Interrupt Process",        1, 0, 0, 2, IP },
216     { "intp",   0,                                      1, 0, 0, 2, IP },
217     { "interrupt", 0,                                   1, 0, 0, 2, IP },
218     { "intr",   0,                                      1, 0, 0, 2, IP },
219     { "nop",    "Send Telnet 'No operation'",           1, 0, 0, 2, NOP },
220     { "eor",    "Send Telnet 'End of Record'",          1, 0, 0, 2, EOR },
221     { "abort",  "Send Telnet 'Abort Process'",          1, 0, 0, 2, ABORT },
222     { "susp",   "Send Telnet 'Suspend Process'",        1, 0, 0, 2, SUSP },
223     { "eof",    "Send Telnet End of File Character",    1, 0, 0, 2, xEOF },
224     { "synch",  "Perform Telnet 'Synch operation'",     1, 0, dosynch, 2, 0 },
225     { "getstatus", "Send request for STATUS",           1, 0, get_status, 6, 0 },
226     { "?",      "Display send options",                 0, 0, send_help, 0, 0 },
227     { "help",   0,                                      0, 0, send_help, 0, 0 },
228     { "do",     0,                                      0, 1, send_docmd, 3, 0 },
229     { "dont",   0,                                      0, 1, send_dontcmd, 3, 0 },
230     { "will",   0,                                      0, 1, send_willcmd, 3, 0 },
231     { "wont",   0,                                      0, 1, send_wontcmd, 3, 0 },
232     { 0 }
233 };
234
235 #define GETSEND(name) ((struct sendlist *) genget(name, (char **) Sendlist, \
236                                 sizeof(struct sendlist)))
237
238 static int
239 sendcmd(int argc, char **argv)
240 {
241     int count;          /* how many bytes we are going to need to send */
242     int i;
243     struct sendlist *s; /* pointer to current command */
244     int success = 0;
245     int needconnect = 0;
246
247     if (argc < 2) {
248         printf("need at least one argument for 'send' command\r\n");
249         printf("'send ?' for help\r\n");
250         return 0;
251     }
252     /*
253      * First, validate all the send arguments.
254      * In addition, we see how much space we are going to need, and
255      * whether or not we will be doing a "SYNCH" operation (which
256      * flushes the network queue).
257      */
258     count = 0;
259     for (i = 1; i < argc; i++) {
260         s = GETSEND(argv[i]);
261         if (s == 0) {
262             printf("Unknown send argument '%s'\r\n'send ?' for help.\r\n",
263                         argv[i]);
264             return 0;
265         } else if (Ambiguous(s)) {
266             printf("Ambiguous send argument '%s'\r\n'send ?' for help.\r\n",
267                         argv[i]);
268             return 0;
269         }
270         if (i + s->narg >= argc) {
271             fprintf(stderr,
272             "Need %d argument%s to 'send %s' command.  'send %s ?' for help.\r\n",
273                 s->narg, s->narg == 1 ? "" : "s", s->name, s->name);
274             return 0;
275         }
276         count += s->nbyte;
277         if (s->handler == send_help) {
278             send_help();
279             return 0;
280         }
281
282         i += s->narg;
283         needconnect += s->needconnect;
284     }
285     if (!connected && needconnect) {
286         printf("?Need to be connected first.\r\n");
287         printf("'send ?' for help\r\n");
288         return 0;
289     }
290     /* Now, do we have enough room? */
291     if (NETROOM() < count) {
292         printf("There is not enough room in the buffer TO the network\r\n");
293         printf("to process your request.  Nothing will be done.\r\n");
294         printf("('send synch' will throw away most data in the network\r\n");
295         printf("buffer, if this might help.)\r\n");
296         return 0;
297     }
298     /* OK, they are all OK, now go through again and actually send */
299     count = 0;
300     for (i = 1; i < argc; i++) {
301         if ((s = GETSEND(argv[i])) == 0) {
302             fprintf(stderr, "Telnet 'send' error - argument disappeared!\r\n");
303             quit();
304             /*NOTREACHED*/
305         }
306         if (s->handler) {
307             count++;
308             success += (*s->handler)((s->narg > 0) ? argv[i+1] : 0,
309                                   (s->narg > 1) ? argv[i+2] : 0);
310             i += s->narg;
311         } else {
312             NET2ADD(IAC, s->what);
313             printoption("SENT", IAC, s->what);
314         }
315     }
316     return (count == success);
317 }
318
319 static int
320 send_tncmd(void (*func)(), char *cmd, char *name);
321
322 static int
323 send_esc()
324 {
325     NETADD(escape);
326     return 1;
327 }
328
329 static int
330 send_docmd(char *name)
331 {
332     return(send_tncmd(send_do, "do", name));
333 }
334
335 static int
336 send_dontcmd(char *name)
337 {
338     return(send_tncmd(send_dont, "dont", name));
339 }
340
341 static int
342 send_willcmd(char *name)
343 {
344     return(send_tncmd(send_will, "will", name));
345 }
346
347 static int
348 send_wontcmd(char *name)
349 {
350     return(send_tncmd(send_wont, "wont", name));
351 }
352
353 extern char *telopts[];         /* XXX */
354
355 static int
356 send_tncmd(void (*func)(), char *cmd, char *name)
357 {
358     char **cpp;
359     int val = 0;
360
361     if (isprefix(name, "help") || isprefix(name, "?")) {
362         int col, len;
363
364         printf("Usage: send %s <value|option>\r\n", cmd);
365         printf("\"value\" must be from 0 to 255\r\n");
366         printf("Valid options are:\r\n\t");
367
368         col = 8;
369         for (cpp = telopts; *cpp; cpp++) {
370             len = strlen(*cpp) + 3;
371             if (col + len > 65) {
372                 printf("\r\n\t");
373                 col = 8;
374             }
375             printf(" \"%s\"", *cpp);
376             col += len;
377         }
378         printf("\r\n");
379         return 0;
380     }
381     cpp = genget(name, telopts, sizeof(char *));
382     if (Ambiguous(cpp)) {
383         fprintf(stderr,"'%s': ambiguous argument ('send %s ?' for help).\r\n",
384                                         name, cmd);
385         return 0;
386     }
387     if (cpp) {
388         val = cpp - telopts;
389     } else {
390         char *cp = name;
391
392         while (*cp >= '0' && *cp <= '9') {
393             val *= 10;
394             val += *cp - '0';
395             cp++;
396         }
397         if (*cp != 0) {
398             fprintf(stderr, "'%s': unknown argument ('send %s ?' for help).\r\n",
399                                         name, cmd);
400             return 0;
401         } else if (val < 0 || val > 255) {
402             fprintf(stderr, "'%s': bad value ('send %s ?' for help).\r\n",
403                                         name, cmd);
404             return 0;
405         }
406     }
407     if (!connected) {
408         printf("?Need to be connected first.\r\n");
409         return 0;
410     }
411     (*func)(val, 1);
412     return 1;
413 }
414
415 static int
416 send_help()
417 {
418     struct sendlist *s; /* pointer to current command */
419     for (s = Sendlist; s->name; s++) {
420         if (s->help)
421             printf("%-15s %s\r\n", s->name, s->help);
422     }
423     return(0);
424 }
425 \f
426 /*
427  * The following are the routines and data structures referred
428  * to by the arguments to the "toggle" command.
429  */
430
431 static int
432 lclchars()
433 {
434     donelclchars = 1;
435     return 1;
436 }
437
438 static int
439 togdebug()
440 {
441 #ifndef NOT43
442     if (net > 0 &&
443         (SetSockOpt(net, SOL_SOCKET, SO_DEBUG, debug)) < 0) {
444             perror("setsockopt (SO_DEBUG)");
445     }
446 #else   /* NOT43 */
447     if (debug) {
448         if (net > 0 && SetSockOpt(net, SOL_SOCKET, SO_DEBUG, 0, 0) < 0)
449             perror("setsockopt (SO_DEBUG)");
450     } else
451         printf("Cannot turn off socket debugging\r\n");
452 #endif  /* NOT43 */
453     return 1;
454 }
455
456 #if defined(KRB4) && defined(HAVE_KRB_DISABLE_DEBUG)
457 #include <krb.h>
458
459 static int
460 togkrbdebug(void)
461 {
462     if(krb_debug)
463         krb_enable_debug();
464     else
465         krb_disable_debug();
466     return 1;
467 }
468 #endif
469
470 static int
471 togcrlf()
472 {
473     if (crlf) {
474         printf("Will send carriage returns as telnet <CR><LF>.\r\n");
475     } else {
476         printf("Will send carriage returns as telnet <CR><NUL>.\r\n");
477     }
478     return 1;
479 }
480
481 int binmode;
482
483 static int
484 togbinary(int val)
485 {
486     donebinarytoggle = 1;
487
488     if (val >= 0) {
489         binmode = val;
490     } else {
491         if (my_want_state_is_will(TELOPT_BINARY) &&
492                                 my_want_state_is_do(TELOPT_BINARY)) {
493             binmode = 1;
494         } else if (my_want_state_is_wont(TELOPT_BINARY) &&
495                                 my_want_state_is_dont(TELOPT_BINARY)) {
496             binmode = 0;
497         }
498         val = binmode ? 0 : 1;
499     }
500
501     if (val == 1) {
502         if (my_want_state_is_will(TELOPT_BINARY) &&
503                                         my_want_state_is_do(TELOPT_BINARY)) {
504             printf("Already operating in binary mode with remote host.\r\n");
505         } else {
506             printf("Negotiating binary mode with remote host.\r\n");
507             tel_enter_binary(3);
508         }
509     } else {
510         if (my_want_state_is_wont(TELOPT_BINARY) &&
511                                         my_want_state_is_dont(TELOPT_BINARY)) {
512             printf("Already in network ascii mode with remote host.\r\n");
513         } else {
514             printf("Negotiating network ascii mode with remote host.\r\n");
515             tel_leave_binary(3);
516         }
517     }
518     return 1;
519 }
520
521 static int
522 togrbinary(int val)
523 {
524     donebinarytoggle = 1;
525
526     if (val == -1)
527         val = my_want_state_is_do(TELOPT_BINARY) ? 0 : 1;
528
529     if (val == 1) {
530         if (my_want_state_is_do(TELOPT_BINARY)) {
531             printf("Already receiving in binary mode.\r\n");
532         } else {
533             printf("Negotiating binary mode on input.\r\n");
534             tel_enter_binary(1);
535         }
536     } else {
537         if (my_want_state_is_dont(TELOPT_BINARY)) {
538             printf("Already receiving in network ascii mode.\r\n");
539         } else {
540             printf("Negotiating network ascii mode on input.\r\n");
541             tel_leave_binary(1);
542         }
543     }
544     return 1;
545 }
546
547 static int
548 togxbinary(int val)
549 {
550     donebinarytoggle = 1;
551
552     if (val == -1)
553         val = my_want_state_is_will(TELOPT_BINARY) ? 0 : 1;
554
555     if (val == 1) {
556         if (my_want_state_is_will(TELOPT_BINARY)) {
557             printf("Already transmitting in binary mode.\r\n");
558         } else {
559             printf("Negotiating binary mode on output.\r\n");
560             tel_enter_binary(2);
561         }
562     } else {
563         if (my_want_state_is_wont(TELOPT_BINARY)) {
564             printf("Already transmitting in network ascii mode.\r\n");
565         } else {
566             printf("Negotiating network ascii mode on output.\r\n");
567             tel_leave_binary(2);
568         }
569     }
570     return 1;
571 }
572
573
574 static int togglehelp (void);
575 #if     defined(AUTHENTICATION)
576 extern int auth_togdebug (int);
577 #endif
578 #if     defined(ENCRYPTION)
579 extern int EncryptAutoEnc (int);
580 extern int EncryptAutoDec (int);
581 extern int EncryptDebug (int);
582 extern int EncryptVerbose (int);
583 #endif
584
585 struct togglelist {
586     char        *name;          /* name of toggle */
587     char        *help;          /* help message */
588     int         (*handler)();   /* routine to do actual setting */
589     int         *variable;
590     char        *actionexplanation;
591 };
592
593 static struct togglelist Togglelist[] = {
594     { "autoflush",
595         "flushing of output when sending interrupt characters",
596             0,
597                 &autoflush,
598                     "flush output when sending interrupt characters" },
599     { "autosynch",
600         "automatic sending of interrupt characters in urgent mode",
601             0,
602                 &autosynch,
603                     "send interrupt characters in urgent mode" },
604 #if     defined(AUTHENTICATION)
605     { "autologin",
606         "automatic sending of login and/or authentication info",
607             0,
608                 &autologin,
609                     "send login name and/or authentication information" },
610     { "authdebug",
611         "authentication debugging",
612             auth_togdebug,
613                 0,
614                      "print authentication debugging information" },
615 #endif
616 #if     defined(ENCRYPTION)
617     { "autoencrypt",
618         "automatic encryption of data stream",
619             EncryptAutoEnc,
620                 0,
621                     "automatically encrypt output" },
622     { "autodecrypt",
623         "automatic decryption of data stream",
624             EncryptAutoDec,
625                 0,
626                     "automatically decrypt input" },
627     { "verbose_encrypt",
628         "verbose encryption output",
629             EncryptVerbose,
630                 0,
631                     "print verbose encryption output" },
632     { "encdebug",
633         "encryption debugging",
634             EncryptDebug,
635                 0,
636                     "print encryption debugging information" },
637 #endif
638 #if defined(KRB5)
639     { "forward",
640         "credentials forwarding",
641             kerberos5_set_forward,
642                 0,
643                     "forward credentials" },
644     { "forwardable",
645         "forwardable flag of forwarded credentials",
646             kerberos5_set_forwardable,
647                 0,
648                     "forward forwardable credentials" },
649 #endif
650    { "skiprc",
651         "don't read ~/.telnetrc file",
652             0,
653                 &skiprc,
654                     "skip reading of ~/.telnetrc file" },
655     { "binary",
656         "sending and receiving of binary data",
657             togbinary,
658                 0,
659                     0 },
660     { "inbinary",
661         "receiving of binary data",
662             togrbinary,
663                 0,
664                     0 },
665     { "outbinary",
666         "sending of binary data",
667             togxbinary,
668                 0,
669                     0 },
670     { "crlf",
671         "sending carriage returns as telnet <CR><LF>",
672             togcrlf,
673                 &crlf,
674                     0 },
675     { "crmod",
676         "mapping of received carriage returns",
677             0,
678                 &crmod,
679                     "map carriage return on output" },
680     { "localchars",
681         "local recognition of certain control characters",
682             lclchars,
683                 &localchars,
684                     "recognize certain control characters" },
685     { " ", "", 0 },             /* empty line */
686     { "debug",
687         "debugging",
688             togdebug,
689                 &debug,
690                     "turn on socket level debugging" },
691 #if defined(KRB4) && defined(HAVE_KRB_DISABLE_DEBUG)
692     { "krb_debug",
693       "kerberos 4 debugging",
694       togkrbdebug,
695       &krb_debug,
696       "turn on kerberos 4 debugging" },
697 #endif
698     { "netdata",
699         "printing of hexadecimal network data (debugging)",
700             0,
701                 &netdata,
702                     "print hexadecimal representation of network traffic" },
703     { "prettydump",
704         "output of \"netdata\" to user readable format (debugging)",
705             0,
706                 &prettydump,
707                     "print user readable output for \"netdata\"" },
708     { "options",
709         "viewing of options processing (debugging)",
710             0,
711                 &showoptions,
712                     "show option processing" },
713     { "termdata",
714         "printing of hexadecimal terminal data (debugging)",
715             0,
716                 &termdata,
717                     "print hexadecimal representation of terminal traffic" },
718     { "?",
719         0,
720             togglehelp },
721     { "help",
722         0,
723             togglehelp },
724     { 0 }
725 };
726
727 static int
728 togglehelp()
729 {
730     struct togglelist *c;
731
732     for (c = Togglelist; c->name; c++) {
733         if (c->help) {
734             if (*c->help)
735                 printf("%-15s toggle %s\r\n", c->name, c->help);
736             else
737                 printf("\r\n");
738         }
739     }
740     printf("\r\n");
741     printf("%-15s %s\r\n", "?", "display help information");
742     return 0;
743 }
744
745 static void
746 settogglehelp(int set)
747 {
748     struct togglelist *c;
749
750     for (c = Togglelist; c->name; c++) {
751         if (c->help) {
752             if (*c->help)
753                 printf("%-15s %s %s\r\n", c->name, set ? "enable" : "disable",
754                                                 c->help);
755             else
756                 printf("\r\n");
757         }
758     }
759 }
760
761 #define GETTOGGLE(name) (struct togglelist *) \
762                 genget(name, (char **) Togglelist, sizeof(struct togglelist))
763
764 static int
765 toggle(int argc, char *argv[])
766 {
767     int retval = 1;
768     char *name;
769     struct togglelist *c;
770
771     if (argc < 2) {
772         fprintf(stderr,
773             "Need an argument to 'toggle' command.  'toggle ?' for help.\r\n");
774         return 0;
775     }
776     argc--;
777     argv++;
778     while (argc--) {
779         name = *argv++;
780         c = GETTOGGLE(name);
781         if (Ambiguous(c)) {
782             fprintf(stderr, "'%s': ambiguous argument ('toggle ?' for help).\r\n",
783                                         name);
784             return 0;
785         } else if (c == 0) {
786             fprintf(stderr, "'%s': unknown argument ('toggle ?' for help).\r\n",
787                                         name);
788             return 0;
789         } else {
790             if (c->variable) {
791                 *c->variable = !*c->variable;           /* invert it */
792                 if (c->actionexplanation) {
793                     printf("%s %s.\r\n", *c->variable? "Will" : "Won't",
794                                                         c->actionexplanation);
795                 }
796             }
797             if (c->handler) {
798                 retval &= (*c->handler)(-1);
799             }
800         }
801     }
802     return retval;
803 }
804 \f
805 /*
806  * The following perform the "set" command.
807  */
808
809 struct termios new_tc = { 0 };
810
811 struct setlist {
812     char *name;                         /* name */
813     char *help;                         /* help information */
814     void (*handler)();
815     cc_t *charp;                        /* where it is located at */
816 };
817
818 static struct setlist Setlist[] = {
819 #ifdef  KLUDGELINEMODE
820     { "echo",   "character to toggle local echoing on/off", 0, &echoc },
821 #endif
822     { "escape", "character to escape back to telnet command mode", 0, &escape },
823     { "rlogin", "rlogin escape character", 0, &rlogin },
824     { "tracefile", "file to write trace information to", SetNetTrace, (cc_t *)NetTraceFile},
825     { " ", "" },
826     { " ", "The following need 'localchars' to be toggled true", 0, 0 },
827     { "flushoutput", "character to cause an Abort Output", 0, &termFlushChar },
828     { "interrupt", "character to cause an Interrupt Process", 0, &termIntChar },
829     { "quit",   "character to cause an Abort process", 0, &termQuitChar },
830     { "eof",    "character to cause an EOF ", 0, &termEofChar },
831     { " ", "" },
832     { " ", "The following are for local editing in linemode", 0, 0 },
833     { "erase",  "character to use to erase a character", 0, &termEraseChar },
834     { "kill",   "character to use to erase a line", 0, &termKillChar },
835     { "lnext",  "character to use for literal next", 0, &termLiteralNextChar },
836     { "susp",   "character to cause a Suspend Process", 0, &termSuspChar },
837     { "reprint", "character to use for line reprint", 0, &termRprntChar },
838     { "worderase", "character to use to erase a word", 0, &termWerasChar },
839     { "start",  "character to use for XON", 0, &termStartChar },
840     { "stop",   "character to use for XOFF", 0, &termStopChar },
841     { "forw1",  "alternate end of line character", 0, &termForw1Char },
842     { "forw2",  "alternate end of line character", 0, &termForw2Char },
843     { "ayt",    "alternate AYT character", 0, &termAytChar },
844     { 0 }
845 };
846
847 static struct setlist *
848 getset(char *name)
849 {
850     return (struct setlist *)
851                 genget(name, (char **) Setlist, sizeof(struct setlist));
852 }
853
854 void
855 set_escape_char(char *s)
856 {
857         if (rlogin != _POSIX_VDISABLE) {
858                 rlogin = (s && *s) ? special(s) : _POSIX_VDISABLE;
859                 printf("Telnet rlogin escape character is '%s'.\r\n",
860                                         control(rlogin));
861         } else {
862                 escape = (s && *s) ? special(s) : _POSIX_VDISABLE;
863                 printf("Telnet escape character is '%s'.\r\n", control(escape));
864         }
865 }
866
867 static int
868 setcmd(int argc, char *argv[])
869 {
870     int value;
871     struct setlist *ct;
872     struct togglelist *c;
873
874     if (argc < 2 || argc > 3) {
875         printf("Format is 'set Name Value'\r\n'set ?' for help.\r\n");
876         return 0;
877     }
878     if ((argc == 2) && (isprefix(argv[1], "?") || isprefix(argv[1], "help"))) {
879         for (ct = Setlist; ct->name; ct++)
880             printf("%-15s %s\r\n", ct->name, ct->help);
881         printf("\r\n");
882         settogglehelp(1);
883         printf("%-15s %s\r\n", "?", "display help information");
884         return 0;
885     }
886
887     ct = getset(argv[1]);
888     if (ct == 0) {
889         c = GETTOGGLE(argv[1]);
890         if (c == 0) {
891             fprintf(stderr, "'%s': unknown argument ('set ?' for help).\r\n",
892                         argv[1]);
893             return 0;
894         } else if (Ambiguous(c)) {
895             fprintf(stderr, "'%s': ambiguous argument ('set ?' for help).\r\n",
896                         argv[1]);
897             return 0;
898         }
899         if (c->variable) {
900             if ((argc == 2) || (strcmp("on", argv[2]) == 0))
901                 *c->variable = 1;
902             else if (strcmp("off", argv[2]) == 0)
903                 *c->variable = 0;
904             else {
905                 printf("Format is 'set togglename [on|off]'\r\n'set ?' for help.\r\n");
906                 return 0;
907             }
908             if (c->actionexplanation) {
909                 printf("%s %s.\r\n", *c->variable? "Will" : "Won't",
910                                                         c->actionexplanation);
911             }
912         }
913         if (c->handler)
914             (*c->handler)(1);
915     } else if (argc != 3) {
916         printf("Format is 'set Name Value'\r\n'set ?' for help.\r\n");
917         return 0;
918     } else if (Ambiguous(ct)) {
919         fprintf(stderr, "'%s': ambiguous argument ('set ?' for help).\r\n",
920                         argv[1]);
921         return 0;
922     } else if (ct->handler) {
923         (*ct->handler)(argv[2]);
924         printf("%s set to \"%s\".\r\n", ct->name, (char *)ct->charp);
925     } else {
926         if (strcmp("off", argv[2])) {
927             value = special(argv[2]);
928         } else {
929             value = _POSIX_VDISABLE;
930         }
931         *(ct->charp) = (cc_t)value;
932         printf("%s character is '%s'.\r\n", ct->name, control(*(ct->charp)));
933     }
934     slc_check();
935     return 1;
936 }
937
938 static int
939 unsetcmd(int argc, char *argv[])
940 {
941     struct setlist *ct;
942     struct togglelist *c;
943     char *name;
944
945     if (argc < 2) {
946         fprintf(stderr,
947             "Need an argument to 'unset' command.  'unset ?' for help.\r\n");
948         return 0;
949     }
950     if (isprefix(argv[1], "?") || isprefix(argv[1], "help")) {
951         for (ct = Setlist; ct->name; ct++)
952             printf("%-15s %s\r\n", ct->name, ct->help);
953         printf("\r\n");
954         settogglehelp(0);
955         printf("%-15s %s\r\n", "?", "display help information");
956         return 0;
957     }
958
959     argc--;
960     argv++;
961     while (argc--) {
962         name = *argv++;
963         ct = getset(name);
964         if (ct == 0) {
965             c = GETTOGGLE(name);
966             if (c == 0) {
967                 fprintf(stderr, "'%s': unknown argument ('unset ?' for help).\r\n",
968                         name);
969                 return 0;
970             } else if (Ambiguous(c)) {
971                 fprintf(stderr, "'%s': ambiguous argument ('unset ?' for help).\r\n",
972                         name);
973                 return 0;
974             }
975             if (c->variable) {
976                 *c->variable = 0;
977                 if (c->actionexplanation) {
978                     printf("%s %s.\r\n", *c->variable? "Will" : "Won't",
979                                                         c->actionexplanation);
980                 }
981             }
982             if (c->handler)
983                 (*c->handler)(0);
984         } else if (Ambiguous(ct)) {
985             fprintf(stderr, "'%s': ambiguous argument ('unset ?' for help).\r\n",
986                         name);
987             return 0;
988         } else if (ct->handler) {
989             (*ct->handler)(0);
990             printf("%s reset to \"%s\".\r\n", ct->name, (char *)ct->charp);
991         } else {
992             *(ct->charp) = _POSIX_VDISABLE;
993             printf("%s character is '%s'.\r\n", ct->name, control(*(ct->charp)));
994         }
995     }
996     return 1;
997 }
998 \f
999 /*
1000  * The following are the data structures and routines for the
1001  * 'mode' command.
1002  */
1003 #ifdef  KLUDGELINEMODE
1004
1005 static int
1006 dokludgemode(void)
1007 {
1008     kludgelinemode = 1;
1009     send_wont(TELOPT_LINEMODE, 1);
1010     send_dont(TELOPT_SGA, 1);
1011     send_dont(TELOPT_ECHO, 1);
1012     return 1;
1013 }
1014 #endif
1015
1016 static int
1017 dolinemode()
1018 {
1019 #ifdef  KLUDGELINEMODE
1020     if (kludgelinemode)
1021         send_dont(TELOPT_SGA, 1);
1022 #endif
1023     send_will(TELOPT_LINEMODE, 1);
1024     send_dont(TELOPT_ECHO, 1);
1025     return 1;
1026 }
1027
1028 static int
1029 docharmode()
1030 {
1031 #ifdef  KLUDGELINEMODE
1032     if (kludgelinemode)
1033         send_do(TELOPT_SGA, 1);
1034     else
1035 #endif
1036     send_wont(TELOPT_LINEMODE, 1);
1037     send_do(TELOPT_ECHO, 1);
1038     return 1;
1039 }
1040
1041 static int
1042 dolmmode(int bit, int on)
1043 {
1044     unsigned char c;
1045
1046     if (my_want_state_is_wont(TELOPT_LINEMODE)) {
1047         printf("?Need to have LINEMODE option enabled first.\r\n");
1048         printf("'mode ?' for help.\r\n");
1049         return 0;
1050     }
1051
1052     if (on)
1053         c = (linemode | bit);
1054     else
1055         c = (linemode & ~bit);
1056     lm_mode(&c, 1, 1);
1057     return 1;
1058 }
1059
1060 static int
1061 tn_setmode(int bit)
1062 {
1063     return dolmmode(bit, 1);
1064 }
1065
1066 static int
1067 tn_clearmode(int bit)
1068 {
1069     return dolmmode(bit, 0);
1070 }
1071
1072 struct modelist {
1073         char    *name;          /* command name */
1074         char    *help;          /* help string */
1075         int     (*handler)();   /* routine which executes command */
1076         int     needconnect;    /* Do we need to be connected to execute? */
1077         int     arg1;
1078 };
1079
1080 static int modehelp(void);
1081
1082 static struct modelist ModeList[] = {
1083     { "character", "Disable LINEMODE option",   docharmode, 1 },
1084 #ifdef  KLUDGELINEMODE
1085     { "",       "(or disable obsolete line-by-line mode)", 0 },
1086 #endif
1087     { "line",   "Enable LINEMODE option",       dolinemode, 1 },
1088 #ifdef  KLUDGELINEMODE
1089     { "",       "(or enable obsolete line-by-line mode)", 0 },
1090 #endif
1091     { "", "", 0 },
1092     { "",       "These require the LINEMODE option to be enabled", 0 },
1093     { "isig",   "Enable signal trapping",       tn_setmode, 1, MODE_TRAPSIG },
1094     { "+isig",  0,                              tn_setmode, 1, MODE_TRAPSIG },
1095     { "-isig",  "Disable signal trapping",      tn_clearmode, 1, MODE_TRAPSIG },
1096     { "edit",   "Enable character editing",     tn_setmode, 1, MODE_EDIT },
1097     { "+edit",  0,                              tn_setmode, 1, MODE_EDIT },
1098     { "-edit",  "Disable character editing",    tn_clearmode, 1, MODE_EDIT },
1099     { "softtabs", "Enable tab expansion",       tn_setmode, 1, MODE_SOFT_TAB },
1100     { "+softtabs", 0,                           tn_setmode, 1, MODE_SOFT_TAB },
1101     { "-softtabs", "Disable tab expansion",     tn_clearmode, 1, MODE_SOFT_TAB },
1102     { "litecho", "Enable literal character echo", tn_setmode, 1, MODE_LIT_ECHO },
1103     { "+litecho", 0,                            tn_setmode, 1, MODE_LIT_ECHO },
1104     { "-litecho", "Disable literal character echo", tn_clearmode, 1, MODE_LIT_ECHO },
1105     { "help",   0,                              modehelp, 0 },
1106 #ifdef  KLUDGELINEMODE
1107     { "kludgeline", 0,                          dokludgemode, 1 },
1108 #endif
1109     { "", "", 0 },
1110     { "?",      "Print help information",       modehelp, 0 },
1111     { 0 },
1112 };
1113
1114
1115 static int
1116 modehelp(void)
1117 {
1118     struct modelist *mt;
1119
1120     printf("format is:  'mode Mode', where 'Mode' is one of:\r\n\r\n");
1121     for (mt = ModeList; mt->name; mt++) {
1122         if (mt->help) {
1123             if (*mt->help)
1124                 printf("%-15s %s\r\n", mt->name, mt->help);
1125             else
1126                 printf("\r\n");
1127         }
1128     }
1129     return 0;
1130 }
1131
1132 #define GETMODECMD(name) (struct modelist *) \
1133                 genget(name, (char **) ModeList, sizeof(struct modelist))
1134
1135 static int
1136 modecmd(int argc, char **argv)
1137 {
1138     struct modelist *mt;
1139
1140     if (argc != 2) {
1141         printf("'mode' command requires an argument\r\n");
1142         printf("'mode ?' for help.\r\n");
1143     } else if ((mt = GETMODECMD(argv[1])) == 0) {
1144         fprintf(stderr, "Unknown mode '%s' ('mode ?' for help).\r\n", argv[1]);
1145     } else if (Ambiguous(mt)) {
1146         fprintf(stderr, "Ambiguous mode '%s' ('mode ?' for help).\r\n", argv[1]);
1147     } else if (mt->needconnect && !connected) {
1148         printf("?Need to be connected first.\r\n");
1149         printf("'mode ?' for help.\r\n");
1150     } else if (mt->handler) {
1151         return (*mt->handler)(mt->arg1);
1152     }
1153     return 0;
1154 }
1155 \f
1156 /*
1157  * The following data structures and routines implement the
1158  * "display" command.
1159  */
1160
1161 static int
1162 display(int argc, char *argv[])
1163 {
1164     struct togglelist *tl;
1165     struct setlist *sl;
1166
1167 #define dotog(tl)       if (tl->variable && tl->actionexplanation) { \
1168                             if (*tl->variable) { \
1169                                 printf("will"); \
1170                             } else { \
1171                                 printf("won't"); \
1172                             } \
1173                             printf(" %s.\r\n", tl->actionexplanation); \
1174                         }
1175
1176 #define doset(sl)   if (sl->name && *sl->name != ' ') { \
1177                         if (sl->handler == 0) \
1178                             printf("%-15s [%s]\r\n", sl->name, control(*sl->charp)); \
1179                         else \
1180                             printf("%-15s \"%s\"\r\n", sl->name, (char *)sl->charp); \
1181                     }
1182
1183     if (argc == 1) {
1184         for (tl = Togglelist; tl->name; tl++) {
1185             dotog(tl);
1186         }
1187         printf("\r\n");
1188         for (sl = Setlist; sl->name; sl++) {
1189             doset(sl);
1190         }
1191     } else {
1192         int i;
1193
1194         for (i = 1; i < argc; i++) {
1195             sl = getset(argv[i]);
1196             tl = GETTOGGLE(argv[i]);
1197             if (Ambiguous(sl) || Ambiguous(tl)) {
1198                 printf("?Ambiguous argument '%s'.\r\n", argv[i]);
1199                 return 0;
1200             } else if (!sl && !tl) {
1201                 printf("?Unknown argument '%s'.\r\n", argv[i]);
1202                 return 0;
1203             } else {
1204                 if (tl) {
1205                     dotog(tl);
1206                 }
1207                 if (sl) {
1208                     doset(sl);
1209                 }
1210             }
1211         }
1212     }
1213 /*@*/optionstatus();
1214 #if     defined(ENCRYPTION)
1215     EncryptStatus();
1216 #endif
1217     return 1;
1218 #undef  doset
1219 #undef  dotog
1220 }
1221 \f
1222 /*
1223  * The following are the data structures, and many of the routines,
1224  * relating to command processing.
1225  */
1226
1227 /*
1228  * Set the escape character.
1229  */
1230 static int
1231 setescape(int argc, char *argv[])
1232 {
1233         char *arg;
1234         char buf[50];
1235
1236         printf(
1237             "Deprecated usage - please use 'set escape%s%s' in the future.\r\n",
1238                                 (argc > 2)? " ":"", (argc > 2)? argv[1]: "");
1239         if (argc > 2)
1240                 arg = argv[1];
1241         else {
1242                 printf("new escape character: ");
1243                 fgets(buf, sizeof(buf), stdin);
1244                 arg = buf;
1245         }
1246         if (arg[0] != '\0')
1247                 escape = arg[0];
1248         printf("Escape character is '%s'.\r\n", control(escape));
1249
1250         fflush(stdout);
1251         return 1;
1252 }
1253
1254 static int
1255 togcrmod()
1256 {
1257     crmod = !crmod;
1258     printf("Deprecated usage - please use 'toggle crmod' in the future.\r\n");
1259     printf("%s map carriage return on output.\r\n", crmod ? "Will" : "Won't");
1260     fflush(stdout);
1261     return 1;
1262 }
1263
1264 static int
1265 telnetsuspend()
1266 {
1267 #ifdef  SIGTSTP
1268     setcommandmode();
1269     {
1270         long oldrows, oldcols, newrows, newcols, err;
1271
1272         err = (TerminalWindowSize(&oldrows, &oldcols) == 0) ? 1 : 0;
1273         kill(0, SIGTSTP);
1274         /*
1275          * If we didn't get the window size before the SUSPEND, but we
1276          * can get them now (?), then send the NAWS to make sure that
1277          * we are set up for the right window size.
1278          */
1279         if (TerminalWindowSize(&newrows, &newcols) && connected &&
1280             (err || ((oldrows != newrows) || (oldcols != newcols)))) {
1281                 sendnaws();
1282         }
1283     }
1284     /* reget parameters in case they were changed */
1285     TerminalSaveState();
1286     setconnmode(0);
1287 #else
1288     printf("Suspend is not supported.  Try the '!' command instead\r\n");
1289 #endif
1290     return 1;
1291 }
1292
1293 static int
1294 shell(int argc, char **argv)
1295 {
1296     long oldrows, oldcols, newrows, newcols, err;
1297
1298     setcommandmode();
1299
1300     err = (TerminalWindowSize(&oldrows, &oldcols) == 0) ? 1 : 0;
1301     switch(fork()) {
1302     case -1:
1303         perror("Fork failed\r\n");
1304         break;
1305
1306     case 0:
1307         {
1308             /*
1309              * Fire up the shell in the child.
1310              */
1311             char *shellp, *shellname;
1312
1313             shellp = getenv("SHELL");
1314             if (shellp == NULL)
1315                 shellp = "/bin/sh";
1316             if ((shellname = strrchr(shellp, '/')) == 0)
1317                 shellname = shellp;
1318             else
1319                 shellname++;
1320             if (argc > 1)
1321                 execl(shellp, shellname, "-c", &saveline[1], 0);
1322             else
1323                 execl(shellp, shellname, 0);
1324             perror("Execl");
1325             _exit(1);
1326         }
1327     default:
1328             wait((int *)0);     /* Wait for the shell to complete */
1329
1330             if (TerminalWindowSize(&newrows, &newcols) && connected &&
1331                 (err || ((oldrows != newrows) || (oldcols != newcols)))) {
1332                     sendnaws();
1333             }
1334             break;
1335     }
1336     return 1;
1337 }
1338
1339 static int
1340 bye(int argc, char **argv)
1341 {
1342     if (connected) {
1343         shutdown(net, 2);
1344         printf("Connection closed.\r\n");
1345         NetClose(net);
1346         connected = 0;
1347         resettermname = 1;
1348 #if     defined(AUTHENTICATION) || defined(ENCRYPTION)
1349         auth_encrypt_connect(connected);
1350 #endif
1351         /* reset options */
1352         tninit();
1353     }
1354     if ((argc != 2) || (strcmp(argv[1], "fromquit") != 0)) 
1355         longjmp(toplevel, 1);
1356     return 0;   /* NOTREACHED */
1357 }
1358
1359 int
1360 quit(void)
1361 {
1362         call(bye, "bye", "fromquit", 0);
1363         Exit(0);
1364         return 0; /*NOTREACHED*/
1365 }
1366
1367 static int
1368 logout()
1369 {
1370         send_do(TELOPT_LOGOUT, 1);
1371         netflush();
1372         return 1;
1373 }
1374
1375 \f
1376 /*
1377  * The SLC command.
1378  */
1379
1380 struct slclist {
1381         char    *name;
1382         char    *help;
1383         void    (*handler)();
1384         int     arg;
1385 };
1386
1387 static void slc_help(void);
1388
1389 struct slclist SlcList[] = {
1390     { "export", "Use local special character definitions",
1391                                                 slc_mode_export,        0 },
1392     { "import", "Use remote special character definitions",
1393                                                 slc_mode_import,        1 },
1394     { "check",  "Verify remote special character definitions",
1395                                                 slc_mode_import,        0 },
1396     { "help",   0,                              slc_help,               0 },
1397     { "?",      "Print help information",       slc_help,               0 },
1398     { 0 },
1399 };
1400
1401 static void
1402 slc_help(void)
1403 {
1404     struct slclist *c;
1405
1406     for (c = SlcList; c->name; c++) {
1407         if (c->help) {
1408             if (*c->help)
1409                 printf("%-15s %s\r\n", c->name, c->help);
1410             else
1411                 printf("\r\n");
1412         }
1413     }
1414 }
1415
1416 static struct slclist *
1417 getslc(char *name)
1418 {
1419     return (struct slclist *)
1420                 genget(name, (char **) SlcList, sizeof(struct slclist));
1421 }
1422
1423 static int
1424 slccmd(int argc, char **argv)
1425 {
1426     struct slclist *c;
1427
1428     if (argc != 2) {
1429         fprintf(stderr,
1430             "Need an argument to 'slc' command.  'slc ?' for help.\r\n");
1431         return 0;
1432     }
1433     c = getslc(argv[1]);
1434     if (c == 0) {
1435         fprintf(stderr, "'%s': unknown argument ('slc ?' for help).\r\n",
1436                                 argv[1]);
1437         return 0;
1438     }
1439     if (Ambiguous(c)) {
1440         fprintf(stderr, "'%s': ambiguous argument ('slc ?' for help).\r\n",
1441                                 argv[1]);
1442         return 0;
1443     }
1444     (*c->handler)(c->arg);
1445     slcstate();
1446     return 1;
1447 }
1448 \f
1449 /*
1450  * The ENVIRON command.
1451  */
1452
1453 struct envlist {
1454         char    *name;
1455         char    *help;
1456         void    (*handler)();
1457         int     narg;
1458 };
1459
1460 static void env_help (void);
1461
1462 struct envlist EnvList[] = {
1463     { "define", "Define an environment variable",
1464                                                 (void (*)())env_define, 2 },
1465     { "undefine", "Undefine an environment variable",
1466                                                 env_undefine,   1 },
1467     { "export", "Mark an environment variable for automatic export",
1468                                                 env_export,     1 },
1469     { "unexport", "Don't mark an environment variable for automatic export",
1470                                                 env_unexport,   1 },
1471     { "send",   "Send an environment variable", env_send,       1 },
1472     { "list",   "List the current environment variables",
1473                                                 env_list,       0 },
1474     { "help",   0,                              env_help,               0 },
1475     { "?",      "Print help information",       env_help,               0 },
1476     { 0 },
1477 };
1478
1479 static void
1480 env_help()
1481 {
1482     struct envlist *c;
1483
1484     for (c = EnvList; c->name; c++) {
1485         if (c->help) {
1486             if (*c->help)
1487                 printf("%-15s %s\r\n", c->name, c->help);
1488             else
1489                 printf("\r\n");
1490         }
1491     }
1492 }
1493
1494 static struct envlist *
1495 getenvcmd(char *name)
1496 {
1497     return (struct envlist *)
1498                 genget(name, (char **) EnvList, sizeof(struct envlist));
1499 }
1500
1501 static int
1502 env_cmd(int argc, char **argv)
1503 {
1504     struct envlist *c;
1505
1506     if (argc < 2) {
1507         fprintf(stderr,
1508             "Need an argument to 'environ' command.  'environ ?' for help.\r\n");
1509         return 0;
1510     }
1511     c = getenvcmd(argv[1]);
1512     if (c == 0) {
1513         fprintf(stderr, "'%s': unknown argument ('environ ?' for help).\r\n",
1514                                 argv[1]);
1515         return 0;
1516     }
1517     if (Ambiguous(c)) {
1518         fprintf(stderr, "'%s': ambiguous argument ('environ ?' for help).\r\n",
1519                                 argv[1]);
1520         return 0;
1521     }
1522     if (c->narg + 2 != argc) {
1523         fprintf(stderr,
1524             "Need %s%d argument%s to 'environ %s' command.  'environ ?' for help.\r\n",
1525                 c->narg < argc + 2 ? "only " : "",
1526                 c->narg, c->narg == 1 ? "" : "s", c->name);
1527         return 0;
1528     }
1529     (*c->handler)(argv[2], argv[3]);
1530     return 1;
1531 }
1532
1533 struct env_lst {
1534         struct env_lst *next;   /* pointer to next structure */
1535         struct env_lst *prev;   /* pointer to previous structure */
1536         unsigned char *var;     /* pointer to variable name */
1537         unsigned char *value;   /* pointer to variable value */
1538         int export;             /* 1 -> export with default list of variables */
1539         int welldefined;        /* A well defined variable */
1540 };
1541
1542 struct env_lst envlisthead;
1543
1544 struct env_lst *
1545 env_find(unsigned char *var)
1546 {
1547         struct env_lst *ep;
1548
1549         for (ep = envlisthead.next; ep; ep = ep->next) {
1550                 if (strcmp((char *)ep->var, (char *)var) == 0)
1551                         return(ep);
1552         }
1553         return(NULL);
1554 }
1555
1556 #ifdef IRIX4
1557 #define environ _environ
1558 #endif
1559
1560 void
1561 env_init(void)
1562 {
1563         char **epp, *cp;
1564         struct env_lst *ep;
1565
1566         for (epp = environ; *epp; epp++) {
1567                 if ((cp = strchr(*epp, '='))) {
1568                         *cp = '\0';
1569                         ep = env_define((unsigned char *)*epp,
1570                                         (unsigned char *)cp+1);
1571                         ep->export = 0;
1572                         *cp = '=';
1573                 }
1574         }
1575         /*
1576          * Special case for DISPLAY variable.  If it is ":0.0" or
1577          * "unix:0.0", we have to get rid of "unix" and insert our
1578          * hostname.
1579          */
1580         if ((ep = env_find((unsigned char*)"DISPLAY"))
1581             && (*ep->value == ':'
1582             || strncmp((char *)ep->value, "unix:", 5) == 0)) {
1583                 char hbuf[256+1];
1584                 char *cp2 = strchr((char *)ep->value, ':');
1585
1586                 /* XXX - should be k_gethostname? */
1587                 gethostname(hbuf, 256);
1588                 hbuf[256] = '\0';
1589
1590                 /* If this is not the full name, try to get it via DNS */
1591                 if (strchr(hbuf, '.') == 0) {
1592                         struct addrinfo hints, *ai, *a;
1593                         int error;
1594
1595                         memset (&hints, 0, sizeof(hints));
1596                         hints.ai_flags = AI_CANONNAME;
1597
1598                         error = getaddrinfo (hbuf, NULL, &hints, &ai);
1599                         if (error == 0) {
1600                                 for (a = ai; a != NULL; a = a->ai_next)
1601                                         if (a->ai_canonname != NULL) {
1602                                                 strlcpy (hbuf,
1603                                                          ai->ai_canonname,
1604                                                          256);
1605                                                 break;
1606                                         }
1607                                 freeaddrinfo (ai);
1608                         }
1609                 }
1610
1611                 asprintf (&cp, "%s%s", hbuf, cp2);
1612                 free (ep->value);
1613                 ep->value = (unsigned char *)cp;
1614         }
1615         /*
1616          * If USER is not defined, but LOGNAME is, then add
1617          * USER with the value from LOGNAME.  By default, we
1618          * don't export the USER variable.
1619          */
1620         if ((env_find((unsigned char*)"USER") == NULL) && 
1621             (ep = env_find((unsigned char*)"LOGNAME"))) {
1622                 env_define((unsigned char *)"USER", ep->value);
1623                 env_unexport((unsigned char *)"USER");
1624         }
1625         env_export((unsigned char *)"DISPLAY");
1626         env_export((unsigned char *)"PRINTER");
1627         env_export((unsigned char *)"XAUTHORITY");
1628 }
1629
1630 struct env_lst *
1631 env_define(unsigned char *var, unsigned char *value)
1632 {
1633         struct env_lst *ep;
1634
1635         if ((ep = env_find(var))) {
1636                 if (ep->var)
1637                         free(ep->var);
1638                 if (ep->value)
1639                         free(ep->value);
1640         } else {
1641                 ep = (struct env_lst *)malloc(sizeof(struct env_lst));
1642                 ep->next = envlisthead.next;
1643                 envlisthead.next = ep;
1644                 ep->prev = &envlisthead;
1645                 if (ep->next)
1646                         ep->next->prev = ep;
1647         }
1648         ep->welldefined = opt_welldefined((char *)var);
1649         ep->export = 1;
1650         ep->var = (unsigned char *)strdup((char *)var);
1651         ep->value = (unsigned char *)strdup((char *)value);
1652         return(ep);
1653 }
1654
1655 void
1656 env_undefine(unsigned char *var)
1657 {
1658         struct env_lst *ep;
1659
1660         if ((ep = env_find(var))) {
1661                 ep->prev->next = ep->next;
1662                 if (ep->next)
1663                         ep->next->prev = ep->prev;
1664                 if (ep->var)
1665                         free(ep->var);
1666                 if (ep->value)
1667                         free(ep->value);
1668                 free(ep);
1669         }
1670 }
1671
1672 void
1673 env_export(unsigned char *var)
1674 {
1675         struct env_lst *ep;
1676
1677         if ((ep = env_find(var)))
1678                 ep->export = 1;
1679 }
1680
1681 void
1682 env_unexport(unsigned char *var)
1683 {
1684         struct env_lst *ep;
1685
1686         if ((ep = env_find(var)))
1687                 ep->export = 0;
1688 }
1689
1690 void
1691 env_send(unsigned char *var)
1692 {
1693         struct env_lst *ep;
1694
1695         if (my_state_is_wont(TELOPT_NEW_ENVIRON)
1696 #ifdef  OLD_ENVIRON
1697             && my_state_is_wont(TELOPT_OLD_ENVIRON)
1698 #endif
1699                 ) {
1700                 fprintf(stderr,
1701                     "Cannot send '%s': Telnet ENVIRON option not enabled\r\n",
1702                                                                         var);
1703                 return;
1704         }
1705         ep = env_find(var);
1706         if (ep == 0) {
1707                 fprintf(stderr, "Cannot send '%s': variable not defined\r\n",
1708                                                                         var);
1709                 return;
1710         }
1711         env_opt_start_info();
1712         env_opt_add(ep->var);
1713         env_opt_end(0);
1714 }
1715
1716 void
1717 env_list(void)
1718 {
1719         struct env_lst *ep;
1720
1721         for (ep = envlisthead.next; ep; ep = ep->next) {
1722                 printf("%c %-20s %s\r\n", ep->export ? '*' : ' ',
1723                                         ep->var, ep->value);
1724         }
1725 }
1726
1727 unsigned char *
1728 env_default(int init, int welldefined)
1729 {
1730         static struct env_lst *nep = NULL;
1731
1732         if (init) {
1733                 nep = &envlisthead;
1734                 return NULL;
1735         }
1736         if (nep) {
1737                 while ((nep = nep->next)) {
1738                         if (nep->export && (nep->welldefined == welldefined))
1739                                 return(nep->var);
1740                 }
1741         }
1742         return(NULL);
1743 }
1744
1745 unsigned char *
1746 env_getvalue(unsigned char *var)
1747 {
1748         struct env_lst *ep;
1749
1750         if ((ep = env_find(var)))
1751                 return(ep->value);
1752         return(NULL);
1753 }
1754
1755
1756 #if     defined(AUTHENTICATION)
1757 /*
1758  * The AUTHENTICATE command.
1759  */
1760
1761 struct authlist {
1762         char    *name;
1763         char    *help;
1764         int     (*handler)();
1765         int     narg;
1766 };
1767
1768 static int
1769         auth_help (void);
1770
1771 struct authlist AuthList[] = {
1772     { "status", "Display current status of authentication information",
1773                                                 auth_status,    0 },
1774     { "disable", "Disable an authentication type ('auth disable ?' for more)",
1775                                                 auth_disable,   1 },
1776     { "enable", "Enable an authentication type ('auth enable ?' for more)",
1777                                                 auth_enable,    1 },
1778     { "help",   0,                              auth_help,              0 },
1779     { "?",      "Print help information",       auth_help,              0 },
1780     { 0 },
1781 };
1782
1783 static int
1784 auth_help()
1785 {
1786     struct authlist *c;
1787
1788     for (c = AuthList; c->name; c++) {
1789         if (c->help) {
1790             if (*c->help)
1791                 printf("%-15s %s\r\n", c->name, c->help);
1792             else
1793                 printf("\r\n");
1794         }
1795     }
1796     return 0;
1797 }
1798
1799 static int
1800 auth_cmd(int argc, char **argv)
1801 {
1802     struct authlist *c;
1803
1804     if (argc < 2) {
1805         fprintf(stderr,
1806             "Need an argument to 'auth' command.  'auth ?' for help.\r\n");
1807         return 0;
1808     }
1809
1810     c = (struct authlist *)
1811                 genget(argv[1], (char **) AuthList, sizeof(struct authlist));
1812     if (c == 0) {
1813         fprintf(stderr, "'%s': unknown argument ('auth ?' for help).\r\n",
1814                                 argv[1]);
1815         return 0;
1816     }
1817     if (Ambiguous(c)) {
1818         fprintf(stderr, "'%s': ambiguous argument ('auth ?' for help).\r\n",
1819                                 argv[1]);
1820         return 0;
1821     }
1822     if (c->narg + 2 != argc) {
1823         fprintf(stderr,
1824             "Need %s%d argument%s to 'auth %s' command.  'auth ?' for help.\r\n",
1825                 c->narg < argc + 2 ? "only " : "",
1826                 c->narg, c->narg == 1 ? "" : "s", c->name);
1827         return 0;
1828     }
1829     return((*c->handler)(argv[2], argv[3]));
1830 }
1831 #endif
1832
1833
1834 #if     defined(ENCRYPTION)
1835 /*
1836  * The ENCRYPT command.
1837  */
1838
1839 struct encryptlist {
1840         char    *name;
1841         char    *help;
1842         int     (*handler)();
1843         int     needconnect;
1844         int     minarg;
1845         int     maxarg;
1846 };
1847
1848 static int
1849         EncryptHelp (void);
1850
1851 struct encryptlist EncryptList[] = {
1852     { "enable", "Enable encryption. ('encrypt enable ?' for more)",
1853                                                 EncryptEnable, 1, 1, 2 },
1854     { "disable", "Disable encryption. ('encrypt enable ?' for more)",
1855                                                 EncryptDisable, 0, 1, 2 },
1856     { "type", "Set encryptiong type. ('encrypt type ?' for more)",
1857                                                 EncryptType, 0, 1, 1 },
1858     { "start", "Start encryption. ('encrypt start ?' for more)",
1859                                                 EncryptStart, 1, 0, 1 },
1860     { "stop", "Stop encryption. ('encrypt stop ?' for more)",
1861                                                 EncryptStop, 1, 0, 1 },
1862     { "input", "Start encrypting the input stream",
1863                                                 EncryptStartInput, 1, 0, 0 },
1864     { "-input", "Stop encrypting the input stream",
1865                                                 EncryptStopInput, 1, 0, 0 },
1866     { "output", "Start encrypting the output stream",
1867                                                 EncryptStartOutput, 1, 0, 0 },
1868     { "-output", "Stop encrypting the output stream",
1869                                                 EncryptStopOutput, 1, 0, 0 },
1870
1871     { "status", "Display current status of authentication information",
1872                                                 EncryptStatus,  0, 0, 0 },
1873     { "help",   0,                              EncryptHelp,    0, 0, 0 },
1874     { "?",      "Print help information",       EncryptHelp,    0, 0, 0 },
1875     { 0 },
1876 };
1877
1878 static int
1879 EncryptHelp()
1880 {
1881     struct encryptlist *c;
1882
1883     for (c = EncryptList; c->name; c++) {
1884         if (c->help) {
1885             if (*c->help)
1886                 printf("%-15s %s\r\n", c->name, c->help);
1887             else
1888                 printf("\r\n");
1889         }
1890     }
1891     return 0;
1892 }
1893
1894 static int
1895 encrypt_cmd(int argc, char **argv)
1896 {
1897     struct encryptlist *c;
1898
1899     c = (struct encryptlist *)
1900                 genget(argv[1], (char **) EncryptList, sizeof(struct encryptlist));
1901     if (c == 0) {
1902         fprintf(stderr, "'%s': unknown argument ('encrypt ?' for help).\r\n",
1903                                 argv[1]);
1904         return 0;
1905     }
1906     if (Ambiguous(c)) {
1907         fprintf(stderr, "'%s': ambiguous argument ('encrypt ?' for help).\r\n",
1908                                 argv[1]);
1909         return 0;
1910     }
1911     argc -= 2;
1912     if (argc < c->minarg || argc > c->maxarg) {
1913         if (c->minarg == c->maxarg) {
1914             fprintf(stderr, "Need %s%d argument%s ",
1915                 c->minarg < argc ? "only " : "", c->minarg,
1916                 c->minarg == 1 ? "" : "s");
1917         } else {
1918             fprintf(stderr, "Need %s%d-%d arguments ",
1919                 c->maxarg < argc ? "only " : "", c->minarg, c->maxarg);
1920         }
1921         fprintf(stderr, "to 'encrypt %s' command.  'encrypt ?' for help.\r\n",
1922                 c->name);
1923         return 0;
1924     }
1925     if (c->needconnect && !connected) {
1926         if (!(argc && (isprefix(argv[2], "help") || isprefix(argv[2], "?")))) {
1927             printf("?Need to be connected first.\r\n");
1928             return 0;
1929         }
1930     }
1931     return ((*c->handler)(argc > 0 ? argv[2] : 0,
1932                         argc > 1 ? argv[3] : 0,
1933                         argc > 2 ? argv[4] : 0));
1934 }
1935 #endif
1936
1937
1938 /*
1939  * Print status about the connection.
1940  */
1941
1942 static int
1943 status(int argc, char **argv)
1944 {
1945     if (connected) {
1946         printf("Connected to %s.\r\n", hostname);
1947         if ((argc < 2) || strcmp(argv[1], "notmuch")) {
1948             int mode = getconnmode();
1949
1950             if (my_want_state_is_will(TELOPT_LINEMODE)) {
1951                 printf("Operating with LINEMODE option\r\n");
1952                 printf("%s line editing\r\n", (mode&MODE_EDIT) ? "Local" : "No");
1953                 printf("%s catching of signals\r\n",
1954                                         (mode&MODE_TRAPSIG) ? "Local" : "No");
1955                 slcstate();
1956 #ifdef  KLUDGELINEMODE
1957             } else if (kludgelinemode && my_want_state_is_dont(TELOPT_SGA)) {
1958                 printf("Operating in obsolete linemode\r\n");
1959 #endif
1960             } else {
1961                 printf("Operating in single character mode\r\n");
1962                 if (localchars)
1963                     printf("Catching signals locally\r\n");
1964             }
1965             printf("%s character echo\r\n", (mode&MODE_ECHO) ? "Local" : "Remote");
1966             if (my_want_state_is_will(TELOPT_LFLOW))
1967                 printf("%s flow control\r\n", (mode&MODE_FLOW) ? "Local" : "No");
1968 #if     defined(ENCRYPTION)
1969             encrypt_display();
1970 #endif
1971         }
1972     } else {
1973         printf("No connection.\r\n");
1974     }
1975     printf("Escape character is '%s'.\r\n", control(escape));
1976     fflush(stdout);
1977     return 1;
1978 }
1979
1980 #ifdef  SIGINFO
1981 /*
1982  * Function that gets called when SIGINFO is received.
1983  */
1984 RETSIGTYPE
1985 ayt_status(int ignore)
1986 {
1987     call(status, "status", "notmuch", 0);
1988 }
1989 #endif
1990
1991 static Command *getcmd(char *name);
1992
1993 static void
1994 cmdrc(char *m1, char *m2)
1995 {
1996     static char rcname[128];
1997     Command *c;
1998     FILE *rcfile;
1999     int gotmachine = 0;
2000     int l1 = strlen(m1);
2001     int l2 = strlen(m2);
2002     char m1save[64];
2003
2004     if (skiprc)
2005         return;
2006
2007     strlcpy(m1save, m1, sizeof(m1save));
2008     m1 = m1save;
2009
2010     if (rcname[0] == 0) {
2011         char *home = getenv("HOME");
2012
2013         snprintf (rcname, sizeof(rcname), "%s/.telnetrc",
2014                   home ? home : "");
2015     }
2016
2017     if ((rcfile = fopen(rcname, "r")) == 0) {
2018         return;
2019     }
2020
2021     for (;;) {
2022         if (fgets(line, sizeof(line), rcfile) == NULL)
2023             break;
2024         if (line[0] == 0)
2025             break;
2026         if (line[0] == '#')
2027             continue;
2028         if (gotmachine) {
2029             if (!isspace(line[0]))
2030                 gotmachine = 0;
2031         }
2032         if (gotmachine == 0) {
2033             if (isspace(line[0]))
2034                 continue;
2035             if (strncasecmp(line, m1, l1) == 0)
2036                 strncpy(line, &line[l1], sizeof(line) - l1);
2037             else if (strncasecmp(line, m2, l2) == 0)
2038                 strncpy(line, &line[l2], sizeof(line) - l2);
2039             else if (strncasecmp(line, "DEFAULT", 7) == 0)
2040                 strncpy(line, &line[7], sizeof(line) - 7);
2041             else
2042                 continue;
2043             if (line[0] != ' ' && line[0] != '\t' && line[0] != '\n')
2044                 continue;
2045             gotmachine = 1;
2046         }
2047         makeargv();
2048         if (margv[0] == 0)
2049             continue;
2050         c = getcmd(margv[0]);
2051         if (Ambiguous(c)) {
2052             printf("?Ambiguous command: %s\r\n", margv[0]);
2053             continue;
2054         }
2055         if (c == 0) {
2056             printf("?Invalid command: %s\r\n", margv[0]);
2057             continue;
2058         }
2059         /*
2060          * This should never happen...
2061          */
2062         if (c->needconnect && !connected) {
2063             printf("?Need to be connected first for %s.\r\n", margv[0]);
2064             continue;
2065         }
2066         (*c->handler)(margc, margv);
2067     }
2068     fclose(rcfile);
2069 }
2070
2071 int
2072 tn(int argc, char **argv)
2073 {
2074     struct servent *sp = 0;
2075     char *cmd, *hostp = 0, *portp = 0;
2076     char *user = 0;
2077     int port = 0;
2078
2079     /* clear the socket address prior to use */
2080
2081     if (connected) {
2082         printf("?Already connected to %s\r\n", hostname);
2083         return 0;
2084     }
2085     if (argc < 2) {
2086         strlcpy(line, "open ", sizeof(line));
2087         printf("(to) ");
2088         fgets(&line[strlen(line)], sizeof(line) - strlen(line), stdin);
2089         makeargv();
2090         argc = margc;
2091         argv = margv;
2092     }
2093     cmd = *argv;
2094     --argc; ++argv;
2095     while (argc) {
2096         if (strcmp(*argv, "help") == 0 || isprefix(*argv, "?"))
2097             goto usage;
2098         if (strcmp(*argv, "-l") == 0) {
2099             --argc; ++argv;
2100             if (argc == 0)
2101                 goto usage;
2102             user = strdup(*argv++);
2103             --argc;
2104             continue;
2105         }
2106         if (strcmp(*argv, "-a") == 0) {
2107             --argc; ++argv;
2108             autologin = 1;
2109             continue;
2110         }
2111         if (hostp == 0) {
2112             hostp = *argv++;
2113             --argc;
2114             continue;
2115         }
2116         if (portp == 0) {
2117             portp = *argv++;
2118             --argc;
2119             continue;
2120         }
2121     usage:
2122         printf("usage: %s [-l user] [-a] host-name [port]\r\n", cmd);
2123         return 0;
2124     }
2125     if (hostp == 0)
2126         goto usage;
2127
2128     strlcpy (_hostname, hostp, sizeof(_hostname));
2129     hostp = _hostname;
2130     if (hostp[0] == '@' || hostp[0] == '!') {
2131         char *p;
2132         hostname = NULL;
2133         for (p = hostp + 1; *p; p++) {
2134             if (*p == ',' || *p == '@')
2135                 hostname = p;
2136         }
2137         if (hostname == NULL) {
2138             fprintf(stderr, "%s: bad source route specification\n", hostp);
2139             return 0;
2140         }
2141         *hostname++ = '\0';
2142     } else
2143         hostname = hostp;
2144
2145     if (portp) {
2146         if (*portp == '-') {
2147             portp++;
2148             telnetport = 1;
2149         } else
2150             telnetport = 0;
2151         port = atoi(portp);
2152         if (port == 0) {
2153             sp = roken_getservbyname(portp, "tcp");
2154             if (sp)
2155                 port = sp->s_port;
2156             else {
2157                 printf("%s: bad port number\r\n", portp);
2158                 return 0;
2159             }
2160         } else {
2161             port = htons(port);
2162         }
2163     } else {
2164         if (sp == 0) {
2165             sp = roken_getservbyname("telnet", "tcp");
2166             if (sp == 0) {
2167                 fprintf(stderr, "telnet: tcp/telnet: unknown service\r\n");
2168                 return 0;
2169             }
2170             port = sp->s_port;
2171         }
2172         telnetport = 1;
2173     }
2174
2175     {
2176         struct addrinfo *ai, *a, hints;
2177         int error;
2178         char portstr[NI_MAXSERV];
2179
2180         memset (&hints, 0, sizeof(hints));
2181         hints.ai_socktype = SOCK_STREAM;
2182         hints.ai_protocol = IPPROTO_TCP;
2183         hints.ai_flags    = AI_CANONNAME;
2184
2185         snprintf (portstr, sizeof(portstr), "%u", ntohs(port));
2186
2187         error = getaddrinfo (hostname, portstr, &hints, &ai);
2188         if (error) {
2189             fprintf (stderr, "%s: %s\r\n", hostname, gai_strerror (error));
2190             return 0;
2191         }
2192
2193         for (a = ai; a != NULL && connected == 0; a = a->ai_next) {
2194             char addrstr[256];
2195
2196             if (a->ai_canonname != NULL)
2197                 strlcpy (_hostname, a->ai_canonname, sizeof(_hostname));
2198
2199             if (getnameinfo (a->ai_addr, a->ai_addrlen,
2200                              addrstr, sizeof(addrstr),
2201                              NULL, 0, NI_NUMERICHOST) != 0)
2202                 strlcpy (addrstr, "unknown address", sizeof(addrstr));
2203                              
2204             printf("Trying %s...\r\n", addrstr);
2205
2206             net = socket (a->ai_family, a->ai_socktype, a->ai_protocol);
2207             if (net < 0) {
2208                 warn ("socket");
2209                 continue;
2210             }
2211
2212 #if     defined(IP_OPTIONS) && defined(IPPROTO_IP) && defined(HAVE_SETSOCKOPT)
2213         if (hostp[0] == '@' || hostp[0] == '!') {
2214             char *srp = 0;
2215             int srlen;
2216             int proto, opt;
2217
2218             if ((srlen = sourceroute(a, hostp, &srp, &proto, &opt)) < 0) {
2219                 (void) NetClose(net);
2220                 net = -1;
2221                 continue;
2222             }
2223             if (srp && setsockopt(net, proto, opt, srp, srlen) < 0)
2224                 perror("setsockopt (source route)");
2225         }
2226 #endif
2227
2228 #if     defined(IPPROTO_IP) && defined(IP_TOS)
2229             if (a->ai_family == AF_INET) {
2230 # if    defined(HAVE_GETTOSBYNAME)
2231                 struct tosent *tp;
2232                 if (tos < 0 && (tp = gettosbyname("telnet", "tcp")))
2233                     tos = tp->t_tos;
2234 # endif
2235                 if (tos < 0)
2236                     tos = 020;  /* Low Delay bit */
2237                 if (tos
2238                     && (setsockopt(net, IPPROTO_IP, IP_TOS,
2239                                    (void *)&tos, sizeof(int)) < 0)
2240                     && (errno != ENOPROTOOPT))
2241                     perror("telnet: setsockopt (IP_TOS) (ignored)");
2242             }
2243 #endif  /* defined(IPPROTO_IP) && defined(IP_TOS) */
2244             if (debug && SetSockOpt(net, SOL_SOCKET, SO_DEBUG, 1) < 0) {
2245                 perror("setsockopt (SO_DEBUG)");
2246             }
2247
2248             if (connect (net, a->ai_addr, a->ai_addrlen) < 0) {
2249                 fprintf (stderr, "telnet: connect to address %s: %s\n",
2250                          addrstr, strerror(errno));
2251                 NetClose(net);
2252                 if (a->ai_next != NULL) {
2253                     continue;
2254                 } else {
2255                     freeaddrinfo (ai);
2256                     return 0;
2257                 }
2258             }
2259             ++connected;
2260 #if     defined(AUTHENTICATION) || defined(ENCRYPTION)
2261             auth_encrypt_connect(connected);
2262 #endif
2263         }
2264         freeaddrinfo (ai);
2265         if (connected == 0)
2266             return 0;
2267     }
2268     cmdrc(hostp, hostname);
2269     set_forward_options();
2270     if (autologin && user == NULL)
2271         user = (char *)get_default_username ();
2272     if (user) {
2273         env_define((unsigned char *)"USER", (unsigned char *)user);
2274         env_export((unsigned char *)"USER");
2275     }
2276     call(status, "status", "notmuch", 0);
2277     if (setjmp(peerdied) == 0)
2278         my_telnet((char *)user);
2279     NetClose(net);
2280     ExitString("Connection closed by foreign host.\r\n",1);
2281     /*NOTREACHED*/
2282     return 0;
2283 }
2284
2285 #define HELPINDENT ((int)sizeof ("connect"))
2286
2287 static char
2288         openhelp[] =    "connect to a site",
2289         closehelp[] =   "close current connection",
2290         logouthelp[] =  "forcibly logout remote user and close the connection",
2291         quithelp[] =    "exit telnet",
2292         statushelp[] =  "print status information",
2293         helphelp[] =    "print help information",
2294         sendhelp[] =    "transmit special characters ('send ?' for more)",
2295         sethelp[] =     "set operating parameters ('set ?' for more)",
2296         unsethelp[] =   "unset operating parameters ('unset ?' for more)",
2297         togglestring[] ="toggle operating parameters ('toggle ?' for more)",
2298         slchelp[] =     "change state of special charaters ('slc ?' for more)",
2299         displayhelp[] = "display operating parameters",
2300 #if     defined(AUTHENTICATION)
2301         authhelp[] =    "turn on (off) authentication ('auth ?' for more)",
2302 #endif
2303 #if     defined(ENCRYPTION)
2304         encrypthelp[] = "turn on (off) encryption ('encrypt ?' for more)",
2305 #endif
2306         zhelp[] =       "suspend telnet",
2307         shellhelp[] =   "invoke a subshell",
2308         envhelp[] =     "change environment variables ('environ ?' for more)",
2309         modestring[] = "try to enter line or character mode ('mode ?' for more)";
2310
2311 static int help(int argc, char **argv);
2312
2313 static Command cmdtab[] = {
2314         { "close",      closehelp,      bye,            1 },
2315         { "logout",     logouthelp,     logout,         1 },
2316         { "display",    displayhelp,    display,        0 },
2317         { "mode",       modestring,     modecmd,        0 },
2318         { "open",       openhelp,       tn,             0 },
2319         { "quit",       quithelp,       quit,           0 },
2320         { "send",       sendhelp,       sendcmd,        0 },
2321         { "set",        sethelp,        setcmd,         0 },
2322         { "unset",      unsethelp,      unsetcmd,       0 },
2323         { "status",     statushelp,     status,         0 },
2324         { "toggle",     togglestring,   toggle,         0 },
2325         { "slc",        slchelp,        slccmd,         0 },
2326 #if     defined(AUTHENTICATION)
2327         { "auth",       authhelp,       auth_cmd,       0 },
2328 #endif
2329 #if     defined(ENCRYPTION)
2330         { "encrypt",    encrypthelp,    encrypt_cmd,    0 },
2331 #endif
2332         { "z",          zhelp,          telnetsuspend,  0 },
2333         { "!",          shellhelp,      shell,          0 },
2334         { "environ",    envhelp,        env_cmd,        0 },
2335         { "?",          helphelp,       help,           0 },
2336         { 0,            0,              0,              0 }
2337 };
2338
2339 static char     crmodhelp[] =   "deprecated command -- use 'toggle crmod' instead";
2340 static char     escapehelp[] =  "deprecated command -- use 'set escape' instead";
2341
2342 static Command cmdtab2[] = {
2343         { "help",       0,              help,           0 },
2344         { "escape",     escapehelp,     setescape,      0 },
2345         { "crmod",      crmodhelp,      togcrmod,       0 },
2346         { 0,            0,              0,              0 }
2347 };
2348
2349
2350 /*
2351  * Call routine with argc, argv set from args (terminated by 0).
2352  */
2353
2354 static int
2355 call(intrtn_t routine, ...)
2356 {
2357     va_list ap;
2358     char *args[100];
2359     int argno = 0;
2360
2361     va_start(ap, routine);
2362     while ((args[argno++] = va_arg(ap, char *)) != 0);
2363     va_end(ap);
2364     return (*routine)(argno-1, args);
2365 }
2366
2367
2368 static Command
2369 *getcmd(char *name)
2370 {
2371     Command *cm;
2372
2373     if ((cm = (Command *) genget(name, (char **) cmdtab, sizeof(Command))))
2374         return cm;
2375     return (Command *) genget(name, (char **) cmdtab2, sizeof(Command));
2376 }
2377
2378 void
2379 command(int top, char *tbuf, int cnt)
2380 {
2381     Command *c;
2382
2383     setcommandmode();
2384     if (!top) {
2385         putchar('\n');
2386     } else {
2387         signal(SIGINT, SIG_DFL);
2388         signal(SIGQUIT, SIG_DFL);
2389     }
2390     for (;;) {
2391         if (rlogin == _POSIX_VDISABLE)
2392                 printf("%s> ", prompt);
2393         if (tbuf) {
2394             char *cp;
2395             cp = line;
2396             while (cnt > 0 && (*cp++ = *tbuf++) != '\n')
2397                 cnt--;
2398             tbuf = 0;
2399             if (cp == line || *--cp != '\n' || cp == line)
2400                 goto getline;
2401             *cp = '\0';
2402             if (rlogin == _POSIX_VDISABLE)
2403                 printf("%s\r\n", line);
2404         } else {
2405         getline:
2406             if (rlogin != _POSIX_VDISABLE)
2407                 printf("%s> ", prompt);
2408             if (fgets(line, sizeof(line), stdin) == NULL) {
2409                 if (feof(stdin) || ferror(stdin)) {
2410                     quit();
2411                     /*NOTREACHED*/
2412                 }
2413                 break;
2414             }
2415         }
2416         if (line[0] == 0)
2417             break;
2418         makeargv();
2419         if (margv[0] == 0) {
2420             break;
2421         }
2422         c = getcmd(margv[0]);
2423         if (Ambiguous(c)) {
2424             printf("?Ambiguous command\r\n");
2425             continue;
2426         }
2427         if (c == 0) {
2428             printf("?Invalid command\r\n");
2429             continue;
2430         }
2431         if (c->needconnect && !connected) {
2432             printf("?Need to be connected first.\r\n");
2433             continue;
2434         }
2435         if ((*c->handler)(margc, margv)) {
2436             break;
2437         }
2438     }
2439     if (!top) {
2440         if (!connected) {
2441             longjmp(toplevel, 1);
2442             /*NOTREACHED*/
2443         }
2444         setconnmode(0);
2445     }
2446 }
2447 \f
2448 /*
2449  * Help command.
2450  */
2451 static int
2452 help(int argc, char **argv)
2453 {
2454         Command *c;
2455
2456         if (argc == 1) {
2457                 printf("Commands may be abbreviated.  Commands are:\r\n\r\n");
2458                 for (c = cmdtab; c->name; c++)
2459                         if (c->help) {
2460                                 printf("%-*s\t%s\r\n", HELPINDENT, c->name,
2461                                                                     c->help);
2462                         }
2463                 return 0;
2464         }
2465         while (--argc > 0) {
2466                 char *arg;
2467                 arg = *++argv;
2468                 c = getcmd(arg);
2469                 if (Ambiguous(c))
2470                         printf("?Ambiguous help command %s\r\n", arg);
2471                 else if (c == (Command *)0)
2472                         printf("?Invalid help command %s\r\n", arg);
2473                 else
2474                         printf("%s\r\n", c->help);
2475         }
2476         return 0;
2477 }
2478
2479
2480 #if     defined(IP_OPTIONS) && defined(IPPROTO_IP)
2481
2482 /*
2483  * Source route is handed in as
2484  *      [!]@hop1@hop2...@dst
2485  *
2486  * If the leading ! is present, it is a strict source route, otherwise it is
2487  * assmed to be a loose source route.  Note that leading ! is effective
2488  * only for IPv4 case.
2489  *
2490  * We fill in the source route option as
2491  *      hop1,hop2,hop3...dest
2492  * and return a pointer to hop1, which will
2493  * be the address to connect() to.
2494  *
2495  * Arguments:
2496  *      ai:     The address (by struct addrinfo) for the final destination.
2497  *
2498  *      arg:    Pointer to route list to decipher
2499  *
2500  *      cpp:    Pointer to a pointer, so that sourceroute() can return
2501  *              the address of result buffer (statically alloc'ed).
2502  *
2503  *      protop/optp:
2504  *              Pointer to an integer.  The pointed variable
2505  *      lenp:   pointer to an integer that contains the
2506  *              length of *cpp if *cpp != NULL.
2507  *
2508  * Return values:
2509  *
2510  *      Returns the length of the option pointed to by *cpp.  If the
2511  *      return value is -1, there was a syntax error in the
2512  *      option, either arg contained unknown characters or too many hosts,
2513  *      or hostname cannot be resolved.
2514  *
2515  *      The caller needs to pass return value (len), *cpp, *protop and *optp
2516  *      to setsockopt(2).
2517  *
2518  *      *cpp:   Points to the result buffer.  The region is statically
2519  *              allocated by the function.
2520  *
2521  *      *protop:
2522  *              protocol # to be passed to setsockopt(2).
2523  *
2524  *      *optp:  option # to be passed to setsockopt(2).
2525  *
2526  */
2527 int
2528 sourceroute(struct addrinfo *ai,
2529             char *arg,
2530             char **cpp,
2531             int *protop,
2532             int *optp)
2533 {
2534         char *cp, *cp2, *lsrp = NULL, *lsrep = NULL;
2535         struct addrinfo hints, *res;
2536         int len, error;
2537         struct sockaddr_in *sin;
2538         register char c;
2539         static char lsr[44];
2540 #ifdef INET6
2541         struct cmsghdr *cmsg = NULL;
2542         struct sockaddr_in6 *sin6;
2543         static char rhbuf[1024];
2544 #endif
2545
2546         /*
2547          * Verify the arguments.
2548          */
2549         if (cpp == NULL)
2550                 return -1;
2551
2552         cp = arg;
2553
2554         *cpp = NULL;
2555         switch (ai->ai_family) {
2556         case AF_INET:
2557                 lsrp = lsr;
2558                 lsrep = lsrp + sizeof(lsr);
2559
2560                 /*
2561                  * Next, decide whether we have a loose source
2562                  * route or a strict source route, and fill in
2563                  * the begining of the option.
2564                  */
2565                 if (*cp == '!') {
2566                         cp++;
2567                         *lsrp++ = IPOPT_SSRR;
2568                 } else
2569                         *lsrp++ = IPOPT_LSRR;
2570                 if (*cp != '@')
2571                         return -1;
2572                 lsrp++;         /* skip over length, we'll fill it in later */
2573                 *lsrp++ = 4;
2574                 cp++;
2575                 *protop = IPPROTO_IP;
2576                 *optp = IP_OPTIONS;
2577                 break;
2578 #ifdef INET6
2579         case AF_INET6:
2580 /* this needs to be updated for rfc2292bis */
2581 #ifdef IPV6_PKTOPTIONS
2582                 cmsg = inet6_rthdr_init(rhbuf, IPV6_RTHDR_TYPE_0);
2583                 if (*cp != '@')
2584                         return -1;
2585                 cp++;
2586                 *protop = IPPROTO_IPV6;
2587                 *optp = IPV6_PKTOPTIONS;
2588                 break;
2589 #else
2590                 return -1;
2591 #endif
2592 #endif
2593         default:
2594                 return -1;
2595         }
2596
2597         memset(&hints, 0, sizeof(hints));
2598         hints.ai_family = ai->ai_family;
2599         hints.ai_socktype = SOCK_STREAM;
2600
2601         for (c = 0;;) {
2602                 if (c == ':')
2603                         cp2 = 0;
2604                 else for (cp2 = cp; (c = *cp2) != '\0'; cp2++) {
2605                         if (c == ',') {
2606                                 *cp2++ = '\0';
2607                                 if (*cp2 == '@')
2608                                         cp2++;
2609                         } else if (c == '@') {
2610                                 *cp2++ = '\0';
2611                         }
2612 #if 0   /*colon conflicts with IPv6 address*/
2613                         else if (c == ':') {
2614                                 *cp2++ = '\0';
2615                         }
2616 #endif
2617                         else
2618                                 continue;
2619                         break;
2620                 }
2621                 if (!c)
2622                         cp2 = 0;
2623
2624                 error = getaddrinfo(cp, NULL, &hints, &res);
2625                 if (error) {
2626                         fprintf(stderr, "%s: %s\n", cp, gai_strerror(error));
2627                         return -1;
2628                 }
2629                 if (ai->ai_family != res->ai_family) {
2630                         freeaddrinfo(res);
2631                         return -1;
2632                 }
2633                 if (ai->ai_family == AF_INET) {
2634                         /*
2635                          * Check to make sure there is space for address
2636                          */
2637                         if (lsrp + 4 > lsrep) {
2638                                 freeaddrinfo(res);
2639                                 return -1;
2640                         }
2641                         sin = (struct sockaddr_in *)res->ai_addr;
2642                         memcpy(lsrp, &sin->sin_addr, sizeof(struct in_addr));
2643                         lsrp += sizeof(struct in_addr);
2644                 }
2645 #ifdef INET6
2646                 else if (ai->ai_family == AF_INET6) {
2647                         sin6 = (struct sockaddr_in6 *)res->ai_addr;
2648                         inet6_rthdr_add(cmsg, &sin6->sin6_addr,
2649                                 IPV6_RTHDR_LOOSE);
2650                 }
2651 #endif
2652                 else {
2653                         freeaddrinfo(res);
2654                         return -1;
2655                 }
2656                 freeaddrinfo(res);
2657                 if (cp2)
2658                         cp = cp2;
2659                 else
2660                         break;
2661         }
2662         if (ai->ai_family == AF_INET) {
2663                 /* record the last hop */
2664                 if (lsrp + 4 > lsrep)
2665                         return -1;
2666                 sin = (struct sockaddr_in *)ai->ai_addr;
2667                 memcpy(lsrp, &sin->sin_addr, sizeof(struct in_addr));
2668                 lsrp += sizeof(struct in_addr);
2669 #ifndef sysV88
2670                 lsr[IPOPT_OLEN] = lsrp - lsr;
2671                 if (lsr[IPOPT_OLEN] <= 7 || lsr[IPOPT_OLEN] > 40)
2672                         return -1;
2673                 *lsrp++ = IPOPT_NOP;    /*32bit word align*/
2674                 len = lsrp - lsr;
2675                 *cpp = lsr;
2676 #else
2677                 ipopt.io_len = lsrp - lsr;
2678                 if (ipopt.io_len <= 5)  /*is 3 better?*/
2679                         return -1;
2680                 *cpp = (char 8)&ipopt;
2681 #endif
2682         }
2683 #ifdef INET6
2684         else if (ai->ai_family == AF_INET6) {
2685                 inet6_rthdr_lasthop(cmsg, IPV6_RTHDR_LOOSE);
2686                 len = cmsg->cmsg_len;
2687                 *cpp = rhbuf;
2688         }
2689 #endif
2690         else
2691                 return -1;
2692         return len;
2693 }
2694 #endif