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