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