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