1 /* $NetBSD: fetch.c,v 1.19 2009/08/11 20:48:06 joerg Exp $ */
3 * Copyright (c) 1998-2004 Dag-Erling Coïdan Smørgrav
4 * Copyright (c) 2008 Joerg Sonnenberger <joerg@NetBSD.org>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer
12 * in this position and unchanged.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
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
17 * derived from this software without specific prior written permission
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
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
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
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
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 * $FreeBSD: fetch.c,v 1.41 2007/12/19 00:26:36 des Exp $
36 #if !defined(NETBSD) && !defined(__minix)
48 auth_t fetchAuthMethod
;
50 char fetchLastErrString
[MAXERRSTRING
];
52 volatile int fetchRestartCalls
= 1;
56 /*** Local data **************************************************************/
59 * Error messages for parser errors
61 #define URL_MALFORMED 1
62 #define URL_BAD_SCHEME 2
63 #define URL_BAD_PORT 3
64 static struct fetcherr url_errlist
[] = {
65 { URL_MALFORMED
, FETCH_URL
, "Malformed URL" },
66 { URL_BAD_SCHEME
, FETCH_URL
, "Invalid URL scheme" },
67 { URL_BAD_PORT
, FETCH_URL
, "Invalid server port" },
68 { -1, FETCH_UNKNOWN
, "Unknown parser error" }
72 /*** Public API **************************************************************/
75 * Select the appropriate protocol for the URL scheme, and return a
76 * read-only stream connected to the document referenced by the URL.
77 * Also fill out the struct url_stat.
80 fetchXGet(struct url
*URL
, struct url_stat
*us
, const char *flags
)
85 us
->atime
= us
->mtime
= 0;
87 if (strcasecmp(URL
->scheme
, SCHEME_FILE
) == 0)
88 return (fetchXGetFile(URL
, us
, flags
));
89 else if (strcasecmp(URL
->scheme
, SCHEME_FTP
) == 0)
90 return (fetchXGetFTP(URL
, us
, flags
));
91 else if (strcasecmp(URL
->scheme
, SCHEME_HTTP
) == 0)
92 return (fetchXGetHTTP(URL
, us
, flags
));
93 else if (strcasecmp(URL
->scheme
, SCHEME_HTTPS
) == 0)
94 return (fetchXGetHTTP(URL
, us
, flags
));
95 url_seterr(URL_BAD_SCHEME
);
100 * Select the appropriate protocol for the URL scheme, and return a
101 * read-only stream connected to the document referenced by the URL.
104 fetchGet(struct url
*URL
, const char *flags
)
106 return (fetchXGet(URL
, NULL
, flags
));
110 * Select the appropriate protocol for the URL scheme, and return a
111 * write-only stream connected to the document referenced by the URL.
114 fetchPut(struct url
*URL
, const char *flags
)
117 if (strcasecmp(URL
->scheme
, SCHEME_FILE
) == 0)
118 return (fetchPutFile(URL
, flags
));
119 else if (strcasecmp(URL
->scheme
, SCHEME_FTP
) == 0)
120 return (fetchPutFTP(URL
, flags
));
121 else if (strcasecmp(URL
->scheme
, SCHEME_HTTP
) == 0)
122 return (fetchPutHTTP(URL
, flags
));
123 else if (strcasecmp(URL
->scheme
, SCHEME_HTTPS
) == 0)
124 return (fetchPutHTTP(URL
, flags
));
125 url_seterr(URL_BAD_SCHEME
);
130 * Select the appropriate protocol for the URL scheme, and return the
131 * size of the document referenced by the URL if it exists.
134 fetchStat(struct url
*URL
, struct url_stat
*us
, const char *flags
)
139 us
->atime
= us
->mtime
= 0;
141 if (strcasecmp(URL
->scheme
, SCHEME_FILE
) == 0)
142 return (fetchStatFile(URL
, us
, flags
));
143 else if (strcasecmp(URL
->scheme
, SCHEME_FTP
) == 0)
144 return (fetchStatFTP(URL
, us
, flags
));
145 else if (strcasecmp(URL
->scheme
, SCHEME_HTTP
) == 0)
146 return (fetchStatHTTP(URL
, us
, flags
));
147 else if (strcasecmp(URL
->scheme
, SCHEME_HTTPS
) == 0)
148 return (fetchStatHTTP(URL
, us
, flags
));
149 url_seterr(URL_BAD_SCHEME
);
154 * Select the appropriate protocol for the URL scheme, and return a
155 * list of files in the directory pointed to by the URL.
158 fetchList(struct url_list
*ue
, struct url
*URL
, const char *pattern
,
162 if (strcasecmp(URL
->scheme
, SCHEME_FILE
) == 0)
163 return (fetchListFile(ue
, URL
, pattern
, flags
));
164 else if (strcasecmp(URL
->scheme
, SCHEME_FTP
) == 0)
165 return (fetchListFTP(ue
, URL
, pattern
, flags
));
166 else if (strcasecmp(URL
->scheme
, SCHEME_HTTP
) == 0)
167 return (fetchListHTTP(ue
, URL
, pattern
, flags
));
168 else if (strcasecmp(URL
->scheme
, SCHEME_HTTPS
) == 0)
169 return (fetchListHTTP(ue
, URL
, pattern
, flags
));
170 url_seterr(URL_BAD_SCHEME
);
175 * Attempt to parse the given URL; if successful, call fetchXGet().
178 fetchXGetURL(const char *URL
, struct url_stat
*us
, const char *flags
)
183 if ((u
= fetchParseURL(URL
)) == NULL
)
186 f
= fetchXGet(u
, us
, flags
);
193 * Attempt to parse the given URL; if successful, call fetchGet().
196 fetchGetURL(const char *URL
, const char *flags
)
198 return (fetchXGetURL(URL
, NULL
, flags
));
202 * Attempt to parse the given URL; if successful, call fetchPut().
205 fetchPutURL(const char *URL
, const char *flags
)
210 if ((u
= fetchParseURL(URL
)) == NULL
)
213 f
= fetchPut(u
, flags
);
220 * Attempt to parse the given URL; if successful, call fetchStat().
223 fetchStatURL(const char *URL
, struct url_stat
*us
, const char *flags
)
228 if ((u
= fetchParseURL(URL
)) == NULL
)
231 s
= fetchStat(u
, us
, flags
);
238 * Attempt to parse the given URL; if successful, call fetchList().
241 fetchListURL(struct url_list
*ue
, const char *URL
, const char *pattern
,
247 if ((u
= fetchParseURL(URL
)) == NULL
)
250 rv
= fetchList(ue
, u
, pattern
, flags
);
260 fetchMakeURL(const char *scheme
, const char *host
, int port
, const char *doc
,
261 const char *user
, const char *pwd
)
265 if (!scheme
|| (!host
&& !doc
)) {
266 url_seterr(URL_MALFORMED
);
270 if (port
< 0 || port
> 65535) {
271 url_seterr(URL_BAD_PORT
);
275 /* allocate struct url */
276 if ((u
= calloc(1, sizeof(*u
))) == NULL
) {
281 if ((u
->doc
= strdup(doc
? doc
: "/")) == NULL
) {
287 #define seturl(x) snprintf(u->x, sizeof(u->x), "%s", x)
299 fetch_urlpath_safe(char x
)
301 if ((x
>= '0' && x
<= '9') || (x
>= 'A' && x
<= 'Z') ||
302 (x
>= 'a' && x
<= 'z'))
317 /* The following are allowed in segment and path components: */
325 /* If something is already quoted... */
334 * Copy an existing URL.
337 fetchCopyURL(const struct url
*src
)
342 /* allocate struct url */
343 if ((dst
= malloc(sizeof(*dst
))) == NULL
) {
347 if ((doc
= strdup(src
->doc
)) == NULL
) {
359 * Split an URL into components. URL syntax is:
360 * [method:/][/[user[:pwd]@]host[:port]/][document]
361 * This almost, but not quite, RFC1738 URL syntax.
364 fetchParseURL(const char *URL
)
371 /* allocate struct url */
372 if ((u
= calloc(1, sizeof(*u
))) == NULL
) {
379 strcpy(u
->scheme
, SCHEME_FILE
);
383 if (strncmp(URL
, "file:", 5) == 0) {
385 strcpy(u
->scheme
, SCHEME_FILE
);
387 if (URL
[0] != '/' || URL
[1] != '/' || URL
[2] != '/') {
388 url_seterr(URL_MALFORMED
);
394 if (strncmp(URL
, "http:", 5) == 0 ||
395 strncmp(URL
, "https:", 6) == 0) {
398 strcpy(u
->scheme
, SCHEME_HTTP
);
401 strcpy(u
->scheme
, SCHEME_HTTPS
);
405 if (URL
[0] != '/' || URL
[1] != '/') {
406 url_seterr(URL_MALFORMED
);
413 if (strncmp(URL
, "ftp:", 4) == 0) {
415 strcpy(u
->scheme
, SCHEME_FTP
);
417 if (URL
[0] != '/' || URL
[1] != '/') {
418 url_seterr(URL_MALFORMED
);
426 url_seterr(URL_BAD_SCHEME
);
430 p
= strpbrk(URL
, "/@");
431 if (p
!= NULL
&& *p
== '@') {
433 for (q
= URL
, i
= 0; (*q
!= ':') && (*q
!= '@'); q
++) {
440 for (q
++, i
= 0; (*q
!= '@'); q
++)
452 if (*p
== '[' && (q
= strchr(p
+ 1, ']')) != NULL
&&
453 (*++q
== '\0' || *q
== '/' || *q
== ':')) {
454 if ((i
= q
- p
- 2) > URL_HOSTLEN
)
456 strncpy(u
->host
, ++p
, i
);
460 for (i
= 0; *p
&& (*p
!= '/') && (*p
!= ':'); p
++)
466 for (q
= ++p
; *q
&& (*q
!= '/'); q
++)
467 if (isdigit((unsigned char)*q
))
468 u
->port
= u
->port
* 10 + (*q
- '0');
471 url_seterr(URL_BAD_PORT
);
483 for (i
= 0; p
[i
] != '\0'; ++i
) {
484 if ((!pre_quoted
&& p
[i
] == '%') ||
485 !fetch_urlpath_safe(p
[i
]))
491 if ((u
->doc
= malloc(count
)) == NULL
) {
495 for (i
= 0; *p
!= '\0'; ++p
) {
496 if ((!pre_quoted
&& *p
== '%') ||
497 !fetch_urlpath_safe(*p
)) {
499 if ((unsigned char)*p
< 160)
500 u
->doc
[i
++] = '0' + ((unsigned char)*p
) / 16;
502 u
->doc
[i
++] = 'a' - 10 + ((unsigned char)*p
) / 16;
503 if ((unsigned char)*p
% 16 < 10)
504 u
->doc
[i
++] = '0' + ((unsigned char)*p
) % 16;
506 u
->doc
[i
++] = 'a' - 10 + ((unsigned char)*p
) % 16;
523 fetchFreeURL(struct url
*u
)
530 xdigit2digit(char digit
)
532 digit
= tolower((unsigned char)digit
);
533 if (digit
>= 'a' && digit
<= 'f')
534 digit
= digit
- 'a' + 10;
543 * Skips optional parts like query or fragment identifier.
546 fetchUnquotePath(struct url
*url
)
552 if ((unquoted
= malloc(strlen(url
->doc
) + 1)) == NULL
)
555 for (i
= 0, iter
= url
->doc
; *iter
!= '\0'; ++iter
) {
556 if (*iter
== '#' || *iter
== '?')
558 if (iter
[0] != '%' ||
559 !isxdigit((unsigned char)iter
[1]) ||
560 !isxdigit((unsigned char)iter
[2])) {
561 unquoted
[i
++] = *iter
;
564 unquoted
[i
++] = xdigit2digit(iter
[1]) * 16 +
565 xdigit2digit(iter
[2]);
574 * Extract the file name component of a URL.
577 fetchUnquoteFilename(struct url
*url
)
579 char *unquoted
, *filename
;
580 const char *last_slash
;
582 if ((unquoted
= fetchUnquotePath(url
)) == NULL
)
585 if ((last_slash
= strrchr(unquoted
, '/')) == NULL
)
587 filename
= strdup(last_slash
+ 1);
593 fetchStringifyURL(const struct url
*url
)
598 /* scheme :// user : pwd @ host :port doc */
599 total
= strlen(url
->scheme
) + 3 + strlen(url
->user
) + 1 +
600 strlen(url
->pwd
) + 1 + strlen(url
->host
) + 6 + strlen(url
->doc
) + 1;
601 if ((doc
= malloc(total
)) == NULL
)
604 snprintf(doc
, total
, "%s%s%s%s%s%s%s:%d%s",
606 url
->scheme
[0] != '\0' ? "://" : "",
608 url
->pwd
[0] != '\0' ? ":" : "",
610 url
->user
[0] != '\0' || url
->pwd
[0] != '\0' ? "@" : "",
615 snprintf(doc
, total
, "%s%s%s%s%s%s%s%s",
617 url
->scheme
[0] != '\0' ? "://" : "",
619 url
->pwd
[0] != '\0' ? ":" : "",
621 url
->user
[0] != '\0' || url
->pwd
[0] != '\0' ? "@" : "",