Annotate format strings. Add a bunch of int casts for size limits.diff -r1.1.1.7 -r1.2 src/external/bsd/fetch/dist/libfetch/common.h
(joerg)
--- src/external/bsd/fetch/dist/libfetch/common.h 2010/03/24 20:51:42 1.1.1.7
+++ src/external/bsd/fetch/dist/libfetch/common.h 2014/01/07 02:13:00 1.2
@@ -1,139 +1,139 @@ | @@ -1,139 +1,139 @@ | |||
1 | /* $NetBSD: common.h,v 1.1.1.7 2010/03/24 20:51:42 joerg Exp $ */ | 1 | /* $NetBSD: common.h,v 1.2 2014/01/07 02:13:00 joerg Exp $ */ | |
2 | /*- | 2 | /*- | |
3 | * Copyright (c) 1998-2004 Dag-Erling Coïdan Smørgrav | 3 | * Copyright (c) 1998-2004 Dag-Erling Coïdan Smørgrav | |
4 | * All rights reserved. | 4 | * All rights reserved. | |
5 | * | 5 | * | |
6 | * Redistribution and use in source and binary forms, with or without | 6 | * Redistribution and use in source and binary forms, with or without | |
7 | * modification, are permitted provided that the following conditions | 7 | * modification, are permitted provided that the following conditions | |
8 | * are met: | 8 | * are met: | |
9 | * 1. Redistributions of source code must retain the above copyright | 9 | * 1. Redistributions of source code must retain the above copyright | |
10 | * notice, this list of conditions and the following disclaimer | 10 | * notice, this list of conditions and the following disclaimer | |
11 | * in this position and unchanged. | 11 | * in this position and unchanged. | |
12 | * 2. Redistributions in binary form must reproduce the above copyright | 12 | * 2. Redistributions in binary form must reproduce the above copyright | |
13 | * notice, this list of conditions and the following disclaimer in the | 13 | * notice, this list of conditions and the following disclaimer in the | |
14 | * documentation and/or other materials provided with the distribution. | 14 | * documentation and/or other materials provided with the distribution. | |
15 | * 3. The name of the author may not be used to endorse or promote products | 15 | * 3. The name of the author may not be used to endorse or promote products | |
16 | * derived from this software without specific prior written permission | 16 | * derived from this software without specific prior written permission | |
17 | * | 17 | * | |
18 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | 18 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | |
19 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | 19 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |
20 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | 20 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | |
21 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | 21 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | |
22 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | 22 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |
23 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 23 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
24 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 24 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
25 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 25 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | 26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
27 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 27 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
28 | * | 28 | * | |
29 | * $FreeBSD: common.h,v 1.30 2007/12/18 11:03:07 des Exp $ | 29 | * $FreeBSD: common.h,v 1.30 2007/12/18 11:03:07 des Exp $ | |
30 | */ | 30 | */ | |
31 | 31 | |||
32 | #ifndef _COMMON_H_INCLUDED | 32 | #ifndef _COMMON_H_INCLUDED | |
33 | #define _COMMON_H_INCLUDED | 33 | #define _COMMON_H_INCLUDED | |
34 | 34 | |||
35 | #define FTP_DEFAULT_PORT 21 | 35 | #define FTP_DEFAULT_PORT 21 | |
36 | #define HTTP_DEFAULT_PORT 80 | 36 | #define HTTP_DEFAULT_PORT 80 | |
37 | #define FTP_DEFAULT_PROXY_PORT 21 | 37 | #define FTP_DEFAULT_PROXY_PORT 21 | |
38 | #define HTTP_DEFAULT_PROXY_PORT 3128 | 38 | #define HTTP_DEFAULT_PROXY_PORT 3128 | |
39 | 39 | |||
40 | #ifdef WITH_SSL | 40 | #ifdef WITH_SSL | |
41 | #include <openssl/crypto.h> | 41 | #include <openssl/crypto.h> | |
42 | #include <openssl/x509.h> | 42 | #include <openssl/x509.h> | |
43 | #include <openssl/pem.h> | 43 | #include <openssl/pem.h> | |
44 | #include <openssl/ssl.h> | 44 | #include <openssl/ssl.h> | |
45 | #include <openssl/err.h> | 45 | #include <openssl/err.h> | |
46 | #endif | 46 | #endif | |
47 | 47 | |||
48 | #if !defined(__sun) && !defined(__hpux) && !defined(__INTERIX) && \ | 48 | #if !defined(__sun) && !defined(__hpux) && !defined(__INTERIX) && \ | |
49 | !defined(__digital__) && !defined(__linux) && !defined(__MINT__) && \ | 49 | !defined(__digital__) && !defined(__linux) && !defined(__MINT__) && \ | |
50 | !defined(__sgi) | 50 | !defined(__sgi) | |
51 | #define HAVE_SA_LEN | 51 | #define HAVE_SA_LEN | |
52 | #endif | 52 | #endif | |
53 | 53 | |||
54 | /* Connection */ | 54 | /* Connection */ | |
55 | typedef struct fetchconn conn_t; | 55 | typedef struct fetchconn conn_t; | |
56 | 56 | |||
57 | struct fetchconn { | 57 | struct fetchconn { | |
58 | int sd; /* socket descriptor */ | 58 | int sd; /* socket descriptor */ | |
59 | char *buf; /* buffer */ | 59 | char *buf; /* buffer */ | |
60 | size_t bufsize; /* buffer size */ | 60 | size_t bufsize; /* buffer size */ | |
61 | size_t buflen; /* length of buffer contents */ | 61 | size_t buflen; /* length of buffer contents */ | |
62 | char *next_buf; /* pending buffer, e.g. after getln */ | 62 | char *next_buf; /* pending buffer, e.g. after getln */ | |
63 | size_t next_len; /* size of pending buffer */ | 63 | size_t next_len; /* size of pending buffer */ | |
64 | int err; /* last protocol reply code */ | 64 | int err; /* last protocol reply code */ | |
65 | #ifdef WITH_SSL | 65 | #ifdef WITH_SSL | |
66 | SSL *ssl; /* SSL handle */ | 66 | SSL *ssl; /* SSL handle */ | |
67 | SSL_CTX *ssl_ctx; /* SSL context */ | 67 | SSL_CTX *ssl_ctx; /* SSL context */ | |
68 | X509 *ssl_cert; /* server certificate */ | 68 | X509 *ssl_cert; /* server certificate */ | |
69 | # if OPENSSL_VERSION_NUMBER < 0x00909000L | 69 | # if OPENSSL_VERSION_NUMBER < 0x00909000L | |
70 | SSL_METHOD *ssl_meth; /* SSL method */ | 70 | SSL_METHOD *ssl_meth; /* SSL method */ | |
71 | # else | 71 | # else | |
72 | const SSL_METHOD *ssl_meth; /* SSL method */ | 72 | const SSL_METHOD *ssl_meth; /* SSL method */ | |
73 | # endif | 73 | # endif | |
74 | #endif | 74 | #endif | |
75 | 75 | |||
76 | char *ftp_home; | 76 | char *ftp_home; | |
77 | 77 | |||
78 | struct url *cache_url; | 78 | struct url *cache_url; | |
79 | int cache_af; | 79 | int cache_af; | |
80 | int (*cache_close)(conn_t *); | 80 | int (*cache_close)(conn_t *); | |
81 | conn_t *next_cached; | 81 | conn_t *next_cached; | |
82 | }; | 82 | }; | |
83 | 83 | |||
84 | /* Structure used for error message lists */ | 84 | /* Structure used for error message lists */ | |
85 | struct fetcherr { | 85 | struct fetcherr { | |
86 | const int num; | 86 | const int num; | |
87 | const int cat; | 87 | const int cat; | |
88 | const char *string; | 88 | const char *string; | |
89 | }; | 89 | }; | |
90 | 90 | |||
91 | void fetch_seterr(struct fetcherr *, int); | 91 | void fetch_seterr(struct fetcherr *, int); | |
92 | void fetch_syserr(void); | 92 | void fetch_syserr(void); | |
93 | void fetch_info(const char *, ...); | 93 | void fetch_info(const char *, ...) __printflike(1, 2); | |
94 | int fetch_default_port(const char *); | 94 | int fetch_default_port(const char *); | |
95 | int fetch_default_proxy_port(const char *); | 95 | int fetch_default_proxy_port(const char *); | |
96 | int fetch_bind(int, int, const char *); | 96 | int fetch_bind(int, int, const char *); | |
97 | conn_t *fetch_cache_get(const struct url *, int); | 97 | conn_t *fetch_cache_get(const struct url *, int); | |
98 | void fetch_cache_put(conn_t *, int (*)(conn_t *)); | 98 | void fetch_cache_put(conn_t *, int (*)(conn_t *)); | |
99 | conn_t *fetch_connect(struct url *, int, int); | 99 | conn_t *fetch_connect(struct url *, int, int); | |
100 | conn_t *fetch_reopen(int); | 100 | conn_t *fetch_reopen(int); | |
101 | int fetch_ssl(conn_t *, int); | 101 | int fetch_ssl(conn_t *, int); | |
102 | ssize_t fetch_read(conn_t *, char *, size_t); | 102 | ssize_t fetch_read(conn_t *, char *, size_t); | |
103 | int fetch_getln(conn_t *); | 103 | int fetch_getln(conn_t *); | |
104 | ssize_t fetch_write(conn_t *, const void *, size_t); | 104 | ssize_t fetch_write(conn_t *, const void *, size_t); | |
105 | int fetch_close(conn_t *); | 105 | int fetch_close(conn_t *); | |
106 | int fetch_add_entry(struct url_list *, struct url *, const char *, int); | 106 | int fetch_add_entry(struct url_list *, struct url *, const char *, int); | |
107 | int fetch_netrc_auth(struct url *url); | 107 | int fetch_netrc_auth(struct url *url); | |
108 | int fetch_no_proxy_match(const char *); | 108 | int fetch_no_proxy_match(const char *); | |
109 | int fetch_urlpath_safe(char); | 109 | int fetch_urlpath_safe(char); | |
110 | 110 | |||
111 | #define ftp_seterr(n) fetch_seterr(ftp_errlist, n) | 111 | #define ftp_seterr(n) fetch_seterr(ftp_errlist, n) | |
112 | #define http_seterr(n) fetch_seterr(http_errlist, n) | 112 | #define http_seterr(n) fetch_seterr(http_errlist, n) | |
113 | #define netdb_seterr(n) fetch_seterr(netdb_errlist, n) | 113 | #define netdb_seterr(n) fetch_seterr(netdb_errlist, n) | |
114 | #define url_seterr(n) fetch_seterr(url_errlist, n) | 114 | #define url_seterr(n) fetch_seterr(url_errlist, n) | |
115 | 115 | |||
116 | fetchIO *fetchIO_unopen(void *, ssize_t (*)(void *, void *, size_t), | 116 | fetchIO *fetchIO_unopen(void *, ssize_t (*)(void *, void *, size_t), | |
117 | ssize_t (*)(void *, const void *, size_t), void (*)(void *)); | 117 | ssize_t (*)(void *, const void *, size_t), void (*)(void *)); | |
118 | 118 | |||
119 | /* | 119 | /* | |
120 | * I don't really like exporting http_request() and ftp_request(), | 120 | * I don't really like exporting http_request() and ftp_request(), | |
121 | * but the HTTP and FTP code occasionally needs to cross-call | 121 | * but the HTTP and FTP code occasionally needs to cross-call | |
122 | * eachother, and this saves me from adding a lot of special-case code | 122 | * eachother, and this saves me from adding a lot of special-case code | |
123 | * to handle those cases. | 123 | * to handle those cases. | |
124 | * | 124 | * | |
125 | * Note that _*_request() free purl, which is way ugly but saves us a | 125 | * Note that _*_request() free purl, which is way ugly but saves us a | |
126 | * whole lot of trouble. | 126 | * whole lot of trouble. | |
127 | */ | 127 | */ | |
128 | fetchIO *http_request(struct url *, const char *, | 128 | fetchIO *http_request(struct url *, const char *, | |
129 | struct url_stat *, struct url *, const char *); | 129 | struct url_stat *, struct url *, const char *); | |
130 | fetchIO *ftp_request(struct url *, const char *, const char *, | 130 | fetchIO *ftp_request(struct url *, const char *, const char *, | |
131 | struct url_stat *, struct url *, const char *); | 131 | struct url_stat *, struct url *, const char *); | |
132 | 132 | |||
133 | 133 | |||
134 | /* | 134 | /* | |
135 | * Check whether a particular flag is set | 135 | * Check whether a particular flag is set | |
136 | */ | 136 | */ | |
137 | #define CHECK_FLAG(x) (flags && strchr(flags, (x))) | 137 | #define CHECK_FLAG(x) (flags && strchr(flags, (x))) | |
138 | 138 | |||
139 | #endif | 139 | #endif |
--- src/external/bsd/fetch/dist/libfetch/ftp.c 2013/10/19 22:58:40 1.6
+++ src/external/bsd/fetch/dist/libfetch/ftp.c 2014/01/07 02:13:00 1.7
@@ -1,1311 +1,1312 @@ | @@ -1,1311 +1,1312 @@ | |||
1 | /* $NetBSD: ftp.c,v 1.6 2013/10/19 22:58:40 mrg Exp $ */ | 1 | /* $NetBSD: ftp.c,v 1.7 2014/01/07 02:13:00 joerg Exp $ */ | |
2 | /*- | 2 | /*- | |
3 | * Copyright (c) 1998-2004 Dag-Erling Coïdan Smørgrav | 3 | * Copyright (c) 1998-2004 Dag-Erling Coïdan Smørgrav | |
4 | * Copyright (c) 2008, 2009, 2010 Joerg Sonnenberger <joerg@NetBSD.org> | 4 | * Copyright (c) 2008, 2009, 2010 Joerg Sonnenberger <joerg@NetBSD.org> | |
5 | * All rights reserved. | 5 | * All rights reserved. | |
6 | * | 6 | * | |
7 | * Redistribution and use in source and binary forms, with or without | 7 | * Redistribution and use in source and binary forms, with or without | |
8 | * modification, are permitted provided that the following conditions | 8 | * modification, are permitted provided that the following conditions | |
9 | * are met: | 9 | * are met: | |
10 | * 1. Redistributions of source code must retain the above copyright | 10 | * 1. Redistributions of source code must retain the above copyright | |
11 | * notice, this list of conditions and the following disclaimer | 11 | * notice, this list of conditions and the following disclaimer | |
12 | * in this position and unchanged. | 12 | * in this position and unchanged. | |
13 | * 2. Redistributions in binary form must reproduce the above copyright | 13 | * 2. Redistributions in binary form must reproduce the above copyright | |
14 | * notice, this list of conditions and the following disclaimer in the | 14 | * notice, this list of conditions and the following disclaimer in the | |
15 | * documentation and/or other materials provided with the distribution. | 15 | * documentation and/or other materials provided with the distribution. | |
16 | * 3. The name of the author may not be used to endorse or promote products | 16 | * 3. The name of the author may not be used to endorse or promote products | |
17 | * derived from this software without specific prior written permission | 17 | * derived from this software without specific prior written permission | |
18 | * | 18 | * | |
19 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | 19 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | |
20 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | 20 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |
21 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | 21 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | |
22 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | 22 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | |
23 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | 23 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |
24 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 24 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
25 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 25 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
26 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 26 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | 27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
28 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 28 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
29 | * | 29 | * | |
30 | * $FreeBSD: ftp.c,v 1.101 2008/01/23 20:57:59 des Exp $ | 30 | * $FreeBSD: ftp.c,v 1.101 2008/01/23 20:57:59 des Exp $ | |
31 | */ | 31 | */ | |
32 | 32 | |||
33 | /* | 33 | /* | |
34 | * Portions of this code were taken from or based on ftpio.c: | 34 | * Portions of this code were taken from or based on ftpio.c: | |
35 | * | 35 | * | |
36 | * ---------------------------------------------------------------------------- | 36 | * ---------------------------------------------------------------------------- | |
37 | * "THE BEER-WARE LICENSE" (Revision 42): | 37 | * "THE BEER-WARE LICENSE" (Revision 42): | |
38 | * <phk@FreeBSD.org> wrote this file. As long as you retain this notice you | 38 | * <phk@FreeBSD.org> wrote this file. As long as you retain this notice you | |
39 | * can do whatever you want with this stuff. If we meet some day, and you think | 39 | * can do whatever you want with this stuff. If we meet some day, and you think | |
40 | * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp | 40 | * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp | |
41 | * ---------------------------------------------------------------------------- | 41 | * ---------------------------------------------------------------------------- | |
42 | * | 42 | * | |
43 | * Major Changelog: | 43 | * Major Changelog: | |
44 | * | 44 | * | |
45 | * Dag-Erling Coïdan Smørgrav | 45 | * Dag-Erling Coïdan Smørgrav | |
46 | * 9 Jun 1998 | 46 | * 9 Jun 1998 | |
47 | * | 47 | * | |
48 | * Incorporated into libfetch | 48 | * Incorporated into libfetch | |
49 | * | 49 | * | |
50 | * Jordan K. Hubbard | 50 | * Jordan K. Hubbard | |
51 | * 17 Jan 1996 | 51 | * 17 Jan 1996 | |
52 | * | 52 | * | |
53 | * Turned inside out. Now returns xfers as new file ids, not as a special | 53 | * Turned inside out. Now returns xfers as new file ids, not as a special | |
54 | * `state' of FTP_t | 54 | * `state' of FTP_t | |
55 | * | 55 | * | |
56 | * $ftpioId: ftpio.c,v 1.30 1998/04/11 07:28:53 phk Exp $ | 56 | * $ftpioId: ftpio.c,v 1.30 1998/04/11 07:28:53 phk Exp $ | |
57 | * | 57 | * | |
58 | */ | 58 | */ | |
59 | 59 | |||
60 | #ifdef __linux__ | 60 | #ifdef __linux__ | |
61 | /* Keep this down to Linux, it can create surprises else where. */ | 61 | /* Keep this down to Linux, it can create surprises else where. */ | |
62 | #define _GNU_SOURCE | 62 | #define _GNU_SOURCE | |
63 | #endif | 63 | #endif | |
64 | 64 | |||
65 | #if HAVE_CONFIG_H | 65 | #if HAVE_CONFIG_H | |
66 | #include "config.h" | 66 | #include "config.h" | |
67 | #endif | 67 | #endif | |
68 | #ifndef NETBSD | 68 | #ifndef NETBSD | |
69 | #include <nbcompat.h> | 69 | #include <nbcompat.h> | |
70 | #endif | 70 | #endif | |
71 | 71 | |||
72 | #include <sys/types.h> | 72 | #include <sys/types.h> | |
73 | #include <sys/socket.h> | 73 | #include <sys/socket.h> | |
74 | 74 | |||
75 | #include <netinet/in.h> | 75 | #include <netinet/in.h> | |
76 | #include <arpa/inet.h> | 76 | #include <arpa/inet.h> | |
77 | 77 | |||
78 | #include <ctype.h> | 78 | #include <ctype.h> | |
79 | #include <errno.h> | 79 | #include <errno.h> | |
80 | #include <fcntl.h> | 80 | #include <fcntl.h> | |
81 | #if defined(HAVE_INTTYPES_H) || defined(NETBSD) | 81 | #if defined(HAVE_INTTYPES_H) || defined(NETBSD) | |
82 | #include <inttypes.h> | 82 | #include <inttypes.h> | |
83 | #endif | 83 | #endif | |
84 | #include <stdarg.h> | 84 | #include <stdarg.h> | |
85 | #ifndef NETBSD | 85 | #ifndef NETBSD | |
86 | #include <nbcompat/netdb.h> | 86 | #include <nbcompat/netdb.h> | |
87 | #include <nbcompat/stdio.h> | 87 | #include <nbcompat/stdio.h> | |
88 | #else | 88 | #else | |
89 | #include <netdb.h> | 89 | #include <netdb.h> | |
90 | #include <stdio.h> | 90 | #include <stdio.h> | |
91 | #endif | 91 | #endif | |
92 | #include <stdlib.h> | 92 | #include <stdlib.h> | |
93 | #include <string.h> | 93 | #include <string.h> | |
94 | #include <time.h> | 94 | #include <time.h> | |
95 | #include <unistd.h> | 95 | #include <unistd.h> | |
96 | 96 | |||
97 | #include "fetch.h" | 97 | #include "fetch.h" | |
98 | #include "common.h" | 98 | #include "common.h" | |
99 | #include "ftperr.h" | 99 | #include "ftperr.h" | |
100 | 100 | |||
101 | #define FTP_ANONYMOUS_USER "anonymous" | 101 | #define FTP_ANONYMOUS_USER "anonymous" | |
102 | 102 | |||
103 | #define FTP_CONNECTION_ALREADY_OPEN 125 | 103 | #define FTP_CONNECTION_ALREADY_OPEN 125 | |
104 | #define FTP_OPEN_DATA_CONNECTION 150 | 104 | #define FTP_OPEN_DATA_CONNECTION 150 | |
105 | #define FTP_OK 200 | 105 | #define FTP_OK 200 | |
106 | #define FTP_FILE_STATUS 213 | 106 | #define FTP_FILE_STATUS 213 | |
107 | #define FTP_SERVICE_READY 220 | 107 | #define FTP_SERVICE_READY 220 | |
108 | #define FTP_TRANSFER_COMPLETE 226 | 108 | #define FTP_TRANSFER_COMPLETE 226 | |
109 | #define FTP_PASSIVE_MODE 227 | 109 | #define FTP_PASSIVE_MODE 227 | |
110 | #define FTP_LPASSIVE_MODE 228 | 110 | #define FTP_LPASSIVE_MODE 228 | |
111 | #define FTP_EPASSIVE_MODE 229 | 111 | #define FTP_EPASSIVE_MODE 229 | |
112 | #define FTP_LOGGED_IN 230 | 112 | #define FTP_LOGGED_IN 230 | |
113 | #define FTP_FILE_ACTION_OK 250 | 113 | #define FTP_FILE_ACTION_OK 250 | |
114 | #define FTP_DIRECTORY_CREATED 257 /* multiple meanings */ | 114 | #define FTP_DIRECTORY_CREATED 257 /* multiple meanings */ | |
115 | #define FTP_FILE_CREATED 257 /* multiple meanings */ | 115 | #define FTP_FILE_CREATED 257 /* multiple meanings */ | |
116 | #define FTP_WORKING_DIRECTORY 257 /* multiple meanings */ | 116 | #define FTP_WORKING_DIRECTORY 257 /* multiple meanings */ | |
117 | #define FTP_NEED_PASSWORD 331 | 117 | #define FTP_NEED_PASSWORD 331 | |
118 | #define FTP_NEED_ACCOUNT 332 | 118 | #define FTP_NEED_ACCOUNT 332 | |
119 | #define FTP_FILE_OK 350 | 119 | #define FTP_FILE_OK 350 | |
120 | #define FTP_SYNTAX_ERROR 500 | 120 | #define FTP_SYNTAX_ERROR 500 | |
121 | #define FTP_PROTOCOL_ERROR 999 | 121 | #define FTP_PROTOCOL_ERROR 999 | |
122 | 122 | |||
123 | #define isftpreply(foo) \ | 123 | #define isftpreply(foo) \ | |
124 | (isdigit((unsigned char)foo[0]) && \ | 124 | (isdigit((unsigned char)foo[0]) && \ | |
125 | isdigit((unsigned char)foo[1]) && \ | 125 | isdigit((unsigned char)foo[1]) && \ | |
126 | isdigit((unsigned char)foo[2]) && \ | 126 | isdigit((unsigned char)foo[2]) && \ | |
127 | (foo[3] == ' ' || foo[3] == '\0')) | 127 | (foo[3] == ' ' || foo[3] == '\0')) | |
128 | #define isftpinfo(foo) \ | 128 | #define isftpinfo(foo) \ | |
129 | (isdigit((unsigned char)foo[0]) && \ | 129 | (isdigit((unsigned char)foo[0]) && \ | |
130 | isdigit((unsigned char)foo[1]) && \ | 130 | isdigit((unsigned char)foo[1]) && \ | |
131 | isdigit((unsigned char)foo[2]) && \ | 131 | isdigit((unsigned char)foo[2]) && \ | |
132 | foo[3] == '-') | 132 | foo[3] == '-') | |
133 | 133 | |||
134 | /* | 134 | /* | |
135 | * Translate IPv4 mapped IPv6 address to IPv4 address | 135 | * Translate IPv4 mapped IPv6 address to IPv4 address | |
136 | */ | 136 | */ | |
137 | static void | 137 | static void | |
138 | unmappedaddr(struct sockaddr_in6 *sin6, socklen_t *len) | 138 | unmappedaddr(struct sockaddr_in6 *sin6, socklen_t *len) | |
139 | { | 139 | { | |
140 | struct sockaddr_in *sin4; | 140 | struct sockaddr_in *sin4; | |
141 | void *addrp; | 141 | void *addrp; | |
142 | uint32_t addr; | 142 | uint32_t addr; | |
143 | int port; | 143 | int port; | |
144 | 144 | |||
145 | if (sin6->sin6_family != AF_INET6 || | 145 | if (sin6->sin6_family != AF_INET6 || | |
146 | !IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) | 146 | !IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) | |
147 | return; | 147 | return; | |
148 | sin4 = (struct sockaddr_in *)(void *)sin6; | 148 | sin4 = (struct sockaddr_in *)(void *)sin6; | |
149 | addrp = &sin6->sin6_addr.s6_addr[12]; | 149 | addrp = &sin6->sin6_addr.s6_addr[12]; | |
150 | addr = *(uint32_t *)addrp; | 150 | addr = *(uint32_t *)addrp; | |
151 | port = sin6->sin6_port; | 151 | port = sin6->sin6_port; | |
152 | memset(sin4, 0, sizeof(struct sockaddr_in)); | 152 | memset(sin4, 0, sizeof(struct sockaddr_in)); | |
153 | sin4->sin_addr.s_addr = addr; | 153 | sin4->sin_addr.s_addr = addr; | |
154 | sin4->sin_port = port; | 154 | sin4->sin_port = port; | |
155 | sin4->sin_family = AF_INET; | 155 | sin4->sin_family = AF_INET; | |
156 | *len = sizeof(struct sockaddr_in); | 156 | *len = sizeof(struct sockaddr_in); | |
157 | #ifdef HAVE_SA_LEN | 157 | #ifdef HAVE_SA_LEN | |
158 | sin4->sin_len = sizeof(struct sockaddr_in); | 158 | sin4->sin_len = sizeof(struct sockaddr_in); | |
159 | #endif | 159 | #endif | |
160 | } | 160 | } | |
161 | 161 | |||
162 | /* | 162 | /* | |
163 | * Get server response | 163 | * Get server response | |
164 | */ | 164 | */ | |
165 | static int | 165 | static int | |
166 | ftp_chkerr(conn_t *conn) | 166 | ftp_chkerr(conn_t *conn) | |
167 | { | 167 | { | |
168 | if (fetch_getln(conn) == -1) { | 168 | if (fetch_getln(conn) == -1) { | |
169 | fetch_syserr(); | 169 | fetch_syserr(); | |
170 | return (-1); | 170 | return (-1); | |
171 | } | 171 | } | |
172 | if (isftpinfo(conn->buf)) { | 172 | if (isftpinfo(conn->buf)) { | |
173 | while (conn->buflen && !isftpreply(conn->buf)) { | 173 | while (conn->buflen && !isftpreply(conn->buf)) { | |
174 | if (fetch_getln(conn) == -1) { | 174 | if (fetch_getln(conn) == -1) { | |
175 | fetch_syserr(); | 175 | fetch_syserr(); | |
176 | return (-1); | 176 | return (-1); | |
177 | } | 177 | } | |
178 | } | 178 | } | |
179 | } | 179 | } | |
180 | 180 | |||
181 | while (conn->buflen && | 181 | while (conn->buflen && | |
182 | isspace((unsigned char)conn->buf[conn->buflen - 1])) | 182 | isspace((unsigned char)conn->buf[conn->buflen - 1])) | |
183 | conn->buflen--; | 183 | conn->buflen--; | |
184 | conn->buf[conn->buflen] = '\0'; | 184 | conn->buf[conn->buflen] = '\0'; | |
185 | 185 | |||
186 | if (!isftpreply(conn->buf)) { | 186 | if (!isftpreply(conn->buf)) { | |
187 | ftp_seterr(FTP_PROTOCOL_ERROR); | 187 | ftp_seterr(FTP_PROTOCOL_ERROR); | |
188 | return (-1); | 188 | return (-1); | |
189 | } | 189 | } | |
190 | 190 | |||
191 | conn->err = (conn->buf[0] - '0') * 100 | 191 | conn->err = (conn->buf[0] - '0') * 100 | |
192 | + (conn->buf[1] - '0') * 10 | 192 | + (conn->buf[1] - '0') * 10 | |
193 | + (conn->buf[2] - '0'); | 193 | + (conn->buf[2] - '0'); | |
194 | 194 | |||
195 | return (conn->err); | 195 | return (conn->err); | |
196 | } | 196 | } | |
197 | 197 | |||
198 | /* | 198 | /* | |
199 | * Send a command and check reply | 199 | * Send a command and check reply | |
200 | */ | 200 | */ | |
201 | __printflike(2, 3) | |||
201 | static int | 202 | static int | |
202 | ftp_cmd(conn_t *conn, const char *fmt, ...) | 203 | ftp_cmd(conn_t *conn, const char *fmt, ...) | |
203 | { | 204 | { | |
204 | va_list ap; | 205 | va_list ap; | |
205 | size_t len; | 206 | size_t len; | |
206 | char *msg; | 207 | char *msg; | |
207 | ssize_t r; | 208 | ssize_t r; | |
208 | 209 | |||
209 | va_start(ap, fmt); | 210 | va_start(ap, fmt); | |
210 | len = vasprintf(&msg, fmt, ap); | 211 | len = vasprintf(&msg, fmt, ap); | |
211 | va_end(ap); | 212 | va_end(ap); | |
212 | 213 | |||
213 | if (msg == NULL) { | 214 | if (msg == NULL) { | |
214 | errno = ENOMEM; | 215 | errno = ENOMEM; | |
215 | fetch_syserr(); | 216 | fetch_syserr(); | |
216 | return (-1); | 217 | return (-1); | |
217 | } | 218 | } | |
218 | 219 | |||
219 | r = fetch_write(conn, msg, len); | 220 | r = fetch_write(conn, msg, len); | |
220 | free(msg); | 221 | free(msg); | |
221 | 222 | |||
222 | if (r == -1) { | 223 | if (r == -1) { | |
223 | fetch_syserr(); | 224 | fetch_syserr(); | |
224 | return (-1); | 225 | return (-1); | |
225 | } | 226 | } | |
226 | 227 | |||
227 | return (ftp_chkerr(conn)); | 228 | return (ftp_chkerr(conn)); | |
228 | } | 229 | } | |
229 | 230 | |||
230 | /* | 231 | /* | |
231 | * Return a pointer to the filename part of a path | 232 | * Return a pointer to the filename part of a path | |
232 | */ | 233 | */ | |
233 | static const char * | 234 | static const char * | |
234 | ftp_filename(const char *file, size_t *len, int *type, int subdir) | 235 | ftp_filename(const char *file, size_t *len, int *type, int subdir) | |
235 | { | 236 | { | |
236 | const char *s; | 237 | const char *s; | |
237 | 238 | |||
238 | if ((s = strrchr(file, '/')) == NULL || subdir) | 239 | if ((s = strrchr(file, '/')) == NULL || subdir) | |
239 | s = file; | 240 | s = file; | |
240 | else | 241 | else | |
241 | s = s + 1; | 242 | s = s + 1; | |
242 | *len = strlen(s); | 243 | *len = strlen(s); | |
243 | if (*len > 7 && strncmp(s + *len - 7, ";type=", 6) == 0) { | 244 | if (*len > 7 && strncmp(s + *len - 7, ";type=", 6) == 0) { | |
244 | *type = s[*len - 1]; | 245 | *type = s[*len - 1]; | |
245 | *len -= 7; | 246 | *len -= 7; | |
246 | } else { | 247 | } else { | |
247 | *type = '\0'; | 248 | *type = '\0'; | |
248 | } | 249 | } | |
249 | return (s); | 250 | return (s); | |
250 | } | 251 | } | |
251 | 252 | |||
252 | /* | 253 | /* | |
253 | * Get current working directory from the reply to a CWD, PWD or CDUP | 254 | * Get current working directory from the reply to a CWD, PWD or CDUP | |
254 | * command. | 255 | * command. | |
255 | */ | 256 | */ | |
256 | static int | 257 | static int | |
257 | ftp_pwd(conn_t *conn, char **pwd) | 258 | ftp_pwd(conn_t *conn, char **pwd) | |
258 | { | 259 | { | |
259 | char *src, *dst, *end; | 260 | char *src, *dst, *end; | |
260 | int q; | 261 | int q; | |
261 | size_t len; | 262 | size_t len; | |
262 | 263 | |||
263 | if (conn->err != FTP_WORKING_DIRECTORY && | 264 | if (conn->err != FTP_WORKING_DIRECTORY && | |
264 | conn->err != FTP_FILE_ACTION_OK) | 265 | conn->err != FTP_FILE_ACTION_OK) | |
265 | return (FTP_PROTOCOL_ERROR); | 266 | return (FTP_PROTOCOL_ERROR); | |
266 | end = conn->buf + conn->buflen; | 267 | end = conn->buf + conn->buflen; | |
267 | src = conn->buf + 4; | 268 | src = conn->buf + 4; | |
268 | if (src >= end || *src++ != '"') | 269 | if (src >= end || *src++ != '"') | |
269 | return (FTP_PROTOCOL_ERROR); | 270 | return (FTP_PROTOCOL_ERROR); | |
270 | len = end - src + 1; | 271 | len = end - src + 1; | |
271 | *pwd = malloc(len); | 272 | *pwd = malloc(len); | |
272 | if (*pwd == NULL) | 273 | if (*pwd == NULL) | |
273 | return (FTP_PROTOCOL_ERROR); | 274 | return (FTP_PROTOCOL_ERROR); | |
274 | for (q = 0, dst = *pwd; src < end; ++src) { | 275 | for (q = 0, dst = *pwd; src < end; ++src) { | |
275 | if (!q && *src == '"') | 276 | if (!q && *src == '"') | |
276 | q = 1; | 277 | q = 1; | |
277 | else if (q && *src != '"') | 278 | else if (q && *src != '"') | |
278 | break; | 279 | break; | |
279 | else if (q) | 280 | else if (q) | |
280 | *dst++ = '"', q = 0; | 281 | *dst++ = '"', q = 0; | |
281 | else | 282 | else | |
282 | *dst++ = *src; | 283 | *dst++ = *src; | |
283 | } | 284 | } | |
284 | *dst = '\0'; | 285 | *dst = '\0'; | |
285 | if (**pwd != '/') { | 286 | if (**pwd != '/') { | |
286 | free(*pwd); | 287 | free(*pwd); | |
287 | *pwd = NULL; | 288 | *pwd = NULL; | |
288 | return (FTP_PROTOCOL_ERROR); | 289 | return (FTP_PROTOCOL_ERROR); | |
289 | } | 290 | } | |
290 | return (FTP_OK); | 291 | return (FTP_OK); | |
291 | } | 292 | } | |
292 | 293 | |||
293 | /* | 294 | /* | |
294 | * Change working directory to the directory that contains the specified | 295 | * Change working directory to the directory that contains the specified | |
295 | * file. | 296 | * file. | |
296 | */ | 297 | */ | |
297 | static int | 298 | static int | |
298 | ftp_cwd(conn_t *conn, const char *path, int subdir) | 299 | ftp_cwd(conn_t *conn, const char *path, int subdir) | |
299 | { | 300 | { | |
300 | const char *beg, *end; | 301 | const char *beg, *end; | |
301 | char *pwd, *dst; | 302 | char *pwd, *dst; | |
302 | int e; | 303 | int e; | |
303 | size_t i, len; | 304 | size_t i, len; | |
304 | 305 | |||
305 | if (*path != '/') { | 306 | if (*path != '/') { | |
306 | ftp_seterr(501); | 307 | ftp_seterr(501); | |
307 | return (-1); | 308 | return (-1); | |
308 | } | 309 | } | |
309 | ++path; | 310 | ++path; | |
310 | 311 | |||
311 | /* Simple case: still in the home directory and no directory change. */ | 312 | /* Simple case: still in the home directory and no directory change. */ | |
312 | if (conn->ftp_home == NULL && strchr(path, '/') == NULL && | 313 | if (conn->ftp_home == NULL && strchr(path, '/') == NULL && | |
313 | (!subdir || *path == '\0')) | 314 | (!subdir || *path == '\0')) | |
314 | return 0; | 315 | return 0; | |
315 | 316 | |||
316 | if ((e = ftp_cmd(conn, "PWD\r\n")) != FTP_WORKING_DIRECTORY || | 317 | if ((e = ftp_cmd(conn, "PWD\r\n")) != FTP_WORKING_DIRECTORY || | |
317 | (e = ftp_pwd(conn, &pwd)) != FTP_OK) { | 318 | (e = ftp_pwd(conn, &pwd)) != FTP_OK) { | |
318 | ftp_seterr(e); | 319 | ftp_seterr(e); | |
319 | return (-1); | 320 | return (-1); | |
320 | } | 321 | } | |
321 | if (conn->ftp_home == NULL && (conn->ftp_home = strdup(pwd)) == NULL) { | 322 | if (conn->ftp_home == NULL && (conn->ftp_home = strdup(pwd)) == NULL) { | |
322 | fetch_syserr(); | 323 | fetch_syserr(); | |
323 | free(pwd); | 324 | free(pwd); | |
324 | return (-1); | 325 | return (-1); | |
325 | } | 326 | } | |
326 | if (*path == '/') { | 327 | if (*path == '/') { | |
327 | while (path[1] == '/') | 328 | while (path[1] == '/') | |
328 | ++path; | 329 | ++path; | |
329 | dst = strdup(path); | 330 | dst = strdup(path); | |
330 | } else if (strcmp(conn->ftp_home, "/") == 0) { | 331 | } else if (strcmp(conn->ftp_home, "/") == 0) { | |
331 | dst = strdup(path - 1); | 332 | dst = strdup(path - 1); | |
332 | } else { | 333 | } else { | |
333 | asprintf(&dst, "%s/%s", conn->ftp_home, path); | 334 | asprintf(&dst, "%s/%s", conn->ftp_home, path); | |
334 | } | 335 | } | |
335 | if (dst == NULL) { | 336 | if (dst == NULL) { | |
336 | fetch_syserr(); | 337 | fetch_syserr(); | |
337 | free(pwd); | 338 | free(pwd); | |
338 | return (-1); | 339 | return (-1); | |
339 | } | 340 | } | |
340 | 341 | |||
341 | if (subdir) | 342 | if (subdir) | |
342 | end = dst + strlen(dst); | 343 | end = dst + strlen(dst); | |
343 | else | 344 | else | |
344 | end = strrchr(dst, '/'); | 345 | end = strrchr(dst, '/'); | |
345 | 346 | |||
346 | for (;;) { | 347 | for (;;) { | |
347 | len = strlen(pwd); | 348 | len = strlen(pwd); | |
348 | 349 | |||
349 | /* Look for a common prefix between PWD and dir to fetch. */ | 350 | /* Look for a common prefix between PWD and dir to fetch. */ | |
350 | for (i = 0; i <= len && i <= (size_t)(end - dst); ++i) | 351 | for (i = 0; i <= len && i <= (size_t)(end - dst); ++i) | |
351 | if (pwd[i] != dst[i]) | 352 | if (pwd[i] != dst[i]) | |
352 | break; | 353 | break; | |
353 | /* Keep going up a dir until we have a matching prefix. */ | 354 | /* Keep going up a dir until we have a matching prefix. */ | |
354 | if (strcmp(pwd, "/") == 0) | 355 | if (strcmp(pwd, "/") == 0) | |
355 | break; | 356 | break; | |
356 | if (pwd[i] == '\0' && (dst[i - 1] == '/' || dst[i] == '/')) | 357 | if (pwd[i] == '\0' && (dst[i - 1] == '/' || dst[i] == '/')) | |
357 | break; | 358 | break; | |
358 | free(pwd); | 359 | free(pwd); | |
359 | if ((e = ftp_cmd(conn, "CDUP\r\n")) != FTP_FILE_ACTION_OK || | 360 | if ((e = ftp_cmd(conn, "CDUP\r\n")) != FTP_FILE_ACTION_OK || | |
360 | (e = ftp_cmd(conn, "PWD\r\n")) != FTP_WORKING_DIRECTORY || | 361 | (e = ftp_cmd(conn, "PWD\r\n")) != FTP_WORKING_DIRECTORY || | |
361 | (e = ftp_pwd(conn, &pwd)) != FTP_OK) { | 362 | (e = ftp_pwd(conn, &pwd)) != FTP_OK) { | |
362 | ftp_seterr(e); | 363 | ftp_seterr(e); | |
363 | free(dst); | 364 | free(dst); | |
364 | return (-1); | 365 | return (-1); | |
365 | } | 366 | } | |
366 | } | 367 | } | |
367 | free(pwd); | 368 | free(pwd); | |
368 | 369 | |||
369 | #ifdef FTP_COMBINE_CWDS | 370 | #ifdef FTP_COMBINE_CWDS | |
370 | /* Skip leading slashes, even "////". */ | 371 | /* Skip leading slashes, even "////". */ | |
371 | for (beg = dst + i; beg < end && *beg == '/'; ++beg, ++i) | 372 | for (beg = dst + i; beg < end && *beg == '/'; ++beg, ++i) | |
372 | /* nothing */ ; | 373 | /* nothing */ ; | |
373 | 374 | |||
374 | /* If there is no trailing dir, we're already there. */ | 375 | /* If there is no trailing dir, we're already there. */ | |
375 | if (beg >= end) { | 376 | if (beg >= end) { | |
376 | free(dst); | 377 | free(dst); | |
377 | return (0); | 378 | return (0); | |
378 | } | 379 | } | |
379 | 380 | |||
380 | /* Change to the directory all in one chunk (e.g., foo/bar/baz). */ | 381 | /* Change to the directory all in one chunk (e.g., foo/bar/baz). */ | |
381 | e = ftp_cmd(conn, "CWD %.*s\r\n", (int)(end - beg), beg); | 382 | e = ftp_cmd(conn, "CWD %.*s\r\n", (int)(end - beg), beg); | |
382 | if (e == FTP_FILE_ACTION_OK) { | 383 | if (e == FTP_FILE_ACTION_OK) { | |
383 | free(dst); | 384 | free(dst); | |
384 | return (0); | 385 | return (0); | |
385 | } | 386 | } | |
386 | #endif /* FTP_COMBINE_CWDS */ | 387 | #endif /* FTP_COMBINE_CWDS */ | |
387 | 388 | |||
388 | /* That didn't work so go back to legacy behavior (multiple CWDs). */ | 389 | /* That didn't work so go back to legacy behavior (multiple CWDs). */ | |
389 | for (beg = dst + i; beg < end; beg = dst + i + 1) { | 390 | for (beg = dst + i; beg < end; beg = dst + i + 1) { | |
390 | while (*beg == '/') | 391 | while (*beg == '/') | |
391 | ++beg, ++i; | 392 | ++beg, ++i; | |
392 | for (++i; dst + i < end && dst[i] != '/'; ++i) | 393 | for (++i; dst + i < end && dst[i] != '/'; ++i) | |
393 | /* nothing */ ; | 394 | /* nothing */ ; | |
394 | e = ftp_cmd(conn, "CWD %.*s\r\n", dst + i - beg, beg); | 395 | e = ftp_cmd(conn, "CWD %.*s\r\n", (int)(dst + i - beg), beg); | |
395 | if (e != FTP_FILE_ACTION_OK) { | 396 | if (e != FTP_FILE_ACTION_OK) { | |
396 | free(dst); | 397 | free(dst); | |
397 | ftp_seterr(e); | 398 | ftp_seterr(e); | |
398 | return (-1); | 399 | return (-1); | |
399 | } | 400 | } | |
400 | } | 401 | } | |
401 | free(dst); | 402 | free(dst); | |
402 | return (0); | 403 | return (0); | |
403 | } | 404 | } | |
404 | 405 | |||
405 | /* | 406 | /* | |
406 | * Set transfer mode and data type | 407 | * Set transfer mode and data type | |
407 | */ | 408 | */ | |
408 | static int | 409 | static int | |
409 | ftp_mode_type(conn_t *conn, int mode, int type) | 410 | ftp_mode_type(conn_t *conn, int mode, int type) | |
410 | { | 411 | { | |
411 | int e; | 412 | int e; | |
412 | 413 | |||
413 | switch (mode) { | 414 | switch (mode) { | |
414 | case 0: | 415 | case 0: | |
415 | case 's': | 416 | case 's': | |
416 | mode = 'S'; | 417 | mode = 'S'; | |
417 | /*FALLTHROUGH*/ | 418 | /*FALLTHROUGH*/ | |
418 | case 'S': | 419 | case 'S': | |
419 | break; | 420 | break; | |
420 | default: | 421 | default: | |
421 | return (FTP_PROTOCOL_ERROR); | 422 | return (FTP_PROTOCOL_ERROR); | |
422 | } | 423 | } | |
423 | if ((e = ftp_cmd(conn, "MODE %c\r\n", mode)) != FTP_OK) { | 424 | if ((e = ftp_cmd(conn, "MODE %c\r\n", mode)) != FTP_OK) { | |
424 | if (mode == 'S') { | 425 | if (mode == 'S') { | |
425 | /* | 426 | /* | |
426 | * Stream mode is supposed to be the default - so | 427 | * Stream mode is supposed to be the default - so | |
427 | * much so that some servers not only do not | 428 | * much so that some servers not only do not | |
428 | * support any other mode, but do not support the | 429 | * support any other mode, but do not support the | |
429 | * MODE command at all. | 430 | * MODE command at all. | |
430 | * | 431 | * | |
431 | * If "MODE S" fails, it is unlikely that we | 432 | * If "MODE S" fails, it is unlikely that we | |
432 | * previously succeeded in setting a different | 433 | * previously succeeded in setting a different | |
433 | * mode. Therefore, we simply hope that the | 434 | * mode. Therefore, we simply hope that the | |
434 | * server is already in the correct mode, and | 435 | * server is already in the correct mode, and | |
435 | * silently ignore the failure. | 436 | * silently ignore the failure. | |
436 | */ | 437 | */ | |
437 | } else { | 438 | } else { | |
438 | return (e); | 439 | return (e); | |
439 | } | 440 | } | |
440 | } | 441 | } | |
441 | 442 | |||
442 | switch (type) { | 443 | switch (type) { | |
443 | case 0: | 444 | case 0: | |
444 | case 'i': | 445 | case 'i': | |
445 | type = 'I'; | 446 | type = 'I'; | |
446 | /*FALLTHROUGH*/ | 447 | /*FALLTHROUGH*/ | |
447 | case 'I': | 448 | case 'I': | |
448 | break; | 449 | break; | |
449 | case 'a': | 450 | case 'a': | |
450 | type = 'A'; | 451 | type = 'A'; | |
451 | /*FALLTHROUGH*/ | 452 | /*FALLTHROUGH*/ | |
452 | case 'A': | 453 | case 'A': | |
453 | break; | 454 | break; | |
454 | case 'd': | 455 | case 'd': | |
455 | type = 'D'; | 456 | type = 'D'; | |
456 | /*FALLTHROUGH*/ | 457 | /*FALLTHROUGH*/ | |
457 | case 'D': | 458 | case 'D': | |
458 | /* can't handle yet */ | 459 | /* can't handle yet */ | |
459 | default: | 460 | default: | |
460 | return (FTP_PROTOCOL_ERROR); | 461 | return (FTP_PROTOCOL_ERROR); | |
461 | } | 462 | } | |
462 | if ((e = ftp_cmd(conn, "TYPE %c\r\n", type)) != FTP_OK) | 463 | if ((e = ftp_cmd(conn, "TYPE %c\r\n", type)) != FTP_OK) | |
463 | return (e); | 464 | return (e); | |
464 | 465 | |||
465 | return (FTP_OK); | 466 | return (FTP_OK); | |
466 | } | 467 | } | |
467 | 468 | |||
468 | /* | 469 | /* | |
469 | * Request and parse file stats | 470 | * Request and parse file stats | |
470 | */ | 471 | */ | |
471 | static int | 472 | static int | |
472 | ftp_stat(conn_t *conn, const char *file, struct url_stat *us) | 473 | ftp_stat(conn_t *conn, const char *file, struct url_stat *us) | |
473 | { | 474 | { | |
474 | char *ln; | 475 | char *ln; | |
475 | const char *filename; | 476 | const char *filename; | |
476 | size_t filenamelen; | 477 | size_t filenamelen; | |
477 | int type; | 478 | int type; | |
478 | struct tm tm; | 479 | struct tm tm; | |
479 | time_t t; | 480 | time_t t; | |
480 | int e; | 481 | int e; | |
481 | 482 | |||
482 | us->size = -1; | 483 | us->size = -1; | |
483 | us->atime = us->mtime = 0; | 484 | us->atime = us->mtime = 0; | |
484 | 485 | |||
485 | filename = ftp_filename(file, &filenamelen, &type, 0); | 486 | filename = ftp_filename(file, &filenamelen, &type, 0); | |
486 | 487 | |||
487 | if ((e = ftp_mode_type(conn, 0, type)) != FTP_OK) { | 488 | if ((e = ftp_mode_type(conn, 0, type)) != FTP_OK) { | |
488 | ftp_seterr(e); | 489 | ftp_seterr(e); | |
489 | return (-1); | 490 | return (-1); | |
490 | } | 491 | } | |
491 | 492 | |||
492 | e = ftp_cmd(conn, "SIZE %.*s\r\n", filenamelen, filename); | 493 | e = ftp_cmd(conn, "SIZE %.*s\r\n", (int)filenamelen, filename); | |
493 | if (e != FTP_FILE_STATUS) { | 494 | if (e != FTP_FILE_STATUS) { | |
494 | ftp_seterr(e); | 495 | ftp_seterr(e); | |
495 | return (-1); | 496 | return (-1); | |
496 | } | 497 | } | |
497 | for (ln = conn->buf + 4; *ln && isspace((unsigned char)*ln); ln++) | 498 | for (ln = conn->buf + 4; *ln && isspace((unsigned char)*ln); ln++) | |
498 | /* nothing */ ; | 499 | /* nothing */ ; | |
499 | for (us->size = 0; *ln && isdigit((unsigned char)*ln); ln++) | 500 | for (us->size = 0; *ln && isdigit((unsigned char)*ln); ln++) | |
500 | us->size = us->size * 10 + *ln - '0'; | 501 | us->size = us->size * 10 + *ln - '0'; | |
501 | if (*ln && !isspace((unsigned char)*ln)) { | 502 | if (*ln && !isspace((unsigned char)*ln)) { | |
502 | ftp_seterr(FTP_PROTOCOL_ERROR); | 503 | ftp_seterr(FTP_PROTOCOL_ERROR); | |
503 | us->size = -1; | 504 | us->size = -1; | |
504 | return (-1); | 505 | return (-1); | |
505 | } | 506 | } | |
506 | if (us->size == 0) | 507 | if (us->size == 0) | |
507 | us->size = -1; | 508 | us->size = -1; | |
508 | 509 | |||
509 | e = ftp_cmd(conn, "MDTM %.*s\r\n", filenamelen, filename); | 510 | e = ftp_cmd(conn, "MDTM %.*s\r\n", (int)filenamelen, filename); | |
510 | if (e != FTP_FILE_STATUS) { | 511 | if (e != FTP_FILE_STATUS) { | |
511 | ftp_seterr(e); | 512 | ftp_seterr(e); | |
512 | return (-1); | 513 | return (-1); | |
513 | } | 514 | } | |
514 | for (ln = conn->buf + 4; *ln && isspace((unsigned char)*ln); ln++) | 515 | for (ln = conn->buf + 4; *ln && isspace((unsigned char)*ln); ln++) | |
515 | /* nothing */ ; | 516 | /* nothing */ ; | |
516 | switch (strspn(ln, "0123456789")) { | 517 | switch (strspn(ln, "0123456789")) { | |
517 | case 14: | 518 | case 14: | |
518 | break; | 519 | break; | |
519 | case 15: | 520 | case 15: | |
520 | ln++; | 521 | ln++; | |
521 | ln[0] = '2'; | 522 | ln[0] = '2'; | |
522 | ln[1] = '0'; | 523 | ln[1] = '0'; | |
523 | break; | 524 | break; | |
524 | default: | 525 | default: | |
525 | ftp_seterr(FTP_PROTOCOL_ERROR); | 526 | ftp_seterr(FTP_PROTOCOL_ERROR); | |
526 | return (-1); | 527 | return (-1); | |
527 | } | 528 | } | |
528 | if (sscanf(ln, "%04d%02d%02d%02d%02d%02d", | 529 | if (sscanf(ln, "%04d%02d%02d%02d%02d%02d", | |
529 | &tm.tm_year, &tm.tm_mon, &tm.tm_mday, | 530 | &tm.tm_year, &tm.tm_mon, &tm.tm_mday, | |
530 | &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) { | 531 | &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) { | |
531 | ftp_seterr(FTP_PROTOCOL_ERROR); | 532 | ftp_seterr(FTP_PROTOCOL_ERROR); | |
532 | return (-1); | 533 | return (-1); | |
533 | } | 534 | } | |
534 | tm.tm_mon--; | 535 | tm.tm_mon--; | |
535 | tm.tm_year -= 1900; | 536 | tm.tm_year -= 1900; | |
536 | tm.tm_isdst = -1; | 537 | tm.tm_isdst = -1; | |
537 | t = timegm(&tm); | 538 | t = timegm(&tm); | |
538 | if (t == (time_t)-1) | 539 | if (t == (time_t)-1) | |
539 | t = time(NULL); | 540 | t = time(NULL); | |
540 | us->mtime = t; | 541 | us->mtime = t; | |
541 | us->atime = t; | 542 | us->atime = t; | |
542 | 543 | |||
543 | return (0); | 544 | return (0); | |
544 | } | 545 | } | |
545 | 546 | |||
546 | /* | 547 | /* | |
547 | * I/O functions for FTP | 548 | * I/O functions for FTP | |
548 | */ | 549 | */ | |
549 | struct ftpio { | 550 | struct ftpio { | |
550 | conn_t *cconn; /* Control connection */ | 551 | conn_t *cconn; /* Control connection */ | |
551 | conn_t *dconn; /* Data connection */ | 552 | conn_t *dconn; /* Data connection */ | |
552 | int dir; /* Direction */ | 553 | int dir; /* Direction */ | |
553 | int eof; /* EOF reached */ | 554 | int eof; /* EOF reached */ | |
554 | int err; /* Error code */ | 555 | int err; /* Error code */ | |
555 | }; | 556 | }; | |
556 | 557 | |||
557 | static ssize_t ftp_readfn(void *, void *, size_t); | 558 | static ssize_t ftp_readfn(void *, void *, size_t); | |
558 | static ssize_t ftp_writefn(void *, const void *, size_t); | 559 | static ssize_t ftp_writefn(void *, const void *, size_t); | |
559 | static void ftp_closefn(void *); | 560 | static void ftp_closefn(void *); | |
560 | 561 | |||
561 | static ssize_t | 562 | static ssize_t | |
562 | ftp_readfn(void *v, void *buf, size_t len) | 563 | ftp_readfn(void *v, void *buf, size_t len) | |
563 | { | 564 | { | |
564 | struct ftpio *io; | 565 | struct ftpio *io; | |
565 | ssize_t r; | 566 | ssize_t r; | |
566 | 567 | |||
567 | io = (struct ftpio *)v; | 568 | io = (struct ftpio *)v; | |
568 | if (io == NULL) { | 569 | if (io == NULL) { | |
569 | errno = EBADF; | 570 | errno = EBADF; | |
570 | return (-1); | 571 | return (-1); | |
571 | } | 572 | } | |
572 | if (io->cconn == NULL || io->dconn == NULL || io->dir == O_WRONLY) { | 573 | if (io->cconn == NULL || io->dconn == NULL || io->dir == O_WRONLY) { | |
573 | errno = EBADF; | 574 | errno = EBADF; | |
574 | return (-1); | 575 | return (-1); | |
575 | } | 576 | } | |
576 | if (io->err) { | 577 | if (io->err) { | |
577 | errno = io->err; | 578 | errno = io->err; | |
578 | return (-1); | 579 | return (-1); | |
579 | } | 580 | } | |
580 | if (io->eof) | 581 | if (io->eof) | |
581 | return (0); | 582 | return (0); | |
582 | r = fetch_read(io->dconn, buf, len); | 583 | r = fetch_read(io->dconn, buf, len); | |
583 | if (r > 0) | 584 | if (r > 0) | |
584 | return (r); | 585 | return (r); | |
585 | if (r == 0) { | 586 | if (r == 0) { | |
586 | io->eof = 1; | 587 | io->eof = 1; | |
587 | return (0); | 588 | return (0); | |
588 | } | 589 | } | |
589 | if (errno != EINTR) | 590 | if (errno != EINTR) | |
590 | io->err = errno; | 591 | io->err = errno; | |
591 | return (-1); | 592 | return (-1); | |
592 | } | 593 | } | |
593 | 594 | |||
594 | static ssize_t | 595 | static ssize_t | |
595 | ftp_writefn(void *v, const void *buf, size_t len) | 596 | ftp_writefn(void *v, const void *buf, size_t len) | |
596 | { | 597 | { | |
597 | struct ftpio *io; | 598 | struct ftpio *io; | |
598 | ssize_t w; | 599 | ssize_t w; | |
599 | 600 | |||
600 | io = (struct ftpio *)v; | 601 | io = (struct ftpio *)v; | |
601 | if (io == NULL) { | 602 | if (io == NULL) { | |
602 | errno = EBADF; | 603 | errno = EBADF; | |
603 | return (-1); | 604 | return (-1); | |
604 | } | 605 | } | |
605 | if (io->cconn == NULL || io->dconn == NULL || io->dir == O_RDONLY) { | 606 | if (io->cconn == NULL || io->dconn == NULL || io->dir == O_RDONLY) { | |
606 | errno = EBADF; | 607 | errno = EBADF; | |
607 | return (-1); | 608 | return (-1); | |
608 | } | 609 | } | |
609 | if (io->err) { | 610 | if (io->err) { | |
610 | errno = io->err; | 611 | errno = io->err; | |
611 | return (-1); | 612 | return (-1); | |
612 | } | 613 | } | |
613 | w = fetch_write(io->dconn, buf, len); | 614 | w = fetch_write(io->dconn, buf, len); | |
614 | if (w >= 0) | 615 | if (w >= 0) | |
615 | return (w); | 616 | return (w); | |
616 | if (errno != EINTR) | 617 | if (errno != EINTR) | |
617 | io->err = errno; | 618 | io->err = errno; | |
618 | return (-1); | 619 | return (-1); | |
619 | } | 620 | } | |
620 | 621 | |||
621 | static int | 622 | static int | |
622 | ftp_disconnect(conn_t *conn) | 623 | ftp_disconnect(conn_t *conn) | |
623 | { | 624 | { | |
624 | ftp_cmd(conn, "QUIT\r\n"); | 625 | ftp_cmd(conn, "QUIT\r\n"); | |
625 | return fetch_close(conn); | 626 | return fetch_close(conn); | |
626 | } | 627 | } | |
627 | 628 | |||
628 | static void | 629 | static void | |
629 | ftp_closefn(void *v) | 630 | ftp_closefn(void *v) | |
630 | { | 631 | { | |
631 | struct ftpio *io; | 632 | struct ftpio *io; | |
632 | 633 | |||
633 | io = (struct ftpio *)v; | 634 | io = (struct ftpio *)v; | |
634 | if (io == NULL) { | 635 | if (io == NULL) { | |
635 | errno = EBADF; | 636 | errno = EBADF; | |
636 | return; | 637 | return; | |
637 | } | 638 | } | |
638 | if (io->dir == -1) | 639 | if (io->dir == -1) | |
639 | return; | 640 | return; | |
640 | if (io->cconn == NULL || io->dconn == NULL) { | 641 | if (io->cconn == NULL || io->dconn == NULL) { | |
641 | errno = EBADF; | 642 | errno = EBADF; | |
642 | return; | 643 | return; | |
643 | } | 644 | } | |
644 | fetch_close(io->dconn); | 645 | fetch_close(io->dconn); | |
645 | io->dconn = NULL; | 646 | io->dconn = NULL; | |
646 | io->dir = -1; | 647 | io->dir = -1; | |
647 | (void)ftp_chkerr(io->cconn); | 648 | (void)ftp_chkerr(io->cconn); | |
648 | fetch_cache_put(io->cconn, ftp_disconnect); | 649 | fetch_cache_put(io->cconn, ftp_disconnect); | |
649 | free(io); | 650 | free(io); | |
650 | return; | 651 | return; | |
651 | } | 652 | } | |
652 | 653 | |||
653 | static fetchIO * | 654 | static fetchIO * | |
654 | ftp_setup(conn_t *cconn, conn_t *dconn, int mode) | 655 | ftp_setup(conn_t *cconn, conn_t *dconn, int mode) | |
655 | { | 656 | { | |
656 | struct ftpio *io; | 657 | struct ftpio *io; | |
657 | fetchIO *f; | 658 | fetchIO *f; | |
658 | 659 | |||
659 | if (cconn == NULL || dconn == NULL) | 660 | if (cconn == NULL || dconn == NULL) | |
660 | return (NULL); | 661 | return (NULL); | |
661 | if ((io = malloc(sizeof(*io))) == NULL) | 662 | if ((io = malloc(sizeof(*io))) == NULL) | |
662 | return (NULL); | 663 | return (NULL); | |
663 | io->cconn = cconn; | 664 | io->cconn = cconn; | |
664 | io->dconn = dconn; | 665 | io->dconn = dconn; | |
665 | io->dir = mode; | 666 | io->dir = mode; | |
666 | io->eof = io->err = 0; | 667 | io->eof = io->err = 0; | |
667 | f = fetchIO_unopen(io, ftp_readfn, ftp_writefn, ftp_closefn); | 668 | f = fetchIO_unopen(io, ftp_readfn, ftp_writefn, ftp_closefn); | |
668 | if (f == NULL) | 669 | if (f == NULL) | |
669 | free(io); | 670 | free(io); | |
670 | return (f); | 671 | return (f); | |
671 | } | 672 | } | |
672 | 673 | |||
673 | /* | 674 | /* | |
674 | * Transfer file | 675 | * Transfer file | |
675 | */ | 676 | */ | |
676 | static fetchIO * | 677 | static fetchIO * | |
677 | ftp_transfer(conn_t *conn, const char *oper, const char *file, const char *op_arg, | 678 | ftp_transfer(conn_t *conn, const char *oper, const char *file, const char *op_arg, | |
678 | int mode, off_t offset, const char *flags) | 679 | int mode, off_t offset, const char *flags) | |
679 | { | 680 | { | |
680 | union anonymous { | 681 | union anonymous { | |
681 | struct sockaddr_storage ss; | 682 | struct sockaddr_storage ss; | |
682 | struct sockaddr sa; | 683 | struct sockaddr sa; | |
683 | struct sockaddr_in6 sin6; | 684 | struct sockaddr_in6 sin6; | |
684 | struct sockaddr_in sin4; | 685 | struct sockaddr_in sin4; | |
685 | } u; | 686 | } u; | |
686 | const char *bindaddr; | 687 | const char *bindaddr; | |
687 | const char *filename; | 688 | const char *filename; | |
688 | size_t filenamelen; | 689 | size_t filenamelen; | |
689 | int type; | 690 | int type; | |
690 | int low, pasv, verbose; | 691 | int low, pasv, verbose; | |
691 | int e, sd = -1; | 692 | int e, sd = -1; | |
692 | socklen_t l; | 693 | socklen_t l; | |
693 | char *s; | 694 | char *s; | |
694 | fetchIO *df; | 695 | fetchIO *df; | |
695 | 696 | |||
696 | /* check flags */ | 697 | /* check flags */ | |
697 | low = CHECK_FLAG('l'); | 698 | low = CHECK_FLAG('l'); | |
698 | pasv = !CHECK_FLAG('a'); | 699 | pasv = !CHECK_FLAG('a'); | |
699 | verbose = CHECK_FLAG('v'); | 700 | verbose = CHECK_FLAG('v'); | |
700 | 701 | |||
701 | /* passive mode */ | 702 | /* passive mode */ | |
702 | if (!pasv) | 703 | if (!pasv) | |
703 | pasv = ((s = getenv("FTP_PASSIVE_MODE")) != NULL && | 704 | pasv = ((s = getenv("FTP_PASSIVE_MODE")) != NULL && | |
704 | strncasecmp(s, "no", 2) != 0); | 705 | strncasecmp(s, "no", 2) != 0); | |
705 | 706 | |||
706 | /* isolate filename */ | 707 | /* isolate filename */ | |
707 | filename = ftp_filename(file, &filenamelen, &type, op_arg != NULL); | 708 | filename = ftp_filename(file, &filenamelen, &type, op_arg != NULL); | |
708 | 709 | |||
709 | /* set transfer mode and data type */ | 710 | /* set transfer mode and data type */ | |
710 | if ((e = ftp_mode_type(conn, 0, type)) != FTP_OK) | 711 | if ((e = ftp_mode_type(conn, 0, type)) != FTP_OK) | |
711 | goto ouch; | 712 | goto ouch; | |
712 | 713 | |||
713 | /* find our own address, bind, and listen */ | 714 | /* find our own address, bind, and listen */ | |
714 | l = sizeof(u.ss); | 715 | l = sizeof(u.ss); | |
715 | if (getsockname(conn->sd, &u.sa, &l) == -1) | 716 | if (getsockname(conn->sd, &u.sa, &l) == -1) | |
716 | goto sysouch; | 717 | goto sysouch; | |
717 | if (u.ss.ss_family == AF_INET6) | 718 | if (u.ss.ss_family == AF_INET6) | |
718 | unmappedaddr(&u.sin6, &l); | 719 | unmappedaddr(&u.sin6, &l); | |
719 | 720 | |||
720 | retry_mode: | 721 | retry_mode: | |
721 | 722 | |||
722 | /* open data socket */ | 723 | /* open data socket */ | |
723 | if ((sd = socket(u.ss.ss_family, SOCK_STREAM, IPPROTO_TCP)) == -1) { | 724 | if ((sd = socket(u.ss.ss_family, SOCK_STREAM, IPPROTO_TCP)) == -1) { | |
724 | fetch_syserr(); | 725 | fetch_syserr(); | |
725 | return (NULL); | 726 | return (NULL); | |
726 | } | 727 | } | |
727 | 728 | |||
728 | if (pasv) { | 729 | if (pasv) { | |
729 | unsigned char addr[64]; | 730 | unsigned char addr[64]; | |
730 | char *ln, *p; | 731 | char *ln, *p; | |
731 | unsigned int i; | 732 | unsigned int i; | |
732 | int port; | 733 | int port; | |
733 | 734 | |||
734 | /* send PASV command */ | 735 | /* send PASV command */ | |
735 | if (verbose) | 736 | if (verbose) | |
736 | fetch_info("setting passive mode"); | 737 | fetch_info("setting passive mode"); | |
737 | switch (u.ss.ss_family) { | 738 | switch (u.ss.ss_family) { | |
738 | case AF_INET: | 739 | case AF_INET: | |
739 | if ((e = ftp_cmd(conn, "PASV\r\n")) != FTP_PASSIVE_MODE) | 740 | if ((e = ftp_cmd(conn, "PASV\r\n")) != FTP_PASSIVE_MODE) | |
740 | goto ouch; | 741 | goto ouch; | |
741 | break; | 742 | break; | |
742 | case AF_INET6: | 743 | case AF_INET6: | |
743 | if ((e = ftp_cmd(conn, "EPSV\r\n")) != FTP_EPASSIVE_MODE) { | 744 | if ((e = ftp_cmd(conn, "EPSV\r\n")) != FTP_EPASSIVE_MODE) { | |
744 | if (e == -1) | 745 | if (e == -1) | |
745 | goto ouch; | 746 | goto ouch; | |
746 | if ((e = ftp_cmd(conn, "LPSV\r\n")) != | 747 | if ((e = ftp_cmd(conn, "LPSV\r\n")) != | |
747 | FTP_LPASSIVE_MODE) | 748 | FTP_LPASSIVE_MODE) | |
748 | goto ouch; | 749 | goto ouch; | |
749 | } | 750 | } | |
750 | break; | 751 | break; | |
751 | default: | 752 | default: | |
752 | e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */ | 753 | e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */ | |
753 | goto ouch; | 754 | goto ouch; | |
754 | } | 755 | } | |
755 | 756 | |||
756 | /* | 757 | /* | |
757 | * Find address and port number. The reply to the PASV command | 758 | * Find address and port number. The reply to the PASV command | |
758 | * is IMHO the one and only weak point in the FTP protocol. | 759 | * is IMHO the one and only weak point in the FTP protocol. | |
759 | */ | 760 | */ | |
760 | ln = conn->buf; | 761 | ln = conn->buf; | |
761 | switch (e) { | 762 | switch (e) { | |
762 | case FTP_PASSIVE_MODE: | 763 | case FTP_PASSIVE_MODE: | |
763 | case FTP_LPASSIVE_MODE: | 764 | case FTP_LPASSIVE_MODE: | |
764 | for (p = ln + 3; *p && !isdigit((unsigned char)*p); p++) | 765 | for (p = ln + 3; *p && !isdigit((unsigned char)*p); p++) | |
765 | /* nothing */ ; | 766 | /* nothing */ ; | |
766 | if (!*p) { | 767 | if (!*p) { | |
767 | e = FTP_PROTOCOL_ERROR; | 768 | e = FTP_PROTOCOL_ERROR; | |
768 | goto ouch; | 769 | goto ouch; | |
769 | } | 770 | } | |
770 | l = (e == FTP_PASSIVE_MODE ? 6 : 21); | 771 | l = (e == FTP_PASSIVE_MODE ? 6 : 21); | |
771 | for (i = 0; *p && i < l; i++, p++) | 772 | for (i = 0; *p && i < l; i++, p++) | |
772 | addr[i] = (unsigned char)strtol(p, &p, 10); | 773 | addr[i] = (unsigned char)strtol(p, &p, 10); | |
773 | if (i < l) { | 774 | if (i < l) { | |
774 | e = FTP_PROTOCOL_ERROR; | 775 | e = FTP_PROTOCOL_ERROR; | |
775 | goto ouch; | 776 | goto ouch; | |
776 | } | 777 | } | |
777 | break; | 778 | break; | |
778 | case FTP_EPASSIVE_MODE: | 779 | case FTP_EPASSIVE_MODE: | |
779 | for (p = ln + 3; *p && *p != '('; p++) | 780 | for (p = ln + 3; *p && *p != '('; p++) | |
780 | /* nothing */ ; | 781 | /* nothing */ ; | |
781 | if (!*p) { | 782 | if (!*p) { | |
782 | e = FTP_PROTOCOL_ERROR; | 783 | e = FTP_PROTOCOL_ERROR; | |
783 | goto ouch; | 784 | goto ouch; | |
784 | } | 785 | } | |
785 | ++p; | 786 | ++p; | |
786 | if (sscanf(p, "%c%c%c%d%c", &addr[0], &addr[1], &addr[2], | 787 | if (sscanf(p, "%c%c%c%d%c", &addr[0], &addr[1], &addr[2], | |
787 | &port, &addr[3]) != 5 || | 788 | &port, &addr[3]) != 5 || | |
788 | addr[0] != addr[1] || | 789 | addr[0] != addr[1] || | |
789 | addr[0] != addr[2] || addr[0] != addr[3]) { | 790 | addr[0] != addr[2] || addr[0] != addr[3]) { | |
790 | e = FTP_PROTOCOL_ERROR; | 791 | e = FTP_PROTOCOL_ERROR; | |
791 | goto ouch; | 792 | goto ouch; | |
792 | } | 793 | } | |
793 | break; | 794 | break; | |
794 | case FTP_SYNTAX_ERROR: | 795 | case FTP_SYNTAX_ERROR: | |
795 | if (verbose) | 796 | if (verbose) | |
796 | fetch_info("passive mode failed"); | 797 | fetch_info("passive mode failed"); | |
797 | /* Close socket and retry with passive mode. */ | 798 | /* Close socket and retry with passive mode. */ | |
798 | pasv = 0; | 799 | pasv = 0; | |
799 | close(sd); | 800 | close(sd); | |
800 | sd = -1; | 801 | sd = -1; | |
801 | goto retry_mode; | 802 | goto retry_mode; | |
802 | } | 803 | } | |
803 | 804 | |||
804 | /* seek to required offset */ | 805 | /* seek to required offset */ | |
805 | if (offset) | 806 | if (offset) | |
806 | if (ftp_cmd(conn, "REST %lu\r\n", (unsigned long)offset) != FTP_FILE_OK) | 807 | if (ftp_cmd(conn, "REST %lu\r\n", (unsigned long)offset) != FTP_FILE_OK) | |
807 | goto sysouch; | 808 | goto sysouch; | |
808 | 809 | |||
809 | /* construct sockaddr for data socket */ | 810 | /* construct sockaddr for data socket */ | |
810 | l = sizeof(u.ss); | 811 | l = sizeof(u.ss); | |
811 | if (getpeername(conn->sd, &u.sa, &l) == -1) | 812 | if (getpeername(conn->sd, &u.sa, &l) == -1) | |
812 | goto sysouch; | 813 | goto sysouch; | |
813 | if (u.ss.ss_family == AF_INET6) | 814 | if (u.ss.ss_family == AF_INET6) | |
814 | unmappedaddr(&u.sin6, &l); | 815 | unmappedaddr(&u.sin6, &l); | |
815 | switch (u.ss.ss_family) { | 816 | switch (u.ss.ss_family) { | |
816 | case AF_INET6: | 817 | case AF_INET6: | |
817 | if (e == FTP_EPASSIVE_MODE) | 818 | if (e == FTP_EPASSIVE_MODE) | |
818 | u.sin6.sin6_port = htons(port); | 819 | u.sin6.sin6_port = htons(port); | |
819 | else { | 820 | else { | |
820 | memcpy(&u.sin6.sin6_addr, addr + 2, 16); | 821 | memcpy(&u.sin6.sin6_addr, addr + 2, 16); | |
821 | memcpy(&u.sin6.sin6_port, addr + 19, 2); | 822 | memcpy(&u.sin6.sin6_port, addr + 19, 2); | |
822 | } | 823 | } | |
823 | break; | 824 | break; | |
824 | case AF_INET: | 825 | case AF_INET: | |
825 | if (e == FTP_EPASSIVE_MODE) | 826 | if (e == FTP_EPASSIVE_MODE) | |
826 | u.sin4.sin_port = htons(port); | 827 | u.sin4.sin_port = htons(port); | |
827 | else { | 828 | else { | |
828 | memcpy(&u.sin4.sin_addr, addr, 4); | 829 | memcpy(&u.sin4.sin_addr, addr, 4); | |
829 | memcpy(&u.sin4.sin_port, addr + 4, 2); | 830 | memcpy(&u.sin4.sin_port, addr + 4, 2); | |
830 | } | 831 | } | |
831 | break; | 832 | break; | |
832 | default: | 833 | default: | |
833 | e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */ | 834 | e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */ | |
834 | break; | 835 | break; | |
835 | } | 836 | } | |
836 | 837 | |||
837 | /* connect to data port */ | 838 | /* connect to data port */ | |
838 | if (verbose) | 839 | if (verbose) | |
839 | fetch_info("opening data connection"); | 840 | fetch_info("opening data connection"); | |
840 | bindaddr = getenv("FETCH_BIND_ADDRESS"); | 841 | bindaddr = getenv("FETCH_BIND_ADDRESS"); | |
841 | if (bindaddr != NULL && *bindaddr != '\0' && | 842 | if (bindaddr != NULL && *bindaddr != '\0' && | |
842 | fetch_bind(sd, u.ss.ss_family, bindaddr) != 0) | 843 | fetch_bind(sd, u.ss.ss_family, bindaddr) != 0) | |
843 | goto sysouch; | 844 | goto sysouch; | |
844 | if (connect(sd, &u.sa, l) == -1) | 845 | if (connect(sd, &u.sa, l) == -1) | |
845 | goto sysouch; | 846 | goto sysouch; | |
846 | 847 | |||
847 | /* make the server initiate the transfer */ | 848 | /* make the server initiate the transfer */ | |
848 | if (verbose) | 849 | if (verbose) | |
849 | fetch_info("initiating transfer"); | 850 | fetch_info("initiating transfer"); | |
850 | if (op_arg) | 851 | if (op_arg) | |
851 | e = ftp_cmd(conn, "%s%s%s\r\n", oper, *op_arg ? " " : "", op_arg); | 852 | e = ftp_cmd(conn, "%s%s%s\r\n", oper, *op_arg ? " " : "", op_arg); | |
852 | else | 853 | else | |
853 | e = ftp_cmd(conn, "%s %.*s\r\n", oper, | 854 | e = ftp_cmd(conn, "%s %.*s\r\n", oper, | |
854 | filenamelen, filename); | 855 | (int)filenamelen, filename); | |
855 | if (e != FTP_CONNECTION_ALREADY_OPEN && e != FTP_OPEN_DATA_CONNECTION) | 856 | if (e != FTP_CONNECTION_ALREADY_OPEN && e != FTP_OPEN_DATA_CONNECTION) | |
856 | goto ouch; | 857 | goto ouch; | |
857 | 858 | |||
858 | } else { | 859 | } else { | |
859 | uint32_t a; | 860 | uint32_t a; | |
860 | uint16_t p; | 861 | uint16_t p; | |
861 | #if defined(IPV6_PORTRANGE) || defined(IP_PORTRANGE) | 862 | #if defined(IPV6_PORTRANGE) || defined(IP_PORTRANGE) | |
862 | int arg; | 863 | int arg; | |
863 | #endif | 864 | #endif | |
864 | int d; | 865 | int d; | |
865 | char hname[INET6_ADDRSTRLEN]; | 866 | char hname[INET6_ADDRSTRLEN]; | |
866 | 867 | |||
867 | switch (u.ss.ss_family) { | 868 | switch (u.ss.ss_family) { | |
868 | case AF_INET6: | 869 | case AF_INET6: | |
869 | u.sin6.sin6_port = 0; | 870 | u.sin6.sin6_port = 0; | |
870 | #ifdef IPV6_PORTRANGE | 871 | #ifdef IPV6_PORTRANGE | |
871 | arg = low ? IPV6_PORTRANGE_DEFAULT : IPV6_PORTRANGE_HIGH; | 872 | arg = low ? IPV6_PORTRANGE_DEFAULT : IPV6_PORTRANGE_HIGH; | |
872 | if (setsockopt(sd, IPPROTO_IPV6, IPV6_PORTRANGE, | 873 | if (setsockopt(sd, IPPROTO_IPV6, IPV6_PORTRANGE, | |
873 | &arg, (socklen_t)sizeof(arg)) == -1) | 874 | &arg, (socklen_t)sizeof(arg)) == -1) | |
874 | goto sysouch; | 875 | goto sysouch; | |
875 | #endif | 876 | #endif | |
876 | break; | 877 | break; | |
877 | case AF_INET: | 878 | case AF_INET: | |
878 | u.sin4.sin_port = 0; | 879 | u.sin4.sin_port = 0; | |
879 | #ifdef IP_PORTRANGE | 880 | #ifdef IP_PORTRANGE | |
880 | arg = low ? IP_PORTRANGE_DEFAULT : IP_PORTRANGE_HIGH; | 881 | arg = low ? IP_PORTRANGE_DEFAULT : IP_PORTRANGE_HIGH; | |
881 | if (setsockopt(sd, IPPROTO_IP, IP_PORTRANGE, | 882 | if (setsockopt(sd, IPPROTO_IP, IP_PORTRANGE, | |
882 | &arg, (socklen_t)sizeof(arg)) == -1) | 883 | &arg, (socklen_t)sizeof(arg)) == -1) | |
883 | goto sysouch; | 884 | goto sysouch; | |
884 | #endif | 885 | #endif | |
885 | break; | 886 | break; | |
886 | } | 887 | } | |
887 | if (verbose) | 888 | if (verbose) | |
888 | fetch_info("binding data socket"); | 889 | fetch_info("binding data socket"); | |
889 | if (bind(sd, &u.sa, l) == -1) | 890 | if (bind(sd, &u.sa, l) == -1) | |
890 | goto sysouch; | 891 | goto sysouch; | |
891 | if (listen(sd, 1) == -1) | 892 | if (listen(sd, 1) == -1) | |
892 | goto sysouch; | 893 | goto sysouch; | |
893 | 894 | |||
894 | /* find what port we're on and tell the server */ | 895 | /* find what port we're on and tell the server */ | |
895 | if (getsockname(sd, &u.sa, &l) == -1) | 896 | if (getsockname(sd, &u.sa, &l) == -1) | |
896 | goto sysouch; | 897 | goto sysouch; | |
897 | switch (u.ss.ss_family) { | 898 | switch (u.ss.ss_family) { | |
898 | case AF_INET: | 899 | case AF_INET: | |
899 | a = ntohl(u.sin4.sin_addr.s_addr); | 900 | a = ntohl(u.sin4.sin_addr.s_addr); | |
900 | p = ntohs(u.sin4.sin_port); | 901 | p = ntohs(u.sin4.sin_port); | |
901 | e = ftp_cmd(conn, "PORT %d,%d,%d,%d,%d,%d\r\n", | 902 | e = ftp_cmd(conn, "PORT %d,%d,%d,%d,%d,%d\r\n", | |
902 | (a >> 24) & 0xff, (a >> 16) & 0xff, | 903 | (a >> 24) & 0xff, (a >> 16) & 0xff, | |
903 | (a >> 8) & 0xff, a & 0xff, | 904 | (a >> 8) & 0xff, a & 0xff, | |
904 | ((unsigned int)p >> 8) & 0xff, p & 0xff); | 905 | ((unsigned int)p >> 8) & 0xff, p & 0xff); | |
905 | break; | 906 | break; | |
906 | case AF_INET6: | 907 | case AF_INET6: | |
907 | e = -1; | 908 | e = -1; | |
908 | u.sin6.sin6_scope_id = 0; | 909 | u.sin6.sin6_scope_id = 0; | |
909 | if (getnameinfo(&u.sa, l, | 910 | if (getnameinfo(&u.sa, l, | |
910 | hname, (socklen_t)sizeof(hname), | 911 | hname, (socklen_t)sizeof(hname), | |
911 | NULL, 0, NI_NUMERICHOST) == 0) { | 912 | NULL, 0, NI_NUMERICHOST) == 0) { | |
912 | e = ftp_cmd(conn, "EPRT |%d|%s|%d|\r\n", 2, hname, | 913 | e = ftp_cmd(conn, "EPRT |%d|%s|%d|\r\n", 2, hname, | |
913 | htons(u.sin6.sin6_port)); | 914 | htons(u.sin6.sin6_port)); | |
914 | if (e == -1) | 915 | if (e == -1) | |
915 | goto ouch; | 916 | goto ouch; | |
916 | } | 917 | } | |
917 | if (e != FTP_OK) { | 918 | if (e != FTP_OK) { | |
918 | uint8_t aa[sizeof(u.sin6.sin6_addr)]; | 919 | uint8_t aa[sizeof(u.sin6.sin6_addr)]; | |
919 | memcpy(aa, &u.sin6.sin6_addr, sizeof(aa)); | 920 | memcpy(aa, &u.sin6.sin6_addr, sizeof(aa)); | |
920 | p = ntohs(u.sin6.sin6_port); | 921 | p = ntohs(u.sin6.sin6_port); | |
921 | e = ftp_cmd(conn, | 922 | e = ftp_cmd(conn, | |
922 | "LPRT %d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\r\n", | 923 | "LPRT %d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\r\n", | |
923 | 6, 16, | 924 | 6, 16, | |
924 | aa[ 0], aa[ 1], aa[ 2], aa[ 3], | 925 | aa[ 0], aa[ 1], aa[ 2], aa[ 3], | |
925 | aa[ 4], aa[ 5], aa[ 6], aa[ 7], | 926 | aa[ 4], aa[ 5], aa[ 6], aa[ 7], | |
926 | aa[ 8], aa[ 9], aa[10], aa[11], | 927 | aa[ 8], aa[ 9], aa[10], aa[11], | |
927 | aa[12], aa[13], aa[14], aa[15], | 928 | aa[12], aa[13], aa[14], aa[15], | |
928 | 2, | 929 | 2, | |
929 | ((unsigned int)p >> 8) & 0xff, p & 0xff); | 930 | ((unsigned int)p >> 8) & 0xff, p & 0xff); | |
930 | } | 931 | } | |
931 | break; | 932 | break; | |
932 | default: | 933 | default: | |
933 | e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */ | 934 | e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */ | |
934 | goto ouch; | 935 | goto ouch; | |
935 | } | 936 | } | |
936 | if (e != FTP_OK) | 937 | if (e != FTP_OK) | |
937 | goto ouch; | 938 | goto ouch; | |
938 | 939 | |||
939 | /* seek to required offset */ | 940 | /* seek to required offset */ | |
940 | if (offset) | 941 | if (offset) | |
941 | if (ftp_cmd(conn, "REST %llu\r\n", (unsigned long long)offset) != FTP_FILE_OK) | 942 | if (ftp_cmd(conn, "REST %llu\r\n", (unsigned long long)offset) != FTP_FILE_OK) | |
942 | goto sysouch; | 943 | goto sysouch; | |
943 | 944 | |||
944 | /* make the server initiate the transfer */ | 945 | /* make the server initiate the transfer */ | |
945 | if (verbose) | 946 | if (verbose) | |
946 | fetch_info("initiating transfer"); | 947 | fetch_info("initiating transfer"); | |
947 | if (op_arg) | 948 | if (op_arg) | |
948 | e = ftp_cmd(conn, "%s%s%s\r\n", oper, *op_arg ? " " : "", op_arg); | 949 | e = ftp_cmd(conn, "%s%s%s\r\n", oper, *op_arg ? " " : "", op_arg); | |
949 | else | 950 | else | |
950 | e = ftp_cmd(conn, "%s %.*s\r\n", oper, | 951 | e = ftp_cmd(conn, "%s %.*s\r\n", oper, | |
951 | filenamelen, filename); | 952 | (int)filenamelen, filename); | |
952 | if (e != FTP_CONNECTION_ALREADY_OPEN && e != FTP_OPEN_DATA_CONNECTION) | 953 | if (e != FTP_CONNECTION_ALREADY_OPEN && e != FTP_OPEN_DATA_CONNECTION) | |
953 | goto ouch; | 954 | goto ouch; | |
954 | 955 | |||
955 | /* accept the incoming connection and go to town */ | 956 | /* accept the incoming connection and go to town */ | |
956 | if ((d = accept(sd, NULL, NULL)) == -1) | 957 | if ((d = accept(sd, NULL, NULL)) == -1) | |
957 | goto sysouch; | 958 | goto sysouch; | |
958 | close(sd); | 959 | close(sd); | |
959 | sd = d; | 960 | sd = d; | |
960 | } | 961 | } | |
961 | 962 | |||
962 | if ((df = ftp_setup(conn, fetch_reopen(sd), mode)) == NULL) | 963 | if ((df = ftp_setup(conn, fetch_reopen(sd), mode)) == NULL) | |
963 | goto sysouch; | 964 | goto sysouch; | |
964 | return (df); | 965 | return (df); | |
965 | 966 | |||
966 | sysouch: | 967 | sysouch: | |
967 | fetch_syserr(); | 968 | fetch_syserr(); | |
968 | if (sd >= 0) | 969 | if (sd >= 0) | |
969 | close(sd); | 970 | close(sd); | |
970 | return (NULL); | 971 | return (NULL); | |
971 | 972 | |||
972 | ouch: | 973 | ouch: | |
973 | if (e != -1) | 974 | if (e != -1) | |
974 | ftp_seterr(e); | 975 | ftp_seterr(e); | |
975 | if (sd >= 0) | 976 | if (sd >= 0) | |
976 | close(sd); | 977 | close(sd); | |
977 | return (NULL); | 978 | return (NULL); | |
978 | } | 979 | } | |
979 | 980 | |||
980 | /* | 981 | /* | |
981 | * Authenticate | 982 | * Authenticate | |
982 | */ | 983 | */ | |
983 | static int | 984 | static int | |
984 | ftp_authenticate(conn_t *conn, struct url *url, struct url *purl) | 985 | ftp_authenticate(conn_t *conn, struct url *url, struct url *purl) | |
985 | { | 986 | { | |
986 | const char *user, *pwd, *login_name; | 987 | const char *user, *pwd, *login_name; | |
987 | char pbuf[URL_USERLEN + 1 + URL_HOSTLEN + 1]; | 988 | char pbuf[URL_USERLEN + 1 + URL_HOSTLEN + 1]; | |
988 | int e, len; | 989 | int e, len; | |
989 | 990 | |||
990 | /* XXX FTP_AUTH, and maybe .netrc */ | 991 | /* XXX FTP_AUTH, and maybe .netrc */ | |
991 | 992 | |||
992 | /* send user name and password */ | 993 | /* send user name and password */ | |
993 | if (url->user[0] == '\0') | 994 | if (url->user[0] == '\0') | |
994 | fetch_netrc_auth(url); | 995 | fetch_netrc_auth(url); | |
995 | user = url->user; | 996 | user = url->user; | |
996 | if (*user == '\0') | 997 | if (*user == '\0') | |
997 | user = getenv("FTP_LOGIN"); | 998 | user = getenv("FTP_LOGIN"); | |
998 | if (user == NULL || *user == '\0') | 999 | if (user == NULL || *user == '\0') | |
999 | user = FTP_ANONYMOUS_USER; | 1000 | user = FTP_ANONYMOUS_USER; | |
1000 | if (purl && url->port == fetch_default_port(url->scheme)) | 1001 | if (purl && url->port == fetch_default_port(url->scheme)) | |
1001 | e = ftp_cmd(conn, "USER %s@%s\r\n", user, url->host); | 1002 | e = ftp_cmd(conn, "USER %s@%s\r\n", user, url->host); | |
1002 | else if (purl) | 1003 | else if (purl) | |
1003 | e = ftp_cmd(conn, "USER %s@%s@%d\r\n", user, url->host, url->port); | 1004 | e = ftp_cmd(conn, "USER %s@%s@%d\r\n", user, url->host, url->port); | |
1004 | else | 1005 | else | |
1005 | e = ftp_cmd(conn, "USER %s\r\n", user); | 1006 | e = ftp_cmd(conn, "USER %s\r\n", user); | |
1006 | 1007 | |||
1007 | /* did the server request a password? */ | 1008 | /* did the server request a password? */ | |
1008 | if (e == FTP_NEED_PASSWORD) { | 1009 | if (e == FTP_NEED_PASSWORD) { | |
1009 | pwd = url->pwd; | 1010 | pwd = url->pwd; | |
1010 | if (*pwd == '\0') | 1011 | if (*pwd == '\0') | |
1011 | pwd = getenv("FTP_PASSWORD"); | 1012 | pwd = getenv("FTP_PASSWORD"); | |
1012 | if (pwd == NULL || *pwd == '\0') { | 1013 | if (pwd == NULL || *pwd == '\0') { | |
1013 | if ((login_name = getlogin()) == 0) | 1014 | if ((login_name = getlogin()) == 0) | |
1014 | login_name = FTP_ANONYMOUS_USER; | 1015 | login_name = FTP_ANONYMOUS_USER; | |
1015 | if ((len = snprintf(pbuf, URL_USERLEN + 2, "%s@", login_name)) < 0) | 1016 | if ((len = snprintf(pbuf, URL_USERLEN + 2, "%s@", login_name)) < 0) | |
1016 | len = 0; | 1017 | len = 0; | |
1017 | else if (len > URL_USERLEN + 1) | 1018 | else if (len > URL_USERLEN + 1) | |
1018 | len = URL_USERLEN + 1; | 1019 | len = URL_USERLEN + 1; | |
1019 | gethostname(pbuf + len, sizeof(pbuf) - len); | 1020 | gethostname(pbuf + len, sizeof(pbuf) - len); | |
1020 | /* MAXHOSTNAMELEN can differ from URL_HOSTLEN + 1 */ | 1021 | /* MAXHOSTNAMELEN can differ from URL_HOSTLEN + 1 */ | |
1021 | pbuf[sizeof(pbuf) - 1] = '\0'; | 1022 | pbuf[sizeof(pbuf) - 1] = '\0'; | |
1022 | pwd = pbuf; | 1023 | pwd = pbuf; | |
1023 | } | 1024 | } | |
1024 | e = ftp_cmd(conn, "PASS %s\r\n", pwd); | 1025 | e = ftp_cmd(conn, "PASS %s\r\n", pwd); | |
1025 | } | 1026 | } | |
1026 | 1027 | |||
1027 | return (e); | 1028 | return (e); | |
1028 | } | 1029 | } | |
1029 | 1030 | |||
1030 | /* | 1031 | /* | |
1031 | * Log on to FTP server | 1032 | * Log on to FTP server | |
1032 | */ | 1033 | */ | |
1033 | static conn_t * | 1034 | static conn_t * | |
1034 | ftp_connect(struct url *url, struct url *purl, const char *flags) | 1035 | ftp_connect(struct url *url, struct url *purl, const char *flags) | |
1035 | { | 1036 | { | |
1036 | conn_t *conn; | 1037 | conn_t *conn; | |
1037 | int e, direct, verbose; | 1038 | int e, direct, verbose; | |
1038 | #ifdef INET6 | 1039 | #ifdef INET6 | |
1039 | int af = AF_UNSPEC; | 1040 | int af = AF_UNSPEC; | |
1040 | #else | 1041 | #else | |
1041 | int af = AF_INET; | 1042 | int af = AF_INET; | |
1042 | #endif | 1043 | #endif | |
1043 | 1044 | |||
1044 | direct = CHECK_FLAG('d'); | 1045 | direct = CHECK_FLAG('d'); | |
1045 | verbose = CHECK_FLAG('v'); | 1046 | verbose = CHECK_FLAG('v'); | |
1046 | if (CHECK_FLAG('4')) | 1047 | if (CHECK_FLAG('4')) | |
1047 | af = AF_INET; | 1048 | af = AF_INET; | |
1048 | else if (CHECK_FLAG('6')) | 1049 | else if (CHECK_FLAG('6')) | |
1049 | af = AF_INET6; | 1050 | af = AF_INET6; | |
1050 | 1051 | |||
1051 | if (direct) | 1052 | if (direct) | |
1052 | purl = NULL; | 1053 | purl = NULL; | |
1053 | 1054 | |||
1054 | /* check for proxy */ | 1055 | /* check for proxy */ | |
1055 | if (purl) { | 1056 | if (purl) { | |
1056 | /* XXX proxy authentication! */ | 1057 | /* XXX proxy authentication! */ | |
1057 | /* XXX connetion caching */ | 1058 | /* XXX connetion caching */ | |
1058 | if (!purl->port) | 1059 | if (!purl->port) | |
1059 | purl->port = fetch_default_port(purl->scheme); | 1060 | purl->port = fetch_default_port(purl->scheme); | |
1060 | 1061 | |||
1061 | conn = fetch_connect(purl, af, verbose); | 1062 | conn = fetch_connect(purl, af, verbose); | |
1062 | } else { | 1063 | } else { | |
1063 | /* no proxy, go straight to target */ | 1064 | /* no proxy, go straight to target */ | |
1064 | if (!url->port) | 1065 | if (!url->port) | |
1065 | url->port = fetch_default_port(url->scheme); | 1066 | url->port = fetch_default_port(url->scheme); | |
1066 | 1067 | |||
1067 | while ((conn = fetch_cache_get(url, af)) != NULL) { | 1068 | while ((conn = fetch_cache_get(url, af)) != NULL) { | |
1068 | e = ftp_cmd(conn, "NOOP\r\n"); | 1069 | e = ftp_cmd(conn, "NOOP\r\n"); | |
1069 | if (e == FTP_OK) | 1070 | if (e == FTP_OK) | |
1070 | return conn; | 1071 | return conn; | |
1071 | fetch_close(conn); | 1072 | fetch_close(conn); | |
1072 | } | 1073 | } | |
1073 | conn = fetch_connect(url, af, verbose); | 1074 | conn = fetch_connect(url, af, verbose); | |
1074 | purl = NULL; | 1075 | purl = NULL; | |
1075 | } | 1076 | } | |
1076 | 1077 | |||
1077 | /* check connection */ | 1078 | /* check connection */ | |
1078 | if (conn == NULL) | 1079 | if (conn == NULL) | |
1079 | /* fetch_connect() has already set an error code */ | 1080 | /* fetch_connect() has already set an error code */ | |
1080 | return (NULL); | 1081 | return (NULL); | |
1081 | 1082 | |||
1082 | /* expect welcome message */ | 1083 | /* expect welcome message */ | |
1083 | if ((e = ftp_chkerr(conn)) != FTP_SERVICE_READY) | 1084 | if ((e = ftp_chkerr(conn)) != FTP_SERVICE_READY) | |
1084 | goto fouch; | 1085 | goto fouch; | |
1085 | 1086 | |||
1086 | /* authenticate */ | 1087 | /* authenticate */ | |
1087 | if ((e = ftp_authenticate(conn, url, purl)) != FTP_LOGGED_IN) | 1088 | if ((e = ftp_authenticate(conn, url, purl)) != FTP_LOGGED_IN) | |
1088 | goto fouch; | 1089 | goto fouch; | |
1089 | 1090 | |||
1090 | /* TODO: Request extended features supported, if any (RFC 3659). */ | 1091 | /* TODO: Request extended features supported, if any (RFC 3659). */ | |
1091 | 1092 | |||
1092 | /* done */ | 1093 | /* done */ | |
1093 | return (conn); | 1094 | return (conn); | |
1094 | 1095 | |||
1095 | fouch: | 1096 | fouch: | |
1096 | if (e != -1) | 1097 | if (e != -1) | |
1097 | ftp_seterr(e); | 1098 | ftp_seterr(e); | |
1098 | fetch_close(conn); | 1099 | fetch_close(conn); | |
1099 | return (NULL); | 1100 | return (NULL); | |
1100 | } | 1101 | } | |
1101 | 1102 | |||
1102 | /* | 1103 | /* | |
1103 | * Check the proxy settings | 1104 | * Check the proxy settings | |
1104 | */ | 1105 | */ | |
1105 | static struct url * | 1106 | static struct url * | |
1106 | ftp_get_proxy(struct url * url, const char *flags) | 1107 | ftp_get_proxy(struct url * url, const char *flags) | |
1107 | { | 1108 | { | |
1108 | struct url *purl; | 1109 | struct url *purl; | |
1109 | char *p, *fp, *FP, *hp, *HP; | 1110 | char *p, *fp, *FP, *hp, *HP; | |
1110 | 1111 | |||
1111 | if (flags != NULL && strchr(flags, 'd') != NULL) | 1112 | if (flags != NULL && strchr(flags, 'd') != NULL) | |
1112 | return NULL; | 1113 | return NULL; | |
1113 | if (fetch_no_proxy_match(url->host)) | 1114 | if (fetch_no_proxy_match(url->host)) | |
1114 | return NULL; | 1115 | return NULL; | |
1115 | 1116 | |||
1116 | FP = getenv("FTP_PROXY"); | 1117 | FP = getenv("FTP_PROXY"); | |
1117 | fp = getenv("ftp_proxy"); | 1118 | fp = getenv("ftp_proxy"); | |
1118 | HP = getenv("HTTP_PROXY"); | 1119 | HP = getenv("HTTP_PROXY"); | |
1119 | hp = getenv("http_proxy"); | 1120 | hp = getenv("http_proxy"); | |
1120 | 1121 | |||
1121 | if ((((p = FP) || (p = fp) || (p = HP) || (p = hp))) && | 1122 | if ((((p = FP) || (p = fp) || (p = HP) || (p = hp))) && | |
1122 | *p && (purl = fetchParseURL(p)) != NULL) { | 1123 | *p && (purl = fetchParseURL(p)) != NULL) { | |
1123 | if (!*purl->scheme) { | 1124 | if (!*purl->scheme) { | |
1124 | if (fp || FP) | 1125 | if (fp || FP) | |
1125 | strcpy(purl->scheme, SCHEME_FTP); | 1126 | strcpy(purl->scheme, SCHEME_FTP); | |
1126 | else | 1127 | else | |
1127 | strcpy(purl->scheme, SCHEME_HTTP); | 1128 | strcpy(purl->scheme, SCHEME_HTTP); | |
1128 | } | 1129 | } | |
1129 | if (!purl->port) | 1130 | if (!purl->port) | |
1130 | purl->port = fetch_default_proxy_port(purl->scheme); | 1131 | purl->port = fetch_default_proxy_port(purl->scheme); | |
1131 | if (strcasecmp(purl->scheme, SCHEME_FTP) == 0 || | 1132 | if (strcasecmp(purl->scheme, SCHEME_FTP) == 0 || | |
1132 | strcasecmp(purl->scheme, SCHEME_HTTP) == 0) | 1133 | strcasecmp(purl->scheme, SCHEME_HTTP) == 0) | |
1133 | return purl; | 1134 | return purl; | |
1134 | fetchFreeURL(purl); | 1135 | fetchFreeURL(purl); | |
1135 | } | 1136 | } | |
1136 | return NULL; | 1137 | return NULL; | |
1137 | } | 1138 | } | |
1138 | 1139 | |||
1139 | /* | 1140 | /* | |
1140 | * Process an FTP request | 1141 | * Process an FTP request | |
1141 | */ | 1142 | */ | |
1142 | fetchIO * | 1143 | fetchIO * | |
1143 | ftp_request(struct url *url, const char *op, const char *op_arg, | 1144 | ftp_request(struct url *url, const char *op, const char *op_arg, | |
1144 | struct url_stat *us, struct url *purl, const char *flags) | 1145 | struct url_stat *us, struct url *purl, const char *flags) | |
1145 | { | 1146 | { | |
1146 | fetchIO *f; | 1147 | fetchIO *f; | |
1147 | char *path; | 1148 | char *path; | |
1148 | conn_t *conn; | 1149 | conn_t *conn; | |
1149 | int if_modified_since, oflag; | 1150 | int if_modified_since, oflag; | |
1150 | struct url_stat local_us; | 1151 | struct url_stat local_us; | |
1151 | 1152 | |||
1152 | /* check if we should use HTTP instead */ | 1153 | /* check if we should use HTTP instead */ | |
1153 | if (purl && strcasecmp(purl->scheme, SCHEME_HTTP) == 0) { | 1154 | if (purl && strcasecmp(purl->scheme, SCHEME_HTTP) == 0) { | |
1154 | if (strcmp(op, "STAT") == 0) | 1155 | if (strcmp(op, "STAT") == 0) | |
1155 | return (http_request(url, "HEAD", us, purl, flags)); | 1156 | return (http_request(url, "HEAD", us, purl, flags)); | |
1156 | else if (strcmp(op, "RETR") == 0) | 1157 | else if (strcmp(op, "RETR") == 0) | |
1157 | return (http_request(url, "GET", us, purl, flags)); | 1158 | return (http_request(url, "GET", us, purl, flags)); | |
1158 | /* | 1159 | /* | |
1159 | * Our HTTP code doesn't support PUT requests yet, so try | 1160 | * Our HTTP code doesn't support PUT requests yet, so try | |
1160 | * a direct connection. | 1161 | * a direct connection. | |
1161 | */ | 1162 | */ | |
1162 | } | 1163 | } | |
1163 | 1164 | |||
1164 | /* connect to server */ | 1165 | /* connect to server */ | |
1165 | conn = ftp_connect(url, purl, flags); | 1166 | conn = ftp_connect(url, purl, flags); | |
1166 | if (purl) | 1167 | if (purl) | |
1167 | fetchFreeURL(purl); | 1168 | fetchFreeURL(purl); | |
1168 | if (conn == NULL) | 1169 | if (conn == NULL) | |
1169 | return (NULL); | 1170 | return (NULL); | |
1170 | 1171 | |||
1171 | if ((path = fetchUnquotePath(url)) == NULL) { | 1172 | if ((path = fetchUnquotePath(url)) == NULL) { | |
1172 | fetch_syserr(); | 1173 | fetch_syserr(); | |
1173 | return NULL; | 1174 | return NULL; | |
1174 | } | 1175 | } | |
1175 | 1176 | |||
1176 | /* change directory */ | 1177 | /* change directory */ | |
1177 | if (ftp_cwd(conn, path, op_arg != NULL) == -1) { | 1178 | if (ftp_cwd(conn, path, op_arg != NULL) == -1) { | |
1178 | free(path); | 1179 | free(path); | |
1179 | return (NULL); | 1180 | return (NULL); | |
1180 | } | 1181 | } | |
1181 | 1182 | |||
1182 | if_modified_since = CHECK_FLAG('i'); | 1183 | if_modified_since = CHECK_FLAG('i'); | |
1183 | if (if_modified_since && us == NULL) | 1184 | if (if_modified_since && us == NULL) | |
1184 | us = &local_us; | 1185 | us = &local_us; | |
1185 | 1186 | |||
1186 | /* stat file */ | 1187 | /* stat file */ | |
1187 | if (us && ftp_stat(conn, path, us) == -1 | 1188 | if (us && ftp_stat(conn, path, us) == -1 | |
1188 | && fetchLastErrCode != FETCH_PROTO | 1189 | && fetchLastErrCode != FETCH_PROTO | |
1189 | && fetchLastErrCode != FETCH_UNAVAIL) { | 1190 | && fetchLastErrCode != FETCH_UNAVAIL) { | |
1190 | free(path); | 1191 | free(path); | |
1191 | return (NULL); | 1192 | return (NULL); | |
1192 | } | 1193 | } | |
1193 | 1194 | |||
1194 | if (if_modified_since && url->last_modified > 0 && | 1195 | if (if_modified_since && url->last_modified > 0 && | |
1195 | url->last_modified >= us->mtime) { | 1196 | url->last_modified >= us->mtime) { | |
1196 | free(path); | 1197 | free(path); | |
1197 | fetchLastErrCode = FETCH_UNCHANGED; | 1198 | fetchLastErrCode = FETCH_UNCHANGED; | |
1198 | snprintf(fetchLastErrString, MAXERRSTRING, "Unchanged"); | 1199 | snprintf(fetchLastErrString, MAXERRSTRING, "Unchanged"); | |
1199 | return NULL; | 1200 | return NULL; | |
1200 | } | 1201 | } | |
1201 | 1202 | |||
1202 | /* just a stat */ | 1203 | /* just a stat */ | |
1203 | if (strcmp(op, "STAT") == 0) { | 1204 | if (strcmp(op, "STAT") == 0) { | |
1204 | free(path); | 1205 | free(path); | |
1205 | return fetchIO_unopen(NULL, NULL, NULL, NULL); | 1206 | return fetchIO_unopen(NULL, NULL, NULL, NULL); | |
1206 | } | 1207 | } | |
1207 | if (strcmp(op, "STOR") == 0 || strcmp(op, "APPE") == 0) | 1208 | if (strcmp(op, "STOR") == 0 || strcmp(op, "APPE") == 0) | |
1208 | oflag = O_WRONLY; | 1209 | oflag = O_WRONLY; | |
1209 | else | 1210 | else | |
1210 | oflag = O_RDONLY; | 1211 | oflag = O_RDONLY; | |
1211 | 1212 | |||
1212 | /* initiate the transfer */ | 1213 | /* initiate the transfer */ | |
1213 | f = (ftp_transfer(conn, op, path, op_arg, oflag, url->offset, flags)); | 1214 | f = (ftp_transfer(conn, op, path, op_arg, oflag, url->offset, flags)); | |
1214 | free(path); | 1215 | free(path); | |
1215 | return f; | 1216 | return f; | |
1216 | } | 1217 | } | |
1217 | 1218 | |||
1218 | /* | 1219 | /* | |
1219 | * Get and stat file | 1220 | * Get and stat file | |
1220 | */ | 1221 | */ | |
1221 | fetchIO * | 1222 | fetchIO * | |
1222 | fetchXGetFTP(struct url *url, struct url_stat *us, const char *flags) | 1223 | fetchXGetFTP(struct url *url, struct url_stat *us, const char *flags) | |
1223 | { | 1224 | { | |
1224 | return (ftp_request(url, "RETR", NULL, us, ftp_get_proxy(url, flags), flags)); | 1225 | return (ftp_request(url, "RETR", NULL, us, ftp_get_proxy(url, flags), flags)); | |
1225 | } | 1226 | } | |
1226 | 1227 | |||
1227 | /* | 1228 | /* | |
1228 | * Get file | 1229 | * Get file | |
1229 | */ | 1230 | */ | |
1230 | fetchIO * | 1231 | fetchIO * | |
1231 | fetchGetFTP(struct url *url, const char *flags) | 1232 | fetchGetFTP(struct url *url, const char *flags) | |
1232 | { | 1233 | { | |
1233 | return (fetchXGetFTP(url, NULL, flags)); | 1234 | return (fetchXGetFTP(url, NULL, flags)); | |
1234 | } | 1235 | } | |
1235 | 1236 | |||
1236 | /* | 1237 | /* | |
1237 | * Put file | 1238 | * Put file | |
1238 | */ | 1239 | */ | |
1239 | fetchIO * | 1240 | fetchIO * | |
1240 | fetchPutFTP(struct url *url, const char *flags) | 1241 | fetchPutFTP(struct url *url, const char *flags) | |
1241 | { | 1242 | { | |
1242 | return (ftp_request(url, CHECK_FLAG('a') ? "APPE" : "STOR", NULL, NULL, | 1243 | return (ftp_request(url, CHECK_FLAG('a') ? "APPE" : "STOR", NULL, NULL, | |
1243 | ftp_get_proxy(url, flags), flags)); | 1244 | ftp_get_proxy(url, flags), flags)); | |
1244 | } | 1245 | } | |
1245 | 1246 | |||
1246 | /* | 1247 | /* | |
1247 | * Get file stats | 1248 | * Get file stats | |
1248 | */ | 1249 | */ | |
1249 | int | 1250 | int | |
1250 | fetchStatFTP(struct url *url, struct url_stat *us, const char *flags) | 1251 | fetchStatFTP(struct url *url, struct url_stat *us, const char *flags) | |
1251 | { | 1252 | { | |
1252 | fetchIO *f; | 1253 | fetchIO *f; | |
1253 | 1254 | |||
1254 | f = ftp_request(url, "STAT", NULL, us, ftp_get_proxy(url, flags), flags); | 1255 | f = ftp_request(url, "STAT", NULL, us, ftp_get_proxy(url, flags), flags); | |
1255 | if (f == NULL) | 1256 | if (f == NULL) | |
1256 | return (-1); | 1257 | return (-1); | |
1257 | fetchIO_close(f); | 1258 | fetchIO_close(f); | |
1258 | return (0); | 1259 | return (0); | |
1259 | } | 1260 | } | |
1260 | 1261 | |||
1261 | /* | 1262 | /* | |
1262 | * List a directory | 1263 | * List a directory | |
1263 | */ | 1264 | */ | |
1264 | int | 1265 | int | |
1265 | fetchListFTP(struct url_list *ue, struct url *url, const char *pattern, const char *flags) | 1266 | fetchListFTP(struct url_list *ue, struct url *url, const char *pattern, const char *flags) | |
1266 | { | 1267 | { | |
1267 | fetchIO *f; | 1268 | fetchIO *f; | |
1268 | char buf[2 * PATH_MAX], *eol, *eos; | 1269 | char buf[2 * PATH_MAX], *eol, *eos; | |
1269 | ssize_t len; | 1270 | ssize_t len; | |
1270 | size_t cur_off; | 1271 | size_t cur_off; | |
1271 | int ret; | 1272 | int ret; | |
1272 | 1273 | |||
1273 | /* XXX What about proxies? */ | 1274 | /* XXX What about proxies? */ | |
1274 | if (pattern == NULL || strcmp(pattern, "*") == 0) | 1275 | if (pattern == NULL || strcmp(pattern, "*") == 0) | |
1275 | pattern = ""; | 1276 | pattern = ""; | |
1276 | f = ftp_request(url, "NLST", pattern, NULL, ftp_get_proxy(url, flags), flags); | 1277 | f = ftp_request(url, "NLST", pattern, NULL, ftp_get_proxy(url, flags), flags); | |
1277 | if (f == NULL) | 1278 | if (f == NULL) | |
1278 | return -1; | 1279 | return -1; | |
1279 | 1280 | |||
1280 | cur_off = 0; | 1281 | cur_off = 0; | |
1281 | ret = 0; | 1282 | ret = 0; | |
1282 | 1283 | |||
1283 | while ((len = fetchIO_read(f, buf + cur_off, sizeof(buf) - cur_off)) > 0) { | 1284 | while ((len = fetchIO_read(f, buf + cur_off, sizeof(buf) - cur_off)) > 0) { | |
1284 | cur_off += len; | 1285 | cur_off += len; | |
1285 | while ((eol = memchr(buf, '\n', cur_off)) != NULL) { | 1286 | while ((eol = memchr(buf, '\n', cur_off)) != NULL) { | |
1286 | if (len == eol - buf) | 1287 | if (len == eol - buf) | |
1287 | break; | 1288 | break; | |
1288 | if (eol != buf) { | 1289 | if (eol != buf) { | |
1289 | if (eol[-1] == '\r') | 1290 | if (eol[-1] == '\r') | |
1290 | eos = eol - 1; | 1291 | eos = eol - 1; | |
1291 | else | 1292 | else | |
1292 | eos = eol; | 1293 | eos = eol; | |
1293 | *eos = '\0'; | 1294 | *eos = '\0'; | |
1294 | ret = fetch_add_entry(ue, url, buf, 0); | 1295 | ret = fetch_add_entry(ue, url, buf, 0); | |
1295 | if (ret) | 1296 | if (ret) | |
1296 | break; | 1297 | break; | |
1297 | cur_off -= eol - buf + 1; | 1298 | cur_off -= eol - buf + 1; | |
1298 | memmove(buf, eol + 1, cur_off); | 1299 | memmove(buf, eol + 1, cur_off); | |
1299 | } | 1300 | } | |
1300 | } | 1301 | } | |
1301 | if (ret) | 1302 | if (ret) | |
1302 | break; | 1303 | break; | |
1303 | } | 1304 | } | |
1304 | if (cur_off != 0 || len < 0) { | 1305 | if (cur_off != 0 || len < 0) { | |
1305 | /* Not RFC conform, bail out. */ | 1306 | /* Not RFC conform, bail out. */ | |
1306 | fetchIO_close(f); | 1307 | fetchIO_close(f); | |
1307 | return -1; | 1308 | return -1; | |
1308 | } | 1309 | } | |
1309 | fetchIO_close(f); | 1310 | fetchIO_close(f); | |
1310 | return ret; | 1311 | return ret; | |
1311 | } | 1312 | } |
--- src/external/bsd/fetch/dist/libfetch/http.c 2011/06/25 20:27:01 1.2
+++ src/external/bsd/fetch/dist/libfetch/http.c 2014/01/07 02:13:00 1.3
@@ -1,1405 +1,1406 @@ | @@ -1,1405 +1,1406 @@ | |||
1 | /* $NetBSD: http.c,v 1.2 2011/06/25 20:27:01 christos Exp $ */ | 1 | /* $NetBSD: http.c,v 1.3 2014/01/07 02:13:00 joerg Exp $ */ | |
2 | /*- | 2 | /*- | |
3 | * Copyright (c) 2000-2004 Dag-Erling Coïdan Smørgrav | 3 | * Copyright (c) 2000-2004 Dag-Erling Coïdan Smørgrav | |
4 | * Copyright (c) 2003 Thomas Klausner <wiz@NetBSD.org> | 4 | * Copyright (c) 2003 Thomas Klausner <wiz@NetBSD.org> | |
5 | * Copyright (c) 2008, 2009 Joerg Sonnenberger <joerg@NetBSD.org> | 5 | * Copyright (c) 2008, 2009 Joerg Sonnenberger <joerg@NetBSD.org> | |
6 | * All rights reserved. | 6 | * All rights reserved. | |
7 | * | 7 | * | |
8 | * Redistribution and use in source and binary forms, with or without | 8 | * Redistribution and use in source and binary forms, with or without | |
9 | * modification, are permitted provided that the following conditions | 9 | * modification, are permitted provided that the following conditions | |
10 | * are met: | 10 | * are met: | |
11 | * 1. Redistributions of source code must retain the above copyright | 11 | * 1. Redistributions of source code must retain the above copyright | |
12 | * notice, this list of conditions and the following disclaimer | 12 | * notice, this list of conditions and the following disclaimer | |
13 | * in this position and unchanged. | 13 | * in this position and unchanged. | |
14 | * 2. Redistributions in binary form must reproduce the above copyright | 14 | * 2. Redistributions in binary form must reproduce the above copyright | |
15 | * notice, this list of conditions and the following disclaimer in the | 15 | * notice, this list of conditions and the following disclaimer in the | |
16 | * documentation and/or other materials provided with the distribution. | 16 | * documentation and/or other materials provided with the distribution. | |
17 | * 3. The name of the author may not be used to endorse or promote products | 17 | * 3. The name of the author may not be used to endorse or promote products | |
18 | * derived from this software without specific prior written permission. | 18 | * derived from this software without specific prior written permission. | |
19 | * | 19 | * | |
20 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | 20 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | |
21 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | 21 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |
22 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | 22 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | |
23 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | 23 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | |
24 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | 24 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |
25 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 25 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
29 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 29 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
30 | * | 30 | * | |
31 | * $FreeBSD: http.c,v 1.83 2008/02/06 11:39:55 des Exp $ | 31 | * $FreeBSD: http.c,v 1.83 2008/02/06 11:39:55 des Exp $ | |
32 | */ | 32 | */ | |
33 | 33 | |||
34 | /* | 34 | /* | |
35 | * The following copyright applies to the base64 code: | 35 | * The following copyright applies to the base64 code: | |
36 | * | 36 | * | |
37 | *- | 37 | *- | |
38 | * Copyright 1997 Massachusetts Institute of Technology | 38 | * Copyright 1997 Massachusetts Institute of Technology | |
39 | * | 39 | * | |
40 | * Permission to use, copy, modify, and distribute this software and | 40 | * Permission to use, copy, modify, and distribute this software and | |
41 | * its documentation for any purpose and without fee is hereby | 41 | * its documentation for any purpose and without fee is hereby | |
42 | * granted, provided that both the above copyright notice and this | 42 | * granted, provided that both the above copyright notice and this | |
43 | * permission notice appear in all copies, that both the above | 43 | * permission notice appear in all copies, that both the above | |
44 | * copyright notice and this permission notice appear in all | 44 | * copyright notice and this permission notice appear in all | |
45 | * supporting documentation, and that the name of M.I.T. not be used | 45 | * supporting documentation, and that the name of M.I.T. not be used | |
46 | * in advertising or publicity pertaining to distribution of the | 46 | * in advertising or publicity pertaining to distribution of the | |
47 | * software without specific, written prior permission. M.I.T. makes | 47 | * software without specific, written prior permission. M.I.T. makes | |
48 | * no representations about the suitability of this software for any | 48 | * no representations about the suitability of this software for any | |
49 | * purpose. It is provided "as is" without express or implied | 49 | * purpose. It is provided "as is" without express or implied | |
50 | * warranty. | 50 | * warranty. | |
51 | * | 51 | * | |
52 | * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS | 52 | * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS | |
53 | * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, | 53 | * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, | |
54 | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | 54 | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
55 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT | 55 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT | |
56 | * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 56 | * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
57 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 57 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
58 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | 58 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | |
59 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | 59 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
60 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | 60 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |
61 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | 61 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | |
62 | * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | 62 | * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
63 | * SUCH DAMAGE. | 63 | * SUCH DAMAGE. | |
64 | */ | 64 | */ | |
65 | 65 | |||
66 | #if defined(__linux__) || defined(__MINT__) | 66 | #if defined(__linux__) || defined(__MINT__) | |
67 | /* Keep this down to Linux or MiNT, it can create surprises elsewhere. */ | 67 | /* Keep this down to Linux or MiNT, it can create surprises elsewhere. */ | |
68 | #define _GNU_SOURCE | 68 | #define _GNU_SOURCE | |
69 | #endif | 69 | #endif | |
70 | 70 | |||
71 | /* Needed for gmtime_r on Interix */ | 71 | /* Needed for gmtime_r on Interix */ | |
72 | #define _REENTRANT | 72 | #define _REENTRANT | |
73 | 73 | |||
74 | #if HAVE_CONFIG_H | 74 | #if HAVE_CONFIG_H | |
75 | #include "config.h" | 75 | #include "config.h" | |
76 | #endif | 76 | #endif | |
77 | #ifndef NETBSD | 77 | #ifndef NETBSD | |
78 | #include <nbcompat.h> | 78 | #include <nbcompat.h> | |
79 | #endif | 79 | #endif | |
80 | 80 | |||
81 | #include <sys/types.h> | 81 | #include <sys/types.h> | |
82 | #include <sys/socket.h> | 82 | #include <sys/socket.h> | |
83 | 83 | |||
84 | #include <ctype.h> | 84 | #include <ctype.h> | |
85 | #include <errno.h> | 85 | #include <errno.h> | |
86 | #include <locale.h> | 86 | #include <locale.h> | |
87 | #include <stdarg.h> | 87 | #include <stdarg.h> | |
88 | #ifndef NETBSD | 88 | #ifndef NETBSD | |
89 | #include <nbcompat/stdio.h> | 89 | #include <nbcompat/stdio.h> | |
90 | #else | 90 | #else | |
91 | #include <stdio.h> | 91 | #include <stdio.h> | |
92 | #endif | 92 | #endif | |
93 | #include <stdlib.h> | 93 | #include <stdlib.h> | |
94 | #include <string.h> | 94 | #include <string.h> | |
95 | #include <time.h> | 95 | #include <time.h> | |
96 | #include <unistd.h> | 96 | #include <unistd.h> | |
97 | 97 | |||
98 | #include <netinet/in.h> | 98 | #include <netinet/in.h> | |
99 | #include <netinet/tcp.h> | 99 | #include <netinet/tcp.h> | |
100 | 100 | |||
101 | #ifndef NETBSD | 101 | #ifndef NETBSD | |
102 | #include <nbcompat/netdb.h> | 102 | #include <nbcompat/netdb.h> | |
103 | #else | 103 | #else | |
104 | #include <netdb.h> | 104 | #include <netdb.h> | |
105 | #endif | 105 | #endif | |
106 | 106 | |||
107 | #include <arpa/inet.h> | 107 | #include <arpa/inet.h> | |
108 | 108 | |||
109 | #include "fetch.h" | 109 | #include "fetch.h" | |
110 | #include "common.h" | 110 | #include "common.h" | |
111 | #include "httperr.h" | 111 | #include "httperr.h" | |
112 | 112 | |||
113 | /* Maximum number of redirects to follow */ | 113 | /* Maximum number of redirects to follow */ | |
114 | #define MAX_REDIRECT 5 | 114 | #define MAX_REDIRECT 5 | |
115 | 115 | |||
116 | /* Symbolic names for reply codes we care about */ | 116 | /* Symbolic names for reply codes we care about */ | |
117 | #define HTTP_OK 200 | 117 | #define HTTP_OK 200 | |
118 | #define HTTP_PARTIAL 206 | 118 | #define HTTP_PARTIAL 206 | |
119 | #define HTTP_MOVED_PERM 301 | 119 | #define HTTP_MOVED_PERM 301 | |
120 | #define HTTP_MOVED_TEMP 302 | 120 | #define HTTP_MOVED_TEMP 302 | |
121 | #define HTTP_SEE_OTHER 303 | 121 | #define HTTP_SEE_OTHER 303 | |
122 | #define HTTP_NOT_MODIFIED 304 | 122 | #define HTTP_NOT_MODIFIED 304 | |
123 | #define HTTP_TEMP_REDIRECT 307 | 123 | #define HTTP_TEMP_REDIRECT 307 | |
124 | #define HTTP_NEED_AUTH 401 | 124 | #define HTTP_NEED_AUTH 401 | |
125 | #define HTTP_NEED_PROXY_AUTH 407 | 125 | #define HTTP_NEED_PROXY_AUTH 407 | |
126 | #define HTTP_BAD_RANGE 416 | 126 | #define HTTP_BAD_RANGE 416 | |
127 | #define HTTP_PROTOCOL_ERROR 999 | 127 | #define HTTP_PROTOCOL_ERROR 999 | |
128 | 128 | |||
129 | #define HTTP_REDIRECT(xyz) ((xyz) == HTTP_MOVED_PERM \ | 129 | #define HTTP_REDIRECT(xyz) ((xyz) == HTTP_MOVED_PERM \ | |
130 | || (xyz) == HTTP_MOVED_TEMP \ | 130 | || (xyz) == HTTP_MOVED_TEMP \ | |
131 | || (xyz) == HTTP_TEMP_REDIRECT \ | 131 | || (xyz) == HTTP_TEMP_REDIRECT \ | |
132 | || (xyz) == HTTP_SEE_OTHER) | 132 | || (xyz) == HTTP_SEE_OTHER) | |
133 | 133 | |||
134 | #define HTTP_ERROR(xyz) ((xyz) > 400 && (xyz) < 599) | 134 | #define HTTP_ERROR(xyz) ((xyz) > 400 && (xyz) < 599) | |
135 | 135 | |||
136 | 136 | |||
137 | /***************************************************************************** | 137 | /***************************************************************************** | |
138 | * I/O functions for decoding chunked streams | 138 | * I/O functions for decoding chunked streams | |
139 | */ | 139 | */ | |
140 | 140 | |||
141 | struct httpio | 141 | struct httpio | |
142 | { | 142 | { | |
143 | conn_t *conn; /* connection */ | 143 | conn_t *conn; /* connection */ | |
144 | int chunked; /* chunked mode */ | 144 | int chunked; /* chunked mode */ | |
145 | int keep_alive; /* keep-alive mode */ | 145 | int keep_alive; /* keep-alive mode */ | |
146 | char *buf; /* chunk buffer */ | 146 | char *buf; /* chunk buffer */ | |
147 | size_t bufsize; /* size of chunk buffer */ | 147 | size_t bufsize; /* size of chunk buffer */ | |
148 | ssize_t buflen; /* amount of data currently in buffer */ | 148 | ssize_t buflen; /* amount of data currently in buffer */ | |
149 | size_t bufpos; /* current read offset in buffer */ | 149 | size_t bufpos; /* current read offset in buffer */ | |
150 | int eof; /* end-of-file flag */ | 150 | int eof; /* end-of-file flag */ | |
151 | int error; /* error flag */ | 151 | int error; /* error flag */ | |
152 | size_t chunksize; /* remaining size of current chunk */ | 152 | size_t chunksize; /* remaining size of current chunk */ | |
153 | off_t contentlength; /* remaining size of the content */ | 153 | off_t contentlength; /* remaining size of the content */ | |
154 | }; | 154 | }; | |
155 | 155 | |||
156 | /* | 156 | /* | |
157 | * Get next chunk header | 157 | * Get next chunk header | |
158 | */ | 158 | */ | |
159 | static ssize_t | 159 | static ssize_t | |
160 | http_new_chunk(struct httpio *io) | 160 | http_new_chunk(struct httpio *io) | |
161 | { | 161 | { | |
162 | char *p; | 162 | char *p; | |
163 | 163 | |||
164 | if (fetch_getln(io->conn) == -1) | 164 | if (fetch_getln(io->conn) == -1) | |
165 | return (-1); | 165 | return (-1); | |
166 | 166 | |||
167 | if (io->conn->buflen < 2 || !isxdigit((unsigned char)*io->conn->buf)) | 167 | if (io->conn->buflen < 2 || !isxdigit((unsigned char)*io->conn->buf)) | |
168 | return (-1); | 168 | return (-1); | |
169 | 169 | |||
170 | for (p = io->conn->buf; *p && !isspace((unsigned char)*p); ++p) { | 170 | for (p = io->conn->buf; *p && !isspace((unsigned char)*p); ++p) { | |
171 | if (*p == ';') | 171 | if (*p == ';') | |
172 | break; | 172 | break; | |
173 | if (!isxdigit((unsigned char)*p)) | 173 | if (!isxdigit((unsigned char)*p)) | |
174 | return (-1); | 174 | return (-1); | |
175 | if (isdigit((unsigned char)*p)) { | 175 | if (isdigit((unsigned char)*p)) { | |
176 | io->chunksize = io->chunksize * 16 + | 176 | io->chunksize = io->chunksize * 16 + | |
177 | *p - '0'; | 177 | *p - '0'; | |
178 | } else { | 178 | } else { | |
179 | io->chunksize = io->chunksize * 16 + | 179 | io->chunksize = io->chunksize * 16 + | |
180 | 10 + tolower((unsigned char)*p) - 'a'; | 180 | 10 + tolower((unsigned char)*p) - 'a'; | |
181 | } | 181 | } | |
182 | } | 182 | } | |
183 | 183 | |||
184 | return (io->chunksize); | 184 | return (io->chunksize); | |
185 | } | 185 | } | |
186 | 186 | |||
187 | /* | 187 | /* | |
188 | * Grow the input buffer to at least len bytes | 188 | * Grow the input buffer to at least len bytes | |
189 | */ | 189 | */ | |
190 | static int | 190 | static int | |
191 | http_growbuf(struct httpio *io, size_t len) | 191 | http_growbuf(struct httpio *io, size_t len) | |
192 | { | 192 | { | |
193 | char *tmp; | 193 | char *tmp; | |
194 | 194 | |||
195 | if (io->bufsize >= len) | 195 | if (io->bufsize >= len) | |
196 | return (0); | 196 | return (0); | |
197 | 197 | |||
198 | if ((tmp = realloc(io->buf, len)) == NULL) | 198 | if ((tmp = realloc(io->buf, len)) == NULL) | |
199 | return (-1); | 199 | return (-1); | |
200 | io->buf = tmp; | 200 | io->buf = tmp; | |
201 | io->bufsize = len; | 201 | io->bufsize = len; | |
202 | return (0); | 202 | return (0); | |
203 | } | 203 | } | |
204 | 204 | |||
205 | /* | 205 | /* | |
206 | * Fill the input buffer, do chunk decoding on the fly | 206 | * Fill the input buffer, do chunk decoding on the fly | |
207 | */ | 207 | */ | |
208 | static ssize_t | 208 | static ssize_t | |
209 | http_fillbuf(struct httpio *io, size_t len) | 209 | http_fillbuf(struct httpio *io, size_t len) | |
210 | { | 210 | { | |
211 | if (io->error) | 211 | if (io->error) | |
212 | return (-1); | 212 | return (-1); | |
213 | if (io->eof) | 213 | if (io->eof) | |
214 | return (0); | 214 | return (0); | |
215 | 215 | |||
216 | if (io->contentlength >= 0 && (off_t)len > io->contentlength) | 216 | if (io->contentlength >= 0 && (off_t)len > io->contentlength) | |
217 | len = io->contentlength; | 217 | len = io->contentlength; | |
218 | 218 | |||
219 | if (io->chunked == 0) { | 219 | if (io->chunked == 0) { | |
220 | if (http_growbuf(io, len) == -1) | 220 | if (http_growbuf(io, len) == -1) | |
221 | return (-1); | 221 | return (-1); | |
222 | if ((io->buflen = fetch_read(io->conn, io->buf, len)) == -1) { | 222 | if ((io->buflen = fetch_read(io->conn, io->buf, len)) == -1) { | |
223 | io->error = 1; | 223 | io->error = 1; | |
224 | return (-1); | 224 | return (-1); | |
225 | } | 225 | } | |
226 | if (io->contentlength) | 226 | if (io->contentlength) | |
227 | io->contentlength -= io->buflen; | 227 | io->contentlength -= io->buflen; | |
228 | io->bufpos = 0; | 228 | io->bufpos = 0; | |
229 | return (io->buflen); | 229 | return (io->buflen); | |
230 | } | 230 | } | |
231 | 231 | |||
232 | if (io->chunksize == 0) { | 232 | if (io->chunksize == 0) { | |
233 | switch (http_new_chunk(io)) { | 233 | switch (http_new_chunk(io)) { | |
234 | case -1: | 234 | case -1: | |
235 | io->error = 1; | 235 | io->error = 1; | |
236 | return (-1); | 236 | return (-1); | |
237 | case 0: | 237 | case 0: | |
238 | io->eof = 1; | 238 | io->eof = 1; | |
239 | if (fetch_getln(io->conn) == -1) | 239 | if (fetch_getln(io->conn) == -1) | |
240 | return (-1); | 240 | return (-1); | |
241 | return (0); | 241 | return (0); | |
242 | } | 242 | } | |
243 | } | 243 | } | |
244 | 244 | |||
245 | if (len > io->chunksize) | 245 | if (len > io->chunksize) | |
246 | len = io->chunksize; | 246 | len = io->chunksize; | |
247 | if (http_growbuf(io, len) == -1) | 247 | if (http_growbuf(io, len) == -1) | |
248 | return (-1); | 248 | return (-1); | |
249 | if ((io->buflen = fetch_read(io->conn, io->buf, len)) == -1) { | 249 | if ((io->buflen = fetch_read(io->conn, io->buf, len)) == -1) { | |
250 | io->error = 1; | 250 | io->error = 1; | |
251 | return (-1); | 251 | return (-1); | |
252 | } | 252 | } | |
253 | io->chunksize -= io->buflen; | 253 | io->chunksize -= io->buflen; | |
254 | if (io->contentlength >= 0) | 254 | if (io->contentlength >= 0) | |
255 | io->contentlength -= io->buflen; | 255 | io->contentlength -= io->buflen; | |
256 | 256 | |||
257 | if (io->chunksize == 0) { | 257 | if (io->chunksize == 0) { | |
258 | char endl[2]; | 258 | char endl[2]; | |
259 | ssize_t len2; | 259 | ssize_t len2; | |
260 | 260 | |||
261 | len2 = fetch_read(io->conn, endl, 2); | 261 | len2 = fetch_read(io->conn, endl, 2); | |
262 | if (len2 == 1 && fetch_read(io->conn, endl + 1, 1) != 1) | 262 | if (len2 == 1 && fetch_read(io->conn, endl + 1, 1) != 1) | |
263 | return (-1); | 263 | return (-1); | |
264 | if (len2 == -1 || endl[0] != '\r' || endl[1] != '\n') | 264 | if (len2 == -1 || endl[0] != '\r' || endl[1] != '\n') | |
265 | return (-1); | 265 | return (-1); | |
266 | } | 266 | } | |
267 | 267 | |||
268 | io->bufpos = 0; | 268 | io->bufpos = 0; | |
269 | 269 | |||
270 | return (io->buflen); | 270 | return (io->buflen); | |
271 | } | 271 | } | |
272 | 272 | |||
273 | /* | 273 | /* | |
274 | * Read function | 274 | * Read function | |
275 | */ | 275 | */ | |
276 | static ssize_t | 276 | static ssize_t | |
277 | http_readfn(void *v, void *buf, size_t len) | 277 | http_readfn(void *v, void *buf, size_t len) | |
278 | { | 278 | { | |
279 | struct httpio *io = (struct httpio *)v; | 279 | struct httpio *io = (struct httpio *)v; | |
280 | size_t l, pos; | 280 | size_t l, pos; | |
281 | 281 | |||
282 | if (io->error) | 282 | if (io->error) | |
283 | return (-1); | 283 | return (-1); | |
284 | if (io->eof) | 284 | if (io->eof) | |
285 | return (0); | 285 | return (0); | |
286 | 286 | |||
287 | for (pos = 0; len > 0; pos += l, len -= l) { | 287 | for (pos = 0; len > 0; pos += l, len -= l) { | |
288 | /* empty buffer */ | 288 | /* empty buffer */ | |
289 | if (!io->buf || (ssize_t)io->bufpos == io->buflen) | 289 | if (!io->buf || (ssize_t)io->bufpos == io->buflen) | |
290 | if (http_fillbuf(io, len) < 1) | 290 | if (http_fillbuf(io, len) < 1) | |
291 | break; | 291 | break; | |
292 | l = io->buflen - io->bufpos; | 292 | l = io->buflen - io->bufpos; | |
293 | if (len < l) | 293 | if (len < l) | |
294 | l = len; | 294 | l = len; | |
295 | memcpy((char *)buf + pos, io->buf + io->bufpos, l); | 295 | memcpy((char *)buf + pos, io->buf + io->bufpos, l); | |
296 | io->bufpos += l; | 296 | io->bufpos += l; | |
297 | } | 297 | } | |
298 | 298 | |||
299 | if (!pos && io->error) | 299 | if (!pos && io->error) | |
300 | return (-1); | 300 | return (-1); | |
301 | return (pos); | 301 | return (pos); | |
302 | } | 302 | } | |
303 | 303 | |||
304 | /* | 304 | /* | |
305 | * Write function | 305 | * Write function | |
306 | */ | 306 | */ | |
307 | static ssize_t | 307 | static ssize_t | |
308 | http_writefn(void *v, const void *buf, size_t len) | 308 | http_writefn(void *v, const void *buf, size_t len) | |
309 | { | 309 | { | |
310 | struct httpio *io = (struct httpio *)v; | 310 | struct httpio *io = (struct httpio *)v; | |
311 | 311 | |||
312 | return (fetch_write(io->conn, buf, len)); | 312 | return (fetch_write(io->conn, buf, len)); | |
313 | } | 313 | } | |
314 | 314 | |||
315 | /* | 315 | /* | |
316 | * Close function | 316 | * Close function | |
317 | */ | 317 | */ | |
318 | static void | 318 | static void | |
319 | http_closefn(void *v) | 319 | http_closefn(void *v) | |
320 | { | 320 | { | |
321 | struct httpio *io = (struct httpio *)v; | 321 | struct httpio *io = (struct httpio *)v; | |
322 | 322 | |||
323 | if (io->keep_alive) { | 323 | if (io->keep_alive) { | |
324 | int val; | 324 | int val; | |
325 | 325 | |||
326 | val = 0; | 326 | val = 0; | |
327 | setsockopt(io->conn->sd, IPPROTO_TCP, TCP_NODELAY, &val, | 327 | setsockopt(io->conn->sd, IPPROTO_TCP, TCP_NODELAY, &val, | |
328 | (socklen_t)sizeof(val)); | 328 | (socklen_t)sizeof(val)); | |
329 | fetch_cache_put(io->conn, fetch_close); | 329 | fetch_cache_put(io->conn, fetch_close); | |
330 | #ifdef TCP_NOPUSH | 330 | #ifdef TCP_NOPUSH | |
331 | val = 1; | 331 | val = 1; | |
332 | setsockopt(io->conn->sd, IPPROTO_TCP, TCP_NOPUSH, &val, | 332 | setsockopt(io->conn->sd, IPPROTO_TCP, TCP_NOPUSH, &val, | |
333 | sizeof(val)); | 333 | sizeof(val)); | |
334 | #endif | 334 | #endif | |
335 | } else { | 335 | } else { | |
336 | fetch_close(io->conn); | 336 | fetch_close(io->conn); | |
337 | } | 337 | } | |
338 | 338 | |||
339 | free(io->buf); | 339 | free(io->buf); | |
340 | free(io); | 340 | free(io); | |
341 | } | 341 | } | |
342 | 342 | |||
343 | /* | 343 | /* | |
344 | * Wrap a file descriptor up | 344 | * Wrap a file descriptor up | |
345 | */ | 345 | */ | |
346 | static fetchIO * | 346 | static fetchIO * | |
347 | http_funopen(conn_t *conn, int chunked, int keep_alive, off_t clength) | 347 | http_funopen(conn_t *conn, int chunked, int keep_alive, off_t clength) | |
348 | { | 348 | { | |
349 | struct httpio *io; | 349 | struct httpio *io; | |
350 | fetchIO *f; | 350 | fetchIO *f; | |
351 | 351 | |||
352 | if ((io = calloc(1, sizeof(*io))) == NULL) { | 352 | if ((io = calloc(1, sizeof(*io))) == NULL) { | |
353 | fetch_syserr(); | 353 | fetch_syserr(); | |
354 | return (NULL); | 354 | return (NULL); | |
355 | } | 355 | } | |
356 | io->conn = conn; | 356 | io->conn = conn; | |
357 | io->chunked = chunked; | 357 | io->chunked = chunked; | |
358 | io->contentlength = clength; | 358 | io->contentlength = clength; | |
359 | io->keep_alive = keep_alive; | 359 | io->keep_alive = keep_alive; | |
360 | f = fetchIO_unopen(io, http_readfn, http_writefn, http_closefn); | 360 | f = fetchIO_unopen(io, http_readfn, http_writefn, http_closefn); | |
361 | if (f == NULL) { | 361 | if (f == NULL) { | |
362 | fetch_syserr(); | 362 | fetch_syserr(); | |
363 | free(io); | 363 | free(io); | |
364 | return (NULL); | 364 | return (NULL); | |
365 | } | 365 | } | |
366 | return (f); | 366 | return (f); | |
367 | } | 367 | } | |
368 | 368 | |||
369 | 369 | |||
370 | /***************************************************************************** | 370 | /***************************************************************************** | |
371 | * Helper functions for talking to the server and parsing its replies | 371 | * Helper functions for talking to the server and parsing its replies | |
372 | */ | 372 | */ | |
373 | 373 | |||
374 | /* Header types */ | 374 | /* Header types */ | |
375 | typedef enum { | 375 | typedef enum { | |
376 | hdr_syserror = -2, | 376 | hdr_syserror = -2, | |
377 | hdr_error = -1, | 377 | hdr_error = -1, | |
378 | hdr_end = 0, | 378 | hdr_end = 0, | |
379 | hdr_unknown = 1, | 379 | hdr_unknown = 1, | |
380 | hdr_connection, | 380 | hdr_connection, | |
381 | hdr_content_length, | 381 | hdr_content_length, | |
382 | hdr_content_range, | 382 | hdr_content_range, | |
383 | hdr_last_modified, | 383 | hdr_last_modified, | |
384 | hdr_location, | 384 | hdr_location, | |
385 | hdr_transfer_encoding, | 385 | hdr_transfer_encoding, | |
386 | hdr_www_authenticate | 386 | hdr_www_authenticate | |
387 | } hdr_t; | 387 | } hdr_t; | |
388 | 388 | |||
389 | /* Names of interesting headers */ | 389 | /* Names of interesting headers */ | |
390 | static struct { | 390 | static struct { | |
391 | hdr_t num; | 391 | hdr_t num; | |
392 | const char *name; | 392 | const char *name; | |
393 | } hdr_names[] = { | 393 | } hdr_names[] = { | |
394 | { hdr_connection, "Connection" }, | 394 | { hdr_connection, "Connection" }, | |
395 | { hdr_content_length, "Content-Length" }, | 395 | { hdr_content_length, "Content-Length" }, | |
396 | { hdr_content_range, "Content-Range" }, | 396 | { hdr_content_range, "Content-Range" }, | |
397 | { hdr_last_modified, "Last-Modified" }, | 397 | { hdr_last_modified, "Last-Modified" }, | |
398 | { hdr_location, "Location" }, | 398 | { hdr_location, "Location" }, | |
399 | { hdr_transfer_encoding, "Transfer-Encoding" }, | 399 | { hdr_transfer_encoding, "Transfer-Encoding" }, | |
400 | { hdr_www_authenticate, "WWW-Authenticate" }, | 400 | { hdr_www_authenticate, "WWW-Authenticate" }, | |
401 | { hdr_unknown, NULL }, | 401 | { hdr_unknown, NULL }, | |
402 | }; | 402 | }; | |
403 | 403 | |||
404 | /* | 404 | /* | |
405 | * Send a formatted line; optionally echo to terminal | 405 | * Send a formatted line; optionally echo to terminal | |
406 | */ | 406 | */ | |
407 | __printflike(2, 3) | |||
407 | static int | 408 | static int | |
408 | http_cmd(conn_t *conn, const char *fmt, ...) | 409 | http_cmd(conn_t *conn, const char *fmt, ...) | |
409 | { | 410 | { | |
410 | va_list ap; | 411 | va_list ap; | |
411 | size_t len; | 412 | size_t len; | |
412 | char *msg; | 413 | char *msg; | |
413 | ssize_t r; | 414 | ssize_t r; | |
414 | 415 | |||
415 | va_start(ap, fmt); | 416 | va_start(ap, fmt); | |
416 | len = vasprintf(&msg, fmt, ap); | 417 | len = vasprintf(&msg, fmt, ap); | |
417 | va_end(ap); | 418 | va_end(ap); | |
418 | 419 | |||
419 | if (msg == NULL) { | 420 | if (msg == NULL) { | |
420 | errno = ENOMEM; | 421 | errno = ENOMEM; | |
421 | fetch_syserr(); | 422 | fetch_syserr(); | |
422 | return (-1); | 423 | return (-1); | |
423 | } | 424 | } | |
424 | 425 | |||
425 | r = fetch_write(conn, msg, len); | 426 | r = fetch_write(conn, msg, len); | |
426 | free(msg); | 427 | free(msg); | |
427 | 428 | |||
428 | if (r == -1) { | 429 | if (r == -1) { | |
429 | fetch_syserr(); | 430 | fetch_syserr(); | |
430 | return (-1); | 431 | return (-1); | |
431 | } | 432 | } | |
432 | 433 | |||
433 | return (0); | 434 | return (0); | |
434 | } | 435 | } | |
435 | 436 | |||
436 | /* | 437 | /* | |
437 | * Get and parse status line | 438 | * Get and parse status line | |
438 | */ | 439 | */ | |
439 | static int | 440 | static int | |
440 | http_get_reply(conn_t *conn) | 441 | http_get_reply(conn_t *conn) | |
441 | { | 442 | { | |
442 | char *p; | 443 | char *p; | |
443 | 444 | |||
444 | if (fetch_getln(conn) == -1) | 445 | if (fetch_getln(conn) == -1) | |
445 | return (-1); | 446 | return (-1); | |
446 | /* | 447 | /* | |
447 | * A valid status line looks like "HTTP/m.n xyz reason" where m | 448 | * A valid status line looks like "HTTP/m.n xyz reason" where m | |
448 | * and n are the major and minor protocol version numbers and xyz | 449 | * and n are the major and minor protocol version numbers and xyz | |
449 | * is the reply code. | 450 | * is the reply code. | |
450 | * Unfortunately, there are servers out there (NCSA 1.5.1, to name | 451 | * Unfortunately, there are servers out there (NCSA 1.5.1, to name | |
451 | * just one) that do not send a version number, so we can't rely | 452 | * just one) that do not send a version number, so we can't rely | |
452 | * on finding one, but if we do, insist on it being 1.0 or 1.1. | 453 | * on finding one, but if we do, insist on it being 1.0 or 1.1. | |
453 | * We don't care about the reason phrase. | 454 | * We don't care about the reason phrase. | |
454 | */ | 455 | */ | |
455 | if (strncmp(conn->buf, "HTTP", 4) != 0) | 456 | if (strncmp(conn->buf, "HTTP", 4) != 0) | |
456 | return (HTTP_PROTOCOL_ERROR); | 457 | return (HTTP_PROTOCOL_ERROR); | |
457 | p = conn->buf + 4; | 458 | p = conn->buf + 4; | |
458 | if (*p == '/') { | 459 | if (*p == '/') { | |
459 | if (p[1] != '1' || p[2] != '.' || (p[3] != '0' && p[3] != '1')) | 460 | if (p[1] != '1' || p[2] != '.' || (p[3] != '0' && p[3] != '1')) | |
460 | return (HTTP_PROTOCOL_ERROR); | 461 | return (HTTP_PROTOCOL_ERROR); | |
461 | p += 4; | 462 | p += 4; | |
462 | } | 463 | } | |
463 | if (*p != ' ' || | 464 | if (*p != ' ' || | |
464 | !isdigit((unsigned char)p[1]) || | 465 | !isdigit((unsigned char)p[1]) || | |
465 | !isdigit((unsigned char)p[2]) || | 466 | !isdigit((unsigned char)p[2]) || | |
466 | !isdigit((unsigned char)p[3])) | 467 | !isdigit((unsigned char)p[3])) | |
467 | return (HTTP_PROTOCOL_ERROR); | 468 | return (HTTP_PROTOCOL_ERROR); | |
468 | 469 | |||
469 | conn->err = (p[1] - '0') * 100 + (p[2] - '0') * 10 + (p[3] - '0'); | 470 | conn->err = (p[1] - '0') * 100 + (p[2] - '0') * 10 + (p[3] - '0'); | |
470 | return (conn->err); | 471 | return (conn->err); | |
471 | } | 472 | } | |
472 | 473 | |||
473 | /* | 474 | /* | |
474 | * Check a header; if the type matches the given string, return a pointer | 475 | * Check a header; if the type matches the given string, return a pointer | |
475 | * to the beginning of the value. | 476 | * to the beginning of the value. | |
476 | */ | 477 | */ | |
477 | static const char * | 478 | static const char * | |
478 | http_match(const char *str, const char *hdr) | 479 | http_match(const char *str, const char *hdr) | |
479 | { | 480 | { | |
480 | while (*str && *hdr && | 481 | while (*str && *hdr && | |
481 | tolower((unsigned char)*str++) == tolower((unsigned char)*hdr++)) | 482 | tolower((unsigned char)*str++) == tolower((unsigned char)*hdr++)) | |
482 | /* nothing */; | 483 | /* nothing */; | |
483 | if (*str || *hdr != ':') | 484 | if (*str || *hdr != ':') | |
484 | return (NULL); | 485 | return (NULL); | |
485 | while (*hdr && isspace((unsigned char)*++hdr)) | 486 | while (*hdr && isspace((unsigned char)*++hdr)) | |
486 | /* nothing */; | 487 | /* nothing */; | |
487 | return (hdr); | 488 | return (hdr); | |
488 | } | 489 | } | |
489 | 490 | |||
490 | /* | 491 | /* | |
491 | * Get the next header and return the appropriate symbolic code. | 492 | * Get the next header and return the appropriate symbolic code. | |
492 | */ | 493 | */ | |
493 | static hdr_t | 494 | static hdr_t | |
494 | http_next_header(conn_t *conn, const char **p) | 495 | http_next_header(conn_t *conn, const char **p) | |
495 | { | 496 | { | |
496 | int i; | 497 | int i; | |
497 | 498 | |||
498 | if (fetch_getln(conn) == -1) | 499 | if (fetch_getln(conn) == -1) | |
499 | return (hdr_syserror); | 500 | return (hdr_syserror); | |
500 | while (conn->buflen && isspace((unsigned char)conn->buf[conn->buflen - 1])) | 501 | while (conn->buflen && isspace((unsigned char)conn->buf[conn->buflen - 1])) | |
501 | conn->buflen--; | 502 | conn->buflen--; | |
502 | conn->buf[conn->buflen] = '\0'; | 503 | conn->buf[conn->buflen] = '\0'; | |
503 | if (conn->buflen == 0) | 504 | if (conn->buflen == 0) | |
504 | return (hdr_end); | 505 | return (hdr_end); | |
505 | /* | 506 | /* | |
506 | * We could check for malformed headers but we don't really care. | 507 | * We could check for malformed headers but we don't really care. | |
507 | * A valid header starts with a token immediately followed by a | 508 | * A valid header starts with a token immediately followed by a | |
508 | * colon; a token is any sequence of non-control, non-whitespace | 509 | * colon; a token is any sequence of non-control, non-whitespace | |
509 | * characters except "()<>@,;:\\\"{}". | 510 | * characters except "()<>@,;:\\\"{}". | |
510 | */ | 511 | */ | |
511 | for (i = 0; hdr_names[i].num != hdr_unknown; i++) | 512 | for (i = 0; hdr_names[i].num != hdr_unknown; i++) | |
512 | if ((*p = http_match(hdr_names[i].name, conn->buf)) != NULL) | 513 | if ((*p = http_match(hdr_names[i].name, conn->buf)) != NULL) | |
513 | return (hdr_names[i].num); | 514 | return (hdr_names[i].num); | |
514 | return (hdr_unknown); | 515 | return (hdr_unknown); | |
515 | } | 516 | } | |
516 | 517 | |||
517 | /* | 518 | /* | |
518 | * Parse a last-modified header | 519 | * Parse a last-modified header | |
519 | */ | 520 | */ | |
520 | static int | 521 | static int | |
521 | http_parse_mtime(const char *p, time_t *mtime) | 522 | http_parse_mtime(const char *p, time_t *mtime) | |
522 | { | 523 | { | |
523 | char locale[64], *r; | 524 | char locale[64], *r; | |
524 | struct tm tm; | 525 | struct tm tm; | |
525 | 526 | |||
526 | strncpy(locale, setlocale(LC_TIME, NULL), sizeof(locale)); | 527 | strncpy(locale, setlocale(LC_TIME, NULL), sizeof(locale)); | |
527 | setlocale(LC_TIME, "C"); | 528 | setlocale(LC_TIME, "C"); | |
528 | r = strptime(p, "%a, %d %b %Y %H:%M:%S GMT", &tm); | 529 | r = strptime(p, "%a, %d %b %Y %H:%M:%S GMT", &tm); | |
529 | /* XXX should add support for date-2 and date-3 */ | 530 | /* XXX should add support for date-2 and date-3 */ | |
530 | setlocale(LC_TIME, locale); | 531 | setlocale(LC_TIME, locale); | |
531 | if (r == NULL) | 532 | if (r == NULL) | |
532 | return (-1); | 533 | return (-1); | |
533 | *mtime = timegm(&tm); | 534 | *mtime = timegm(&tm); | |
534 | return (0); | 535 | return (0); | |
535 | } | 536 | } | |
536 | 537 | |||
537 | /* | 538 | /* | |
538 | * Parse a content-length header | 539 | * Parse a content-length header | |
539 | */ | 540 | */ | |
540 | static int | 541 | static int | |
541 | http_parse_length(const char *p, off_t *length) | 542 | http_parse_length(const char *p, off_t *length) | |
542 | { | 543 | { | |
543 | off_t len; | 544 | off_t len; | |
544 | 545 | |||
545 | for (len = 0; *p && isdigit((unsigned char)*p); ++p) | 546 | for (len = 0; *p && isdigit((unsigned char)*p); ++p) | |
546 | len = len * 10 + (*p - '0'); | 547 | len = len * 10 + (*p - '0'); | |
547 | if (*p) | 548 | if (*p) | |
548 | return (-1); | 549 | return (-1); | |
549 | *length = len; | 550 | *length = len; | |
550 | return (0); | 551 | return (0); | |
551 | } | 552 | } | |
552 | 553 | |||
553 | /* | 554 | /* | |
554 | * Parse a content-range header | 555 | * Parse a content-range header | |
555 | */ | 556 | */ | |
556 | static int | 557 | static int | |
557 | http_parse_range(const char *p, off_t *offset, off_t *length, off_t *size) | 558 | http_parse_range(const char *p, off_t *offset, off_t *length, off_t *size) | |
558 | { | 559 | { | |
559 | off_t first, last, len; | 560 | off_t first, last, len; | |
560 | 561 | |||
561 | if (strncasecmp(p, "bytes ", 6) != 0) | 562 | if (strncasecmp(p, "bytes ", 6) != 0) | |
562 | return (-1); | 563 | return (-1); | |
563 | p += 6; | 564 | p += 6; | |
564 | if (*p == '*') { | 565 | if (*p == '*') { | |
565 | first = last = -1; | 566 | first = last = -1; | |
566 | ++p; | 567 | ++p; | |
567 | } else { | 568 | } else { | |
568 | for (first = 0; *p && isdigit((unsigned char)*p); ++p) | 569 | for (first = 0; *p && isdigit((unsigned char)*p); ++p) | |
569 | first = first * 10 + *p - '0'; | 570 | first = first * 10 + *p - '0'; | |
570 | if (*p != '-') | 571 | if (*p != '-') | |
571 | return (-1); | 572 | return (-1); | |
572 | for (last = 0, ++p; *p && isdigit((unsigned char)*p); ++p) | 573 | for (last = 0, ++p; *p && isdigit((unsigned char)*p); ++p) | |
573 | last = last * 10 + *p - '0'; | 574 | last = last * 10 + *p - '0'; | |
574 | } | 575 | } | |
575 | if (first > last || *p != '/') | 576 | if (first > last || *p != '/') | |
576 | return (-1); | 577 | return (-1); | |
577 | for (len = 0, ++p; *p && isdigit((unsigned char)*p); ++p) | 578 | for (len = 0, ++p; *p && isdigit((unsigned char)*p); ++p) | |
578 | len = len * 10 + *p - '0'; | 579 | len = len * 10 + *p - '0'; | |
579 | if (*p || len < last - first + 1) | 580 | if (*p || len < last - first + 1) | |
580 | return (-1); | 581 | return (-1); | |
581 | if (first == -1) | 582 | if (first == -1) | |
582 | *length = 0; | 583 | *length = 0; | |
583 | else | 584 | else | |
584 | *length = last - first + 1; | 585 | *length = last - first + 1; | |
585 | *offset = first; | 586 | *offset = first; | |
586 | *size = len; | 587 | *size = len; | |
587 | return (0); | 588 | return (0); | |
588 | } | 589 | } | |
589 | 590 | |||
590 | 591 | |||
591 | /***************************************************************************** | 592 | /***************************************************************************** | |
592 | * Helper functions for authorization | 593 | * Helper functions for authorization | |
593 | */ | 594 | */ | |
594 | 595 | |||
595 | /* | 596 | /* | |
596 | * Base64 encoding | 597 | * Base64 encoding | |
597 | */ | 598 | */ | |
598 | static char * | 599 | static char * | |
599 | http_base64(const char *src) | 600 | http_base64(const char *src) | |
600 | { | 601 | { | |
601 | static const char base64[] = | 602 | static const char base64[] = | |
602 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ" | 603 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ" | |
603 | "abcdefghijklmnopqrstuvwxyz" | 604 | "abcdefghijklmnopqrstuvwxyz" | |
604 | "0123456789+/"; | 605 | "0123456789+/"; | |
605 | char *str, *dst; | 606 | char *str, *dst; | |
606 | size_t l; | 607 | size_t l; | |
607 | unsigned int t, r; | 608 | unsigned int t, r; | |
608 | 609 | |||
609 | l = strlen(src); | 610 | l = strlen(src); | |
610 | if ((str = malloc(((l + 2) / 3) * 4 + 1)) == NULL) | 611 | if ((str = malloc(((l + 2) / 3) * 4 + 1)) == NULL) | |
611 | return (NULL); | 612 | return (NULL); | |
612 | dst = str; | 613 | dst = str; | |
613 | r = 0; | 614 | r = 0; | |
614 | 615 | |||
615 | while (l >= 3) { | 616 | while (l >= 3) { | |
616 | t = (src[0] << 16) | (src[1] << 8) | src[2]; | 617 | t = (src[0] << 16) | (src[1] << 8) | src[2]; | |
617 | dst[0] = base64[(t >> 18) & 0x3f]; | 618 | dst[0] = base64[(t >> 18) & 0x3f]; | |
618 | dst[1] = base64[(t >> 12) & 0x3f]; | 619 | dst[1] = base64[(t >> 12) & 0x3f]; | |
619 | dst[2] = base64[(t >> 6) & 0x3f]; | 620 | dst[2] = base64[(t >> 6) & 0x3f]; | |
620 | dst[3] = base64[(t >> 0) & 0x3f]; | 621 | dst[3] = base64[(t >> 0) & 0x3f]; | |
621 | src += 3; l -= 3; | 622 | src += 3; l -= 3; | |
622 | dst += 4; r += 4; | 623 | dst += 4; r += 4; | |
623 | } | 624 | } | |
624 | 625 | |||
625 | switch (l) { | 626 | switch (l) { | |
626 | case 2: | 627 | case 2: | |
627 | t = (src[0] << 16) | (src[1] << 8); | 628 | t = (src[0] << 16) | (src[1] << 8); | |
628 | dst[0] = base64[(t >> 18) & 0x3f]; | 629 | dst[0] = base64[(t >> 18) & 0x3f]; | |
629 | dst[1] = base64[(t >> 12) & 0x3f]; | 630 | dst[1] = base64[(t >> 12) & 0x3f]; | |
630 | dst[2] = base64[(t >> 6) & 0x3f]; | 631 | dst[2] = base64[(t >> 6) & 0x3f]; | |
631 | dst[3] = '='; | 632 | dst[3] = '='; | |
632 | dst += 4; | 633 | dst += 4; | |
633 | r += 4; | 634 | r += 4; | |
634 | break; | 635 | break; | |
635 | case 1: | 636 | case 1: | |
636 | t = src[0] << 16; | 637 | t = src[0] << 16; | |
637 | dst[0] = base64[(t >> 18) & 0x3f]; | 638 | dst[0] = base64[(t >> 18) & 0x3f]; | |
638 | dst[1] = base64[(t >> 12) & 0x3f]; | 639 | dst[1] = base64[(t >> 12) & 0x3f]; | |
639 | dst[2] = dst[3] = '='; | 640 | dst[2] = dst[3] = '='; | |
640 | dst += 4; | 641 | dst += 4; | |
641 | r += 4; | 642 | r += 4; | |
642 | break; | 643 | break; | |
643 | case 0: | 644 | case 0: | |
644 | break; | 645 | break; | |
645 | } | 646 | } | |
646 | 647 | |||
647 | *dst = 0; | 648 | *dst = 0; | |
648 | return (str); | 649 | return (str); | |
649 | } | 650 | } | |
650 | 651 | |||
651 | /* | 652 | /* | |
652 | * Encode username and password | 653 | * Encode username and password | |
653 | */ | 654 | */ | |
654 | static int | 655 | static int | |
655 | http_basic_auth(conn_t *conn, const char *hdr, const char *usr, const char *pwd) | 656 | http_basic_auth(conn_t *conn, const char *hdr, const char *usr, const char *pwd) | |
656 | { | 657 | { | |
657 | char *upw, *auth; | 658 | char *upw, *auth; | |
658 | int r; | 659 | int r; | |
659 | 660 | |||
660 | if (asprintf(&upw, "%s:%s", usr, pwd) == -1) | 661 | if (asprintf(&upw, "%s:%s", usr, pwd) == -1) | |
661 | return (-1); | 662 | return (-1); | |
662 | auth = http_base64(upw); | 663 | auth = http_base64(upw); | |
663 | free(upw); | 664 | free(upw); | |
664 | if (auth == NULL) | 665 | if (auth == NULL) | |
665 | return (-1); | 666 | return (-1); | |
666 | r = http_cmd(conn, "%s: Basic %s\r\n", hdr, auth); | 667 | r = http_cmd(conn, "%s: Basic %s\r\n", hdr, auth); | |
667 | free(auth); | 668 | free(auth); | |
668 | return (r); | 669 | return (r); | |
669 | } | 670 | } | |
670 | 671 | |||
671 | /* | 672 | /* | |
672 | * Send an authorization header | 673 | * Send an authorization header | |
673 | */ | 674 | */ | |
674 | static int | 675 | static int | |
675 | http_authorize(conn_t *conn, const char *hdr, const char *p) | 676 | http_authorize(conn_t *conn, const char *hdr, const char *p) | |
676 | { | 677 | { | |
677 | /* basic authorization */ | 678 | /* basic authorization */ | |
678 | if (strncasecmp(p, "basic:", 6) == 0) { | 679 | if (strncasecmp(p, "basic:", 6) == 0) { | |
679 | char *user, *pwd, *str; | 680 | char *user, *pwd, *str; | |
680 | int r; | 681 | int r; | |
681 | 682 | |||
682 | /* skip realm */ | 683 | /* skip realm */ | |
683 | for (p += 6; *p && *p != ':'; ++p) | 684 | for (p += 6; *p && *p != ':'; ++p) | |
684 | /* nothing */ ; | 685 | /* nothing */ ; | |
685 | if (!*p || strchr(++p, ':') == NULL) | 686 | if (!*p || strchr(++p, ':') == NULL) | |
686 | return (-1); | 687 | return (-1); | |
687 | if ((str = strdup(p)) == NULL) | 688 | if ((str = strdup(p)) == NULL) | |
688 | return (-1); /* XXX */ | 689 | return (-1); /* XXX */ | |
689 | user = str; | 690 | user = str; | |
690 | pwd = strchr(str, ':'); | 691 | pwd = strchr(str, ':'); | |
691 | *pwd++ = '\0'; | 692 | *pwd++ = '\0'; | |
692 | r = http_basic_auth(conn, hdr, user, pwd); | 693 | r = http_basic_auth(conn, hdr, user, pwd); | |
693 | free(str); | 694 | free(str); | |
694 | return (r); | 695 | return (r); | |
695 | } | 696 | } | |
696 | return (-1); | 697 | return (-1); | |
697 | } | 698 | } | |
698 | 699 | |||
699 | 700 | |||
700 | /***************************************************************************** | 701 | /***************************************************************************** | |
701 | * Helper functions for connecting to a server or proxy | 702 | * Helper functions for connecting to a server or proxy | |
702 | */ | 703 | */ | |
703 | 704 | |||
704 | /* | 705 | /* | |
705 | * Connect to the correct HTTP server or proxy. | 706 | * Connect to the correct HTTP server or proxy. | |
706 | */ | 707 | */ | |
707 | static conn_t * | 708 | static conn_t * | |
708 | http_connect(struct url *URL, struct url *purl, const char *flags, int *cached) | 709 | http_connect(struct url *URL, struct url *purl, const char *flags, int *cached) | |
709 | { | 710 | { | |
710 | conn_t *conn; | 711 | conn_t *conn; | |
711 | int af, verbose; | 712 | int af, verbose; | |
712 | #ifdef TCP_NOPUSH | 713 | #ifdef TCP_NOPUSH | |
713 | int val; | 714 | int val; | |
714 | #endif | 715 | #endif | |
715 | 716 | |||
716 | *cached = 1; | 717 | *cached = 1; | |
717 | 718 | |||
718 | #ifdef INET6 | 719 | #ifdef INET6 | |
719 | af = AF_UNSPEC; | 720 | af = AF_UNSPEC; | |
720 | #else | 721 | #else | |
721 | af = AF_INET; | 722 | af = AF_INET; | |
722 | #endif | 723 | #endif | |
723 | 724 | |||
724 | verbose = CHECK_FLAG('v'); | 725 | verbose = CHECK_FLAG('v'); | |
725 | if (CHECK_FLAG('4')) | 726 | if (CHECK_FLAG('4')) | |
726 | af = AF_INET; | 727 | af = AF_INET; | |
727 | #ifdef INET6 | 728 | #ifdef INET6 | |
728 | else if (CHECK_FLAG('6')) | 729 | else if (CHECK_FLAG('6')) | |
729 | af = AF_INET6; | 730 | af = AF_INET6; | |
730 | #endif | 731 | #endif | |
731 | 732 | |||
732 | if (purl && strcasecmp(URL->scheme, SCHEME_HTTPS) != 0) { | 733 | if (purl && strcasecmp(URL->scheme, SCHEME_HTTPS) != 0) { | |
733 | URL = purl; | 734 | URL = purl; | |
734 | } else if (strcasecmp(URL->scheme, SCHEME_FTP) == 0) { | 735 | } else if (strcasecmp(URL->scheme, SCHEME_FTP) == 0) { | |
735 | /* can't talk http to an ftp server */ | 736 | /* can't talk http to an ftp server */ | |
736 | /* XXX should set an error code */ | 737 | /* XXX should set an error code */ | |
737 | return (NULL); | 738 | return (NULL); | |
738 | } | 739 | } | |
739 | 740 | |||
740 | if ((conn = fetch_cache_get(URL, af)) != NULL) { | 741 | if ((conn = fetch_cache_get(URL, af)) != NULL) { | |
741 | *cached = 1; | 742 | *cached = 1; | |
742 | return (conn); | 743 | return (conn); | |
743 | } | 744 | } | |
744 | 745 | |||
745 | if ((conn = fetch_connect(URL, af, verbose)) == NULL) | 746 | if ((conn = fetch_connect(URL, af, verbose)) == NULL) | |
746 | /* fetch_connect() has already set an error code */ | 747 | /* fetch_connect() has already set an error code */ | |
747 | return (NULL); | 748 | return (NULL); | |
748 | if (strcasecmp(URL->scheme, SCHEME_HTTPS) == 0 && | 749 | if (strcasecmp(URL->scheme, SCHEME_HTTPS) == 0 && | |
749 | fetch_ssl(conn, verbose) == -1) { | 750 | fetch_ssl(conn, verbose) == -1) { | |
750 | fetch_close(conn); | 751 | fetch_close(conn); | |
751 | /* grrr */ | 752 | /* grrr */ | |
752 | #ifdef EAUTH | 753 | #ifdef EAUTH | |
753 | errno = EAUTH; | 754 | errno = EAUTH; | |
754 | #else | 755 | #else | |
755 | errno = EPERM; | 756 | errno = EPERM; | |
756 | #endif | 757 | #endif | |
757 | fetch_syserr(); | 758 | fetch_syserr(); | |
758 | return (NULL); | 759 | return (NULL); | |
759 | } | 760 | } | |
760 | 761 | |||
761 | #ifdef TCP_NOPUSH | 762 | #ifdef TCP_NOPUSH | |
762 | val = 1; | 763 | val = 1; | |
763 | setsockopt(conn->sd, IPPROTO_TCP, TCP_NOPUSH, &val, sizeof(val)); | 764 | setsockopt(conn->sd, IPPROTO_TCP, TCP_NOPUSH, &val, sizeof(val)); | |
764 | #endif | 765 | #endif | |
765 | 766 | |||
766 | return (conn); | 767 | return (conn); | |
767 | } | 768 | } | |
768 | 769 | |||
769 | static struct url * | 770 | static struct url * | |
770 | http_get_proxy(struct url * url, const char *flags) | 771 | http_get_proxy(struct url * url, const char *flags) | |
771 | { | 772 | { | |
772 | struct url *purl; | 773 | struct url *purl; | |
773 | char *p; | 774 | char *p; | |
774 | 775 | |||
775 | if (flags != NULL && strchr(flags, 'd') != NULL) | 776 | if (flags != NULL && strchr(flags, 'd') != NULL) | |
776 | return (NULL); | 777 | return (NULL); | |
777 | if (fetch_no_proxy_match(url->host)) | 778 | if (fetch_no_proxy_match(url->host)) | |
778 | return (NULL); | 779 | return (NULL); | |
779 | if (((p = getenv("HTTP_PROXY")) || (p = getenv("http_proxy"))) && | 780 | if (((p = getenv("HTTP_PROXY")) || (p = getenv("http_proxy"))) && | |
780 | *p && (purl = fetchParseURL(p))) { | 781 | *p && (purl = fetchParseURL(p))) { | |
781 | if (!*purl->scheme) | 782 | if (!*purl->scheme) | |
782 | strcpy(purl->scheme, SCHEME_HTTP); | 783 | strcpy(purl->scheme, SCHEME_HTTP); | |
783 | if (!purl->port) | 784 | if (!purl->port) | |
784 | purl->port = fetch_default_proxy_port(purl->scheme); | 785 | purl->port = fetch_default_proxy_port(purl->scheme); | |
785 | if (strcasecmp(purl->scheme, SCHEME_HTTP) == 0) | 786 | if (strcasecmp(purl->scheme, SCHEME_HTTP) == 0) | |
786 | return (purl); | 787 | return (purl); | |
787 | fetchFreeURL(purl); | 788 | fetchFreeURL(purl); | |
788 | } | 789 | } | |
789 | return (NULL); | 790 | return (NULL); | |
790 | } | 791 | } | |
791 | 792 | |||
792 | static void | 793 | static void | |
793 | set_if_modified_since(conn_t *conn, time_t last_modified) | 794 | set_if_modified_since(conn_t *conn, time_t last_modified) | |
794 | { | 795 | { | |
795 | static const char weekdays[] = "SunMonTueWedThuFriSat"; | 796 | static const char weekdays[] = "SunMonTueWedThuFriSat"; | |
796 | static const char months[] = "JanFebMarAprMayJunJulAugSepOctNovDec"; | 797 | static const char months[] = "JanFebMarAprMayJunJulAugSepOctNovDec"; | |
797 | struct tm tm; | 798 | struct tm tm; | |
798 | char buf[80]; | 799 | char buf[80]; | |
799 | gmtime_r(&last_modified, &tm); | 800 | gmtime_r(&last_modified, &tm); | |
800 | snprintf(buf, sizeof(buf), "%.3s, %02d %.3s %4d %02d:%02d:%02d GMT", | 801 | snprintf(buf, sizeof(buf), "%.3s, %02d %.3s %4d %02d:%02d:%02d GMT", | |
801 | weekdays + tm.tm_wday * 3, tm.tm_mday, months + tm.tm_mon * 3, | 802 | weekdays + tm.tm_wday * 3, tm.tm_mday, months + tm.tm_mon * 3, | |
802 | tm.tm_year + 1900, tm.tm_hour, tm.tm_min, tm.tm_sec); | 803 | tm.tm_year + 1900, tm.tm_hour, tm.tm_min, tm.tm_sec); | |
803 | http_cmd(conn, "If-Modified-Since: %s\r\n", buf); | 804 | http_cmd(conn, "If-Modified-Since: %s\r\n", buf); | |
804 | } | 805 | } | |
805 | 806 | |||
806 | 807 | |||
807 | /***************************************************************************** | 808 | /***************************************************************************** | |
808 | * Core | 809 | * Core | |
809 | */ | 810 | */ | |
810 | 811 | |||
811 | /* | 812 | /* | |
812 | * Send a request and process the reply | 813 | * Send a request and process the reply | |
813 | * | 814 | * | |
814 | * XXX This function is way too long, the do..while loop should be split | 815 | * XXX This function is way too long, the do..while loop should be split | |
815 | * XXX off into a separate function. | 816 | * XXX off into a separate function. | |
816 | */ | 817 | */ | |
817 | fetchIO * | 818 | fetchIO * | |
818 | http_request(struct url *URL, const char *op, struct url_stat *us, | 819 | http_request(struct url *URL, const char *op, struct url_stat *us, | |
819 | struct url *purl, const char *flags) | 820 | struct url *purl, const char *flags) | |
820 | { | 821 | { | |
821 | conn_t *conn; | 822 | conn_t *conn; | |
822 | struct url *url, *new; | 823 | struct url *url, *new; | |
823 | int chunked, direct, if_modified_since, need_auth, noredirect; | 824 | int chunked, direct, if_modified_since, need_auth, noredirect; | |
824 | int keep_alive, verbose, cached; | 825 | int keep_alive, verbose, cached; | |
825 | int e, i, n, val; | 826 | int e, i, n, val; | |
826 | off_t offset, clength, length, size; | 827 | off_t offset, clength, length, size; | |
827 | time_t mtime; | 828 | time_t mtime; | |
828 | const char *p; | 829 | const char *p; | |
829 | fetchIO *f; | 830 | fetchIO *f; | |
830 | hdr_t h; | 831 | hdr_t h; | |
831 | char hbuf[URL_HOSTLEN + 7], *host; | 832 | char hbuf[URL_HOSTLEN + 7], *host; | |
832 | 833 | |||
833 | direct = CHECK_FLAG('d'); | 834 | direct = CHECK_FLAG('d'); | |
834 | noredirect = CHECK_FLAG('A'); | 835 | noredirect = CHECK_FLAG('A'); | |
835 | verbose = CHECK_FLAG('v'); | 836 | verbose = CHECK_FLAG('v'); | |
836 | if_modified_since = CHECK_FLAG('i'); | 837 | if_modified_since = CHECK_FLAG('i'); | |
837 | keep_alive = 0; | 838 | keep_alive = 0; | |
838 | 839 | |||
839 | if (direct && purl) { | 840 | if (direct && purl) { | |
840 | fetchFreeURL(purl); | 841 | fetchFreeURL(purl); | |
841 | purl = NULL; | 842 | purl = NULL; | |
842 | } | 843 | } | |
843 | 844 | |||
844 | /* try the provided URL first */ | 845 | /* try the provided URL first */ | |
845 | url = URL; | 846 | url = URL; | |
846 | 847 | |||
847 | /* if the A flag is set, we only get one try */ | 848 | /* if the A flag is set, we only get one try */ | |
848 | n = noredirect ? 1 : MAX_REDIRECT; | 849 | n = noredirect ? 1 : MAX_REDIRECT; | |
849 | i = 0; | 850 | i = 0; | |
850 | 851 | |||
851 | e = HTTP_PROTOCOL_ERROR; | 852 | e = HTTP_PROTOCOL_ERROR; | |
852 | need_auth = 0; | 853 | need_auth = 0; | |
853 | do { | 854 | do { | |
854 | new = NULL; | 855 | new = NULL; | |
855 | chunked = 0; | 856 | chunked = 0; | |
856 | offset = 0; | 857 | offset = 0; | |
857 | clength = -1; | 858 | clength = -1; | |
858 | length = -1; | 859 | length = -1; | |
859 | size = -1; | 860 | size = -1; | |
860 | mtime = 0; | 861 | mtime = 0; | |
861 | 862 | |||
862 | /* check port */ | 863 | /* check port */ | |
863 | if (!url->port) | 864 | if (!url->port) | |
864 | url->port = fetch_default_port(url->scheme); | 865 | url->port = fetch_default_port(url->scheme); | |
865 | 866 | |||
866 | /* were we redirected to an FTP URL? */ | 867 | /* were we redirected to an FTP URL? */ | |
867 | if (purl == NULL && strcmp(url->scheme, SCHEME_FTP) == 0) { | 868 | if (purl == NULL && strcmp(url->scheme, SCHEME_FTP) == 0) { | |
868 | if (strcmp(op, "GET") == 0) | 869 | if (strcmp(op, "GET") == 0) | |
869 | return (ftp_request(url, "RETR", NULL, us, purl, flags)); | 870 | return (ftp_request(url, "RETR", NULL, us, purl, flags)); | |
870 | else if (strcmp(op, "HEAD") == 0) | 871 | else if (strcmp(op, "HEAD") == 0) | |
871 | return (ftp_request(url, "STAT", NULL, us, purl, flags)); | 872 | return (ftp_request(url, "STAT", NULL, us, purl, flags)); | |
872 | } | 873 | } | |
873 | 874 | |||
874 | /* connect to server or proxy */ | 875 | /* connect to server or proxy */ | |
875 | if ((conn = http_connect(url, purl, flags, &cached)) == NULL) | 876 | if ((conn = http_connect(url, purl, flags, &cached)) == NULL) | |
876 | goto ouch; | 877 | goto ouch; | |
877 | 878 | |||
878 | host = url->host; | 879 | host = url->host; | |
879 | #ifdef INET6 | 880 | #ifdef INET6 | |
880 | if (strchr(url->host, ':')) { | 881 | if (strchr(url->host, ':')) { | |
881 | snprintf(hbuf, sizeof(hbuf), "[%s]", url->host); | 882 | snprintf(hbuf, sizeof(hbuf), "[%s]", url->host); | |
882 | host = hbuf; | 883 | host = hbuf; | |
883 | } | 884 | } | |
884 | #endif | 885 | #endif | |
885 | if (url->port != fetch_default_port(url->scheme)) { | 886 | if (url->port != fetch_default_port(url->scheme)) { | |
886 | if (host != hbuf) { | 887 | if (host != hbuf) { | |
887 | strcpy(hbuf, host); | 888 | strcpy(hbuf, host); | |
888 | host = hbuf; | 889 | host = hbuf; | |
889 | } | 890 | } | |
890 | snprintf(hbuf + strlen(hbuf), | 891 | snprintf(hbuf + strlen(hbuf), | |
891 | sizeof(hbuf) - strlen(hbuf), ":%d", url->port); | 892 | sizeof(hbuf) - strlen(hbuf), ":%d", url->port); | |
892 | } | 893 | } | |
893 | 894 | |||
894 | /* send request */ | 895 | /* send request */ | |
895 | if (verbose) | 896 | if (verbose) | |
896 | fetch_info("requesting %s://%s%s", | 897 | fetch_info("requesting %s://%s%s", | |
897 | url->scheme, host, url->doc); | 898 | url->scheme, host, url->doc); | |
898 | if (purl) { | 899 | if (purl) { | |
899 | http_cmd(conn, "%s %s://%s%s HTTP/1.1\r\n", | 900 | http_cmd(conn, "%s %s://%s%s HTTP/1.1\r\n", | |
900 | op, url->scheme, host, url->doc); | 901 | op, url->scheme, host, url->doc); | |
901 | } else { | 902 | } else { | |
902 | http_cmd(conn, "%s %s HTTP/1.1\r\n", | 903 | http_cmd(conn, "%s %s HTTP/1.1\r\n", | |
903 | op, url->doc); | 904 | op, url->doc); | |
904 | } | 905 | } | |
905 | 906 | |||
906 | if (if_modified_since && url->last_modified > 0) | 907 | if (if_modified_since && url->last_modified > 0) | |
907 | set_if_modified_since(conn, url->last_modified); | 908 | set_if_modified_since(conn, url->last_modified); | |
908 | 909 | |||
909 | /* virtual host */ | 910 | /* virtual host */ | |
910 | http_cmd(conn, "Host: %s\r\n", host); | 911 | http_cmd(conn, "Host: %s\r\n", host); | |
911 | 912 | |||
912 | /* proxy authorization */ | 913 | /* proxy authorization */ | |
913 | if (purl) { | 914 | if (purl) { | |
914 | if (*purl->user || *purl->pwd) | 915 | if (*purl->user || *purl->pwd) | |
915 | http_basic_auth(conn, "Proxy-Authorization", | 916 | http_basic_auth(conn, "Proxy-Authorization", | |
916 | purl->user, purl->pwd); | 917 | purl->user, purl->pwd); | |
917 | else if ((p = getenv("HTTP_PROXY_AUTH")) != NULL && *p != '\0') | 918 | else if ((p = getenv("HTTP_PROXY_AUTH")) != NULL && *p != '\0') | |
918 | http_authorize(conn, "Proxy-Authorization", p); | 919 | http_authorize(conn, "Proxy-Authorization", p); | |
919 | } | 920 | } | |
920 | 921 | |||
921 | /* server authorization */ | 922 | /* server authorization */ | |
922 | if (need_auth || *url->user || *url->pwd) { | 923 | if (need_auth || *url->user || *url->pwd) { | |
923 | if (*url->user || *url->pwd) | 924 | if (*url->user || *url->pwd) | |
924 | http_basic_auth(conn, "Authorization", url->user, url->pwd); | 925 | http_basic_auth(conn, "Authorization", url->user, url->pwd); | |
925 | else if ((p = getenv("HTTP_AUTH")) != NULL && *p != '\0') | 926 | else if ((p = getenv("HTTP_AUTH")) != NULL && *p != '\0') | |
926 | http_authorize(conn, "Authorization", p); | 927 | http_authorize(conn, "Authorization", p); | |
927 | else if (fetchAuthMethod && fetchAuthMethod(url) == 0) { | 928 | else if (fetchAuthMethod && fetchAuthMethod(url) == 0) { | |
928 | http_basic_auth(conn, "Authorization", url->user, url->pwd); | 929 | http_basic_auth(conn, "Authorization", url->user, url->pwd); | |
929 | } else { | 930 | } else { | |
930 | http_seterr(HTTP_NEED_AUTH); | 931 | http_seterr(HTTP_NEED_AUTH); | |
931 | goto ouch; | 932 | goto ouch; | |
932 | } | 933 | } | |
933 | } | 934 | } | |
934 | 935 | |||
935 | /* other headers */ | 936 | /* other headers */ | |
936 | if ((p = getenv("HTTP_REFERER")) != NULL && *p != '\0') { | 937 | if ((p = getenv("HTTP_REFERER")) != NULL && *p != '\0') { | |
937 | if (strcasecmp(p, "auto") == 0) | 938 | if (strcasecmp(p, "auto") == 0) | |
938 | http_cmd(conn, "Referer: %s://%s%s\r\n", | 939 | http_cmd(conn, "Referer: %s://%s%s\r\n", | |
939 | url->scheme, host, url->doc); | 940 | url->scheme, host, url->doc); | |
940 | else | 941 | else | |
941 | http_cmd(conn, "Referer: %s\r\n", p); | 942 | http_cmd(conn, "Referer: %s\r\n", p); | |
942 | } | 943 | } | |
943 | if ((p = getenv("HTTP_USER_AGENT")) != NULL && *p != '\0') | 944 | if ((p = getenv("HTTP_USER_AGENT")) != NULL && *p != '\0') | |
944 | http_cmd(conn, "User-Agent: %s\r\n", p); | 945 | http_cmd(conn, "User-Agent: %s\r\n", p); | |
945 | else | 946 | else | |
946 | http_cmd(conn, "User-Agent: %s\r\n", _LIBFETCH_VER); | 947 | http_cmd(conn, "User-Agent: %s\r\n", _LIBFETCH_VER); | |
947 | if (url->offset > 0) | 948 | if (url->offset > 0) | |
948 | http_cmd(conn, "Range: bytes=%lld-\r\n", (long long)url->offset); | 949 | http_cmd(conn, "Range: bytes=%lld-\r\n", (long long)url->offset); | |
949 | http_cmd(conn, "\r\n"); | 950 | http_cmd(conn, "\r\n"); | |
950 | 951 | |||
951 | /* | 952 | /* | |
952 | * Force the queued request to be dispatched. Normally, one | 953 | * Force the queued request to be dispatched. Normally, one | |
953 | * would do this with shutdown(2) but squid proxies can be | 954 | * would do this with shutdown(2) but squid proxies can be | |
954 | * configured to disallow such half-closed connections. To | 955 | * configured to disallow such half-closed connections. To | |
955 | * be compatible with such configurations, fiddle with socket | 956 | * be compatible with such configurations, fiddle with socket | |
956 | * options to force the pending data to be written. | 957 | * options to force the pending data to be written. | |
957 | */ | 958 | */ | |
958 | #ifdef TCP_NOPUSH | 959 | #ifdef TCP_NOPUSH | |
959 | val = 0; | 960 | val = 0; | |
960 | setsockopt(conn->sd, IPPROTO_TCP, TCP_NOPUSH, &val, | 961 | setsockopt(conn->sd, IPPROTO_TCP, TCP_NOPUSH, &val, | |
961 | sizeof(val)); | 962 | sizeof(val)); | |
962 | #endif | 963 | #endif | |
963 | val = 1; | 964 | val = 1; | |
964 | setsockopt(conn->sd, IPPROTO_TCP, TCP_NODELAY, &val, | 965 | setsockopt(conn->sd, IPPROTO_TCP, TCP_NODELAY, &val, | |
965 | (socklen_t)sizeof(val)); | 966 | (socklen_t)sizeof(val)); | |
966 | 967 | |||
967 | /* get reply */ | 968 | /* get reply */ | |
968 | switch (http_get_reply(conn)) { | 969 | switch (http_get_reply(conn)) { | |
969 | case HTTP_OK: | 970 | case HTTP_OK: | |
970 | case HTTP_PARTIAL: | 971 | case HTTP_PARTIAL: | |
971 | case HTTP_NOT_MODIFIED: | 972 | case HTTP_NOT_MODIFIED: | |
972 | /* fine */ | 973 | /* fine */ | |
973 | break; | 974 | break; | |
974 | case HTTP_MOVED_PERM: | 975 | case HTTP_MOVED_PERM: | |
975 | case HTTP_MOVED_TEMP: | 976 | case HTTP_MOVED_TEMP: | |
976 | case HTTP_SEE_OTHER: | 977 | case HTTP_SEE_OTHER: | |
977 | /* | 978 | /* | |
978 | * Not so fine, but we still have to read the | 979 | * Not so fine, but we still have to read the | |
979 | * headers to get the new location. | 980 | * headers to get the new location. | |
980 | */ | 981 | */ | |
981 | break; | 982 | break; | |
982 | case HTTP_NEED_AUTH: | 983 | case HTTP_NEED_AUTH: | |
983 | if (need_auth) { | 984 | if (need_auth) { | |
984 | /* | 985 | /* | |
985 | * We already sent out authorization code, | 986 | * We already sent out authorization code, | |
986 | * so there's nothing more we can do. | 987 | * so there's nothing more we can do. | |
987 | */ | 988 | */ | |
988 | http_seterr(conn->err); | 989 | http_seterr(conn->err); | |
989 | goto ouch; | 990 | goto ouch; | |
990 | } | 991 | } | |
991 | /* try again, but send the password this time */ | 992 | /* try again, but send the password this time */ | |
992 | if (verbose) | 993 | if (verbose) | |
993 | fetch_info("server requires authorization"); | 994 | fetch_info("server requires authorization"); | |
994 | break; | 995 | break; | |
995 | case HTTP_NEED_PROXY_AUTH: | 996 | case HTTP_NEED_PROXY_AUTH: | |
996 | /* | 997 | /* | |
997 | * If we're talking to a proxy, we already sent | 998 | * If we're talking to a proxy, we already sent | |
998 | * our proxy authorization code, so there's | 999 | * our proxy authorization code, so there's | |
999 | * nothing more we can do. | 1000 | * nothing more we can do. | |
1000 | */ | 1001 | */ | |
1001 | http_seterr(conn->err); | 1002 | http_seterr(conn->err); | |
1002 | goto ouch; | 1003 | goto ouch; | |
1003 | case HTTP_BAD_RANGE: | 1004 | case HTTP_BAD_RANGE: | |
1004 | /* | 1005 | /* | |
1005 | * This can happen if we ask for 0 bytes because | 1006 | * This can happen if we ask for 0 bytes because | |
1006 | * we already have the whole file. Consider this | 1007 | * we already have the whole file. Consider this | |
1007 | * a success for now, and check sizes later. | 1008 | * a success for now, and check sizes later. | |
1008 | */ | 1009 | */ | |
1009 | break; | 1010 | break; | |
1010 | case HTTP_PROTOCOL_ERROR: | 1011 | case HTTP_PROTOCOL_ERROR: | |
1011 | /* fall through */ | 1012 | /* fall through */ | |
1012 | case -1: | 1013 | case -1: | |
1013 | --i; | 1014 | --i; | |
1014 | if (cached) | 1015 | if (cached) | |
1015 | continue; | 1016 | continue; | |
1016 | fetch_syserr(); | 1017 | fetch_syserr(); | |
1017 | goto ouch; | 1018 | goto ouch; | |
1018 | default: | 1019 | default: | |
1019 | http_seterr(conn->err); | 1020 | http_seterr(conn->err); | |
1020 | if (!verbose) | 1021 | if (!verbose) | |
1021 | goto ouch; | 1022 | goto ouch; | |
1022 | /* fall through so we can get the full error message */ | 1023 | /* fall through so we can get the full error message */ | |
1023 | } | 1024 | } | |
1024 | 1025 | |||
1025 | /* get headers */ | 1026 | /* get headers */ | |
1026 | do { | 1027 | do { | |
1027 | switch ((h = http_next_header(conn, &p))) { | 1028 | switch ((h = http_next_header(conn, &p))) { | |
1028 | case hdr_syserror: | 1029 | case hdr_syserror: | |
1029 | fetch_syserr(); | 1030 | fetch_syserr(); | |
1030 | goto ouch; | 1031 | goto ouch; | |
1031 | case hdr_error: | 1032 | case hdr_error: | |
1032 | http_seterr(HTTP_PROTOCOL_ERROR); | 1033 | http_seterr(HTTP_PROTOCOL_ERROR); | |
1033 | goto ouch; | 1034 | goto ouch; | |
1034 | case hdr_connection: | 1035 | case hdr_connection: | |
1035 | /* XXX too weak? */ | 1036 | /* XXX too weak? */ | |
1036 | keep_alive = (strcasecmp(p, "keep-alive") == 0); | 1037 | keep_alive = (strcasecmp(p, "keep-alive") == 0); | |
1037 | break; | 1038 | break; | |
1038 | case hdr_content_length: | 1039 | case hdr_content_length: | |
1039 | http_parse_length(p, &clength); | 1040 | http_parse_length(p, &clength); | |
1040 | break; | 1041 | break; | |
1041 | case hdr_content_range: | 1042 | case hdr_content_range: | |
1042 | http_parse_range(p, &offset, &length, &size); | 1043 | http_parse_range(p, &offset, &length, &size); | |
1043 | break; | 1044 | break; | |
1044 | case hdr_last_modified: | 1045 | case hdr_last_modified: | |
1045 | http_parse_mtime(p, &mtime); | 1046 | http_parse_mtime(p, &mtime); | |
1046 | break; | 1047 | break; | |
1047 | case hdr_location: | 1048 | case hdr_location: | |
1048 | if (!HTTP_REDIRECT(conn->err)) | 1049 | if (!HTTP_REDIRECT(conn->err)) | |
1049 | break; | 1050 | break; | |
1050 | if (new) | 1051 | if (new) | |
1051 | free(new); | 1052 | free(new); | |
1052 | if (verbose) | 1053 | if (verbose) | |
1053 | fetch_info("%d redirect to %s", conn->err, p); | 1054 | fetch_info("%d redirect to %s", conn->err, p); | |
1054 | if (*p == '/') | 1055 | if (*p == '/') | |
1055 | /* absolute path */ | 1056 | /* absolute path */ | |
1056 | new = fetchMakeURL(url->scheme, url->host, url->port, p, | 1057 | new = fetchMakeURL(url->scheme, url->host, url->port, p, | |
1057 | url->user, url->pwd); | 1058 | url->user, url->pwd); | |
1058 | else | 1059 | else | |
1059 | new = fetchParseURL(p); | 1060 | new = fetchParseURL(p); | |
1060 | if (new == NULL) { | 1061 | if (new == NULL) { | |
1061 | /* XXX should set an error code */ | 1062 | /* XXX should set an error code */ | |
1062 | goto ouch; | 1063 | goto ouch; | |
1063 | } | 1064 | } | |
1064 | if (!*new->user && !*new->pwd) { | 1065 | if (!*new->user && !*new->pwd) { | |
1065 | strcpy(new->user, url->user); | 1066 | strcpy(new->user, url->user); | |
1066 | strcpy(new->pwd, url->pwd); | 1067 | strcpy(new->pwd, url->pwd); | |
1067 | } | 1068 | } | |
1068 | new->offset = url->offset; | 1069 | new->offset = url->offset; | |
1069 | new->length = url->length; | 1070 | new->length = url->length; | |
1070 | break; | 1071 | break; | |
1071 | case hdr_transfer_encoding: | 1072 | case hdr_transfer_encoding: | |
1072 | /* XXX weak test*/ | 1073 | /* XXX weak test*/ | |
1073 | chunked = (strcasecmp(p, "chunked") == 0); | 1074 | chunked = (strcasecmp(p, "chunked") == 0); | |
1074 | break; | 1075 | break; | |
1075 | case hdr_www_authenticate: | 1076 | case hdr_www_authenticate: | |
1076 | if (conn->err != HTTP_NEED_AUTH) | 1077 | if (conn->err != HTTP_NEED_AUTH) | |
1077 | break; | 1078 | break; | |
1078 | /* if we were smarter, we'd check the method and realm */ | 1079 | /* if we were smarter, we'd check the method and realm */ | |
1079 | break; | 1080 | break; | |
1080 | case hdr_end: | 1081 | case hdr_end: | |
1081 | /* fall through */ | 1082 | /* fall through */ | |
1082 | case hdr_unknown: | 1083 | case hdr_unknown: | |
1083 | /* ignore */ | 1084 | /* ignore */ | |
1084 | break; | 1085 | break; | |
1085 | } | 1086 | } | |
1086 | } while (h > hdr_end); | 1087 | } while (h > hdr_end); | |
1087 | 1088 | |||
1088 | /* we need to provide authentication */ | 1089 | /* we need to provide authentication */ | |
1089 | if (conn->err == HTTP_NEED_AUTH) { | 1090 | if (conn->err == HTTP_NEED_AUTH) { | |
1090 | e = conn->err; | 1091 | e = conn->err; | |
1091 | need_auth = 1; | 1092 | need_auth = 1; | |
1092 | fetch_close(conn); | 1093 | fetch_close(conn); | |
1093 | conn = NULL; | 1094 | conn = NULL; | |
1094 | continue; | 1095 | continue; | |
1095 | } | 1096 | } | |
1096 | 1097 | |||
1097 | /* requested range not satisfiable */ | 1098 | /* requested range not satisfiable */ | |
1098 | if (conn->err == HTTP_BAD_RANGE) { | 1099 | if (conn->err == HTTP_BAD_RANGE) { | |
1099 | if (url->offset == size && url->length == 0) { | 1100 | if (url->offset == size && url->length == 0) { | |
1100 | /* asked for 0 bytes; fake it */ | 1101 | /* asked for 0 bytes; fake it */ | |
1101 | offset = url->offset; | 1102 | offset = url->offset; | |
1102 | conn->err = HTTP_OK; | 1103 | conn->err = HTTP_OK; | |
1103 | break; | 1104 | break; | |
1104 | } else { | 1105 | } else { | |
1105 | http_seterr(conn->err); | 1106 | http_seterr(conn->err); | |
1106 | goto ouch; | 1107 | goto ouch; | |
1107 | } | 1108 | } | |
1108 | } | 1109 | } | |
1109 | 1110 | |||
1110 | /* we have a hit or an error */ | 1111 | /* we have a hit or an error */ | |
1111 | if (conn->err == HTTP_OK || | 1112 | if (conn->err == HTTP_OK || | |
1112 | conn->err == HTTP_PARTIAL || | 1113 | conn->err == HTTP_PARTIAL || | |
1113 | conn->err == HTTP_NOT_MODIFIED || | 1114 | conn->err == HTTP_NOT_MODIFIED || | |
1114 | HTTP_ERROR(conn->err)) | 1115 | HTTP_ERROR(conn->err)) | |
1115 | break; | 1116 | break; | |
1116 | 1117 | |||
1117 | /* all other cases: we got a redirect */ | 1118 | /* all other cases: we got a redirect */ | |
1118 | e = conn->err; | 1119 | e = conn->err; | |
1119 | need_auth = 0; | 1120 | need_auth = 0; | |
1120 | fetch_close(conn); | 1121 | fetch_close(conn); | |
1121 | conn = NULL; | 1122 | conn = NULL; | |
1122 | if (!new) | 1123 | if (!new) | |
1123 | break; | 1124 | break; | |
1124 | if (url != URL) | 1125 | if (url != URL) | |
1125 | fetchFreeURL(url); | 1126 | fetchFreeURL(url); | |
1126 | url = new; | 1127 | url = new; | |
1127 | } while (++i < n); | 1128 | } while (++i < n); | |
1128 | 1129 | |||
1129 | /* we failed, or ran out of retries */ | 1130 | /* we failed, or ran out of retries */ | |
1130 | if (conn == NULL) { | 1131 | if (conn == NULL) { | |
1131 | http_seterr(e); | 1132 | http_seterr(e); | |
1132 | goto ouch; | 1133 | goto ouch; | |
1133 | } | 1134 | } | |
1134 | 1135 | |||
1135 | /* check for inconsistencies */ | 1136 | /* check for inconsistencies */ | |
1136 | if (clength != -1 && length != -1 && clength != length) { | 1137 | if (clength != -1 && length != -1 && clength != length) { | |
1137 | http_seterr(HTTP_PROTOCOL_ERROR); | 1138 | http_seterr(HTTP_PROTOCOL_ERROR); | |
1138 | goto ouch; | 1139 | goto ouch; | |
1139 | } | 1140 | } | |
1140 | if (clength == -1) | 1141 | if (clength == -1) | |
1141 | clength = length; | 1142 | clength = length; | |
1142 | if (clength != -1) | 1143 | if (clength != -1) | |
1143 | length = offset + clength; | 1144 | length = offset + clength; | |
1144 | if (length != -1 && size != -1 && length != size) { | 1145 | if (length != -1 && size != -1 && length != size) { | |
1145 | http_seterr(HTTP_PROTOCOL_ERROR); | 1146 | http_seterr(HTTP_PROTOCOL_ERROR); | |
1146 | goto ouch; | 1147 | goto ouch; | |
1147 | } | 1148 | } | |
1148 | if (size == -1) | 1149 | if (size == -1) | |
1149 | size = length; | 1150 | size = length; | |
1150 | 1151 | |||
1151 | /* fill in stats */ | 1152 | /* fill in stats */ | |
1152 | if (us) { | 1153 | if (us) { | |
1153 | us->size = size; | 1154 | us->size = size; | |
1154 | us->atime = us->mtime = mtime; | 1155 | us->atime = us->mtime = mtime; | |
1155 | } | 1156 | } | |
1156 | 1157 | |||
1157 | /* too far? */ | 1158 | /* too far? */ | |
1158 | if (URL->offset > 0 && offset > URL->offset) { | 1159 | if (URL->offset > 0 && offset > URL->offset) { | |
1159 | http_seterr(HTTP_PROTOCOL_ERROR); | 1160 | http_seterr(HTTP_PROTOCOL_ERROR); | |
1160 | goto ouch; | 1161 | goto ouch; | |
1161 | } | 1162 | } | |
1162 | 1163 | |||
1163 | /* report back real offset and size */ | 1164 | /* report back real offset and size */ | |
1164 | URL->offset = offset; | 1165 | URL->offset = offset; | |
1165 | URL->length = clength; | 1166 | URL->length = clength; | |
1166 | 1167 | |||
1167 | if (clength == -1 && !chunked) | 1168 | if (clength == -1 && !chunked) | |
1168 | keep_alive = 0; | 1169 | keep_alive = 0; | |
1169 | 1170 | |||
1170 | if (conn->err == HTTP_NOT_MODIFIED) { | 1171 | if (conn->err == HTTP_NOT_MODIFIED) { | |
1171 | http_seterr(HTTP_NOT_MODIFIED); | 1172 | http_seterr(HTTP_NOT_MODIFIED); | |
1172 | if (keep_alive) { | 1173 | if (keep_alive) { | |
1173 | fetch_cache_put(conn, fetch_close); | 1174 | fetch_cache_put(conn, fetch_close); | |
1174 | conn = NULL; | 1175 | conn = NULL; | |
1175 | } | 1176 | } | |
1176 | goto ouch; | 1177 | goto ouch; | |
1177 | } | 1178 | } | |
1178 | 1179 | |||
1179 | /* wrap it up in a fetchIO */ | 1180 | /* wrap it up in a fetchIO */ | |
1180 | if ((f = http_funopen(conn, chunked, keep_alive, clength)) == NULL) { | 1181 | if ((f = http_funopen(conn, chunked, keep_alive, clength)) == NULL) { | |
1181 | fetch_syserr(); | 1182 | fetch_syserr(); | |
1182 | goto ouch; | 1183 | goto ouch; | |
1183 | } | 1184 | } | |
1184 | 1185 | |||
1185 | if (url != URL) | 1186 | if (url != URL) | |
1186 | fetchFreeURL(url); | 1187 | fetchFreeURL(url); | |
1187 | if (purl) | 1188 | if (purl) | |
1188 | fetchFreeURL(purl); | 1189 | fetchFreeURL(purl); | |
1189 | 1190 | |||
1190 | if (HTTP_ERROR(conn->err)) { | 1191 | if (HTTP_ERROR(conn->err)) { | |
1191 | 1192 | |||
1192 | if (keep_alive) { | 1193 | if (keep_alive) { | |
1193 | char buf[512]; | 1194 | char buf[512]; | |
1194 | do { | 1195 | do { | |
1195 | } while (fetchIO_read(f, buf, sizeof(buf)) > 0); | 1196 | } while (fetchIO_read(f, buf, sizeof(buf)) > 0); | |
1196 | } | 1197 | } | |
1197 | 1198 | |||
1198 | fetchIO_close(f); | 1199 | fetchIO_close(f); | |
1199 | f = NULL; | 1200 | f = NULL; | |
1200 | } | 1201 | } | |
1201 | 1202 | |||
1202 | return (f); | 1203 | return (f); | |
1203 | 1204 | |||
1204 | ouch: | 1205 | ouch: | |
1205 | if (url != URL) | 1206 | if (url != URL) | |
1206 | fetchFreeURL(url); | 1207 | fetchFreeURL(url); | |
1207 | if (purl) | 1208 | if (purl) | |
1208 | fetchFreeURL(purl); | 1209 | fetchFreeURL(purl); | |
1209 | if (conn != NULL) | 1210 | if (conn != NULL) | |
1210 | fetch_close(conn); | 1211 | fetch_close(conn); | |
1211 | return (NULL); | 1212 | return (NULL); | |
1212 | } | 1213 | } | |
1213 | 1214 | |||
1214 | 1215 | |||
1215 | /***************************************************************************** | 1216 | /***************************************************************************** | |
1216 | * Entry points | 1217 | * Entry points | |
1217 | */ | 1218 | */ | |
1218 | 1219 | |||
1219 | /* | 1220 | /* | |
1220 | * Retrieve and stat a file by HTTP | 1221 | * Retrieve and stat a file by HTTP | |
1221 | */ | 1222 | */ | |
1222 | fetchIO * | 1223 | fetchIO * | |
1223 | fetchXGetHTTP(struct url *URL, struct url_stat *us, const char *flags) | 1224 | fetchXGetHTTP(struct url *URL, struct url_stat *us, const char *flags) | |
1224 | { | 1225 | { | |
1225 | return (http_request(URL, "GET", us, http_get_proxy(URL, flags), flags)); | 1226 | return (http_request(URL, "GET", us, http_get_proxy(URL, flags), flags)); | |
1226 | } | 1227 | } | |
1227 | 1228 | |||
1228 | /* | 1229 | /* | |
1229 | * Retrieve a file by HTTP | 1230 | * Retrieve a file by HTTP | |
1230 | */ | 1231 | */ | |
1231 | fetchIO * | 1232 | fetchIO * | |
1232 | fetchGetHTTP(struct url *URL, const char *flags) | 1233 | fetchGetHTTP(struct url *URL, const char *flags) | |
1233 | { | 1234 | { | |
1234 | return (fetchXGetHTTP(URL, NULL, flags)); | 1235 | return (fetchXGetHTTP(URL, NULL, flags)); | |
1235 | } | 1236 | } | |
1236 | 1237 | |||
1237 | /* | 1238 | /* | |
1238 | * Store a file by HTTP | 1239 | * Store a file by HTTP | |
1239 | */ | 1240 | */ | |
1240 | fetchIO * | 1241 | fetchIO * | |
1241 | /*ARGSUSED*/ | 1242 | /*ARGSUSED*/ | |
1242 | fetchPutHTTP(struct url *URL __unused, const char *flags __unused) | 1243 | fetchPutHTTP(struct url *URL __unused, const char *flags __unused) | |
1243 | { | 1244 | { | |
1244 | fprintf(stderr, "fetchPutHTTP(): not implemented\n"); | 1245 | fprintf(stderr, "fetchPutHTTP(): not implemented\n"); | |
1245 | return (NULL); | 1246 | return (NULL); | |
1246 | } | 1247 | } | |
1247 | 1248 | |||
1248 | /* | 1249 | /* | |
1249 | * Get an HTTP document's metadata | 1250 | * Get an HTTP document's metadata | |
1250 | */ | 1251 | */ | |
1251 | int | 1252 | int | |
1252 | fetchStatHTTP(struct url *URL, struct url_stat *us, const char *flags) | 1253 | fetchStatHTTP(struct url *URL, struct url_stat *us, const char *flags) | |
1253 | { | 1254 | { | |
1254 | fetchIO *f; | 1255 | fetchIO *f; | |
1255 | 1256 | |||
1256 | f = http_request(URL, "HEAD", us, http_get_proxy(URL, flags), flags); | 1257 | f = http_request(URL, "HEAD", us, http_get_proxy(URL, flags), flags); | |
1257 | if (f == NULL) | 1258 | if (f == NULL) | |
1258 | return (-1); | 1259 | return (-1); | |
1259 | fetchIO_close(f); | 1260 | fetchIO_close(f); | |
1260 | return (0); | 1261 | return (0); | |
1261 | } | 1262 | } | |
1262 | 1263 | |||
1263 | enum http_states { | 1264 | enum http_states { | |
1264 | ST_NONE, | 1265 | ST_NONE, | |
1265 | ST_LT, | 1266 | ST_LT, | |
1266 | ST_LTA, | 1267 | ST_LTA, | |
1267 | ST_TAGA, | 1268 | ST_TAGA, | |
1268 | ST_H, | 1269 | ST_H, | |
1269 | ST_R, | 1270 | ST_R, | |
1270 | ST_E, | 1271 | ST_E, | |
1271 | ST_F, | 1272 | ST_F, | |
1272 | ST_HREF, | 1273 | ST_HREF, | |
1273 | ST_HREFQ, | 1274 | ST_HREFQ, | |
1274 | ST_TAG, | 1275 | ST_TAG, | |
1275 | ST_TAGAX, | 1276 | ST_TAGAX, | |
1276 | ST_TAGAQ | 1277 | ST_TAGAQ | |
1277 | }; | 1278 | }; | |
1278 | 1279 | |||
1279 | struct index_parser { | 1280 | struct index_parser { | |
1280 | struct url_list *ue; | 1281 | struct url_list *ue; | |
1281 | struct url *url; | 1282 | struct url *url; | |
1282 | enum http_states state; | 1283 | enum http_states state; | |
1283 | }; | 1284 | }; | |
1284 | 1285 | |||
1285 | static ssize_t | 1286 | static ssize_t | |
1286 | parse_index(struct index_parser *parser, const char *buf, size_t len) | 1287 | parse_index(struct index_parser *parser, const char *buf, size_t len) | |
1287 | { | 1288 | { | |
1288 | char *end_attr, p = *buf; | 1289 | char *end_attr, p = *buf; | |
1289 | 1290 | |||
1290 | switch (parser->state) { | 1291 | switch (parser->state) { | |
1291 | case ST_NONE: | 1292 | case ST_NONE: | |
1292 | /* Plain text, not in markup */ | 1293 | /* Plain text, not in markup */ | |
1293 | if (p == '<') | 1294 | if (p == '<') | |
1294 | parser->state = ST_LT; | 1295 | parser->state = ST_LT; | |
1295 | return 1; | 1296 | return 1; | |
1296 | case ST_LT: | 1297 | case ST_LT: | |
1297 | /* In tag -- "<" already found */ | 1298 | /* In tag -- "<" already found */ | |
1298 | if (p == '>') | 1299 | if (p == '>') | |
1299 | parser->state = ST_NONE; | 1300 | parser->state = ST_NONE; | |
1300 | else if (p == 'a' || p == 'A') | 1301 | else if (p == 'a' || p == 'A') | |
1301 | parser->state = ST_LTA; | 1302 | parser->state = ST_LTA; | |
1302 | else if (!isspace((unsigned char)p)) | 1303 | else if (!isspace((unsigned char)p)) | |
1303 | parser->state = ST_TAG; | 1304 | parser->state = ST_TAG; | |
1304 | return 1; | 1305 | return 1; | |
1305 | case ST_LTA: | 1306 | case ST_LTA: | |
1306 | /* In tag -- "<a" already found */ | 1307 | /* In tag -- "<a" already found */ | |
1307 | if (p == '>') | 1308 | if (p == '>') | |
1308 | parser->state = ST_NONE; | 1309 | parser->state = ST_NONE; | |
1309 | else if (p == '"') | 1310 | else if (p == '"') | |
1310 | parser->state = ST_TAGAQ; | 1311 | parser->state = ST_TAGAQ; | |
1311 | else if (isspace((unsigned char)p)) | 1312 | else if (isspace((unsigned char)p)) | |
1312 | parser->state = ST_TAGA; | 1313 | parser->state = ST_TAGA; | |
1313 | else | 1314 | else | |
1314 | parser->state = ST_TAG; | 1315 | parser->state = ST_TAG; | |
1315 | return 1; | 1316 | return 1; | |
1316 | case ST_TAG: | 1317 | case ST_TAG: | |
1317 | /* In tag, but not "<a" -- disregard */ | 1318 | /* In tag, but not "<a" -- disregard */ | |
1318 | if (p == '>') | 1319 | if (p == '>') | |
1319 | parser->state = ST_NONE; | 1320 | parser->state = ST_NONE; | |
1320 | return 1; | 1321 | return 1; | |
1321 | case ST_TAGA: | 1322 | case ST_TAGA: | |
1322 | /* In a-tag -- "<a " already found */ | 1323 | /* In a-tag -- "<a " already found */ | |
1323 | if (p == '>') | 1324 | if (p == '>') | |
1324 | parser->state = ST_NONE; | 1325 | parser->state = ST_NONE; | |
1325 | else if (p == '"') | 1326 | else if (p == '"') | |
1326 | parser->state = ST_TAGAQ; | 1327 | parser->state = ST_TAGAQ; | |
1327 | else if (p == 'h' || p == 'H') | 1328 | else if (p == 'h' || p == 'H') | |
1328 | parser->state = ST_H; | 1329 | parser->state = ST_H; | |
1329 | else if (!isspace((unsigned char)p)) | 1330 | else if (!isspace((unsigned char)p)) | |
1330 | parser->state = ST_TAGAX; | 1331 | parser->state = ST_TAGAX; | |
1331 | return 1; | 1332 | return 1; | |
1332 | case ST_TAGAX: | 1333 | case ST_TAGAX: | |
1333 | /* In unknown keyword in a-tag */ | 1334 | /* In unknown keyword in a-tag */ | |
1334 | if (p == '>') | 1335 | if (p == '>') | |
1335 | parser->state = ST_NONE; | 1336 | parser->state = ST_NONE; | |
1336 | else if (p == '"') | 1337 | else if (p == '"') | |
1337 | parser->state = ST_TAGAQ; | 1338 | parser->state = ST_TAGAQ; | |
1338 | else if (isspace((unsigned char)p)) | 1339 | else if (isspace((unsigned char)p)) | |
1339 | parser->state = ST_TAGA; | 1340 | parser->state = ST_TAGA; | |
1340 | return 1; | 1341 | return 1; | |
1341 | case ST_TAGAQ: | 1342 | case ST_TAGAQ: | |
1342 | /* In a-tag, unknown argument for keys. */ | 1343 | /* In a-tag, unknown argument for keys. */ | |
1343 | if (p == '>') | 1344 | if (p == '>') | |
1344 | parser->state = ST_NONE; | 1345 | parser->state = ST_NONE; | |
1345 | else if (p == '"') | 1346 | else if (p == '"') | |
1346 | parser->state = ST_TAGA; | 1347 | parser->state = ST_TAGA; | |
1347 | return 1; | 1348 | return 1; | |
1348 | case ST_H: | 1349 | case ST_H: | |
1349 | /* In a-tag -- "<a h" already found */ | 1350 | /* In a-tag -- "<a h" already found */ | |
1350 | if (p == '>') | 1351 | if (p == '>') | |
1351 | parser->state = ST_NONE; | 1352 | parser->state = ST_NONE; | |
1352 | else if (p == '"') | 1353 | else if (p == '"') | |
1353 | parser->state = ST_TAGAQ; | 1354 | parser->state = ST_TAGAQ; | |
1354 | else if (p == 'r' || p == 'R') | 1355 | else if (p == 'r' || p == 'R') | |
1355 | parser->state = ST_R; | 1356 | parser->state = ST_R; | |
1356 | else if (isspace((unsigned char)p)) | 1357 | else if (isspace((unsigned char)p)) | |
1357 | parser->state = ST_TAGA; | 1358 | parser->state = ST_TAGA; | |
1358 | else | 1359 | else | |
1359 | parser->state = ST_TAGAX; | 1360 | parser->state = ST_TAGAX; | |
1360 | return 1; | 1361 | return 1; | |
1361 | case ST_R: | 1362 | case ST_R: | |
1362 | /* In a-tag -- "<a hr" already found */ | 1363 | /* In a-tag -- "<a hr" already found */ | |
1363 | if (p == '>') | 1364 | if (p == '>') | |
1364 | parser->state = ST_NONE; | 1365 | parser->state = ST_NONE; | |
1365 | else if (p == '"') | 1366 | else if (p == '"') | |
1366 | parser->state = ST_TAGAQ; | 1367 | parser->state = ST_TAGAQ; | |
1367 | else if (p == 'e' || p == 'E') | 1368 | else if (p == 'e' || p == 'E') | |
1368 | parser->state = ST_E; | 1369 | parser->state = ST_E; | |
1369 | else if (isspace((unsigned char)p)) | 1370 | else if (isspace((unsigned char)p)) | |
1370 | parser->state = ST_TAGA; | 1371 | parser->state = ST_TAGA; | |
1371 | else | 1372 | else | |
1372 | parser->state = ST_TAGAX; | 1373 | parser->state = ST_TAGAX; | |
1373 | return 1; | 1374 | return 1; | |
1374 | case ST_E: | 1375 | case ST_E: | |
1375 | /* In a-tag -- "<a hre" already found */ | 1376 | /* In a-tag -- "<a hre" already found */ | |
1376 | if (p == '>') | 1377 | if (p == '>') | |
1377 | parser->state = ST_NONE; | 1378 | parser->state = ST_NONE; | |
1378 | else if (p == '"') | 1379 | else if (p == '"') | |
1379 | parser->state = ST_TAGAQ; | 1380 | parser->state = ST_TAGAQ; | |
1380 | else if (p == 'f' || p == 'F') | 1381 | else if (p == 'f' || p == 'F') | |
1381 | parser->state = ST_F; | 1382 | parser->state = ST_F; | |
1382 | else if (isspace((unsigned char)p)) | 1383 | else if (isspace((unsigned char)p)) | |
1383 | parser->state = ST_TAGA; | 1384 | parser->state = ST_TAGA; | |
1384 | else | 1385 | else | |
1385 | parser->state = ST_TAGAX; | 1386 | parser->state = ST_TAGAX; | |
1386 | return 1; | 1387 | return 1; | |
1387 | case ST_F: | 1388 | case ST_F: | |
1388 | /* In a-tag -- "<a href" already found */ | 1389 | /* In a-tag -- "<a href" already found */ | |
1389 | if (p == '>') | 1390 | if (p == '>') | |
1390 | parser->state = ST_NONE; | 1391 | parser->state = ST_NONE; | |
1391 | else if (p == '"') | 1392 | else if (p == '"') | |
1392 | parser->state = ST_TAGAQ; | 1393 | parser->state = ST_TAGAQ; | |
1393 | else if (p == '=') | 1394 | else if (p == '=') | |
1394 | parser->state = ST_HREF; | 1395 | parser->state = ST_HREF; | |
1395 | else if (!isspace((unsigned char)p)) | 1396 | else if (!isspace((unsigned char)p)) | |
1396 | parser->state = ST_TAGAX; | 1397 | parser->state = ST_TAGAX; | |
1397 | return 1; | 1398 | return 1; | |
1398 | case ST_HREF: | 1399 | case ST_HREF: | |
1399 | /* In a-tag -- "<a href=" already found */ | 1400 | /* In a-tag -- "<a href=" already found */ | |
1400 | if (p == '>') | 1401 | if (p == '>') | |
1401 | parser->state = ST_NONE; | 1402 | parser->state = ST_NONE; | |
1402 | else if (p == '"') | 1403 | else if (p == '"') | |
1403 | parser->state = ST_HREFQ; | 1404 | parser->state = ST_HREFQ; | |
1404 | else if (!isspace((unsigned char)p)) | 1405 | else if (!isspace((unsigned char)p)) | |
1405 | parser->state = ST_TAGA; | 1406 | parser->state = ST_TAGA; |