[BACK]Return to fetch.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / usr.bin / ftp

Annotation of src/usr.bin/ftp/fetch.c, Revision 1.116

1.116   ! lukem       1: /*     $NetBSD: fetch.c,v 1.115 2000/06/05 09:22:52 lukem Exp $        */
1.1       lukem       2:
                      3: /*-
1.110     lukem       4:  * Copyright (c) 1997-2000 The NetBSD Foundation, Inc.
1.1       lukem       5:  * All rights reserved.
                      6:  *
                      7:  * This code is derived from software contributed to The NetBSD Foundation
1.54      lukem       8:  * by Luke Mewburn.
1.1       lukem       9:  *
1.114     lukem      10:  * This code is derived from software contributed to The NetBSD Foundation
                     11:  * by Scott Aaron Bamford.
                     12:  *
1.1       lukem      13:  * Redistribution and use in source and binary forms, with or without
                     14:  * modification, are permitted provided that the following conditions
                     15:  * are met:
                     16:  * 1. Redistributions of source code must retain the above copyright
                     17:  *    notice, this list of conditions and the following disclaimer.
                     18:  * 2. Redistributions in binary form must reproduce the above copyright
                     19:  *    notice, this list of conditions and the following disclaimer in the
                     20:  *    documentation and/or other materials provided with the distribution.
                     21:  * 3. All advertising materials mentioning features or use of this software
                     22:  *    must display the following acknowledgement:
1.84      lukem      23:  *     This product includes software developed by the NetBSD
                     24:  *     Foundation, Inc. and its contributors.
1.1       lukem      25:  * 4. Neither the name of The NetBSD Foundation nor the names of its
                     26:  *    contributors may be used to endorse or promote products derived
                     27:  *    from this software without specific prior written permission.
                     28:  *
                     29:  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
                     30:  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
                     31:  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
1.14      lukem      32:  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
                     33:  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
1.1       lukem      34:  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
                     35:  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
                     36:  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
                     37:  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
                     38:  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
                     39:  * POSSIBILITY OF SUCH DAMAGE.
                     40:  */
                     41:
1.12      lukem      42: #include <sys/cdefs.h>
1.1       lukem      43: #ifndef lint
1.116   ! lukem      44: __RCSID("$NetBSD: fetch.c,v 1.115 2000/06/05 09:22:52 lukem Exp $");
1.1       lukem      45: #endif /* not lint */
                     46:
                     47: /*
                     48:  * FTP User Program -- Command line file retrieval
                     49:  */
                     50:
                     51: #include <sys/types.h>
                     52: #include <sys/param.h>
                     53: #include <sys/socket.h>
1.30      lukem      54: #include <sys/stat.h>
1.32      lukem      55: #include <sys/time.h>
1.1       lukem      56:
                     57: #include <netinet/in.h>
                     58:
1.2       lukem      59: #include <arpa/ftp.h>
1.1       lukem      60: #include <arpa/inet.h>
                     61:
                     62: #include <ctype.h>
                     63: #include <err.h>
1.22      lukem      64: #include <errno.h>
1.1       lukem      65: #include <netdb.h>
                     66: #include <fcntl.h>
                     67: #include <stdio.h>
                     68: #include <stdlib.h>
                     69: #include <string.h>
                     70: #include <unistd.h>
1.57      christos   71: #include <time.h>
1.24      lukem      72: #include <util.h>
1.1       lukem      73:
                     74: #include "ftp_var.h"
1.100     lukem      75: #include "version.h"
1.1       lukem      76:
1.27      lukem      77: typedef enum {
                     78:        UNKNOWN_URL_T=-1,
                     79:        HTTP_URL_T,
                     80:        FTP_URL_T,
1.53      lukem      81:        FILE_URL_T,
                     82:        CLASSIC_URL_T
1.27      lukem      83: } url_t;
                     84:
1.111     lukem      85: void           aborthttp(int);
                     86: static int     auth_url(const char *, char **, const char *, const char *);
                     87: static void    base64_encode(const char *, size_t, char *);
                     88: static int     go_fetch(const char *);
                     89: static int     fetch_ftp(const char *);
                     90: static int     fetch_url(const char *, const char *, char *, char *);
                     91: static int     parse_url(const char *, const char *, url_t *, char **,
                     92:                            char **, char **, char **, in_port_t *, char **);
                     93: static void    url_decode(char *);
1.12      lukem      94:
1.42      lukem      95: static int     redirect_loop;
                     96:
1.12      lukem      97:
1.25      lukem      98: #define        ABOUT_URL       "about:"        /* propaganda */
                     99: #define        FILE_URL        "file://"       /* file URL prefix */
1.1       lukem     100: #define        FTP_URL         "ftp://"        /* ftp URL prefix */
                    101: #define        HTTP_URL        "http://"       /* http URL prefix */
                    102:
                    103:
1.27      lukem     104: /*
1.44      lukem     105:  * Generate authorization response based on given authentication challenge.
                    106:  * Returns -1 if an error occurred, otherwise 0.
                    107:  * Sets response to a malloc(3)ed string; caller should free.
                    108:  */
                    109: static int
1.111     lukem     110: auth_url(const char *challenge, char **response, const char *guser,
                    111:        const char *gpass)
1.44      lukem     112: {
                    113:        char            *cp, *ep, *clear, *line, *realm, *scheme;
1.82      lukem     114:        char             user[BUFSIZ], *pass;
                    115:        int              rval;
                    116:        size_t           len, clen, rlen;
1.44      lukem     117:
                    118:        *response = NULL;
                    119:        clear = realm = scheme = NULL;
                    120:        rval = -1;
                    121:        line = xstrdup(challenge);
                    122:        cp = line;
                    123:
                    124:        if (debug)
                    125:                fprintf(ttyout, "auth_url: challenge `%s'\n", challenge);
                    126:
                    127:        scheme = strsep(&cp, " ");
1.91      lukem     128: #define        SCHEME_BASIC "Basic"
1.44      lukem     129:        if (strncasecmp(scheme, SCHEME_BASIC, sizeof(SCHEME_BASIC) - 1) != 0) {
                    130:                warnx("Unsupported WWW Authentication challenge - `%s'",
                    131:                    challenge);
                    132:                goto cleanup_auth_url;
                    133:        }
                    134:        cp += strspn(cp, " ");
                    135:
1.91      lukem     136: #define        REALM "realm=\""
1.44      lukem     137:        if (strncasecmp(cp, REALM, sizeof(REALM) - 1) == 0)
                    138:                cp += sizeof(REALM) - 1;
                    139:        else {
                    140:                warnx("Unsupported WWW Authentication challenge - `%s'",
                    141:                    challenge);
                    142:                goto cleanup_auth_url;
                    143:        }
                    144:        if ((ep = strchr(cp, '\"')) != NULL) {
                    145:                size_t len = ep - cp;
                    146:
                    147:                realm = (char *)xmalloc(len + 1);
1.78      lukem     148:                (void)strlcpy(realm, cp, len + 1);
1.44      lukem     149:        } else {
                    150:                warnx("Unsupported WWW Authentication challenge - `%s'",
                    151:                    challenge);
                    152:                goto cleanup_auth_url;
                    153:        }
                    154:
1.78      lukem     155:        if (guser != NULL)
                    156:                (void)strlcpy(user, guser, sizeof(user));
                    157:        else {
1.54      lukem     158:                fprintf(ttyout, "Username for `%s': ", realm);
                    159:                (void)fflush(ttyout);
1.90      lukem     160:                if (fgets(user, sizeof(user) - 1, stdin) == NULL) {
                    161:                        clearerr(stdin);
1.54      lukem     162:                        goto cleanup_auth_url;
1.90      lukem     163:                }
1.54      lukem     164:                user[strlen(user) - 1] = '\0';
                    165:        }
                    166:        if (gpass != NULL)
                    167:                pass = (char *)gpass;
                    168:        else
                    169:                pass = getpass("Password: ");
1.44      lukem     170:
1.72      lukem     171:        clen = strlen(user) + strlen(pass) + 2; /* user + ":" + pass + "\0" */
                    172:        clear = (char *)xmalloc(clen);
1.78      lukem     173:        (void)strlcpy(clear, user, clen);
                    174:        (void)strlcat(clear, ":", clen);
                    175:        (void)strlcat(clear, pass, clen);
1.54      lukem     176:        if (gpass == NULL)
1.80      lukem     177:                memset(pass, 0, strlen(pass));
1.44      lukem     178:
1.64      lukem     179:                                                /* scheme + " " + enc + "\0" */
1.74      lukem     180:        rlen = strlen(scheme) + 1 + (clen + 2) * 4 / 3 + 1;
1.72      lukem     181:        *response = (char *)xmalloc(rlen);
1.78      lukem     182:        (void)strlcpy(*response, scheme, rlen);
1.72      lukem     183:        len = strlcat(*response, " ", rlen);
                    184:        base64_encode(clear, clen, *response + len);
1.80      lukem     185:        memset(clear, 0, clen);
1.44      lukem     186:        rval = 0;
                    187:
                    188: cleanup_auth_url:
                    189:        FREEPTR(clear);
                    190:        FREEPTR(line);
                    191:        FREEPTR(realm);
                    192:        return (rval);
                    193: }
                    194:
                    195: /*
                    196:  * Encode len bytes starting at clear using base64 encoding into encoded,
                    197:  * which should be at least ((len + 2) * 4 / 3 + 1) in size.
                    198:  */
                    199: void
1.111     lukem     200: base64_encode(const char *clear, size_t len, char *encoded)
1.44      lukem     201: {
                    202:        static const char enc[] =
                    203:            "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1.82      lukem     204:        char    *cp;
                    205:        int      i;
1.44      lukem     206:
                    207:        cp = encoded;
                    208:        for (i = 0; i < len; i += 3) {
                    209:                *(cp++) = enc[((clear[i + 0] >> 2))];
                    210:                *(cp++) = enc[((clear[i + 0] << 4) & 0x30)
                    211:                            | ((clear[i + 1] >> 4) & 0x0f)];
                    212:                *(cp++) = enc[((clear[i + 1] << 2) & 0x3c)
                    213:                            | ((clear[i + 2] >> 6) & 0x03)];
                    214:                *(cp++) = enc[((clear[i + 2]     ) & 0x3f)];
                    215:        }
                    216:        *cp = '\0';
                    217:        while (i-- > len)
                    218:                *(--cp) = '=';
                    219: }
                    220:
1.50      lukem     221: /*
                    222:  * Decode %xx escapes in given string, `in-place'.
                    223:  */
                    224: static void
1.111     lukem     225: url_decode(char *url)
1.50      lukem     226: {
                    227:        unsigned char *p, *q;
                    228:
                    229:        if (EMPTYSTRING(url))
                    230:                return;
1.75      lukem     231:        p = q = (unsigned char *)url;
1.50      lukem     232:
1.91      lukem     233: #define        HEXTOINT(x) (x - (isdigit(x) ? '0' : (islower(x) ? 'a' : 'A') - 10))
1.50      lukem     234:        while (*p) {
                    235:                if (p[0] == '%'
                    236:                    && p[1] && isxdigit((unsigned char)p[1])
                    237:                    && p[2] && isxdigit((unsigned char)p[2])) {
                    238:                        *q++ = HEXTOINT(p[1]) * 16 + HEXTOINT(p[2]);
                    239:                        p+=3;
                    240:                } else
                    241:                        *q++ = *p++;
                    242:        }
                    243:        *q = '\0';
                    244: }
                    245:
