Annotation of src/usr.bin/ftp/fetch.c, Revision 1.101
1.101 ! halex 1: /* $OpenBSD: fetch.c,v 1.100 2010/06/29 23:12:33 halex Exp $ */
1.15 millert 2: /* $NetBSD: fetch.c,v 1.14 1997/08/18 10:20:20 lukem Exp $ */
1.1 millert 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: *
20: * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21: * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22: * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
1.15 millert 23: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
1.1 millert 25: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30: * POSSIBILITY OF SUCH DAMAGE.
31: */
32:
33: /*
34: * FTP User Program -- Command line file retrieval
35: */
36:
37: #include <sys/types.h>
38: #include <sys/param.h>
39: #include <sys/socket.h>
1.22 deraadt 40: #include <sys/stat.h>
1.1 millert 41:
42: #include <netinet/in.h>
43:
44: #include <arpa/ftp.h>
45: #include <arpa/inet.h>
46:
47: #include <ctype.h>
48: #include <err.h>
1.17 millert 49: #include <libgen.h>
1.58 grunk 50: #include <limits.h>
1.1 millert 51: #include <netdb.h>
52: #include <fcntl.h>
1.3 millert 53: #include <signal.h>
1.1 millert 54: #include <stdio.h>
1.61 deraadt 55: #include <stdarg.h>
1.19 deraadt 56: #include <errno.h>
1.1 millert 57: #include <stdlib.h>
58: #include <string.h>
59: #include <unistd.h>
1.40 fgsch 60: #include <util.h>
1.73 drahn 61: #include <resolv.h>
1.1 millert 62:
1.61 deraadt 63: #ifndef SMALL
64: #include <openssl/ssl.h>
65: #include <openssl/err.h>
1.78 martynas 66: #else /* !SMALL */
1.61 deraadt 67: #define SSL void
1.78 martynas 68: #endif /* !SMALL */
1.61 deraadt 69:
1.1 millert 70: #include "ftp_var.h"
1.86 martynas 71: #include "cmds.h"
1.1 millert 72:
1.38 millert 73: static int url_get(const char *, const char *, const char *);
74: void aborthttp(int);
75: void abortfile(int);
1.42 deraadt 76: char hextochar(const char *);
77: char *urldecode(const char *);
1.61 deraadt 78: int ftp_printf(FILE *, SSL *, const char *, ...) __attribute__((format(printf, 3, 4)));
79: char *ftp_readline(FILE *, SSL *, size_t *);
1.65 ray 80: size_t ftp_read(FILE *, SSL *, char *, size_t);
1.61 deraadt 81: #ifndef SMALL
1.81 espie 82: int proxy_connect(int, char *, char *);
1.61 deraadt 83: int SSL_vprintf(SSL *, const char *, va_list);
84: char *SSL_readline(SSL *, size_t *);
1.78 martynas 85: #endif /* !SMALL */
1.14 millert 86:
1.1 millert 87: #define FTP_URL "ftp://" /* ftp URL prefix */
88: #define HTTP_URL "http://" /* http URL prefix */
1.61 deraadt 89: #define HTTPS_URL "https://" /* https URL prefix */
1.22 deraadt 90: #define FILE_URL "file:" /* file URL prefix */
1.6 millert 91: #define FTP_PROXY "ftp_proxy" /* env var with ftp proxy location */
1.1 millert 92: #define HTTP_PROXY "http_proxy" /* env var with http proxy location */
93:
1.73 drahn 94: #define COOKIE_MAX_LEN 42
1.1 millert 95:
96: #define EMPTYSTRING(x) ((x) == NULL || (*(x) == '\0'))
97:
1.37 heko 98: static const char *at_encoding_warning =
1.53 deraadt 99: "Extra `@' characters in usernames and passwords should be encoded as %%40";
1.37 heko 100:
1.1 millert 101: jmp_buf httpabort;
102:
1.54 fgsch 103: static int redirect_loop;
104:
1.1 millert 105: /*
1.95 martynas 106: * Determine whether the character needs encoding, per RFC1738:
107: * - No corresponding graphic US-ASCII.
108: * - Unsafe characters.
109: */
110: static int
111: unsafe_char(const char *c)
112: {
113: const char *unsafe_chars = " <>\"#{}|\\^~[]`";
114:
115: /*
116: * No corresponding graphic US-ASCII.
117: * Control characters and octets not used in US-ASCII.
118: */
119: return (iscntrl(*c) || !isascii(*c) ||
120:
121: /*
122: * Unsafe characters.
123: * '%' is also unsafe, if is not followed by two
124: * hexadecimal digits.
125: */
126: strchr(unsafe_chars, *c) != NULL ||
127: (*c == '%' && (!isxdigit(*++c) || !isxdigit(*++c))));
128: }
129:
130: /*
131: * Encode given URL, per RFC1738.
132: * Allocate and return string to the caller.
133: */
134: static char *
135: url_encode(const char *path)
136: {
137: size_t i, length, new_length;
138: char *epath, *epathp;
139:
140: length = new_length = strlen(path);
141:
142: /*
143: * First pass:
144: * Count unsafe characters, and determine length of the
145: * final URL.
146: */
147: for (i = 0; i < length; i++)
148: if (unsafe_char(path + i))
149: new_length += 2;
150:
151: epath = epathp = malloc(new_length + 1); /* One more for '\0'. */
152: if (epath == NULL)
153: return NULL;
154:
155: /*
156: * Second pass:
157: * Encode, and copy final URL.
158: */
159: for (i = 0; i < length; i++)
160: if (unsafe_char(path + i)) {
161: snprintf(epathp, 4, "%%" "%02x", path[i]);
162: epathp += 3;
163: } else
164: *(epathp++) = path[i];
165:
166: *epathp = '\0';
167: return (epath);
168: }
169:
170: /*
1.6 millert 171: * Retrieve URL, via the proxy in $proxyvar if necessary.
1.1 millert 172: * Modifies the string argument given.
173: * Returns -1 on failure, 0 on success
174: */
1.14 millert 175: static int
1.50 deraadt 176: url_get(const char *origline, const char *proxyenv, const char *outfile)
1.1 millert 177: {
1.69 jsg 178: char pbuf[NI_MAXSERV], hbuf[NI_MAXHOST], *cp, *portnum, *path, ststr[4];
1.62 ray 179: char *hosttail, *cause = "unknown", *newline, *host, *port, *buf = NULL;
1.95 martynas 180: char *epath;
1.53 deraadt 181: int error, i, isftpurl = 0, isfileurl = 0, isredirect = 0, rval = -1;
1.25 itojun 182: struct addrinfo hints, *res0, *res;
1.34 millert 183: const char * volatile savefile;
1.62 ray 184: char * volatile proxyurl = NULL;
1.73 drahn 185: char *cookie = NULL;
1.53 deraadt 186: volatile int s = -1, out;
1.97 martynas 187: volatile sig_t oldintr, oldinti;
1.53 deraadt 188: FILE *fin = NULL;
1.1 millert 189: off_t hashbytes;
1.58 grunk 190: const char *errstr;
1.61 deraadt 191: size_t len, wlen;
192: #ifndef SMALL
193: char *sslpath = NULL, *sslhost = NULL;
1.70 deraadt 194: int ishttpsurl = 0;
1.61 deraadt 195: SSL_CTX *ssl_ctx = NULL;
1.78 martynas 196: #endif /* !SMALL */
1.61 deraadt 197: SSL *ssl = NULL;
1.70 deraadt 198: int status;
1.14 millert 199:
1.97 martynas 200: direction = "received";
201:
1.62 ray 202: newline = strdup(origline);
203: if (newline == NULL)
1.14 millert 204: errx(1, "Can't allocate memory to parse URL");
1.62 ray 205: if (strncasecmp(newline, HTTP_URL, sizeof(HTTP_URL) - 1) == 0)
206: host = newline + sizeof(HTTP_URL) - 1;
207: else if (strncasecmp(newline, FTP_URL, sizeof(FTP_URL) - 1) == 0) {
208: host = newline + sizeof(FTP_URL) - 1;
1.14 millert 209: isftpurl = 1;
1.62 ray 210: } else if (strncasecmp(newline, FILE_URL, sizeof(FILE_URL) - 1) == 0) {
211: host = newline + sizeof(FILE_URL) - 1;
1.22 deraadt 212: isfileurl = 1;
1.61 deraadt 213: #ifndef SMALL
1.62 ray 214: } else if (strncasecmp(newline, HTTPS_URL, sizeof(HTTPS_URL) - 1) == 0) {
215: host = newline + sizeof(HTTPS_URL) - 1;
1.61 deraadt 216: ishttpsurl = 1;
1.78 martynas 217: #endif /* !SMALL */
1.14 millert 218: } else
1.62 ray 219: errx(1, "url_get: Invalid URL '%s'", newline);
1.6 millert 220:
1.22 deraadt 221: if (isfileurl) {
222: path = host;
223: } else {
1.93 martynas 224: path = strchr(host, '/'); /* Find path */
1.22 deraadt 225: if (EMPTYSTRING(path)) {
1.93 martynas 226: if (outfile) { /* No slash, but */
227: path=strchr(host,'\0'); /* we have outfile. */
228: goto noslash;
229: }
1.22 deraadt 230: if (isftpurl)
231: goto noftpautologin;
1.94 martynas 232: warnx("No `/' after host (use -o): %s", origline);
1.22 deraadt 233: goto cleanup_url_get;
234: }
235: *path++ = '\0';
1.93 martynas 236: if (EMPTYSTRING(path) && !outfile) {
1.22 deraadt 237: if (isftpurl)
238: goto noftpautologin;
1.94 martynas 239: warnx("No filename after host (use -o): %s", origline);
1.22 deraadt 240: goto cleanup_url_get;
241: }
1.14 millert 242: }
1.1 millert 243:
1.93 martynas 244: noslash:
1.17 millert 245: if (outfile)
246: savefile = outfile;
1.93 martynas 247: else {
248: if (path[strlen(path) - 1] == '/') /* Consider no file */
249: savefile = NULL; /* after dir invalid. */
250: else
251: savefile = basename(path);
1.75 martynas 252: }
253:
1.14 millert 254: if (EMPTYSTRING(savefile)) {
255: if (isftpurl)
256: goto noftpautologin;
1.94 martynas 257: warnx("No filename after directory (use -o): %s", origline);
1.6 millert 258: goto cleanup_url_get;
1.14 millert 259: }
1.1 millert 260:
1.93 martynas 261: #ifndef SMALL
1.100 halex 262: if (resume && pipeout) {
1.93 martynas 263: warnx("can't append to stdout");
264: goto cleanup_url_get;
265: }
266: #endif /* !SMALL */
267:
1.59 uwe 268: if (!isfileurl && proxyenv != NULL) { /* use proxy */
1.61 deraadt 269: #ifndef SMALL
270: if (ishttpsurl) {
271: sslpath = strdup(path);
272: sslhost = strdup(host);
273: if (! sslpath || ! sslhost)
274: errx(1, "Can't allocate memory for https path/host.");
275: }
1.78 martynas 276: #endif /* !SMALL */
1.62 ray 277: proxyurl = strdup(proxyenv);
278: if (proxyurl == NULL)
1.14 millert 279: errx(1, "Can't allocate memory for proxy URL.");
1.62 ray 280: if (strncasecmp(proxyurl, HTTP_URL, sizeof(HTTP_URL) - 1) == 0)
281: host = proxyurl + sizeof(HTTP_URL) - 1;
282: else if (strncasecmp(proxyurl, FTP_URL, sizeof(FTP_URL) - 1) == 0)
283: host = proxyurl + sizeof(FTP_URL) - 1;
1.6 millert 284: else {
1.14 millert 285: warnx("Malformed proxy URL: %s", proxyenv);
1.6 millert 286: goto cleanup_url_get;
287: }
1.14 millert 288: if (EMPTYSTRING(host)) {
289: warnx("Malformed proxy URL: %s", proxyenv);
1.6 millert 290: goto cleanup_url_get;
1.14 millert 291: }
1.93 martynas 292: if (*--path == '\0')
293: *path = '/'; /* add / back to real path */
1.1 millert 294: path = strchr(host, '/'); /* remove trailing / on host */
1.42 deraadt 295: if (!EMPTYSTRING(path))
1.73 drahn 296: *path++ = '\0'; /* i guess this ++ is useless */
297:
298: path = strchr(host, '@'); /* look for credentials in proxy */
299: if (!EMPTYSTRING(path)) {
1.81 espie 300: *path = '\0';
1.73 drahn 301: cookie = strchr(host, ':');
302: if (EMPTYSTRING(cookie)) {
303: warnx("Malformed proxy URL: %s", proxyenv);
304: goto cleanup_url_get;
305: }
306: cookie = malloc(COOKIE_MAX_LEN);
1.81 espie 307: if (cookie == NULL)
308: errx(1, "out of memory");
309: if (b64_ntop(host, strlen(host), cookie, COOKIE_MAX_LEN) == -1)
310: errx(1, "error in base64 encoding");
311: *path = '@'; /* restore @ in proxyurl */
1.73 drahn 312: /*
1.81 espie 313: * This removes the password from proxyurl,
1.73 drahn 314: * filling with stars
315: */
1.81 espie 316: for (host = 1 + strchr(proxyurl + 5, ':'); *host != '@';
1.73 drahn 317: host++)
318: *host = '*';
319:
1.81 espie 320: host = path + 1;
1.73 drahn 321: }
1.62 ray 322: path = newline;
1.1 millert 323: }
324:
1.22 deraadt 325: if (isfileurl) {
326: struct stat st;
327:
328: s = open(path, O_RDONLY);
329: if (s == -1) {
330: warn("Can't open file %s", path);
331: goto cleanup_url_get;
332: }
333:
334: if (fstat(s, &st) == -1)
335: filesize = -1;
336: else
337: filesize = st.st_size;
338:
1.83 martynas 339: /* Open the output file. */
1.100 halex 340: if (!pipeout) {
1.75 martynas 341: #ifndef SMALL
342: if (resume)
1.82 deraadt 343: out = open(savefile, O_CREAT | O_WRONLY |
344: O_APPEND, 0666);
345:
1.75 martynas 346: else
1.78 martynas 347: #endif /* !SMALL */
1.75 martynas 348: out = open(savefile, O_CREAT | O_WRONLY |
349: O_TRUNC, 0666);
1.22 deraadt 350: if (out < 0) {
351: warn("Can't open %s", savefile);
352: goto cleanup_url_get;
353: }
354: } else
355: out = fileno(stdout);
356:
1.75 martynas 357: #ifndef SMALL
358: if (resume) {
359: if (fstat(out, &st) == -1) {
360: warn("Can't fstat %s", savefile);
361: goto cleanup_url_get;
362: }
363: if (lseek(s, st.st_size, SEEK_SET) == -1) {
364: warn("Can't lseek %s", path);
365: goto cleanup_url_get;
366: }
367: restart_point = st.st_size;
368: }
1.78 martynas 369: #endif /* !SMALL */
1.75 martynas 370:
1.22 deraadt 371: /* Trap signals */
372: oldintr = NULL;
1.97 martynas 373: oldinti = NULL;
1.22 deraadt 374: if (setjmp(httpabort)) {
375: if (oldintr)
376: (void)signal(SIGINT, oldintr);
1.97 martynas 377: if (oldinti)
378: (void)signal(SIGINFO, oldinti);
1.22 deraadt 379: goto cleanup_url_get;
380: }
381: oldintr = signal(SIGINT, abortfile);
1.42 deraadt 382:
1.22 deraadt 383: bytes = 0;
384: hashbytes = mark;
1.84 martynas 385: progressmeter(-1, path);
1.40 fgsch 386:
387: if ((buf = malloc(4096)) == NULL)
1.47 deraadt 388: errx(1, "Can't allocate memory for transfer buffer");
1.42 deraadt 389:
1.22 deraadt 390: /* Finally, suck down the file. */
391: i = 0;
1.97 martynas 392: oldinti = signal(SIGINFO, psummary);
1.40 fgsch 393: while ((len = read(s, buf, 4096)) > 0) {
1.22 deraadt 394: bytes += len;
395: for (cp = buf; len > 0; len -= i, cp += i) {
396: if ((i = write(out, cp, len)) == -1) {
397: warn("Writing %s", savefile);
1.97 martynas 398: signal(SIGINFO, oldinti);
1.22 deraadt 399: goto cleanup_url_get;
400: }
401: else if (i == 0)
402: break;
403: }
404: if (hash && !progress) {
405: while (bytes >= hashbytes) {
406: (void)putc('#', ttyout);
407: hashbytes += mark;
408: }
409: (void)fflush(ttyout);
410: }
411: }
1.97 martynas 412: signal(SIGINFO, oldinti);
1.22 deraadt 413: if (hash && !progress && bytes > 0) {
414: if (bytes < mark)
415: (void)putc('#', ttyout);
416: (void)putc('\n', ttyout);
417: (void)fflush(ttyout);
418: }
419: if (len != 0) {
420: warn("Reading from file");
421: goto cleanup_url_get;
422: }
1.84 martynas 423: progressmeter(1, NULL);
1.22 deraadt 424: if (verbose)
1.97 martynas 425: ptransfer(0);
1.22 deraadt 426: (void)signal(SIGINT, oldintr);
1.42 deraadt 427:
1.40 fgsch 428: rval = 0;
429: goto cleanup_url_get;
1.22 deraadt 430: }
431:
1.28 itojun 432: if (*host == '[' && (hosttail = strrchr(host, ']')) != NULL &&
433: (hosttail[1] == '\0' || hosttail[1] == ':')) {
434: host++;
435: *hosttail++ = '\0';
436: } else
437: hosttail = host;
438:
439: portnum = strrchr(hosttail, ':'); /* find portnum */
1.1 millert 440: if (portnum != NULL)
441: *portnum++ = '\0';
442:
1.80 martynas 443: #ifndef SMALL
1.1 millert 444: if (debug)
1.10 deraadt 445: fprintf(ttyout, "host %s, port %s, path %s, save as %s.\n",
1.1 millert 446: host, portnum, path, savefile);
1.80 martynas 447: #endif /* !SMALL */
1.1 millert 448:
1.25 itojun 449: memset(&hints, 0, sizeof(hints));
1.39 deraadt 450: hints.ai_family = family;
1.25 itojun 451: hints.ai_socktype = SOCK_STREAM;
1.61 deraadt 452: #ifndef SMALL
453: port = portnum ? portnum : (ishttpsurl ? httpsport : httpport);
1.78 martynas 454: #else /* !SMALL */
1.25 itojun 455: port = portnum ? portnum : httpport;
1.78 martynas 456: #endif /* !SMALL */
1.25 itojun 457: error = getaddrinfo(host, port, &hints, &res0);
1.61 deraadt 458: /*
459: * If the services file is corrupt/missing, fall back
460: * on our hard-coded defines.
461: */
1.30 deraadt 462: if (error == EAI_SERVICE && port == httpport) {
463: snprintf(pbuf, sizeof(pbuf), "%d", HTTP_PORT);
464: error = getaddrinfo(host, pbuf, &hints, &res0);
1.61 deraadt 465: #ifndef SMALL
466: } else if (error == EAI_SERVICE && port == httpsport) {
467: snprintf(pbuf, sizeof(pbuf), "%d", HTTPS_PORT);
468: error = getaddrinfo(host, pbuf, &hints, &res0);
1.78 martynas 469: #endif /* !SMALL */
1.30 deraadt 470: }
1.25 itojun 471: if (error) {
472: warnx("%s: %s", gai_strerror(error), host);
473: goto cleanup_url_get;
1.1 millert 474: }
475:
1.25 itojun 476: s = -1;
477: for (res = res0; res; res = res->ai_next) {
1.44 itojun 478: if (getnameinfo(res->ai_addr, res->ai_addrlen, hbuf,
479: sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0)
480: strlcpy(hbuf, "(unknown)", sizeof(hbuf));
1.41 deraadt 481: if (verbose)
1.44 itojun 482: fprintf(ttyout, "Trying %s...\n", hbuf);
1.14 millert 483:
1.25 itojun 484: s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
485: if (s == -1) {
486: cause = "socket";
487: continue;
1.1 millert 488: }
489:
1.25 itojun 490: again:
491: if (connect(s, res->ai_addr, res->ai_addrlen) < 0) {
1.57 otto 492: int save_errno;
493:
1.25 itojun 494: if (errno == EINTR)
495: goto again;
1.57 otto 496: save_errno = errno;
1.25 itojun 497: close(s);
1.57 otto 498: errno = save_errno;
1.25 itojun 499: s = -1;
500: cause = "connect";
1.19 deraadt 501: continue;
502: }
1.25 itojun 503:
1.29 itojun 504: /* get port in numeric */
505: if (getnameinfo(res->ai_addr, res->ai_addrlen, NULL, 0,
506: pbuf, sizeof(pbuf), NI_NUMERICSERV) == 0)
507: port = pbuf;
508: else
509: port = NULL;
510:
1.61 deraadt 511: #ifndef SMALL
512: if (proxyenv && sslhost)
1.81 espie 513: proxy_connect(s, sslhost, cookie);
1.78 martynas 514: #endif /* !SMALL */
1.25 itojun 515: break;
516: }
517: freeaddrinfo(res0);
518: if (s < 0) {
1.33 millert 519: warn("%s", cause);
1.6 millert 520: goto cleanup_url_get;
1.1 millert 521: }
522:
1.61 deraadt 523: #ifndef SMALL
524: if (ishttpsurl) {
525: if (proxyenv && sslpath) {
526: ishttpsurl = 0;
1.62 ray 527: proxyurl = NULL;
1.61 deraadt 528: path = sslpath;
529: }
530: SSL_library_init();
531: SSL_load_error_strings();
532: SSLeay_add_ssl_algorithms();
533: ssl_ctx = SSL_CTX_new(SSLv23_client_method());
534: ssl = SSL_new(ssl_ctx);
535: if (ssl == NULL || ssl_ctx == NULL) {
536: ERR_print_errors_fp(ttyout);
537: goto cleanup_url_get;
538: }
539: if (SSL_set_fd(ssl, s) == 0) {
540: ERR_print_errors_fp(ttyout);
541: goto cleanup_url_get;
542: }
543: if (SSL_connect(ssl) <= 0) {
544: ERR_print_errors_fp(ttyout);
1.72 ray 545: goto cleanup_url_get;
1.61 deraadt 546: }
547: } else {
548: fin = fdopen(s, "r+");
549: }
1.78 martynas 550: #else /* !SMALL */
1.40 fgsch 551: fin = fdopen(s, "r+");
1.78 martynas 552: #endif /* !SMALL */
1.40 fgsch 553:
1.55 fgsch 554: if (verbose)
555: fprintf(ttyout, "Requesting %s", origline);
1.95 martynas 556:
1.1 millert 557: /*
1.40 fgsch 558: * Construct and send the request. Proxy requests don't want leading /.
1.1 millert 559: */
1.74 pyr 560: #ifndef SMALL
561: cookie_get(host, path, ishttpsurl, &buf);
1.78 martynas 562: #endif /* !SMALL */
1.95 martynas 563:
564: epath = url_encode(path);
565: if (epath == NULL)
566: return (-1);
1.62 ray 567: if (proxyurl) {
1.55 fgsch 568: if (verbose)
1.81 espie 569: fprintf(ttyout, " (via %s)\n", proxyurl);
1.32 itojun 570: /*
571: * Host: directive must use the destination host address for
572: * the original URI (path). We do not attach it at this moment.
573: */
1.73 drahn 574: if (cookie)
575: ftp_printf(fin, ssl, "GET %s HTTP/1.0\r\n"
1.74 pyr 576: "Proxy-Authorization: Basic %s%s\r\n%s\r\n\r\n",
1.95 martynas 577: epath, cookie, buf ? buf : "", HTTP_USER_AGENT);
1.73 drahn 578: else
1.74 pyr 579: ftp_printf(fin, ssl, "GET %s HTTP/1.0\r\n%s%s\r\n\r\n",
1.95 martynas 580: epath, buf ? buf : "", HTTP_USER_AGENT);
1.73 drahn 581:
1.28 itojun 582: } else {
1.90 martynas 583: #ifndef SMALL
584: if (resume) {
585: struct stat stbuf;
586:
587: if (stat(savefile, &stbuf) == 0)
588: restart_point = stbuf.st_size;
589: else
590: restart_point = 0;
591: }
592: #endif /* !SMALL */
1.95 martynas 593: ftp_printf(fin, ssl, "GET /%s %s\r\nHost: ", epath,
1.75 martynas 594: #ifndef SMALL
1.99 halex 595: restart_point ? "HTTP/1.1\r\nConnection: close" :
1.78 martynas 596: #endif /* !SMALL */
1.75 martynas 597: "HTTP/1.0");
1.32 itojun 598: if (strchr(host, ':')) {
599: char *h, *p;
600:
1.55 fgsch 601: /*
602: * strip off scoped address portion, since it's
603: * local to node
604: */
1.32 itojun 605: h = strdup(host);
606: if (h == NULL)
607: errx(1, "Can't allocate memory.");
608: if ((p = strchr(h, '%')) != NULL)
609: *p = '\0';
1.61 deraadt 610: ftp_printf(fin, ssl, "[%s]", h);
1.32 itojun 611: free(h);
1.55 fgsch 612: } else
1.61 deraadt 613: ftp_printf(fin, ssl, "%s", host);
1.55 fgsch 614:
615: /*
616: * Send port number only if it's specified and does not equal
617: * 80. Some broken HTTP servers get confused if you explicitly
618: * send them the port number.
619: */
1.61 deraadt 620: #ifndef SMALL
621: if (port && strcmp(port, (ishttpsurl ? "443" : "80")) != 0)
622: ftp_printf(fin, ssl, ":%s", port);
1.90 martynas 623: if (restart_point)
1.75 martynas 624: ftp_printf(fin, ssl, "\r\nRange: bytes=%lld-",
625: (long long)restart_point);
1.78 martynas 626: #else /* !SMALL */
1.55 fgsch 627: if (port && strcmp(port, "80") != 0)
1.61 deraadt 628: ftp_printf(fin, ssl, ":%s", port);
1.78 martynas 629: #endif /* !SMALL */
1.74 pyr 630: ftp_printf(fin, ssl, "\r\n%s%s\r\n\r\n",
631: buf ? buf : "", HTTP_USER_AGENT);
1.55 fgsch 632: if (verbose)
633: fprintf(ttyout, "\n");
1.28 itojun 634: }
1.95 martynas 635: free(epath);
1.74 pyr 636:
637: #ifndef SMALL
638: free(buf);
1.78 martynas 639: #endif /* !SMALL */
1.74 pyr 640: buf = NULL;
641:
1.61 deraadt 642: if (fin != NULL && fflush(fin) == EOF) {
1.14 millert 643: warn("Writing HTTP request");
1.6 millert 644: goto cleanup_url_get;
1.1 millert 645: }
1.61 deraadt 646: if ((buf = ftp_readline(fin, ssl, &len)) == NULL) {
1.40 fgsch 647: warn("Receiving HTTP reply");
648: goto cleanup_url_get;
1.1 millert 649: }
1.40 fgsch 650:
651: while (len > 0 && (buf[len-1] == '\r' || buf[len-1] == '\n'))
652: buf[--len] = '\0';
1.80 martynas 653: #ifndef SMALL
1.40 fgsch 654: if (debug)
655: fprintf(ttyout, "received '%s'\n", buf);
1.80 martynas 656: #endif /* !SMALL */
1.40 fgsch 657:
1.1 millert 658: cp = strchr(buf, ' ');
659: if (cp == NULL)
660: goto improper;
661: else
662: cp++;
1.69 jsg 663:
664: strlcpy(ststr, cp, sizeof(ststr));
1.75 martynas 665: status = strtonum(ststr, 200, 416, &errstr);
1.69 jsg 666: if (errstr) {
667: warnx("Error retrieving file: %s", cp);
668: goto cleanup_url_get;
669: }
670:
671: switch (status) {
672: case 200: /* OK */
1.75 martynas 673: #ifndef SMALL
1.98 phessler 674: /*
675: * When we request a partial file, and we receive an HTTP 200
676: * it is a good indication that the server doesn't support
677: * range requests, and is about to send us the entire file.
678: * If the restart_point == 0, then we are not actually
679: * requesting a partial file, and an HTTP 200 is appropriate.
680: */
681: if (resume && restart_point != 0) {
682: warnx("Server does not support resume.");
683: restart_point = resume = 0;
684: }
1.101 ! halex 685: /* FALLTHROUGH */
1.75 martynas 686: case 206: /* Partial Content */
1.91 halex 687: #endif /* !SMALL */
1.69 jsg 688: break;
689: case 301: /* Moved Permanently */
690: case 302: /* Found */
691: case 303: /* See Other */
692: case 307: /* Temporary Redirect */
1.40 fgsch 693: isredirect++;
1.54 fgsch 694: if (redirect_loop++ > 10) {
695: warnx("Too many redirections requested");
696: goto cleanup_url_get;
697: }
1.69 jsg 698: break;
1.75 martynas 699: #ifndef SMALL
700: case 416: /* Requested Range Not Satisfiable */
701: warnx("File is already fully retrieved.");
702: goto cleanup_url_get;
1.78 martynas 703: #endif /* !SMALL */
1.69 jsg 704: default:
1.1 millert 705: warnx("Error retrieving file: %s", cp);
1.6 millert 706: goto cleanup_url_get;
1.1 millert 707: }
708:
709: /*
710: * Read the rest of the header.
711: */
1.40 fgsch 712: free(buf);
713: filesize = -1;
714:
1.62 ray 715: for (;;) {
1.61 deraadt 716: if ((buf = ftp_readline(fin, ssl, &len)) == NULL) {
1.40 fgsch 717: warn("Receiving HTTP reply");
718: goto cleanup_url_get;
719: }
1.61 deraadt 720:
1.40 fgsch 721: while (len > 0 && (buf[len-1] == '\r' || buf[len-1] == '\n'))
722: buf[--len] = '\0';
723: if (len == 0)
1.1 millert 724: break;
1.80 martynas 725: #ifndef SMALL
1.40 fgsch 726: if (debug)
727: fprintf(ttyout, "received '%s'\n", buf);
1.80 martynas 728: #endif /* !SMALL */
1.1 millert 729:
1.40 fgsch 730: /* Look for some headers */
731: cp = buf;
1.1 millert 732: #define CONTENTLEN "Content-Length: "
1.40 fgsch 733: if (strncasecmp(cp, CONTENTLEN, sizeof(CONTENTLEN) - 1) == 0) {
734: cp += sizeof(CONTENTLEN) - 1;
1.58 grunk 735: filesize = strtonum(cp, 0, LLONG_MAX, &errstr);
736: if (errstr != NULL)
1.40 fgsch 737: goto improper;
1.75 martynas 738: #ifndef SMALL
1.90 martynas 739: if (restart_point)
1.75 martynas 740: filesize += restart_point;
1.78 martynas 741: #endif /* !SMALL */
1.40 fgsch 742: #define LOCATION "Location: "
743: } else if (isredirect &&
744: strncasecmp(cp, LOCATION, sizeof(LOCATION) - 1) == 0) {
745: cp += sizeof(LOCATION) - 1;
746: if (verbose)
747: fprintf(ttyout, "Redirected to %s\n", cp);
748: if (fin != NULL)
749: fclose(fin);
750: else if (s != -1)
751: close(s);
1.67 steven 752: free(proxyurl);
1.62 ray 753: free(newline);
1.81 espie 754: free(cookie);
1.96 sthen 755: rval = url_get(cp, proxyenv, savefile);
1.67 steven 756: free(buf);
1.40 fgsch 757: return (rval);
758: }
1.1 millert 759: }
760:
1.17 millert 761: /* Open the output file. */
1.100 halex 762: if (!pipeout) {
1.75 martynas 763: #ifndef SMALL
764: if (resume)
1.83 martynas 765: out = open(savefile, O_CREAT | O_WRONLY | O_APPEND,
766: 0666);
1.75 martynas 767: else
1.78 martynas 768: #endif /* !SMALL */
1.75 martynas 769: out = open(savefile, O_CREAT | O_WRONLY | O_TRUNC,
770: 0666);
1.10 deraadt 771: if (out < 0) {
772: warn("Can't open %s", savefile);
773: goto cleanup_url_get;
774: }
775: } else
1.17 millert 776: out = fileno(stdout);
1.1 millert 777:
778: /* Trap signals */
779: oldintr = NULL;
1.97 martynas 780: oldinti = NULL;
1.1 millert 781: if (setjmp(httpabort)) {
782: if (oldintr)
1.2 millert 783: (void)signal(SIGINT, oldintr);
1.97 martynas 784: if (oldinti)
785: (void)signal(SIGINFO, oldinti);
1.6 millert 786: goto cleanup_url_get;
1.1 millert 787: }
788: oldintr = signal(SIGINT, aborthttp);
789:
790: bytes = 0;
791: hashbytes = mark;
1.84 martynas 792: progressmeter(-1, path);
1.43 millert 793:
794: free(buf);
1.1 millert 795:
796: /* Finally, suck down the file. */
1.40 fgsch 797: if ((buf = malloc(4096)) == NULL)
1.47 deraadt 798: errx(1, "Can't allocate memory for transfer buffer");
1.1 millert 799: i = 0;
1.61 deraadt 800: len = 1;
1.97 martynas 801: oldinti = signal(SIGINFO, psummary);
1.61 deraadt 802: while (len > 0) {
803: len = ftp_read(fin, ssl, buf, 4096);
1.1 millert 804: bytes += len;
1.61 deraadt 805: for (cp = buf, wlen = len; wlen > 0; wlen -= i, cp += i) {
806: if ((i = write(out, cp, wlen)) == -1) {
1.1 millert 807: warn("Writing %s", savefile);
1.97 martynas 808: signal(SIGINFO, oldinti);
1.6 millert 809: goto cleanup_url_get;
1.1 millert 810: }
811: else if (i == 0)
812: break;
813: }
814: if (hash && !progress) {
815: while (bytes >= hashbytes) {
1.10 deraadt 816: (void)putc('#', ttyout);
1.1 millert 817: hashbytes += mark;
818: }
1.10 deraadt 819: (void)fflush(ttyout);
1.1 millert 820: }
821: }
1.97 martynas 822: signal(SIGINFO, oldinti);
1.1 millert 823: if (hash && !progress && bytes > 0) {
824: if (bytes < mark)
1.10 deraadt 825: (void)putc('#', ttyout);
826: (void)putc('\n', ttyout);
827: (void)fflush(ttyout);
1.1 millert 828: }
829: if (len != 0) {
830: warn("Reading from socket");
1.6 millert 831: goto cleanup_url_get;
1.1 millert 832: }
1.84 martynas 833: progressmeter(1, NULL);
1.75 martynas 834: if (
835: #ifndef SMALL
836: !resume &&
1.78 martynas 837: #endif /* !SMALL */
1.75 martynas 838: filesize != -1 && len == 0 && bytes != filesize) {
1.24 deraadt 839: if (verbose)
840: fputs("Read short file.\n", ttyout);
841: goto cleanup_url_get;
842: }
843:
1.1 millert 844: if (verbose)
1.97 martynas 845: ptransfer(0);
1.2 millert 846: (void)signal(SIGINT, oldintr);
1.1 millert 847:
1.40 fgsch 848: rval = 0;
849: goto cleanup_url_get;
1.1 millert 850:
1.14 millert 851: noftpautologin:
852: warnx(
853: "Auto-login using ftp URLs isn't supported when using $ftp_proxy");
854: goto cleanup_url_get;
855:
1.1 millert 856: improper:
1.8 millert 857: warnx("Improper response from %s", host);
1.14 millert 858:
1.6 millert 859: cleanup_url_get:
1.61 deraadt 860: #ifndef SMALL
861: if (ssl) {
862: SSL_shutdown(ssl);
863: SSL_free(ssl);
864: }
1.78 martynas 865: #endif /* !SMALL */
1.40 fgsch 866: if (fin != NULL)
867: fclose(fin);
868: else if (s != -1)
1.1 millert 869: close(s);
1.67 steven 870: free(buf);
871: free(proxyurl);
1.62 ray 872: free(newline);
1.81 espie 873: free(cookie);
1.40 fgsch 874: return (rval);
1.1 millert 875: }
876:
877: /*
878: * Abort a http retrieval
879: */
1.51 deraadt 880: /* ARGSUSED */
1.1 millert 881: void
1.51 deraadt 882: aborthttp(int signo)
1.1 millert 883: {
884:
885: alarmtimer(0);
1.10 deraadt 886: fputs("\nhttp fetch aborted.\n", ttyout);
887: (void)fflush(ttyout);
1.1 millert 888: longjmp(httpabort, 1);
889: }
890:
891: /*
1.22 deraadt 892: * Abort a http retrieval
893: */
1.51 deraadt 894: /* ARGSUSED */
1.22 deraadt 895: void
1.51 deraadt 896: abortfile(int signo)
1.22 deraadt 897: {
898:
899: alarmtimer(0);
900: fputs("\nfile fetch aborted.\n", ttyout);
901: (void)fflush(ttyout);
902: longjmp(httpabort, 1);
903: }
904:
905: /*
1.1 millert 906: * Retrieve multiple files from the command line, transferring
907: * files of the form "host:path", "ftp://host/path" using the
908: * ftp protocol, and files of the form "http://host/path" using
909: * the http protocol.
1.2 millert 910: * If path has a trailing "/", then return (-1);
1.1 millert 911: * the path will be cd-ed into and the connection remains open,
912: * and the function will return -1 (to indicate the connection
913: * is alive).
914: * If an error occurs the return value will be the offset+1 in
915: * argv[] of the file that caused a problem (i.e, argv[x]
916: * returns x+1)
917: * Otherwise, 0 is returned if all files retrieved successfully.
918: */
919: int
1.50 deraadt 920: auto_fetch(int argc, char *argv[], char *outfile)
1.1 millert 921: {
922: char *xargv[5];
1.62 ray 923: char *cp, *url, *host, *dir, *file, *portnum;
924: char *username, *pass, *pathstart;
1.6 millert 925: char *ftpproxy, *httpproxy;
1.14 millert 926: int rval, xargc;
927: volatile int argpos;
1.49 krw 928: int dirhasglob, filehasglob, oautologin;
1.14 millert 929: char rempath[MAXPATHLEN];
1.1 millert 930:
931: argpos = 0;
932:
933: if (setjmp(toplevel)) {
934: if (connected)
935: disconnect(0, NULL);
1.2 millert 936: return (argpos + 1);
1.1 millert 937: }
1.3 millert 938: (void)signal(SIGINT, (sig_t)intr);
939: (void)signal(SIGPIPE, (sig_t)lostpeer);
1.1 millert 940:
1.45 millert 941: if ((ftpproxy = getenv(FTP_PROXY)) != NULL && *ftpproxy == '\0')
942: ftpproxy = NULL;
943: if ((httpproxy = getenv(HTTP_PROXY)) != NULL && *httpproxy == '\0')
944: httpproxy = NULL;
1.6 millert 945:
1.1 millert 946: /*
947: * Loop through as long as there's files to fetch.
948: */
1.62 ray 949: for (rval = 0; (rval == 0) && (argpos < argc); free(url), argpos++) {
1.1 millert 950: if (strchr(argv[argpos], ':') == NULL)
951: break;
1.62 ray 952: host = dir = file = portnum = username = pass = NULL;
1.1 millert 953:
954: /*
955: * We muck with the string, so we make a copy.
956: */
1.62 ray 957: url = strdup(argv[argpos]);
958: if (url == NULL)
1.1 millert 959: errx(1, "Can't allocate memory for auto-fetch.");
960:
961: /*
962: * Try HTTP URL-style arguments first.
963: */
1.62 ray 964: if (strncasecmp(url, HTTP_URL, sizeof(HTTP_URL) - 1) == 0 ||
1.61 deraadt 965: #ifndef SMALL
966: /* even if we compiled without SSL, url_get will check */
1.62 ray 967: strncasecmp(url, HTTPS_URL, sizeof(HTTPS_URL) -1) == 0 ||
1.78 martynas 968: #endif /* !SMALL */
1.62 ray 969: strncasecmp(url, FILE_URL, sizeof(FILE_URL) - 1) == 0) {
1.54 fgsch 970: redirect_loop = 0;
1.62 ray 971: if (url_get(url, httpproxy, outfile) == -1)
1.1 millert 972: rval = argpos + 1;
973: continue;
974: }
975:
976: /*
1.6 millert 977: * Try FTP URL-style arguments next. If ftpproxy is
978: * set, use url_get() instead of standard ftp.
979: * Finally, try host:file.
1.1 millert 980: */
1.62 ray 981: host = url;
982: if (strncasecmp(url, FTP_URL, sizeof(FTP_URL) - 1) == 0) {
1.37 heko 983: char *passend, *passagain, *userend;
1.31 itojun 984:
1.6 millert 985: if (ftpproxy) {
1.62 ray 986: if (url_get(url, ftpproxy, outfile) == -1)
1.6 millert 987: rval = argpos + 1;
988: continue;
989: }
1.1 millert 990: host += sizeof(FTP_URL) - 1;
1.8 millert 991: dir = strchr(host, '/');
1.1 millert 992:
1.8 millert 993: /* Look for [user:pass@]host[:port] */
1.31 itojun 994:
995: /* check if we have "user:pass@" */
1.37 heko 996: userend = strchr(host, ':');
1.31 itojun 997: passend = strchr(host, '@');
998: if (passend && userend && userend < passend &&
999: (!dir || passend < dir)) {
1.62 ray 1000: username = host;
1.31 itojun 1001: pass = userend + 1;
1002: host = passend + 1;
1003: *userend = *passend = '\0';
1.37 heko 1004: passagain = strchr(host, '@');
1.42 deraadt 1005: if (strchr(pass, '@') != NULL ||
1.37 heko 1006: (passagain != NULL && passagain < dir)) {
1007: warnx(at_encoding_warning);
1008: goto bad_ftp_url;
1.42 deraadt 1009: }
1.31 itojun 1010:
1.77 martynas 1011: if (EMPTYSTRING(username)) {
1.11 millert 1012: bad_ftp_url:
1.31 itojun 1013: warnx("Invalid URL: %s", argv[argpos]);
1014: rval = argpos + 1;
1015: continue;
1016: }
1.62 ray 1017: username = urldecode(username);
1.37 heko 1018: pass = urldecode(pass);
1.8 millert 1019: }
1.31 itojun 1020:
1021: #ifdef INET6
1022: /* check [host]:port, or [host] */
1023: if (host[0] == '[') {
1024: cp = strchr(host, ']');
1025: if (cp && (!dir || cp < dir)) {
1026: if (cp + 1 == dir || cp[1] == ':') {
1027: host++;
1028: *cp++ = '\0';
1029: } else
1030: cp = NULL;
1031: } else
1032: cp = host;
1.25 itojun 1033: } else
1034: cp = host;
1.31 itojun 1035: #else
1036: cp = host;
1.25 itojun 1037: #endif
1.31 itojun 1038:
1039: /* split off host[:port] if there is */
1040: if (cp) {
1041: portnum = strchr(cp, ':');
1.52 henning 1042: pathstart = strchr(cp, '/');
1043: /* : in path is not a port # indicator */
1044: if (portnum && pathstart &&
1045: pathstart < portnum)
1046: portnum = NULL;
1047:
1.31 itojun 1048: if (!portnum)
1049: ;
1050: else {
1051: if (!dir)
1052: ;
1053: else if (portnum + 1 < dir) {
1054: *portnum++ = '\0';
1055: /*
1056: * XXX should check if portnum
1057: * is decimal number
1058: */
1059: } else {
1060: /* empty portnum */
1061: goto bad_ftp_url;
1062: }
1063: }
1064: } else
1065: portnum = NULL;
1.8 millert 1066: } else { /* classic style `host:file' */
1067: dir = strchr(host, ':');
1068: }
1.1 millert 1069: if (EMPTYSTRING(host)) {
1070: rval = argpos + 1;
1071: continue;
1072: }
1073:
1074: /*
1.9 millert 1075: * If dir is NULL, the file wasn't specified
1.1 millert 1076: * (URL looked something like ftp://host)
1077: */
1.8 millert 1078: if (dir != NULL)
1079: *dir++ = '\0';
1.1 millert 1080:
1081: /*
1082: * Extract the file and (if present) directory name.
1083: */
1.42 deraadt 1084: if (!EMPTYSTRING(dir)) {
1.8 millert 1085: cp = strrchr(dir, '/');
1.1 millert 1086: if (cp != NULL) {
1087: *cp++ = '\0';
1088: file = cp;
1089: } else {
1090: file = dir;
1091: dir = NULL;
1092: }
1093: }
1.80 martynas 1094: #ifndef SMALL
1.1 millert 1095: if (debug)
1.42 deraadt 1096: fprintf(ttyout,
1097: "user %s:%s host %s port %s dir %s file %s\n",
1.76 martynas 1098: username, pass ? "XXXX" : NULL, host, portnum,
1099: dir, file);
1.80 martynas 1100: #endif /* !SMALL */
1.1 millert 1101:
1102: /*
1.49 krw 1103: * Set up the connection.
1.1 millert 1104: */
1.49 krw 1105: if (connected)
1106: disconnect(0, NULL);
1107: xargv[0] = __progname;
1108: xargv[1] = host;
1.8 millert 1109: xargv[2] = NULL;
1.49 krw 1110: xargc = 2;
1111: if (!EMPTYSTRING(portnum)) {
1112: xargv[2] = portnum;
1113: xargv[3] = NULL;
1114: xargc = 3;
1115: }
1116: oautologin = autologin;
1.88 martynas 1117: if (username == NULL)
1.89 halex 1118: anonftp = 1;
1119: else {
1120: anonftp = 0;
1.49 krw 1121: autologin = 0;
1.89 halex 1122: }
1.49 krw 1123: setpeer(xargc, xargv);
1124: autologin = oautologin;
1.87 martynas 1125: if (connected == 0 ||
1126: (connected == 1 && autologin && (username == NULL ||
1127: !ftp_login(host, username, pass)))) {
1.49 krw 1128: warnx("Can't connect or login to host `%s'", host);
1.8 millert 1129: rval = argpos + 1;
1130: continue;
1.1 millert 1131: }
1.49 krw 1132:
1133: /* Always use binary transfers. */
1134: setbinary(0, NULL);
1.1 millert 1135:
1.4 millert 1136: dirhasglob = filehasglob = 0;
1137: if (doglob) {
1.42 deraadt 1138: if (!EMPTYSTRING(dir) &&
1.4 millert 1139: strpbrk(dir, "*?[]{}") != NULL)
1140: dirhasglob = 1;
1.42 deraadt 1141: if (!EMPTYSTRING(file) &&
1.4 millert 1142: strpbrk(file, "*?[]{}") != NULL)
1143: filehasglob = 1;
1144: }
1145:
1.1 millert 1146: /* Change directories, if necessary. */
1.42 deraadt 1147: if (!EMPTYSTRING(dir) && !dirhasglob) {
1.1 millert 1148: xargv[0] = "cd";
1149: xargv[1] = dir;
1150: xargv[2] = NULL;
1151: cd(2, xargv);
1.42 deraadt 1152: if (!dirchange) {
1.1 millert 1153: rval = argpos + 1;
1154: continue;
1155: }
1156: }
1157:
1158: if (EMPTYSTRING(file)) {
1.86 martynas 1159: #ifndef SMALL
1.1 millert 1160: rval = -1;
1.86 martynas 1161: #else /* !SMALL */
1162: recvrequest("NLST", "-", NULL, "w", 0, 0);
1163: rval = 0;
1164: #endif /* !SMALL */
1.1 millert 1165: continue;
1166: }
1167:
1.21 marc 1168: if (verbose)
1.10 deraadt 1169: fprintf(ttyout, "Retrieving %s/%s\n", dir ? dir : "", file);
1.1 millert 1170:
1.4 millert 1171: if (dirhasglob) {
1172: snprintf(rempath, sizeof(rempath), "%s/%s", dir, file);
1173: file = rempath;
1174: }
1175:
1176: /* Fetch the file(s). */
1.10 deraadt 1177: xargc = 2;
1.1 millert 1178: xargv[0] = "get";
1179: xargv[1] = file;
1180: xargv[2] = NULL;
1.4 millert 1181: if (dirhasglob || filehasglob) {
1182: int ointeractive;
1183:
1184: ointeractive = interactive;
1185: interactive = 0;
1186: xargv[0] = "mget";
1.78 martynas 1187: #ifndef SMALL
1188: if (resume) {
1189: xargc = 3;
1190: xargv[1] = "-c";
1191: xargv[2] = file;
1192: xargv[3] = NULL;
1193: }
1194: #endif /* !SMALL */
1.10 deraadt 1195: mget(xargc, xargv);
1.5 millert 1196: interactive = ointeractive;
1.10 deraadt 1197: } else {
1.17 millert 1198: if (outfile != NULL) {
1199: xargv[2] = outfile;
1200: xargv[3] = NULL;
1.10 deraadt 1201: xargc++;
1202: }
1.75 martynas 1203: #ifndef SMALL
1204: if (resume)
1205: reget(xargc, xargv);
1206: else
1.78 martynas 1207: #endif /* !SMALL */
1.75 martynas 1208: get(xargc, xargv);
1.10 deraadt 1209: }
1.1 millert 1210:
1.4 millert 1211: if ((code / 100) != COMPLETE)
1.1 millert 1212: rval = argpos + 1;
1213: }
1214: if (connected && rval != -1)
1215: disconnect(0, NULL);
1216: return (rval);
1.37 heko 1217: }
1218:
1219: char *
1.50 deraadt 1220: urldecode(const char *str)
1.37 heko 1221: {
1.53 deraadt 1222: char *ret, c;
1223: int i, reallen;
1.37 heko 1224:
1.53 deraadt 1225: if (str == NULL)
1226: return NULL;
1227: if ((ret = malloc(strlen(str)+1)) == NULL)
1228: err(1, "Can't allocate memory for URL decoding");
1229: for (i = 0, reallen = 0; str[i] != '\0'; i++, reallen++, ret++) {
1230: c = str[i];
1231: if (c == '+') {
1232: *ret = ' ';
1233: continue;
1234: }
1.61 deraadt 1235:
1236: /* Cannot use strtol here because next char
1237: * after %xx may be a digit.
1238: */
1.53 deraadt 1239: if (c == '%' && isxdigit(str[i+1]) && isxdigit(str[i+2])) {
1240: *ret = hextochar(&str[i+1]);
1241: i+=2;
1242: continue;
1243: }
1244: *ret = c;
1245: }
1246: *ret = '\0';
1247:
1248: return ret-reallen;
1.37 heko 1249: }
1250:
1251: char
1.50 deraadt 1252: hextochar(const char *str)
1.37 heko 1253: {
1.53 deraadt 1254: char c, ret;
1.37 heko 1255:
1.53 deraadt 1256: c = str[0];
1257: ret = c;
1258: if (isalpha(c))
1259: ret -= isupper(c) ? 'A' - 10 : 'a' - 10;
1260: else
1261: ret -= '0';
1262: ret *= 16;
1263:
1264: c = str[1];
1265: ret += c;
1266: if (isalpha(c))
1267: ret -= isupper(c) ? 'A' - 10 : 'a' - 10;
1268: else
1269: ret -= '0';
1270: return ret;
1.25 itojun 1271: }
1272:
1273: int
1.50 deraadt 1274: isurl(const char *p)
1.25 itojun 1275: {
1.27 millert 1276:
1.26 deraadt 1277: if (strncasecmp(p, FTP_URL, sizeof(FTP_URL) - 1) == 0 ||
1278: strncasecmp(p, HTTP_URL, sizeof(HTTP_URL) - 1) == 0 ||
1.61 deraadt 1279: #ifndef SMALL
1280: strncasecmp(p, HTTPS_URL, sizeof(HTTPS_URL) - 1) == 0 ||
1.78 martynas 1281: #endif /* !SMALL */
1.27 millert 1282: strncasecmp(p, FILE_URL, sizeof(FILE_URL) - 1) == 0 ||
1283: strstr(p, ":/"))
1284: return (1);
1285: return (0);
1.1 millert 1286: }
1.61 deraadt 1287:
1288: char *
1289: ftp_readline(FILE *fp, SSL *ssl, size_t *lenp)
1290: {
1291: if (fp != NULL)
1292: return fparseln(fp, lenp, NULL, "\0\0\0", 0);
1293: #ifndef SMALL
1294: else if (ssl != NULL)
1295: return SSL_readline(ssl, lenp);
1.78 martynas 1296: #endif /* !SMALL */
1.61 deraadt 1297: else
1298: return NULL;
1299: }
1300:
1.65 ray 1301: size_t
1.61 deraadt 1302: ftp_read(FILE *fp, SSL *ssl, char *buf, size_t len)
1303: {
1.65 ray 1304: size_t ret;
1.61 deraadt 1305: if (fp != NULL)
1306: ret = fread(buf, sizeof(char), len, fp);
1307: #ifndef SMALL
1.65 ray 1308: else if (ssl != NULL) {
1309: int nr;
1310:
1311: if (len > INT_MAX)
1312: len = INT_MAX;
1313: if ((nr = SSL_read(ssl, buf, (int)len)) <= 0)
1314: ret = 0;
1315: else
1316: ret = nr;
1317: }
1.78 martynas 1318: #endif /* !SMALL */
1.61 deraadt 1319: else
1320: ret = 0;
1321: return (ret);
1322: }
1323:
1324: int
1325: ftp_printf(FILE *fp, SSL *ssl, const char *fmt, ...)
1326: {
1327: int ret;
1328: va_list ap;
1329:
1330: va_start(ap, fmt);
1331:
1332: if (fp != NULL)
1333: ret = vfprintf(fp, fmt, ap);
1334: #ifndef SMALL
1335: else if (ssl != NULL)
1336: ret = SSL_vprintf((SSL*)ssl, fmt, ap);
1.78 martynas 1337: #endif /* !SMALL */
1.61 deraadt 1338: else
1.92 jsg 1339: ret = 0;
1.61 deraadt 1340:
1341: va_end(ap);
1342: return (ret);
1343: }
1344:
1345: #ifndef SMALL
1346: int
1347: SSL_vprintf(SSL *ssl, const char *fmt, va_list ap)
1348: {
1349: int ret;
1350: char *string;
1351:
1352: if ((ret = vasprintf(&string, fmt, ap)) == -1)
1353: return ret;
1.64 ray 1354: ret = SSL_write(ssl, string, ret);
1.61 deraadt 1355: free(string);
1356: return ret;
1357: }
1358:
1359: char *
1360: SSL_readline(SSL *ssl, size_t *lenp)
1361: {
1.63 ray 1362: size_t i, len;
1.61 deraadt 1363: char *buf, *q, c;
1364:
1365: len = 128;
1366: if ((buf = malloc(len)) == NULL)
1367: errx(1, "Can't allocate memory for transfer buffer");
1368: for (i = 0; ; i++) {
1369: if (i >= len - 1) {
1370: if ((q = realloc(buf, 2 * len)) == NULL)
1371: errx(1, "Can't expand transfer buffer");
1372: buf = q;
1373: len *= 2;
1374: }
1375: if (SSL_read(ssl, &c, 1) <= 0)
1376: break;
1377: buf[i] = c;
1378: if (c == '\n')
1379: break;
1380: }
1381: *lenp = i;
1382: return (buf);
1383: }
1384:
1385: int
1.81 espie 1386: proxy_connect(int socket, char *host, char *cookie)
1.61 deraadt 1387: {
1.66 ray 1388: int l;
1.61 deraadt 1389: char buf[1024];
1390: char *connstr, *hosttail, *port;
1391:
1392: if (*host == '[' && (hosttail = strrchr(host, ']')) != NULL &&
1393: (hosttail[1] == '\0' || hosttail[1] == ':')) {
1394: host++;
1395: *hosttail++ = '\0';
1396: } else
1397: hosttail = host;
1398:
1399: port = strrchr(hosttail, ':'); /* find portnum */
1400: if (port != NULL)
1401: *port++ = '\0';
1402: if (!port)
1403: port = "443";
1404:
1.81 espie 1405: if (cookie) {
1406: l = asprintf(&connstr, "CONNECT %s:%s HTTP/1.1\r\n"
1407: "Proxy-Authorization: Basic %s\r\n%s\r\n\r\n",
1408: host, port, cookie, HTTP_USER_AGENT);
1409: } else {
1410: l = asprintf(&connstr, "CONNECT %s:%s HTTP/1.1\r\n%s\r\n\r\n",
1411: host, port, HTTP_USER_AGENT);
1412: }
1413:
1.66 ray 1414: if (l == -1)
1.61 deraadt 1415: errx(1, "Could not allocate memory to assemble connect string!");
1.80 martynas 1416: #ifndef SMALL
1.68 ray 1417: if (debug)
1418: printf("%s", connstr);
1.80 martynas 1419: #endif /* !SMALL */
1.66 ray 1420: if (write(socket, connstr, l) != l)
1.68 ray 1421: err(1, "Could not send connect string");
1.61 deraadt 1422: read(socket, &buf, sizeof(buf)); /* only proxy header XXX: error handling? */
1.71 ray 1423: free(connstr);
1.61 deraadt 1424: return(200);
1425: }
1.78 martynas 1426: #endif /* !SMALL */