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

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

CVSweb <webmaster@jp.NetBSD.org>