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