1.44      lukem     246:
                    247: /*
1.27      lukem     248:  * Parse URL of form:
1.63      lukem     249:  *     <type>://[<user>[:<password>@]]<host>[:<port>][/<path>]
1.27      lukem     250:  * Returns -1 if a parse error occurred, otherwise 0.
1.50      lukem     251:  * It's the caller's responsibility to url_decode() the returned
                    252:  * user, pass and path.
1.63      lukem     253:  *
1.27      lukem     254:  * Sets type to url_t, each of the given char ** pointers to a
                    255:  * malloc(3)ed strings of the relevant section, and port to
1.41      lukem     256:  * the number given, or ftpport if ftp://, or httpport if http://.
1.52      lukem     257:  *
1.63      lukem     258:  * If <host> is surrounded by `[' and ']', it's parsed as an
1.102     lukem     259:  * IPv6 address (as per RFC 2732).
1.63      lukem     260:  *
                    261:  * XXX: this is not totally RFC 1738 compliant; <path> will have the
1.53      lukem     262:  * leading `/' unless it's an ftp:// URL, as this makes things easier
                    263:  * for file:// and http:// URLs. ftp:// URLs have the `/' between the
                    264:  * host and the url-path removed, but any additional leading slashes
                    265:  * in the url-path are retained (because they imply that we should
                    266:  * later do "CWD" with a null argument).
                    267:  *
                    268:  * Examples:
                    269:  *      input url                       output path
                    270:  *      ---------                       -----------
1.63      lukem     271:  *     "ftp://host"                    NULL
                    272:  *     "http://host/"                  NULL
                    273:  *     "file://host/dir/file"          "dir/file"
                    274:  *     "ftp://host/"                   ""
                    275:  *     "ftp://host//"                  NULL
1.53      lukem     276:  *     "ftp://host//dir/file"          "/dir/file"
1.27      lukem     277:  */
                    278: static int
1.111     lukem     279: parse_url(const char *url, const char *desc, url_t *type,
                    280:                char **user, char **pass, char **host, char **port,
                    281:                in_port_t *portnum, char **path)
1.27      lukem     282: {
1.82      lukem     283:        const char      *origurl;
                    284:        char            *cp, *ep, *thost, *tport;
                    285:        size_t           len;
1.27      lukem     286:
                    287:        if (url == NULL || desc == NULL || type == NULL || user == NULL
1.82      lukem     288:            || pass == NULL || host == NULL || port == NULL || portnum == NULL
                    289:            || path == NULL)
1.27      lukem     290:                errx(1, "parse_url: invoked with NULL argument!");
                    291:
1.82      lukem     292:        origurl = url;
1.27      lukem     293:        *type = UNKNOWN_URL_T;
1.63      lukem     294:        *user = *pass = *host = *port = *path = NULL;
1.82      lukem     295:        *portnum = 0;
1.63      lukem     296:        tport = NULL;
1.27      lukem     297:
                    298:        if (strncasecmp(url, HTTP_URL, sizeof(HTTP_URL) - 1) == 0) {
                    299:                url += sizeof(HTTP_URL) - 1;
                    300:                *type = HTTP_URL_T;
1.82      lukem     301:                *portnum = HTTP_PORT;
1.63      lukem     302:                tport = httpport;
1.27      lukem     303:        } else if (strncasecmp(url, FTP_URL, sizeof(FTP_URL) - 1) == 0) {
                    304:                url += sizeof(FTP_URL) - 1;
                    305:                *type = FTP_URL_T;
1.82      lukem     306:                *portnum = FTP_PORT;
1.63      lukem     307:                tport = ftpport;
1.27      lukem     308:        } else if (strncasecmp(url, FILE_URL, sizeof(FILE_URL) - 1) == 0) {
                    309:                url += sizeof(FILE_URL) - 1;
                    310:                *type = FILE_URL_T;
                    311:        } else {
                    312:                warnx("Invalid %s `%s'", desc, url);
                    313: cleanup_parse_url:
                    314:                FREEPTR(*user);
                    315:                FREEPTR(*pass);
                    316:                FREEPTR(*host);
1.60      itojun    317:                FREEPTR(*port);
1.27      lukem     318:                FREEPTR(*path);
                    319:                return (-1);
                    320:        }
                    321:
                    322:        if (*url == '\0')
                    323:                return (0);
                    324:
                    325:                        /* find [user[:pass]@]host[:port] */
                    326:        ep = strchr(url, '/');
                    327:        if (ep == NULL)
                    328:                thost = xstrdup(url);
                    329:        else {
1.50      lukem     330:                len = ep - url;
1.27      lukem     331:                thost = (char *)xmalloc(len + 1);
1.78      lukem     332:                (void)strlcpy(thost, url, len + 1);
1.53      lukem     333:                if (*type == FTP_URL_T) /* skip first / for ftp URLs */
                    334:                        ep++;
1.27      lukem     335:                *path = xstrdup(ep);
                    336:        }
                    337:
1.63      lukem     338:        cp = strchr(thost, '@');        /* look for user[:pass]@ in URLs */
1.54      lukem     339:        if (cp != NULL) {
                    340:                if (*type == FTP_URL_T)
                    341:                        anonftp = 0;    /* disable anonftp */
1.27      lukem     342:                *user = thost;
                    343:                *cp = '\0';
1.63      lukem     344:                thost = xstrdup(cp + 1);
1.27      lukem     345:                cp = strchr(*user, ':');
                    346:                if (cp != NULL) {
                    347:                        *cp = '\0';
                    348:                        *pass = xstrdup(cp + 1);
                    349:                }
1.63      lukem     350:        }
                    351:
                    352: #ifdef INET6
                    353:                        /*
                    354:                         * Check if thost is an encoded IPv6 address, as per
1.102     lukem     355:                         * RFC 2732:
1.63      lukem     356:                         *      `[' ipv6-address ']'
                    357:                         */
                    358:        if (*thost == '[') {
                    359:                cp = thost + 1;
                    360:                if ((ep = strchr(cp, ']')) == NULL ||
1.106     itojun    361:                    (ep[1] != '\0' && ep[1] != ':')) {
1.63      lukem     362:                        warnx("Invalid address `%s' in %s `%s'",
1.82      lukem     363:                            thost, desc, origurl);
1.63      lukem     364:                        goto cleanup_parse_url;
                    365:                }
1.103     lukem     366:                len = ep - cp;          /* change `[xyz]' -> `xyz' */
1.63      lukem     367:                memmove(thost, thost + 1, len);
                    368:                thost[len] = '\0';
                    369:                if (! isipv6addr(thost)) {
                    370:                        warnx("Invalid IPv6 address `%s' in %s `%s'",
1.82      lukem     371:                            thost, desc, origurl);
1.63      lukem     372:                        goto cleanup_parse_url;
                    373:                }
                    374:                cp = ep + 1;
                    375:                if (*cp == ':')
                    376:                        cp++;
                    377:                else
                    378:                        cp = NULL;
1.27      lukem     379:        } else
1.63      lukem     380: #endif /* INET6 */
                    381:            if ((cp = strchr(thost, ':')) != NULL)
                    382:                *cp++ =  '\0';
                    383:        *host = thost;
1.60      itojun    384:
1.27      lukem     385:                        /* look for [:port] */
                    386:        if (cp != NULL) {
1.82      lukem     387:                long    nport;
1.27      lukem     388:
1.63      lukem     389:                nport = strtol(cp, &ep, 10);
1.82      lukem     390:                if (*ep != '\0' && ep == cp) {
                    391:                        struct servent  *svp;
                    392:
                    393:                        svp = getservbyname(cp, "tcp");
                    394:                        if (svp == NULL) {
                    395:                                warnx("Unknown port `%s' in %s `%s'",
                    396:                                    cp, desc, origurl);
                    397:                                goto cleanup_parse_url;
                    398:                        } else
                    399:                                nport = ntohs(svp->s_port);
                    400:                } else if (nport < 1 || nport > MAX_IN_PORT_T || *ep != '\0') {
                    401:                        warnx("Invalid port `%s' in %s `%s'", cp, desc,
                    402:                            origurl);
1.27      lukem     403:                        goto cleanup_parse_url;
                    404:                }
1.82      lukem     405:                *portnum = nport;
1.63      lukem     406:                tport = cp;
1.27      lukem     407:        }
1.82      lukem     408:
1.95      lukem     409:        if (tport != NULL)
1.63      lukem     410:                *port = xstrdup(tport);
1.81      lukem     411:        if (*path == NULL)
                    412:                *path = xstrdup("");
1.27      lukem     413:
                    414:        if (debug)
                    415:                fprintf(ttyout,
1.107     itojun    416:                    "parse_url: user `%s' pass `%s' host %s port %s(%d) "
                    417:                    "path `%s'\n",
1.53      lukem     418:                    *user ? *user : "<null>", *pass ? *pass : "<null>",
1.60      itojun    419:                    *host ? *host : "<null>", *port ? *port : "<null>",
1.82      lukem     420:                    *portnum ? *portnum : -1, *path ? *path : "<null>");
1.27      lukem     421:
                    422:        return (0);
                    423: }
                    424:
