xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.bin/ftp/cmds.c (revision ea4ea50f064c5468142b24627acad09a41f060cb)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  *	Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23  *	Use is subject to license terms.
24  */
25 
26 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
27 /*	All Rights Reserved  	*/
28 
29 /*
30  *	University Copyright- Copyright (c) 1982, 1986, 1988
31  *	The Regents of the University of California
32  *	All Rights Reserved
33  *
34  *	University Acknowledgment- Portions of this document are derived from
35  *	software developed by the University of California, Berkeley, and its
36  *	contributors.
37  */
38 
39 /*
40  * FTP User Program -- Command Routines.
41  */
42 #define	FTP_NAMES
43 #include "ftp_var.h"
44 
45 FILE	*tmp_nlst = NULL;	/* tmp file; holds NLST results for mget, etc */
46 
47 static char *mname;
48 static jmp_buf jabort;
49 static jmp_buf abortprox;
50 
51 static char *remglob(char *argv[], int doswitch);
52 static char *onoff(int bool);
53 static int confirm(char *cmd, char *file);
54 static int globulize(char **cpp);
55 static void proxabort(int sig);
56 static void mabort(int sig);
57 static char *dotrans(char *name);
58 static char *domap(char *name);
59 static void getit(int argc, char *argv[], int restartit, char *mode);
60 
61 static char *getlevel(int);
62 
63 /* Prompt for command argument, add to buffer with space separator */
64 static int
65 prompt_for_arg(char *buffer, int buffer_size, char *prompt)
66 {
67 	if (strlen(buffer) > buffer_size - 2) {
68 		(void) printf("Line too long\n");
69 		return (-1);
70 	}
71 	strcat(buffer, " ");
72 	stop_timer();
73 	(void) printf("(%s) ", prompt);
74 	if (fgets(buffer + strlen(buffer), buffer_size - strlen(buffer), stdin)
75 	    == NULL) {
76 		reset_timer();
77 		return (-1);
78 	}
79 
80 	/* Flush what didn't fit in the buffer */
81 	if (buffer[strlen(buffer)-1] != '\n') {
82 		while (fgetc(stdin) != '\n' && !ferror(stdin) && !feof(stdin))
83 			;
84 		(void) printf("Line too long\n");
85 		reset_timer();
86 		return (-1);
87 	} else
88 		buffer[strlen(buffer)-1] = 0;
89 
90 	reset_timer();
91 	return (0);
92 }
93 
94 
95 /*
96  * Connect to peer server and
97  * auto-login, if possible.
98  */
99 void
100 setpeer(int argc, char *argv[])
101 {
102 	char *host;
103 
104 	if (connected) {
105 		(void) printf("Already connected to %s, use close first.\n",
106 			hostname);
107 		code = -1;
108 		return;
109 	}
110 	if (argc < 2) {
111 		if (prompt_for_arg(line, sizeof (line), "to") == -1) {
112 			code = -1;
113 			return;
114 		}
115 		makeargv();
116 		argc = margc;
117 		argv = margv;
118 	}
119 	if (argc > 3 || argc < 2) {
120 		(void) printf("usage: %s host-name [port]\n", argv[0]);
121 		code = -1;
122 		return;
123 	}
124 	strcpy(typename, "ascii");
125 	host = hookup(argv[1], (argc > 2 ? argv[2] : "ftp"));
126 	if (host) {
127 		int overbose;
128 		extern char reply_string[];
129 
130 		connected = 1;
131 		/*
132 		 * Set up defaults for FTP.
133 		 */
134 		clevel = dlevel = PROT_C;
135 		if (autoauth) {
136 			if (do_auth() && autoencrypt) {
137 			    clevel = PROT_P;
138 			    setpbsz(1<<20);
139 			    if (command("PROT P") == COMPLETE)
140 				dlevel = PROT_P;
141 			    else {
142 				(void) fprintf(stderr,
143 					"%s: couldn't enable encryption\n",
144 					argv[0]);
145 				/* unable to encrypt command channel, too! */
146 				dlevel = clevel = PROT_C;
147 			    }
148 			}
149 			if ((auth_type != AUTHTYPE_NONE) && (clevel == PROT_C))
150 				clevel = PROT_S;
151 		}
152 
153 		if (autologin)
154 			(void) login(argv[1]);
155 		/* if skipsyst is enabled, then don't send SYST command */
156 		if (skipsyst)
157 			return;
158 
159 		overbose = verbose;
160 		if (debug == 0)
161 			verbose = -1;
162 		if (command("SYST") == COMPLETE && overbose) {
163 			char *cp, c;
164 
165 			cp = index(reply_string+4, ' ');
166 			if (cp == NULL)
167 				cp = index(reply_string+4, '\r');
168 			if (cp) {
169 				if (cp[-1] == '.')
170 					cp--;
171 				c = *cp;
172 				*cp = '\0';
173 			}
174 
175 			(void) printf("Remote system type is %s.\n",
176 				reply_string+4);
177 			if (cp)
178 				*cp = c;
179 		}
180 		if (strncmp(reply_string, "215 UNIX Type: L8", 17) == 0) {
181 			setbinary(0, NULL);
182 			if (overbose)
183 				(void) printf(
184 				    "Using %s mode to transfer files.\n",
185 				    typename);
186 		} else if (overbose &&
187 		    strncmp(reply_string, "215 TOPS20", 10) == 0) {
188 			(void) printf(
189 			    "Remember to set tenex mode when transfering "
190 			    "binary files from this machine.\n");
191 		}
192 		verbose = overbose;
193 	}
194 }
195 
196 static struct types {
197 	char	*t_name;
198 	char	*t_mode;
199 	int	t_type;
200 	char	*t_arg;
201 } types[] = {
202 	{ "ascii",	"A",	TYPE_A,	0 },
203 	{ "binary",	"I",	TYPE_I,	0 },
204 	{ "image",	"I",	TYPE_I,	0 },
205 	{ "ebcdic",	"E",	TYPE_E,	0 },
206 	{ "tenex",	"L",	TYPE_L,	bytename },
207 	0
208 };
209 
210 /*
211  * Set transfer type.
212  */
213 void
214 settype(int argc, char *argv[])
215 {
216 	struct types *p;
217 	int comret;
218 
219 	if (argc > 2) {
220 		char *sep;
221 
222 		(void) printf("usage: %s [", argv[0]);
223 		sep = " ";
224 		for (p = types; p->t_name; p++) {
225 			(void) printf("%s%s", sep, p->t_name);
226 			if (*sep == ' ')
227 				sep = " | ";
228 		}
229 		(void) printf(" ]\n");
230 		code = -1;
231 		return;
232 	}
233 	if (argc < 2) {
234 		(void) printf("Using %s mode to transfer files.\n", typename);
235 		code = 0;
236 		return;
237 	}
238 	for (p = types; p->t_name; p++)
239 		if (strcmp(argv[1], p->t_name) == 0)
240 			break;
241 	if (p->t_name == 0) {
242 		(void) printf("%s: unknown mode\n", argv[1]);
243 		code = -1;
244 		return;
245 	}
246 	if ((p->t_arg != NULL) && (*(p->t_arg) != '\0'))
247 		comret = command("TYPE %s %s", p->t_mode, p->t_arg);
248 	else
249 		comret = command("TYPE %s", p->t_mode);
250 	if (comret == COMPLETE) {
251 		(void) strcpy(typename, p->t_name);
252 		type = p->t_type;
253 	}
254 }
255 
256 /*
257  * Set binary transfer type.
258  */
259 /*ARGSUSED*/
260 void
261 setbinary(int argc, char *argv[])
262 {
263 	call(settype, "type", "binary", 0);
264 }
265 
266 /*
267  * Set ascii transfer type.
268  */
269 /*ARGSUSED*/
270 void
271 setascii(int argc, char *argv[])
272 {
273 	call(settype, "type", "ascii", 0);
274 }
275 
276 /*
277  * Set tenex transfer type.
278  */
279 /*ARGSUSED*/
280 void
281 settenex(int argc, char *argv[])
282 {
283 	call(settype, "type", "tenex", 0);
284 }
285 
286 /*
287  * Set ebcdic transfer type.
288  */
289 /*ARGSUSED*/
290 void
291 setebcdic(int argc, char *argv[])
292 {
293 	call(settype, "type", "ebcdic", 0);
294 }
295 
296 /*
297  * Set file transfer mode.
298  */
299 /*ARGSUSED*/
300 void
301 setmode(int argc, char *argv[])
302 {
303 	(void) printf("We only support %s mode, sorry.\n", modename);
304 	code = -1;
305 }
306 
307 /*
308  * Set file transfer format.
309  */
310 /*ARGSUSED*/
311 void
312 setform(int argc, char *argv[])
313 {
314 	(void) printf("We only support %s format, sorry.\n", formname);
315 	code = -1;
316 }
317 
318 /*
319  * Set file transfer structure.
320  */
321 /*ARGSUSED*/
322 void
323 setstruct(int argc, char *argv[])
324 {
325 
326 	(void) printf("We only support %s structure, sorry.\n", structname);
327 	code = -1;
328 }
329 
330 /*
331  * Send a single file.
332  */
333 void
334 put(int argc, char *argv[])
335 {
336 	char *cmd;
337 	int loc = 0;
338 	char *oldargv1;
339 
340 	if (argc == 2) {
341 		argc++;
342 		argv[2] = argv[1];
343 		loc++;
344 	}
345 	if (argc < 2) {
346 		if (prompt_for_arg(line, sizeof (line), "local-file") == -1) {
347 			code = -1;
348 			return;
349 		}
350 		makeargv();
351 		argc = margc;
352 		argv = margv;
353 	}
354 	if (argc < 2) {
355 usage:
356 		(void) printf("usage: %s local-file remote-file\n", argv[0]);
357 		code = -1;
358 		return;
359 	}
360 	if (argc < 3) {
361 		if (prompt_for_arg(line, sizeof (line), "remote-file") == -1) {
362 			code = -1;
363 			return;
364 		}
365 		makeargv();
366 		argc = margc;
367 		argv = margv;
368 	}
369 	if (argc < 3)
370 		goto usage;
371 	oldargv1 = argv[1];
372 	if (!globulize(&argv[1])) {
373 		code = -1;
374 		return;
375 	}
376 	/*
377 	 * If "globulize" modifies argv[1], and argv[2] is a copy of
378 	 * the old argv[1], make it a copy of the new argv[1].
379 	 */
380 	if (argv[1] != oldargv1 && argv[2] == oldargv1) {
381 		argv[2] = argv[1];
382 	}
383 	cmd = (argv[0][0] == 'a') ? "APPE" : ((sunique) ? "STOU" : "STOR");
384 	if (loc && ntflag) {
385 		argv[2] = dotrans(argv[2]);
386 	}
387 	if (loc && mapflag) {
388 		argv[2] = domap(argv[2]);
389 	}
390 	sendrequest(cmd, argv[1], argv[2], 1);
391 }
392 
393 /*ARGSUSED*/
394 static void
395 mabort(int sig)
396 {
397 	int ointer;
398 
399 	(void) printf("\n");
400 	(void) fflush(stdout);
401 	if (mflag && fromatty) {
402 		ointer = interactive;
403 		interactive = 1;
404 		if (confirm("Continue with", mname)) {
405 			interactive = ointer;
406 			longjmp(jabort, 0);
407 		}
408 		interactive = ointer;
409 	}
410 	mflag = 0;
411 	longjmp(jabort, 0);
412 }
413 
414 /*
415  * Send multiple files.
416  */
417 void
418 mput(int argc, char *argv[])
419 {
420 	int i;
421 	int ointer;
422 	void (*oldintr)();
423 	char *tp;
424 	int	len;
425 
426 	if (argc < 2) {
427 		if (prompt_for_arg(line, sizeof (line), "local-files") == -1) {
428 			code = -1;
429 			return;
430 		}
431 		makeargv();
432 		argc = margc;
433 		argv = margv;
434 	}
435 	if (argc < 2) {
436 		(void) printf("usage: %s local-files\n", argv[0]);
437 		code = -1;
438 		return;
439 	}
440 	mname = argv[0];
441 	mflag = 1;
442 	oldintr = signal(SIGINT, mabort);
443 	(void) setjmp(jabort);
444 	if (proxy) {
445 		char *cp, *tp2, tmpbuf[MAXPATHLEN];
446 
447 		while ((cp = remglob(argv, 0)) != NULL) {
448 			if (*cp == 0) {
449 				mflag = 0;
450 				continue;
451 			}
452 			if (mflag && confirm(argv[0], cp)) {
453 				tp = cp;
454 				if (mcase) {
455 					while (*tp) {
456 						if ((len =
457 						    mblen(tp, MB_CUR_MAX)) <= 0)
458 							len = 1;
459 						if (islower(*tp))
460 							break;
461 						tp += len;
462 					}
463 					if (!*tp) {
464 						tp = cp;
465 						tp2 = tmpbuf;
466 						while (*tp) {
467 							if ((len = mblen(tp,
468 							    MB_CUR_MAX)) <= 0)
469 								len = 1;
470 							memcpy(tp2, tp, len);
471 							if (isupper(*tp2)) {
472 								*tp2 = 'a' +
473 								    *tp2 - 'A';
474 							}
475 							tp += len;
476 							tp2 += len;
477 						}
478 						*tp2 = 0;
479 						tp = tmpbuf;
480 					}
481 				}
482 				if (ntflag) {
483 					tp = dotrans(tp);
484 				}
485 				if (mapflag) {
486 					tp = domap(tp);
487 				}
488 				sendrequest((sunique) ? "STOU" : "STOR",
489 				    cp, tp, 0);
490 				if (!mflag && fromatty) {
491 					ointer = interactive;
492 					interactive = 1;
493 					if (confirm("Continue with", "mput")) {
494 						mflag++;
495 					}
496 					interactive = ointer;
497 				}
498 			}
499 		}
500 		(void) signal(SIGINT, oldintr);
501 		mflag = 0;
502 		return;
503 	}
504 	for (i = 1; i < argc; i++) {
505 		char **cpp, **gargs;
506 
507 		if (!doglob) {
508 			if (mflag && confirm(argv[0], argv[i])) {
509 				tp = (ntflag) ? dotrans(argv[i]) : argv[i];
510 				tp = (mapflag) ? domap(tp) : tp;
511 				sendrequest((sunique) ? "STOU" : "STOR",
512 				    argv[i], tp, 1);
513 				if (!mflag && fromatty) {
514 					ointer = interactive;
515 					interactive = 1;
516 					if (confirm("Continue with", "mput")) {
517 						mflag++;
518 					}
519 					interactive = ointer;
520 				}
521 			}
522 			continue;
523 		}
524 		gargs = glob(argv[i]);
525 		if (globerr != NULL) {
526 			(void) printf("%s\n", globerr);
527 			if (gargs)
528 				blkfree(gargs);
529 			continue;
530 		}
531 		for (cpp = gargs; cpp && *cpp != NULL; cpp++) {
532 			if (mflag && confirm(argv[0], *cpp)) {
533 				tp = (ntflag) ? dotrans(*cpp) : *cpp;
534 				tp = (mapflag) ? domap(tp) : tp;
535 				sendrequest((sunique) ? "STOU" : "STOR",
536 				    *cpp, tp, 0);
537 				if (!mflag && fromatty) {
538 					ointer = interactive;
539 					interactive = 1;
540 					if (confirm("Continue with", "mput")) {
541 						mflag++;
542 					}
543 					interactive = ointer;
544 				}
545 			}
546 		}
547 		if (gargs != NULL)
548 			blkfree(gargs);
549 	}
550 	(void) signal(SIGINT, oldintr);
551 	mflag = 0;
552 }
553 
554 /*
555  * Restart transfer at a specific offset.
556  */
557 void
558 restart(int argc, char *argv[])
559 {
560 	off_t orestart_point = restart_point;
561 
562 	if (argc > 2) {
563 		(void) printf("usage: %s [marker]\n", argv[0]);
564 		code = -1;
565 		return;
566 	}
567 	if (argc == 2) {
568 		longlong_t rp;
569 		char *endp;
570 
571 		errno = 0;
572 		rp = strtoll(argv[1], &endp, 10);
573 		if (errno || rp < 0 || *endp != '\0')
574 			(void) printf("%s: Invalid offset `%s'\n",
575 				argv[0], argv[1]);
576 		else
577 			restart_point = rp;
578 	}
579 	if (restart_point == 0) {
580 		if (orestart_point == 0)
581 			(void) printf("No restart marker defined\n");
582 		else
583 			(void) printf("Restart marker cleared\n");
584 	} else
585 		(void) printf(
586 			"Restarting at %lld for next get, put or append\n",
587 			(longlong_t)restart_point);
588 }
589 
590 void
591 reget(int argc, char *argv[])
592 {
593 	getit(argc, argv, 1, "r+w");
594 }
595 
596 void
597 get(int argc, char *argv[])
598 {
599 	getit(argc, argv, 0, restart_point ? "r+w" : "w");
600 }
601 
602 /*
603  * Receive one file.
604  */
605 static void
606 getit(int argc, char *argv[], int restartit, char *mode)
607 {
608 	int loc = 0;
609 	int len;
610 	int allowpipe = 1;
611 
612 	if (argc == 2) {
613 		argc++;
614 		argv[2] = argv[1];
615 		/* Only permit !file if two arguments. */
616 		allowpipe = 0;
617 		loc++;
618 	}
619 	if (argc < 2) {
620 		if (prompt_for_arg(line, sizeof (line), "remote-file") == -1) {
621 			code = -1;
622 			return;
623 		}
624 		makeargv();
625 		argc = margc;
626 		argv = margv;
627 	}
628 	if (argc < 2) {
629 usage:
630 		(void) printf("usage: %s remote-file [ local-file ]\n",
631 			argv[0]);
632 		code = -1;
633 		return;
634 	}
635 	if (argc < 3) {
636 		if (prompt_for_arg(line, sizeof (line), "local-file") == -1) {
637 			code = -1;
638 			return;
639 		}
640 		makeargv();
641 		argc = margc;
642 		argv = margv;
643 	}
644 	if (argc < 3)
645 		goto usage;
646 	if (!globulize(&argv[2])) {
647 		code = -1;
648 		return;
649 	}
650 	if (loc && mcase) {
651 		char *tp = argv[1], *tp2, tmpbuf[MAXPATHLEN];
652 
653 		while (*tp) {
654 			if ((len = mblen(tp, MB_CUR_MAX)) <= 0)
655 				len = 1;
656 			if (islower(*tp))
657 				break;
658 			tp += len;
659 		}
660 		if (!*tp) {
661 			tp = argv[2];
662 			tp2 = tmpbuf;
663 			while (*tp) {
664 				if ((len = mblen(tp, MB_CUR_MAX)) <= 0)
665 					len = 1;
666 				memcpy(tp2, tp, len);
667 				if (isupper(*tp2))
668 					*tp2 = 'a' + *tp2 - 'A';
669 				tp += len;
670 				tp2 += len;
671 			}
672 			*tp2 = 0;
673 			argv[2] = tmpbuf;
674 		}
675 	}
676 	if (loc && ntflag) {
677 		argv[2] = dotrans(argv[2]);
678 	}
679 	if (loc && mapflag) {
680 		argv[2] = domap(argv[2]);
681 	}
682 	if (restartit) {
683 		struct stat stbuf;
684 
685 		if (stat(argv[2], &stbuf) < 0) {
686 			perror(argv[2]);
687 			code = -1;
688 			return;
689 		}
690 		restart_point = stbuf.st_size;
691 	}
692 	recvrequest("RETR", argv[2], argv[1], mode, allowpipe);
693 	restart_point = 0;
694 }
695 
696 /*
697  * Get multiple files.
698  */
699 void
700 mget(int argc, char *argv[])
701 {
702 	char *cp, *tp, *tp2, tmpbuf[MAXPATHLEN];
703 	int ointer;
704 	void (*oldintr)();
705 	int need_convert;
706 	int	len;
707 
708 	if (argc < 2) {
709 		if (prompt_for_arg(line, sizeof (line), "remote-files") < 0) {
710 			code = -1;
711 			return;
712 		}
713 		makeargv();
714 		argc = margc;
715 		argv = margv;
716 	}
717 	if (argc < 2) {
718 		(void) printf("usage: %s remote-files\n", argv[0]);
719 		code = -1;
720 		return;
721 	}
722 	mname = argv[0];
723 	mflag = 1;
724 	oldintr = signal(SIGINT, mabort);
725 	(void) setjmp(jabort);
726 	while ((cp = remglob(argv, proxy)) != NULL) {
727 		if (*cp == '\0') {
728 			mflag = 0;
729 			continue;
730 		}
731 		if (mflag && confirm(argv[0], cp)) {
732 			strcpy(tmpbuf, cp);
733 			tp =  tmpbuf;
734 			need_convert = 1;
735 			if (mcase) {
736 				tp2 = tp;
737 				while (*tp2 && need_convert) {
738 				/* Need any case convert? */
739 					if (islower(*tp2))
740 						need_convert = 0;
741 					if ((len = mblen(tp2, MB_CUR_MAX)) <= 0)
742 						len = 1;
743 					tp2 += len;
744 				}
745 				tp2 = tp;
746 				while (need_convert && *tp2) {
747 				/* Convert to lower case */
748 					if (isupper(*tp2))
749 						*tp2 = tolower(*tp2);
750 					if ((len = mblen(tp2, MB_CUR_MAX)) <= 0)
751 						len = 1;
752 					tp2 += len;
753 				}
754 			}
755 
756 			if (ntflag) {
757 				tp = dotrans(tp);
758 			}
759 			if (mapflag) {
760 				tp = domap(tp);
761 			}
762 			recvrequest("RETR", tp, cp, "w", 0);
763 			restart_point = 0;
764 			if (!mflag && fromatty) {
765 				ointer = interactive;
766 				interactive = 1;
767 				if (confirm("Continue with", "mget")) {
768 					mflag++;
769 				}
770 				interactive = ointer;
771 			}
772 		}
773 	}
774 	(void) signal(SIGINT, oldintr);
775 	mflag = 0;
776 }
777 
778 static char *
779 remglob(char *argv[], int doswitch)
780 {
781 	static char buf[MAXPATHLEN];
782 	static char **args;
783 	int oldverbose, oldhash;
784 	char *cp;
785 
786 	if (!mflag) {
787 		if (!doglob) {
788 			args = NULL;
789 		} else {
790 			if (tmp_nlst != NULL) {
791 				(void) fclose(tmp_nlst);
792 				tmp_nlst = NULL;
793 			}
794 		}
795 		return (NULL);
796 	}
797 	if (!doglob) {
798 		if (args == NULL)
799 			args = argv;
800 		if ((cp = *++args) == NULL)
801 			args = NULL;
802 		return (cp);
803 	}
804 	if (tmp_nlst == NULL) {
805 		if ((tmp_nlst = tmpfile()) == NULL) {
806 			(void) printf("%s\n", strerror(errno));
807 			return (NULL);
808 		}
809 		oldverbose = verbose, verbose = 0;
810 		oldhash = hash, hash = 0;
811 		if (doswitch) {
812 			pswitch(!proxy);
813 		}
814 		for (; *++argv != NULL; )
815 			recvrequest("NLST", NULL, *argv, "", 0);
816 		rewind(tmp_nlst);
817 		if (doswitch) {
818 			pswitch(!proxy);
819 		}
820 		verbose = oldverbose; hash = oldhash;
821 	}
822 	reset_timer();
823 	if (fgets(buf, sizeof (buf), tmp_nlst) == NULL) {
824 		(void) fclose(tmp_nlst), tmp_nlst = NULL;
825 		return (NULL);
826 	}
827 	if ((cp = index(buf, '\n')) != NULL)
828 		*cp = '\0';
829 	return (buf);
830 }
831 
832 static char *
833 onoff(int bool)
834 {
835 	return (bool ? "on" : "off");
836 }
837 
838 /*
839  * Show status.
840  */
841 /*ARGSUSED*/
842 void
843 status(int argc, char *argv[])
844 {
845 	int i;
846 	char *levelp;
847 
848 	if (connected)
849 		(void) printf("Connected to %s.\n", hostname);
850 	else
851 		(void) printf("Not connected.\n");
852 	if (!proxy) {
853 		pswitch(1);
854 		if (connected) {
855 			(void) printf("Connected for proxy commands to %s.\n",
856 			    hostname);
857 		} else {
858 			(void) printf("No proxy connection.\n");
859 		}
860 		pswitch(0);
861 	}
862 
863 	if (auth_type != AUTHTYPE_NONE)
864 		(void) printf("Authentication type: %s\n",
865 			GSS_AUTHTYPE_NAME(auth_type));
866 	else
867 		(void) printf("Not authenticated.\n");
868 	(void) printf("Mechanism: %s\n", mechstr);
869 	(void) printf("Autoauth: %s; Autologin: %s\n",
870 		onoff(autoauth), onoff(autologin));
871 	levelp = getlevel(clevel);
872 	(void) printf("Control Channel Protection Level: %s\n",
873 		levelp ? levelp : "<unknown>");
874 	levelp = getlevel(dlevel);
875 	(void) printf("Data Channel Protection Level: %s\n",
876 		levelp ? levelp : "<unknown>");
877 
878 	(void) printf("Passive mode: %s.\n", onoff(passivemode));
879 	(void) printf("Mode: %s; Type: %s; Form: %s; Structure: %s\n",
880 		modename, typename, formname, structname);
881 	(void) printf("Verbose: %s; Bell: %s; Prompting: %s; Globbing: %s\n",
882 		onoff(verbose), onoff(bell), onoff(interactive),
883 		onoff(doglob));
884 	(void) printf("Store unique: %s; Receive unique: %s\n", onoff(sunique),
885 		onoff(runique));
886 	(void) printf("Case: %s; CR stripping: %s\n",
887 		onoff(mcase), onoff(crflag));
888 	if (ntflag) {
889 		(void) printf("Ntrans: (in) %s (out) %s\n", ntin, ntout);
890 	} else {
891 		(void) printf("Ntrans: off\n");
892 	}
893 	if (mapflag) {
894 		(void) printf("Nmap: (in) %s (out) %s\n", mapin, mapout);
895 	} else {
896 		(void) printf("Nmap: off\n");
897 	}
898 	(void) printf("Hash mark printing: %s; Use of PORT cmds: %s\n",
899 		onoff(hash), onoff(sendport));
900 	if (macnum > 0) {
901 		(void) printf("Macros:\n");
902 		for (i = 0; i < macnum; i++) {
903 			(void) printf("\t%s\n", macros[i].mac_name);
904 		}
905 	}
906 	code = 0;
907 }
908 
909 /*
910  * Set beep on cmd completed mode.
911  */
912 /*ARGSUSED*/
913 void
914 setbell(int argc, char *argv[])
915 {
916 	bell = !bell;
917 	(void) printf("Bell mode %s.\n", onoff(bell));
918 	code = bell;
919 }
920 
921 /*
922  * Turn on packet tracing.
923  */
924 /*ARGSUSED*/
925 void
926 settrace(int argc, char *argv[])
927 {
928 	trace = !trace;
929 	(void) printf("Packet tracing %s.\n", onoff(trace));
930 	code = trace;
931 }
932 
933 /*
934  * Toggle hash mark printing during transfers.
935  */
936 /*ARGSUSED*/
937 void
938 sethash(int argc, char *argv[])
939 {
940 	hash = !hash;
941 	(void) printf("Hash mark printing %s", onoff(hash));
942 	code = hash;
943 	if (hash)
944 		(void) printf(" (%d bytes/hash mark)", HASHSIZ);
945 	(void) printf(".\n");
946 }
947 
948 /*
949  * Turn on printing of server echo's.
950  */
951 /*ARGSUSED*/
952 void
953 setverbose(int argc, char *argv[])
954 {
955 	verbose = !verbose;
956 	(void) printf("Verbose mode %s.\n", onoff(verbose));
957 	code = verbose;
958 }
959 
960 /*
961  * Toggle PORT cmd use before each data connection.
962  */
963 /*ARGSUSED*/
964 void
965 setport(int argc, char *argv[])
966 {
967 	sendport = !sendport;
968 	(void) printf("Use of PORT cmds %s.\n", onoff(sendport));
969 	code = sendport;
970 }
971 
972 /*
973  * Turn on interactive prompting
974  * during mget, mput, and mdelete.
975  */
976 /*ARGSUSED*/
977 void
978 setprompt(int argc, char *argv[])
979 {
980 	interactive = !interactive;
981 	(void) printf("Interactive mode %s.\n", onoff(interactive));
982 	code = interactive;
983 }
984 
985 /*
986  * Toggle metacharacter interpretation
987  * on local file names.
988  */
989 /*ARGSUSED*/
990 void
991 setglob(int argc, char *argv[])
992 {
993 	doglob = !doglob;
994 	(void) printf("Globbing %s.\n", onoff(doglob));
995 	code = doglob;
996 }
997 
998 /*
999  * Set debugging mode on/off and/or
1000  * set level of debugging.
1001  */
1002 void
1003 setdebug(int argc, char *argv[])
1004 {
1005 	int val;
1006 
1007 	if (argc > 1) {
1008 		val = atoi(argv[1]);
1009 		if (val < 0) {
1010 			(void) printf("%s: bad debugging value.\n", argv[1]);
1011 			code = -1;
1012 			return;
1013 		}
1014 	} else
1015 		val = !debug;
1016 	debug = val;
1017 	if (debug)
1018 		options |= SO_DEBUG;
1019 	else
1020 		options &= ~SO_DEBUG;
1021 	(void) printf("Debugging %s (debug=%d).\n", onoff(debug), debug);
1022 	code = debug > 0;
1023 }
1024 
1025 /*
1026  * Set current working directory
1027  * on remote machine.
1028  */
1029 void
1030 cd(int argc, char *argv[])
1031 {
1032 	if (argc < 2) {
1033 		if (prompt_for_arg(line, sizeof (line), "remote-directory") <
1034 		    0) {
1035 			code = -1;
1036 			return;
1037 		}
1038 		makeargv();
1039 		argc = margc;
1040 		argv = margv;
1041 	}
1042 	if (argc < 2) {
1043 		(void) printf("usage: %s remote-directory\n", argv[0]);
1044 		code = -1;
1045 		return;
1046 	}
1047 	(void) command("CWD %s", argv[1]);
1048 }
1049 
1050 /*
1051  * Set current working directory
1052  * on local machine.
1053  */
1054 void
1055 lcd(int argc, char *argv[])
1056 {
1057 	char buf[MAXPATHLEN], *bufptr;
1058 
1059 	if (argc < 2)
1060 		argc++, argv[1] = home;
1061 	if (argc != 2) {
1062 		(void) printf("usage: %s local-directory\n", argv[0]);
1063 		code = -1;
1064 		return;
1065 	}
1066 	if (!globulize(&argv[1])) {
1067 		code = -1;
1068 		return;
1069 	}
1070 	if (chdir(argv[1]) < 0) {
1071 		perror(argv[1]);
1072 		code = -1;
1073 		return;
1074 	}
1075 	bufptr = getcwd(buf, MAXPATHLEN);
1076 	/*
1077 	 * Even though chdir may succeed, getcwd may fail if a component
1078 	 * of the pwd is unreadable. In this case, print the argument to
1079 	 * chdir as the resultant directory, since we know it succeeded above.
1080 	 */
1081 	(void) printf("Local directory now %s\n", (bufptr ? bufptr : argv[1]));
1082 	code = 0;
1083 }
1084 
1085 /*
1086  * Delete a single file.
1087  */
1088 void
1089 delete(int argc, char *argv[])
1090 {
1091 
1092 	if (argc < 2) {
1093 		if (prompt_for_arg(line, sizeof (line), "remote-file") < 0) {
1094 			code = -1;
1095 			return;
1096 		}
1097 		makeargv();
1098 		argc = margc;
1099 		argv = margv;
1100 	}
1101 	if (argc < 2) {
1102 		(void) printf("usage: %s remote-file\n", argv[0]);
1103 		code = -1;
1104 		return;
1105 	}
1106 	(void) command("DELE %s", argv[1]);
1107 }
1108 
1109 /*
1110  * Delete multiple files.
1111  */
1112 void
1113 mdelete(int argc, char *argv[])
1114 {
1115 	char *cp;
1116 	int ointer;
1117 	void (*oldintr)();
1118 
1119 	if (argc < 2) {
1120 		if (prompt_for_arg(line, sizeof (line), "remote-files") < 0) {
1121 			code = -1;
1122 			return;
1123 		}
1124 		makeargv();
1125 		argc = margc;
1126 		argv = margv;
1127 	}
1128 	if (argc < 2) {
1129 		(void) printf("usage: %s remote-files\n", argv[0]);
1130 		code = -1;
1131 		return;
1132 	}
1133 	mname = argv[0];
1134 	mflag = 1;
1135 	oldintr = signal(SIGINT, mabort);
1136 	(void) setjmp(jabort);
1137 	while ((cp = remglob(argv, 0)) != NULL) {
1138 		if (*cp == '\0') {
1139 			mflag = 0;
1140 			continue;
1141 		}
1142 		if (mflag && confirm(argv[0], cp)) {
1143 			(void) command("DELE %s", cp);
1144 			if (!mflag && fromatty) {
1145 				ointer = interactive;
1146 				interactive = 1;
1147 				if (confirm("Continue with", "mdelete")) {
1148 					mflag++;
1149 				}
1150 				interactive = ointer;
1151 			}
1152 		}
1153 	}
1154 	(void) signal(SIGINT, oldintr);
1155 	mflag = 0;
1156 }
1157 
1158 /*
1159  * Rename a remote file.
1160  */
1161 void
1162 renamefile(int argc, char *argv[])
1163 {
1164 
1165 	if (argc < 2) {
1166 		if (prompt_for_arg(line, sizeof (line), "from-name") < 0) {
1167 			code = -1;
1168 			return;
1169 		}
1170 		makeargv();
1171 		argc = margc;
1172 		argv = margv;
1173 	}
1174 	if (argc < 2) {
1175 usage:
1176 		(void) printf("%s from-name to-name\n", argv[0]);
1177 		code = -1;
1178 		return;
1179 	}
1180 	if (argc < 3) {
1181 		if (prompt_for_arg(line, sizeof (line), "to-name") < 0) {
1182 			code = -1;
1183 			return;
1184 		}
1185 		makeargv();
1186 		argc = margc;
1187 		argv = margv;
1188 	}
1189 	if (argc < 3)
1190 		goto usage;
1191 	if (command("RNFR %s", argv[1]) == CONTINUE)
1192 		(void) command("RNTO %s", argv[2]);
1193 }
1194 
1195 /*
1196  * Get a directory listing
1197  * of remote files.
1198  */
1199 void
1200 ls(int argc, char *argv[])
1201 {
1202 	char *cmd;
1203 
1204 	if (argc < 2)
1205 		argc++, argv[1] = NULL;
1206 	if (argc < 3)
1207 		argc++, argv[2] = "-";
1208 	if (argc > 3) {
1209 		(void) printf("usage: %s remote-directory local-file\n",
1210 			argv[0]);
1211 		code = -1;
1212 		return;
1213 	}
1214 	if (ls_invokes_NLST) {
1215 		cmd = ((argv[0][0] == 'l' || argv[0][0] == 'n') ?
1216 		    "NLST" : "LIST");
1217 	} else {
1218 		cmd = ((argv[0][0] == 'n') ? "NLST" : "LIST");
1219 	}
1220 	if (strcmp(argv[2], "-") && !globulize(&argv[2])) {
1221 		code = -1;
1222 		return;
1223 	}
1224 	recvrequest(cmd, argv[2], argv[1], "w", 1);
1225 }
1226 
1227 /*
1228  * Get a directory listing
1229  * of multiple remote files.
1230  */
1231 void
1232 mls(int argc, char *argv[])
1233 {
1234 	char *cmd, mode[1], *dest;
1235 	int ointer, i;
1236 	void (*oldintr)();
1237 
1238 	if (argc < 2) {
1239 		if (prompt_for_arg(line, sizeof (line), "remote-files") < 0) {
1240 			code = -1;
1241 			return;
1242 		}
1243 		makeargv();
1244 		argc = margc;
1245 		argv = margv;
1246 	}
1247 	if (argc < 3) {
1248 		if (prompt_for_arg(line, sizeof (line), "local-file") < 0) {
1249 			code = -1;
1250 			return;
1251 		}
1252 		makeargv();
1253 		argc = margc;
1254 		argv = margv;
1255 	}
1256 	if (argc < 3) {
1257 		(void) printf("usage: %s remote-files local-file\n", argv[0]);
1258 		code = -1;
1259 		return;
1260 	}
1261 	dest = argv[argc - 1];
1262 	argv[argc - 1] = NULL;
1263 	if (strcmp(dest, "-") && *dest != '|')
1264 		if (!globulize(&dest) ||
1265 		    !confirm("output to local-file:", dest)) {
1266 			code = -1;
1267 			return;
1268 		}
1269 	cmd = argv[0][1] == 'l' ? "NLST" : "LIST";
1270 	mname = argv[0];
1271 	mflag = 1;
1272 	oldintr = signal(SIGINT, mabort);
1273 	(void) setjmp(jabort);
1274 	for (i = 1; mflag && i < argc-1; ++i) {
1275 		*mode = (i == 1) ? 'w' : 'a';
1276 		recvrequest(cmd, dest, argv[i], mode, 1);
1277 		if (!mflag && fromatty) {
1278 			ointer = interactive;
1279 			interactive = 1;
1280 			if (confirm("Continue with", argv[0])) {
1281 				mflag ++;
1282 			}
1283 			interactive = ointer;
1284 		}
1285 	}
1286 	(void) signal(SIGINT, oldintr);
1287 	mflag = 0;
1288 }
1289 
1290 /*
1291  * Do a shell escape
1292  */
1293 /*ARGSUSED*/
1294 void
1295 shell(int argc, char *argv[])
1296 {
1297 	pid_t pid;
1298 	void (*old1)(), (*old2)();
1299 	char *shellstring, *namep;
1300 	int status;
1301 
1302 	stop_timer();
1303 	old1 = signal(SIGINT, SIG_IGN);
1304 	old2 = signal(SIGQUIT, SIG_IGN);
1305 	if ((pid = fork()) == 0) {
1306 		closefrom(STDERR_FILENO + 1);
1307 		(void) signal(SIGINT, SIG_DFL);
1308 		(void) signal(SIGQUIT, SIG_DFL);
1309 		shellstring = getenv("SHELL");
1310 		if (shellstring == NULL)
1311 			shellstring = "/bin/sh";
1312 		namep = rindex(shellstring, '/');
1313 		if (namep == NULL)
1314 			namep = shellstring;
1315 		if (argc > 1) {
1316 			if (debug) {
1317 				(void) printf("%s -c %s\n", shellstring,
1318 					altarg);
1319 				(void) fflush(stdout);
1320 			}
1321 			execl(shellstring, namep, "-c", altarg, (char *)0);
1322 		} else {
1323 			if (debug) {
1324 				(void) printf("%s\n", shellstring);
1325 				(void) fflush(stdout);
1326 			}
1327 			execl(shellstring, namep, (char *)0);
1328 		}
1329 		perror(shellstring);
1330 		code = -1;
1331 		exit(1);
1332 		}
1333 	if (pid > 0)
1334 		while (wait(&status) != pid)
1335 			;
1336 	(void) signal(SIGINT, old1);
1337 	(void) signal(SIGQUIT, old2);
1338 	reset_timer();
1339 	if (pid == (pid_t)-1) {
1340 		perror("Try again later");
1341 		code = -1;
1342 	} else {
1343 		code = 0;
1344 	}
1345 }
1346 
1347 /*
1348  * Send new user information (re-login)
1349  */
1350 void
1351 user(int argc, char *argv[])
1352 {
1353 	char acct[80];
1354 	int n, aflag = 0;
1355 
1356 	if (argc < 2) {
1357 		if (prompt_for_arg(line, sizeof (line), "username") < 0) {
1358 			code = -1;
1359 			return;
1360 		}
1361 		makeargv();
1362 		argc = margc;
1363 		argv = margv;
1364 	}
1365 	if (argc > 4) {
1366 		(void) printf("usage: %s username [password] [account]\n",
1367 			argv[0]);
1368 		code = -1;
1369 		return;
1370 	}
1371 	if (argv[1] == 0) {
1372 		(void) printf("access for user (nil) denied\n");
1373 		code = -1;
1374 		return;
1375 	}
1376 	n = command("USER %s", argv[1]);
1377 	if (n == CONTINUE) {
1378 		int oldclevel;
1379 		if (argc < 3)
1380 			argv[2] = mygetpass("Password: "), argc++;
1381 		if ((oldclevel = clevel) == PROT_S)
1382 			clevel = PROT_P;
1383 		n = command("PASS %s", argv[2]);
1384 		/* level may have changed */
1385 		if (clevel == PROT_P)
1386 			clevel = oldclevel;
1387 	}
1388 	if (n == CONTINUE) {
1389 		if (argc < 4) {
1390 			(void) printf("Account: "); (void) fflush(stdout);
1391 			stop_timer();
1392 			(void) fgets(acct, sizeof (acct) - 1, stdin);
1393 			reset_timer();
1394 			acct[strlen(acct) - 1] = '\0';
1395 			argv[3] = acct; argc++;
1396 		}
1397 		n = command("ACCT %s", argv[3]);
1398 		aflag++;
1399 	}
1400 	if (n != COMPLETE) {
1401 		(void) fprintf(stdout, "Login failed.\n");
1402 		return;
1403 	}
1404 	if (!aflag && argc == 4) {
1405 		(void) command("ACCT %s", argv[3]);
1406 	}
1407 }
1408 
1409 /*
1410  * Print working directory.
1411  */
1412 /*ARGSUSED*/
1413 void
1414 pwd(int argc, char *argv[])
1415 {
1416 	(void) command("PWD");
1417 }
1418 
1419 /*
1420  * Make a directory.
1421  */
1422 void
1423 makedir(int argc, char *argv[])
1424 {
1425 	if (argc < 2) {
1426 		if (prompt_for_arg(line, sizeof (line), "directory-name") <
1427 		    0) {
1428 			code = -1;
1429 			return;
1430 		}
1431 		makeargv();
1432 		argc = margc;
1433 		argv = margv;
1434 	}
1435 	if (argc < 2) {
1436 		(void) printf("usage: %s directory-name\n", argv[0]);
1437 		code = -1;
1438 		return;
1439 	}
1440 	(void) command("MKD %s", argv[1]);
1441 }
1442 
1443 /*
1444  * Remove a directory.
1445  */
1446 void
1447 removedir(int argc, char *argv[])
1448 {
1449 	if (argc < 2) {
1450 		if (prompt_for_arg(line, sizeof (line), "directory-name") <
1451 		    0) {
1452 			code = -1;
1453 			return;
1454 		}
1455 		makeargv();
1456 		argc = margc;
1457 		argv = margv;
1458 	}
1459 	if (argc < 2) {
1460 		(void) printf("usage: %s directory-name\n", argv[0]);
1461 		code = -1;
1462 		return;
1463 	}
1464 	(void) command("RMD %s", argv[1]);
1465 }
1466 
1467 /*
1468  * Send a line, verbatim, to the remote machine.
1469  */
1470 void
1471 quote(int argc, char *argv[])
1472 {
1473 	int i, n, len;
1474 	char buf[FTPBUFSIZ];
1475 
1476 	if (argc < 2) {
1477 		if (prompt_for_arg(line, sizeof (line),
1478 		    "command line to send") == -1) {
1479 			code = -1;
1480 			return;
1481 		}
1482 		makeargv();
1483 		argc = margc;
1484 		argv = margv;
1485 	}
1486 	if (argc < 2) {
1487 		(void) printf("usage: %s line-to-send\n", argv[0]);
1488 		code = -1;
1489 		return;
1490 	}
1491 	len = snprintf(buf, sizeof (buf), "%s", argv[1]);
1492 	if (len >= 0 && len < sizeof (buf) - 1) {
1493 		for (i = 2; i < argc; i++) {
1494 			n = snprintf(&buf[len], sizeof (buf) - len, " %s",
1495 					argv[i]);
1496 			if (n < 0 || n >= sizeof (buf) - len)
1497 				break;
1498 			len += n;
1499 		}
1500 	}
1501 	if (command("%s", buf) == PRELIM) {
1502 		while (getreply(0) == PRELIM)
1503 			;
1504 	}
1505 }
1506 
1507 /*
1508  * Send a line, verbatim, to the remote machine as a SITE command.
1509  */
1510 void
1511 site(int argc, char *argv[])
1512 {
1513 	int i, n, len;
1514 	char buf[FTPBUFSIZ];
1515 
1516 	if (argc < 2) {
1517 		if (prompt_for_arg(line, sizeof (line),
1518 		    "arguments to SITE command") == -1) {
1519 			code = -1;
1520 			return;
1521 		}
1522 		makeargv();
1523 		argc = margc;
1524 		argv = margv;
1525 	}
1526 	if (argc < 2) {
1527 		(void) printf("usage: %s arg1 [arg2] ...\n", argv[0]);
1528 		code = -1;
1529 		return;
1530 	}
1531 	len = snprintf(buf, sizeof (buf), "%s", argv[1]);
1532 	if (len >= 0 && len < sizeof (buf) - 1) {
1533 		for (i = 2; i < argc; i++) {
1534 			n = snprintf(&buf[len], sizeof (buf) - len, " %s",
1535 					argv[i]);
1536 			if (n < 0 || n >= sizeof (buf) - len)
1537 				break;
1538 			len += n;
1539 		}
1540 	}
1541 	if (command("SITE %s", buf) == PRELIM) {
1542 		while (getreply(0) == PRELIM)
1543 			;
1544 	}
1545 }
1546 
1547 /*
1548  * Ask the other side for help.
1549  */
1550 void
1551 rmthelp(int argc, char *argv[])
1552 {
1553 	int oldverbose = verbose;
1554 
1555 	verbose = 1;
1556 	(void) command(argc == 1 ? "HELP" : "HELP %s", argv[1]);
1557 	verbose = oldverbose;
1558 }
1559 
1560 /*
1561  * Terminate session and exit.
1562  */
1563 /*ARGSUSED*/
1564 void
1565 quit(int argc, char *argv[])
1566 {
1567 	if (connected)
1568 		disconnect(0, NULL);
1569 	pswitch(1);
1570 	if (connected) {
1571 		disconnect(0, NULL);
1572 	}
1573 	exit(0);
1574 }
1575 
1576 /*
1577  * Terminate session, but don't exit.
1578  */
1579 /*ARGSUSED*/
1580 void
1581 disconnect(int argc, char *argv[])
1582 {
1583 	extern FILE *ctrl_in, *ctrl_out;
1584 	extern int data;
1585 
1586 	if (!connected)
1587 		return;
1588 	(void) command("QUIT");
1589 	if (ctrl_in) {
1590 		reset_timer();
1591 		(void) fclose(ctrl_in);
1592 	}
1593 	if (ctrl_out) {
1594 		reset_timer();
1595 		(void) fclose(ctrl_out);
1596 	}
1597 	ctrl_out = ctrl_in = NULL;
1598 	connected = 0;
1599 	data = -1;
1600 	if (!proxy) {
1601 		macnum = 0;
1602 	}
1603 
1604 	auth_type = AUTHTYPE_NONE;
1605 	clevel = dlevel = PROT_C;
1606 	goteof = 0;
1607 }
1608 
1609 static int
1610 confirm(char *cmd, char *file)
1611 {
1612 	char line[FTPBUFSIZ];
1613 
1614 	if (!interactive)
1615 		return (1);
1616 	stop_timer();
1617 	(void) printf("%s %s? ", cmd, file);
1618 	(void) fflush(stdout);
1619 	*line = '\0';
1620 	(void) fgets(line, sizeof (line), stdin);
1621 	reset_timer();
1622 	return (*line != 'n' && *line != 'N');
1623 }
1624 
1625 void
1626 fatal(char *msg)
1627 {
1628 	(void) fprintf(stderr, "ftp: %s\n", msg);
1629 	exit(1);
1630 }
1631 
1632 /*
1633  * Glob a local file name specification with
1634  * the expectation of a single return value.
1635  * Can't control multiple values being expanded
1636  * from the expression, we return only the first.
1637  */
1638 static int
1639 globulize(char **cpp)
1640 {
1641 	char **globbed;
1642 
1643 	if (!doglob)
1644 		return (1);
1645 	globbed = glob(*cpp);
1646 	if (globbed != NULL && *globbed == NULL && globerr == NULL)
1647 		globerr = "No match";
1648 	if (globerr != NULL) {
1649 		(void) printf("%s: %s\n", *cpp, globerr);
1650 		if (globbed)
1651 			blkfree(globbed);
1652 		return (0);
1653 	}
1654 	if (globbed) {
1655 		*cpp = strdup(*globbed);
1656 		blkfree(globbed);
1657 		if (!*cpp)
1658 			return (0);
1659 	}
1660 	return (1);
1661 }
1662 
1663 void
1664 account(int argc, char *argv[])
1665 {
1666 	char acct[50], *ap;
1667 
1668 	if (argc > 1) {
1669 		++argv;
1670 		--argc;
1671 		(void) strncpy(acct, *argv, 49);
1672 		acct[49] = '\0';
1673 		while (argc > 1) {
1674 			--argc;
1675 			++argv;
1676 			(void) strncat(acct, *argv, 49 - strlen(acct));
1677 		}
1678 		ap = acct;
1679 	} else {
1680 		ap = mygetpass("Account:");
1681 	}
1682 	(void) command("ACCT %s", ap);
1683 }
1684 
1685 /*ARGSUSED*/
1686 static void
1687 proxabort(int sig)
1688 {
1689 	extern int proxy;
1690 
1691 	if (!proxy) {
1692 		pswitch(1);
1693 	}
1694 	if (connected) {
1695 		proxflag = 1;
1696 	} else {
1697 		proxflag = 0;
1698 	}
1699 	pswitch(0);
1700 	longjmp(abortprox, 1);
1701 }
1702 
1703 void
1704 doproxy(int argc, char *argv[])
1705 {
1706 	void (*oldintr)();
1707 	struct cmd *c;
1708 
1709 	if (argc < 2) {
1710 		if (prompt_for_arg(line, sizeof (line), "command") == -1) {
1711 			code = -1;
1712 			return;
1713 		}
1714 		makeargv();
1715 		argc = margc;
1716 		argv = margv;
1717 	}
1718 	if (argc < 2) {
1719 		(void) printf("usage: %s command\n", argv[0]);
1720 		code = -1;
1721 		return;
1722 	}
1723 	c = getcmd(argv[1]);
1724 	if (c == (struct cmd *)-1) {
1725 		(void) printf("?Ambiguous command\n");
1726 		(void) fflush(stdout);
1727 		code = -1;
1728 		return;
1729 	}
1730 	if (c == 0) {
1731 		(void) printf("?Invalid command\n");
1732 		(void) fflush(stdout);
1733 		code = -1;
1734 		return;
1735 	}
1736 	if (!c->c_proxy) {
1737 		(void) printf("?Invalid proxy command\n");
1738 		(void) fflush(stdout);
1739 		code = -1;
1740 		return;
1741 	}
1742 	if (setjmp(abortprox)) {
1743 		code = -1;
1744 		return;
1745 	}
1746 	oldintr = signal(SIGINT, (void (*)())proxabort);
1747 	pswitch(1);
1748 	if (c->c_conn && !connected) {
1749 		(void) printf("Not connected\n");
1750 		(void) fflush(stdout);
1751 		pswitch(0);
1752 		(void) signal(SIGINT, oldintr);
1753 		code = -1;
1754 		return;
1755 	}
1756 	(*c->c_handler)(argc-1, argv+1);
1757 	if (connected) {
1758 		proxflag = 1;
1759 	} else {
1760 		proxflag = 0;
1761 	}
1762 	pswitch(0);
1763 	(void) signal(SIGINT, oldintr);
1764 }
1765 
1766 /*ARGSUSED*/
1767 void
1768 setcase(int argc, char *argv[])
1769 {
1770 	mcase = !mcase;
1771 	(void) printf("Case mapping %s.\n", onoff(mcase));
1772 	code = mcase;
1773 }
1774 
1775 /*ARGSUSED*/
1776 void
1777 setcr(int argc, char *argv[])
1778 {
1779 	crflag = !crflag;
1780 	(void) printf("Carriage Return stripping %s.\n", onoff(crflag));
1781 	code = crflag;
1782 }
1783 
1784 void
1785 setntrans(int argc, char *argv[])
1786 {
1787 	if (argc == 1) {
1788 		ntflag = 0;
1789 		(void) printf("Ntrans off.\n");
1790 		code = ntflag;
1791 		return;
1792 	}
1793 	ntflag++;
1794 	code = ntflag;
1795 	(void) strncpy(ntin, argv[1], 16);
1796 	ntin[16] = '\0';
1797 	if (argc == 2) {
1798 		ntout[0] = '\0';
1799 		return;
1800 	}
1801 	(void) strncpy(ntout, argv[2], 16);
1802 	ntout[16] = '\0';
1803 }
1804 
1805 static char *
1806 dotrans(char *name)
1807 {
1808 	static char new[MAXPATHLEN];
1809 	char *cp1, *cp2 = new;
1810 	int i, ostop, found;
1811 
1812 	for (ostop = 0; *(ntout + ostop) && ostop < 16; ostop++)
1813 		;
1814 	for (cp1 = name; *cp1; cp1++) {
1815 		found = 0;
1816 		for (i = 0; *(ntin + i) && i < 16; i++) {
1817 			if (*cp1 == *(ntin + i)) {
1818 				found++;
1819 				if (i < ostop) {
1820 					*cp2++ = *(ntout + i);
1821 				}
1822 				break;
1823 			}
1824 		}
1825 		if (!found) {
1826 			*cp2++ = *cp1;
1827 		}
1828 	}
1829 	*cp2 = '\0';
1830 	return (new);
1831 }
1832 
1833 void
1834 setnmap(int argc, char *argv[])
1835 {
1836 	char *cp;
1837 
1838 	if (argc == 1) {
1839 		mapflag = 0;
1840 		(void) printf("Nmap off.\n");
1841 		code = mapflag;
1842 		return;
1843 	}
1844 	if (argc < 3) {
1845 		if (prompt_for_arg(line, sizeof (line), "mapout") == -1) {
1846 			code = -1;
1847 			return;
1848 		}
1849 		makeargv();
1850 		argc = margc;
1851 		argv = margv;
1852 	}
1853 	if (argc < 3) {
1854 		(void) printf("Usage: %s [mapin mapout]\n", argv[0]);
1855 		code = -1;
1856 		return;
1857 	}
1858 	mapflag = 1;
1859 	code = 1;
1860 	cp = index(altarg, ' ');
1861 	if (proxy) {
1862 		while (*++cp == ' ')
1863 			/* NULL */;
1864 		altarg = cp;
1865 		cp = index(altarg, ' ');
1866 	}
1867 	*cp = '\0';
1868 	(void) strncpy(mapin, altarg, MAXPATHLEN - 1);
1869 	while (*++cp == ' ')
1870 		/* NULL */;
1871 	(void) strncpy(mapout, cp, MAXPATHLEN - 1);
1872 }
1873 
1874 static char *
1875 domap(char *name)
1876 {
1877 	static char new[MAXPATHLEN];
1878 	char *cp1 = name, *cp2 = mapin;
1879 	char *tp[9], *te[9];
1880 	int i, toks[9], toknum, match = 1;
1881 	wchar_t	wc1, wc2;
1882 	int	len1, len2;
1883 
1884 	for (i = 0; i < 9; ++i) {
1885 		toks[i] = 0;
1886 	}
1887 	while (match && *cp1 && *cp2) {
1888 		if ((len1 = mbtowc(&wc1, cp1, MB_CUR_MAX)) <= 0) {
1889 			wc1 = (unsigned char)*cp1;
1890 			len1 = 1;
1891 		}
1892 		cp1 += len1;
1893 		if ((len2 = mbtowc(&wc2, cp2, MB_CUR_MAX)) <= 0) {
1894 			wc2 = (unsigned char)*cp2;
1895 			len2 = 1;
1896 		}
1897 		cp2 += len2;
1898 
1899 		switch (wc2) {
1900 		case '\\':
1901 			if ((len2 = mbtowc(&wc2, cp2, MB_CUR_MAX)) <= 0) {
1902 				wc2 = (unsigned char)*cp2;
1903 				len2 = 1;
1904 			}
1905 			cp2 += len2;
1906 			if (wc2 != wc1)
1907 				match = 0;
1908 			break;
1909 
1910 		case '$':
1911 			if (*cp2 >= '1' && *cp2 <= '9') {
1912 				if ((len2 =
1913 				    mbtowc(&wc2, cp2 + 1, MB_CUR_MAX)) <= 0) {
1914 					wc2 = (unsigned char)*(cp2 + 1);
1915 					len2 = 1;
1916 				}
1917 				if (wc1 != wc2) {
1918 					toks[toknum = *cp2 - '1']++;
1919 					tp[toknum] = cp1 - len1;
1920 					while (*cp1) {
1921 						if ((len1 = mbtowc(&wc1,
1922 						    cp1, MB_CUR_MAX)) <= 0) {
1923 							wc1 =
1924 							    (unsigned char)*cp1;
1925 							len1 = 1;
1926 						}
1927 						cp1 += len1;
1928 						if (wc2 == wc1)
1929 							break;
1930 					}
1931 					if (*cp1 == 0 && wc2 != wc1)
1932 						te[toknum] = cp1;
1933 					else
1934 						te[toknum] = cp1 - len1;
1935 				}
1936 				cp2++;			/* Consume the digit */
1937 				if (wc2)
1938 					cp2 += len2;	/* Consume wide char */
1939 				break;
1940 			}
1941 			/* FALLTHROUGH */
1942 		default:
1943 			if (wc2 != wc1)
1944 				match = 0;
1945 			break;
1946 		}
1947 	}
1948 
1949 	cp1 = new;
1950 	*cp1 = '\0';
1951 	cp2 = mapout;
1952 	while (*cp2) {
1953 		match = 0;
1954 		switch (*cp2) {
1955 		case '\\':
1956 			cp2++;
1957 			if (*cp2) {
1958 				if ((len2 = mblen(cp2, MB_CUR_MAX)) <= 0)
1959 					len2 = 1;
1960 				memcpy(cp1, cp2, len2);
1961 				cp1 += len2;
1962 				cp2 += len2;
1963 			}
1964 			break;
1965 
1966 		case '[':
1967 LOOP:
1968 			cp2++;
1969 			if (*cp2 == '$' && isdigit(*(cp2+1))) {
1970 				if (*++cp2 == '0') {
1971 					char *cp3 = name;
1972 
1973 					while (*cp3) {
1974 						*cp1++ = *cp3++;
1975 					}
1976 					match = 1;
1977 				} else if (toks[toknum = *cp2 - '1']) {
1978 					char *cp3 = tp[toknum];
1979 
1980 					while (cp3 != te[toknum]) {
1981 						*cp1++ = *cp3++;
1982 					}
1983 					match = 1;
1984 				}
1985 			} else {
1986 				while (*cp2 && *cp2 != ',' && *cp2 != ']') {
1987 					if (*cp2 == '\\') {
1988 						cp2++;
1989 						continue;
1990 					}
1991 
1992 					if (*cp2 == '$' && isdigit(*(cp2+1))) {
1993 						if (*++cp2 == '0') {
1994 							char *cp3 = name;
1995 
1996 							while (*cp3)
1997 								*cp1++ = *cp3++;
1998 							continue;
1999 						}
2000 						if (toks[toknum = *cp2 - '1']) {
2001 							char *cp3 = tp[toknum];
2002 
2003 							while (cp3 !=
2004 							    te[toknum])
2005 								*cp1++ = *cp3++;
2006 						}
2007 						continue;
2008 					}
2009 					if (*cp2) {
2010 						if ((len2 =
2011 						    mblen(cp2, MB_CUR_MAX)) <=
2012 						    0) {
2013 							len2 = 1;
2014 						}
2015 						memcpy(cp1, cp2, len2);
2016 						cp1 += len2;
2017 						cp2 += len2;
2018 					}
2019 				}
2020 				if (!*cp2) {
2021 					(void) printf(
2022 						"nmap: unbalanced brackets\n");
2023 					return (name);
2024 				}
2025 				match = 1;
2026 			}
2027 			if (match) {
2028 				while (*cp2 && *cp2 != ']') {
2029 					if (*cp2 == '\\' && *(cp2 + 1)) {
2030 						cp2++;
2031 					}
2032 					if ((len2 = mblen(cp2, MB_CUR_MAX)) <=
2033 					    0)
2034 						len2 = 1;
2035 					cp2 += len2;
2036 				}
2037 				if (!*cp2) {
2038 					(void) printf(
2039 						"nmap: unbalanced brackets\n");
2040 					return (name);
2041 				}
2042 				cp2++;
2043 				break;
2044 			}
2045 			switch (*++cp2) {
2046 				case ',':
2047 					goto LOOP;
2048 				case ']':
2049 					break;
2050 				default:
2051 					cp2--;
2052 					goto LOOP;
2053 			}
2054 			cp2++;
2055 			break;
2056 		case '$':
2057 			if (isdigit(*(cp2 + 1))) {
2058 				if (*++cp2 == '0') {
2059 					char *cp3 = name;
2060 
2061 					while (*cp3) {
2062 						*cp1++ = *cp3++;
2063 					}
2064 				} else if (toks[toknum = *cp2 - '1']) {
2065 					char *cp3 = tp[toknum];
2066 
2067 					while (cp3 != te[toknum]) {
2068 						*cp1++ = *cp3++;
2069 					}
2070 				}
2071 				cp2++;
2072 				break;
2073 			}
2074 			/* FALLTHROUGH */
2075 		default:
2076 			if ((len2 = mblen(cp2, MB_CUR_MAX)) <= 0)
2077 				len2 = 1;
2078 			memcpy(cp1, cp2, len2);
2079 			cp1 += len2;
2080 			cp2 += len2;
2081 			break;
2082 		}
2083 	}
2084 	*cp1 = '\0';
2085 	if (!*new) {
2086 		return (name);
2087 	}
2088 	return (new);
2089 }
2090 
2091 /*ARGSUSED*/
2092 void
2093 setsunique(int argc, char *argv[])
2094 {
2095 	sunique = !sunique;
2096 	(void) printf("Store unique %s.\n", onoff(sunique));
2097 	code = sunique;
2098 }
2099 
2100 /*ARGSUSED*/
2101 void
2102 setrunique(int argc, char *argv[])
2103 {
2104 	runique = !runique;
2105 	(void) printf("Receive unique %s.\n", onoff(runique));
2106 	code = runique;
2107 }
2108 
2109 /*ARGSUSED*/
2110 void
2111 setpassive(int argc, char *argv[])
2112 {
2113 	passivemode = !passivemode;
2114 	(void) printf("Passive mode %s.\n", onoff(passivemode));
2115 	code = passivemode;
2116 }
2117 
2118 void
2119 settcpwindow(int argc, char *argv[])
2120 {
2121 	int owindowsize = tcpwindowsize;
2122 
2123 	if (argc > 2) {
2124 		(void) printf("usage: %s [size]\n", argv[0]);
2125 		code = -1;
2126 		return;
2127 	}
2128 	if (argc == 2) {
2129 		int window;
2130 		char *endp;
2131 
2132 		errno = 0;
2133 		window = (int)strtol(argv[1], &endp, 10);
2134 		if (errno || window < 0 || *endp != '\0')
2135 			(void) printf("%s: Invalid size `%s'\n",
2136 				argv[0], argv[1]);
2137 		else
2138 			tcpwindowsize = window;
2139 	}
2140 	if (tcpwindowsize == 0) {
2141 		if (owindowsize == 0)
2142 			(void) printf("No TCP window size defined\n");
2143 		else
2144 			(void) printf("TCP window size cleared\n");
2145 	} else
2146 		(void) printf("TCP window size is set to %d\n", tcpwindowsize);
2147 }
2148 
2149 /* change directory to parent directory */
2150 /*ARGSUSED*/
2151 void
2152 cdup(int argc, char *argv[])
2153 {
2154 	(void) command("CDUP");
2155 }
2156 
2157 void
2158 macdef(int argc, char *argv[])
2159 {
2160 	char *tmp;
2161 	int c;
2162 
2163 	if (macnum == 16) {
2164 		(void) printf("Limit of 16 macros have already been defined\n");
2165 		code = -1;
2166 		return;
2167 	}
2168 	if (argc < 2) {
2169 		if (prompt_for_arg(line, sizeof (line), "macro name") == -1) {
2170 			code = -1;
2171 			return;
2172 		}
2173 		makeargv();
2174 		argc = margc;
2175 		argv = margv;
2176 	}
2177 	if (argc != 2) {
2178 		(void) printf("Usage: %s macro_name\n", argv[0]);
2179 		code = -1;
2180 		return;
2181 	}
2182 	if (interactive) {
2183 		(void) printf("Enter macro line by line, terminating "
2184 		    "it with a null line\n");
2185 	}
2186 	(void) strncpy(macros[macnum].mac_name, argv[1], 8);
2187 	if (macnum == 0) {
2188 		macros[macnum].mac_start = macbuf;
2189 	} else {
2190 		macros[macnum].mac_start = macros[macnum - 1].mac_end + 1;
2191 	}
2192 	tmp = macros[macnum].mac_start;
2193 	while (tmp != macbuf+4096) {
2194 		if ((c = getchar()) == EOF) {
2195 			(void) printf("macdef:end of file encountered\n");
2196 			code = -1;
2197 			return;
2198 		}
2199 		if ((*tmp = c) == '\n') {
2200 			if (tmp == macros[macnum].mac_start) {
2201 				macros[macnum++].mac_end = tmp;
2202 				code = 0;
2203 				return;
2204 			}
2205 			if (*(tmp-1) == '\0') {
2206 				macros[macnum++].mac_end = tmp - 1;
2207 				code = 0;
2208 				return;
2209 			}
2210 			*tmp = '\0';
2211 		}
2212 		tmp++;
2213 	}
2214 	for (;;) {
2215 		while ((c = getchar()) != '\n' && c != EOF)
2216 			/* NULL */;
2217 		if (c == EOF || getchar() == '\n') {
2218 			(void) printf(
2219 				"Macro not defined - 4k buffer exceeded\n");
2220 			code = -1;
2221 			return;
2222 		}
2223 	}
2224 }
2225 
2226 /*
2227  * The p_name strings are for the getlevel and setlevel commands.
2228  * The name strings for printing are in the arpa/ftp.h file in the
2229  * protnames[] array of strings.
2230  */
2231 static	struct	levels {
2232 	char	*p_name;
2233 	char	*p_mode;
2234 	int	p_level;
2235 } levels[] = {
2236 	{ "clear",	"C",	PROT_C },
2237 	{ "safe",	"S",	PROT_S },
2238 	{ "private",	"P",	PROT_P },
2239 	NULL
2240 };
2241 
2242 /*
2243  * Return a pointer to a string which is the readable version of the
2244  * protection level, or NULL if the input level is not found.
2245  */
2246 static char *
2247 getlevel(int level)
2248 {
2249 	struct levels *p;
2250 
2251 	for (p = levels; (p != NULL) && (p->p_level != level); p++)
2252 		;
2253 	return (p ? p->p_name : NULL);
2254 }
2255 
2256 static char *plevel[] = {
2257 	"protect",
2258 	"",
2259 	NULL
2260 };
2261 
2262 /*
2263  * Set control channel protection level.
2264  */
2265 void
2266 setclevel(int argc, char *argv[])
2267 {
2268 	struct levels *p;
2269 	char *levelp;
2270 	int comret;
2271 
2272 	if (argc > 2) {
2273 		char *sep;
2274 
2275 		(void) printf("usage: %s [", argv[0]);
2276 		sep = " ";
2277 		for (p = levels; p->p_name; p++) {
2278 			(void) printf("%s%s", sep, p->p_name);
2279 			if (*sep == ' ')
2280 				sep = " | ";
2281 		}
2282 		(void) printf(" ]\n");
2283 		code = -1;
2284 		return;
2285 	}
2286 	if (argc < 2) {
2287 		levelp = getlevel(clevel);
2288 		(void) printf("Using %s protection level for commands.\n",
2289 			levelp ? levelp : "<unknown>");
2290 		code = 0;
2291 		return;
2292 	}
2293 	for (p = levels; (p != NULL) && (p->p_name); p++)
2294 		if (strcmp(argv[1], p->p_name) == 0)
2295 			break;
2296 	if (p->p_name == 0) {
2297 		(void) printf("%s: unknown protection level\n", argv[1]);
2298 		code = -1;
2299 		return;
2300 	}
2301 	if (auth_type == AUTHTYPE_NONE) {
2302 		if (strcmp(p->p_name, "clear"))
2303 			(void) printf("Cannot set protection level to %s\n",
2304 				argv[1]);
2305 		return;
2306 	}
2307 	if (strcmp(p->p_name, "clear") == 0) {
2308 		comret = command("CCC");
2309 		if (comret == COMPLETE)
2310 			clevel = PROT_C;
2311 		return;
2312 	}
2313 	clevel = p->p_level;
2314 	(void) printf("Control channel protection level set to %s.\n",
2315 		p->p_name);
2316 }
2317 
2318 /*
2319  * Set data channel protection level.
2320  */
2321 void
2322 setdlevel(int argc, char *argv[])
2323 {
2324 	struct levels *p;
2325 	int comret;
2326 
2327 	if (argc != 2) {
2328 		char *sep;
2329 
2330 		(void) printf("usage: %s [", argv[0]);
2331 		sep = " ";
2332 		for (p = levels; p->p_name; p++) {
2333 			(void) printf("%s%s", sep, p->p_name);
2334 			if (*sep == ' ')
2335 				sep = " | ";
2336 		}
2337 		(void) printf(" ]\n");
2338 		code = -1;
2339 		return;
2340 	}
2341 	for (p = levels; p->p_name; p++)
2342 		if (strcmp(argv[1], p->p_name) == 0)
2343 			break;
2344 	if (p->p_name == 0) {
2345 		(void) printf("%s: unknown protection level\n", argv[1]);
2346 		code = -1;
2347 		return;
2348 	}
2349 	if (auth_type == AUTHTYPE_NONE) {
2350 		if (strcmp(p->p_name, "clear"))
2351 			(void) printf("Cannot set protection level to %s\n",
2352 				argv[1]);
2353 		return;
2354 	}
2355 	/* Start with a PBSZ of 1 meg */
2356 	if (p->p_level != PROT_C)
2357 		setpbsz(1<<20);
2358 	comret = command("PROT %s", p->p_mode);
2359 	if (comret == COMPLETE)
2360 		dlevel = p->p_level;
2361 }
2362 
2363 /*
2364  * Set clear command protection level.
2365  */
2366 /* VARARGS */
2367 void
2368 ccc(int argc, char *argv[])
2369 {
2370 	plevel[1] = "clear";
2371 	setclevel(2, plevel);
2372 }
2373 
2374 /*
2375  * Set clear data protection level.
2376  */
2377 /* VARARGS */
2378 void
2379 setclear(int argc, char *argv[])
2380 {
2381 	plevel[1] = "clear";
2382 	setdlevel(2, plevel);
2383 }
2384 
2385 /*
2386  * Set safe data protection level.
2387  */
2388 /* VARARGS */
2389 void
2390 setsafe(int argc, char *argv[])
2391 {
2392 	plevel[1] = "safe";
2393 	setdlevel(2, plevel);
2394 }
2395 
2396 /*
2397  * Set private data protection level.
2398  */
2399 /* VARARGS */
2400 void
2401 setprivate(int argc, char *argv[])
2402 {
2403 	plevel[1] = "private";
2404 	setdlevel(2, plevel);
2405 }
2406 
2407 /*
2408  * Set mechanism type
2409  */
2410 void
2411 setmech(int  argc, char *argv[])
2412 {
2413 	char	tempmech[MECH_SZ];
2414 
2415 	if (argc < 2) {
2416 		if (prompt_for_arg(line, sizeof (line), "mech-type") == -1) {
2417 			code = -1;
2418 			return;
2419 		}
2420 		makeargv();
2421 		argc = margc;
2422 		argv = margv;
2423 	}
2424 
2425 	if (argc != 2) {
2426 		(void) printf("usage: %s [ mechanism type ]\n", argv[0]);
2427 		code = -1;
2428 		return;
2429 	}
2430 
2431 	if ((strlcpy(tempmech, argv[1], MECH_SZ) >= MECH_SZ) ||
2432 		__gss_mech_to_oid(tempmech, (gss_OID*)&mechoid) !=
2433 			GSS_S_COMPLETE) {
2434 		(void) printf("%s: %s: not a valid security mechanism\n",
2435 			argv[0], tempmech);
2436 		code = -1;
2437 		return;
2438 	} else {
2439 		(void) strlcpy(mechstr, tempmech, MECH_SZ);
2440 		(void) printf("Using %s mechanism type\n", mechstr);
2441 		code = 0;
2442 		return;
2443 	}
2444 }
2445