xref: /openbsd/usr.bin/ftp/small.c (revision 898184e3)
1 /*	$OpenBSD: small.c,v 1.1 2009/05/05 19:35:30 martynas Exp $	*/
2 /*	$NetBSD: cmds.c,v 1.27 1997/08/18 10:20:15 lukem Exp $	*/
3 
4 /*
5  * Copyright (C) 1997 and 1998 WIDE Project.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the project nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 /*
34  * Copyright (c) 1985, 1989, 1993, 1994
35  *	The Regents of the University of California.  All rights reserved.
36  *
37  * Redistribution and use in source and binary forms, with or without
38  * modification, are permitted provided that the following conditions
39  * are met:
40  * 1. Redistributions of source code must retain the above copyright
41  *    notice, this list of conditions and the following disclaimer.
42  * 2. Redistributions in binary form must reproduce the above copyright
43  *    notice, this list of conditions and the following disclaimer in the
44  *    documentation and/or other materials provided with the distribution.
45  * 3. Neither the name of the University nor the names of its contributors
46  *    may be used to endorse or promote products derived from this software
47  *    without specific prior written permission.
48  *
49  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
50  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
51  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
52  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
53  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
54  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
55  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
56  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
57  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
58  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
59  * SUCH DAMAGE.
60  */
61 
62 /*
63  * FTP User Program -- Command Routines.
64  */
65 #include <sys/types.h>
66 #include <sys/socket.h>
67 #include <sys/stat.h>
68 #include <sys/wait.h>
69 #include <arpa/ftp.h>
70 
71 #include <ctype.h>
72 #include <err.h>
73 #include <fnmatch.h>
74 #include <glob.h>
75 #include <netdb.h>
76 #include <stdio.h>
77 #include <stdlib.h>
78 #include <string.h>
79 #include <unistd.h>
80 
81 #include "ftp_var.h"
82 #include "pathnames.h"
83 #include "small.h"
84 
85 jmp_buf	jabort;
86 char   *mname;
87 char   *home = "/";
88 
89 struct	types {
90 	char	*t_name;
91 	char	*t_mode;
92 	int	t_type;
93 	char	*t_arg;
94 } types[] = {
95 	{ "ascii",	"A",	TYPE_A,	0 },
96 	{ "binary",	"I",	TYPE_I,	0 },
97 	{ "image",	"I",	TYPE_I,	0 },
98 	{ "ebcdic",	"E",	TYPE_E,	0 },
99 	{ "tenex",	"L",	TYPE_L,	bytename },
100 	{ NULL }
101 };
102 
103 /*
104  * Set transfer type.
105  */
106 void
107 settype(int argc, char *argv[])
108 {
109 	struct types *p;
110 	int comret;
111 
112 	if (argc > 2) {
113 		char *sep;
114 
115 		fprintf(ttyout, "usage: %s [", argv[0]);
116 		sep = "";
117 		for (p = types; p->t_name; p++) {
118 			fprintf(ttyout, "%s%s", sep, p->t_name);
119 			sep = " | ";
120 		}
121 		fputs("]\n", ttyout);
122 		code = -1;
123 		return;
124 	}
125 	if (argc < 2) {
126 		fprintf(ttyout, "Using %s mode to transfer files.\n", typename);
127 		code = 0;
128 		return;
129 	}
130 	for (p = types; p->t_name; p++)
131 		if (strcmp(argv[1], p->t_name) == 0)
132 			break;
133 	if (p->t_name == 0) {
134 		fprintf(ttyout, "%s: unknown mode.\n", argv[1]);
135 		code = -1;
136 		return;
137 	}
138 	if ((p->t_arg != NULL) && (*(p->t_arg) != '\0'))
139 		comret = command("TYPE %s %s", p->t_mode, p->t_arg);
140 	else
141 		comret = command("TYPE %s", p->t_mode);
142 	if (comret == COMPLETE) {
143 		(void)strlcpy(typename, p->t_name, sizeof typename);
144 		curtype = type = p->t_type;
145 	}
146 }
147 
148 /*
149  * Internal form of settype; changes current type in use with server
150  * without changing our notion of the type for data transfers.
151  * Used to change to and from ascii for listings.
152  */
153 void
154 changetype(int newtype, int show)
155 {
156 	struct types *p;
157 	int comret, oldverbose = verbose;
158 
159 	if (newtype == 0)
160 		newtype = TYPE_I;
161 	if (newtype == curtype)
162 		return;
163 	if (
164 #ifndef SMALL
165 	    !debug &&
166 #endif /* !SMALL */
167 	    show == 0)
168 		verbose = 0;
169 	for (p = types; p->t_name; p++)
170 		if (newtype == p->t_type)
171 			break;
172 	if (p->t_name == 0) {
173 		warnx("internal error: unknown type %d.", newtype);
174 		return;
175 	}
176 	if (newtype == TYPE_L && bytename[0] != '\0')
177 		comret = command("TYPE %s %s", p->t_mode, bytename);
178 	else
179 		comret = command("TYPE %s", p->t_mode);
180 	if (comret == COMPLETE)
181 		curtype = newtype;
182 	verbose = oldverbose;
183 }
184 
185 char *stype[] = {
186 	"type",
187 	"",
188 	0
189 };
190 
191 /*
192  * Set binary transfer type.
193  */
194 /*ARGSUSED*/
195 void
196 setbinary(int argc, char *argv[])
197 {
198 
199 	stype[1] = "binary";
200 	settype(2, stype);
201 }
202 
203 void
204 get(int argc, char *argv[])
205 {
206 
207 	(void)getit(argc, argv, 0, restart_point ? "a+w" : "w" );
208 }
209 
210 /*
211  * Receive one file.
212  */
213 int
214 getit(int argc, char *argv[], int restartit, const char *mode)
215 {
216 	int loc = 0;
217 	int rval = 0;
218 	char *oldargv1, *oldargv2, *globargv2;
219 
220 	if (argc == 2) {
221 		argc++;
222 		argv[2] = argv[1];
223 		loc++;
224 	}
225 #ifndef SMALL
226 	if (argc < 2 && !another(&argc, &argv, "remote-file"))
227 		goto usage;
228 	if ((argc < 3 && !another(&argc, &argv, "local-file")) || argc > 3) {
229 usage:
230 		fprintf(ttyout, "usage: %s remote-file [local-file]\n",
231 		    argv[0]);
232 		code = -1;
233 		return (0);
234 	}
235 #endif /* !SMALL */
236 	oldargv1 = argv[1];
237 	oldargv2 = argv[2];
238 	if (!globulize(&argv[2])) {
239 		code = -1;
240 		return (0);
241 	}
242 	globargv2 = argv[2];
243 	if (loc && mcase) {
244 		char *tp = argv[1], *tp2, tmpbuf[MAXPATHLEN];
245 
246 		while (*tp && !islower(*tp)) {
247 			tp++;
248 		}
249 		if (!*tp) {
250 			tp = argv[2];
251 			tp2 = tmpbuf;
252 			while ((*tp2 = *tp) != '\0') {
253 				if (isupper(*tp2)) {
254 					*tp2 = tolower(*tp2);
255 				}
256 				tp++;
257 				tp2++;
258 			}
259 			argv[2] = tmpbuf;
260 		}
261 	}
262 	if (loc && ntflag)
263 		argv[2] = dotrans(argv[2]);
264 	if (loc && mapflag)
265 		argv[2] = domap(argv[2]);
266 #ifndef SMALL
267 	if (restartit) {
268 		struct stat stbuf;
269 		int ret;
270 
271 		ret = stat(argv[2], &stbuf);
272 		if (restartit == 1) {
273 			restart_point = (ret < 0) ? 0 : stbuf.st_size;
274 		} else {
275 			if (ret == 0) {
276 				time_t mtime;
277 
278 				mtime = remotemodtime(argv[1], 0);
279 				if (mtime == -1)
280 					goto freegetit;
281 				if (stbuf.st_mtime >= mtime) {
282 					rval = 1;
283 					goto freegetit;
284 				}
285 			}
286 		}
287 	}
288 #endif /* !SMALL */
289 
290 	recvrequest("RETR", argv[2], argv[1], mode,
291 	    argv[1] != oldargv1 || argv[2] != oldargv2 || !interactive, loc);
292 	restart_point = 0;
293 freegetit:
294 	if (oldargv2 != globargv2)	/* free up after globulize() */
295 		free(globargv2);
296 	return (rval);
297 }
298 
299 /* XXX - Signal race. */
300 /* ARGSUSED */
301 void
302 mabort(int signo)
303 {
304 	alarmtimer(0);
305 	putc('\n', ttyout);
306 	(void)fflush(ttyout);
307 #ifndef SMALL
308 	if (mflag && fromatty)
309 		if (confirm(mname, NULL))
310 			longjmp(jabort, 1);
311 #endif /* !SMALL */
312 	mflag = 0;
313 	longjmp(jabort, 1);
314 }
315 
316 /*
317  * Get multiple files.
318  */
319 void
320 mget(int argc, char *argv[])
321 {
322 	extern int optind, optreset;
323 	sig_t oldintr;
324 	int ch, xargc = 2;
325 	char *cp, localcwd[MAXPATHLEN], *xargv[] = { argv[0], NULL, NULL };
326 	static int restartit = 0;
327 #ifndef SMALL
328 	extern char *optarg;
329 	const char *errstr;
330 	int i = 1;
331 	char type = 0, *dummyargv[] = { argv[0], ".", NULL };
332 	FILE *ftemp = NULL;
333 	static int depth = 0, max_depth = 0;
334 
335 	optind = optreset = 1;
336 
337 	if (depth)
338 		depth++;
339 
340 	while ((ch = getopt(argc, argv, "cd:nr")) != -1) {
341 		switch(ch) {
342 		case 'c':
343 			restartit = 1;
344 			break;
345 		case 'd':
346 			max_depth = strtonum(optarg, 0, INT_MAX, &errstr);
347 			if (errstr != NULL) {
348 				fprintf(ttyout, "bad depth value, %s: %s\n",
349 				    errstr, optarg);
350 				code = -1;
351 				return;
352 			}
353 			break;
354 		case 'n':
355 			restartit = -1;
356 			break;
357 		case 'r':
358 			depth = 1;
359 			break;
360 		default:
361 			goto usage;
362 		}
363 	}
364 
365 	if (argc - optind < 1 && !another(&argc, &argv, "remote-files")) {
366 usage:
367 		fprintf(ttyout, "usage: %s [-cnr] [-d depth] remote-files\n",
368 		    argv[0]);
369 		code = -1;
370 		return;
371 	}
372 
373 	argv[optind - 1] = argv[0];
374 	argc -= optind - 1;
375 	argv += optind - 1;
376 #endif /* !SMALL */
377 
378 	mname = argv[0];
379 	mflag = 1;
380 	if (getcwd(localcwd, sizeof(localcwd)) == NULL)
381 		err(1, "can't get cwd");
382 
383 	oldintr = signal(SIGINT, mabort);
384 	(void)setjmp(jabort);
385 	while ((cp =
386 #ifdef SMALL
387 	    remglob(argv, proxy, NULL)) != NULL
388 	    ) {
389 #else /* SMALL */
390 	    depth ? remglob2(dummyargv, proxy, NULL, &ftemp, &type) :
391 	    remglob(argv, proxy, NULL)) != NULL
392 	    || (mflag && depth && ++i < argc)
393 	    ) {
394 		if (cp == NULL)
395 			continue;
396 #endif /* SMALL */
397 		if (*cp == '\0') {
398 			mflag = 0;
399 			continue;
400 		}
401 		if (!mflag)
402 			continue;
403 #ifndef SMALL
404 		if (depth && fnmatch(argv[i], cp, FNM_PATHNAME) != 0)
405 			continue;
406 #endif /* !SMALL */
407 		if (!fileindir(cp, localcwd)) {
408 			fprintf(ttyout, "Skipping non-relative filename `%s'\n",
409 			    cp);
410 			continue;
411 		}
412 #ifndef SMALL
413 		if (type == 'd' && depth == max_depth)
414 			continue;
415 		if (!confirm(argv[0], cp))
416 			continue;
417 		if (type == 'd') {
418 			mkdir(cp, 0755);
419 			if (chdir(cp) != 0) {
420 				warn("local: %s", cp);
421 				continue;
422 			}
423 
424 			xargv[1] = cp;
425 			cd(xargc, xargv);
426 			if (dirchange != 1)
427 				goto out;
428 
429 			xargv[1] = "*";
430 			mget(xargc, xargv);
431 
432 			xargv[1] = "..";
433 			cd(xargc, xargv);
434 			if (dirchange != 1) {
435 				mflag = 0;
436 				goto out;
437 			}
438 
439 out:
440 			if (chdir("..") != 0) {
441 				warn("local: %s", cp);
442 				mflag = 0;
443 			}
444 			continue;
445 		}
446 		if (type == 's')
447 			/* Currently ignored. */
448 			continue;
449 #endif /* !SMALL */
450 		xargv[1] = cp;
451 		(void)getit(xargc, xargv, restartit,
452 		    (restartit == 1 || restart_point) ? "a+w" : "w");
453 #ifndef SMALL
454 		if (!mflag && fromatty) {
455 			if (confirm(argv[0], NULL))
456 				mflag = 1;
457 		}
458 #endif /* !SMALL */
459 	}
460 	(void)signal(SIGINT, oldintr);
461 #ifndef SMALL
462 	if (depth)
463 		depth--;
464 	if (depth == 0 || mflag == 0)
465 		depth = max_depth = mflag = restartit = 0;
466 #else /* !SMALL */
467 	mflag = 0;
468 #endif /* !SMALL */
469 }
470 
471 /*
472  * Set current working directory on remote machine.
473  */
474 void
475 cd(int argc, char *argv[])
476 {
477 	int r;
478 
479 #ifndef SMALL
480 	if ((argc < 2 && !another(&argc, &argv, "remote-directory")) ||
481 	    argc > 2) {
482 		fprintf(ttyout, "usage: %s remote-directory\n", argv[0]);
483 		code = -1;
484 		return;
485 	}
486 #endif /* !SMALL */
487 	r = command("CWD %s", argv[1]);
488 	if (r == ERROR && code == 500) {
489 		if (verbose)
490 			fputs("CWD command not recognized, trying XCWD.\n", ttyout);
491 		r = command("XCWD %s", argv[1]);
492 	}
493 	if (r == ERROR && code == 550) {
494 		dirchange = 0;
495 		return;
496 	}
497 	if (r == COMPLETE)
498 		dirchange = 1;
499 }
500 
501 /*
502  * Terminate session, but don't exit.
503  */
504 /* ARGSUSED */
505 void
506 disconnect(int argc, char *argv[])
507 {
508 
509 	if (!connected)
510 		return;
511 	(void)command("QUIT");
512 	if (cout) {
513 		(void)fclose(cout);
514 	}
515 	cout = NULL;
516 	connected = 0;
517 	data = -1;
518 #ifndef SMALL
519 	if (!proxy) {
520 		macnum = 0;
521 	}
522 #endif /* !SMALL */
523 }
524 
525 char *
526 dotrans(char *name)
527 {
528 	static char new[MAXPATHLEN];
529 	char *cp1, *cp2 = new;
530 	int i, ostop, found;
531 
532 	for (ostop = 0; *(ntout + ostop) && ostop < 16; ostop++)
533 		continue;
534 	for (cp1 = name; *cp1; cp1++) {
535 		found = 0;
536 		for (i = 0; *(ntin + i) && i < 16; i++) {
537 			if (*cp1 == *(ntin + i)) {
538 				found++;
539 				if (i < ostop) {
540 					*cp2++ = *(ntout + i);
541 				}
542 				break;
543 			}
544 		}
545 		if (!found) {
546 			*cp2++ = *cp1;
547 		}
548 	}
549 	*cp2 = '\0';
550 	return (new);
551 }
552 
553 char *
554 domap(char *name)
555 {
556 	static char new[MAXPATHLEN];
557 	char *cp1 = name, *cp2 = mapin;
558 	char *tp[9], *te[9];
559 	int i, toks[9], toknum = 0, match = 1;
560 
561 	for (i=0; i < 9; ++i) {
562 		toks[i] = 0;
563 	}
564 	while (match && *cp1 && *cp2) {
565 		switch (*cp2) {
566 			case '\\':
567 				if (*++cp2 != *cp1) {
568 					match = 0;
569 				}
570 				break;
571 			case '$':
572 				if (*(cp2+1) >= '1' && (*cp2+1) <= '9') {
573 					if (*cp1 != *(++cp2+1)) {
574 						toks[toknum = *cp2 - '1']++;
575 						tp[toknum] = cp1;
576 						while (*++cp1 && *(cp2+1)
577 							!= *cp1);
578 						te[toknum] = cp1;
579 					}
580 					cp2++;
581 					break;
582 				}
583 				/* FALLTHROUGH */
584 			default:
585 				if (*cp2 != *cp1) {
586 					match = 0;
587 				}
588 				break;
589 		}
590 		if (match && *cp1) {
591 			cp1++;
592 		}
593 		if (match && *cp2) {
594 			cp2++;
595 		}
596 	}
597 	if (!match && *cp1) /* last token mismatch */
598 	{
599 		toks[toknum] = 0;
600 	}
601 	cp1 = new;
602 	*cp1 = '\0';
603 	cp2 = mapout;
604 	while (*cp2) {
605 		match = 0;
606 		switch (*cp2) {
607 			case '\\':
608 				if (*(cp2 + 1)) {
609 					*cp1++ = *++cp2;
610 				}
611 				break;
612 			case '[':
613 LOOP:
614 				if (*++cp2 == '$' && isdigit(*(cp2+1))) {
615 					if (*++cp2 == '0') {
616 						char *cp3 = name;
617 
618 						while (*cp3) {
619 							*cp1++ = *cp3++;
620 						}
621 						match = 1;
622 					}
623 					else if (toks[toknum = *cp2 - '1']) {
624 						char *cp3 = tp[toknum];
625 
626 						while (cp3 != te[toknum]) {
627 							*cp1++ = *cp3++;
628 						}
629 						match = 1;
630 					}
631 				}
632 				else {
633 					while (*cp2 && *cp2 != ',' &&
634 					    *cp2 != ']') {
635 						if (*cp2 == '\\') {
636 							cp2++;
637 						}
638 						else if (*cp2 == '$' &&
639    						        isdigit(*(cp2+1))) {
640 							if (*++cp2 == '0') {
641 							   char *cp3 = name;
642 
643 							   while (*cp3) {
644 								*cp1++ = *cp3++;
645 							   }
646 							}
647 							else if (toks[toknum =
648 							    *cp2 - '1']) {
649 							   char *cp3=tp[toknum];
650 
651 							   while (cp3 !=
652 								  te[toknum]) {
653 								*cp1++ = *cp3++;
654 							   }
655 							}
656 						}
657 						else if (*cp2) {
658 							*cp1++ = *cp2++;
659 						}
660 					}
661 					if (!*cp2) {
662 						fputs(
663 "nmap: unbalanced brackets.\n", ttyout);
664 						return (name);
665 					}
666 					match = 1;
667 					cp2--;
668 				}
669 				if (match) {
670 					while (*++cp2 && *cp2 != ']') {
671 					      if (*cp2 == '\\' && *(cp2 + 1)) {
672 							cp2++;
673 					      }
674 					}
675 					if (!*cp2) {
676 						fputs(
677 "nmap: unbalanced brackets.\n", ttyout);
678 						return (name);
679 					}
680 					break;
681 				}
682 				switch (*++cp2) {
683 					case ',':
684 						goto LOOP;
685 					case ']':
686 						break;
687 					default:
688 						cp2--;
689 						goto LOOP;
690 				}
691 				break;
692 			case '$':
693 				if (isdigit(*(cp2 + 1))) {
694 					if (*++cp2 == '0') {
695 						char *cp3 = name;
696 
697 						while (*cp3) {
698 							*cp1++ = *cp3++;
699 						}
700 					}
701 					else if (toks[toknum = *cp2 - '1']) {
702 						char *cp3 = tp[toknum];
703 
704 						while (cp3 != te[toknum]) {
705 							*cp1++ = *cp3++;
706 						}
707 					}
708 					break;
709 				}
710 				/* FALLTHROUGH */
711 			default:
712 				*cp1++ = *cp2;
713 				break;
714 		}
715 		cp2++;
716 	}
717 	*cp1 = '\0';
718 	if (!*new) {
719 		return (name);
720 	}
721 	return (new);
722 }
723 
724