1.89      lukem     425: sigjmp_buf     httpabort;
1.2       lukem     426:
1.1       lukem     427: /*
1.50      lukem     428:  * Retrieve URL, via a proxy if necessary, using HTTP.
                    429:  * If proxyenv is set, use that for the proxy, otherwise try ftp_proxy or
                    430:  * http_proxy as appropriate.
                    431:  * Supports HTTP redirects.
1.41      lukem     432:  * Returns -1 on failure, 0 on completed xfer, 1 if ftp connection
                    433:  * is still open (e.g, ftp xfer with trailing /)
1.1       lukem     434:  */
1.12      lukem     435: static int
1.111     lukem     436: fetch_url(const char *url, const char *proxyenv, char *proxyauth, char *wwwauth)
1.1       lukem     437: {
1.105     lukem     438: #if defined(NI_NUMERICHOST) && defined(INET6)
1.98      itojun    439:        struct addrinfo         hints, *res, *res0 = NULL;
1.66      christos  440:        int                     error;
1.98      itojun    441:        char                    hbuf[NI_MAXHOST];
1.66      christos  442: #else
                    443:        struct sockaddr_in      sin;
                    444:        struct hostent          *hp = NULL;
                    445: #endif
1.104     lukem     446:        volatile sigfunc        oldintr, oldintp;
1.42      lukem     447:        volatile int            s;
1.95      lukem     448:        struct stat             sb;
1.96      lukem     449:        int                     ischunked, isproxy, rval, hcode;
1.42      lukem     450:        size_t                  len;
1.73      lukem     451:        static size_t           bufsize;
                    452:        static char             *xferbuf;
1.44      lukem     453:        char                    *cp, *ep, *buf, *savefile;
                    454:        char                    *auth, *location, *message;
1.63      lukem     455:        char                    *user, *pass, *host, *port, *path, *decodedpath;
1.54      lukem     456:        char                    *puser, *ppass;
1.95      lukem     457:        off_t                   hashbytes, rangestart, rangeend, entitylen;
1.111     lukem     458:        int                      (*closefunc)(FILE *);
1.42      lukem     459:        FILE                    *fin, *fout;
                    460:        time_t                  mtime;
                    461:        url_t                   urltype;
1.60      itojun    462:        in_port_t               portnum;
1.1       lukem     463:
1.86      lukem     464:        oldintr = oldintp = NULL;
1.22      lukem     465:        closefunc = NULL;
1.24      lukem     466:        fin = fout = NULL;
1.1       lukem     467:        s = -1;
1.44      lukem     468:        buf = savefile = NULL;
                    469:        auth = location = message = NULL;
1.52      lukem     470:        ischunked = isproxy = hcode = 0;
1.42      lukem     471:        rval = 1;
1.54      lukem     472:        user = pass = host = path = decodedpath = puser = ppass = NULL;
1.1       lukem     473:
1.27      lukem     474: #ifdef __GNUC__                        /* shut up gcc warnings */
1.22      lukem     475:        (void)&closefunc;
1.24      lukem     476:        (void)&fin;
1.22      lukem     477:        (void)&fout;
1.24      lukem     478:        (void)&buf;
1.12      lukem     479:        (void)&savefile;
1.42      lukem     480:        (void)&rval;
1.28      fvdl      481:        (void)&isproxy;
1.51      christos  482:        (void)&hcode;
                    483:        (void)&ischunked;
                    484:        (void)&message;
                    485:        (void)&location;
                    486:        (void)&auth;
                    487:        (void)&decodedpath;
1.12      lukem     488: #endif
                    489:
1.82      lukem     490:        if (parse_url(url, "URL", &urltype, &user, &pass, &host, &port,
                    491:            &portnum, &path) == -1)
1.42      lukem     492:                goto cleanup_fetch_url;
1.5       lukem     493:
1.27      lukem     494:        if (urltype == FILE_URL_T && ! EMPTYSTRING(host)
                    495:            && strcasecmp(host, "localhost") != 0) {
                    496:                warnx("No support for non local file URL `%s'", url);
1.42      lukem     497:                goto cleanup_fetch_url;
1.9       lukem     498:        }
1.27      lukem     499:
1.9       lukem     500:        if (EMPTYSTRING(path)) {
1.42      lukem     501:                if (urltype == FTP_URL_T) {
1.52      lukem     502:                        rval = fetch_ftp(url);
1.42      lukem     503:                        goto cleanup_fetch_url;
                    504:                }
1.27      lukem     505:                if (urltype != HTTP_URL_T || outfile == NULL)  {
                    506:                        warnx("Invalid URL (no file after host) `%s'", url);
1.42      lukem     507:                        goto cleanup_fetch_url;
1.27      lukem     508:                }
1.9       lukem     509:        }
1.1       lukem     510:
1.50      lukem     511:        decodedpath = xstrdup(path);
                    512:        url_decode(decodedpath);
                    513:
1.22      lukem     514:        if (outfile)
1.29      lukem     515:                savefile = xstrdup(outfile);
1.22      lukem     516:        else {
1.50      lukem     517:                cp = strrchr(decodedpath, '/');         /* find savefile */
1.29      lukem     518:                if (cp != NULL)
                    519:                        savefile = xstrdup(cp + 1);
1.22      lukem     520:                else
1.50      lukem     521:                        savefile = xstrdup(decodedpath);
1.22      lukem     522:        }
1.9       lukem     523:        if (EMPTYSTRING(savefile)) {
1.42      lukem     524:                if (urltype == FTP_URL_T) {
1.52      lukem     525:                        rval = fetch_ftp(url);
1.42      lukem     526:                        goto cleanup_fetch_url;
                    527:                }
1.27      lukem     528:                warnx("Invalid URL (no file after directory) `%s'", url);
1.42      lukem     529:                goto cleanup_fetch_url;
1.52      lukem     530:        } else {
                    531:                if (debug)
                    532:                        fprintf(ttyout, "got savefile as `%s'\n", savefile);
1.9       lukem     533:        }
1.1       lukem     534:
1.95      lukem     535:        restart_point = 0;
1.25      lukem     536:        filesize = -1;
1.95      lukem     537:        rangestart = rangeend = entitylen = -1;
1.25      lukem     538:        mtime = -1;
1.95      lukem     539:        if (restartautofetch) {
                    540:                if (strcmp(savefile, "-") != 0 && *savefile != '|' &&
                    541:                    stat(savefile, &sb) == 0)
                    542:                        restart_point = sb.st_size;
                    543:        }
1.25      lukem     544:        if (urltype == FILE_URL_T) {            /* file:// URLs */
                    545:                direction = "copied";
1.50      lukem     546:                fin = fopen(decodedpath, "r");
1.25      lukem     547:                if (fin == NULL) {
1.50      lukem     548:                        warn("Cannot open file `%s'", decodedpath);
1.42      lukem     549:                        goto cleanup_fetch_url;
1.5       lukem     550:                }
1.25      lukem     551:                if (fstat(fileno(fin), &sb) == 0) {
                    552:                        mtime = sb.st_mtime;
                    553:                        filesize = sb.st_size;
                    554:                }
1.95      lukem     555:                if (restart_point) {
                    556:                        if (lseek(fileno(fin), restart_point, SEEK_SET) < 0) {
                    557:                                warn("Can't lseek to restart `%s'",
                    558:                                    decodedpath);
                    559:                                goto cleanup_fetch_url;
                    560:                        }
                    561:                }
                    562:                if (verbose) {
                    563:                        fprintf(ttyout, "Copying %s", decodedpath);
                    564:                        if (restart_point)
                    565: #ifndef NO_QUAD
                    566:                                fprintf(ttyout, " (restarting at %lld)",
                    567:                                    (long long)restart_point);
                    568: #else
                    569:                                fprintf(ttyout, " (restarting at %ld)",
                    570:                                    (long)restart_point);
                    571: #endif
                    572:                        fputs("\n", ttyout);
                    573:                }
1.25      lukem     574:        } else {                                /* ftp:// or http:// URLs */
1.65      lukem     575:                char *leading;
                    576:                int hasleading;
                    577:
1.42      lukem     578:                if (proxyenv == NULL) {
                    579:                        if (urltype == HTTP_URL_T)
1.91      lukem     580:                                proxyenv = getoptionvalue("http_proxy");
1.42      lukem     581:                        else if (urltype == FTP_URL_T)
1.91      lukem     582:                                proxyenv = getoptionvalue("ftp_proxy");
1.42      lukem     583:                }
1.25      lukem     584:                direction = "retrieved";
1.91      lukem     585:                if (! EMPTYSTRING(proxyenv)) {                  /* use proxy */
1.27      lukem     586:                        url_t purltype;
1.54      lukem     587:                        char *phost, *ppath;
1.91      lukem     588:                        char *pport, *no_proxy;
1.27      lukem     589:
                    590:                        isproxy = 1;
                    591:
                    592:                                /* check URL against list of no_proxied sites */
1.91      lukem     593:                        no_proxy = getoptionvalue("no_proxy");
                    594:                        if (! EMPTYSTRING(no_proxy)) {
1.27      lukem     595:                                char *np, *np_copy;
                    596:                                long np_port;
                    597:                                size_t hlen, plen;
                    598:
                    599:                                np_copy = xstrdup(no_proxy);
                    600:                                hlen = strlen(host);
                    601:                                while ((cp = strsep(&np_copy, " ,")) != NULL) {
                    602:                                        if (*cp == '\0')
                    603:                                                continue;
1.60      itojun    604:                                        if ((np = strrchr(cp, ':')) != NULL) {
1.27      lukem     605:                                                *np = '\0';
                    606:                                                np_port =
                    607:                                                    strtol(np + 1, &ep, 10);
                    608:                                                if (*ep != '\0')
                    609:                                                        continue;
1.82      lukem     610:                                                if (np_port != portnum)
1.27      lukem     611:                                                        continue;
                    612:                                        }
                    613:                                        plen = strlen(cp);
1.109     lukem     614:                                        if (hlen < plen)
                    615:                                                continue;
1.27      lukem     616:                                        if (strncasecmp(host + hlen - plen,
                    617:                                            cp, plen) == 0) {
                    618:                                                isproxy = 0;
                    619:                                                break;
                    620:                                        }
                    621:                                }
                    622:                                FREEPTR(np_copy);
1.25      lukem     623:                        }
1.1       lukem     624:
1.27      lukem     625:                        if (isproxy) {
                    626:                                if (parse_url(proxyenv, "proxy URL", &purltype,
1.82      lukem     627:                                    &puser, &ppass, &phost, &pport, &portnum,
                    628:                                    &ppath) == -1)
1.42      lukem     629:                                        goto cleanup_fetch_url;
1.27      lukem     630:
                    631:                                if ((purltype != HTTP_URL_T
                    632:                                     && purltype != FTP_URL_T) ||
                    633:                                    EMPTYSTRING(phost) ||
                    634:                                    (! EMPTYSTRING(ppath)
                    635:                                     && strcmp(ppath, "/") != 0)) {
                    636:                                        warnx("Malformed proxy URL `%s'",
                    637:                                            proxyenv);
                    638:                                        FREEPTR(phost);
1.60      itojun    639:                                        FREEPTR(pport);
1.27      lukem     640:                                        FREEPTR(ppath);
1.42      lukem     641:                                        goto cleanup_fetch_url;
1.27      lukem     642:                                }
1.112     itojun    643:                                if (isipv6addr(host) &&
                    644:                                    strchr(host, '%') != NULL) {
                    645:                                        warnx(
                    646: "Scoped address notation `%s' disallowed via web proxy",
                    647:                                            host);
                    648:                                        FREEPTR(phost);
                    649:                                        FREEPTR(pport);
                    650:                                        FREEPTR(ppath);
                    651:                                        goto cleanup_fetch_url;
                    652:                                }
1.12      lukem     653:
1.27      lukem     654:                                FREEPTR(host);
                    655:                                host = phost;
1.62      tron      656:                                FREEPTR(port);
                    657:                                port = pport;
1.27      lukem     658:                                FREEPTR(path);
1.54      lukem     659:                                path = xstrdup(url);
1.27      lukem     660:                                FREEPTR(ppath);
                    661:                        }
1.91      lukem     662:                } /* ! EMPTYSTRING(proxyenv) */
1.25      lukem     663:
1.105     lukem     664: #if !defined(NI_NUMERICHOST) || !defined(INET6)
1.25      lukem     665:                memset(&sin, 0, sizeof(sin));
                    666:                sin.sin_family = AF_INET;
                    667:
                    668:                if (isdigit((unsigned char)host[0])) {
                    669:                        if (inet_aton(host, &sin.sin_addr) == 0) {
1.27      lukem     670:                                warnx("Invalid IP address `%s'", host);
1.42      lukem     671:                                goto cleanup_fetch_url;
1.25      lukem     672:                        }
                    673:                } else {
                    674:                        hp = gethostbyname(host);
                    675:                        if (hp == NULL) {
                    676:                                warnx("%s: %s", host, hstrerror(h_errno));
1.42      lukem     677:                                goto cleanup_fetch_url;
1.25      lukem     678:                        }
                    679:                        if (hp->h_addrtype != AF_INET) {
1.27      lukem     680:                                warnx("`%s': not an Internet address?", host);
1.42      lukem     681:                                goto cleanup_fetch_url;
1.25      lukem     682:                        }
1.66      christos  683:                        if (hp->h_length > sizeof(sin.sin_addr))
                    684:                                hp->h_length = sizeof(sin.sin_addr);
1.25      lukem     685:                        memcpy(&sin.sin_addr, hp->h_addr, hp->h_length);
1.1       lukem     686:                }
1.82      lukem     687:                sin.sin_port = htons(portnum);
1.25      lukem     688:
                    689:                s = socket(AF_INET, SOCK_STREAM, 0);
                    690:                if (s == -1) {
                    691:                        warn("Can't create socket");
1.42      lukem     692:                        goto cleanup_fetch_url;
1.22      lukem     693:                }
1.1       lukem     694:
1.25      lukem     695:                while (xconnect(s, (struct sockaddr *)&sin,
                    696:                    sizeof(sin)) == -1) {
                    697:                        if (errno == EINTR)
                    698:                                continue;
                    699:                        if (hp && hp->h_addr_list[1]) {
                    700:                                int oerrno = errno;
                    701:                                char *ia;
                    702:
                    703:                                ia = inet_ntoa(sin.sin_addr);
                    704:                                errno = oerrno;
1.27      lukem     705:                                warn("Connect to address `%s'", ia);
1.25      lukem     706:                                hp->h_addr_list++;
                    707:                                memcpy(&sin.sin_addr, hp->h_addr_list[0],
                    708:                                    (size_t)hp->h_length);
1.49      lukem     709:                                if (verbose)
                    710:                                        fprintf(ttyout, "Trying %s...\n",
                    711:                                            inet_ntoa(sin.sin_addr));
1.25      lukem     712:                                (void)close(s);
                    713:                                s = socket(AF_INET, SOCK_STREAM, 0);
                    714:                                if (s < 0) {
                    715:                                        warn("Can't create socket");
1.42      lukem     716:                                        goto cleanup_fetch_url;
1.25      lukem     717:                                }
                    718:                                continue;
                    719:                        }
1.27      lukem     720:                        warn("Can't connect to `%s'", host);
1.42      lukem     721:                        goto cleanup_fetch_url;
1.25      lukem     722:                }
1.60      itojun    723: #else
                    724:                memset(&hints, 0, sizeof(hints));
1.65      lukem     725:                hints.ai_flags = 0;
1.60      itojun    726:                hints.ai_family = AF_UNSPEC;
                    727:                hints.ai_socktype = SOCK_STREAM;
                    728:                hints.ai_protocol = 0;
1.98      itojun    729:                error = getaddrinfo(host, port, &hints, &res0);
1.60      itojun    730:                if (error) {
1.69      mycroft   731:                        warnx(gai_strerror(error));
1.60      itojun    732:                        goto cleanup_fetch_url;
                    733:                }
1.98      itojun    734:                if (res0->ai_canonname)
                    735:                        host = res0->ai_canonname;
1.60      itojun    736:
1.98      itojun    737:                s = -1;
                    738:                for (res = res0; res; res = res->ai_next) {
1.113     itojun    739:                        /*
                    740:                         * see comment in hookup()
                    741:                         */
                    742:                        ai_unmapped(res);
1.98      itojun    743:                        if (getnameinfo(res->ai_addr, res->ai_addrlen,
                    744:                                        hbuf, sizeof(hbuf), NULL, 0,
                    745:                                        NI_NUMERICHOST) != 0)
                    746:                                strncpy(hbuf, "invalid", sizeof(hbuf));
                    747:
                    748:                        if (verbose && res != res0)
                    749:                                fprintf(ttyout, "Trying %s...\n", hbuf);
                    750:
                    751:                        s = socket(res->ai_family, res->ai_socktype,
                    752:                                res->ai_protocol);
1.60      itojun    753:                        if (s < 0) {
                    754:                                warn("Can't create socket");
1.98      itojun    755:                                continue;
1.60      itojun    756:                        }
                    757:
                    758:                        if (xconnect(s, res->ai_addr, res->ai_addrlen) < 0) {
                    759:                                warn("Connect to address `%s'", hbuf);
                    760:                                close(s);
1.98      itojun    761:                                s = -1;
                    762:                                continue;
1.60      itojun    763:                        }
1.98      itojun    764:
                    765:                        /* success */
1.60      itojun    766:                        break;
                    767:                }
1.98      itojun    768:                freeaddrinfo(res0);
                    769:
                    770:                if (s < 0) {
                    771:                        warn("Can't connect to %s", host);
                    772:                        goto cleanup_fetch_url;
                    773:                }
1.60      itojun    774: #endif
1.24      lukem     775:
1.25      lukem     776:                fin = fdopen(s, "r+");
                    777:                /*
1.27      lukem     778:                 * Construct and send the request.
1.25      lukem     779:                 */
1.65      lukem     780:                if (verbose)
                    781:                        fprintf(ttyout, "Requesting %s\n", url);
                    782:                leading = "  (";
                    783:                hasleading = 0;
1.27      lukem     784:                if (isproxy) {
1.65      lukem     785:                        if (verbose) {
                    786:                                fprintf(ttyout, "%svia %s:%s", leading,
                    787:                                    host, port);
                    788:                                leading = ", ";
                    789:                                hasleading++;
                    790:                        }
1.44      lukem     791:                        fprintf(fin, "GET %s HTTP/1.0\r\n", path);
1.47      lukem     792:                        if (flushcache)
                    793:                                fprintf(fin, "Pragma: no-cache\r\n");
1.27      lukem     794:                } else {
                    795:                        fprintf(fin, "GET %s HTTP/1.1\r\n", path);
1.106     itojun    796:                        if (strchr(host, ':')) {
1.112     itojun    797:                                char *h, *p;
                    798:
                    799:                                /*
                    800:                                 * strip off IPv6 scope identifier, since it is
                    801:                                 * local to the node
                    802:                                 */
                    803:                                h = xstrdup(host);
                    804:                                if (isipv6addr(h) &&
                    805:                                    (p = strchr(h, '%')) != NULL) {
                    806:                                        *p = '\0';
                    807:                                }
                    808:                                fprintf(fin, "Host: [%s]:%d\r\n", h, portnum);
                    809:                                free(h);
1.106     itojun    810:                        } else
                    811:                                fprintf(fin, "Host: %s:%d\r\n", host, portnum);
1.40      explorer  812:                        fprintf(fin, "Accept: */*\r\n");
1.42      lukem     813:                        fprintf(fin, "Connection: close\r\n");
1.95      lukem     814:                        if (restart_point) {
                    815:                                fputs(leading, ttyout);
                    816: #ifndef NO_QUAD
                    817:                                fprintf(fin, "Range: bytes=%lld-\r\n",
                    818:                                    (long long)restart_point);
                    819:                                fprintf(ttyout, "restarting at %lld",
                    820:                                    (long long)restart_point);
                    821: #else
                    822:                                fprintf(fin, "Range: bytes=%ld-\r\n",
                    823:                                    (long)restart_point);
                    824:                                fprintf(ttyout, "restarting at %ld",
                    825:                                    (long)restart_point);
                    826: #endif
                    827:                                leading = ", ";
                    828:                                hasleading++;
                    829:                        }
1.47      lukem     830:                        if (flushcache)
                    831:                                fprintf(fin, "Cache-Control: no-cache\r\n");
1.25      lukem     832:                }
1.94      lukem     833:                fprintf(fin, "User-Agent: %s/%s\r\n", FTP_PRODUCT, FTP_VERSION);
1.44      lukem     834:                if (wwwauth) {
1.65      lukem     835:                        if (verbose) {
                    836:                                fprintf(ttyout, "%swith authorization",
                    837:                                    leading);
                    838:                                leading = ", ";
                    839:                                hasleading++;
                    840:                        }
1.44      lukem     841:                        fprintf(fin, "Authorization: %s\r\n", wwwauth);
                    842:                }
1.46      lukem     843:                if (proxyauth) {
1.65      lukem     844:                        if (verbose) {
1.49      lukem     845:                                fprintf(ttyout,
1.65      lukem     846:                                    "%swith proxy authorization", leading);
                    847:                                leading = ", ";
                    848:                                hasleading++;
                    849:                        }
1.44      lukem     850:                        fprintf(fin, "Proxy-Authorization: %s\r\n", proxyauth);
1.46      lukem     851:                }
1.65      lukem     852:                if (verbose && hasleading)
                    853:                        fputs(")\n", ttyout);
1.44      lukem     854:                fprintf(fin, "\r\n");
1.25      lukem     855:                if (fflush(fin) == EOF) {
                    856:                        warn("Writing HTTP request");
1.42      lukem     857:                        goto cleanup_fetch_url;
1.25      lukem     858:                }
1.1       lukem     859:
1.25      lukem     860:                                /* Read the response */
1.24      lukem     861:                if ((buf = fparseln(fin, &len, NULL, "\0\0\0", 0)) == NULL) {
                    862:                        warn("Receiving HTTP reply");
1.42      lukem     863:                        goto cleanup_fetch_url;
1.24      lukem     864:                }
                    865:                while (len > 0 && (buf[len-1] == '\r' || buf[len-1] == '\n'))
                    866:                        buf[--len] = '\0';
                    867:                if (debug)
1.27      lukem     868:                        fprintf(ttyout, "received `%s'\n", buf);
1.1       lukem     869:
1.42      lukem     870:                                /* Determine HTTP response code */
1.25      lukem     871:                cp = strchr(buf, ' ');
                    872:                if (cp == NULL)
                    873:                        goto improper;
                    874:                else
                    875:                        cp++;
1.42      lukem     876:                hcode = strtol(cp, &ep, 10);
                    877:                if (*ep != '\0' && !isspace((unsigned char)*ep))
                    878:                        goto improper;
                    879:                message = xstrdup(cp);
1.25      lukem     880:
                    881:                                /* Read the rest of the header. */
1.27      lukem     882:                FREEPTR(buf);
1.25      lukem     883:                while (1) {
                    884:                        if ((buf = fparseln(fin, &len, NULL, "\0\0\0", 0))
                    885:                            == NULL) {
                    886:                                warn("Receiving HTTP reply");
1.42      lukem     887:                                goto cleanup_fetch_url;
1.25      lukem     888:                        }
                    889:                        while (len > 0 &&
                    890:                            (buf[len-1] == '\r' || buf[len-1] == '\n'))
                    891:                                buf[--len] = '\0';
                    892:                        if (len == 0)
                    893:                                break;
                    894:                        if (debug)
1.27      lukem     895:                                fprintf(ttyout, "received `%s'\n", buf);
1.25      lukem     896:
                    897:                                /* Look for some headers */
                    898:                        cp = buf;
1.42      lukem     899:
1.91      lukem     900: #define        CONTENTLEN "Content-Length: "
1.25      lukem     901:                        if (strncasecmp(cp, CONTENTLEN,
1.44      lukem     902:                                        sizeof(CONTENTLEN) - 1) == 0) {
1.25      lukem     903:                                cp += sizeof(CONTENTLEN) - 1;
1.95      lukem     904: #ifndef NO_QUAD
1.108     lukem     905:                                filesize = strtoll(cp, &ep, 10);
1.95      lukem     906: #else
1.25      lukem     907:                                filesize = strtol(cp, &ep, 10);
1.95      lukem     908: #endif
                    909:                                if (filesize < 0 || *ep != '\0')
1.25      lukem     910:                                        goto improper;
                    911:                                if (debug)
                    912: #ifndef NO_QUAD
1.95      lukem     913:                                        fprintf(ttyout, "parsed len as: %lld\n",
1.25      lukem     914:                                            (long long)filesize);
                    915: #else
1.95      lukem     916:                                        fprintf(ttyout, "parsed len as: %ld\n",
1.25      lukem     917:                                            (long)filesize);
                    918: #endif
1.42      lukem     919:
1.95      lukem     920: #define CONTENTRANGE "Content-Range: bytes "
                    921:                        } else if (strncasecmp(cp, CONTENTRANGE,
                    922:                                        sizeof(CONTENTRANGE) - 1) == 0) {
                    923:                                cp += sizeof(CONTENTRANGE) - 1;
                    924: #ifndef NO_QUAD
1.108     lukem     925:                                rangestart = strtoll(cp, &ep, 10);
1.95      lukem     926: #else
                    927:                                rangestart = strtol(cp, &ep, 10);
                    928: #endif
                    929:                                if (rangestart < 0 || *ep != '-')
                    930:                                        goto improper;
                    931:                                cp = ep + 1;
                    932:
                    933: #ifndef NO_QUAD
1.108     lukem     934:                                rangeend = strtoll(cp, &ep, 10);
1.95      lukem     935: #else
                    936:                                rangeend = strtol(cp, &ep, 10);
                    937: #endif
                    938:                                if (rangeend < 0 || *ep != '/' ||
                    939:                                    rangeend < rangestart)
                    940:                                        goto improper;
                    941:                                cp = ep + 1;
                    942:
                    943: #ifndef NO_QUAD
1.108     lukem     944:                                entitylen = strtoll(cp, &ep, 10);
1.95      lukem     945: #else
                    946:                                entitylen = strtol(cp, &ep, 10);
                    947: #endif
                    948:                                if (entitylen < 0 || *ep != '\0')
                    949:                                        goto improper;
                    950:
                    951:                                if (debug)
                    952: #ifndef NO_QUAD
                    953:                                        fprintf(ttyout,
                    954:                                            "parsed range as: %lld-%lld/%lld\n",
                    955:                                            (long long)rangestart,
                    956:                                            (long long)rangeend,
                    957:                                            (long long)entitylen);
                    958: #else
                    959:                                        fprintf(ttyout,
                    960:                                            "parsed range as: %ld-%ld/%ld\n",
                    961:                                            (long)rangestart,
                    962:                                            (long)rangeend,
                    963:                                            (long)entitylen);
                    964: #endif
                    965:                                if (! restart_point) {
                    966:                                        warnx(
                    967:                                    "Received unexpected Content-Range header");
                    968:                                        goto cleanup_fetch_url;
                    969:                                }
                    970:
1.91      lukem     971: #define        LASTMOD "Last-Modified: "
1.25      lukem     972:                        } else if (strncasecmp(cp, LASTMOD,
1.44      lukem     973:                                                sizeof(LASTMOD) - 1) == 0) {
1.25      lukem     974:                                struct tm parsed;
                    975:                                char *t;
                    976:
                    977:                                cp += sizeof(LASTMOD) - 1;
                    978:                                                        /* RFC 1123 */
                    979:                                if ((t = strptime(cp,
                    980:                                                "%a, %d %b %Y %H:%M:%S GMT",
                    981:                                                &parsed))
                    982:                                                        /* RFC 850 */
                    983:                                    || (t = strptime(cp,
                    984:                                                "%a, %d-%b-%y %H:%M:%S GMT",
                    985:                                                &parsed))
                    986:                                                        /* asctime */
                    987:                                    || (t = strptime(cp,
                    988:                                                "%a, %b %d %H:%M:%S %Y",
                    989:                                                &parsed))) {
1.38      lukem     990:                                        parsed.tm_isdst = -1;
1.25      lukem     991:                                        if (*t == '\0')
1.76      lukem     992:                                                mtime = timegm(&parsed);
1.38      lukem     993:                                        if (debug && mtime != -1) {
1.25      lukem     994:                                                fprintf(ttyout,
                    995:                                                    "parsed date as: %s",
                    996:                                                    ctime(&mtime));
1.38      lukem     997:                                        }
1.25      lukem     998:                                }
1.42      lukem     999:
1.91      lukem    1000: #define        LOCATION "Location: "
1.42      lukem    1001:                        } else if (strncasecmp(cp, LOCATION,
1.44      lukem    1002:                                                sizeof(LOCATION) - 1) == 0) {
1.25      lukem    1003:                                cp += sizeof(LOCATION) - 1;
1.42      lukem    1004:                                location = xstrdup(cp);
1.25      lukem    1005:                                if (debug)
                   1006:                                        fprintf(ttyout,
                   1007:                                            "parsed location as: %s\n", cp);
1.44      lukem    1008:
1.91      lukem    1009: #define        TRANSENC "Transfer-Encoding: "
1.45      lukem    1010:                        } else if (strncasecmp(cp, TRANSENC,
                   1011:                                                sizeof(TRANSENC) - 1) == 0) {
                   1012:                                cp += sizeof(TRANSENC) - 1;
1.109     lukem    1013:                                if (strcasecmp(cp, "binary") == 0) {
                   1014:                                        warnx(
                   1015:                        "Bogus transfer encoding - `%s' (fetching anyway)",
                   1016:                                            cp);
                   1017:                                        continue;
                   1018:                                }
1.45      lukem    1019:                                if (strcasecmp(cp, "chunked") != 0) {
                   1020:                                        warnx(
                   1021:                                    "Unsupported transfer encoding - `%s'",
                   1022:                                            cp);
                   1023:                                        goto cleanup_fetch_url;
                   1024:                                }
                   1025:                                ischunked++;
                   1026:                                if (debug)
                   1027:                                        fprintf(ttyout,
                   1028:                                            "using chunked encoding\n");
                   1029:
1.91      lukem    1030: #define        PROXYAUTH "Proxy-Authenticate: "
1.44      lukem    1031:                        } else if (strncasecmp(cp, PROXYAUTH,
                   1032:                                                sizeof(PROXYAUTH) - 1) == 0) {
                   1033:                                cp += sizeof(PROXYAUTH) - 1;
                   1034:                                FREEPTR(auth);
                   1035:                                auth = xstrdup(cp);
                   1036:                                if (debug)
                   1037:                                        fprintf(ttyout,
                   1038:                                            "parsed proxy-auth as: %s\n", cp);
                   1039:
1.91      lukem    1040: #define        WWWAUTH "WWW-Authenticate: "
1.44      lukem    1041:                        } else if (strncasecmp(cp, WWWAUTH,
                   1042:                            sizeof(WWWAUTH) - 1) == 0) {
                   1043:                                cp += sizeof(WWWAUTH) - 1;
                   1044:                                FREEPTR(auth);
                   1045:                                auth = xstrdup(cp);
                   1046:                                if (debug)
                   1047:                                        fprintf(ttyout,
                   1048:                                            "parsed www-auth as: %s\n", cp);
                   1049:
1.25      lukem    1050:                        }
1.44      lukem    1051:
1.24      lukem    1052:                }
1.54      lukem    1053:                                /* finished parsing header */
1.27      lukem    1054:                FREEPTR(buf);
1.1       lukem    1055:
1.52      lukem    1056:                switch (hcode) {
                   1057:                case 200:
                   1058:                        break;
1.95      lukem    1059:                case 206:
                   1060:                        if (! restart_point) {
                   1061:                                warnx("Not expecting partial content header");
                   1062:                                goto cleanup_fetch_url;
                   1063:                        }
                   1064:                        break;
1.52      lukem    1065:                case 300:
                   1066:                case 301:
                   1067:                case 302:
                   1068:                case 303:
                   1069:                case 305:
                   1070:                        if (EMPTYSTRING(location)) {
                   1071:                                warnx(
                   1072:                                "No redirection Location provided by server");
                   1073:                                goto cleanup_fetch_url;
                   1074:                        }
                   1075:                        if (redirect_loop++ > 5) {
                   1076:                                warnx("Too many redirections requested");
                   1077:                                goto cleanup_fetch_url;
                   1078:                        }
                   1079:                        if (hcode == 305) {
                   1080:                                if (verbose)
                   1081:                                        fprintf(ttyout, "Redirected via %s\n",
                   1082:                                            location);
                   1083:                                rval = fetch_url(url, location,
                   1084:                                    proxyauth, wwwauth);
                   1085:                        } else {
                   1086:                                if (verbose)
                   1087:                                        fprintf(ttyout, "Redirected to %s\n",
                   1088:                                            location);
                   1089:                                rval = go_fetch(location);
                   1090:                        }
1.42      lukem    1091:                        goto cleanup_fetch_url;
1.52      lukem    1092:                case 401:
                   1093:                case 407:
                   1094:                    {
                   1095:                        char **authp;
1.54      lukem    1096:                        char *auser, *apass;
1.52      lukem    1097:
                   1098:                        fprintf(ttyout, "%s\n", message);
                   1099:                        if (EMPTYSTRING(auth)) {
                   1100:                                warnx(
                   1101:                            "No authentication challenge provided by server");
                   1102:                                goto cleanup_fetch_url;
                   1103:                        }
1.54      lukem    1104:                        if (hcode == 401) {
                   1105:                                authp = &wwwauth;
                   1106:                                auser = user;
                   1107:                                apass = pass;
                   1108:                        } else {
                   1109:                                authp = &proxyauth;
                   1110:                                auser = puser;
                   1111:                                apass = ppass;
                   1112:                        }
1.52      lukem    1113:                        if (*authp != NULL) {
                   1114:                                char reply[10];
                   1115:
                   1116:                                fprintf(ttyout,
                   1117:                                    "Authorization failed. Retry (y/n)? ");
1.90      lukem    1118:                                if (fgets(reply, sizeof(reply), stdin)
                   1119:                                    == NULL) {
                   1120:                                        clearerr(stdin);
1.52      lukem    1121:                                        goto cleanup_fetch_url;
1.90      lukem    1122:                                } else {
                   1123:                                        if (tolower(reply[0]) != 'y')
                   1124:                                                goto cleanup_fetch_url;
                   1125:                                }
1.54      lukem    1126:                                auser = NULL;
                   1127:                                apass = NULL;
1.52      lukem    1128:                        }
1.54      lukem    1129:                        if (auth_url(auth, authp, auser, apass) == 0) {
1.52      lukem    1130:                                rval = fetch_url(url, proxyenv,
                   1131:                                    proxyauth, wwwauth);
1.80      lukem    1132:                                memset(*authp, 0, strlen(*authp));
1.52      lukem    1133:                                FREEPTR(*authp);
                   1134:                        }
1.42      lukem    1135:                        goto cleanup_fetch_url;
1.52      lukem    1136:                    }
                   1137:                default:
                   1138:                        if (message)
                   1139:                                warnx("Error retrieving file - `%s'", message);
                   1140:                        else
                   1141:                                warnx("Unknown error retrieving file");
1.44      lukem    1142:                        goto cleanup_fetch_url;
                   1143:                }
1.52      lukem    1144:        }               /* end of ftp:// or http:// specific setup */
1.42      lukem    1145:
1.22      lukem    1146:                        /* Open the output file. */
                   1147:        if (strcmp(savefile, "-") == 0) {
                   1148:                fout = stdout;
                   1149:        } else if (*savefile == '|') {
1.77      lukem    1150:                oldintp = xsignal(SIGPIPE, SIG_IGN);
1.22      lukem    1151:                fout = popen(savefile + 1, "w");
                   1152:                if (fout == NULL) {
1.27      lukem    1153:                        warn("Can't run `%s'", savefile + 1);
1.42      lukem    1154:                        goto cleanup_fetch_url;
1.22      lukem    1155:                }
                   1156:                closefunc = pclose;
                   1157:        } else {
1.95      lukem    1158:                if (restart_point){
                   1159:                        if (entitylen != -1)
                   1160:                                filesize = entitylen;
                   1161:                        if (rangestart != -1 && rangestart != restart_point) {
                   1162:                                warnx(
                   1163:                                    "Size of `%s' differs from save file `%s'",
                   1164:                                    url, savefile);
                   1165:                                goto cleanup_fetch_url;
                   1166:                        }
                   1167:                        fout = fopen(savefile, "a");
                   1168:                } else
                   1169:                        fout = fopen(savefile, "w");
1.22      lukem    1170:                if (fout == NULL) {
1.27      lukem    1171:                        warn("Can't open `%s'", savefile);
1.42      lukem    1172:                        goto cleanup_fetch_url;
1.22      lukem    1173:                }
                   1174:                closefunc = fclose;
1.1       lukem    1175:        }
                   1176:
1.22      lukem    1177:                        /* Trap signals */
1.88      lukem    1178:        if (sigsetjmp(httpabort, 1))
1.42      lukem    1179:                goto cleanup_fetch_url;
1.86      lukem    1180:        (void)xsignal(SIGQUIT, psummary);
1.77      lukem    1181:        oldintr = xsignal(SIGINT, aborthttp);
1.2       lukem    1182:
1.73      lukem    1183:        if (rcvbuf_size > bufsize) {
                   1184:                if (xferbuf)
                   1185:                        (void)free(xferbuf);
                   1186:                bufsize = rcvbuf_size;
                   1187:                xferbuf = xmalloc(bufsize);
                   1188:        }
                   1189:
1.1       lukem    1190:        bytes = 0;
                   1191:        hashbytes = mark;
                   1192:        progressmeter(-1);
1.2       lukem    1193:
1.24      lukem    1194:                        /* Finally, suck down the file. */
1.45      lukem    1195:        do {
1.77      lukem    1196:                long chunksize;
1.45      lukem    1197:
                   1198:                chunksize = 0;
                   1199:                                        /* read chunksize */
                   1200:                if (ischunked) {
1.73      lukem    1201:                        if (fgets(xferbuf, bufsize, fin) == NULL) {
1.45      lukem    1202:                                warnx("Unexpected EOF reading chunksize");
                   1203:                                goto cleanup_fetch_url;
                   1204:                        }
1.73      lukem    1205:                        chunksize = strtol(xferbuf, &ep, 16);
1.103     lukem    1206:
                   1207:                                /*
                   1208:                                 * XXX: Work around bug in Apache 1.3.9, which
                   1209:                                 *      incorrectly puts a trailing space after
                   1210:                                 *      the chunksize.
                   1211:                                 */
                   1212:                        if (*ep == ' ')
                   1213:                                ep++;
                   1214:
1.45      lukem    1215:                        if (strcmp(ep, "\r\n") != 0) {
                   1216:                                warnx("Unexpected data following chunksize");
                   1217:                                goto cleanup_fetch_url;
                   1218:                        }
                   1219:                        if (debug)
1.59      lukem    1220:                                fprintf(ttyout,
                   1221: #ifndef NO_QUAD
1.75      lukem    1222:                                    "got chunksize of %lld\n",
1.45      lukem    1223:                                    (long long)chunksize);
1.59      lukem    1224: #else
                   1225:                                    "got chunksize of %ld\n",
                   1226:                                    (long)chunksize);
                   1227: #endif
1.45      lukem    1228:                        if (chunksize == 0)
                   1229:                                break;
                   1230:                }
1.59      lukem    1231:                                        /* transfer file or chunk */
                   1232:                while (1) {
                   1233:                        struct timeval then, now, td;
                   1234:                        off_t bufrem;
                   1235:
1.71      lukem    1236:                        if (rate_get)
                   1237:                                (void)gettimeofday(&then, NULL);
1.73      lukem    1238:                        bufrem = rate_get ? rate_get : bufsize;
1.101     lukem    1239:                        if (ischunked)
                   1240:                                bufrem = MIN(chunksize, bufrem);
1.59      lukem    1241:                        while (bufrem > 0) {
1.73      lukem    1242:                                len = fread(xferbuf, sizeof(char),
1.101     lukem    1243:                                    MIN(bufsize, bufrem), fin);
1.59      lukem    1244:                                if (len <= 0)
                   1245:                                        goto chunkdone;
                   1246:                                bytes += len;
                   1247:                                bufrem -= len;
1.73      lukem    1248:                                if (fwrite(xferbuf, sizeof(char), len, fout)
1.59      lukem    1249:                                    != len) {
                   1250:                                        warn("Writing `%s'", savefile);
                   1251:                                        goto cleanup_fetch_url;
                   1252:                                }
1.101     lukem    1253:                                if (hash && !progress) {
                   1254:                                        while (bytes >= hashbytes) {
                   1255:                                                (void)putc('#', ttyout);
                   1256:                                                hashbytes += mark;
                   1257:                                        }
                   1258:                                        (void)fflush(ttyout);
                   1259:                                }
                   1260:                                if (ischunked) {
                   1261:                                        chunksize -= len;
                   1262:                                        if (chunksize <= 0)
                   1263:                                                break;
1.45      lukem    1264:                                }
1.59      lukem    1265:                        }
                   1266:                        if (rate_get) {
                   1267:                                while (1) {
                   1268:                                        (void)gettimeofday(&now, NULL);
                   1269:                                        timersub(&now, &then, &td);
                   1270:                                        if (td.tv_sec > 0)
                   1271:                                                break;
                   1272:                                        usleep(1000000 - td.tv_usec);
                   1273:                                }
                   1274:                        }
1.101     lukem    1275:                        if (ischunked && chunksize <= 0)
                   1276:                                break;
1.1       lukem    1277:                }
1.45      lukem    1278:                                        /* read CRLF after chunk*/
1.59      lukem    1279:  chunkdone:
1.45      lukem    1280:                if (ischunked) {
1.73      lukem    1281:                        if (fgets(xferbuf, bufsize, fin) == NULL)
1.45      lukem    1282:                                break;
1.73      lukem    1283:                        if (strcmp(xferbuf, "\r\n") != 0) {
1.45      lukem    1284:                                warnx("Unexpected data following chunk");
                   1285:                                goto cleanup_fetch_url;
1.1       lukem    1286:                        }
                   1287:                }
1.45      lukem    1288:        } while (ischunked);
1.1       lukem    1289:        if (hash && !progress && bytes > 0) {
                   1290:                if (bytes < mark)
1.22      lukem    1291:                        (void)putc('#', ttyout);
                   1292:                (void)putc('\n', ttyout);
1.1       lukem    1293:        }
1.25      lukem    1294:        if (ferror(fin)) {
                   1295:                warn("Reading file");
1.42      lukem    1296:                goto cleanup_fetch_url;
1.1       lukem    1297:        }
1.2       lukem    1298:        progressmeter(1);
1.85      lukem    1299:        bytes = 0;
1.25      lukem    1300:        (void)fflush(fout);
                   1301:        if (closefunc == fclose && mtime != -1) {
                   1302:                struct timeval tval[2];
                   1303:
                   1304:                (void)gettimeofday(&tval[0], NULL);
                   1305:                tval[1].tv_sec = mtime;
                   1306:                tval[1].tv_usec = 0;
1.31      lukem    1307:                (*closefunc)(fout);
                   1308:                fout = NULL;
                   1309:
                   1310:                if (utimes(savefile, tval) == -1) {
1.25      lukem    1311:                        fprintf(ttyout,
                   1312:                            "Can't change modification time to %s",
                   1313:                            asctime(localtime(&mtime)));
                   1314:                }
                   1315:        }
                   1316:        if (bytes > 0)
                   1317:                ptransfer(0);
