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