Annotation of src/usr.bin/ftp/small.c, Revision 1.2
1.2 ! deraadt 1: /* $OpenBSD: small.c,v 1.1 2009/05/05 19:35:30 martynas 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: char *t_arg;
95: } types[] = {
96: { "ascii", "A", TYPE_A, 0 },
97: { "binary", "I", TYPE_I, 0 },
98: { "image", "I", TYPE_I, 0 },
99: { "ebcdic", "E", TYPE_E, 0 },
100: { "tenex", "L", TYPE_L, bytename },
101: { NULL }
102: };
103:
104: /*
105: * Set transfer type.
106: */
107: void
108: settype(int argc, char *argv[])
109: {
110: struct types *p;
111: int comret;
112:
113: if (argc > 2) {
114: char *sep;
115:
116: fprintf(ttyout, "usage: %s [", argv[0]);
117: sep = "";
118: for (p = types; p->t_name; p++) {
119: fprintf(ttyout, "%s%s", sep, p->t_name);
120: sep = " | ";
121: }
122: fputs("]\n", ttyout);
123: code = -1;
124: return;
125: }
126: if (argc < 2) {
127: fprintf(ttyout, "Using %s mode to transfer files.\n", typename);
128: code = 0;
129: return;
130: }
131: for (p = types; p->t_name; p++)
132: if (strcmp(argv[1], p->t_name) == 0)
133: break;
134: if (p->t_name == 0) {
135: fprintf(ttyout, "%s: unknown mode.\n", argv[1]);
136: code = -1;
137: return;
138: }
139: if ((p->t_arg != NULL) && (*(p->t_arg) != '\0'))
140: comret = command("TYPE %s %s", p->t_mode, p->t_arg);
141: else
142: comret = command("TYPE %s", p->t_mode);
143: if (comret == COMPLETE) {
144: (void)strlcpy(typename, p->t_name, sizeof typename);
145: curtype = type = p->t_type;
146: }
147: }
148:
149: /*
150: * Internal form of settype; changes current type in use with server
151: * without changing our notion of the type for data transfers.
152: * Used to change to and from ascii for listings.
153: */
154: void
155: changetype(int newtype, int show)
156: {
157: struct types *p;
158: int comret, oldverbose = verbose;
159:
160: if (newtype == 0)
161: newtype = TYPE_I;
162: if (newtype == curtype)
163: return;
164: if (
165: #ifndef SMALL
166: !debug &&
167: #endif /* !SMALL */
168: show == 0)
169: verbose = 0;
170: for (p = types; p->t_name; p++)
171: if (newtype == p->t_type)
172: break;
173: if (p->t_name == 0) {
174: warnx("internal error: unknown type %d.", newtype);
175: return;
176: }
177: if (newtype == TYPE_L && bytename[0] != '\0')
178: comret = command("TYPE %s %s", p->t_mode, bytename);
179: else
180: comret = command("TYPE %s", p->t_mode);
181: if (comret == COMPLETE)
182: curtype = newtype;
183: verbose = oldverbose;
184: }
185:
186: char *stype[] = {
187: "type",
188: "",
189: 0
190: };
191:
192: /*
193: * Set binary transfer type.
194: */
195: /*ARGSUSED*/
196: void
197: setbinary(int argc, char *argv[])
198: {
199:
200: stype[1] = "binary";
201: settype(2, stype);
202: }
203:
204: void
205: get(int argc, char *argv[])
206: {
207:
208: (void)getit(argc, argv, 0, restart_point ? "a+w" : "w" );
209: }
210:
211: /*
212: * Receive one file.
213: */
214: int
215: getit(int argc, char *argv[], int restartit, const char *mode)
216: {
217: int loc = 0;
218: int rval = 0;
219: char *oldargv1, *oldargv2, *globargv2;
220:
221: if (argc == 2) {
222: argc++;
223: argv[2] = argv[1];
224: loc++;
225: }
226: #ifndef SMALL
227: if (argc < 2 && !another(&argc, &argv, "remote-file"))
228: goto usage;
229: if ((argc < 3 && !another(&argc, &argv, "local-file")) || argc > 3) {
230: usage:
231: fprintf(ttyout, "usage: %s remote-file [local-file]\n",
232: argv[0]);
233: code = -1;
234: return (0);
235: }
236: #endif /* !SMALL */
237: oldargv1 = argv[1];
238: oldargv2 = argv[2];
239: if (!globulize(&argv[2])) {
240: code = -1;
241: return (0);
242: }
243: globargv2 = argv[2];
244: if (loc && mcase) {
245: char *tp = argv[1], *tp2, tmpbuf[MAXPATHLEN];
246:
247: while (*tp && !islower(*tp)) {
248: tp++;
249: }
250: if (!*tp) {
251: tp = argv[2];
252: tp2 = tmpbuf;
253: while ((*tp2 = *tp) != '\0') {
254: if (isupper(*tp2)) {
255: *tp2 = tolower(*tp2);
256: }
257: tp++;
258: tp2++;
259: }
260: argv[2] = tmpbuf;
261: }
262: }
263: if (loc && ntflag)
264: argv[2] = dotrans(argv[2]);
265: if (loc && mapflag)
266: argv[2] = domap(argv[2]);
267: #ifndef SMALL
268: if (restartit) {
269: struct stat stbuf;
270: int ret;
271:
272: ret = stat(argv[2], &stbuf);
273: if (restartit == 1) {
274: restart_point = (ret < 0) ? 0 : stbuf.st_size;
275: } else {
276: if (ret == 0) {
277: time_t mtime;
278:
279: mtime = remotemodtime(argv[1], 0);
280: if (mtime == -1)
281: goto freegetit;
282: if (stbuf.st_mtime >= mtime) {
283: rval = 1;
284: goto freegetit;
285: }
286: }
287: }
288: }
289: #endif /* !SMALL */
290:
291: recvrequest("RETR", argv[2], argv[1], mode,
292: argv[1] != oldargv1 || argv[2] != oldargv2 || !interactive, loc);
293: restart_point = 0;
294: freegetit:
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;
331: int ch, xargc = 2;
332: char *cp, localcwd[MAXPATHLEN], *xargv[] = { argv[0], NULL, NULL };
333: static int restartit = 0;
334: #ifndef SMALL
335: extern char *optarg;
336: const char *errstr;
337: int i = 1;
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: {
535: static char new[MAXPATHLEN];
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: {
563: static char new[MAXPATHLEN];
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:
621: if (*++cp2 == '$' && isdigit(*(cp2+1))) {
622: if (*++cp2 == '0') {
623: char *cp3 = name;
624:
625: while (*cp3) {
626: *cp1++ = *cp3++;
627: }
628: match = 1;
629: }
630: else if (toks[toknum = *cp2 - '1']) {
631: char *cp3 = tp[toknum];
632:
633: while (cp3 != te[toknum]) {
634: *cp1++ = *cp3++;
635: }
636: match = 1;
637: }
638: }
639: else {
640: while (*cp2 && *cp2 != ',' &&
641: *cp2 != ']') {
642: if (*cp2 == '\\') {
643: cp2++;
644: }
645: else if (*cp2 == '$' &&
646: isdigit(*(cp2+1))) {
647: if (*++cp2 == '0') {
648: char *cp3 = name;
649:
650: while (*cp3) {
651: *cp1++ = *cp3++;
652: }
653: }
654: else if (toks[toknum =
655: *cp2 - '1']) {
656: char *cp3=tp[toknum];
657:
658: while (cp3 !=
659: te[toknum]) {
660: *cp1++ = *cp3++;
661: }
662: }
663: }
664: else if (*cp2) {
665: *cp1++ = *cp2++;
666: }
667: }
668: if (!*cp2) {
669: fputs(
670: "nmap: unbalanced brackets.\n", ttyout);
671: return (name);
672: }
673: match = 1;
674: cp2--;
675: }
676: if (match) {
677: while (*++cp2 && *cp2 != ']') {
678: if (*cp2 == '\\' && *(cp2 + 1)) {
679: cp2++;
680: }
681: }
682: if (!*cp2) {
683: fputs(
684: "nmap: unbalanced brackets.\n", ttyout);
685: return (name);
686: }
687: break;
688: }
689: switch (*++cp2) {
690: case ',':
691: goto LOOP;
692: case ']':
693: break;
694: default:
695: cp2--;
696: goto LOOP;
697: }
698: break;
699: case '$':
700: if (isdigit(*(cp2 + 1))) {
701: if (*++cp2 == '0') {
702: char *cp3 = name;
703:
704: while (*cp3) {
705: *cp1++ = *cp3++;
706: }
707: }
708: else if (toks[toknum = *cp2 - '1']) {
709: char *cp3 = tp[toknum];
710:
711: while (cp3 != te[toknum]) {
712: *cp1++ = *cp3++;
713: }
714: }
715: break;
716: }
717: /* FALLTHROUGH */
718: default:
719: *cp1++ = *cp2;
720: break;
721: }
722: cp2++;
723: }
724: *cp1 = '\0';
725: if (!*new) {
726: return (name);
727: }
728: return (new);
729: }
730: