Annotation of src/usr.bin/ftp/small.c, Revision 1.7
1.7 ! krw 1: /* $OpenBSD: small.c,v 1.6 2016/05/25 15:36:01 krw 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;
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;
292: freegetit:
293: if (oldargv2 != globargv2) /* free up after globulize() */
294: free(globargv2);
295: return (rval);
296: }
297:
298: /* XXX - Signal race. */
299: /* ARGSUSED */
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: /* ARGSUSED */
510: void
511: disconnect(int argc, char *argv[])
512: {
513:
514: if (!connected)
515: return;
516: (void)command("QUIT");
517: if (cout) {
518: (void)fclose(cout);
519: }
520: cout = NULL;
521: connected = 0;
522: data = -1;
523: #ifndef SMALL
524: if (!proxy) {
525: macnum = 0;
526: }
527: #endif /* !SMALL */
528: }
529:
530: char *
531: dotrans(char *name)
532: {
1.3 deraadt 533: static char new[PATH_MAX];
1.1 martynas 534: char *cp1, *cp2 = new;
535: int i, ostop, found;
536:
537: for (ostop = 0; *(ntout + ostop) && ostop < 16; ostop++)
538: continue;
539: for (cp1 = name; *cp1; cp1++) {
540: found = 0;
541: for (i = 0; *(ntin + i) && i < 16; i++) {
542: if (*cp1 == *(ntin + i)) {
543: found++;
544: if (i < ostop) {
545: *cp2++ = *(ntout + i);
546: }
547: break;
548: }
549: }
550: if (!found) {
551: *cp2++ = *cp1;
552: }
553: }
554: *cp2 = '\0';
555: return (new);
556: }
557:
558: char *
559: domap(char *name)
560: {
1.3 deraadt 561: static char new[PATH_MAX];
1.1 martynas 562: char *cp1 = name, *cp2 = mapin;
563: char *tp[9], *te[9];
564: int i, toks[9], toknum = 0, match = 1;
565:
566: for (i=0; i < 9; ++i) {
567: toks[i] = 0;
568: }
569: while (match && *cp1 && *cp2) {
570: switch (*cp2) {
571: case '\\':
572: if (*++cp2 != *cp1) {
573: match = 0;
574: }
575: break;
576: case '$':
577: if (*(cp2+1) >= '1' && (*cp2+1) <= '9') {
578: if (*cp1 != *(++cp2+1)) {
579: toks[toknum = *cp2 - '1']++;
580: tp[toknum] = cp1;
581: while (*++cp1 && *(cp2+1)
582: != *cp1);
583: te[toknum] = cp1;
584: }
585: cp2++;
586: break;
587: }
588: /* FALLTHROUGH */
589: default:
590: if (*cp2 != *cp1) {
591: match = 0;
592: }
593: break;
594: }
595: if (match && *cp1) {
596: cp1++;
597: }
598: if (match && *cp2) {
599: cp2++;
600: }
601: }
602: if (!match && *cp1) /* last token mismatch */
603: {
604: toks[toknum] = 0;
605: }
606: cp1 = new;
607: *cp1 = '\0';
608: cp2 = mapout;
609: while (*cp2) {
610: match = 0;
611: switch (*cp2) {
612: case '\\':
613: if (*(cp2 + 1)) {
614: *cp1++ = *++cp2;
615: }
616: break;
617: case '[':
618: LOOP:
1.5 mmcc 619: if (*++cp2 == '$' && isdigit((unsigned char)*(cp2 + 1))) {
1.1 martynas 620: if (*++cp2 == '0') {
621: char *cp3 = name;
622:
623: while (*cp3) {
624: *cp1++ = *cp3++;
625: }
626: match = 1;
627: }
628: else if (toks[toknum = *cp2 - '1']) {
629: char *cp3 = tp[toknum];
630:
631: while (cp3 != te[toknum]) {
632: *cp1++ = *cp3++;
633: }
634: match = 1;
635: }
636: }
637: else {
638: while (*cp2 && *cp2 != ',' &&
639: *cp2 != ']') {
640: if (*cp2 == '\\') {
641: cp2++;
642: }
643: else if (*cp2 == '$' &&
1.5 mmcc 644: isdigit((unsigned char)*(cp2 + 1))) {
1.1 martynas 645: if (*++cp2 == '0') {
646: char *cp3 = name;
647:
648: while (*cp3) {
649: *cp1++ = *cp3++;
650: }
651: }
652: else if (toks[toknum =
653: *cp2 - '1']) {
654: char *cp3=tp[toknum];
655:
656: while (cp3 !=
657: te[toknum]) {
658: *cp1++ = *cp3++;
659: }
660: }
661: }
662: else if (*cp2) {
663: *cp1++ = *cp2++;
664: }
665: }
666: if (!*cp2) {
667: fputs(
668: "nmap: unbalanced brackets.\n", ttyout);
669: return (name);
670: }
671: match = 1;
672: cp2--;
673: }
674: if (match) {
675: while (*++cp2 && *cp2 != ']') {
676: if (*cp2 == '\\' && *(cp2 + 1)) {
677: cp2++;
678: }
679: }
680: if (!*cp2) {
681: fputs(
682: "nmap: unbalanced brackets.\n", ttyout);
683: return (name);
684: }
685: break;
686: }
687: switch (*++cp2) {
688: case ',':
689: goto LOOP;
690: case ']':
691: break;
692: default:
693: cp2--;
694: goto LOOP;
695: }
696: break;
697: case '$':
1.5 mmcc 698: if (isdigit((unsigned char)*(cp2 + 1))) {
1.1 martynas 699: if (*++cp2 == '0') {
700: char *cp3 = name;
701:
702: while (*cp3) {
703: *cp1++ = *cp3++;
704: }
705: }
706: else if (toks[toknum = *cp2 - '1']) {
707: char *cp3 = tp[toknum];
708:
709: while (cp3 != te[toknum]) {
710: *cp1++ = *cp3++;
711: }
712: }
713: break;
714: }
715: /* FALLTHROUGH */
716: default:
717: *cp1++ = *cp2;
718: break;
719: }
720: cp2++;
721: }
722: *cp1 = '\0';
723: if (!*new) {
724: return (name);
725: }
726: return (new);
727: }
728: