xref: /openbsd-src/usr.bin/ftp/cmds.c (revision b2ea75c1b17e1a9a339660e7ed45cd24946b230e)
1 /*	$OpenBSD: cmds.c,v 1.37 2001/07/12 05:17:07 deraadt Exp $	*/
2 /*	$NetBSD: cmds.c,v 1.27 1997/08/18 10:20:15 lukem Exp $	*/
3 
4 /*
5  * Copyright (C) 1997 and 1998 WIDE Project.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the project nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 /*
34  * Copyright (c) 1985, 1989, 1993, 1994
35  *	The Regents of the University of California.  All rights reserved.
36  *
37  * Redistribution and use in source and binary forms, with or without
38  * modification, are permitted provided that the following conditions
39  * are met:
40  * 1. Redistributions of source code must retain the above copyright
41  *    notice, this list of conditions and the following disclaimer.
42  * 2. Redistributions in binary form must reproduce the above copyright
43  *    notice, this list of conditions and the following disclaimer in the
44  *    documentation and/or other materials provided with the distribution.
45  * 3. All advertising materials mentioning features or use of this software
46  *    must display the following acknowledgement:
47  *	This product includes software developed by the University of
48  *	California, Berkeley and its contributors.
49  * 4. Neither the name of the University nor the names of its contributors
50  *    may be used to endorse or promote products derived from this software
51  *    without specific prior written permission.
52  *
53  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
54  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
55  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
56  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
57  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
58  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
59  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
60  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
61  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
62  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
63  * SUCH DAMAGE.
64  */
65 
66 #ifndef lint
67 #if 0
68 static char sccsid[] = "@(#)cmds.c	8.6 (Berkeley) 10/9/94";
69 #else
70 static char rcsid[] = "$OpenBSD: cmds.c,v 1.37 2001/07/12 05:17:07 deraadt Exp $";
71 #endif
72 #endif /* not lint */
73 
74 /*
75  * FTP User Program -- Command Routines.
76  */
77 #include <sys/types.h>
78 #include <sys/socket.h>
79 #include <sys/stat.h>
80 #include <sys/wait.h>
81 #include <arpa/ftp.h>
82 
83 #include <ctype.h>
84 #include <err.h>
85 #include <glob.h>
86 #include <netdb.h>
87 #include <stdio.h>
88 #include <stdlib.h>
89 #include <string.h>
90 #include <unistd.h>
91 
92 #include "ftp_var.h"
93 #include "pathnames.h"
94 
95 jmp_buf	jabort;
96 char   *mname;
97 char   *home = "/";
98 
99 struct	types {
100 	char	*t_name;
101 	char	*t_mode;
102 	int	t_type;
103 	char	*t_arg;
104 } types[] = {
105 	{ "ascii",	"A",	TYPE_A,	0 },
106 	{ "binary",	"I",	TYPE_I,	0 },
107 	{ "image",	"I",	TYPE_I,	0 },
108 	{ "ebcdic",	"E",	TYPE_E,	0 },
109 	{ "tenex",	"L",	TYPE_L,	bytename },
110 	{ NULL }
111 };
112 
113 /*
114  * Set transfer type.
115  */
116 void
117 settype(argc, argv)
118 	int argc;
119 	char *argv[];
120 {
121 	struct types *p;
122 	int comret;
123 
124 	if (argc > 2) {
125 		char *sep;
126 
127 		fprintf(ttyout, "usage: %s [", argv[0]);
128 		sep = " ";
129 		for (p = types; p->t_name; p++) {
130 			fprintf(ttyout, "%s%s", sep, p->t_name);
131 			sep = " | ";
132 		}
133 		fputs(" ]\n", ttyout);
134 		code = -1;
135 		return;
136 	}
137 	if (argc < 2) {
138 		fprintf(ttyout, "Using %s mode to transfer files.\n", typename);
139 		code = 0;
140 		return;
141 	}
142 	for (p = types; p->t_name; p++)
143 		if (strcmp(argv[1], p->t_name) == 0)
144 			break;
145 	if (p->t_name == 0) {
146 		fprintf(ttyout, "%s: unknown mode.\n", argv[1]);
147 		code = -1;
148 		return;
149 	}
150 	if ((p->t_arg != NULL) && (*(p->t_arg) != '\0'))
151 		comret = command("TYPE %s %s", p->t_mode, p->t_arg);
152 	else
153 		comret = command("TYPE %s", p->t_mode);
154 	if (comret == COMPLETE) {
155 		(void)strcpy(typename, p->t_name);
156 		curtype = type = p->t_type;
157 	}
158 }
159 
160 /*
161  * Internal form of settype; changes current type in use with server
162  * without changing our notion of the type for data transfers.
163  * Used to change to and from ascii for listings.
164  */
165 void
166 changetype(newtype, show)
167 	int newtype, show;
168 {
169 	struct types *p;
170 	int comret, oldverbose = verbose;
171 
172 	if (newtype == 0)
173 		newtype = TYPE_I;
174 	if (newtype == curtype)
175 		return;
176 	if (debug == 0 && show == 0)
177 		verbose = 0;
178 	for (p = types; p->t_name; p++)
179 		if (newtype == p->t_type)
180 			break;
181 	if (p->t_name == 0) {
182 		warnx("internal error: unknown type %d.", newtype);
183 		return;
184 	}
185 	if (newtype == TYPE_L && bytename[0] != '\0')
186 		comret = command("TYPE %s %s", p->t_mode, bytename);
187 	else
188 		comret = command("TYPE %s", p->t_mode);
189 	if (comret == COMPLETE)
190 		curtype = newtype;
191 	verbose = oldverbose;
192 }
193 
194 char *stype[] = {
195 	"type",
196 	"",
197 	0
198 };
199 
200 /*
201  * Set binary transfer type.
202  */
203 /*VARARGS*/
204 void
205 setbinary(argc, argv)
206 	int argc;
207 	char *argv[];
208 {
209 
210 	stype[1] = "binary";
211 	settype(2, stype);
212 }
213 
214 /*
215  * Set ascii transfer type.
216  */
217 /*VARARGS*/
218 void
219 setascii(argc, argv)
220 	int argc;
221 	char *argv[];
222 {
223 
224 	stype[1] = "ascii";
225 	settype(2, stype);
226 }
227 
228 /*
229  * Set tenex transfer type.
230  */
231 /*VARARGS*/
232 void
233 settenex(argc, argv)
234 	int argc;
235 	char *argv[];
236 {
237 
238 	stype[1] = "tenex";
239 	settype(2, stype);
240 }
241 
242 /*
243  * Set file transfer mode.
244  */
245 /*ARGSUSED*/
246 void
247 setftmode(argc, argv)
248 	int argc;
249 	char *argv[];
250 {
251 
252 	fprintf(ttyout, "We only support %s mode, sorry.\n", modename);
253 	code = -1;
254 }
255 
256 /*
257  * Set file transfer format.
258  */
259 /*ARGSUSED*/
260 void
261 setform(argc, argv)
262 	int argc;
263 	char *argv[];
264 {
265 
266 	fprintf(ttyout, "We only support %s format, sorry.\n", formname);
267 	code = -1;
268 }
269 
270 /*
271  * Set file transfer structure.
272  */
273 /*ARGSUSED*/
274 void
275 setstruct(argc, argv)
276 	int argc;
277 	char *argv[];
278 {
279 
280 	fprintf(ttyout, "We only support %s structure, sorry.\n", structname);
281 	code = -1;
282 }
283 
284 /*
285  * Send a single file.
286  */
287 void
288 put(argc, argv)
289 	int argc;
290 	char *argv[];
291 {
292 	char *cmd;
293 	int loc = 0;
294 	char *oldargv1, *oldargv2;
295 
296 	if (argc == 2) {
297 		argc++;
298 		argv[2] = argv[1];
299 		loc++;
300 	}
301 	if (argc < 2 && !another(&argc, &argv, "local-file"))
302 		goto usage;
303 	if ((argc < 3 && !another(&argc, &argv, "remote-file")) || argc > 3) {
304 usage:
305 		fprintf(ttyout, "usage: %s local-file [ remote-file ]\n", argv[0]);
306 		code = -1;
307 		return;
308 	}
309 	oldargv1 = argv[1];
310 	oldargv2 = argv[2];
311 	if (!globulize(&argv[1])) {
312 		code = -1;
313 		return;
314 	}
315 	/*
316 	 * If "globulize" modifies argv[1], and argv[2] is a copy of
317 	 * the old argv[1], make it a copy of the new argv[1].
318 	 */
319 	if (argv[1] != oldargv1 && argv[2] == oldargv1) {
320 		argv[2] = argv[1];
321 	}
322 	cmd = (argv[0][0] == 'a') ? "APPE" : ((sunique) ? "STOU" : "STOR");
323 	if (loc && ntflag) {
324 		argv[2] = dotrans(argv[2]);
325 	}
326 	if (loc && mapflag) {
327 		argv[2] = domap(argv[2]);
328 	}
329 	sendrequest(cmd, argv[1], argv[2],
330 	    argv[1] != oldargv1 || argv[2] != oldargv2);
331 	if (oldargv1 != argv[1])	/* free up after globulize() */
332 		free(argv[1]);
333 }
334 
335 /*
336  * Send multiple files.
337  */
338 void
339 mput(argc, argv)
340 	int argc;
341 	char *argv[];
342 {
343 	int i;
344 	sig_t oldintr;
345 	int ointer;
346 	char *tp;
347 
348 	if (argc < 2 && !another(&argc, &argv, "local-files")) {
349 		fprintf(ttyout, "usage: %s local-files\n", argv[0]);
350 		code = -1;
351 		return;
352 	}
353 	mname = argv[0];
354 	mflag = 1;
355 	oldintr = signal(SIGINT, mabort);
356 	(void)setjmp(jabort);
357 	if (proxy) {
358 		char *cp, *tp2, tmpbuf[MAXPATHLEN];
359 
360 		while ((cp = remglob(argv, 0, NULL)) != NULL) {
361 			if (*cp == '\0') {
362 				mflag = 0;
363 				continue;
364 			}
365 			if (mflag && confirm(argv[0], cp)) {
366 				tp = cp;
367 				if (mcase) {
368 					while (*tp && !islower(*tp)) {
369 						tp++;
370 					}
371 					if (!*tp) {
372 						tp = cp;
373 						tp2 = tmpbuf;
374 						while ((*tp2 = *tp) != '\0') {
375 						     if (isupper(*tp2)) {
376 							    *tp2 =
377 								tolower(*tp2);
378 						     }
379 						     tp++;
380 						     tp2++;
381 						}
382 					}
383 					tp = tmpbuf;
384 				}
385 				if (ntflag) {
386 					tp = dotrans(tp);
387 				}
388 				if (mapflag) {
389 					tp = domap(tp);
390 				}
391 				sendrequest((sunique) ? "STOU" : "STOR",
392 				    cp, tp, cp != tp || !interactive);
393 				if (!mflag && fromatty) {
394 					ointer = interactive;
395 					interactive = 1;
396 					if (confirm("Continue with", "mput")) {
397 						mflag++;
398 					}
399 					interactive = ointer;
400 				}
401 			}
402 		}
403 		(void)signal(SIGINT, oldintr);
404 		mflag = 0;
405 		return;
406 	}
407 	for (i = 1; i < argc; i++) {
408 		char **cpp;
409 		glob_t gl;
410 		int flags;
411 
412 		if (!doglob) {
413 			if (mflag && confirm(argv[0], argv[i])) {
414 				tp = (ntflag) ? dotrans(argv[i]) : argv[i];
415 				tp = (mapflag) ? domap(tp) : tp;
416 				sendrequest((sunique) ? "STOU" : "STOR",
417 				    argv[i], tp, tp != argv[i] || !interactive);
418 				if (!mflag && fromatty) {
419 					ointer = interactive;
420 					interactive = 1;
421 					if (confirm("Continue with", "mput")) {
422 						mflag++;
423 					}
424 					interactive = ointer;
425 				}
426 			}
427 			continue;
428 		}
429 
430 		memset(&gl, 0, sizeof(gl));
431 		flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
432 		if (glob(argv[i], flags, NULL, &gl) || gl.gl_pathc == 0) {
433 			warnx("%s: not found", argv[i]);
434 			globfree(&gl);
435 			continue;
436 		}
437 		for (cpp = gl.gl_pathv; cpp && *cpp != NULL; cpp++) {
438 			if (mflag && confirm(argv[0], *cpp)) {
439 				tp = (ntflag) ? dotrans(*cpp) : *cpp;
440 				tp = (mapflag) ? domap(tp) : tp;
441 				sendrequest((sunique) ? "STOU" : "STOR",
442 				    *cpp, tp, *cpp != tp || !interactive);
443 				if (!mflag && fromatty) {
444 					ointer = interactive;
445 					interactive = 1;
446 					if (confirm("Continue with", "mput")) {
447 						mflag++;
448 					}
449 					interactive = ointer;
450 				}
451 			}
452 		}
453 		globfree(&gl);
454 	}
455 	(void)signal(SIGINT, oldintr);
456 	mflag = 0;
457 }
458 
459 void
460 reget(argc, argv)
461 	int argc;
462 	char *argv[];
463 {
464 
465 	(void)getit(argc, argv, 1, "r+w");
466 }
467 
468 void
469 get(argc, argv)
470 	int argc;
471 	char *argv[];
472 {
473 
474 	(void)getit(argc, argv, 0, restart_point ? "r+w" : "w" );
475 }
476 
477 /*
478  * Receive one file.
479  */
480 int
481 getit(argc, argv, restartit, mode)
482 	int argc;
483 	char *argv[];
484 	int restartit;
485 	const char *mode;
486 {
487 	int loc = 0;
488 	int rval = 0;
489 	char *oldargv1, *oldargv2, *globargv2;
490 
491 	if (argc == 2) {
492 		argc++;
493 		argv[2] = argv[1];
494 		loc++;
495 	}
496 	if (argc < 2 && !another(&argc, &argv, "remote-file"))
497 		goto usage;
498 	if ((argc < 3 && !another(&argc, &argv, "local-file")) || argc > 3) {
499 usage:
500 		fprintf(ttyout, "usage: %s remote-file [ local-file ]\n", argv[0]);
501 		code = -1;
502 		return (0);
503 	}
504 	oldargv1 = argv[1];
505 	oldargv2 = argv[2];
506 	if (!globulize(&argv[2])) {
507 		code = -1;
508 		return (0);
509 	}
510 	globargv2 = argv[2];
511 	if (loc && mcase) {
512 		char *tp = argv[1], *tp2, tmpbuf[MAXPATHLEN];
513 
514 		while (*tp && !islower(*tp)) {
515 			tp++;
516 		}
517 		if (!*tp) {
518 			tp = argv[2];
519 			tp2 = tmpbuf;
520 			while ((*tp2 = *tp) != '\0') {
521 				if (isupper(*tp2)) {
522 					*tp2 = tolower(*tp2);
523 				}
524 				tp++;
525 				tp2++;
526 			}
527 			argv[2] = tmpbuf;
528 		}
529 	}
530 	if (loc && ntflag)
531 		argv[2] = dotrans(argv[2]);
532 	if (loc && mapflag)
533 		argv[2] = domap(argv[2]);
534 	if (restartit) {
535 		struct stat stbuf;
536 		int ret;
537 
538 		ret = stat(argv[2], &stbuf);
539 		if (restartit == 1) {
540 			if (ret < 0) {
541 				warn("local: %s", argv[2]);
542 				goto freegetit;
543 			}
544 			restart_point = stbuf.st_size;
545 		} else {
546 			if (ret == 0) {
547 				time_t mtime;
548 
549 				mtime = remotemodtime(argv[1], 0);
550 				if (mtime == -1)
551 					goto freegetit;
552 				if (stbuf.st_mtime >= mtime) {
553 					rval = 1;
554 					goto freegetit;
555 				}
556 			}
557 		}
558 	}
559 
560 	recvrequest("RETR", argv[2], argv[1], mode,
561 	    argv[1] != oldargv1 || argv[2] != oldargv2, loc);
562 	restart_point = 0;
563 freegetit:
564 	if (oldargv2 != globargv2)	/* free up after globulize() */
565 		free(globargv2);
566 	return (rval);
567 }
568 
569 /* ARGSUSED */
570 void
571 mabort(signo)
572 	int signo;
573 {
574 	int ointer, oconf;
575 
576 	alarmtimer(0);
577 	putc('\n', ttyout);
578 	(void)fflush(ttyout);
579 	if (mflag && fromatty) {
580 		ointer = interactive;
581 		oconf = confirmrest;
582 		interactive = 1;
583 		confirmrest = 0;
584 		if (confirm("Continue with", mname)) {
585 			interactive = ointer;
586 			confirmrest = oconf;
587 			longjmp(jabort, 0);
588 		}
589 		interactive = ointer;
590 		confirmrest = oconf;
591 	}
592 	mflag = 0;
593 	longjmp(jabort, 0);
594 }
595 
596 /*
597  * Get multiple files.
598  */
599 void
600 mget(argc, argv)
601 	int argc;
602 	char *argv[];
603 {
604 	sig_t oldintr;
605 	int ch, ointer;
606 	char *cp, *tp, *tp2, tmpbuf[MAXPATHLEN];
607 
608 	if (argc < 2 && !another(&argc, &argv, "remote-files")) {
609 		fprintf(ttyout, "usage: %s remote-files\n", argv[0]);
610 		code = -1;
611 		return;
612 	}
613 	mname = argv[0];
614 	mflag = 1;
615 	oldintr = signal(SIGINT, mabort);
616 	(void)setjmp(jabort);
617 	while ((cp = remglob(argv, proxy, NULL)) != NULL) {
618 		if (*cp == '\0') {
619 			mflag = 0;
620 			continue;
621 		}
622 		if (mflag && confirm(argv[0], cp)) {
623 			tp = cp;
624 			if (mcase) {
625 				for (tp2 = tmpbuf; (ch = *tp++) != 0; )
626 					*tp2++ = isupper(ch) ? tolower(ch) : ch;
627 				*tp2 = '\0';
628 				tp = tmpbuf;
629 			}
630 			if (ntflag)
631 				tp = dotrans(tp);
632 			if (mapflag)
633 				tp = domap(tp);
634 			recvrequest("RETR", tp, cp, "w",
635 			    tp != cp || !interactive, 1);
636 			if (!mflag && fromatty) {
637 				ointer = interactive;
638 				interactive = 1;
639 				if (confirm("Continue with", "mget")) {
640 					mflag++;
641 				}
642 				interactive = ointer;
643 			}
644 		}
645 	}
646 	(void)signal(SIGINT, oldintr);
647 	mflag = 0;
648 }
649 
650 char *
651 onoff(bool)
652 	int bool;
653 {
654 
655 	return (bool ? "on" : "off");
656 }
657 
658 /*
659  * Show status.
660  */
661 /*ARGSUSED*/
662 void
663 status(argc, argv)
664 	int argc;
665 	char *argv[];
666 {
667 	int i;
668 
669 	if (connected)
670 		fprintf(ttyout, "Connected %sto %s.\n",
671 		    connected == -1 ? "and logged in" : "", hostname);
672 	else
673 		fputs("Not connected.\n", ttyout);
674 	if (!proxy) {
675 		pswitch(1);
676 		if (connected) {
677 			fprintf(ttyout, "Connected for proxy commands to %s.\n",
678 			    hostname);
679 		}
680 		else {
681 			fputs("No proxy connection.\n", ttyout);
682 		}
683 		pswitch(0);
684 	}
685 	fprintf(ttyout, "Gate ftp: %s, server %s, port %s.\n", onoff(gatemode),
686 	    *gateserver ? gateserver : "(none)", gateport);
687 	fprintf(ttyout, "Passive mode: %s.\n", onoff(passivemode));
688 	fprintf(ttyout, "Mode: %s; Type: %s; Form: %s; Structure: %s.\n",
689 		modename, typename, formname, structname);
690 	fprintf(ttyout, "Verbose: %s; Bell: %s; Prompting: %s; Globbing: %s.\n",
691 		onoff(verbose), onoff(bell), onoff(interactive),
692 		onoff(doglob));
693 	fprintf(ttyout, "Store unique: %s; Receive unique: %s.\n", onoff(sunique),
694 		onoff(runique));
695 	fprintf(ttyout, "Preserve modification times: %s.\n", onoff(preserve));
696 	fprintf(ttyout, "Case: %s; CR stripping: %s.\n", onoff(mcase), onoff(crflag));
697 	if (ntflag) {
698 		fprintf(ttyout, "Ntrans: (in) %s (out) %s\n", ntin, ntout);
699 	}
700 	else {
701 		fputs("Ntrans: off.\n", ttyout);
702 	}
703 	if (mapflag) {
704 		fprintf(ttyout, "Nmap: (in) %s (out) %s\n", mapin, mapout);
705 	}
706 	else {
707 		fputs("Nmap: off.\n", ttyout);
708 	}
709 	fprintf(ttyout, "Hash mark printing: %s; Mark count: %d; Progress bar: %s.\n",
710 	    onoff(hash), mark, onoff(progress));
711 	fprintf(ttyout, "Use of PORT/LPRT cmds: %s.\n", onoff(sendport));
712 	fprintf(ttyout, "Use of EPSV/EPRT cmds for IPv4: %s%s.\n", onoff(epsv4),
713 	    epsv4bad ? " (disabled for this connection)" : "");
714 #ifndef SMALL
715 	fprintf(ttyout, "Command line editing: %s.\n", onoff(editing));
716 #endif /* !SMALL */
717 	if (macnum > 0) {
718 		fputs("Macros:\n", ttyout);
719 		for (i=0; i<macnum; i++) {
720 			fprintf(ttyout, "\t%s\n", macros[i].mac_name);
721 		}
722 	}
723 	code = 0;
724 }
725 
726 /*
727  * Toggle a variable
728  */
729 int
730 togglevar(argc, argv, var, mesg)
731 	int   argc;
732 	char *argv[];
733 	int  *var;
734 	const char *mesg;
735 {
736 	if (argc < 2) {
737 		*var = !*var;
738 	} else if (argc == 2 && strcasecmp(argv[1], "on") == 0) {
739 		*var = 1;
740 	} else if (argc == 2 && strcasecmp(argv[1], "off") == 0) {
741 		*var = 0;
742 	} else {
743 		fprintf(ttyout, "usage: %s [ on | off ]\n", argv[0]);
744 		return (-1);
745 	}
746 	if (mesg)
747 		fprintf(ttyout, "%s %s.\n", mesg, onoff(*var));
748 	return (*var);
749 }
750 
751 /*
752  * Set beep on cmd completed mode.
753  */
754 /*VARARGS*/
755 void
756 setbell(argc, argv)
757 	int argc;
758 	char *argv[];
759 {
760 
761 	code = togglevar(argc, argv, &bell, "Bell mode");
762 }
763 
764 #ifndef SMALL
765 /*
766  * Set command line editing
767  */
768 /*VARARGS*/
769 void
770 setedit(argc, argv)
771 	int argc;
772 	char *argv[];
773 {
774 
775 	code = togglevar(argc, argv, &editing, "Editing mode");
776 	controlediting();
777 }
778 #endif /* !SMALL */
779 
780 /*
781  * Toggle use of IPv4 EPSV/EPRT
782  */
783 /*VARARGS*/
784 void
785 setepsv4(argc, argv)
786 	int argc;
787 	char *argv[];
788 {
789 
790 	code = togglevar(argc, argv, &epsv4, "EPSV/EPRT on IPv4");
791 	epsv4bad = 0;
792 }
793 
794 /*
795  * Turn on packet tracing.
796  */
797 /*VARARGS*/
798 void
799 settrace(argc, argv)
800 	int argc;
801 	char *argv[];
802 {
803 
804 	code = togglevar(argc, argv, &trace, "Packet tracing");
805 }
806 
807 /*
808  * Toggle hash mark printing during transfers, or set hash mark bytecount.
809  */
810 /*VARARGS*/
811 void
812 sethash(argc, argv)
813 	int argc;
814 	char *argv[];
815 {
816 	if (argc == 1)
817 		hash = !hash;
818 	else if (argc != 2) {
819 		fprintf(ttyout, "usage: %s [ on | off | bytecount ]\n", argv[0]);
820 		code = -1;
821 		return;
822 	} else if (strcasecmp(argv[1], "on") == 0)
823 		hash = 1;
824 	else if (strcasecmp(argv[1], "off") == 0)
825 		hash = 0;
826 	else {
827 		long nmark;
828 		char *ep;
829 
830 		nmark = strtol(argv[1], &ep, 10);
831 		if (nmark < 1 || nmark > INT_MAX || *ep != '\0') {
832 			fprintf(ttyout, "%s: bad bytecount value.\n", argv[1]);
833 			code = -1;
834 			return;
835 		}
836 		mark = nmark;
837 		hash = 1;
838 	}
839 	fprintf(ttyout, "Hash mark printing %s", onoff(hash));
840 	if (hash)
841 		fprintf(ttyout, " (%d bytes/hash mark)", mark);
842 	fputs(".\n", ttyout);
843 	code = hash;
844 }
845 
846 /*
847  * Turn on printing of server echo's.
848  */
849 /*VARARGS*/
850 void
851 setverbose(argc, argv)
852 	int argc;
853 	char *argv[];
854 {
855 
856 	code = togglevar(argc, argv, &verbose, "Verbose mode");
857 }
858 
859 /*
860  * Toggle PORT/LPRT cmd use before each data connection.
861  */
862 /*VARARGS*/
863 void
864 setport(argc, argv)
865 	int argc;
866 	char *argv[];
867 {
868 
869 	code = togglevar(argc, argv, &sendport, "Use of PORT/LPRT cmds");
870 }
871 
872 /*
873  * Toggle transfer progress bar.
874  */
875 /*VARARGS*/
876 void
877 setprogress(argc, argv)
878 	int argc;
879 	char *argv[];
880 {
881 
882 	code = togglevar(argc, argv, &progress, "Progress bar");
883 }
884 
885 /*
886  * Turn on interactive prompting during mget, mput, and mdelete.
887  */
888 /*VARARGS*/
889 void
890 setprompt(argc, argv)
891 	int argc;
892 	char *argv[];
893 {
894 
895 	code = togglevar(argc, argv, &interactive, "Interactive mode");
896 }
897 
898 /*
899  * Toggle gate-ftp mode, or set gate-ftp server
900  */
901 /*VARARGS*/
902 void
903 setgate(argc, argv)
904 	int argc;
905 	char *argv[];
906 {
907 	static char gsbuf[MAXHOSTNAMELEN];
908 
909 	if (argc > 3) {
910 		fprintf(ttyout, "usage: %s [ on | off | gateserver [ port ] ]\n",
911 		    argv[0]);
912 		code = -1;
913 		return;
914 	} else if (argc < 2) {
915 		gatemode = !gatemode;
916 	} else {
917 		if (argc == 2 && strcasecmp(argv[1], "on") == 0)
918 			gatemode = 1;
919 		else if (argc == 2 && strcasecmp(argv[1], "off") == 0)
920 			gatemode = 0;
921 		else {
922 			if (argc == 3) {
923 #if 0
924 				char *ep;
925 				long port;
926 
927 				port = strtol(argv[2], &ep, 10);
928 				if (port < 0 || port > USHRT_MAX || *ep != '\0') {
929 					fprintf(ttyout,
930 					    "%s: bad gateport value.\n",
931 					    argv[2]);
932 					code = -1;
933 					return;
934 				}
935 				gateport = htons(port);
936 #else
937 				gateport = strdup(argv[2]);
938 #endif
939 			}
940 			strlcpy(gsbuf, argv[1], sizeof(gsbuf));
941 			gateserver = gsbuf;
942 			gatemode = 1;
943 		}
944 	}
945 	if (gatemode && (gateserver == NULL || *gateserver == '\0')) {
946 		fprintf(ttyout,
947 		    "Disabling gate-ftp mode - no gate-ftp server defined.\n");
948 		gatemode = 0;
949 	} else {
950 		fprintf(ttyout, "Gate ftp: %s, server %s, port %s.\n",
951 		    onoff(gatemode),
952 		    *gateserver ? gateserver : "(none)", gateport);
953 	}
954 	code = gatemode;
955 }
956 
957 /*
958  * Toggle metacharacter interpretation on local file names.
959  */
960 /*VARARGS*/
961 void
962 setglob(argc, argv)
963 	int argc;
964 	char *argv[];
965 {
966 
967 	code = togglevar(argc, argv, &doglob, "Globbing");
968 }
969 
970 /*
971  * Toggle preserving modification times on retrieved files.
972  */
973 /*VARARGS*/
974 void
975 setpreserve(argc, argv)
976 	int argc;
977 	char *argv[];
978 {
979 
980 	code = togglevar(argc, argv, &preserve, "Preserve modification times");
981 }
982 
983 /*
984  * Set debugging mode on/off and/or set level of debugging.
985  */
986 /*VARARGS*/
987 void
988 setdebug(argc, argv)
989 	int argc;
990 	char *argv[];
991 {
992 	if (argc > 2) {
993 		fprintf(ttyout, "usage: %s [ on | off | debuglevel ]\n", argv[0]);
994 		code = -1;
995 		return;
996 	} else if (argc == 2) {
997 		if (strcasecmp(argv[1], "on") == 0)
998 			debug = 1;
999 		else if (strcasecmp(argv[1], "off") == 0)
1000 			debug = 0;
1001 		else {
1002 			char *ep;
1003 			long val;
1004 
1005 			val = strtol(argv[1], &ep, 10);
1006 			if (val < 0 || val > INT_MAX || *ep != '\0') {
1007 				fprintf(ttyout, "%s: bad debugging value.\n",
1008 				    argv[1]);
1009 				code = -1;
1010 				return;
1011 			}
1012 			debug = (int)val;
1013 		}
1014 	} else
1015 		debug = !debug;
1016 	if (debug)
1017 		options |= SO_DEBUG;
1018 	else
1019 		options &= ~SO_DEBUG;
1020 	fprintf(ttyout, "Debugging %s (debug=%d).\n", onoff(debug), debug);
1021 	code = debug > 0;
1022 }
1023 
1024 /*
1025  * Set current working directory on remote machine.
1026  */
1027 void
1028 cd(argc, argv)
1029 	int argc;
1030 	char *argv[];
1031 {
1032 	int r;
1033 
1034 	if ((argc < 2 && !another(&argc, &argv, "remote-directory")) ||
1035 	    argc > 2) {
1036 		fprintf(ttyout, "usage: %s remote-directory\n", argv[0]);
1037 		code = -1;
1038 		return;
1039 	}
1040 	r = command("CWD %s", argv[1]);
1041 	if (r == ERROR && code == 500) {
1042 		if (verbose)
1043 			fputs("CWD command not recognized, trying XCWD.\n", ttyout);
1044 		r = command("XCWD %s", argv[1]);
1045 	}
1046 	if (r == COMPLETE)
1047 		dirchange = 1;
1048 }
1049 
1050 /*
1051  * Set current working directory on local machine.
1052  */
1053 void
1054 lcd(argc, argv)
1055 	int argc;
1056 	char *argv[];
1057 {
1058 	char buf[MAXPATHLEN];
1059 	char *oldargv1;
1060 
1061 	if (argc < 2)
1062 		argc++, argv[1] = home;
1063 	if (argc != 2) {
1064 		fprintf(ttyout, "usage: %s local-directory\n", argv[0]);
1065 		code = -1;
1066 		return;
1067 	}
1068 	oldargv1 = argv[1];
1069 	if (!globulize(&argv[1])) {
1070 		code = -1;
1071 		return;
1072 	}
1073 	if (chdir(argv[1]) < 0) {
1074 		warn("local: %s", argv[1]);
1075 		code = -1;
1076 	} else {
1077 		if (getcwd(buf, sizeof(buf)) != NULL)
1078 			fprintf(ttyout, "Local directory now %s\n", buf);
1079 		else
1080 			warn("getcwd: %s", argv[1]);
1081 		code = 0;
1082 	}
1083 	if (oldargv1 != argv[1])	/* free up after globulize() */
1084 		free(argv[1]);
1085 }
1086 
1087 /*
1088  * Delete a single file.
1089  */
1090 void
1091 delete(argc, argv)
1092 	int argc;
1093 	char *argv[];
1094 {
1095 
1096 	if ((argc < 2 && !another(&argc, &argv, "remote-file")) || argc > 2) {
1097 		fprintf(ttyout, "usage: %s remote-file\n", argv[0]);
1098 		code = -1;
1099 		return;
1100 	}
1101 	(void)command("DELE %s", argv[1]);
1102 }
1103 
1104 /*
1105  * Delete multiple files.
1106  */
1107 void
1108 mdelete(argc, argv)
1109 	int argc;
1110 	char *argv[];
1111 {
1112 	sig_t oldintr;
1113 	int ointer;
1114 	char *cp;
1115 
1116 	if (argc < 2 && !another(&argc, &argv, "remote-files")) {
1117 		fprintf(ttyout, "usage: %s remote-files\n", argv[0]);
1118 		code = -1;
1119 		return;
1120 	}
1121 	mname = argv[0];
1122 	mflag = 1;
1123 	oldintr = signal(SIGINT, mabort);
1124 	(void)setjmp(jabort);
1125 	while ((cp = remglob(argv, 0, NULL)) != NULL) {
1126 		if (*cp == '\0') {
1127 			mflag = 0;
1128 			continue;
1129 		}
1130 		if (mflag && confirm(argv[0], cp)) {
1131 			(void)command("DELE %s", cp);
1132 			if (!mflag && fromatty) {
1133 				ointer = interactive;
1134 				interactive = 1;
1135 				if (confirm("Continue with", "mdelete")) {
1136 					mflag++;
1137 				}
1138 				interactive = ointer;
1139 			}
1140 		}
1141 	}
1142 	(void)signal(SIGINT, oldintr);
1143 	mflag = 0;
1144 }
1145 
1146 /*
1147  * Rename a remote file.
1148  */
1149 void
1150 renamefile(argc, argv)
1151 	int argc;
1152 	char *argv[];
1153 {
1154 
1155 	if (argc < 2 && !another(&argc, &argv, "from-name"))
1156 		goto usage;
1157 	if ((argc < 3 && !another(&argc, &argv, "to-name")) || argc > 3) {
1158 usage:
1159 		fprintf(ttyout, "usage: %s from-name to-name\n", argv[0]);
1160 		code = -1;
1161 		return;
1162 	}
1163 	if (command("RNFR %s", argv[1]) == CONTINUE)
1164 		(void)command("RNTO %s", argv[2]);
1165 }
1166 
1167 /*
1168  * Get a directory listing of remote files.
1169  */
1170 void
1171 ls(argc, argv)
1172 	int argc;
1173 	char *argv[];
1174 {
1175 	const char *cmd;
1176 	char *oldargv2, *globargv2;
1177 
1178 	if (argc < 2)
1179 		argc++, argv[1] = NULL;
1180 	if (argc < 3)
1181 		argc++, argv[2] = "-";
1182 	if (argc > 3) {
1183 		fprintf(ttyout, "usage: %s remote-directory local-file\n", argv[0]);
1184 		code = -1;
1185 		return;
1186 	}
1187 	cmd = strcmp(argv[0], "nlist") == 0 ? "NLST" : "LIST";
1188 	oldargv2 = argv[2];
1189 	if (strcmp(argv[2], "-") && !globulize(&argv[2])) {
1190 		code = -1;
1191 		return;
1192 	}
1193 	globargv2 = argv[2];
1194 	if (strcmp(argv[2], "-") && *argv[2] != '|' && (!globulize(&argv[2]) ||
1195 	    !confirm("output to local-file:", argv[2]))) {
1196 		code = -1;
1197 		goto freels;
1198 	}
1199 	recvrequest(cmd, argv[2], argv[1], "w", 0, 0);
1200 
1201 	/* flush results in case commands are coming from a pipe */
1202 	fflush(ttyout);
1203 freels:
1204 	if (argv[2] != globargv2)		/* free up after globulize() */
1205 		free(argv[2]);
1206 	if (globargv2 != oldargv2)
1207 		free(globargv2);
1208 }
1209 
1210 /*
1211  * Get a directory listing of multiple remote files.
1212  */
1213 void
1214 mls(argc, argv)
1215 	int argc;
1216 	char *argv[];
1217 {
1218 	sig_t oldintr;
1219 	int ointer, i;
1220 	char mode[1], *dest, *odest;
1221 
1222 	if (argc < 2 && !another(&argc, &argv, "remote-files"))
1223 		goto usage;
1224 	if (argc < 3 && !another(&argc, &argv, "local-file")) {
1225 usage:
1226 		fprintf(ttyout, "usage: %s remote-files local-file\n", argv[0]);
1227 		code = -1;
1228 		return;
1229 	}
1230 	odest = dest = argv[argc - 1];
1231 	argv[argc - 1] = NULL;
1232 	if (strcmp(dest, "-") && *dest != '|')
1233 		if (!globulize(&dest) ||
1234 		    !confirm("output to local-file:", dest)) {
1235 			code = -1;
1236 			return;
1237 	}
1238 	mname = argv[0];
1239 	mflag = 1;
1240 	oldintr = signal(SIGINT, mabort);
1241 	(void)setjmp(jabort);
1242 	for (i = 1; mflag && i < argc-1; ++i) {
1243 		*mode = (i == 1) ? 'w' : 'a';
1244 		recvrequest("LIST", dest, argv[i], mode, 0, 0);
1245 		if (!mflag && fromatty) {
1246 			ointer = interactive;
1247 			interactive = 1;
1248 			if (confirm("Continue with", argv[0])) {
1249 				mflag ++;
1250 			}
1251 			interactive = ointer;
1252 		}
1253 	}
1254 	(void)signal(SIGINT, oldintr);
1255 	mflag = 0;
1256 	if (dest != odest)			/* free up after globulize() */
1257 		free(dest);
1258 }
1259 
1260 /*
1261  * Do a shell escape
1262  */
1263 /*ARGSUSED*/
1264 void
1265 shell(argc, argv)
1266 	int argc;
1267 	char *argv[];
1268 {
1269 	pid_t pid;
1270 	sig_t old1, old2;
1271 	char shellnam[MAXPATHLEN], *shell, *namep;
1272 	int wait_status;
1273 
1274 	old1 = signal (SIGINT, SIG_IGN);
1275 	old2 = signal (SIGQUIT, SIG_IGN);
1276 	if ((pid = fork()) == 0) {
1277 		for (pid = 3; pid < 20; pid++)
1278 			(void)close(pid);
1279 		(void)signal(SIGINT, SIG_DFL);
1280 		(void)signal(SIGQUIT, SIG_DFL);
1281 		shell = getenv("SHELL");
1282 		if (shell == NULL)
1283 			shell = _PATH_BSHELL;
1284 		namep = strrchr(shell, '/');
1285 		if (namep == NULL)
1286 			namep = shell;
1287 		shellnam[0] = '-';
1288 		(void)strlcpy(shellnam + 1, ++namep, sizeof(shellnam) - 1);
1289 		if (strcmp(namep, "sh") != 0)
1290 			shellnam[0] = '+';
1291 		if (debug) {
1292 			fputs(shell, ttyout);
1293 			fputc('\n', ttyout);
1294 			(void)fflush(ttyout);
1295 		}
1296 		if (argc > 1) {
1297 			execl(shell, shellnam, "-c", altarg, (char *)0);
1298 		}
1299 		else {
1300 			execl(shell, shellnam, (char *)0);
1301 		}
1302 		warn("%s", shell);
1303 		code = -1;
1304 		exit(1);
1305 	}
1306 	if (pid > 0)
1307 		while (wait(&wait_status) != pid)
1308 			;
1309 	(void)signal(SIGINT, old1);
1310 	(void)signal(SIGQUIT, old2);
1311 	if (pid == -1) {
1312 		warn("Try again later");
1313 		code = -1;
1314 	}
1315 	else {
1316 		code = 0;
1317 	}
1318 }
1319 
1320 /*
1321  * Send new user information (re-login)
1322  */
1323 void
1324 user(argc, argv)
1325 	int argc;
1326 	char *argv[];
1327 {
1328 	char acct[80];
1329 	int n, aflag = 0;
1330 
1331 	if (argc < 2)
1332 		(void)another(&argc, &argv, "username");
1333 	if (argc < 2 || argc > 4) {
1334 		fprintf(ttyout, "usage: %s username [password] [account]\n", argv[0]);
1335 		code = -1;
1336 		return;
1337 	}
1338 	n = command("USER %s", argv[1]);
1339 	if (n == CONTINUE) {
1340 		if (argc < 3 )
1341 			argv[2] = getpass("Password: "), argc++;
1342 		n = command("PASS %s", argv[2]);
1343 	}
1344 	if (n == CONTINUE) {
1345 		if (argc < 4) {
1346 			(void)fputs("Account: ", ttyout);
1347 			(void)fflush(ttyout);
1348 			(void)fgets(acct, sizeof(acct) - 1, stdin);
1349 			acct[strlen(acct) - 1] = '\0';
1350 			argv[3] = acct; argc++;
1351 		}
1352 		n = command("ACCT %s", argv[3]);
1353 		aflag++;
1354 	}
1355 	if (n != COMPLETE) {
1356 		fputs("Login failed.\n", ttyout);
1357 		return;
1358 	}
1359 	if (!aflag && argc == 4) {
1360 		(void)command("ACCT %s", argv[3]);
1361 	}
1362 	connected = -1;
1363 }
1364 
1365 /*
1366  * Print working directory on remote machine.
1367  */
1368 /*VARARGS*/
1369 void
1370 pwd(argc, argv)
1371 	int argc;
1372 	char *argv[];
1373 {
1374 	int oldverbose = verbose;
1375 
1376 	/*
1377 	 * If we aren't verbose, this doesn't do anything!
1378 	 */
1379 	verbose = 1;
1380 	if (command("PWD") == ERROR && code == 500) {
1381 		fputs("PWD command not recognized, trying XPWD.\n", ttyout);
1382 		(void)command("XPWD");
1383 	}
1384 	verbose = oldverbose;
1385 }
1386 
1387 /*
1388  * Print working directory on local machine.
1389  */
1390 void
1391 lpwd(argc, argv)
1392 	int argc;
1393 	char *argv[];
1394 {
1395 	char buf[MAXPATHLEN];
1396 
1397 	if (getcwd(buf, sizeof(buf)) != NULL)
1398 		fprintf(ttyout, "Local directory %s\n", buf);
1399 	else
1400 		warn("getcwd");
1401 	code = 0;
1402 }
1403 
1404 /*
1405  * Make a directory.
1406  */
1407 void
1408 makedir(argc, argv)
1409 	int argc;
1410 	char *argv[];
1411 {
1412 
1413 	if ((argc < 2 && !another(&argc, &argv, "directory-name")) ||
1414 	    argc > 2) {
1415 		fprintf(ttyout, "usage: %s directory-name\n", argv[0]);
1416 		code = -1;
1417 		return;
1418 	}
1419 	if (command("MKD %s", argv[1]) == ERROR && code == 500) {
1420 		if (verbose)
1421 			fputs("MKD command not recognized, trying XMKD.\n", ttyout);
1422 		(void)command("XMKD %s", argv[1]);
1423 	}
1424 }
1425 
1426 /*
1427  * Remove a directory.
1428  */
1429 void
1430 removedir(argc, argv)
1431 	int argc;
1432 	char *argv[];
1433 {
1434 
1435 	if ((argc < 2 && !another(&argc, &argv, "directory-name")) ||
1436 	    argc > 2) {
1437 		fprintf(ttyout, "usage: %s directory-name\n", argv[0]);
1438 		code = -1;
1439 		return;
1440 	}
1441 	if (command("RMD %s", argv[1]) == ERROR && code == 500) {
1442 		if (verbose)
1443 			fputs("RMD command not recognized, trying XRMD.\n", ttyout);
1444 		(void)command("XRMD %s", argv[1]);
1445 	}
1446 }
1447 
1448 /*
1449  * Send a line, verbatim, to the remote machine.
1450  */
1451 void
1452 quote(argc, argv)
1453 	int argc;
1454 	char *argv[];
1455 {
1456 
1457 	if (argc < 2 && !another(&argc, &argv, "command line to send")) {
1458 		fprintf(ttyout, "usage: %s line-to-send\n", argv[0]);
1459 		code = -1;
1460 		return;
1461 	}
1462 	quote1("", argc, argv);
1463 }
1464 
1465 /*
1466  * Send a SITE command to the remote machine.  The line
1467  * is sent verbatim to the remote machine, except that the
1468  * word "SITE" is added at the front.
1469  */
1470 void
1471 site(argc, argv)
1472 	int argc;
1473 	char *argv[];
1474 {
1475 
1476 	if (argc < 2 && !another(&argc, &argv, "arguments to SITE command")) {
1477 		fprintf(ttyout, "usage: %s line-to-send\n", argv[0]);
1478 		code = -1;
1479 		return;
1480 	}
1481 	quote1("SITE", argc, argv);
1482 }
1483 
1484 /*
1485  * Turn argv[1..argc) into a space-separated string, then prepend initial text.
1486  * Send the result as a one-line command and get response.
1487  */
1488 void
1489 quote1(initial, argc, argv)
1490 	const char *initial;
1491 	int argc;
1492 	char *argv[];
1493 {
1494 	int i, len;
1495 	char buf[BUFSIZ];		/* must be >= sizeof(line) */
1496 
1497 	(void)strlcpy(buf, initial, sizeof(buf));
1498 	if (argc > 1) {
1499 		for (i = 1, len = strlen(buf); i < argc && len < sizeof(buf)-1; i++) {
1500 
1501 			/* Sanity check */
1502 			if (len >= sizeof(buf) - 1)
1503 				break;
1504 
1505 			/* Space for next arg */
1506 			if (len > 1)
1507 				buf[len++] = ' ';
1508 
1509 			/* Sanity check */
1510 			if (len >= sizeof(buf) - 1)
1511 				break;
1512 
1513 			/* Copy next argument, NUL terminate always */
1514 			strlcpy(&buf[len], argv[i], sizeof(buf) - len);
1515 
1516 			/* Update string length */
1517 			len = strlen(buf);
1518 		}
1519 	}
1520 
1521 	/* Make double (triple?) sure the sucker is NUL terminated */
1522 	buf[sizeof(buf) - 1] = '\0';
1523 
1524 	if (command("%s", buf) == PRELIM) {
1525 		while (getreply(0) == PRELIM)
1526 			continue;
1527 	}
1528 }
1529 
1530 void
1531 do_chmod(argc, argv)
1532 	int argc;
1533 	char *argv[];
1534 {
1535 
1536 	if (argc < 2 && !another(&argc, &argv, "mode"))
1537 		goto usage;
1538 	if ((argc < 3 && !another(&argc, &argv, "file-name")) || argc > 3) {
1539 usage:
1540 		fprintf(ttyout, "usage: %s mode file-name\n", argv[0]);
1541 		code = -1;
1542 		return;
1543 	}
1544 	(void)command("SITE CHMOD %s %s", argv[1], argv[2]);
1545 }
1546 
1547 void
1548 do_umask(argc, argv)
1549 	int argc;
1550 	char *argv[];
1551 {
1552 	int oldverbose = verbose;
1553 
1554 	verbose = 1;
1555 	(void)command(argc == 1 ? "SITE UMASK" : "SITE UMASK %s", argv[1]);
1556 	verbose = oldverbose;
1557 }
1558 
1559 void
1560 idle(argc, argv)
1561 	int argc;
1562 	char *argv[];
1563 {
1564 	int oldverbose = verbose;
1565 
1566 	verbose = 1;
1567 	(void)command(argc == 1 ? "SITE IDLE" : "SITE IDLE %s", argv[1]);
1568 	verbose = oldverbose;
1569 }
1570 
1571 /*
1572  * Ask the other side for help.
1573  */
1574 void
1575 rmthelp(argc, argv)
1576 	int argc;
1577 	char *argv[];
1578 {
1579 	int oldverbose = verbose;
1580 
1581 	verbose = 1;
1582 	(void)command(argc == 1 ? "HELP" : "HELP %s", argv[1]);
1583 	verbose = oldverbose;
1584 }
1585 
1586 /*
1587  * Terminate session and exit.
1588  */
1589 /*VARARGS*/
1590 void
1591 quit(argc, argv)
1592 	int argc;
1593 	char *argv[];
1594 {
1595 
1596 	if (connected)
1597 		disconnect(0, 0);
1598 	pswitch(1);
1599 	if (connected) {
1600 		disconnect(0, 0);
1601 	}
1602 	exit(0);
1603 }
1604 
1605 /*
1606  * Terminate session, but don't exit.
1607  */
1608 void
1609 disconnect(argc, argv)
1610 	int argc;
1611 	char *argv[];
1612 {
1613 
1614 	if (!connected)
1615 		return;
1616 	(void)command("QUIT");
1617 	if (cout) {
1618 		(void)fclose(cout);
1619 	}
1620 	cout = NULL;
1621 	connected = 0;
1622 	data = -1;
1623 	if (!proxy) {
1624 		macnum = 0;
1625 	}
1626 }
1627 
1628 void
1629 account(argc, argv)
1630 	int argc;
1631 	char *argv[];
1632 {
1633 	char *ap;
1634 
1635 	if (argc > 2) {
1636 		fprintf(ttyout, "usage: %s [password]\n", argv[0]);
1637 		code = -1;
1638 		return;
1639 	}
1640 	else if (argc == 2)
1641 		ap = argv[1];
1642 	else
1643 		ap = getpass("Account:");
1644 	(void)command("ACCT %s", ap);
1645 }
1646 
1647 jmp_buf abortprox;
1648 
1649 void
1650 proxabort(notused)
1651 	int notused;
1652 {
1653 
1654 	alarmtimer(0);
1655 	if (!proxy) {
1656 		pswitch(1);
1657 	}
1658 	if (connected) {
1659 		proxflag = 1;
1660 	}
1661 	else {
1662 		proxflag = 0;
1663 	}
1664 	pswitch(0);
1665 	longjmp(abortprox, 1);
1666 }
1667 
1668 void
1669 doproxy(argc, argv)
1670 	int argc;
1671 	char *argv[];
1672 {
1673 	struct cmd *c;
1674 	int cmdpos;
1675 	sig_t oldintr;
1676 
1677 	if (argc < 2 && !another(&argc, &argv, "command")) {
1678 		fprintf(ttyout, "usage: %s command\n", argv[0]);
1679 		code = -1;
1680 		return;
1681 	}
1682 	c = getcmd(argv[1]);
1683 	if (c == (struct cmd *) -1) {
1684 		fputs("?Ambiguous command.\n", ttyout);
1685 		(void)fflush(ttyout);
1686 		code = -1;
1687 		return;
1688 	}
1689 	if (c == 0) {
1690 		fputs("?Invalid command.\n", ttyout);
1691 		(void)fflush(ttyout);
1692 		code = -1;
1693 		return;
1694 	}
1695 	if (!c->c_proxy) {
1696 		fputs("?Invalid proxy command.\n", ttyout);
1697 		(void)fflush(ttyout);
1698 		code = -1;
1699 		return;
1700 	}
1701 	if (setjmp(abortprox)) {
1702 		code = -1;
1703 		return;
1704 	}
1705 	oldintr = signal(SIGINT, proxabort);
1706 	pswitch(1);
1707 	if (c->c_conn && !connected) {
1708 		fputs("Not connected.\n", ttyout);
1709 		(void)fflush(ttyout);
1710 		pswitch(0);
1711 		(void)signal(SIGINT, oldintr);
1712 		code = -1;
1713 		return;
1714 	}
1715 	cmdpos = strcspn(line, " \t");
1716 	if (cmdpos > 0)		/* remove leading "proxy " from input buffer */
1717 		memmove(line, line + cmdpos + 1, strlen(line) - cmdpos + 1);
1718 	(*c->c_handler)(argc-1, argv+1);
1719 	if (connected) {
1720 		proxflag = 1;
1721 	}
1722 	else {
1723 		proxflag = 0;
1724 	}
1725 	pswitch(0);
1726 	(void)signal(SIGINT, oldintr);
1727 }
1728 
1729 void
1730 setcase(argc, argv)
1731 	int argc;
1732 	char *argv[];
1733 {
1734 
1735 	code = togglevar(argc, argv, &mcase, "Case mapping");
1736 }
1737 
1738 void
1739 setcr(argc, argv)
1740 	int argc;
1741 	char *argv[];
1742 {
1743 
1744 	code = togglevar(argc, argv, &crflag, "Carriage Return stripping");
1745 }
1746 
1747 void
1748 setntrans(argc, argv)
1749 	int argc;
1750 	char *argv[];
1751 {
1752 	if (argc == 1) {
1753 		ntflag = 0;
1754 		fputs("Ntrans off.\n", ttyout);
1755 		code = ntflag;
1756 		return;
1757 	}
1758 	ntflag++;
1759 	code = ntflag;
1760 	(void)strlcpy(ntin, argv[1], sizeof(ntin));
1761 	if (argc == 2) {
1762 		ntout[0] = '\0';
1763 		return;
1764 	}
1765 	(void)strlcpy(ntout, argv[2], sizeof(ntout));
1766 }
1767 
1768 char *
1769 dotrans(name)
1770 	char *name;
1771 {
1772 	static char new[MAXPATHLEN];
1773 	char *cp1, *cp2 = new;
1774 	int i, ostop, found;
1775 
1776 	for (ostop = 0; *(ntout + ostop) && ostop < 16; ostop++)
1777 		continue;
1778 	for (cp1 = name; *cp1; cp1++) {
1779 		found = 0;
1780 		for (i = 0; *(ntin + i) && i < 16; i++) {
1781 			if (*cp1 == *(ntin + i)) {
1782 				found++;
1783 				if (i < ostop) {
1784 					*cp2++ = *(ntout + i);
1785 				}
1786 				break;
1787 			}
1788 		}
1789 		if (!found) {
1790 			*cp2++ = *cp1;
1791 		}
1792 	}
1793 	*cp2 = '\0';
1794 	return (new);
1795 }
1796 
1797 void
1798 setnmap(argc, argv)
1799 	int argc;
1800 	char *argv[];
1801 {
1802 	char *cp;
1803 
1804 	if (argc == 1) {
1805 		mapflag = 0;
1806 		fputs("Nmap off.\n", ttyout);
1807 		code = mapflag;
1808 		return;
1809 	}
1810 	if ((argc < 3 && !another(&argc, &argv, "mapout")) || argc > 3) {
1811 		fprintf(ttyout, "usage: %s [mapin mapout]\n", argv[0]);
1812 		code = -1;
1813 		return;
1814 	}
1815 	mapflag = 1;
1816 	code = 1;
1817 	cp = strchr(altarg, ' ');
1818 	if (proxy) {
1819 		while(*++cp == ' ')
1820 			continue;
1821 		altarg = cp;
1822 		cp = strchr(altarg, ' ');
1823 	}
1824 	*cp = '\0';
1825 	(void)strncpy(mapin, altarg, MAXPATHLEN - 1);
1826 	while (*++cp == ' ')
1827 		continue;
1828 	(void)strncpy(mapout, cp, MAXPATHLEN - 1);
1829 }
1830 
1831 char *
1832 domap(name)
1833 	char *name;
1834 {
1835 	static char new[MAXPATHLEN];
1836 	char *cp1 = name, *cp2 = mapin;
1837 	char *tp[9], *te[9];
1838 	int i, toks[9], toknum = 0, match = 1;
1839 
1840 	for (i=0; i < 9; ++i) {
1841 		toks[i] = 0;
1842 	}
1843 	while (match && *cp1 && *cp2) {
1844 		switch (*cp2) {
1845 			case '\\':
1846 				if (*++cp2 != *cp1) {
1847 					match = 0;
1848 				}
1849 				break;
1850 			case '$':
1851 				if (*(cp2+1) >= '1' && (*cp2+1) <= '9') {
1852 					if (*cp1 != *(++cp2+1)) {
1853 						toks[toknum = *cp2 - '1']++;
1854 						tp[toknum] = cp1;
1855 						while (*++cp1 && *(cp2+1)
1856 							!= *cp1);
1857 						te[toknum] = cp1;
1858 					}
1859 					cp2++;
1860 					break;
1861 				}
1862 				/* FALLTHROUGH */
1863 			default:
1864 				if (*cp2 != *cp1) {
1865 					match = 0;
1866 				}
1867 				break;
1868 		}
1869 		if (match && *cp1) {
1870 			cp1++;
1871 		}
1872 		if (match && *cp2) {
1873 			cp2++;
1874 		}
1875 	}
1876 	if (!match && *cp1) /* last token mismatch */
1877 	{
1878 		toks[toknum] = 0;
1879 	}
1880 	cp1 = new;
1881 	*cp1 = '\0';
1882 	cp2 = mapout;
1883 	while (*cp2) {
1884 		match = 0;
1885 		switch (*cp2) {
1886 			case '\\':
1887 				if (*(cp2 + 1)) {
1888 					*cp1++ = *++cp2;
1889 				}
1890 				break;
1891 			case '[':
1892 LOOP:
1893 				if (*++cp2 == '$' && isdigit(*(cp2+1))) {
1894 					if (*++cp2 == '0') {
1895 						char *cp3 = name;
1896 
1897 						while (*cp3) {
1898 							*cp1++ = *cp3++;
1899 						}
1900 						match = 1;
1901 					}
1902 					else if (toks[toknum = *cp2 - '1']) {
1903 						char *cp3 = tp[toknum];
1904 
1905 						while (cp3 != te[toknum]) {
1906 							*cp1++ = *cp3++;
1907 						}
1908 						match = 1;
1909 					}
1910 				}
1911 				else {
1912 					while (*cp2 && *cp2 != ',' &&
1913 					    *cp2 != ']') {
1914 						if (*cp2 == '\\') {
1915 							cp2++;
1916 						}
1917 						else if (*cp2 == '$' &&
1918    						        isdigit(*(cp2+1))) {
1919 							if (*++cp2 == '0') {
1920 							   char *cp3 = name;
1921 
1922 							   while (*cp3) {
1923 								*cp1++ = *cp3++;
1924 							   }
1925 							}
1926 							else if (toks[toknum =
1927 							    *cp2 - '1']) {
1928 							   char *cp3=tp[toknum];
1929 
1930 							   while (cp3 !=
1931 								  te[toknum]) {
1932 								*cp1++ = *cp3++;
1933 							   }
1934 							}
1935 						}
1936 						else if (*cp2) {
1937 							*cp1++ = *cp2++;
1938 						}
1939 					}
1940 					if (!*cp2) {
1941 						fputs(
1942 "nmap: unbalanced brackets.\n", ttyout);
1943 						return (name);
1944 					}
1945 					match = 1;
1946 					cp2--;
1947 				}
1948 				if (match) {
1949 					while (*++cp2 && *cp2 != ']') {
1950 					      if (*cp2 == '\\' && *(cp2 + 1)) {
1951 							cp2++;
1952 					      }
1953 					}
1954 					if (!*cp2) {
1955 						fputs(
1956 "nmap: unbalanced brackets.\n", ttyout);
1957 						return (name);
1958 					}
1959 					break;
1960 				}
1961 				switch (*++cp2) {
1962 					case ',':
1963 						goto LOOP;
1964 					case ']':
1965 						break;
1966 					default:
1967 						cp2--;
1968 						goto LOOP;
1969 				}
1970 				break;
1971 			case '$':
1972 				if (isdigit(*(cp2 + 1))) {
1973 					if (*++cp2 == '0') {
1974 						char *cp3 = name;
1975 
1976 						while (*cp3) {
1977 							*cp1++ = *cp3++;
1978 						}
1979 					}
1980 					else if (toks[toknum = *cp2 - '1']) {
1981 						char *cp3 = tp[toknum];
1982 
1983 						while (cp3 != te[toknum]) {
1984 							*cp1++ = *cp3++;
1985 						}
1986 					}
1987 					break;
1988 				}
1989 				/* intentional drop through */
1990 			default:
1991 				*cp1++ = *cp2;
1992 				break;
1993 		}
1994 		cp2++;
1995 	}
1996 	*cp1 = '\0';
1997 	if (!*new) {
1998 		return (name);
1999 	}
2000 	return (new);
2001 }
2002 
2003 void
2004 setpassive(argc, argv)
2005 	int argc;
2006 	char *argv[];
2007 {
2008 
2009 	code = togglevar(argc, argv, &passivemode,
2010 	    verbose ? "Passive mode" : NULL);
2011 }
2012 
2013 void
2014 setsunique(argc, argv)
2015 	int argc;
2016 	char *argv[];
2017 {
2018 
2019 	code = togglevar(argc, argv, &sunique, "Store unique");
2020 }
2021 
2022 void
2023 setrunique(argc, argv)
2024 	int argc;
2025 	char *argv[];
2026 {
2027 
2028 	code = togglevar(argc, argv, &runique, "Receive unique");
2029 }
2030 
2031 /* change directory to parent directory */
2032 void
2033 cdup(argc, argv)
2034 	int argc;
2035 	char *argv[];
2036 {
2037 	int r;
2038 
2039 	r = command("CDUP");
2040 	if (r == ERROR && code == 500) {
2041 		if (verbose)
2042 			fputs("CDUP command not recognized, trying XCUP.\n", ttyout);
2043 		r = command("XCUP");
2044 	}
2045 	if (r == COMPLETE)
2046 		dirchange = 1;
2047 }
2048 
2049 /*
2050  * Restart transfer at specific point
2051  */
2052 void
2053 restart(argc, argv)
2054 	int argc;
2055 	char *argv[];
2056 {
2057 	quad_t nrestart_point;
2058 	char *ep;
2059 
2060 	if (argc != 2)
2061 		fputs("restart: offset not specified.\n", ttyout);
2062 	else {
2063 		restart_point = strtoq(argv[1], &ep, 10);
2064 		if (nrestart_point == QUAD_MAX || *ep != '\0')
2065 			fputs("restart: invalid offset.\n", ttyout);
2066 		else {
2067 			fprintf(ttyout, "Restarting at %lld. Execute get, put or"
2068 				"append  to initiate transfer\n",
2069 				(long long)nrestart_point);
2070 			restart_point = nrestart_point;
2071 		}
2072 	}
2073 }
2074 
2075 /*
2076  * Show remote system type
2077  */
2078 void
2079 syst(argc, argv)
2080 	int argc;
2081 	char *argv[];
2082 {
2083 
2084 	(void)command("SYST");
2085 }
2086 
2087 void
2088 macdef(argc, argv)
2089 	int argc;
2090 	char *argv[];
2091 {
2092 	char *tmp;
2093 	int c;
2094 
2095 	if (macnum == 16) {
2096 		fputs("Limit of 16 macros have already been defined.\n", ttyout);
2097 		code = -1;
2098 		return;
2099 	}
2100 	if ((argc < 2 && !another(&argc, &argv, "macro name")) || argc > 2) {
2101 		fprintf(ttyout, "usage: %s macro_name\n", argv[0]);
2102 		code = -1;
2103 		return;
2104 	}
2105 	if (interactive)
2106 		fputs(
2107 "Enter macro line by line, terminating it with a null line.\n", ttyout);
2108 	(void)strlcpy(macros[macnum].mac_name, argv[1],
2109 	    sizeof(macros[macnum].mac_name));
2110 	if (macnum == 0)
2111 		macros[macnum].mac_start = macbuf;
2112 	else
2113 		macros[macnum].mac_start = macros[macnum - 1].mac_end + 1;
2114 	tmp = macros[macnum].mac_start;
2115 	while (tmp != macbuf+4096) {
2116 		if ((c = getchar()) == EOF) {
2117 			fputs("macdef: end of file encountered.\n", ttyout);
2118 			code = -1;
2119 			return;
2120 		}
2121 		if ((*tmp = c) == '\n') {
2122 			if (tmp == macros[macnum].mac_start) {
2123 				macros[macnum++].mac_end = tmp;
2124 				code = 0;
2125 				return;
2126 			}
2127 			if (*(tmp-1) == '\0') {
2128 				macros[macnum++].mac_end = tmp - 1;
2129 				code = 0;
2130 				return;
2131 			}
2132 			*tmp = '\0';
2133 		}
2134 		tmp++;
2135 	}
2136 	while (1) {
2137 		while ((c = getchar()) != '\n' && c != EOF)
2138 			/* LOOP */;
2139 		if (c == EOF || getchar() == '\n') {
2140 			fputs("Macro not defined - 4K buffer exceeded.\n", ttyout);
2141 			code = -1;
2142 			return;
2143 		}
2144 	}
2145 }
2146 
2147 /*
2148  * Get size of file on remote machine
2149  */
2150 void
2151 sizecmd(argc, argv)
2152 	int argc;
2153 	char *argv[];
2154 {
2155 	off_t size;
2156 
2157 	if ((argc < 2 && !another(&argc, &argv, "filename")) || argc > 2) {
2158 		fprintf(ttyout, "usage: %s filename\n", argv[0]);
2159 		code = -1;
2160 		return;
2161 	}
2162 	size = remotesize(argv[1], 1);
2163 	if (size != -1)
2164 		fprintf(ttyout, "%s\t%lld\n", argv[1], (long long)size);
2165 	code = size;
2166 }
2167 
2168 /*
2169  * Get last modification time of file on remote machine
2170  */
2171 void
2172 modtime(argc, argv)
2173 	int argc;
2174 	char *argv[];
2175 {
2176 	time_t mtime;
2177 
2178 	if ((argc < 2 && !another(&argc, &argv, "filename")) || argc > 2) {
2179 		fprintf(ttyout, "usage: %s filename\n", argv[0]);
2180 		code = -1;
2181 		return;
2182 	}
2183 	mtime = remotemodtime(argv[1], 1);
2184 	if (mtime != -1)
2185 		fprintf(ttyout, "%s\t%s", argv[1], asctime(localtime(&mtime)));
2186 	code = mtime;
2187 }
2188 
2189 /*
2190  * Show status on remote machine
2191  */
2192 void
2193 rmtstatus(argc, argv)
2194 	int argc;
2195 	char *argv[];
2196 {
2197 
2198 	(void)command(argc > 1 ? "STAT %s" : "STAT" , argv[1]);
2199 }
2200 
2201 /*
2202  * Get file if modtime is more recent than current file
2203  */
2204 void
2205 newer(argc, argv)
2206 	int argc;
2207 	char *argv[];
2208 {
2209 
2210 	if (getit(argc, argv, -1, "w"))
2211 		fprintf(ttyout, "Local file \"%s\" is newer than remote file \"%s\".\n",
2212 			argv[2], argv[1]);
2213 }
2214 
2215 /*
2216  * Display one file through $PAGER (defaults to "more").
2217  */
2218 void
2219 page(argc, argv)
2220 	int argc;
2221 	char *argv[];
2222 {
2223 	int orestart_point, ohash, overbose;
2224 	char *p, *pager, *oldargv1;
2225 
2226 	if ((argc < 2 && !another(&argc, &argv, "filename")) || argc > 2) {
2227 		fprintf(ttyout, "usage: %s filename\n", argv[0]);
2228 		code = -1;
2229 		return;
2230 	}
2231 	oldargv1 = argv[1];
2232 	if (!globulize(&argv[1])) {
2233 		code = -1;
2234 		return;
2235 	}
2236 	p = getenv("PAGER");
2237 	if (p == NULL || (*p == '\0'))
2238 		p = PAGER;
2239 	if ((pager = malloc(strlen(p) + 2)) == NULL)
2240 		errx(1, "Can't allocate memory for $PAGER");
2241 	(void)sprintf(pager, "|%s", p);
2242 
2243 	orestart_point = restart_point;
2244 	ohash = hash;
2245 	overbose = verbose;
2246 	restart_point = hash = verbose = 0;
2247 	recvrequest("RETR", pager, argv[1], "r+w", 1, 0);
2248 	(void)free(pager);
2249 	restart_point = orestart_point;
2250 	hash = ohash;
2251 	verbose = overbose;
2252 	if (oldargv1 != argv[1])	/* free up after globulize() */
2253 		free(argv[1]);
2254 }
2255