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