1.1       lukem    1318:
1.42      lukem    1319:        rval = 0;
                   1320:        goto cleanup_fetch_url;
1.11      lukem    1321:
1.1       lukem    1322: improper:
1.27      lukem    1323:        warnx("Improper response from `%s'", host);
1.11      lukem    1324:
1.42      lukem    1325: cleanup_fetch_url:
1.86      lukem    1326:        if (oldintr)
                   1327:                (void)xsignal(SIGINT, oldintr);
                   1328:        if (oldintp)
                   1329:                (void)xsignal(SIGPIPE, oldintp);
1.24      lukem    1330:        if (fin != NULL)
                   1331:                fclose(fin);
                   1332:        else if (s != -1)
1.1       lukem    1333:                close(s);
1.22      lukem    1334:        if (closefunc != NULL && fout != NULL)
                   1335:                (*closefunc)(fout);
1.29      lukem    1336:        FREEPTR(savefile);
1.27      lukem    1337:        FREEPTR(user);
                   1338:        FREEPTR(pass);
                   1339:        FREEPTR(host);
1.60      itojun   1340:        FREEPTR(port);
1.27      lukem    1341:        FREEPTR(path);
1.50      lukem    1342:        FREEPTR(decodedpath);
1.54      lukem    1343:        FREEPTR(puser);
                   1344:        FREEPTR(ppass);
