Annotation of src/usr.bin/ftp/small.c, Revision 1.13
1.13 ! guenther 1: /* $OpenBSD: small.c,v 1.12 2019/11/18 04:37:35 deraadt Exp $ */
1.1 martynas 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.
1.9 krw 7: *
1.1 martynas 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.
1.9 krw 19: *
1.1 martynas 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>
1.2 deraadt 80: #include <errno.h>
1.1 martynas 81:
82: #include "ftp_var.h"
83: #include "pathnames.h"
84: #include "small.h"
85:
86: jmp_buf jabort;
87: char *mname;
88: char *home = "/";
89:
90: struct types {
91: char *t_name;
92: char *t_mode;
93: int t_type;
94: } types[] = {
1.4 tedu 95: { "ascii", "A", TYPE_A },
96: { "binary", "I", TYPE_I },
97: { "image", "I", TYPE_I },
1.1 martynas 98: { NULL }
99: };
100:
101: /*
102: * Set transfer type.
103: */
104: void
105: settype(int argc, char *argv[])
106: {
107: struct types *p;
108: int comret;
109:
110: if (argc > 2) {
111: char *sep;
112:
113: fprintf(ttyout, "usage: %s [", argv[0]);
114: sep = "";
115: for (p = types; p->t_name; p++) {
116: fprintf(ttyout, "%s%s", sep, p->t_name);
117: sep = " | ";
118: }
119: fputs("]\n", ttyout);
120: code = -1;
121: return;
122: }
123: if (argc < 2) {
124: fprintf(ttyout, "Using %s mode to transfer files.\n", typename);
125: code = 0;
126: return;
127: }
128: for (p = types; p->t_name; p++)
129: if (strcmp(argv[1], p->t_name) == 0)
130: break;
131: if (p->t_name == 0) {
132: fprintf(ttyout, "%s: unknown mode.\n", argv[1]);
133: code = -1;
134: return;
135: }
1.4 tedu 136: comret = command("TYPE %s", p->t_mode);
1.1 martynas 137: if (comret == COMPLETE) {
138: (void)strlcpy(typename, p->t_name, sizeof typename);
139: curtype = type = p->t_type;
140: }
141: }
142:
143: /*
144: * Internal form of settype; changes current type in use with server
145: * without changing our notion of the type for data transfers.
146: * Used to change to and from ascii for listings.
147: */
148: void
149: changetype(int newtype, int show)
150: {
151: struct types *p;
152: int comret, oldverbose = verbose;
153:
154: if (newtype == 0)
155: newtype = TYPE_I;
156: if (newtype == curtype)
157: return;
158: if (
159: #ifndef SMALL
160: !debug &&
161: #endif /* !SMALL */
162: show == 0)
163: verbose = 0;
164: for (p = types; p->t_name; p++)
165: if (newtype == p->t_type)
166: break;
167: if (p->t_name == 0) {
168: warnx("internal error: unknown type %d.", newtype);
169: return;
170: }
171: if (newtype == TYPE_L && bytename[0] != '\0')
172: comret = command("TYPE %s %s", p->t_mode, bytename);
173: else
174: comret = command("TYPE %s", p->t_mode);
175: if (comret == COMPLETE)
176: curtype = newtype;
177: verbose = oldverbose;
178: }
179:
180: char *stype[] = {
181: "type",
182: "",
183: 0
184: };
185:
186: /*
187: * Set binary transfer type.
188: */
189: void
190: setbinary(int argc, char *argv[])
191: {
192:
193: stype[1] = "binary";
194: settype(2, stype);
195: }
196:
197: void
198: get(int argc, char *argv[])
199: {
200:
201: (void)getit(argc, argv, 0, restart_point ? "a+w" : "w" );
202: }
203:
204: /*
205: * Receive one file.
206: */
207: int
208: getit(int argc, char *argv[], int restartit, const char *mode)
209: {
210: int loc = 0;
211: int rval = 0;
212: char *oldargv1, *oldargv2, *globargv2;
213:
214: if (argc == 2) {
215: argc++;
216: argv[2] = argv[1];
217: loc++;
218: }
219: #ifndef SMALL
220: if (argc < 2 && !another(&argc, &argv, "remote-file"))
221: goto usage;
222: if ((argc < 3 && !another(&argc, &argv, "local-file")) || argc > 3) {
223: usage:
224: fprintf(ttyout, "usage: %s remote-file [local-file]\n",
225: argv[0]);
226: code = -1;
227: return (0);
228: }
229: #endif /* !SMALL */
230: oldargv1 = argv[1];
231: oldargv2 = argv[2];
232: if (!globulize(&argv[2])) {
233: code = -1;
234: return (0);
235: }
236: globargv2 = argv[2];
237: if (loc && mcase) {
1.3 deraadt 238: char *tp = argv[1], *tp2, tmpbuf[PATH_MAX];
1.1 martynas 239:
1.5 mmcc 240: while (*tp && !islower((unsigned char)*tp)) {
1.1 martynas 241: tp++;
242: }
243: if (!*tp) {
244: tp = argv[2];
245: tp2 = tmpbuf;
246: while ((*tp2 = *tp) != '\0') {
1.5 mmcc 247: if (isupper((unsigned char)*tp2)) {
248: *tp2 = tolower((unsigned char)*tp2);
1.1 martynas 249: }
250: tp++;
251: tp2++;
252: }
253: argv[2] = tmpbuf;
254: }
255: }
256: if (loc && ntflag)
257: argv[2] = dotrans(argv[2]);
258: if (loc && mapflag)
259: argv[2] = domap(argv[2]);
260: #ifndef SMALL
261: if (restartit) {
262: struct stat stbuf;
263: int ret;
264:
265: ret = stat(argv[2], &stbuf);
266: if (restartit == 1) {
267: restart_point = (ret < 0) ? 0 : stbuf.st_size;
268: } else {
269: if (ret == 0) {
270: time_t mtime;
271:
272: mtime = remotemodtime(argv[1], 0);
273: if (mtime == -1)
274: goto freegetit;
275: if (stbuf.st_mtime >= mtime) {
276: rval = 1;
1.6 krw 277: fprintf(ttyout,
278: "Local file \"%s\" is newer "\
279: "than remote file \"%s\".\n",
280: argv[2], argv[1]);
1.1 martynas 281: goto freegetit;
282: }
283: }
284: }
285: }
286: #endif /* !SMALL */
287:
288: recvrequest("RETR", argv[2], argv[1], mode,
289: argv[1] != oldargv1 || argv[2] != oldargv2 || !interactive, loc);
290: restart_point = 0;
1.8 krw 291: #ifndef SMALL
1.1 martynas 292: freegetit:
1.8 krw 293: #endif
1.1 martynas 294: if (oldargv2 != globargv2) /* free up after globulize() */
295: free(globargv2);
296: return (rval);
297: }
298:
299: /* XXX - Signal race. */
300: void
301: mabort(int signo)
302: {
1.2 deraadt 303: int save_errno = errno;
304:
1.1 martynas 305: alarmtimer(0);
1.2 deraadt 306: (void) write(fileno(ttyout), "\n\r", 2);
1.1 martynas 307: #ifndef SMALL
1.2 deraadt 308: if (mflag && fromatty) {
309: /* XXX signal race, crazy unbelievable stdio misuse */
310: if (confirm(mname, NULL)) {
311: errno = save_errno;
1.1 martynas 312: longjmp(jabort, 1);
1.2 deraadt 313: }
314: }
1.1 martynas 315: #endif /* !SMALL */
316: mflag = 0;
1.2 deraadt 317: errno = save_errno;
1.1 martynas 318: longjmp(jabort, 1);
319: }
320:
321: /*
322: * Get multiple files.
323: */
324: void
325: mget(int argc, char *argv[])
326: {
327: extern int optind, optreset;
328: sig_t oldintr;
1.7 krw 329: int xargc = 2;
1.3 deraadt 330: char *cp, localcwd[PATH_MAX], *xargv[] = { argv[0], NULL, NULL };
1.1 martynas 331: static int restartit = 0;
332: #ifndef SMALL
333: extern char *optarg;
334: const char *errstr;
1.7 krw 335: int ch, i = 1;
1.1 martynas 336: char type = 0, *dummyargv[] = { argv[0], ".", NULL };
337: FILE *ftemp = NULL;
338: static int depth = 0, max_depth = 0;
339:
340: optind = optreset = 1;
341:
342: if (depth)
343: depth++;
344:
345: while ((ch = getopt(argc, argv, "cd:nr")) != -1) {
346: switch(ch) {
347: case 'c':
348: restartit = 1;
349: break;
350: case 'd':
351: max_depth = strtonum(optarg, 0, INT_MAX, &errstr);
352: if (errstr != NULL) {
353: fprintf(ttyout, "bad depth value, %s: %s\n",
354: errstr, optarg);
355: code = -1;
356: return;
357: }
358: break;
359: case 'n':
360: restartit = -1;
361: break;
362: case 'r':
363: depth = 1;
364: break;
365: default:
366: goto usage;
367: }
368: }
369:
370: if (argc - optind < 1 && !another(&argc, &argv, "remote-files")) {
371: usage:
372: fprintf(ttyout, "usage: %s [-cnr] [-d depth] remote-files\n",
373: argv[0]);
374: code = -1;
375: return;
376: }
377:
378: argv[optind - 1] = argv[0];
379: argc -= optind - 1;
380: argv += optind - 1;
381: #endif /* !SMALL */
382:
383: mname = argv[0];
384: mflag = 1;
385: if (getcwd(localcwd, sizeof(localcwd)) == NULL)
386: err(1, "can't get cwd");
387:
388: oldintr = signal(SIGINT, mabort);
389: (void)setjmp(jabort);
390: while ((cp =
391: #ifdef SMALL
392: remglob(argv, proxy, NULL)) != NULL
393: ) {
394: #else /* SMALL */
395: depth ? remglob2(dummyargv, proxy, NULL, &ftemp, &type) :
396: remglob(argv, proxy, NULL)) != NULL
397: || (mflag && depth && ++i < argc)
398: ) {
399: if (cp == NULL)
400: continue;
401: #endif /* SMALL */
402: if (*cp == '\0') {
403: mflag = 0;
404: continue;
405: }
406: if (!mflag)
407: continue;
408: #ifndef SMALL
409: if (depth && fnmatch(argv[i], cp, FNM_PATHNAME) != 0)
410: continue;
411: #endif /* !SMALL */
412: if (!fileindir(cp, localcwd)) {
413: fprintf(ttyout, "Skipping non-relative filename `%s'\n",
414: cp);
415: continue;
416: }
417: #ifndef SMALL
418: if (type == 'd' && depth == max_depth)
419: continue;
420: if (!confirm(argv[0], cp))
421: continue;
422: if (type == 'd') {
423: mkdir(cp, 0755);
424: if (chdir(cp) != 0) {
425: warn("local: %s", cp);
426: continue;
427: }
428:
429: xargv[1] = cp;
430: cd(xargc, xargv);
431: if (dirchange != 1)
432: goto out;
433:
434: xargv[1] = "*";
435: mget(xargc, xargv);
436:
437: xargv[1] = "..";
438: cd(xargc, xargv);
439: if (dirchange != 1) {
440: mflag = 0;
441: goto out;
442: }
443:
444: out:
445: if (chdir("..") != 0) {
446: warn("local: %s", cp);
447: mflag = 0;
448: }
449: continue;
450: }
451: if (type == 's')
452: /* Currently ignored. */
453: continue;
454: #endif /* !SMALL */
455: xargv[1] = cp;
456: (void)getit(xargc, xargv, restartit,
457: (restartit == 1 || restart_point) ? "a+w" : "w");
458: #ifndef SMALL
459: if (!mflag && fromatty) {
460: if (confirm(argv[0], NULL))
461: mflag = 1;
462: }
463: #endif /* !SMALL */
464: }
465: (void)signal(SIGINT, oldintr);
466: #ifndef SMALL
467: if (depth)
468: depth--;
469: if (depth == 0 || mflag == 0)
470: depth = max_depth = mflag = restartit = 0;
471: #else /* !SMALL */
472: mflag = 0;
473: #endif /* !SMALL */
474: }
475:
476: /*
477: * Set current working directory on remote machine.
478: */
479: void
480: cd(int argc, char *argv[])
481: {
482: int r;
483:
484: #ifndef SMALL
485: if ((argc < 2 && !another(&argc, &argv, "remote-directory")) ||
486: argc > 2) {
487: fprintf(ttyout, "usage: %s remote-directory\n", argv[0]);
488: code = -1;
489: return;
490: }
491: #endif /* !SMALL */
492: r = command("CWD %s", argv[1]);
493: if (r == ERROR && code == 500) {
494: if (verbose)
495: fputs("CWD command not recognized, trying XCWD.\n", ttyout);
496: r = command("XCWD %s", argv[1]);
497: }
498: if (r == ERROR && code == 550) {
499: dirchange = 0;
500: return;
501: }
502: if (r == COMPLETE)
503: dirchange = 1;
504: }
505:
506: /*
507: * Terminate session, but don't exit.
508: */
509: void
510: disconnect(int argc, char *argv[])
511: {
512:
513: if (!connected)
514: return;
515: (void)command("QUIT");
516: if (cout) {
517: (void)fclose(cout);
518: }
519: cout = NULL;
520: connected = 0;
521: data = -1;
522: #ifndef SMALL
523: if (!proxy) {
524: macnum = 0;
525: }
526: #endif /* !SMALL */
527: }
528:
529: char *
530: dotrans(char *name)
531: {
1.3 deraadt 532: static char new[PATH_MAX];
1.1 martynas 533: char *cp1, *cp2 = new;
534: int i, ostop, found;
535:
536: for (ostop = 0; *(ntout + ostop) && ostop < 16; ostop++)
537: continue;
538: for (cp1 = name; *cp1; cp1++) {
539: found = 0;
540: for (i = 0; *(ntin + i) && i < 16; i++) {
541: if (*cp1 == *(ntin + i)) {
542: found++;
543: if (i < ostop) {
544: *cp2++ = *(ntout + i);
545: }
546: break;
547: }
548: }
549: if (!found) {
550: *cp2++ = *cp1;
551: }
552: }
553: *cp2 = '\0';
554: return (new);
555: }
556:
557: char *
558: domap(char *name)
559: {
1.3 deraadt 560: static char new[PATH_MAX];
1.1 martynas 561: char *cp1 = name, *cp2 = mapin;
562: char *tp[9], *te[9];
563: int i, toks[9], toknum = 0, match = 1;
564:
565: for (i=0; i < 9; ++i) {
566: toks[i] = 0;
567: }
568: while (match && *cp1 && *cp2) {
569: switch (*cp2) {
570: case '\\':
571: if (*++cp2 != *cp1) {
572: match = 0;
573: }
574: break;
575: case '$':
576: if (*(cp2+1) >= '1' && (*cp2+1) <= '9') {
577: if (*cp1 != *(++cp2+1)) {
578: toks[toknum = *cp2 - '1']++;
579: tp[toknum] = cp1;
580: while (*++cp1 && *(cp2+1)
581: != *cp1);
582: te[toknum] = cp1;
583: }
584: cp2++;
585: break;
586: }
587: /* FALLTHROUGH */
588: default:
589: if (*cp2 != *cp1) {
590: match = 0;
591: }
592: break;
593: }
594: if (match && *cp1) {
595: cp1++;
596: }
597: if (match && *cp2) {
598: cp2++;
599: }
600: }
601: if (!match && *cp1) /* last token mismatch */
602: {
603: toks[toknum] = 0;
604: }
605: cp1 = new;
606: *cp1 = '\0';
607: cp2 = mapout;
608: while (*cp2) {
609: match = 0;
610: switch (*cp2) {
611: case '\\':
612: if (*(cp2 + 1)) {
613: *cp1++ = *++cp2;
614: }
615: break;
616: case '[':
617: LOOP:
1.5 mmcc 618: if (*++cp2 == '$' && isdigit((unsigned char)*(cp2 + 1))) {
1.1 martynas 619: if (*++cp2 == '0') {
620: char *cp3 = name;
621:
622: while (*cp3) {
623: *cp1++ = *cp3++;
624: }
625: match = 1;
1.12 deraadt 626: } else if (toks[toknum = *cp2 - '1']) {
1.1 martynas 627: char *cp3 = tp[toknum];
628:
629: while (cp3 != te[toknum]) {
630: *cp1++ = *cp3++;
631: }
632: match = 1;
633: }
1.12 deraadt 634: } else {
1.1 martynas 635: while (*cp2 && *cp2 != ',' &&
636: *cp2 != ']') {
637: if (*cp2 == '\\') {
638: cp2++;
1.12 deraadt 639: } else if (*cp2 == '$' &&
640: isdigit((unsigned char)*(cp2 + 1))) {
1.1 martynas 641: if (*++cp2 == '0') {
642: char *cp3 = name;
643:
644: while (*cp3) {
645: *cp1++ = *cp3++;
646: }
1.12 deraadt 647: } else if (toks[toknum =
1.1 martynas 648: *cp2 - '1']) {
1.12 deraadt 649: char *cp3=tp[toknum];
1.1 martynas 650:
1.12 deraadt 651: while (cp3 !=
652: te[toknum]) {
653: *cp1++ = *cp3++;
654: }
1.1 martynas 655: }
1.12 deraadt 656: } else if (*cp2) {
1.1 martynas 657: *cp1++ = *cp2++;
658: }
659: }
660: if (!*cp2) {
661: fputs(
662: "nmap: unbalanced brackets.\n", ttyout);
663: return (name);
664: }
665: match = 1;
666: cp2--;
667: }
668: if (match) {
669: while (*++cp2 && *cp2 != ']') {
1.12 deraadt 670: if (*cp2 == '\\' && *(cp2 + 1)) {
1.1 martynas 671: cp2++;
1.12 deraadt 672: }
1.1 martynas 673: }
674: if (!*cp2) {
675: fputs(
676: "nmap: unbalanced brackets.\n", ttyout);
677: return (name);
678: }
679: break;
680: }
681: switch (*++cp2) {
682: case ',':
683: goto LOOP;
684: case ']':
685: break;
686: default:
687: cp2--;
688: goto LOOP;
689: }
690: break;
691: case '$':
1.5 mmcc 692: if (isdigit((unsigned char)*(cp2 + 1))) {
1.1 martynas 693: if (*++cp2 == '0') {
694: char *cp3 = name;
695:
696: while (*cp3) {
697: *cp1++ = *cp3++;
698: }
1.12 deraadt 699: } else if (toks[toknum = *cp2 - '1']) {
1.1 martynas 700: char *cp3 = tp[toknum];
701:
702: while (cp3 != te[toknum]) {
703: *cp1++ = *cp3++;
704: }
705: }
706: break;
707: }
708: /* FALLTHROUGH */
709: default:
710: *cp1++ = *cp2;
711: break;
712: }
713: cp2++;
714: }
715: *cp1 = '\0';
716: if (!*new) {
717: return (name);
718: }
719: return (new);
720: }
721: