[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.99

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

CVSweb <webmaster@jp.NetBSD.org>