1*0920b4f2Sagc /*-
2*0920b4f2Sagc * Copyright (c) 1998-2004 Dag-Erling Co�dan Sm�rgrav
3*0920b4f2Sagc * All rights reserved.
4*0920b4f2Sagc *
5*0920b4f2Sagc * Redistribution and use in source and binary forms, with or without
6*0920b4f2Sagc * modification, are permitted provided that the following conditions
7*0920b4f2Sagc * are met:
8*0920b4f2Sagc * 1. Redistributions of source code must retain the above copyright
9*0920b4f2Sagc * notice, this list of conditions and the following disclaimer
10*0920b4f2Sagc * in this position and unchanged.
11*0920b4f2Sagc * 2. Redistributions in binary form must reproduce the above copyright
12*0920b4f2Sagc * notice, this list of conditions and the following disclaimer in the
13*0920b4f2Sagc * documentation and/or other materials provided with the distribution.
14*0920b4f2Sagc * 3. The name of the author may not be used to endorse or promote products
15*0920b4f2Sagc * derived from this software without specific prior written permission
16*0920b4f2Sagc *
17*0920b4f2Sagc * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18*0920b4f2Sagc * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19*0920b4f2Sagc * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20*0920b4f2Sagc * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21*0920b4f2Sagc * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22*0920b4f2Sagc * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23*0920b4f2Sagc * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24*0920b4f2Sagc * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25*0920b4f2Sagc * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26*0920b4f2Sagc * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27*0920b4f2Sagc */
28*0920b4f2Sagc
29*0920b4f2Sagc #include "free2net.h"
30*0920b4f2Sagc
31*0920b4f2Sagc #include <sys/cdefs.h>
32*0920b4f2Sagc __FBSDID("$FreeBSD: src/lib/libfetch/fetch.c,v 1.37.6.1 2006/11/11 00:16:07 des Exp $");
33*0920b4f2Sagc
34*0920b4f2Sagc #include <sys/param.h>
35*0920b4f2Sagc #include <sys/errno.h>
36*0920b4f2Sagc
37*0920b4f2Sagc #include <ctype.h>
38*0920b4f2Sagc #include <stdio.h>
39*0920b4f2Sagc #include <stdlib.h>
40*0920b4f2Sagc #include <string.h>
41*0920b4f2Sagc
42*0920b4f2Sagc #include "fetch.h"
43*0920b4f2Sagc #include "common.h"
44*0920b4f2Sagc
45*0920b4f2Sagc auth_t fetchAuthMethod;
46*0920b4f2Sagc int fetchLastErrCode;
47*0920b4f2Sagc char fetchLastErrString[MAXERRSTRING];
48*0920b4f2Sagc int fetchTimeout;
49*0920b4f2Sagc int fetchRestartCalls = 1;
50*0920b4f2Sagc int fetchDebug;
51*0920b4f2Sagc
52*0920b4f2Sagc
53*0920b4f2Sagc /*** Local data **************************************************************/
54*0920b4f2Sagc
55*0920b4f2Sagc /*
56*0920b4f2Sagc * Error messages for parser errors
57*0920b4f2Sagc */
58*0920b4f2Sagc #define URL_MALFORMED 1
59*0920b4f2Sagc #define URL_BAD_SCHEME 2
60*0920b4f2Sagc #define URL_BAD_PORT 3
61*0920b4f2Sagc static struct fetcherr _url_errlist[] = {
62*0920b4f2Sagc { URL_MALFORMED, FETCH_URL, "Malformed URL" },
63*0920b4f2Sagc { URL_BAD_SCHEME, FETCH_URL, "Invalid URL scheme" },
64*0920b4f2Sagc { URL_BAD_PORT, FETCH_URL, "Invalid server port" },
65*0920b4f2Sagc { -1, FETCH_UNKNOWN, "Unknown parser error" }
66*0920b4f2Sagc };
67*0920b4f2Sagc
68*0920b4f2Sagc
69*0920b4f2Sagc /*** Public API **************************************************************/
70*0920b4f2Sagc
71*0920b4f2Sagc /*
72*0920b4f2Sagc * Select the appropriate protocol for the URL scheme, and return a
73*0920b4f2Sagc * read-only stream connected to the document referenced by the URL.
74*0920b4f2Sagc * Also fill out the struct url_stat.
75*0920b4f2Sagc */
76*0920b4f2Sagc FILE *
fetchXGet(struct url * URL,struct url_stat * us,const char * flags)77*0920b4f2Sagc fetchXGet(struct url *URL, struct url_stat *us, const char *flags)
78*0920b4f2Sagc {
79*0920b4f2Sagc if (us != NULL) {
80*0920b4f2Sagc us->size = -1;
81*0920b4f2Sagc us->atime = us->mtime = 0;
82*0920b4f2Sagc }
83*0920b4f2Sagc if (strcasecmp(URL->scheme, SCHEME_FILE) == 0)
84*0920b4f2Sagc return (fetchXGetFile(URL, us, flags));
85*0920b4f2Sagc else if (strcasecmp(URL->scheme, SCHEME_FTP) == 0)
86*0920b4f2Sagc return (fetchXGetFTP(URL, us, flags));
87*0920b4f2Sagc else if (strcasecmp(URL->scheme, SCHEME_HTTP) == 0)
88*0920b4f2Sagc return (fetchXGetHTTP(URL, us, flags));
89*0920b4f2Sagc else if (strcasecmp(URL->scheme, SCHEME_HTTPS) == 0)
90*0920b4f2Sagc return (fetchXGetHTTP(URL, us, flags));
91*0920b4f2Sagc _url_seterr(URL_BAD_SCHEME);
92*0920b4f2Sagc return (NULL);
93*0920b4f2Sagc }
94*0920b4f2Sagc
95*0920b4f2Sagc /*
96*0920b4f2Sagc * Select the appropriate protocol for the URL scheme, and return a
97*0920b4f2Sagc * read-only stream connected to the document referenced by the URL.
98*0920b4f2Sagc */
99*0920b4f2Sagc FILE *
fetchGet(struct url * URL,const char * flags)100*0920b4f2Sagc fetchGet(struct url *URL, const char *flags)
101*0920b4f2Sagc {
102*0920b4f2Sagc return (fetchXGet(URL, NULL, flags));
103*0920b4f2Sagc }
104*0920b4f2Sagc
105*0920b4f2Sagc /*
106*0920b4f2Sagc * Select the appropriate protocol for the URL scheme, and return a
107*0920b4f2Sagc * write-only stream connected to the document referenced by the URL.
108*0920b4f2Sagc */
109*0920b4f2Sagc FILE *
fetchPut(struct url * URL,const char * flags)110*0920b4f2Sagc fetchPut(struct url *URL, const char *flags)
111*0920b4f2Sagc {
112*0920b4f2Sagc if (strcasecmp(URL->scheme, SCHEME_FILE) == 0)
113*0920b4f2Sagc return (fetchPutFile(URL, flags));
114*0920b4f2Sagc else if (strcasecmp(URL->scheme, SCHEME_FTP) == 0)
115*0920b4f2Sagc return (fetchPutFTP(URL, flags));
116*0920b4f2Sagc else if (strcasecmp(URL->scheme, SCHEME_HTTP) == 0)
117*0920b4f2Sagc return (fetchPutHTTP(URL, flags));
118*0920b4f2Sagc else if (strcasecmp(URL->scheme, SCHEME_HTTPS) == 0)
119*0920b4f2Sagc return (fetchPutHTTP(URL, flags));
120*0920b4f2Sagc _url_seterr(URL_BAD_SCHEME);
121*0920b4f2Sagc return (NULL);
122*0920b4f2Sagc }
123*0920b4f2Sagc
124*0920b4f2Sagc /*
125*0920b4f2Sagc * Select the appropriate protocol for the URL scheme, and return the
126*0920b4f2Sagc * size of the document referenced by the URL if it exists.
127*0920b4f2Sagc */
128*0920b4f2Sagc int
fetchStat(struct url * URL,struct url_stat * us,const char * flags)129*0920b4f2Sagc fetchStat(struct url *URL, struct url_stat *us, const char *flags)
130*0920b4f2Sagc {
131*0920b4f2Sagc if (us != NULL) {
132*0920b4f2Sagc us->size = -1;
133*0920b4f2Sagc us->atime = us->mtime = 0;
134*0920b4f2Sagc }
135*0920b4f2Sagc if (strcasecmp(URL->scheme, SCHEME_FILE) == 0)
136*0920b4f2Sagc return (fetchStatFile(URL, us, flags));
137*0920b4f2Sagc else if (strcasecmp(URL->scheme, SCHEME_FTP) == 0)
138*0920b4f2Sagc return (fetchStatFTP(URL, us, flags));
139*0920b4f2Sagc else if (strcasecmp(URL->scheme, SCHEME_HTTP) == 0)
140*0920b4f2Sagc return (fetchStatHTTP(URL, us, flags));
141*0920b4f2Sagc else if (strcasecmp(URL->scheme, SCHEME_HTTPS) == 0)
142*0920b4f2Sagc return (fetchStatHTTP(URL, us, flags));
143*0920b4f2Sagc _url_seterr(URL_BAD_SCHEME);
144*0920b4f2Sagc return (-1);
145*0920b4f2Sagc }
146*0920b4f2Sagc
147*0920b4f2Sagc /*
148*0920b4f2Sagc * Select the appropriate protocol for the URL scheme, and return a
149*0920b4f2Sagc * list of files in the directory pointed to by the URL.
150*0920b4f2Sagc */
151*0920b4f2Sagc struct url_ent *
fetchList(struct url * URL,const char * flags)152*0920b4f2Sagc fetchList(struct url *URL, const char *flags)
153*0920b4f2Sagc {
154*0920b4f2Sagc if (strcasecmp(URL->scheme, SCHEME_FILE) == 0)
155*0920b4f2Sagc return (fetchListFile(URL, flags));
156*0920b4f2Sagc else if (strcasecmp(URL->scheme, SCHEME_FTP) == 0)
157*0920b4f2Sagc return (fetchListFTP(URL, flags));
158*0920b4f2Sagc else if (strcasecmp(URL->scheme, SCHEME_HTTP) == 0)
159*0920b4f2Sagc return (fetchListHTTP(URL, flags));
160*0920b4f2Sagc else if (strcasecmp(URL->scheme, SCHEME_HTTPS) == 0)
161*0920b4f2Sagc return (fetchListHTTP(URL, flags));
162*0920b4f2Sagc _url_seterr(URL_BAD_SCHEME);
163*0920b4f2Sagc return (NULL);
164*0920b4f2Sagc }
165*0920b4f2Sagc
166*0920b4f2Sagc /*
167*0920b4f2Sagc * Attempt to parse the given URL; if successful, call fetchXGet().
168*0920b4f2Sagc */
169*0920b4f2Sagc FILE *
fetchXGetURL(const char * URL,struct url_stat * us,const char * flags)170*0920b4f2Sagc fetchXGetURL(const char *URL, struct url_stat *us, const char *flags)
171*0920b4f2Sagc {
172*0920b4f2Sagc struct url *u;
173*0920b4f2Sagc FILE *f;
174*0920b4f2Sagc
175*0920b4f2Sagc if ((u = fetchParseURL(URL)) == NULL)
176*0920b4f2Sagc return (NULL);
177*0920b4f2Sagc
178*0920b4f2Sagc f = fetchXGet(u, us, flags);
179*0920b4f2Sagc
180*0920b4f2Sagc fetchFreeURL(u);
181*0920b4f2Sagc return (f);
182*0920b4f2Sagc }
183*0920b4f2Sagc
184*0920b4f2Sagc /*
185*0920b4f2Sagc * Attempt to parse the given URL; if successful, call fetchGet().
186*0920b4f2Sagc */
187*0920b4f2Sagc FILE *
fetchGetURL(const char * URL,const char * flags)188*0920b4f2Sagc fetchGetURL(const char *URL, const char *flags)
189*0920b4f2Sagc {
190*0920b4f2Sagc return (fetchXGetURL(URL, NULL, flags));
191*0920b4f2Sagc }
192*0920b4f2Sagc
193*0920b4f2Sagc /*
194*0920b4f2Sagc * Attempt to parse the given URL; if successful, call fetchPut().
195*0920b4f2Sagc */
196*0920b4f2Sagc FILE *
fetchPutURL(const char * URL,const char * flags)197*0920b4f2Sagc fetchPutURL(const char *URL, const char *flags)
198*0920b4f2Sagc {
199*0920b4f2Sagc struct url *u;
200*0920b4f2Sagc FILE *f;
201*0920b4f2Sagc
202*0920b4f2Sagc if ((u = fetchParseURL(URL)) == NULL)
203*0920b4f2Sagc return (NULL);
204*0920b4f2Sagc
205*0920b4f2Sagc f = fetchPut(u, flags);
206*0920b4f2Sagc
207*0920b4f2Sagc fetchFreeURL(u);
208*0920b4f2Sagc return (f);
209*0920b4f2Sagc }
210*0920b4f2Sagc
211*0920b4f2Sagc /*
212*0920b4f2Sagc * Attempt to parse the given URL; if successful, call fetchStat().
213*0920b4f2Sagc */
214*0920b4f2Sagc int
fetchStatURL(const char * URL,struct url_stat * us,const char * flags)215*0920b4f2Sagc fetchStatURL(const char *URL, struct url_stat *us, const char *flags)
216*0920b4f2Sagc {
217*0920b4f2Sagc struct url *u;
218*0920b4f2Sagc int s;
219*0920b4f2Sagc
220*0920b4f2Sagc if ((u = fetchParseURL(URL)) == NULL)
221*0920b4f2Sagc return (-1);
222*0920b4f2Sagc
223*0920b4f2Sagc s = fetchStat(u, us, flags);
224*0920b4f2Sagc
225*0920b4f2Sagc fetchFreeURL(u);
226*0920b4f2Sagc return (s);
227*0920b4f2Sagc }
228*0920b4f2Sagc
229*0920b4f2Sagc /*
230*0920b4f2Sagc * Attempt to parse the given URL; if successful, call fetchList().
231*0920b4f2Sagc */
232*0920b4f2Sagc struct url_ent *
fetchListURL(const char * URL,const char * flags)233*0920b4f2Sagc fetchListURL(const char *URL, const char *flags)
234*0920b4f2Sagc {
235*0920b4f2Sagc struct url *u;
236*0920b4f2Sagc struct url_ent *ue;
237*0920b4f2Sagc
238*0920b4f2Sagc if ((u = fetchParseURL(URL)) == NULL)
239*0920b4f2Sagc return (NULL);
240*0920b4f2Sagc
241*0920b4f2Sagc ue = fetchList(u, flags);
242*0920b4f2Sagc
243*0920b4f2Sagc fetchFreeURL(u);
244*0920b4f2Sagc return (ue);
245*0920b4f2Sagc }
246*0920b4f2Sagc
247*0920b4f2Sagc /*
248*0920b4f2Sagc * Make a URL
249*0920b4f2Sagc */
250*0920b4f2Sagc struct url *
fetchMakeURL(const char * scheme,const char * host,int port,const char * doc,const char * user,const char * pwd)251*0920b4f2Sagc fetchMakeURL(const char *scheme, const char *host, int port, const char *doc,
252*0920b4f2Sagc const char *user, const char *pwd)
253*0920b4f2Sagc {
254*0920b4f2Sagc struct url *u;
255*0920b4f2Sagc
256*0920b4f2Sagc if (!scheme || (!host && !doc)) {
257*0920b4f2Sagc _url_seterr(URL_MALFORMED);
258*0920b4f2Sagc return (NULL);
259*0920b4f2Sagc }
260*0920b4f2Sagc
261*0920b4f2Sagc if (port < 0 || port > 65535) {
262*0920b4f2Sagc _url_seterr(URL_BAD_PORT);
263*0920b4f2Sagc return (NULL);
264*0920b4f2Sagc }
265*0920b4f2Sagc
266*0920b4f2Sagc /* allocate struct url */
267*0920b4f2Sagc if ((u = calloc(1, sizeof(*u))) == NULL) {
268*0920b4f2Sagc _fetch_syserr();
269*0920b4f2Sagc return (NULL);
270*0920b4f2Sagc }
271*0920b4f2Sagc
272*0920b4f2Sagc if ((u->doc = strdup(doc ? doc : "/")) == NULL) {
273*0920b4f2Sagc _fetch_syserr();
274*0920b4f2Sagc free(u);
275*0920b4f2Sagc return (NULL);
276*0920b4f2Sagc }
277*0920b4f2Sagc
278*0920b4f2Sagc #define seturl(x) snprintf(u->x, sizeof(u->x), "%s", x)
279*0920b4f2Sagc seturl(scheme);
280*0920b4f2Sagc seturl(host);
281*0920b4f2Sagc seturl(user);
282*0920b4f2Sagc seturl(pwd);
283*0920b4f2Sagc #undef seturl
284*0920b4f2Sagc u->port = port;
285*0920b4f2Sagc
286*0920b4f2Sagc return (u);
287*0920b4f2Sagc }
288*0920b4f2Sagc
289*0920b4f2Sagc /*
290*0920b4f2Sagc * Split an URL into components. URL syntax is:
291*0920b4f2Sagc * [method:/][/[user[:pwd]@]host[:port]/][document]
292*0920b4f2Sagc * This almost, but not quite, RFC1738 URL syntax.
293*0920b4f2Sagc */
294*0920b4f2Sagc struct url *
fetchParseURL(const char * URL)295*0920b4f2Sagc fetchParseURL(const char *URL)
296*0920b4f2Sagc {
297*0920b4f2Sagc char *doc;
298*0920b4f2Sagc const char *p, *q;
299*0920b4f2Sagc struct url *u;
300*0920b4f2Sagc int i;
301*0920b4f2Sagc
302*0920b4f2Sagc /* allocate struct url */
303*0920b4f2Sagc if ((u = calloc(1, sizeof(*u))) == NULL) {
304*0920b4f2Sagc _fetch_syserr();
305*0920b4f2Sagc return (NULL);
306*0920b4f2Sagc }
307*0920b4f2Sagc
308*0920b4f2Sagc /* scheme name */
309*0920b4f2Sagc if ((p = strstr(URL, ":/")) != NULL) {
310*0920b4f2Sagc snprintf(u->scheme, URL_SCHEMELEN+1,
311*0920b4f2Sagc "%.*s", (int)(p - URL), URL);
312*0920b4f2Sagc URL = ++p;
313*0920b4f2Sagc /*
314*0920b4f2Sagc * Only one slash: no host, leave slash as part of document
315*0920b4f2Sagc * Two slashes: host follows, strip slashes
316*0920b4f2Sagc */
317*0920b4f2Sagc if (URL[1] == '/')
318*0920b4f2Sagc URL = (p += 2);
319*0920b4f2Sagc } else {
320*0920b4f2Sagc p = URL;
321*0920b4f2Sagc }
322*0920b4f2Sagc if (!*URL || *URL == '/' || *URL == '.' ||
323*0920b4f2Sagc (u->scheme[0] == '\0' &&
324*0920b4f2Sagc strchr(URL, '/') == NULL && strchr(URL, ':') == NULL))
325*0920b4f2Sagc goto nohost;
326*0920b4f2Sagc
327*0920b4f2Sagc p = strpbrk(URL, "/@");
328*0920b4f2Sagc if (p && *p == '@') {
329*0920b4f2Sagc /* username */
330*0920b4f2Sagc for (q = URL, i = 0; (*q != ':') && (*q != '@'); q++)
331*0920b4f2Sagc if (i < URL_USERLEN)
332*0920b4f2Sagc u->user[i++] = *q;
333*0920b4f2Sagc
334*0920b4f2Sagc /* password */
335*0920b4f2Sagc if (*q == ':')
336*0920b4f2Sagc for (q++, i = 0; (*q != ':') && (*q != '@'); q++)
337*0920b4f2Sagc if (i < URL_PWDLEN)
338*0920b4f2Sagc u->pwd[i++] = *q;
339*0920b4f2Sagc
340*0920b4f2Sagc p++;
341*0920b4f2Sagc } else {
342*0920b4f2Sagc p = URL;
343*0920b4f2Sagc }
344*0920b4f2Sagc
345*0920b4f2Sagc /* hostname */
346*0920b4f2Sagc #ifdef INET6
347*0920b4f2Sagc if (*p == '[' && (q = strchr(p + 1, ']')) != NULL &&
348*0920b4f2Sagc (*++q == '\0' || *q == '/' || *q == ':')) {
349*0920b4f2Sagc if ((i = q - p - 2) > MAXHOSTNAMELEN)
350*0920b4f2Sagc i = MAXHOSTNAMELEN;
351*0920b4f2Sagc strncpy(u->host, ++p, i);
352*0920b4f2Sagc p = q;
353*0920b4f2Sagc } else
354*0920b4f2Sagc #endif
355*0920b4f2Sagc for (i = 0; *p && (*p != '/') && (*p != ':'); p++)
356*0920b4f2Sagc if (i < MAXHOSTNAMELEN)
357*0920b4f2Sagc u->host[i++] = *p;
358*0920b4f2Sagc
359*0920b4f2Sagc /* port */
360*0920b4f2Sagc if (*p == ':') {
361*0920b4f2Sagc for (q = ++p; *q && (*q != '/'); q++)
362*0920b4f2Sagc if (isdigit((unsigned)*q))
363*0920b4f2Sagc u->port = u->port * 10 + (*q - '0');
364*0920b4f2Sagc else {
365*0920b4f2Sagc /* invalid port */
366*0920b4f2Sagc _url_seterr(URL_BAD_PORT);
367*0920b4f2Sagc goto ouch;
368*0920b4f2Sagc }
369*0920b4f2Sagc p = q;
370*0920b4f2Sagc }
371*0920b4f2Sagc
372*0920b4f2Sagc nohost:
373*0920b4f2Sagc /* document */
374*0920b4f2Sagc if (!*p)
375*0920b4f2Sagc p = "/";
376*0920b4f2Sagc
377*0920b4f2Sagc if (strcasecmp(u->scheme, SCHEME_HTTP) == 0 ||
378*0920b4f2Sagc strcasecmp(u->scheme, SCHEME_HTTPS) == 0) {
379*0920b4f2Sagc const char hexnums[] = "0123456789abcdef";
380*0920b4f2Sagc
381*0920b4f2Sagc /* percent-escape whitespace. */
382*0920b4f2Sagc if ((doc = malloc(strlen(p) * 3 + 1)) == NULL) {
383*0920b4f2Sagc _fetch_syserr();
384*0920b4f2Sagc goto ouch;
385*0920b4f2Sagc }
386*0920b4f2Sagc u->doc = doc;
387*0920b4f2Sagc while (*p != '\0') {
388*0920b4f2Sagc if (!isspace((unsigned)*p)) {
389*0920b4f2Sagc *doc++ = *p++;
390*0920b4f2Sagc } else {
391*0920b4f2Sagc *doc++ = '%';
392*0920b4f2Sagc *doc++ = hexnums[((unsigned int)*p) >> 4];
393*0920b4f2Sagc *doc++ = hexnums[((unsigned int)*p) & 0xf];
394*0920b4f2Sagc p++;
395*0920b4f2Sagc }
396*0920b4f2Sagc }
397*0920b4f2Sagc *doc = '\0';
398*0920b4f2Sagc } else if ((u->doc = strdup(p)) == NULL) {
399*0920b4f2Sagc _fetch_syserr();
400*0920b4f2Sagc goto ouch;
401*0920b4f2Sagc }
402*0920b4f2Sagc
403*0920b4f2Sagc DEBUG(fprintf(stderr,
404*0920b4f2Sagc "scheme: [%s]\n"
405*0920b4f2Sagc "user: [%s]\n"
406*0920b4f2Sagc "password: [%s]\n"
407*0920b4f2Sagc "host: [%s]\n"
408*0920b4f2Sagc "port: [%d]\n"
409*0920b4f2Sagc "document: [%s]\n",
410*0920b4f2Sagc u->scheme, u->user, u->pwd,
411*0920b4f2Sagc u->host, u->port, u->doc));
412*0920b4f2Sagc
413*0920b4f2Sagc return (u);
414*0920b4f2Sagc
415*0920b4f2Sagc ouch:
416*0920b4f2Sagc free(u);
417*0920b4f2Sagc return (NULL);
418*0920b4f2Sagc }
419*0920b4f2Sagc
420*0920b4f2Sagc /*
421*0920b4f2Sagc * Free a URL
422*0920b4f2Sagc */
423*0920b4f2Sagc void
fetchFreeURL(struct url * u)424*0920b4f2Sagc fetchFreeURL(struct url *u)
425*0920b4f2Sagc {
426*0920b4f2Sagc free(u->doc);
427*0920b4f2Sagc free(u);
428*0920b4f2Sagc }
429