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