1*040ec644SDavid van Moolenbroek /* $NetBSD: fetch.c,v 1.1.1.8 2009/08/21 15:12:27 joerg Exp $ */
2*040ec644SDavid van Moolenbroek /*-
3*040ec644SDavid van Moolenbroek * Copyright (c) 1998-2004 Dag-Erling Co�dan Sm�rgrav
4*040ec644SDavid van Moolenbroek * Copyright (c) 2008 Joerg Sonnenberger <joerg@NetBSD.org>
5*040ec644SDavid van Moolenbroek * All rights reserved.
6*040ec644SDavid van Moolenbroek *
7*040ec644SDavid van Moolenbroek * Redistribution and use in source and binary forms, with or without
8*040ec644SDavid van Moolenbroek * modification, are permitted provided that the following conditions
9*040ec644SDavid van Moolenbroek * are met:
10*040ec644SDavid van Moolenbroek * 1. Redistributions of source code must retain the above copyright
11*040ec644SDavid van Moolenbroek * notice, this list of conditions and the following disclaimer
12*040ec644SDavid van Moolenbroek * in this position and unchanged.
13*040ec644SDavid van Moolenbroek * 2. Redistributions in binary form must reproduce the above copyright
14*040ec644SDavid van Moolenbroek * notice, this list of conditions and the following disclaimer in the
15*040ec644SDavid van Moolenbroek * documentation and/or other materials provided with the distribution.
16*040ec644SDavid van Moolenbroek * 3. The name of the author may not be used to endorse or promote products
17*040ec644SDavid van Moolenbroek * derived from this software without specific prior written permission
18*040ec644SDavid van Moolenbroek *
19*040ec644SDavid van Moolenbroek * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20*040ec644SDavid van Moolenbroek * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21*040ec644SDavid van Moolenbroek * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22*040ec644SDavid van Moolenbroek * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23*040ec644SDavid van Moolenbroek * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24*040ec644SDavid van Moolenbroek * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25*040ec644SDavid van Moolenbroek * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26*040ec644SDavid van Moolenbroek * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27*040ec644SDavid van Moolenbroek * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28*040ec644SDavid van Moolenbroek * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29*040ec644SDavid van Moolenbroek *
30*040ec644SDavid van Moolenbroek * $FreeBSD: fetch.c,v 1.41 2007/12/19 00:26:36 des Exp $
31*040ec644SDavid van Moolenbroek */
32*040ec644SDavid van Moolenbroek
33*040ec644SDavid van Moolenbroek #if HAVE_CONFIG_H
34*040ec644SDavid van Moolenbroek #include "config.h"
35*040ec644SDavid van Moolenbroek #endif
36*040ec644SDavid van Moolenbroek #ifndef NETBSD
37*040ec644SDavid van Moolenbroek #include <nbcompat.h>
38*040ec644SDavid van Moolenbroek #endif
39*040ec644SDavid van Moolenbroek
40*040ec644SDavid van Moolenbroek #include <ctype.h>
41*040ec644SDavid van Moolenbroek #include <errno.h>
42*040ec644SDavid van Moolenbroek #include <stdio.h>
43*040ec644SDavid van Moolenbroek #include <stdlib.h>
44*040ec644SDavid van Moolenbroek #include <string.h>
45*040ec644SDavid van Moolenbroek
46*040ec644SDavid van Moolenbroek #include "fetch.h"
47*040ec644SDavid van Moolenbroek #include "common.h"
48*040ec644SDavid van Moolenbroek
49*040ec644SDavid van Moolenbroek auth_t fetchAuthMethod;
50*040ec644SDavid van Moolenbroek int fetchLastErrCode;
51*040ec644SDavid van Moolenbroek char fetchLastErrString[MAXERRSTRING];
52*040ec644SDavid van Moolenbroek int fetchTimeout;
53*040ec644SDavid van Moolenbroek volatile int fetchRestartCalls = 1;
54*040ec644SDavid van Moolenbroek int fetchDebug;
55*040ec644SDavid van Moolenbroek
56*040ec644SDavid van Moolenbroek
57*040ec644SDavid van Moolenbroek /*** Local data **************************************************************/
58*040ec644SDavid van Moolenbroek
59*040ec644SDavid van Moolenbroek /*
60*040ec644SDavid van Moolenbroek * Error messages for parser errors
61*040ec644SDavid van Moolenbroek */
62*040ec644SDavid van Moolenbroek #define URL_MALFORMED 1
63*040ec644SDavid van Moolenbroek #define URL_BAD_SCHEME 2
64*040ec644SDavid van Moolenbroek #define URL_BAD_PORT 3
65*040ec644SDavid van Moolenbroek static struct fetcherr url_errlist[] = {
66*040ec644SDavid van Moolenbroek { URL_MALFORMED, FETCH_URL, "Malformed URL" },
67*040ec644SDavid van Moolenbroek { URL_BAD_SCHEME, FETCH_URL, "Invalid URL scheme" },
68*040ec644SDavid van Moolenbroek { URL_BAD_PORT, FETCH_URL, "Invalid server port" },
69*040ec644SDavid van Moolenbroek { -1, FETCH_UNKNOWN, "Unknown parser error" }
70*040ec644SDavid van Moolenbroek };
71*040ec644SDavid van Moolenbroek
72*040ec644SDavid van Moolenbroek
73*040ec644SDavid van Moolenbroek /*** Public API **************************************************************/
74*040ec644SDavid van Moolenbroek
75*040ec644SDavid van Moolenbroek /*
76*040ec644SDavid van Moolenbroek * Select the appropriate protocol for the URL scheme, and return a
77*040ec644SDavid van Moolenbroek * read-only stream connected to the document referenced by the URL.
78*040ec644SDavid van Moolenbroek * Also fill out the struct url_stat.
79*040ec644SDavid van Moolenbroek */
80*040ec644SDavid van Moolenbroek fetchIO *
fetchXGet(struct url * URL,struct url_stat * us,const char * flags)81*040ec644SDavid van Moolenbroek fetchXGet(struct url *URL, struct url_stat *us, const char *flags)
82*040ec644SDavid van Moolenbroek {
83*040ec644SDavid van Moolenbroek
84*040ec644SDavid van Moolenbroek if (us != NULL) {
85*040ec644SDavid van Moolenbroek us->size = -1;
86*040ec644SDavid van Moolenbroek us->atime = us->mtime = 0;
87*040ec644SDavid van Moolenbroek }
88*040ec644SDavid van Moolenbroek if (strcasecmp(URL->scheme, SCHEME_FILE) == 0)
89*040ec644SDavid van Moolenbroek return (fetchXGetFile(URL, us, flags));
90*040ec644SDavid van Moolenbroek else if (strcasecmp(URL->scheme, SCHEME_FTP) == 0)
91*040ec644SDavid van Moolenbroek return (fetchXGetFTP(URL, us, flags));
92*040ec644SDavid van Moolenbroek else if (strcasecmp(URL->scheme, SCHEME_HTTP) == 0)
93*040ec644SDavid van Moolenbroek return (fetchXGetHTTP(URL, us, flags));
94*040ec644SDavid van Moolenbroek else if (strcasecmp(URL->scheme, SCHEME_HTTPS) == 0)
95*040ec644SDavid van Moolenbroek return (fetchXGetHTTP(URL, us, flags));
96*040ec644SDavid van Moolenbroek url_seterr(URL_BAD_SCHEME);
97*040ec644SDavid van Moolenbroek return (NULL);
98*040ec644SDavid van Moolenbroek }
99*040ec644SDavid van Moolenbroek
100*040ec644SDavid van Moolenbroek /*
101*040ec644SDavid van Moolenbroek * Select the appropriate protocol for the URL scheme, and return a
102*040ec644SDavid van Moolenbroek * read-only stream connected to the document referenced by the URL.
103*040ec644SDavid van Moolenbroek */
104*040ec644SDavid van Moolenbroek fetchIO *
fetchGet(struct url * URL,const char * flags)105*040ec644SDavid van Moolenbroek fetchGet(struct url *URL, const char *flags)
106*040ec644SDavid van Moolenbroek {
107*040ec644SDavid van Moolenbroek return (fetchXGet(URL, NULL, flags));
108*040ec644SDavid van Moolenbroek }
109*040ec644SDavid van Moolenbroek
110*040ec644SDavid van Moolenbroek /*
111*040ec644SDavid van Moolenbroek * Select the appropriate protocol for the URL scheme, and return a
112*040ec644SDavid van Moolenbroek * write-only stream connected to the document referenced by the URL.
113*040ec644SDavid van Moolenbroek */
114*040ec644SDavid van Moolenbroek fetchIO *
fetchPut(struct url * URL,const char * flags)115*040ec644SDavid van Moolenbroek fetchPut(struct url *URL, const char *flags)
116*040ec644SDavid van Moolenbroek {
117*040ec644SDavid van Moolenbroek
118*040ec644SDavid van Moolenbroek if (strcasecmp(URL->scheme, SCHEME_FILE) == 0)
119*040ec644SDavid van Moolenbroek return (fetchPutFile(URL, flags));
120*040ec644SDavid van Moolenbroek else if (strcasecmp(URL->scheme, SCHEME_FTP) == 0)
121*040ec644SDavid van Moolenbroek return (fetchPutFTP(URL, flags));
122*040ec644SDavid van Moolenbroek else if (strcasecmp(URL->scheme, SCHEME_HTTP) == 0)
123*040ec644SDavid van Moolenbroek return (fetchPutHTTP(URL, flags));
124*040ec644SDavid van Moolenbroek else if (strcasecmp(URL->scheme, SCHEME_HTTPS) == 0)
125*040ec644SDavid van Moolenbroek return (fetchPutHTTP(URL, flags));
126*040ec644SDavid van Moolenbroek url_seterr(URL_BAD_SCHEME);
127*040ec644SDavid van Moolenbroek return (NULL);
128*040ec644SDavid van Moolenbroek }
129*040ec644SDavid van Moolenbroek
130*040ec644SDavid van Moolenbroek /*
131*040ec644SDavid van Moolenbroek * Select the appropriate protocol for the URL scheme, and return the
132*040ec644SDavid van Moolenbroek * size of the document referenced by the URL if it exists.
133*040ec644SDavid van Moolenbroek */
134*040ec644SDavid van Moolenbroek int
fetchStat(struct url * URL,struct url_stat * us,const char * flags)135*040ec644SDavid van Moolenbroek fetchStat(struct url *URL, struct url_stat *us, const char *flags)
136*040ec644SDavid van Moolenbroek {
137*040ec644SDavid van Moolenbroek
138*040ec644SDavid van Moolenbroek if (us != NULL) {
139*040ec644SDavid van Moolenbroek us->size = -1;
140*040ec644SDavid van Moolenbroek us->atime = us->mtime = 0;
141*040ec644SDavid van Moolenbroek }
142*040ec644SDavid van Moolenbroek if (strcasecmp(URL->scheme, SCHEME_FILE) == 0)
143*040ec644SDavid van Moolenbroek return (fetchStatFile(URL, us, flags));
144*040ec644SDavid van Moolenbroek else if (strcasecmp(URL->scheme, SCHEME_FTP) == 0)
145*040ec644SDavid van Moolenbroek return (fetchStatFTP(URL, us, flags));
146*040ec644SDavid van Moolenbroek else if (strcasecmp(URL->scheme, SCHEME_HTTP) == 0)
147*040ec644SDavid van Moolenbroek return (fetchStatHTTP(URL, us, flags));
148*040ec644SDavid van Moolenbroek else if (strcasecmp(URL->scheme, SCHEME_HTTPS) == 0)
149*040ec644SDavid van Moolenbroek return (fetchStatHTTP(URL, us, flags));
150*040ec644SDavid van Moolenbroek url_seterr(URL_BAD_SCHEME);
151*040ec644SDavid van Moolenbroek return (-1);
152*040ec644SDavid van Moolenbroek }
153*040ec644SDavid van Moolenbroek
154*040ec644SDavid van Moolenbroek /*
155*040ec644SDavid van Moolenbroek * Select the appropriate protocol for the URL scheme, and return a
156*040ec644SDavid van Moolenbroek * list of files in the directory pointed to by the URL.
157*040ec644SDavid van Moolenbroek */
158*040ec644SDavid van Moolenbroek int
fetchList(struct url_list * ue,struct url * URL,const char * pattern,const char * flags)159*040ec644SDavid van Moolenbroek fetchList(struct url_list *ue, struct url *URL, const char *pattern,
160*040ec644SDavid van Moolenbroek const char *flags)
161*040ec644SDavid van Moolenbroek {
162*040ec644SDavid van Moolenbroek
163*040ec644SDavid van Moolenbroek if (strcasecmp(URL->scheme, SCHEME_FILE) == 0)
164*040ec644SDavid van Moolenbroek return (fetchListFile(ue, URL, pattern, flags));
165*040ec644SDavid van Moolenbroek else if (strcasecmp(URL->scheme, SCHEME_FTP) == 0)
166*040ec644SDavid van Moolenbroek return (fetchListFTP(ue, URL, pattern, flags));
167*040ec644SDavid van Moolenbroek else if (strcasecmp(URL->scheme, SCHEME_HTTP) == 0)
168*040ec644SDavid van Moolenbroek return (fetchListHTTP(ue, URL, pattern, flags));
169*040ec644SDavid van Moolenbroek else if (strcasecmp(URL->scheme, SCHEME_HTTPS) == 0)
170*040ec644SDavid van Moolenbroek return (fetchListHTTP(ue, URL, pattern, flags));
171*040ec644SDavid van Moolenbroek url_seterr(URL_BAD_SCHEME);
172*040ec644SDavid van Moolenbroek return -1;
173*040ec644SDavid van Moolenbroek }
174*040ec644SDavid van Moolenbroek
175*040ec644SDavid van Moolenbroek /*
176*040ec644SDavid van Moolenbroek * Attempt to parse the given URL; if successful, call fetchXGet().
177*040ec644SDavid van Moolenbroek */
178*040ec644SDavid van Moolenbroek fetchIO *
fetchXGetURL(const char * URL,struct url_stat * us,const char * flags)179*040ec644SDavid van Moolenbroek fetchXGetURL(const char *URL, struct url_stat *us, const char *flags)
180*040ec644SDavid van Moolenbroek {
181*040ec644SDavid van Moolenbroek struct url *u;
182*040ec644SDavid van Moolenbroek fetchIO *f;
183*040ec644SDavid van Moolenbroek
184*040ec644SDavid van Moolenbroek if ((u = fetchParseURL(URL)) == NULL)
185*040ec644SDavid van Moolenbroek return (NULL);
186*040ec644SDavid van Moolenbroek
187*040ec644SDavid van Moolenbroek f = fetchXGet(u, us, flags);
188*040ec644SDavid van Moolenbroek
189*040ec644SDavid van Moolenbroek fetchFreeURL(u);
190*040ec644SDavid van Moolenbroek return (f);
191*040ec644SDavid van Moolenbroek }
192*040ec644SDavid van Moolenbroek
193*040ec644SDavid van Moolenbroek /*
194*040ec644SDavid van Moolenbroek * Attempt to parse the given URL; if successful, call fetchGet().
195*040ec644SDavid van Moolenbroek */
196*040ec644SDavid van Moolenbroek fetchIO *
fetchGetURL(const char * URL,const char * flags)197*040ec644SDavid van Moolenbroek fetchGetURL(const char *URL, const char *flags)
198*040ec644SDavid van Moolenbroek {
199*040ec644SDavid van Moolenbroek return (fetchXGetURL(URL, NULL, flags));
200*040ec644SDavid van Moolenbroek }
201*040ec644SDavid van Moolenbroek
202*040ec644SDavid van Moolenbroek /*
203*040ec644SDavid van Moolenbroek * Attempt to parse the given URL; if successful, call fetchPut().
204*040ec644SDavid van Moolenbroek */
205*040ec644SDavid van Moolenbroek fetchIO *
fetchPutURL(const char * URL,const char * flags)206*040ec644SDavid van Moolenbroek fetchPutURL(const char *URL, const char *flags)
207*040ec644SDavid van Moolenbroek {
208*040ec644SDavid van Moolenbroek struct url *u;
209*040ec644SDavid van Moolenbroek fetchIO *f;
210*040ec644SDavid van Moolenbroek
211*040ec644SDavid van Moolenbroek if ((u = fetchParseURL(URL)) == NULL)
212*040ec644SDavid van Moolenbroek return (NULL);
213*040ec644SDavid van Moolenbroek
214*040ec644SDavid van Moolenbroek f = fetchPut(u, flags);
215*040ec644SDavid van Moolenbroek
216*040ec644SDavid van Moolenbroek fetchFreeURL(u);
217*040ec644SDavid van Moolenbroek return (f);
218*040ec644SDavid van Moolenbroek }
219*040ec644SDavid van Moolenbroek
220*040ec644SDavid van Moolenbroek /*
221*040ec644SDavid van Moolenbroek * Attempt to parse the given URL; if successful, call fetchStat().
222*040ec644SDavid van Moolenbroek */
223*040ec644SDavid van Moolenbroek int
fetchStatURL(const char * URL,struct url_stat * us,const char * flags)224*040ec644SDavid van Moolenbroek fetchStatURL(const char *URL, struct url_stat *us, const char *flags)
225*040ec644SDavid van Moolenbroek {
226*040ec644SDavid van Moolenbroek struct url *u;
227*040ec644SDavid van Moolenbroek int s;
228*040ec644SDavid van Moolenbroek
229*040ec644SDavid van Moolenbroek if ((u = fetchParseURL(URL)) == NULL)
230*040ec644SDavid van Moolenbroek return (-1);
231*040ec644SDavid van Moolenbroek
232*040ec644SDavid van Moolenbroek s = fetchStat(u, us, flags);
233*040ec644SDavid van Moolenbroek
234*040ec644SDavid van Moolenbroek fetchFreeURL(u);
235*040ec644SDavid van Moolenbroek return (s);
236*040ec644SDavid van Moolenbroek }
237*040ec644SDavid van Moolenbroek
238*040ec644SDavid van Moolenbroek /*
239*040ec644SDavid van Moolenbroek * Attempt to parse the given URL; if successful, call fetchList().
240*040ec644SDavid van Moolenbroek */
241*040ec644SDavid van Moolenbroek int
fetchListURL(struct url_list * ue,const char * URL,const char * pattern,const char * flags)242*040ec644SDavid van Moolenbroek fetchListURL(struct url_list *ue, const char *URL, const char *pattern,
243*040ec644SDavid van Moolenbroek const char *flags)
244*040ec644SDavid van Moolenbroek {
245*040ec644SDavid van Moolenbroek struct url *u;
246*040ec644SDavid van Moolenbroek int rv;
247*040ec644SDavid van Moolenbroek
248*040ec644SDavid van Moolenbroek if ((u = fetchParseURL(URL)) == NULL)
249*040ec644SDavid van Moolenbroek return -1;
250*040ec644SDavid van Moolenbroek
251*040ec644SDavid van Moolenbroek rv = fetchList(ue, u, pattern, flags);
252*040ec644SDavid van Moolenbroek
253*040ec644SDavid van Moolenbroek fetchFreeURL(u);
254*040ec644SDavid van Moolenbroek return rv;
255*040ec644SDavid van Moolenbroek }
256*040ec644SDavid van Moolenbroek
257*040ec644SDavid van Moolenbroek /*
258*040ec644SDavid van Moolenbroek * Make a URL
259*040ec644SDavid van Moolenbroek */
260*040ec644SDavid van Moolenbroek struct url *
fetchMakeURL(const char * scheme,const char * host,int port,const char * doc,const char * user,const char * pwd)261*040ec644SDavid van Moolenbroek fetchMakeURL(const char *scheme, const char *host, int port, const char *doc,
262*040ec644SDavid van Moolenbroek const char *user, const char *pwd)
263*040ec644SDavid van Moolenbroek {
264*040ec644SDavid van Moolenbroek struct url *u;
265*040ec644SDavid van Moolenbroek
266*040ec644SDavid van Moolenbroek if (!scheme || (!host && !doc)) {
267*040ec644SDavid van Moolenbroek url_seterr(URL_MALFORMED);
268*040ec644SDavid van Moolenbroek return (NULL);
269*040ec644SDavid van Moolenbroek }
270*040ec644SDavid van Moolenbroek
271*040ec644SDavid van Moolenbroek if (port < 0 || port > 65535) {
272*040ec644SDavid van Moolenbroek url_seterr(URL_BAD_PORT);
273*040ec644SDavid van Moolenbroek return (NULL);
274*040ec644SDavid van Moolenbroek }
275*040ec644SDavid van Moolenbroek
276*040ec644SDavid van Moolenbroek /* allocate struct url */
277*040ec644SDavid van Moolenbroek if ((u = calloc(1, sizeof(*u))) == NULL) {
278*040ec644SDavid van Moolenbroek fetch_syserr();
279*040ec644SDavid van Moolenbroek return (NULL);
280*040ec644SDavid van Moolenbroek }
281*040ec644SDavid van Moolenbroek
282*040ec644SDavid van Moolenbroek if ((u->doc = strdup(doc ? doc : "/")) == NULL) {
283*040ec644SDavid van Moolenbroek fetch_syserr();
284*040ec644SDavid van Moolenbroek free(u);
285*040ec644SDavid van Moolenbroek return (NULL);
286*040ec644SDavid van Moolenbroek }
287*040ec644SDavid van Moolenbroek
288*040ec644SDavid van Moolenbroek #define seturl(x) snprintf(u->x, sizeof(u->x), "%s", x)
289*040ec644SDavid van Moolenbroek seturl(scheme);
290*040ec644SDavid van Moolenbroek seturl(host);
291*040ec644SDavid van Moolenbroek seturl(user);
292*040ec644SDavid van Moolenbroek seturl(pwd);
293*040ec644SDavid van Moolenbroek #undef seturl
294*040ec644SDavid van Moolenbroek u->port = port;
295*040ec644SDavid van Moolenbroek
296*040ec644SDavid van Moolenbroek return (u);
297*040ec644SDavid van Moolenbroek }
298*040ec644SDavid van Moolenbroek
299*040ec644SDavid van Moolenbroek int
fetch_urlpath_safe(char x)300*040ec644SDavid van Moolenbroek fetch_urlpath_safe(char x)
301*040ec644SDavid van Moolenbroek {
302*040ec644SDavid van Moolenbroek if ((x >= '0' && x <= '9') || (x >= 'A' && x <= 'Z') ||
303*040ec644SDavid van Moolenbroek (x >= 'a' && x <= 'z'))
304*040ec644SDavid van Moolenbroek return 1;
305*040ec644SDavid van Moolenbroek
306*040ec644SDavid van Moolenbroek switch (x) {
307*040ec644SDavid van Moolenbroek case '$':
308*040ec644SDavid van Moolenbroek case '-':
309*040ec644SDavid van Moolenbroek case '_':
310*040ec644SDavid van Moolenbroek case '.':
311*040ec644SDavid van Moolenbroek case '+':
312*040ec644SDavid van Moolenbroek case '!':
313*040ec644SDavid van Moolenbroek case '*':
314*040ec644SDavid van Moolenbroek case '\'':
315*040ec644SDavid van Moolenbroek case '(':
316*040ec644SDavid van Moolenbroek case ')':
317*040ec644SDavid van Moolenbroek case ',':
318*040ec644SDavid van Moolenbroek /* The following are allowed in segment and path components: */
319*040ec644SDavid van Moolenbroek case '?':
320*040ec644SDavid van Moolenbroek case ':':
321*040ec644SDavid van Moolenbroek case '@':
322*040ec644SDavid van Moolenbroek case '&':
323*040ec644SDavid van Moolenbroek case '=':
324*040ec644SDavid van Moolenbroek case '/':
325*040ec644SDavid van Moolenbroek case ';':
326*040ec644SDavid van Moolenbroek /* If something is already quoted... */
327*040ec644SDavid van Moolenbroek case '%':
328*040ec644SDavid van Moolenbroek return 1;
329*040ec644SDavid van Moolenbroek default:
330*040ec644SDavid van Moolenbroek return 0;
331*040ec644SDavid van Moolenbroek }
332*040ec644SDavid van Moolenbroek }
333*040ec644SDavid van Moolenbroek
334*040ec644SDavid van Moolenbroek /*
335*040ec644SDavid van Moolenbroek * Copy an existing URL.
336*040ec644SDavid van Moolenbroek */
337*040ec644SDavid van Moolenbroek struct url *
fetchCopyURL(const struct url * src)338*040ec644SDavid van Moolenbroek fetchCopyURL(const struct url *src)
339*040ec644SDavid van Moolenbroek {
340*040ec644SDavid van Moolenbroek struct url *dst;
341*040ec644SDavid van Moolenbroek char *doc;
342*040ec644SDavid van Moolenbroek
343*040ec644SDavid van Moolenbroek /* allocate struct url */
344*040ec644SDavid van Moolenbroek if ((dst = malloc(sizeof(*dst))) == NULL) {
345*040ec644SDavid van Moolenbroek fetch_syserr();
346*040ec644SDavid van Moolenbroek return (NULL);
347*040ec644SDavid van Moolenbroek }
348*040ec644SDavid van Moolenbroek if ((doc = strdup(src->doc)) == NULL) {
349*040ec644SDavid van Moolenbroek fetch_syserr();
350*040ec644SDavid van Moolenbroek free(dst);
351*040ec644SDavid van Moolenbroek return (NULL);
352*040ec644SDavid van Moolenbroek }
353*040ec644SDavid van Moolenbroek *dst = *src;
354*040ec644SDavid van Moolenbroek dst->doc = doc;
355*040ec644SDavid van Moolenbroek
356*040ec644SDavid van Moolenbroek return dst;
357*040ec644SDavid van Moolenbroek }
358*040ec644SDavid van Moolenbroek
359*040ec644SDavid van Moolenbroek /*
360*040ec644SDavid van Moolenbroek * Split an URL into components. URL syntax is:
361*040ec644SDavid van Moolenbroek * [method:/][/[user[:pwd]@]host[:port]/][document]
362*040ec644SDavid van Moolenbroek * This almost, but not quite, RFC1738 URL syntax.
363*040ec644SDavid van Moolenbroek */
364*040ec644SDavid van Moolenbroek struct url *
fetchParseURL(const char * URL)365*040ec644SDavid van Moolenbroek fetchParseURL(const char *URL)
366*040ec644SDavid van Moolenbroek {
367*040ec644SDavid van Moolenbroek const char *p, *q;
368*040ec644SDavid van Moolenbroek struct url *u;
369*040ec644SDavid van Moolenbroek size_t i, count;
370*040ec644SDavid van Moolenbroek int pre_quoted;
371*040ec644SDavid van Moolenbroek
372*040ec644SDavid van Moolenbroek /* allocate struct url */
373*040ec644SDavid van Moolenbroek if ((u = calloc(1, sizeof(*u))) == NULL) {
374*040ec644SDavid van Moolenbroek fetch_syserr();
375*040ec644SDavid van Moolenbroek return (NULL);
376*040ec644SDavid van Moolenbroek }
377*040ec644SDavid van Moolenbroek
378*040ec644SDavid van Moolenbroek if (*URL == '/') {
379*040ec644SDavid van Moolenbroek pre_quoted = 0;
380*040ec644SDavid van Moolenbroek strcpy(u->scheme, SCHEME_FILE);
381*040ec644SDavid van Moolenbroek p = URL;
382*040ec644SDavid van Moolenbroek goto quote_doc;
383*040ec644SDavid van Moolenbroek }
384*040ec644SDavid van Moolenbroek if (strncmp(URL, "file:", 5) == 0) {
385*040ec644SDavid van Moolenbroek pre_quoted = 1;
386*040ec644SDavid van Moolenbroek strcpy(u->scheme, SCHEME_FILE);
387*040ec644SDavid van Moolenbroek URL += 5;
388*040ec644SDavid van Moolenbroek if (URL[0] != '/' || URL[1] != '/' || URL[2] != '/') {
389*040ec644SDavid van Moolenbroek url_seterr(URL_MALFORMED);
390*040ec644SDavid van Moolenbroek goto ouch;
391*040ec644SDavid van Moolenbroek }
392*040ec644SDavid van Moolenbroek p = URL + 2;
393*040ec644SDavid van Moolenbroek goto quote_doc;
394*040ec644SDavid van Moolenbroek }
395*040ec644SDavid van Moolenbroek if (strncmp(URL, "http:", 5) == 0 ||
396*040ec644SDavid van Moolenbroek strncmp(URL, "https:", 6) == 0) {
397*040ec644SDavid van Moolenbroek pre_quoted = 1;
398*040ec644SDavid van Moolenbroek if (URL[4] == ':') {
399*040ec644SDavid van Moolenbroek strcpy(u->scheme, SCHEME_HTTP);
400*040ec644SDavid van Moolenbroek URL += 5;
401*040ec644SDavid van Moolenbroek } else {
402*040ec644SDavid van Moolenbroek strcpy(u->scheme, SCHEME_HTTPS);
403*040ec644SDavid van Moolenbroek URL += 6;
404*040ec644SDavid van Moolenbroek }
405*040ec644SDavid van Moolenbroek
406*040ec644SDavid van Moolenbroek if (URL[0] != '/' || URL[1] != '/') {
407*040ec644SDavid van Moolenbroek url_seterr(URL_MALFORMED);
408*040ec644SDavid van Moolenbroek goto ouch;
409*040ec644SDavid van Moolenbroek }
410*040ec644SDavid van Moolenbroek URL += 2;
411*040ec644SDavid van Moolenbroek p = URL;
412*040ec644SDavid van Moolenbroek goto find_user;
413*040ec644SDavid van Moolenbroek }
414*040ec644SDavid van Moolenbroek if (strncmp(URL, "ftp:", 4) == 0) {
415*040ec644SDavid van Moolenbroek pre_quoted = 1;
416*040ec644SDavid van Moolenbroek strcpy(u->scheme, SCHEME_FTP);
417*040ec644SDavid van Moolenbroek URL += 4;
418*040ec644SDavid van Moolenbroek if (URL[0] != '/' || URL[1] != '/') {
419*040ec644SDavid van Moolenbroek url_seterr(URL_MALFORMED);
420*040ec644SDavid van Moolenbroek goto ouch;
421*040ec644SDavid van Moolenbroek }
422*040ec644SDavid van Moolenbroek URL += 2;
423*040ec644SDavid van Moolenbroek p = URL;
424*040ec644SDavid van Moolenbroek goto find_user;
425*040ec644SDavid van Moolenbroek }
426*040ec644SDavid van Moolenbroek
427*040ec644SDavid van Moolenbroek url_seterr(URL_BAD_SCHEME);
428*040ec644SDavid van Moolenbroek goto ouch;
429*040ec644SDavid van Moolenbroek
430*040ec644SDavid van Moolenbroek find_user:
431*040ec644SDavid van Moolenbroek p = strpbrk(URL, "/@");
432*040ec644SDavid van Moolenbroek if (p != NULL && *p == '@') {
433*040ec644SDavid van Moolenbroek /* username */
434*040ec644SDavid van Moolenbroek for (q = URL, i = 0; (*q != ':') && (*q != '@'); q++) {
435*040ec644SDavid van Moolenbroek if (i < URL_USERLEN)
436*040ec644SDavid van Moolenbroek u->user[i++] = *q;
437*040ec644SDavid van Moolenbroek }
438*040ec644SDavid van Moolenbroek
439*040ec644SDavid van Moolenbroek /* password */
440*040ec644SDavid van Moolenbroek if (*q == ':') {
441*040ec644SDavid van Moolenbroek for (q++, i = 0; (*q != '@'); q++)
442*040ec644SDavid van Moolenbroek if (i < URL_PWDLEN)
443*040ec644SDavid van Moolenbroek u->pwd[i++] = *q;
444*040ec644SDavid van Moolenbroek }
445*040ec644SDavid van Moolenbroek
446*040ec644SDavid van Moolenbroek p++;
447*040ec644SDavid van Moolenbroek } else {
448*040ec644SDavid van Moolenbroek p = URL;
449*040ec644SDavid van Moolenbroek }
450*040ec644SDavid van Moolenbroek
451*040ec644SDavid van Moolenbroek /* hostname */
452*040ec644SDavid van Moolenbroek #ifdef INET6
453*040ec644SDavid van Moolenbroek if (*p == '[' && (q = strchr(p + 1, ']')) != NULL &&
454*040ec644SDavid van Moolenbroek (*++q == '\0' || *q == '/' || *q == ':')) {
455*040ec644SDavid van Moolenbroek if ((i = q - p - 2) > URL_HOSTLEN)
456*040ec644SDavid van Moolenbroek i = URL_HOSTLEN;
457*040ec644SDavid van Moolenbroek strncpy(u->host, ++p, i);
458*040ec644SDavid van Moolenbroek p = q;
459*040ec644SDavid van Moolenbroek } else
460*040ec644SDavid van Moolenbroek #endif
461*040ec644SDavid van Moolenbroek for (i = 0; *p && (*p != '/') && (*p != ':'); p++)
462*040ec644SDavid van Moolenbroek if (i < URL_HOSTLEN)
463*040ec644SDavid van Moolenbroek u->host[i++] = *p;
464*040ec644SDavid van Moolenbroek
465*040ec644SDavid van Moolenbroek /* port */
466*040ec644SDavid van Moolenbroek if (*p == ':') {
467*040ec644SDavid van Moolenbroek for (q = ++p; *q && (*q != '/'); q++)
468*040ec644SDavid van Moolenbroek if (isdigit((unsigned char)*q))
469*040ec644SDavid van Moolenbroek u->port = u->port * 10 + (*q - '0');
470*040ec644SDavid van Moolenbroek else {
471*040ec644SDavid van Moolenbroek /* invalid port */
472*040ec644SDavid van Moolenbroek url_seterr(URL_BAD_PORT);
473*040ec644SDavid van Moolenbroek goto ouch;
474*040ec644SDavid van Moolenbroek }
475*040ec644SDavid van Moolenbroek p = q;
476*040ec644SDavid van Moolenbroek }
477*040ec644SDavid van Moolenbroek
478*040ec644SDavid van Moolenbroek /* document */
479*040ec644SDavid van Moolenbroek if (!*p)
480*040ec644SDavid van Moolenbroek p = "/";
481*040ec644SDavid van Moolenbroek
482*040ec644SDavid van Moolenbroek quote_doc:
483*040ec644SDavid van Moolenbroek count = 1;
484*040ec644SDavid van Moolenbroek for (i = 0; p[i] != '\0'; ++i) {
485*040ec644SDavid van Moolenbroek if ((!pre_quoted && p[i] == '%') ||
486*040ec644SDavid van Moolenbroek !fetch_urlpath_safe(p[i]))
487*040ec644SDavid van Moolenbroek count += 3;
488*040ec644SDavid van Moolenbroek else
489*040ec644SDavid van Moolenbroek ++count;
490*040ec644SDavid van Moolenbroek }
491*040ec644SDavid van Moolenbroek
492*040ec644SDavid van Moolenbroek if ((u->doc = malloc(count)) == NULL) {
493*040ec644SDavid van Moolenbroek fetch_syserr();
494*040ec644SDavid van Moolenbroek goto ouch;
495*040ec644SDavid van Moolenbroek }
496*040ec644SDavid van Moolenbroek for (i = 0; *p != '\0'; ++p) {
497*040ec644SDavid van Moolenbroek if ((!pre_quoted && *p == '%') ||
498*040ec644SDavid van Moolenbroek !fetch_urlpath_safe(*p)) {
499*040ec644SDavid van Moolenbroek u->doc[i++] = '%';
500*040ec644SDavid van Moolenbroek if ((unsigned char)*p < 160)
501*040ec644SDavid van Moolenbroek u->doc[i++] = '0' + ((unsigned char)*p) / 16;
502*040ec644SDavid van Moolenbroek else
503*040ec644SDavid van Moolenbroek u->doc[i++] = 'a' - 10 + ((unsigned char)*p) / 16;
504*040ec644SDavid van Moolenbroek if ((unsigned char)*p % 16 < 10)
505*040ec644SDavid van Moolenbroek u->doc[i++] = '0' + ((unsigned char)*p) % 16;
506*040ec644SDavid van Moolenbroek else
507*040ec644SDavid van Moolenbroek u->doc[i++] = 'a' - 10 + ((unsigned char)*p) % 16;
508*040ec644SDavid van Moolenbroek } else
509*040ec644SDavid van Moolenbroek u->doc[i++] = *p;
510*040ec644SDavid van Moolenbroek }
511*040ec644SDavid van Moolenbroek u->doc[i] = '\0';
512*040ec644SDavid van Moolenbroek
513*040ec644SDavid van Moolenbroek return (u);
514*040ec644SDavid van Moolenbroek
515*040ec644SDavid van Moolenbroek ouch:
516*040ec644SDavid van Moolenbroek free(u);
517*040ec644SDavid van Moolenbroek return (NULL);
518*040ec644SDavid van Moolenbroek }
519*040ec644SDavid van Moolenbroek
520*040ec644SDavid van Moolenbroek /*
521*040ec644SDavid van Moolenbroek * Free a URL
522*040ec644SDavid van Moolenbroek */
523*040ec644SDavid van Moolenbroek void
fetchFreeURL(struct url * u)524*040ec644SDavid van Moolenbroek fetchFreeURL(struct url *u)
525*040ec644SDavid van Moolenbroek {
526*040ec644SDavid van Moolenbroek free(u->doc);
527*040ec644SDavid van Moolenbroek free(u);
528*040ec644SDavid van Moolenbroek }
529*040ec644SDavid van Moolenbroek
530*040ec644SDavid van Moolenbroek static char
xdigit2digit(char digit)531*040ec644SDavid van Moolenbroek xdigit2digit(char digit)
532*040ec644SDavid van Moolenbroek {
533*040ec644SDavid van Moolenbroek digit = tolower((unsigned char)digit);
534*040ec644SDavid van Moolenbroek if (digit >= 'a' && digit <= 'f')
535*040ec644SDavid van Moolenbroek digit = digit - 'a' + 10;
536*040ec644SDavid van Moolenbroek else
537*040ec644SDavid van Moolenbroek digit = digit - '0';
538*040ec644SDavid van Moolenbroek
539*040ec644SDavid van Moolenbroek return digit;
540*040ec644SDavid van Moolenbroek }
541*040ec644SDavid van Moolenbroek
542*040ec644SDavid van Moolenbroek /*
543*040ec644SDavid van Moolenbroek * Unquote whole URL.
544*040ec644SDavid van Moolenbroek * Skips optional parts like query or fragment identifier.
545*040ec644SDavid van Moolenbroek */
546*040ec644SDavid van Moolenbroek char *
fetchUnquotePath(struct url * url)547*040ec644SDavid van Moolenbroek fetchUnquotePath(struct url *url)
548*040ec644SDavid van Moolenbroek {
549*040ec644SDavid van Moolenbroek char *unquoted;
550*040ec644SDavid van Moolenbroek const char *iter;
551*040ec644SDavid van Moolenbroek size_t i;
552*040ec644SDavid van Moolenbroek
553*040ec644SDavid van Moolenbroek if ((unquoted = malloc(strlen(url->doc) + 1)) == NULL)
554*040ec644SDavid van Moolenbroek return NULL;
555*040ec644SDavid van Moolenbroek
556*040ec644SDavid van Moolenbroek for (i = 0, iter = url->doc; *iter != '\0'; ++iter) {
557*040ec644SDavid van Moolenbroek if (*iter == '#' || *iter == '?')
558*040ec644SDavid van Moolenbroek break;
559*040ec644SDavid van Moolenbroek if (iter[0] != '%' ||
560*040ec644SDavid van Moolenbroek !isxdigit((unsigned char)iter[1]) ||
561*040ec644SDavid van Moolenbroek !isxdigit((unsigned char)iter[2])) {
562*040ec644SDavid van Moolenbroek unquoted[i++] = *iter;
563*040ec644SDavid van Moolenbroek continue;
564*040ec644SDavid van Moolenbroek }
565*040ec644SDavid van Moolenbroek unquoted[i++] = xdigit2digit(iter[1]) * 16 +
566*040ec644SDavid van Moolenbroek xdigit2digit(iter[2]);
567*040ec644SDavid van Moolenbroek iter += 2;
568*040ec644SDavid van Moolenbroek }
569*040ec644SDavid van Moolenbroek unquoted[i] = '\0';
570*040ec644SDavid van Moolenbroek return unquoted;
571*040ec644SDavid van Moolenbroek }
572*040ec644SDavid van Moolenbroek
573*040ec644SDavid van Moolenbroek
574*040ec644SDavid van Moolenbroek /*
575*040ec644SDavid van Moolenbroek * Extract the file name component of a URL.
576*040ec644SDavid van Moolenbroek */
577*040ec644SDavid van Moolenbroek char *
fetchUnquoteFilename(struct url * url)578*040ec644SDavid van Moolenbroek fetchUnquoteFilename(struct url *url)
579*040ec644SDavid van Moolenbroek {
580*040ec644SDavid van Moolenbroek char *unquoted, *filename;
581*040ec644SDavid van Moolenbroek const char *last_slash;
582*040ec644SDavid van Moolenbroek
583*040ec644SDavid van Moolenbroek if ((unquoted = fetchUnquotePath(url)) == NULL)
584*040ec644SDavid van Moolenbroek return NULL;
585*040ec644SDavid van Moolenbroek
586*040ec644SDavid van Moolenbroek if ((last_slash = strrchr(unquoted, '/')) == NULL)
587*040ec644SDavid van Moolenbroek return unquoted;
588*040ec644SDavid van Moolenbroek filename = strdup(last_slash + 1);
589*040ec644SDavid van Moolenbroek free(unquoted);
590*040ec644SDavid van Moolenbroek return filename;
591*040ec644SDavid van Moolenbroek }
592*040ec644SDavid van Moolenbroek
593*040ec644SDavid van Moolenbroek char *
fetchStringifyURL(const struct url * url)594*040ec644SDavid van Moolenbroek fetchStringifyURL(const struct url *url)
595*040ec644SDavid van Moolenbroek {
596*040ec644SDavid van Moolenbroek size_t total;
597*040ec644SDavid van Moolenbroek char *doc;
598*040ec644SDavid van Moolenbroek
599*040ec644SDavid van Moolenbroek /* scheme :// user : pwd @ host :port doc */
600*040ec644SDavid van Moolenbroek total = strlen(url->scheme) + 3 + strlen(url->user) + 1 +
601*040ec644SDavid van Moolenbroek strlen(url->pwd) + 1 + strlen(url->host) + 6 + strlen(url->doc) + 1;
602*040ec644SDavid van Moolenbroek if ((doc = malloc(total)) == NULL)
603*040ec644SDavid van Moolenbroek return NULL;
604*040ec644SDavid van Moolenbroek if (url->port != 0)
605*040ec644SDavid van Moolenbroek snprintf(doc, total, "%s%s%s%s%s%s%s:%d%s",
606*040ec644SDavid van Moolenbroek url->scheme,
607*040ec644SDavid van Moolenbroek url->scheme[0] != '\0' ? "://" : "",
608*040ec644SDavid van Moolenbroek url->user,
609*040ec644SDavid van Moolenbroek url->pwd[0] != '\0' ? ":" : "",
610*040ec644SDavid van Moolenbroek url->pwd,
611*040ec644SDavid van Moolenbroek url->user[0] != '\0' || url->pwd[0] != '\0' ? "@" : "",
612*040ec644SDavid van Moolenbroek url->host,
613*040ec644SDavid van Moolenbroek (int)url->port,
614*040ec644SDavid van Moolenbroek url->doc);
615*040ec644SDavid van Moolenbroek else {
616*040ec644SDavid van Moolenbroek snprintf(doc, total, "%s%s%s%s%s%s%s%s",
617*040ec644SDavid van Moolenbroek url->scheme,
618*040ec644SDavid van Moolenbroek url->scheme[0] != '\0' ? "://" : "",
619*040ec644SDavid van Moolenbroek url->user,
620*040ec644SDavid van Moolenbroek url->pwd[0] != '\0' ? ":" : "",
621*040ec644SDavid van Moolenbroek url->pwd,
622*040ec644SDavid van Moolenbroek url->user[0] != '\0' || url->pwd[0] != '\0' ? "@" : "",
623*040ec644SDavid van Moolenbroek url->host,
624*040ec644SDavid van Moolenbroek url->doc);
625*040ec644SDavid van Moolenbroek }
626*040ec644SDavid van Moolenbroek return doc;
627*040ec644SDavid van Moolenbroek }
628