1.27      lukem    1345:        FREEPTR(buf);
1.44      lukem    1346:        FREEPTR(auth);
1.42      lukem    1347:        FREEPTR(location);
                   1348:        FREEPTR(message);
                   1349:        return (rval);
1.1       lukem    1350: }
                   1351:
                   1352: /*
1.50      lukem    1353:  * Abort a HTTP retrieval
1.2       lukem    1354:  */
                   1355: void
1.111     lukem    1356: aborthttp(int notused)
1.2       lukem    1357: {
1.88      lukem    1358:        char msgbuf[100];
                   1359:        int len;
1.2       lukem    1360:
                   1361:        alarmtimer(0);
1.88      lukem    1362:        len = strlcpy(msgbuf, "\nHTTP fetch aborted.\n", sizeof(msgbuf));
                   1363:        write(fileno(ttyout), msgbuf, len);
                   1364:        siglongjmp(httpabort, 1);
1.2       lukem    1365: }
                   1366:
                   1367: /*
1.50      lukem    1368:  * Retrieve ftp URL or classic ftp argument using FTP.
1.63      lukem    1369:  * Returns 1 on failure, 0 on completed xfer, -1 if ftp connection
1.42      lukem    1370:  * is still open (e.g, ftp xfer with trailing /)
                   1371:  */
                   1372: static int
