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