xref: /openbsd/usr.bin/ftp/cmds.c (revision 5b133f3f)
1 /*	$OpenBSD: cmds.c,v 1.85 2023/03/08 04:43:11 guenther 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 #ifndef SMALL
63 
64 /*
65  * FTP User Program -- Command Routines.
66  */
67 #include <sys/types.h>
68 #include <sys/socket.h>
69 #include <sys/stat.h>
70 #include <sys/wait.h>
71 #include <arpa/ftp.h>
72 
73 #include <ctype.h>
74 #include <err.h>
75 #include <fnmatch.h>
76 #include <glob.h>
77 #include <netdb.h>
78 #include <stdio.h>
79 #include <stdlib.h>
80 #include <string.h>
81 #include <unistd.h>
82 #include <errno.h>
83 
84 #include "ftp_var.h"
85 #include "pathnames.h"
86 #include "cmds.h"
87 
88 /*
89  * Set ascii transfer type.
90  */
91 void
setascii(int argc,char * argv[])92 setascii(int argc, char *argv[])
93 {
94 
95 	stype[1] = "ascii";
96 	settype(2, stype);
97 }
98 
99 /*
100  * Set file transfer mode.
101  */
102 void
setftmode(int argc,char * argv[])103 setftmode(int argc, char *argv[])
104 {
105 
106 	fprintf(ttyout, "We only support %s mode, sorry.\n", modename);
107 	code = -1;
108 }
109 
110 /*
111  * Set file transfer format.
112  */
113 void
setform(int argc,char * argv[])114 setform(int argc, char *argv[])
115 {
116 
117 	fprintf(ttyout, "We only support %s format, sorry.\n", formname);
118 	code = -1;
119 }
120 
121 /*
122  * Set file transfer structure.
123  */
124 void
setstruct(int argc,char * argv[])125 setstruct(int argc, char *argv[])
126 {
127 
128 	fprintf(ttyout, "We only support %s structure, sorry.\n", structname);
129 	code = -1;
130 }
131 
132 void
reput(int argc,char * argv[])133 reput(int argc, char *argv[])
134 {
135 
136 	(void)putit(argc, argv, 1);
137 }
138 
139 void
put(int argc,char * argv[])140 put(int argc, char *argv[])
141 {
142 
143 	(void)putit(argc, argv, 0);
144 }
145 
146 /*
147  * Send a single file.
148  */
149 void
putit(int argc,char * argv[],int restartit)150 putit(int argc, char *argv[], int restartit)
151 {
152 	char *cmd;
153 	int loc = 0;
154 	char *oldargv1, *oldargv2;
155 
156 	if (argc == 2) {
157 		argc++;
158 		argv[2] = argv[1];
159 		loc++;
160 	}
161 	if (argc < 2 && !another(&argc, &argv, "local-file"))
162 		goto usage;
163 	if ((argc < 3 && !another(&argc, &argv, "remote-file")) || argc > 3) {
164 usage:
165 		fprintf(ttyout, "usage: %s local-file [remote-file]\n",
166 		    argv[0]);
167 		code = -1;
168 		return;
169 	}
170 	oldargv1 = argv[1];
171 	oldargv2 = argv[2];
172 	if (!globulize(&argv[1])) {
173 		code = -1;
174 		return;
175 	}
176 	/*
177 	 * If "globulize" modifies argv[1], and argv[2] is a copy of
178 	 * the old argv[1], make it a copy of the new argv[1].
179 	 */
180 	if (argv[1] != oldargv1 && argv[2] == oldargv1) {
181 		argv[2] = argv[1];
182 	}
183 	if (restartit == 1) {
184 		if (curtype != type)
185 			changetype(type, 0);
186 		restart_point = remotesize(argv[2], 1);
187 		if (restart_point < 0) {
188 			restart_point = 0;
189 			code = -1;
190 			return;
191 		}
192 	}
193 	if (strcmp(argv[0], "append") == 0) {
194 		restartit = 1;
195 	}
196 	cmd = restartit ? "APPE" : ((sunique) ? "STOU" : "STOR");
197 	if (loc && ntflag) {
198 		argv[2] = dotrans(argv[2]);
199 	}
200 	if (loc && mapflag) {
201 		argv[2] = domap(argv[2]);
202 	}
203 	sendrequest(cmd, argv[1], argv[2],
204 	    argv[1] != oldargv1 || argv[2] != oldargv2);
205 	restart_point = 0;
206 	if (oldargv1 != argv[1])	/* free up after globulize() */
207 		free(argv[1]);
208 }
209 
210 /*
211  * Send multiple files.
212  */
213 void
mput(int argc,char * argv[])214 mput(int argc, char *argv[])
215 {
216 	extern int optind, optreset;
217 	int ch, i, restartit = 0;
218 	sig_t oldintr;
219 	char *cmd, *tp, *xargv[] = { argv[0], NULL, NULL };
220 	const char *errstr;
221 	static int depth = 0, max_depth = 0;
222 
223 	optind = optreset = 1;
224 
225 	if (depth)
226 		depth++;
227 
228 	while ((ch = getopt(argc, argv, "cd:r")) != -1) {
229 		switch(ch) {
230 		case 'c':
231 			restartit = 1;
232 			break;
233 		case 'd':
234 			max_depth = strtonum(optarg, 0, INT_MAX, &errstr);
235 			if (errstr != NULL) {
236 				fprintf(ttyout, "bad depth value, %s: %s\n",
237 				    errstr, optarg);
238 				code = -1;
239 				return;
240 			}
241 			break;
242 		case 'r':
243 			depth = 1;
244 			break;
245 		default:
246 			goto usage;
247 		}
248 	}
249 
250 	if (argc - optind < 1 && !another(&argc, &argv, "local-files")) {
251 usage:
252 		fprintf(ttyout, "usage: %s [-cr] [-d depth] local-files\n",
253 		    argv[0]);
254 		code = -1;
255 		return;
256 	}
257 
258 	argv[optind - 1] = argv[0];
259 	argc -= optind - 1;
260 	argv += optind - 1;
261 
262 	mname = argv[0];
263 	mflag = 1;
264 
265 	oldintr = signal(SIGINT, mabort);
266 	(void)setjmp(jabort);
267 	if (proxy) {
268 		char *cp, *tp2, tmpbuf[PATH_MAX];
269 
270 		while ((cp = remglob(argv, 0, NULL)) != NULL) {
271 			if (*cp == '\0') {
272 				mflag = 0;
273 				continue;
274 			}
275 			if (mflag && confirm(argv[0], cp)) {
276 				tp = cp;
277 				if (mcase) {
278 					while (*tp && !islower((unsigned char)*tp)) {
279 						tp++;
280 					}
281 					if (!*tp) {
282 						tp = cp;
283 						tp2 = tmpbuf;
284 						while ((*tp2 = *tp) != '\0') {
285 							if (isupper((unsigned char)*tp2)) {
286 								*tp2 =
287 								    tolower((unsigned char)*tp2);
288 							}
289 							tp++;
290 							tp2++;
291 						}
292 					}
293 					tp = tmpbuf;
294 				}
295 				if (ntflag) {
296 					tp = dotrans(tp);
297 				}
298 				if (mapflag) {
299 					tp = domap(tp);
300 				}
301 				if (restartit == 1) {
302 					off_t ret;
303 
304 					if (curtype != type)
305 						changetype(type, 0);
306 					ret = remotesize(tp, 0);
307 					restart_point = (ret < 0) ? 0 : ret;
308 				}
309 				cmd = restartit ? "APPE" : ((sunique) ?
310 				    "STOU" : "STOR");
311 				sendrequest(cmd, cp, tp,
312 				    cp != tp || !interactive);
313 				restart_point = 0;
314 				if (!mflag && fromatty) {
315 					if (confirm(argv[0], NULL))
316 						mflag = 1;
317 				}
318 			}
319 		}
320 		(void)signal(SIGINT, oldintr);
321 		mflag = 0;
322 		return;
323 	}
324 
325 	for (i = 1; i < argc; i++) {
326 		char **cpp;
327 		glob_t gl;
328 		int flags;
329 
330 		/* Copy files without word expansion */
331 		if (!doglob) {
332 			if (mflag && confirm(argv[0], argv[i])) {
333 				tp = (ntflag) ? dotrans(argv[i]) : argv[i];
334 				tp = (mapflag) ? domap(tp) : tp;
335 				if (restartit == 1) {
336 					off_t ret;
337 
338 					if (curtype != type)
339 						changetype(type, 0);
340 					ret = remotesize(tp, 0);
341 					restart_point = (ret < 0) ? 0 : ret;
342 				}
343 				cmd = restartit ? "APPE" : ((sunique) ?
344 				    "STOU" : "STOR");
345 				sendrequest(cmd, argv[i], tp,
346 				    tp != argv[i] || !interactive);
347 				restart_point = 0;
348 				if (!mflag && fromatty) {
349 					if (confirm(argv[0], NULL))
350 						mflag = 1;
351 				}
352 			}
353 			continue;
354 		}
355 
356 		/* expanding file names */
357 		memset(&gl, 0, sizeof(gl));
358 		flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
359 		if (glob(argv[i], flags, NULL, &gl) || gl.gl_pathc == 0) {
360 			warnx("%s: not found", argv[i]);
361 			globfree(&gl);
362 			continue;
363 		}
364 
365 		/* traverse all expanded file names */
366 		for (cpp = gl.gl_pathv; cpp && *cpp != NULL; cpp++) {
367 			struct stat filestat;
368 
369 			if (!mflag)
370 				continue;
371 			if (stat(*cpp, &filestat) != 0) {
372 				warn("local: %s", *cpp);
373 				continue;
374 			}
375 			if (S_ISDIR(filestat.st_mode) && depth == max_depth)
376 				continue;
377 			if (!confirm(argv[0], *cpp))
378 				continue;
379 
380 			/*
381 			 * If file is a directory then create a new one
382 			 * at the remote machine.
383 			 */
384 			if (S_ISDIR(filestat.st_mode)) {
385 				xargv[1] = *cpp;
386 				makedir(2, xargv);
387 				cd(2, xargv);
388 				if (dirchange != 1) {
389 					warnx("remote: %s", *cpp);
390 					continue;
391 				}
392 
393 				if (chdir(*cpp) != 0) {
394 					warn("local: %s", *cpp);
395 					goto out;
396 				}
397 
398 				/* Copy the whole directory recursively. */
399 				xargv[1] = "*";
400 				mput(2, xargv);
401 
402 				if (chdir("..") != 0) {
403 					mflag = 0;
404 					warn("local: %s", *cpp);
405 					goto out;
406 				}
407 
408  out:
409 				xargv[1] = "..";
410 				cd(2, xargv);
411 				if (dirchange != 1) {
412 					warnx("remote: %s", *cpp);
413 					mflag = 0;
414 				}
415 				continue;
416 			}
417 
418 			tp = (ntflag) ? dotrans(*cpp) : *cpp;
419 			tp = (mapflag) ? domap(tp) : tp;
420 			if (restartit == 1) {
421 				off_t ret;
422 
423 				if (curtype != type)
424 					changetype(type, 0);
425 				ret = remotesize(tp, 0);
426 				restart_point = (ret < 0) ? 0 : ret;
427 			}
428 			cmd = restartit ? "APPE" : ((sunique) ?
429 			    "STOU" : "STOR");
430 			sendrequest(cmd, *cpp, tp,
431 			    *cpp != tp || !interactive);
432 			restart_point = 0;
433 			if (!mflag && fromatty) {
434 				if (confirm(argv[0], NULL))
435 					mflag = 1;
436 			}
437 		}
438 		globfree(&gl);
439 	}
440 
441 	(void)signal(SIGINT, oldintr);
442 
443 	if (depth)
444 		depth--;
445 	if (depth == 0 || mflag == 0)
446 		depth = max_depth = mflag = 0;
447 }
448 
449 void
reget(int argc,char * argv[])450 reget(int argc, char *argv[])
451 {
452 
453 	(void)getit(argc, argv, 1, "a+w");
454 }
455 
456 char *
onoff(int bool)457 onoff(int bool)
458 {
459 
460 	return (bool ? "on" : "off");
461 }
462 
463 /*
464  * Show status.
465  */
466 void
status(int argc,char * argv[])467 status(int argc, char *argv[])
468 {
469 	int i;
470 
471 	if (connected)
472 		fprintf(ttyout, "Connected %sto %s.\n",
473 		    connected == -1 ? "and logged in" : "", hostname);
474 	else
475 		fputs("Not connected.\n", ttyout);
476 	if (!proxy) {
477 		pswitch(1);
478 		if (connected) {
479 			fprintf(ttyout, "Connected for proxy commands to %s.\n",
480 			    hostname);
481 		}
482 		else {
483 			fputs("No proxy connection.\n", ttyout);
484 		}
485 		pswitch(0);
486 	}
487 	fprintf(ttyout, "Gate ftp: %s, server %s, port %s.\n", onoff(gatemode),
488 	    *gateserver ? gateserver : "(none)", gateport);
489 	fprintf(ttyout, "Passive mode: %s.\n", onoff(passivemode));
490 	fprintf(ttyout, "Mode: %s; Type: %s; Form: %s; Structure: %s.\n",
491 		modename, typename, formname, structname);
492 	fprintf(ttyout, "Verbose: %s; Bell: %s; Prompting: %s; Globbing: %s.\n",
493 		onoff(verbose), onoff(bell), onoff(interactive),
494 		onoff(doglob));
495 	fprintf(ttyout, "Store unique: %s; Receive unique: %s.\n", onoff(sunique),
496 		onoff(runique));
497 	fprintf(ttyout, "Preserve modification times: %s.\n", onoff(preserve));
498 	fprintf(ttyout, "Case: %s; CR stripping: %s.\n", onoff(mcase), onoff(crflag));
499 	if (ntflag) {
500 		fprintf(ttyout, "Ntrans: (in) %s (out) %s\n", ntin, ntout);
501 	}
502 	else {
503 		fputs("Ntrans: off.\n", ttyout);
504 	}
505 	if (mapflag) {
506 		fprintf(ttyout, "Nmap: (in) %s (out) %s\n", mapin, mapout);
507 	}
508 	else {
509 		fputs("Nmap: off.\n", ttyout);
510 	}
511 	fprintf(ttyout, "Hash mark printing: %s; Mark count: %d; Progress bar: %s.\n",
512 	    onoff(hash), mark, onoff(progress));
513 	fprintf(ttyout, "Use of PORT/LPRT cmds: %s.\n", onoff(sendport));
514 	fprintf(ttyout, "Use of EPSV/EPRT cmds for IPv4: %s%s.\n", onoff(epsv4),
515 	    epsv4bad ? " (disabled for this connection)" : "");
516 	fprintf(ttyout, "Command line editing: %s.\n", onoff(editing));
517 	if (macnum > 0) {
518 		fputs("Macros:\n", ttyout);
519 		for (i=0; i<macnum; i++) {
520 			fprintf(ttyout, "\t%s\n", macros[i].mac_name);
521 		}
522 	}
523 	code = 0;
524 }
525 
526 /*
527  * Toggle a variable
528  */
529 int
togglevar(int argc,char * argv[],int * var,const char * mesg)530 togglevar(int argc, char *argv[], int *var, const char *mesg)
531 {
532 	if (argc < 2) {
533 		*var = !*var;
534 	} else if (argc == 2 && strcasecmp(argv[1], "on") == 0) {
535 		*var = 1;
536 	} else if (argc == 2 && strcasecmp(argv[1], "off") == 0) {
537 		*var = 0;
538 	} else {
539 		fprintf(ttyout, "usage: %s [on | off]\n", argv[0]);
540 		return (-1);
541 	}
542 	if (mesg)
543 		fprintf(ttyout, "%s %s.\n", mesg, onoff(*var));
544 	return (*var);
545 }
546 
547 /*
548  * Set beep on cmd completed mode.
549  */
550 void
setbell(int argc,char * argv[])551 setbell(int argc, char *argv[])
552 {
553 
554 	code = togglevar(argc, argv, &bell, "Bell mode");
555 }
556 
557 /*
558  * Set command line editing
559  */
560 void
setedit(int argc,char * argv[])561 setedit(int argc, char *argv[])
562 {
563 
564 	code = togglevar(argc, argv, &editing, "Editing mode");
565 	controlediting();
566 }
567 
568 /*
569  * Toggle use of IPv4 EPSV/EPRT
570  */
571 void
setepsv4(int argc,char * argv[])572 setepsv4(int argc, char *argv[])
573 {
574 
575 	code = togglevar(argc, argv, &epsv4, "EPSV/EPRT on IPv4");
576 	epsv4bad = 0;
577 }
578 
579 /*
580  * Turn on packet tracing.
581  */
582 void
settrace(int argc,char * argv[])583 settrace(int argc, char *argv[])
584 {
585 
586 	code = togglevar(argc, argv, &trace, "Packet tracing");
587 }
588 
589 /*
590  * Toggle hash mark printing during transfers, or set hash mark bytecount.
591  */
592 void
sethash(int argc,char * argv[])593 sethash(int argc, char *argv[])
594 {
595 	if (argc == 1)
596 		hash = !hash;
597 	else if (argc != 2) {
598 		fprintf(ttyout, "usage: %s [on | off | size]\n", argv[0]);
599 		code = -1;
600 		return;
601 	} else if (strcasecmp(argv[1], "on") == 0)
602 		hash = 1;
603 	else if (strcasecmp(argv[1], "off") == 0)
604 		hash = 0;
605 	else {
606 		int nmark;
607 		const char *errstr;
608 
609 		nmark = strtonum(argv[1], 1, INT_MAX, &errstr);
610 		if (errstr) {
611 			fprintf(ttyout, "bytecount value is %s: %s\n",
612 			    errstr, argv[1]);
613 			code = -1;
614 			return;
615 		}
616 		mark = nmark;
617 		hash = 1;
618 	}
619 	fprintf(ttyout, "Hash mark printing %s", onoff(hash));
620 	if (hash)
621 		fprintf(ttyout, " (%d bytes/hash mark)", mark);
622 	fputs(".\n", ttyout);
623 	code = hash;
624 }
625 
626 /*
627  * Turn on printing of server echo's.
628  */
629 void
setverbose(int argc,char * argv[])630 setverbose(int argc, char *argv[])
631 {
632 
633 	code = togglevar(argc, argv, &verbose, "Verbose mode");
634 }
635 
636 /*
637  * Toggle PORT/LPRT cmd use before each data connection.
638  */
639 void
setport(int argc,char * argv[])640 setport(int argc, char *argv[])
641 {
642 
643 	code = togglevar(argc, argv, &sendport, "Use of PORT/LPRT cmds");
644 }
645 
646 /*
647  * Toggle transfer progress bar.
648  */
649 void
setprogress(int argc,char * argv[])650 setprogress(int argc, char *argv[])
651 {
652 
653 	code = togglevar(argc, argv, &progress, "Progress bar");
654 }
655 
656 /*
657  * Turn on interactive prompting during mget, mput, and mdelete.
658  */
659 void
setprompt(int argc,char * argv[])660 setprompt(int argc, char *argv[])
661 {
662 
663 	code = togglevar(argc, argv, &interactive, "Interactive mode");
664 }
665 
666 /*
667  * Toggle gate-ftp mode, or set gate-ftp server
668  */
669 void
setgate(int argc,char * argv[])670 setgate(int argc, char *argv[])
671 {
672 	static char gsbuf[HOST_NAME_MAX+1];
673 
674 	if (argc > 3) {
675 		fprintf(ttyout, "usage: %s [on | off | host [port]]\n",
676 		    argv[0]);
677 		code = -1;
678 		return;
679 	} else if (argc < 2) {
680 		gatemode = !gatemode;
681 	} else {
682 		if (argc == 2 && strcasecmp(argv[1], "on") == 0)
683 			gatemode = 1;
684 		else if (argc == 2 && strcasecmp(argv[1], "off") == 0)
685 			gatemode = 0;
686 		else {
687 			if (argc == 3) {
688 				gateport = strdup(argv[2]);
689 				if (gateport == NULL)
690 					err(1, NULL);
691 			}
692 			strlcpy(gsbuf, argv[1], sizeof(gsbuf));
693 			gateserver = gsbuf;
694 			gatemode = 1;
695 		}
696 	}
697 	if (gatemode && (gateserver == NULL || *gateserver == '\0')) {
698 		fprintf(ttyout,
699 		    "Disabling gate-ftp mode - no gate-ftp server defined.\n");
700 		gatemode = 0;
701 	} else {
702 		fprintf(ttyout, "Gate ftp: %s, server %s, port %s.\n",
703 		    onoff(gatemode),
704 		    *gateserver ? gateserver : "(none)", gateport);
705 	}
706 	code = gatemode;
707 }
708 
709 /*
710  * Toggle metacharacter interpretation on local file names.
711  */
712 void
setglob(int argc,char * argv[])713 setglob(int argc, char *argv[])
714 {
715 
716 	code = togglevar(argc, argv, &doglob, "Globbing");
717 }
718 
719 /*
720  * Toggle preserving modification times on retrieved files.
721  */
722 void
setpreserve(int argc,char * argv[])723 setpreserve(int argc, char *argv[])
724 {
725 
726 	code = togglevar(argc, argv, &preserve, "Preserve modification times");
727 }
728 
729 /*
730  * Set debugging mode on/off and/or set level of debugging.
731  */
732 void
setdebug(int argc,char * argv[])733 setdebug(int argc, char *argv[])
734 {
735 	if (argc > 2) {
736 		fprintf(ttyout, "usage: %s [on | off | debuglevel]\n", argv[0]);
737 		code = -1;
738 		return;
739 	} else if (argc == 2) {
740 		if (strcasecmp(argv[1], "on") == 0)
741 			debug = 1;
742 		else if (strcasecmp(argv[1], "off") == 0)
743 			debug = 0;
744 		else {
745 			const char *errstr;
746 			int val;
747 
748 			val = strtonum(argv[1], 0, INT_MAX, &errstr);
749 			if (errstr) {
750 				fprintf(ttyout, "debugging value is %s: %s\n",
751 				    errstr, argv[1]);
752 				code = -1;
753 				return;
754 			}
755 			debug = val;
756 		}
757 	} else
758 		debug = !debug;
759 	if (debug)
760 		options |= SO_DEBUG;
761 	else
762 		options &= ~SO_DEBUG;
763 	fprintf(ttyout, "Debugging %s (debug=%d).\n", onoff(debug), debug);
764 	code = debug > 0;
765 }
766 
767 /*
768  * Set current working directory on local machine.
769  */
770 void
lcd(int argc,char * argv[])771 lcd(int argc, char *argv[])
772 {
773 	char buf[PATH_MAX];
774 	char *oldargv1;
775 
776 	if (argc < 2)
777 		argc++, argv[1] = home;
778 	if (argc != 2) {
779 		fprintf(ttyout, "usage: %s [local-directory]\n", argv[0]);
780 		code = -1;
781 		return;
782 	}
783 	oldargv1 = argv[1];
784 	if (!globulize(&argv[1])) {
785 		code = -1;
786 		return;
787 	}
788 	if (chdir(argv[1]) == -1) {
789 		warn("local: %s", argv[1]);
790 		code = -1;
791 	} else {
792 		if (getcwd(buf, sizeof(buf)) != NULL)
793 			fprintf(ttyout, "Local directory now %s\n", buf);
794 		else
795 			warn("getcwd: %s", argv[1]);
796 		code = 0;
797 	}
798 	if (oldargv1 != argv[1])	/* free up after globulize() */
799 		free(argv[1]);
800 }
801 
802 /*
803  * Delete a single file.
804  */
805 void
deletecmd(int argc,char * argv[])806 deletecmd(int argc, char *argv[])
807 {
808 
809 	if ((argc < 2 && !another(&argc, &argv, "remote-file")) || argc > 2) {
810 		fprintf(ttyout, "usage: %s remote-file\n", argv[0]);
811 		code = -1;
812 		return;
813 	}
814 	(void)command("DELE %s", argv[1]);
815 }
816 
817 /*
818  * Delete multiple files.
819  */
820 void
mdelete(int argc,char * argv[])821 mdelete(int argc, char *argv[])
822 {
823 	sig_t oldintr;
824 	char *cp;
825 
826 	if (argc < 2 && !another(&argc, &argv, "remote-files")) {
827 		fprintf(ttyout, "usage: %s remote-files\n", argv[0]);
828 		code = -1;
829 		return;
830 	}
831 	mname = argv[0];
832 	mflag = 1;
833 	oldintr = signal(SIGINT, mabort);
834 	(void)setjmp(jabort);
835 	while ((cp = remglob(argv, 0, NULL)) != NULL) {
836 		if (*cp == '\0') {
837 			mflag = 0;
838 			continue;
839 		}
840 		if (mflag && confirm(argv[0], cp)) {
841 			(void)command("DELE %s", cp);
842 			if (!mflag && fromatty) {
843 				if (confirm(argv[0], NULL))
844 					mflag = 1;
845 			}
846 		}
847 	}
848 	(void)signal(SIGINT, oldintr);
849 	mflag = 0;
850 }
851 
852 /*
853  * Rename a remote file.
854  */
855 void
renamefile(int argc,char * argv[])856 renamefile(int argc, char *argv[])
857 {
858 
859 	if (argc < 2 && !another(&argc, &argv, "from-name"))
860 		goto usage;
861 	if ((argc < 3 && !another(&argc, &argv, "to-name")) || argc > 3) {
862 usage:
863 		fprintf(ttyout, "usage: %s from-name to-name\n", argv[0]);
864 		code = -1;
865 		return;
866 	}
867 	if (command("RNFR %s", argv[1]) == CONTINUE)
868 		(void)command("RNTO %s", argv[2]);
869 }
870 
871 /*
872  * Get a directory listing of remote files.
873  */
874 void
ls(int argc,char * argv[])875 ls(int argc, char *argv[])
876 {
877 	const char *cmd;
878 	char *oldargv2, *globargv2;
879 
880 	if (argc < 2)
881 		argc++, argv[1] = NULL;
882 	if (argc < 3)
883 		argc++, argv[2] = "-";
884 	if (argc > 3) {
885 		fprintf(ttyout, "usage: %s [remote-directory [local-file]]\n",
886 		    argv[0]);
887 		code = -1;
888 		return;
889 	}
890 	cmd = strcmp(argv[0], "nlist") == 0 ? "NLST" : "LIST";
891 	oldargv2 = argv[2];
892 	if (strcmp(argv[2], "-") && !globulize(&argv[2])) {
893 		code = -1;
894 		return;
895 	}
896 	globargv2 = argv[2];
897 	if (strcmp(argv[2], "-") && *argv[2] != '|' && (!globulize(&argv[2]) ||
898 	    !confirm("output to local-file:", argv[2]))) {
899 		code = -1;
900 		goto freels;
901 	}
902 	recvrequest(cmd, argv[2], argv[1], "w", 0, 0);
903 
904 	/* flush results in case commands are coming from a pipe */
905 	fflush(ttyout);
906 freels:
907 	if (argv[2] != globargv2)		/* free up after globulize() */
908 		free(argv[2]);
909 	if (globargv2 != oldargv2)
910 		free(globargv2);
911 }
912 
913 /*
914  * Get a directory listing of multiple remote files.
915  */
916 void
mls(int argc,char * argv[])917 mls(int argc, char *argv[])
918 {
919 	sig_t oldintr;
920 	int i;
921 	char lmode[1], *dest, *odest;
922 
923 	if (argc < 2 && !another(&argc, &argv, "remote-files"))
924 		goto usage;
925 	if (argc < 3 && !another(&argc, &argv, "local-file")) {
926 usage:
927 		fprintf(ttyout, "usage: %s remote-files local-file\n", argv[0]);
928 		code = -1;
929 		return;
930 	}
931 	odest = dest = argv[argc - 1];
932 	argv[argc - 1] = NULL;
933 	if (strcmp(dest, "-") && *dest != '|')
934 		if (!globulize(&dest) ||
935 		    !confirm("output to local-file:", dest)) {
936 			code = -1;
937 			return;
938 	}
939 	mname = argv[0];
940 	mflag = 1;
941 	oldintr = signal(SIGINT, mabort);
942 	(void)setjmp(jabort);
943 	for (i = 1; mflag && i < argc-1; ++i) {
944 		*lmode = (i == 1) ? 'w' : 'a';
945 		recvrequest("LIST", dest, argv[i], lmode, 0, 0);
946 		if (!mflag && fromatty) {
947 			if (confirm(argv[0], NULL))
948 				mflag ++;
949 		}
950 	}
951 	(void)signal(SIGINT, oldintr);
952 	mflag = 0;
953 	if (dest != odest)			/* free up after globulize() */
954 		free(dest);
955 }
956 
957 /*
958  * Do a shell escape
959  */
960 void
shell(int argc,char * argv[])961 shell(int argc, char *argv[])
962 {
963 	pid_t pid;
964 	sig_t old1, old2;
965 	char shellnam[PATH_MAX], *shellp, *namep;
966 	int wait_status;
967 
968 	old1 = signal (SIGINT, SIG_IGN);
969 	old2 = signal (SIGQUIT, SIG_IGN);
970 	if ((pid = fork()) == 0) {
971 		(void)closefrom(3);
972 		(void)signal(SIGINT, SIG_DFL);
973 		(void)signal(SIGQUIT, SIG_DFL);
974 		shellp = getenv("SHELL");
975 		if (shellp == NULL || *shellp == '\0')
976 			shellp = _PATH_BSHELL;
977 		namep = strrchr(shellp, '/');
978 		if (namep == NULL)
979 			namep = shellp;
980 		shellnam[0] = '-';
981 		(void)strlcpy(shellnam + 1, ++namep, sizeof(shellnam) - 1);
982 		if (strcmp(namep, "sh") != 0)
983 			shellnam[0] = '+';
984 		if (debug) {
985 			fputs(shellp, ttyout);
986 			fputc('\n', ttyout);
987 			(void)fflush(ttyout);
988 		}
989 		if (argc > 1) {
990 			execl(shellp, shellnam, "-c", altarg, (char *)NULL);
991 		}
992 		else {
993 			execl(shellp, shellnam, (char *)NULL);
994 		}
995 		warn("%s", shellp);
996 		code = -1;
997 		exit(1);
998 	}
999 	if (pid > 0)
1000 		while (wait(&wait_status) != pid)
1001 			;
1002 	(void)signal(SIGINT, old1);
1003 	(void)signal(SIGQUIT, old2);
1004 	if (pid == -1) {
1005 		warn("Try again later");
1006 		code = -1;
1007 	}
1008 	else {
1009 		code = 0;
1010 	}
1011 }
1012 
1013 /*
1014  * Send new user information (re-login)
1015  */
1016 void
user(int argc,char * argv[])1017 user(int argc, char *argv[])
1018 {
1019 	char acctname[80];
1020 	int n, aflag = 0;
1021 
1022 	if (argc < 2)
1023 		(void)another(&argc, &argv, "username");
1024 	if (argc < 2 || argc > 4) {
1025 		fprintf(ttyout, "usage: %s username [password [account]]\n",
1026 		    argv[0]);
1027 		code = -1;
1028 		return;
1029 	}
1030 	n = command("USER %s", argv[1]);
1031 	if (n == CONTINUE) {
1032 		if (argc < 3 )
1033 			argv[2] = getpass("Password:"), argc++;
1034 		n = command("PASS %s", argv[2]);
1035 	}
1036 	if (n == CONTINUE) {
1037 		if (argc < 4) {
1038 			(void)fputs("Account: ", ttyout);
1039 			(void)fflush(ttyout);
1040 			if (fgets(acctname, sizeof(acctname), stdin) == NULL) {
1041 				clearerr(stdin);
1042 				goto fail;
1043 			}
1044 
1045 			acctname[strcspn(acctname, "\n")] = '\0';
1046 
1047 			argv[3] = acctname;
1048 			argc++;
1049 		}
1050 		n = command("ACCT %s", argv[3]);
1051 		aflag++;
1052 	}
1053 	if (n != COMPLETE) {
1054  fail:
1055 		fputs("Login failed.\n", ttyout);
1056 		return;
1057 	}
1058 	if (!aflag && argc == 4) {
1059 		(void)command("ACCT %s", argv[3]);
1060 	}
1061 	connected = -1;
1062 }
1063 
1064 /*
1065  * Print working directory on remote machine.
1066  */
1067 void
pwd(int argc,char * argv[])1068 pwd(int argc, char *argv[])
1069 {
1070 	int oldverbose = verbose;
1071 
1072 	/*
1073 	 * If we aren't verbose, this doesn't do anything!
1074 	 */
1075 	verbose = 1;
1076 	if (command("PWD") == ERROR && code == 500) {
1077 		fputs("PWD command not recognized, trying XPWD.\n", ttyout);
1078 		(void)command("XPWD");
1079 	}
1080 	verbose = oldverbose;
1081 }
1082 
1083 /*
1084  * Print working directory on local machine.
1085  */
1086 void
lpwd(int argc,char * argv[])1087 lpwd(int argc, char *argv[])
1088 {
1089 	char buf[PATH_MAX];
1090 
1091 	if (getcwd(buf, sizeof(buf)) != NULL)
1092 		fprintf(ttyout, "Local directory %s\n", buf);
1093 	else
1094 		warn("getcwd");
1095 	code = 0;
1096 }
1097 
1098 /*
1099  * Make a directory.
1100  */
1101 void
makedir(int argc,char * argv[])1102 makedir(int argc, char *argv[])
1103 {
1104 
1105 	if ((argc < 2 && !another(&argc, &argv, "directory-name")) ||
1106 	    argc > 2) {
1107 		fprintf(ttyout, "usage: %s directory-name\n", argv[0]);
1108 		code = -1;
1109 		return;
1110 	}
1111 	if (command("MKD %s", argv[1]) == ERROR && code == 500) {
1112 		if (verbose)
1113 			fputs("MKD command not recognized, trying XMKD.\n", ttyout);
1114 		(void)command("XMKD %s", argv[1]);
1115 	}
1116 }
1117 
1118 /*
1119  * Remove a directory.
1120  */
1121 void
removedir(int argc,char * argv[])1122 removedir(int argc, char *argv[])
1123 {
1124 
1125 	if ((argc < 2 && !another(&argc, &argv, "directory-name")) ||
1126 	    argc > 2) {
1127 		fprintf(ttyout, "usage: %s directory-name\n", argv[0]);
1128 		code = -1;
1129 		return;
1130 	}
1131 	if (command("RMD %s", argv[1]) == ERROR && code == 500) {
1132 		if (verbose)
1133 			fputs("RMD command not recognized, trying XRMD.\n", ttyout);
1134 		(void)command("XRMD %s", argv[1]);
1135 	}
1136 }
1137 
1138 /*
1139  * Send a line, verbatim, to the remote machine.
1140  */
1141 void
quote(int argc,char * argv[])1142 quote(int argc, char *argv[])
1143 {
1144 
1145 	if (argc < 2 && !another(&argc, &argv, "command line to send")) {
1146 		fprintf(ttyout, "usage: %s arg ...\n", argv[0]);
1147 		code = -1;
1148 		return;
1149 	}
1150 	quote1("", argc, argv);
1151 }
1152 
1153 /*
1154  * Send a SITE command to the remote machine.  The line
1155  * is sent verbatim to the remote machine, except that the
1156  * word "SITE" is added at the front.
1157  */
1158 void
site(int argc,char * argv[])1159 site(int argc, char *argv[])
1160 {
1161 
1162 	if (argc < 2 && !another(&argc, &argv, "arguments to SITE command")) {
1163 		fprintf(ttyout, "usage: %s arg ...\n", argv[0]);
1164 		code = -1;
1165 		return;
1166 	}
1167 	quote1("SITE", argc, argv);
1168 }
1169 
1170 /*
1171  * Turn argv[1..argc) into a space-separated string, then prepend initial text.
1172  * Send the result as a one-line command and get response.
1173  */
1174 void
quote1(const char * initial,int argc,char * argv[])1175 quote1(const char *initial, int argc, char *argv[])
1176 {
1177 	int i, len;
1178 	char buf[BUFSIZ];		/* must be >= sizeof(line) */
1179 
1180 	(void)strlcpy(buf, initial, sizeof(buf));
1181 	if (argc > 1) {
1182 		for (i = 1, len = strlen(buf); i < argc && len < sizeof(buf)-1; i++) {
1183 			/* Space for next arg */
1184 			if (len > 1)
1185 				buf[len++] = ' ';
1186 
1187 			/* Sanity check */
1188 			if (len >= sizeof(buf) - 1)
1189 				break;
1190 
1191 			/* Copy next argument, NUL terminate always */
1192 			strlcpy(&buf[len], argv[i], sizeof(buf) - len);
1193 
1194 			/* Update string length */
1195 			len = strlen(buf);
1196 		}
1197 	}
1198 
1199 	/* Make double (triple?) sure the sucker is NUL terminated */
1200 	buf[sizeof(buf) - 1] = '\0';
1201 
1202 	if (command("%s", buf) == PRELIM) {
1203 		while (getreply(0) == PRELIM)
1204 			continue;
1205 	}
1206 }
1207 
1208 void
do_chmod(int argc,char * argv[])1209 do_chmod(int argc, char *argv[])
1210 {
1211 
1212 	if (argc < 2 && !another(&argc, &argv, "mode"))
1213 		goto usage;
1214 	if ((argc < 3 && !another(&argc, &argv, "file")) || argc > 3) {
1215 usage:
1216 		fprintf(ttyout, "usage: %s mode file\n", argv[0]);
1217 		code = -1;
1218 		return;
1219 	}
1220 	(void)command("SITE CHMOD %s %s", argv[1], argv[2]);
1221 }
1222 
1223 void
do_umask(int argc,char * argv[])1224 do_umask(int argc, char *argv[])
1225 {
1226 	int oldverbose = verbose;
1227 
1228 	verbose = 1;
1229 	(void)command(argc == 1 ? "SITE UMASK" : "SITE UMASK %s", argv[1]);
1230 	verbose = oldverbose;
1231 }
1232 
1233 void
idle(int argc,char * argv[])1234 idle(int argc, char *argv[])
1235 {
1236 	int oldverbose = verbose;
1237 
1238 	verbose = 1;
1239 	(void)command(argc == 1 ? "SITE IDLE" : "SITE IDLE %s", argv[1]);
1240 	verbose = oldverbose;
1241 }
1242 
1243 /*
1244  * Ask the other side for help.
1245  */
1246 void
rmthelp(int argc,char * argv[])1247 rmthelp(int argc, char *argv[])
1248 {
1249 	int oldverbose = verbose;
1250 
1251 	verbose = 1;
1252 	(void)command(argc == 1 ? "HELP" : "HELP %s", argv[1]);
1253 	verbose = oldverbose;
1254 }
1255 
1256 /*
1257  * Terminate session and exit.
1258  */
1259 void
quit(int argc,char * argv[])1260 quit(int argc, char *argv[])
1261 {
1262 
1263 	if (connected)
1264 		disconnect(0, 0);
1265 	pswitch(1);
1266 	if (connected) {
1267 		disconnect(0, 0);
1268 	}
1269 	exit(0);
1270 }
1271 
1272 void
account(int argc,char * argv[])1273 account(int argc, char *argv[])
1274 {
1275 	char *ap;
1276 
1277 	if (argc > 2) {
1278 		fprintf(ttyout, "usage: %s [password]\n", argv[0]);
1279 		code = -1;
1280 		return;
1281 	}
1282 	else if (argc == 2)
1283 		ap = argv[1];
1284 	else
1285 		ap = getpass("Account:");
1286 	(void)command("ACCT %s", ap);
1287 }
1288 
1289 jmp_buf abortprox;
1290 
1291 void
proxabort(int signo)1292 proxabort(int signo)
1293 {
1294 	int save_errno = errno;
1295 
1296 	alarmtimer(0);
1297 	if (!proxy) {
1298 		pswitch(1);
1299 	}
1300 	if (connected) {
1301 		proxflag = 1;
1302 	}
1303 	else {
1304 		proxflag = 0;
1305 	}
1306 	pswitch(0);
1307 	errno = save_errno;
1308 	longjmp(abortprox, 1);
1309 }
1310 
1311 void
doproxy(int argc,char * argv[])1312 doproxy(int argc, char *argv[])
1313 {
1314 	struct cmd *c;
1315 	int cmdpos;
1316 	sig_t oldintr;
1317 
1318 	if (argc < 2 && !another(&argc, &argv, "command")) {
1319 		fprintf(ttyout, "usage: %s command\n", argv[0]);
1320 		code = -1;
1321 		return;
1322 	}
1323 	c = getcmd(argv[1]);
1324 	if (c == (struct cmd *) -1) {
1325 		fputs("?Ambiguous command.\n", ttyout);
1326 		(void)fflush(ttyout);
1327 		code = -1;
1328 		return;
1329 	}
1330 	if (c == 0) {
1331 		fputs("?Invalid command.\n", ttyout);
1332 		(void)fflush(ttyout);
1333 		code = -1;
1334 		return;
1335 	}
1336 	if (!c->c_proxy) {
1337 		fputs("?Invalid proxy command.\n", ttyout);
1338 		(void)fflush(ttyout);
1339 		code = -1;
1340 		return;
1341 	}
1342 	if (setjmp(abortprox)) {
1343 		code = -1;
1344 		return;
1345 	}
1346 	oldintr = signal(SIGINT, proxabort);
1347 	pswitch(1);
1348 	if (c->c_conn && !connected) {
1349 		fputs("Not connected.\n", ttyout);
1350 		(void)fflush(ttyout);
1351 		pswitch(0);
1352 		(void)signal(SIGINT, oldintr);
1353 		code = -1;
1354 		return;
1355 	}
1356 	cmdpos = strcspn(line, " \t");
1357 	if (cmdpos > 0)		/* remove leading "proxy " from input buffer */
1358 		memmove(line, line + cmdpos + 1, strlen(line) - cmdpos + 1);
1359 	(*c->c_handler)(argc-1, argv+1);
1360 	if (connected) {
1361 		proxflag = 1;
1362 	}
1363 	else {
1364 		proxflag = 0;
1365 	}
1366 	pswitch(0);
1367 	(void)signal(SIGINT, oldintr);
1368 }
1369 
1370 void
setcase(int argc,char * argv[])1371 setcase(int argc, char *argv[])
1372 {
1373 
1374 	code = togglevar(argc, argv, &mcase, "Case mapping");
1375 }
1376 
1377 void
setcr(int argc,char * argv[])1378 setcr(int argc, char *argv[])
1379 {
1380 
1381 	code = togglevar(argc, argv, &crflag, "Carriage Return stripping");
1382 }
1383 
1384 void
setntrans(int argc,char * argv[])1385 setntrans(int argc, char *argv[])
1386 {
1387 	if (argc == 1) {
1388 		ntflag = 0;
1389 		fputs("Ntrans off.\n", ttyout);
1390 		code = ntflag;
1391 		return;
1392 	}
1393 	ntflag++;
1394 	code = ntflag;
1395 	(void)strlcpy(ntin, argv[1], sizeof(ntin));
1396 	if (argc == 2) {
1397 		ntout[0] = '\0';
1398 		return;
1399 	}
1400 	(void)strlcpy(ntout, argv[2], sizeof(ntout));
1401 }
1402 
1403 void
setnmap(int argc,char * argv[])1404 setnmap(int argc, char *argv[])
1405 {
1406 	char *cp;
1407 
1408 	if (argc == 1) {
1409 		mapflag = 0;
1410 		fputs("Nmap off.\n", ttyout);
1411 		code = mapflag;
1412 		return;
1413 	}
1414 	if ((argc < 3 && !another(&argc, &argv, "outpattern")) || argc > 3) {
1415 		fprintf(ttyout, "usage: %s [inpattern outpattern]\n", argv[0]);
1416 		code = -1;
1417 		return;
1418 	}
1419 	mapflag = 1;
1420 	code = 1;
1421 	cp = strchr(altarg, ' ');
1422 	if (proxy) {
1423 		while(*++cp == ' ')
1424 			continue;
1425 		altarg = cp;
1426 		cp = strchr(altarg, ' ');
1427 	}
1428 	*cp = '\0';
1429 	(void)strncpy(mapin, altarg, PATH_MAX - 1);
1430 	while (*++cp == ' ')
1431 		continue;
1432 	(void)strncpy(mapout, cp, PATH_MAX - 1);
1433 }
1434 
1435 void
setpassive(int argc,char * argv[])1436 setpassive(int argc, char *argv[])
1437 {
1438 
1439 	code = togglevar(argc, argv, &passivemode,
1440 	    verbose ? "Passive mode" : NULL);
1441 }
1442 
1443 void
setsunique(int argc,char * argv[])1444 setsunique(int argc, char *argv[])
1445 {
1446 
1447 	code = togglevar(argc, argv, &sunique, "Store unique");
1448 }
1449 
1450 void
setrunique(int argc,char * argv[])1451 setrunique(int argc, char *argv[])
1452 {
1453 
1454 	code = togglevar(argc, argv, &runique, "Receive unique");
1455 }
1456 
1457 /* change directory to parent directory */
1458 void
cdup(int argc,char * argv[])1459 cdup(int argc, char *argv[])
1460 {
1461 	int r;
1462 
1463 	r = command("CDUP");
1464 	if (r == ERROR && code == 500) {
1465 		if (verbose)
1466 			fputs("CDUP command not recognized, trying XCUP.\n", ttyout);
1467 		r = command("XCUP");
1468 	}
1469 	if (r == COMPLETE)
1470 		dirchange = 1;
1471 }
1472 
1473 /*
1474  * Restart transfer at specific point
1475  */
1476 void
restart(int argc,char * argv[])1477 restart(int argc, char *argv[])
1478 {
1479 	off_t nrestart_point;
1480 	char *ep;
1481 
1482 	if (argc != 2)
1483 		fputs("restart: offset not specified.\n", ttyout);
1484 	else {
1485 		nrestart_point = strtoll(argv[1], &ep, 10);
1486 		if (nrestart_point == LLONG_MAX || *ep != '\0')
1487 			fputs("restart: invalid offset.\n", ttyout);
1488 		else {
1489 			fprintf(ttyout, "Restarting at %lld. Execute get, put "
1490 				"or append to initiate transfer\n",
1491 				(long long)nrestart_point);
1492 			restart_point = nrestart_point;
1493 		}
1494 	}
1495 }
1496 
1497 /*
1498  * Show remote system type
1499  */
1500 void
syst(int argc,char * argv[])1501 syst(int argc, char *argv[])
1502 {
1503 
1504 	(void)command("SYST");
1505 }
1506 
1507 void
macdef(int argc,char * argv[])1508 macdef(int argc, char *argv[])
1509 {
1510 	char *tmp;
1511 	int c;
1512 
1513 	if (macnum == 16) {
1514 		fputs("Limit of 16 macros have already been defined.\n", ttyout);
1515 		code = -1;
1516 		return;
1517 	}
1518 	if ((argc < 2 && !another(&argc, &argv, "macro-name")) || argc > 2) {
1519 		fprintf(ttyout, "usage: %s macro-name\n", argv[0]);
1520 		code = -1;
1521 		return;
1522 	}
1523 	if (interactive)
1524 		fputs(
1525 "Enter macro line by line, terminating it with a null line.\n", ttyout);
1526 	(void)strlcpy(macros[macnum].mac_name, argv[1],
1527 	    sizeof(macros[macnum].mac_name));
1528 	if (macnum == 0)
1529 		macros[macnum].mac_start = macbuf;
1530 	else
1531 		macros[macnum].mac_start = macros[macnum - 1].mac_end + 1;
1532 	tmp = macros[macnum].mac_start;
1533 	while (tmp != macbuf+4096) {
1534 		if ((c = getchar()) == EOF) {
1535 			fputs("macdef: end of file encountered.\n", ttyout);
1536 			code = -1;
1537 			return;
1538 		}
1539 		if ((*tmp = c) == '\n') {
1540 			if (tmp == macros[macnum].mac_start) {
1541 				macros[macnum++].mac_end = tmp;
1542 				code = 0;
1543 				return;
1544 			}
1545 			if (*(tmp-1) == '\0') {
1546 				macros[macnum++].mac_end = tmp - 1;
1547 				code = 0;
1548 				return;
1549 			}
1550 			*tmp = '\0';
1551 		}
1552 		tmp++;
1553 	}
1554 	while (1) {
1555 		while ((c = getchar()) != '\n' && c != EOF)
1556 			/* LOOP */;
1557 		if (c == EOF || getchar() == '\n') {
1558 			fputs("Macro not defined - 4K buffer exceeded.\n", ttyout);
1559 			code = -1;
1560 			return;
1561 		}
1562 	}
1563 }
1564 
1565 /*
1566  * Get size of file on remote machine
1567  */
1568 void
sizecmd(int argc,char * argv[])1569 sizecmd(int argc, char *argv[])
1570 {
1571 	off_t size;
1572 
1573 	if ((argc < 2 && !another(&argc, &argv, "file")) || argc > 2) {
1574 		fprintf(ttyout, "usage: %s file\n", argv[0]);
1575 		code = -1;
1576 		return;
1577 	}
1578 	size = remotesize(argv[1], 1);
1579 	if (size != -1)
1580 		fprintf(ttyout, "%s\t%lld\n", argv[1], (long long)size);
1581 	code = size;
1582 }
1583 
1584 /*
1585  * Get last modification time of file on remote machine
1586  */
1587 void
modtime(int argc,char * argv[])1588 modtime(int argc, char *argv[])
1589 {
1590 	time_t mtime;
1591 
1592 	if ((argc < 2 && !another(&argc, &argv, "file")) || argc > 2) {
1593 		fprintf(ttyout, "usage: %s file\n", argv[0]);
1594 		code = -1;
1595 		return;
1596 	}
1597 	mtime = remotemodtime(argv[1], 1);
1598 	if (mtime != -1)
1599 		fprintf(ttyout, "%s\t%s", argv[1], asctime(localtime(&mtime)));
1600 	code = mtime;
1601 }
1602 
1603 /*
1604  * Show status on remote machine
1605  */
1606 void
rmtstatus(int argc,char * argv[])1607 rmtstatus(int argc, char *argv[])
1608 {
1609 
1610 	(void)command(argc > 1 ? "STAT %s" : "STAT" , argv[1]);
1611 }
1612 
1613 /*
1614  * Get file if modtime is more recent than current file
1615  */
1616 void
newer(int argc,char * argv[])1617 newer(int argc, char *argv[])
1618 {
1619 
1620 	(void)getit(argc, argv, -1, "w");
1621 }
1622 
1623 /*
1624  * Display one file through $PAGER (defaults to "more").
1625  */
1626 void
page(int argc,char * argv[])1627 page(int argc, char *argv[])
1628 {
1629 	off_t orestart_point;
1630 	int ohash, overbose;
1631 	char *p, *pager, *oldargv1;
1632 
1633 	if ((argc < 2 && !another(&argc, &argv, "file")) || argc > 2) {
1634 		fprintf(ttyout, "usage: %s file\n", argv[0]);
1635 		code = -1;
1636 		return;
1637 	}
1638 	oldargv1 = argv[1];
1639 	if (!globulize(&argv[1])) {
1640 		code = -1;
1641 		return;
1642 	}
1643 	p = getenv("PAGER");
1644 	if (p == NULL || (*p == '\0'))
1645 		p = PAGER;
1646 	if (asprintf(&pager, "|%s", p) == -1)
1647 		errx(1, "Can't allocate memory for $PAGER");
1648 
1649 	orestart_point = restart_point;
1650 	ohash = hash;
1651 	overbose = verbose;
1652 	restart_point = hash = verbose = 0;
1653 	recvrequest("RETR", pager, argv[1], "r+w", 1, 0);
1654 	(void)free(pager);
1655 	restart_point = orestart_point;
1656 	hash = ohash;
1657 	verbose = overbose;
1658 	if (oldargv1 != argv[1])	/* free up after globulize() */
1659 		free(argv[1]);
1660 }
1661 
1662 #endif /* !SMALL */
1663 
1664