1.111     lukem    1373: fetch_ftp(const char *url)
1.42      lukem    1374: {
                   1375:        char            *cp, *xargv[5], rempath[MAXPATHLEN];
                   1376:        char            *host, *path, *dir, *file, *user, *pass;
1.60      itojun   1377:        char            *port;
1.82      lukem    1378:        int              dirhasglob, filehasglob, oautologin, rval, type, xargc;
                   1379:        in_port_t        portnum;
                   1380:        url_t            urltype;
1.42      lukem    1381:
                   1382:        host = path = dir = file = user = pass = NULL;
1.60      itojun   1383:        port = NULL;
1.42      lukem    1384:        rval = 1;
1.53      lukem    1385:        type = TYPE_I;
1.42      lukem    1386:
                   1387:        if (strncasecmp(url, FTP_URL, sizeof(FTP_URL) - 1) == 0) {
                   1388:                if ((parse_url(url, "URL", &urltype, &user, &pass,
1.82      lukem    1389:                    &host, &port, &portnum, &path) == -1) ||
1.42      lukem    1390:                    (user != NULL && *user == '\0') ||
                   1391:                    (pass != NULL && *pass == '\0') ||
                   1392:                    EMPTYSTRING(host)) {
                   1393:                        warnx("Invalid URL `%s'", url);
                   1394:                        goto cleanup_fetch_ftp;
                   1395:                }
1.50      lukem    1396:                url_decode(user);
                   1397:                url_decode(pass);
1.53      lukem    1398:                /*
                   1399:                 * Note: Don't url_decode(path) here.  We need to keep the
                   1400:                 * distinction between "/" and "%2F" until later.
                   1401:                 */
                   1402:
                   1403:                                        /* check for trailing ';type=[aid]' */
1.63      lukem    1404:                if (! EMPTYSTRING(path) && (cp = strrchr(path, ';')) != NULL) {
1.53      lukem    1405:                        if (strcasecmp(cp, ";type=a") == 0)
                   1406:                                type = TYPE_A;
                   1407:                        else if (strcasecmp(cp, ";type=i") == 0)
                   1408:                                type = TYPE_I;
                   1409:                        else if (strcasecmp(cp, ";type=d") == 0) {
                   1410:                                warnx(
                   1411:                            "Directory listing via a URL is not supported");
                   1412:                                goto cleanup_fetch_ftp;
                   1413:                        } else {
                   1414:                                warnx("Invalid suffix `%s' in URL `%s'", cp,
                   1415:                                    url);
                   1416:                                goto cleanup_fetch_ftp;
                   1417:                        }
                   1418:                        *cp = 0;
                   1419:                }
1.97      lukem    1420:        } else {                        /* classic style `[user@]host:[file]' */
1.53      lukem    1421:                urltype = CLASSIC_URL_T;
1.42      lukem    1422:                host = xstrdup(url);
1.97      lukem    1423:                cp = strchr(host, '@');
                   1424:                if (cp != NULL) {
                   1425:                        *cp = '\0';
                   1426:                        user = host;
                   1427:                        anonftp = 0;    /* disable anonftp */
                   1428:                        host = xstrdup(cp + 1);
                   1429:                }
1.42      lukem    1430:                cp = strchr(host, ':');
                   1431:                if (cp != NULL) {
                   1432:                        *cp = '\0';
                   1433:                        path = xstrdup(cp + 1);
                   1434:                }
                   1435:        }
                   1436:        if (EMPTYSTRING(host))
                   1437:                goto cleanup_fetch_ftp;
                   1438:
1.50      lukem    1439:                        /* Extract the file and (if present) directory name. */
1.42      lukem    1440:        dir = path;
                   1441:        if (! EMPTYSTRING(dir)) {
1.53      lukem    1442:                /*
1.97      lukem    1443:                 * If we are dealing with classic `[user@]host:[path]' syntax,
                   1444:                 * then a path of the form `/file' (resulting from input of the
                   1445:                 * form `host:/file') means that we should do "CWD /" before
                   1446:                 * retrieving the file.  So we set dir="/" and file="file".
1.53      lukem    1447:                 *
1.97      lukem    1448:                 * But if we are dealing with URLs like `ftp://host/path' then
                   1449:                 * a path of the form `/file' (resulting from a URL of the form
                   1450:                 * `ftp://host//file') means that we should do `CWD ' (with an
                   1451:                 * empty argument) before retrieving the file.  So we set
1.53      lukem    1452:                 * dir="" and file="file".
                   1453:                 *
1.97      lukem    1454:                 * If the path does not contain / at all, we set dir=NULL.
                   1455:                 * (We get a path without any slashes if we are dealing with
                   1456:                 * classic `[user@]host:[file]' or URL `ftp://host/file'.)
1.53      lukem    1457:                 *
1.97      lukem    1458:                 * In all other cases, we set dir to a string that does not
                   1459:                 * include the final '/' that separates the dir part from the
                   1460:                 * file part of the path.  (This will be the empty string if
                   1461:                 * and only if we are dealing with a path of the form `/file'
                   1462:                 * resulting from an URL of the form `ftp://host//file'.)
1.53      lukem    1463:                 */
1.42      lukem    1464:                cp = strrchr(dir, '/');
1.53      lukem    1465:                if (cp == dir && urltype == CLASSIC_URL_T) {
1.50      lukem    1466:                        file = cp + 1;
                   1467:                        dir = "/";
                   1468:                } else if (cp != NULL) {
1.42      lukem    1469:                        *cp++ = '\0';
                   1470:                        file = cp;
                   1471:                } else {
                   1472:                        file = dir;
                   1473:                        dir = NULL;
                   1474:                }
1.63      lukem    1475:        } else
                   1476:                dir = NULL;
1.53      lukem    1477:        if (urltype == FTP_URL_T && file != NULL) {
                   1478:                url_decode(file);
                   1479:                /* but still don't url_decode(dir) */
                   1480:        }
1.42      lukem    1481:        if (debug)
                   1482:                fprintf(ttyout,
1.107     itojun   1483:                    "fetch_ftp: user `%s' pass `%s' host %s port %s "
                   1484:                    "path `%s' dir `%s' file `%s'\n",
1.53      lukem    1485:                    user ? user : "<null>", pass ? pass : "<null>",
1.60      itojun   1486:                    host ? host : "<null>", port ? port : "<null>",
                   1487:                    path ? path : "<null>",
1.53      lukem    1488:                    dir ? dir : "<null>", file ? file : "<null>");
1.42      lukem    1489:
                   1490:        dirhasglob = filehasglob = 0;
1.53      lukem    1491:        if (doglob && urltype == CLASSIC_URL_T) {
1.42      lukem    1492:                if (! EMPTYSTRING(dir) && strpbrk(dir, "*?[]{}") != NULL)
                   1493:                        dirhasglob = 1;
                   1494:                if (! EMPTYSTRING(file) && strpbrk(file, "*?[]{}") != NULL)
                   1495:                        filehasglob = 1;
                   1496:        }
                   1497:
1.50      lukem    1498:                        /* Set up the connection */
                   1499:        if (connected)
                   1500:                disconnect(0, NULL);
                   1501:        xargv[0] = __progname;
                   1502:        xargv[1] = host;
                   1503:        xargv[2] = NULL;
                   1504:        xargc = 2;
                   1505:        if (port) {
1.60      itojun   1506:                xargv[2] = port;
1.50      lukem    1507:                xargv[3] = NULL;
                   1508:                xargc = 3;
                   1509:        }
                   1510:        oautologin = autologin;
1.116   ! lukem    1511:                /* don't autologin in setpeer(), use ftp_login() below */
        !          1512:        autologin = 0;
1.50      lukem    1513:        setpeer(xargc, xargv);
                   1514:        autologin = oautologin;
1.116   ! lukem    1515:        if ((connected == 0) ||
        !          1516:            (connected == 1 && !ftp_login(host, user, pass))) {
1.50      lukem    1517:                warnx("Can't connect or login to host `%s'", host);
                   1518:                goto cleanup_fetch_ftp;
                   1519:        }
1.42      lukem    1520:
1.53      lukem    1521:        switch (type) {
                   1522:        case TYPE_A:
                   1523:                setascii(0, NULL);
                   1524:                break;
                   1525:        case TYPE_I:
                   1526:                setbinary(0, NULL);
                   1527:                break;
                   1528:        default:
1.91      lukem    1529:                errx(1, "fetch_ftp: unknown transfer type %d", type);
1.53      lukem    1530:        }
                   1531:
1.63      lukem    1532:                /*
                   1533:                 * Change directories, if necessary.
                   1534:                 *
                   1535:                 * Note: don't use EMPTYSTRING(dir) below, because
                   1536:                 * dir=="" means something different from dir==NULL.
                   1537:                 */
1.53      lukem    1538:        if (dir != NULL && !dirhasglob) {
                   1539:                char *nextpart;
1.42      lukem    1540:
1.53      lukem    1541:                /*
1.97      lukem    1542:                 * If we are dealing with a classic `[user@]host:[path]'
                   1543:                 * (urltype is CLASSIC_URL_T) then we have a raw directory
1.53      lukem    1544:                 * name (not encoded in any way) and we can change
                   1545:                 * directories in one step.
                   1546:                 *
                   1547:                 * If we are dealing with an `ftp://host/path' URL
                   1548:                 * (urltype is FTP_URL_T), then RFC 1738 says we need to
                   1549:                 * send a separate CWD command for each unescaped "/"
                   1550:                 * in the path, and we have to interpret %hex escaping
                   1551:                 * *after* we find the slashes.  It's possible to get
                   1552:                 * empty components here, (from multiple adjacent
                   1553:                 * slashes in the path) and RFC 1738 says that we should
                   1554:                 * still do `CWD ' (with a null argument) in such cases.
                   1555:                 *
1.63      lukem    1556:                 * Many ftp servers don't support `CWD ', so if there's an
                   1557:                 * error performing that command, bail out with a descriptive
                   1558:                 * message.
1.53      lukem    1559:                 *
                   1560:                 * Examples:
                   1561:                 *
1.63      lukem    1562:                 * host:                        dir="", urltype=CLASSIC_URL_T
                   1563:                 *              logged in (to default directory)
1.53      lukem    1564:                 * host:file                    dir=NULL, urltype=CLASSIC_URL_T
                   1565:                 *              "RETR file"
1.63      lukem    1566:                 * host:dir/                    dir="dir", urltype=CLASSIC_URL_T
                   1567:                 *              "CWD dir", logged in
                   1568:                 * ftp://host/                  dir="", urltype=FTP_URL_T
                   1569:                 *              logged in (to default directory)
                   1570:                 * ftp://host/dir/              dir="dir", urltype=FTP_URL_T
                   1571:                 *              "CWD dir", logged in
1.53      lukem    1572:                 * ftp://host/file              dir=NULL, urltype=FTP_URL_T
                   1573:                 *              "RETR file"
                   1574:                 * ftp://host//file             dir="", urltype=FTP_URL_T
1.63      lukem    1575:                 *              "CWD ", "RETR file"
1.53      lukem    1576:                 * host:/file                   dir="/", urltype=CLASSIC_URL_T
                   1577:                 *              "CWD /", "RETR file"
                   1578:                 * ftp://host///file            dir="/", urltype=FTP_URL_T
1.63      lukem    1579:                 *              "CWD ", "CWD ", "RETR file"
1.53      lukem    1580:                 * ftp://host/%2F/file          dir="%2F", urltype=FTP_URL_T
                   1581:                 *              "CWD /", "RETR file"
                   1582:                 * ftp://host/foo/file          dir="foo", urltype=FTP_URL_T
                   1583:                 *              "CWD foo", "RETR file"
                   1584:                 * ftp://host/foo/bar/file      dir="foo/bar"
                   1585:                 *              "CWD foo", "CWD bar", "RETR file"
                   1586:                 * ftp://host//foo/bar/file     dir="/foo/bar"
1.63      lukem    1587:                 *              "CWD ", "CWD foo", "CWD bar", "RETR file"
1.53      lukem    1588:                 * ftp://host/foo//bar/file     dir="foo//bar"
1.63      lukem    1589:                 *              "CWD foo", "CWD ", "CWD bar", "RETR file"
1.53      lukem    1590:                 * ftp://host/%2F/foo/bar/file  dir="%2F/foo/bar"
                   1591:                 *              "CWD /", "CWD foo", "CWD bar", "RETR file"
                   1592:                 * ftp://host/%2Ffoo/bar/file   dir="%2Ffoo/bar"
                   1593:                 *              "CWD /foo", "CWD bar", "RETR file"
                   1594:                 * ftp://host/%2Ffoo%2Fbar/file dir="%2Ffoo%2Fbar"
                   1595:                 *              "CWD /foo/bar", "RETR file"
                   1596:                 * ftp://host/%2Ffoo%2Fbar%2Ffile       dir=NULL
                   1597:                 *              "RETR /foo/bar/file"
                   1598:                 *
                   1599:                 * Note that we don't need `dir' after this point.
                   1600:                 */
                   1601:                do {
                   1602:                        if (urltype == FTP_URL_T) {
                   1603:                                nextpart = strchr(dir, '/');
                   1604:                                if (nextpart) {
                   1605:                                        *nextpart = '\0';
                   1606:                                        nextpart++;
                   1607:                                }
                   1608:                                url_decode(dir);
                   1609:                        } else
                   1610:                                nextpart = NULL;
                   1611:                        if (debug)
                   1612:                                fprintf(ttyout, "dir `%s', nextpart `%s'\n",
                   1613:                                    dir ? dir : "<null>",
                   1614:                                    nextpart ? nextpart : "<null>");
1.63      lukem    1615:                        if (urltype == FTP_URL_T || *dir != '\0') {
1.53      lukem    1616:                                xargv[0] = "cd";
                   1617:                                xargv[1] = dir;
                   1618:                                xargv[2] = NULL;
                   1619:                                dirchange = 0;
                   1620:                                cd(2, xargv);
1.63      lukem    1621:                                if (! dirchange) {
                   1622:                                        if (*dir == '\0' && code == 500)
                   1623:                                                fprintf(stderr,
                   1624: "\n"
                   1625: "ftp: The `CWD ' command (without a directory), which is required by\n"
                   1626: "     RFC 1738 to support the empty directory in the URL pathname (`//'),\n"
                   1627: "     conflicts with the server's conformance to RFC 959.\n"
                   1628: "     Try the same URL without the `//' in the URL pathname.\n"
                   1629: "\n");
1.53      lukem    1630:                                        goto cleanup_fetch_ftp;
1.63      lukem    1631:                                }
1.53      lukem    1632:                        }
                   1633:                        dir = nextpart;
                   1634:                } while (dir != NULL);
1.42      lukem    1635:        }
                   1636:
                   1637:        if (EMPTYSTRING(file)) {
                   1638:                rval = -1;
                   1639:                goto cleanup_fetch_ftp;
                   1640:        }
                   1641:
                   1642:        if (dirhasglob) {
1.78      lukem    1643:                (void)strlcpy(rempath, dir,     sizeof(rempath));
                   1644:                (void)strlcat(rempath, "/",     sizeof(rempath));
                   1645:                (void)strlcat(rempath, file,    sizeof(rempath));
1.42      lukem    1646:                file = rempath;
                   1647:        }
                   1648:
                   1649:                        /* Fetch the file(s). */
                   1650:        xargc = 2;
                   1651:        xargv[0] = "get";
                   1652:        xargv[1] = file;
                   1653:        xargv[2] = NULL;
                   1654:        if (dirhasglob || filehasglob) {
                   1655:                int ointeractive;
                   1656:
                   1657:                ointeractive = interactive;
                   1658:                interactive = 0;
                   1659:                xargv[0] = "mget";
                   1660:                mget(xargc, xargv);
                   1661:                interactive = ointeractive;
                   1662:        } else {
1.53      lukem    1663:                if (outfile == NULL) {
                   1664:                        cp = strrchr(file, '/');        /* find savefile */
                   1665:                        if (cp != NULL)
                   1666:                                outfile = cp + 1;
                   1667:                        else
                   1668:                                outfile = file;
1.42      lukem    1669:                }
1.53      lukem    1670:                xargv[2] = (char *)outfile;
                   1671:                xargv[3] = NULL;
                   1672:                xargc++;
1.52      lukem    1673:                if (restartautofetch)
                   1674:                        reget(xargc, xargv);
                   1675:                else
                   1676:                        get(xargc, xargv);
1.42      lukem    1677:        }
                   1678:
                   1679:        if ((code / 100) == COMPLETE)
                   1680:                rval = 0;
                   1681:
                   1682: cleanup_fetch_ftp:
                   1683:        FREEPTR(host);
                   1684:        FREEPTR(path);
                   1685:        FREEPTR(user);
                   1686:        FREEPTR(pass);
                   1687:        return (rval);
                   1688: }
                   1689:
                   1690: /*
                   1691:  * Retrieve the given file to outfile.
                   1692:  * Supports arguments of the form:
                   1693:  *     "host:path", "ftp://host/path"  if $ftpproxy, call fetch_url() else
                   1694:  *                                     call fetch_ftp()
1.50      lukem    1695:  *     "http://host/path"              call fetch_url() to use HTTP
1.42      lukem    1696:  *     "file:///path"                  call fetch_url() to copy
                   1697:  *     "about:..."                     print a message
                   1698:  *
                   1699:  * Returns 1 on failure, 0 on completed xfer, -1 if ftp connection
                   1700:  * is still open (e.g, ftp xfer with trailing /)
                   1701:  */
                   1702: static int
