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