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