1.111     lukem    1703: go_fetch(const char *url)
1.42      lukem    1704: {
1.91      lukem    1705:        char *proxy;
1.42      lukem    1706:
                   1707:        /*
                   1708:         * Check for about:*
                   1709:         */
                   1710:        if (strncasecmp(url, ABOUT_URL, sizeof(ABOUT_URL) - 1) == 0) {
                   1711:                url += sizeof(ABOUT_URL) -1;
                   1712:                if (strcasecmp(url, "ftp") == 0) {
1.87      lukem    1713:                        fputs(
                   1714: "This version of ftp has been enhanced by Luke Mewburn <lukem@netbsd.org>\n"
                   1715: "for the NetBSD project.  Execute `man ftp' for more details.\n", ttyout);
                   1716:                } else if (strcasecmp(url, "lukem") == 0) {
                   1717:                        fputs(
                   1718: "Luke Mewburn is the author of most of the enhancements in this ftp client.\n"
                   1719: "Please email feedback to <lukem@netbsd.org>.\n", ttyout);
1.42      lukem    1720:                } else if (strcasecmp(url, "netbsd") == 0) {
1.87      lukem    1721:                        fputs(
                   1722: "NetBSD is a freely available and redistributable UNIX-like operating system.\n"
                   1723: "For more information, see http://www.netbsd.org/index.html\n", ttyout);
1.92      lukem    1724:                } else if (strcasecmp(url, "version") == 0) {
1.115     lukem    1725:                        fprintf(ttyout, "Version: %s %s%s\n",
                   1726:                            FTP_PRODUCT, FTP_VERSION,
                   1727: #ifdef INET6
                   1728:                            ""
                   1729: #else
                   1730:                            " (-IPv6)"
                   1731: #endif
                   1732:                        );
1.42      lukem    1733:                } else {
                   1734:                        fprintf(ttyout, "`%s' is an interesting topic.\n", url);
                   1735:                }
1.92      lukem    1736:                fputs("\n", ttyout);
1.42      lukem    1737:                return (0);
                   1738:        }
                   1739:
                   1740:        /*
                   1741:         * Check for file:// and http:// URLs.
                   1742:         */
                   1743:        if (strncasecmp(url, HTTP_URL, sizeof(HTTP_URL) - 1) == 0 ||
                   1744:            strncasecmp(url, FILE_URL, sizeof(FILE_URL) - 1) == 0)
1.52      lukem    1745:                return (fetch_url(url, NULL, NULL, NULL));
1.42      lukem    1746:
                   1747:        /*
                   1748:         * Try FTP URL-style and host:file arguments next.
                   1749:         * If ftpproxy is set with an FTP URL, use fetch_url()
                   1750:         * Othewise, use fetch_ftp().
                   1751:         */
1.91      lukem    1752:        proxy = getoptionvalue("ftp_proxy");
                   1753:        if (!EMPTYSTRING(proxy) &&
                   1754:            strncasecmp(url, FTP_URL, sizeof(FTP_URL) - 1) == 0)
1.52      lukem    1755:                return (fetch_url(url, NULL, NULL, NULL));
1.42      lukem    1756:
1.52      lukem    1757:        return (fetch_ftp(url));
1.42      lukem    1758: }
                   1759:
                   1760: /*
                   1761:  * Retrieve multiple files from the command line,
                   1762:  * calling go_fetch() for each file.
                   1763:  *
                   1764:  * If an ftp path has a trailing "/", the path will be cd-ed into and
1.41      lukem    1765:  * the connection remains open, and the function will return -1
                   1766:  * (to indicate the connection is alive).
1.1       lukem    1767:  * If an error occurs the return value will be the offset+1 in
                   1768:  * argv[] of the file that caused a problem (i.e, argv[x]
                   1769:  * returns x+1)
                   1770:  * Otherwise, 0 is returned if all files retrieved successfully.
                   1771:  */
                   1772: int
