Annotation of src/usr.bin/ftp/fetch.c, Revision 1.2
1.2 ! millert 1: /* $OpenBSD: fetch.c,v 1.2 1997/02/01 10:45:00 lukem Exp $ */
1.1 millert 2: /* $NetBSD: fetch.c,v 1.2 1997/02/01 10:45:00 lukem Exp $ */
3:
4: /*-
5: * Copyright (c) 1997 The NetBSD Foundation, Inc.
6: * All rights reserved.
7: *
8: * This code is derived from software contributed to The NetBSD Foundation
9: * by Jason Thorpe and Luke Mewburn.
10: *
11: * Redistribution and use in source and binary forms, with or without
12: * modification, are permitted provided that the following conditions
13: * are met:
14: * 1. Redistributions of source code must retain the above copyright
15: * notice, this list of conditions and the following disclaimer.
16: * 2. Redistributions in binary form must reproduce the above copyright
17: * notice, this list of conditions and the following disclaimer in the
18: * documentation and/or other materials provided with the distribution.
19: * 3. All advertising materials mentioning features or use of this software
20: * must display the following acknowledgement:
21: * This product includes software developed by the NetBSD
22: * Foundation, Inc. and its contributors.
23: * 4. Neither the name of The NetBSD Foundation nor the names of its
24: * contributors may be used to endorse or promote products derived
25: * from this software without specific prior written permission.
26: *
27: * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
28: * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
29: * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
31: * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37: * POSSIBILITY OF SUCH DAMAGE.
38: */
39:
40: #ifndef lint
1.2 ! millert 41: static char rcsid[] = "$OpenBSD: fetch.c,v 1.2 1997/02/01 10:45:00 lukem Exp $";
1.1 millert 42: #endif /* not lint */
43:
44: /*
45: * FTP User Program -- Command line file retrieval
46: */
47:
48: #include <sys/types.h>
49: #include <sys/param.h>
50: #include <sys/socket.h>
51:
52: #include <netinet/in.h>
53:
54: #include <arpa/ftp.h>
55: #include <arpa/inet.h>
56:
57: #include <ctype.h>
58: #include <err.h>
59: #include <netdb.h>
60: #include <fcntl.h>
61: #include <stdio.h>
62: #include <stdlib.h>
63: #include <string.h>
64: #include <unistd.h>
65:
66: #include "ftp_var.h"
67:
68: #define FTP_URL "ftp://" /* ftp URL prefix */
69: #define HTTP_URL "http://" /* http URL prefix */
70: #define HTTP_PROXY "http_proxy" /* env var with http proxy location */
71:
72:
73: #define EMPTYSTRING(x) ((x) == NULL || (*(x) == '\0'))
74:
75: jmp_buf httpabort;
76:
77: /*
78: * Retrieve an http:// URL, via a proxy if necessary.
79: * Modifies the string argument given.
80: * Returns -1 on failure, 0 on success
81: */
82: int
83: http_get(line)
84: char *line;
85: {
86: struct sockaddr_in sin;
87: int i, out, port, s;
88: size_t buflen, len;
89: char c, *cp, *cp2, *savefile, *portnum, *path, buf[4096];
90: char *proxyenv, *proxy, *host;
91: sig_t oldintr;
92: off_t hashbytes;
93:
94: s = -1;
95: proxy = NULL;
96:
97: host = line + sizeof(HTTP_URL) - 1;
98: path = strchr(host, '/'); /* find path */
99: if (EMPTYSTRING(path))
100: goto cleanup_http_get;
101: *path++ = '\0';
102: if (EMPTYSTRING(path))
103: goto cleanup_http_get;
104:
105: savefile = strrchr(path, '/'); /* find savefile */
106: if (savefile != NULL)
107: savefile++;
108: else
109: savefile = path;
110: if (EMPTYSTRING(savefile))
111: goto cleanup_http_get;
112:
113: proxyenv = getenv(HTTP_PROXY);
114: if (proxyenv != NULL) { /* use proxy */
115: if (strncmp(proxyenv, HTTP_URL, sizeof(HTTP_URL) - 1) != 0) {
116: warnx("Malformed proxy url: %s", proxyenv);
117: goto cleanup_http_get;
118: }
119: proxy = strdup(proxyenv);
120: if (proxy == NULL)
121: errx(1, "Can't allocate memory for proxy url.");
122: host = proxy + sizeof(HTTP_URL) - 1;
123: if (EMPTYSTRING(host))
124: goto cleanup_http_get;
125: *--path = '/'; /* add / back to real path */
126: path = strchr(host, '/'); /* remove trailing / on host */
127: if (! EMPTYSTRING(path))
128: *path++ = '\0';
129: path = line;
130: }
131:
132: portnum = strchr(host, ':'); /* find portnum */
133: if (portnum != NULL)
134: *portnum++ = '\0';
135:
136: if (debug)
137: printf("host %s, port %s, path %s, save as %s.\n",
138: host, portnum, path, savefile);
139:
140: memset(&sin, 0, sizeof(sin));
141: sin.sin_family = AF_INET;
142:
143: if (isdigit(host[0])) {
144: if (inet_aton(host, &sin.sin_addr) == 0) {
145: warnx("invalid IP address: %s", host);
146: goto cleanup_http_get;
147: }
148: } else {
149: struct hostent *hp;
150:
151: hp = gethostbyname(host);
152: if (hp == NULL) {
153: warnx("%s: %s", host, hstrerror(h_errno));
154: goto cleanup_http_get;
155: }
156: if (hp->h_addrtype != AF_INET) {
157: warnx("%s: not an Internet address?", host);
158: goto cleanup_http_get;
159: }
160: memcpy(&sin.sin_addr, hp->h_addr, hp->h_length);
161: }
162:
163: if (! EMPTYSTRING(portnum)) {
164: port = atoi(portnum);
165: if (port < 1 || (port & 0xffff) != port) {
166: warnx("invalid port: %s", portnum);
167: goto cleanup_http_get;
168: }
169: port = htons(port);
170: } else
171: port = httpport;
172: sin.sin_port = port;
173:
174: s = socket(AF_INET, SOCK_STREAM, 0);
175: if (s == -1) {
176: warnx("Can't create socket");
177: goto cleanup_http_get;
178: }
179:
180: if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) == -1) {
181: warn("Can't connect to %s", host);
182: goto cleanup_http_get;
183: }
184:
185: /*
186: * Construct and send the request. We're expecting a return
187: * status of "200". Proxy requests don't want leading /.
188: */
189: if (!proxy)
190: printf("Requesting %s:%d/%s\n", line, ntohs(port), path);
191: else
192: printf("Requesting %s (via %s)\n", line, proxyenv);
193: snprintf(buf, sizeof(buf), "GET %s%s HTTP/1.0\n\n",
194: proxy ? "" : "/", path);
195: buflen = strlen(buf);
196: if (write(s, buf, buflen) < buflen) {
197: warn("write");
198: goto cleanup_http_get;
199: }
200: memset(buf, 0, sizeof(buf));
201: for (i = 0, buflen = sizeof(buf), cp = buf; i < buflen; cp++, i++) {
202: if (read(s, cp, 1) != 1)
203: goto improper;
204: if (*cp == '\n')
205: break;
206: }
207: buf[buflen - 1] = '\0'; /* sanity */
208: cp = strchr(buf, ' ');
209: if (cp == NULL)
210: goto improper;
211: else
212: cp++;
213: if (strncmp(cp, "200", 3)) {
214: warnx("Error retrieving file: %s", cp);
215: goto cleanup_http_get;
216: }
217:
218: /*
219: * Read the rest of the header.
220: */
221: memset(buf, 0, sizeof(buf));
222: c = '\0';
223: for (i = 0, buflen = sizeof(buf), cp = buf; i < buflen; cp++, i++) {
224: if (read(s, cp, 1) != 1)
225: goto improper;
226: if (*cp == '\n' && c == '\n')
227: break;
228: c = *cp;
229: }
230: buf[buflen - 1] = '\0'; /* sanity */
231:
232: /*
233: * Look for the "Content-length: " header.
234: */
235: #define CONTENTLEN "Content-Length: "
236: for (cp = buf; *cp != '\0'; cp++) {
237: if (tolower(*cp) == 'c' &&
238: strncasecmp(cp, CONTENTLEN, sizeof(CONTENTLEN) - 1) == 0)
239: break;
240: }
241: if (*cp == '\0')
242: goto improper;
243: cp += sizeof(CONTENTLEN) - 1;
244: cp2 = strchr(cp, '\n');
245: if (cp2 == NULL)
246: goto improper;
247: else
248: *cp2 = '\0';
249: filesize = atoi(cp);
250: if (filesize < 1)
251: goto improper;
252:
253: /* Open the output file. */
254: out = open(savefile, O_CREAT | O_WRONLY | O_TRUNC, 0666);
255: if (out < 0) {
256: warn("Can't open %s", savefile);
257: goto cleanup_http_get;
258: }
259:
260: /* Trap signals */
261: oldintr = NULL;
262: if (setjmp(httpabort)) {
263: if (oldintr)
1.2 ! millert 264: (void)signal(SIGINT, oldintr);
1.1 millert 265: goto cleanup_http_get;
266: }
267: oldintr = signal(SIGINT, aborthttp);
268:
269: bytes = 0;
270: hashbytes = mark;
271: progressmeter(-1);
272:
273: /* Finally, suck down the file. */
274: i = 0;
275: while ((len = read(s, buf, sizeof(buf))) > 0) {
276: bytes += len;
277: for (cp = buf; len > 0; len -= i, cp += i) {
278: if ((i = write(out, cp, len)) == -1) {
279: warn("Writing %s", savefile);
280: goto cleanup_http_get;
281: }
282: else if (i == 0)
283: break;
284: }
285: if (hash && !progress) {
286: while (bytes >= hashbytes) {
1.2 ! millert 287: (void)putchar('#');
1.1 millert 288: hashbytes += mark;
289: }
1.2 ! millert 290: (void)fflush(stdout);
1.1 millert 291: }
292: }
293: if (hash && !progress && bytes > 0) {
294: if (bytes < mark)
1.2 ! millert 295: (void)putchar('#');
! 296: (void)putchar('\n');
! 297: (void)fflush(stdout);
1.1 millert 298: }
299: if (len != 0) {
300: warn("Reading from socket");
301: goto cleanup_http_get;
302: }
303: progressmeter(1);
304: if (verbose)
1.2 ! millert 305: puts("Successfully retrieved file.");
! 306: (void)signal(SIGINT, oldintr);
1.1 millert 307:
308: close(s);
309: close(out);
310: if (proxy)
311: free(proxy);
1.2 ! millert 312: return (0);
1.1 millert 313:
314: improper:
315: warnx("improper response from %s", host);
316: cleanup_http_get:
317: if (s != -1)
318: close(s);
319: if (proxy)
320: free(proxy);
1.2 ! millert 321: return (-1);
1.1 millert 322: }
323:
324: /*
325: * Abort a http retrieval
326: */
327: void
328: aborthttp()
329: {
330:
331: alarmtimer(0);
1.2 ! millert 332: puts("\nhttp fetch aborted");
! 333: (void)fflush(stdout);
1.1 millert 334: longjmp(httpabort, 1);
335: }
336:
337: /*
338: * Retrieve multiple files from the command line, transferring
339: * files of the form "host:path", "ftp://host/path" using the
340: * ftp protocol, and files of the form "http://host/path" using
341: * the http protocol.
1.2 ! millert 342: * If path has a trailing "/", then return (-1);
1.1 millert 343: * the path will be cd-ed into and the connection remains open,
344: * and the function will return -1 (to indicate the connection
345: * is alive).
346: * If an error occurs the return value will be the offset+1 in
347: * argv[] of the file that caused a problem (i.e, argv[x]
348: * returns x+1)
349: * Otherwise, 0 is returned if all files retrieved successfully.
350: */
351: int
352: auto_fetch(argc, argv)
353: int argc;
354: char *argv[];
355: {
356: static char lasthost[MAXHOSTNAMELEN];
357: char *xargv[5];
358: char *cp, *line, *host, *dir, *file, *portnum;
359: int rval, xargc, argpos;
360:
361: argpos = 0;
362:
363: if (setjmp(toplevel)) {
364: if (connected)
365: disconnect(0, NULL);
1.2 ! millert 366: return (argpos + 1);
1.1 millert 367: }
1.2 ! millert 368: (void)signal(SIGINT, intr);
! 369: (void)signal(SIGPIPE, lostpeer);
1.1 millert 370:
371: /*
372: * Loop through as long as there's files to fetch.
373: */
374: for (rval = 0; (rval == 0) && (argpos < argc); free(line), argpos++) {
375: if (strchr(argv[argpos], ':') == NULL)
376: break;
377: host = dir = file = portnum = NULL;
378:
379: /*
380: * We muck with the string, so we make a copy.
381: */
382: line = strdup(argv[argpos]);
383: if (line == NULL)
384: errx(1, "Can't allocate memory for auto-fetch.");
385:
386: /*
387: * Try HTTP URL-style arguments first.
388: */
389: if (strncmp(line, HTTP_URL, sizeof(HTTP_URL) - 1) == 0) {
390: if (http_get(line) == -1)
391: rval = argpos + 1;
392: continue;
393: }
394:
395: /*
396: * Try FTP URL-style arguments next, then host:file.
397: */
398: host = line;
399: if (strncmp(line, FTP_URL, sizeof(FTP_URL) - 1) == 0) {
400: host += sizeof(FTP_URL) - 1;
401: cp = strchr(host, '/');
402:
403: /* Look for a port number after the host name. */
404: portnum = strchr(host, ':');
405: if (portnum != NULL)
406: *portnum++ = '\0';
407: } else /* classic style `host:file' */
408: cp = strchr(host, ':');
409: if (EMPTYSTRING(host)) {
410: rval = argpos + 1;
411: continue;
412: }
413:
414: /*
415: * If cp is NULL, the file wasn't specified
416: * (URL looked something like ftp://host)
417: */
418: if (cp != NULL)
419: *cp++ = '\0';
420:
421: /*
422: * Extract the file and (if present) directory name.
423: */
424: dir = cp;
425: if (! EMPTYSTRING(dir)) {
426: cp = strrchr(cp, '/');
427: if (cp != NULL) {
428: *cp++ = '\0';
429: file = cp;
430: } else {
431: file = dir;
432: dir = NULL;
433: }
434: }
435: if (debug)
436: printf("host '%s', dir '%s', file '%s'\n",
437: host, dir, file);
438:
439: /*
440: * Set up the connection if we don't have one.
441: */
442: if (strcmp(host, lasthost) != 0) {
443: strcpy(lasthost, host);
444: if (connected)
445: disconnect(0, NULL);
446: xargv[0] = __progname;
447: xargv[1] = host;
448: xargv[2] = NULL;
449: xargc = 2;
450: if (portnum != NULL) {
451: xargv[2] = portnum;
452: xargv[3] = NULL;
453: xargc = 3;
454: }
455: setpeer(xargc, xargv);
456: if (connected == 0) {
457: warnx("Can't connect to host `%s'", host);
458: rval = argpos + 1;
459: continue;
460: }
461:
462: /* Always use binary transfers. */
463: setbinary(0, NULL);
464: }
465: else /* already have connection, cd back to '/' */
466: {
467: xargv[0] = "cd";
468: xargv[1] = "/";
469: xargv[2] = NULL;
470: cd(2, xargv);
471: if (! dirchange) {
472: rval = argpos + 1;
473: continue;
474: }
475: }
476:
477: /* Change directories, if necessary. */
478: if (! EMPTYSTRING(dir)) {
479: xargv[0] = "cd";
480: xargv[1] = dir;
481: xargv[2] = NULL;
482: cd(2, xargv);
483: if (! dirchange) {
484: rval = argpos + 1;
485: continue;
486: }
487: }
488:
489: if (EMPTYSTRING(file)) {
490: rval = -1;
491: continue;
492: }
493:
494: if (!verbose)
495: printf("Retrieving %s/%s\n", dir ? dir : "", file);
496:
497: /* Fetch the file. */
498: xargv[0] = "get";
499: xargv[1] = file;
500: xargv[2] = NULL;
501: get(2, xargv);
502:
503: if ((code / 100) != COMPLETE) /* XXX: is this valid? */
504: rval = argpos + 1;
505: }
506: if (connected && rval != -1)
507: disconnect(0, NULL);
508: return (rval);
509: }