1.111     lukem    1773: auto_fetch(int argc, char *argv[])
1.1       lukem    1774: {
1.42      lukem    1775:        volatile int    argpos;
                   1776:        int             rval;
1.22      lukem    1777:
1.2       lukem    1778:        argpos = 0;
1.1       lukem    1779:
1.88      lukem    1780:        if (sigsetjmp(toplevel, 1)) {
1.2       lukem    1781:                if (connected)
                   1782:                        disconnect(0, NULL);
1.3       lukem    1783:                return (argpos + 1);
1.2       lukem    1784:        }
1.88      lukem    1785:        (void)xsignal(SIGINT, intr);
1.91      lukem    1786:        (void)xsignal(SIGPIPE, lostpeer);
1.1       lukem    1787:
                   1788:        /*
                   1789:         * Loop through as long as there's files to fetch.
                   1790:         */
1.27      lukem    1791:        for (rval = 0; (rval == 0) && (argpos < argc); argpos++) {
1.1       lukem    1792:                if (strchr(argv[argpos], ':') == NULL)
                   1793:                        break;
1.42      lukem    1794:                redirect_loop = 0;
1.90      lukem    1795:                if (!anonftp)
                   1796:                        anonftp = 2;    /* Handle "automatic" transfers. */
1.52      lukem    1797:                rval = go_fetch(argv[argpos]);
                   1798:                if (outfile != NULL && strcmp(outfile, "-") != 0
                   1799:                    && outfile[0] != '|')
                   1800:                        outfile = NULL;
1.42      lukem    1801:                if (rval > 0)
1.1       lukem    1802:                        rval = argpos + 1;
1.42      lukem    1803:        }
1.3       lukem    1804:
1.1       lukem    1805:        if (connected && rval != -1)
                   1806:                disconnect(0, NULL);
1.114     lukem    1807:        return (rval);
                   1808: }
                   1809:
                   1810:
                   1811: int
                   1812: auto_put(int argc, char **argv, const char *uploadserver)
                   1813: {
                   1814:        char    *uargv[4], *path, *pathsep;
                   1815:        int      uargc, rval, len;
                   1816:
                   1817:        uargc = 0;
                   1818:        uargv[uargc++] = "mput";
                   1819:        uargv[uargc++] = argv[0];
                   1820:        uargv[2] = uargv[3] = NULL;
                   1821:        pathsep = NULL;
                   1822:        rval = 1;
                   1823:
                   1824:        if (debug)
                   1825:                fprintf(ttyout, "auto_put: target `%s'\n", uploadserver);
                   1826:
                   1827:        path = xstrdup(uploadserver);
                   1828:        len = strlen(path);
                   1829:        if (path[len - 1] != '/' && path[len - 1] != ':') {
                   1830:                        /*
                   1831:                         * make sure we always pass a directory to auto_fetch
                   1832:                         */
                   1833:                if (argc > 1) {         /* more than one file to upload */
                   1834:                        int len;
                   1835:
                   1836:                        len = strlen(uploadserver) + 2; /* path + "/" + "\0" */
                   1837:                        free(path);
                   1838:                        path = (char *)xmalloc(len);
                   1839:                        (void)strlcpy(path, uploadserver, len);
                   1840:                        (void)strlcat(path, "/", len);
                   1841:                } else {                /* single file to upload */
                   1842:                        uargv[0] = "put";
                   1843:                        pathsep = strrchr(path, '/');
                   1844:                        if (pathsep == NULL) {
                   1845:                                pathsep = strrchr(path, ':');
                   1846:                                if (pathsep == NULL) {
                   1847:                                        warnx("Invalid URL `%s'", path);
                   1848:                                        goto cleanup_auto_put;
                   1849:                                }
                   1850:                                pathsep++;
                   1851:                                uargv[2] = xstrdup(pathsep);
                   1852:                                pathsep[0] = '/';
                   1853:                        } else
                   1854:                                uargv[2] = xstrdup(pathsep + 1);
                   1855:                        pathsep[1] = '\0';
                   1856:                        uargc++;
                   1857:                }
                   1858:        }
                   1859:        if (debug)
                   1860:                fprintf(ttyout, "autoput: url `%s' argv[2] `%s'\n",
                   1861:                    path, uargv[2] ? uargv[2] : "<null>");
                   1862:
                   1863:                        /* connect and cwd */
                   1864:        rval = auto_fetch(1, &path);
                   1865:        free(path);
                   1866:        if(rval >= 0)
                   1867:                goto cleanup_auto_put;
                   1868:
                   1869:                        /* XXX : is this the best way? */
                   1870:        if (uargc == 3) {
                   1871:                uargv[1] = argv[0];
                   1872:                put(uargc, uargv);
                   1873:                goto cleanup_auto_put;
                   1874:        }
                   1875:
                   1876:        for(; argv[0] != NULL; argv++) {
                   1877:                uargv[1] = argv[0];
                   1878:                mput(uargc, uargv);
                   1879:        }
                   1880:        rval = 0;
                   1881:
                   1882: cleanup_auto_put:
                   1883:        FREEPTR(uargv[2]);
1.1       lukem    1884:        return (rval);
                   1885: }

CVSweb <webmaster@jp.NetBSD.org>