xref: /freebsd/lib/libfetch/fetch.c (revision 144d39d4b818a9d1f2c2fe54d683ad5d149fa067)
1 /*-
2  * Copyright (c) 1998 Dag-Erling Co�dan Sm�rgrav
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  * $FreeBSD$
29  */
30 
31 #include <sys/param.h>
32 #include <sys/errno.h>
33 
34 #include <ctype.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 
39 #include "fetch.h"
40 #include "common.h"
41 
42 
43 int	 fetchLastErrCode;
44 char	 fetchLastErrString[MAXERRSTRING];
45 int	 fetchTimeout;
46 int	 fetchRestartCalls = 1;
47 
48 
49 /*** Local data **************************************************************/
50 
51 /*
52  * Error messages for parser errors
53  */
54 #define URL_MALFORMED		1
55 #define URL_BAD_SCHEME		2
56 #define URL_BAD_PORT		3
57 static struct fetcherr _url_errlist[] = {
58     { URL_MALFORMED,	FETCH_URL,	"Malformed URL" },
59     { URL_BAD_SCHEME,	FETCH_URL,	"Invalid URL scheme" },
60     { URL_BAD_PORT,	FETCH_URL,	"Invalid server port" },
61     { -1,		FETCH_UNKNOWN,	"Unknown parser error" }
62 };
63 
64 
65 /*** Public API **************************************************************/
66 
67 /*
68  * Select the appropriate protocol for the URL scheme, and return a
69  * read-only stream connected to the document referenced by the URL.
70  * Also fill out the struct url_stat.
71  */
72 FILE *
73 fetchXGet(struct url *URL, struct url_stat *us, char *flags)
74 {
75     int direct;
76 
77     direct = (flags && strchr(flags, 'd'));
78     if (strcasecmp(URL->scheme, "file") == 0)
79 	return fetchXGetFile(URL, us, flags);
80     else if (strcasecmp(URL->scheme, "http") == 0)
81 	return fetchXGetHTTP(URL, us, flags);
82     else if (strcasecmp(URL->scheme, "ftp") == 0) {
83 	if (!direct &&
84 	    getenv("FTP_PROXY") == NULL && getenv("HTTP_PROXY") != NULL)
85 	    return fetchXGetHTTP(URL, us, flags);
86 	return fetchXGetFTP(URL, us, flags);
87     } else {
88 	_url_seterr(URL_BAD_SCHEME);
89 	return NULL;
90     }
91 }
92 
93 /*
94  * Select the appropriate protocol for the URL scheme, and return a
95  * read-only stream connected to the document referenced by the URL.
96  */
97 FILE *
98 fetchGet(struct url *URL, char *flags)
99 {
100     return fetchXGet(URL, NULL, flags);
101 }
102 
103 /*
104  * Select the appropriate protocol for the URL scheme, and return a
105  * write-only stream connected to the document referenced by the URL.
106  */
107 FILE *
108 fetchPut(struct url *URL, char *flags)
109 {
110     int direct;
111 
112     direct = (flags && strchr(flags, 'd'));
113     if (strcasecmp(URL->scheme, "file") == 0)
114 	return fetchPutFile(URL, flags);
115     else if (strcasecmp(URL->scheme, "http") == 0)
116 	return fetchPutHTTP(URL, flags);
117     else if (strcasecmp(URL->scheme, "ftp") == 0) {
118 	if (!direct &&
119 	    getenv("FTP_PROXY") == NULL && getenv("HTTP_PROXY") != NULL)
120 	    return fetchPutHTTP(URL, flags);
121 	return fetchPutFTP(URL, flags);
122     } else {
123 	_url_seterr(URL_BAD_SCHEME);
124 	return NULL;
125     }
126 }
127 
128 /*
129  * Select the appropriate protocol for the URL scheme, and return the
130  * size of the document referenced by the URL if it exists.
131  */
132 int
133 fetchStat(struct url *URL, struct url_stat *us, char *flags)
134 {
135     int direct;
136 
137     direct = (flags && strchr(flags, 'd'));
138     if (strcasecmp(URL->scheme, "file") == 0)
139 	return fetchStatFile(URL, us, flags);
140     else if (strcasecmp(URL->scheme, "http") == 0)
141 	return fetchStatHTTP(URL, us, flags);
142     else if (strcasecmp(URL->scheme, "ftp") == 0) {
143 	if (!direct &&
144 	    getenv("FTP_PROXY") == NULL && getenv("HTTP_PROXY") != NULL)
145 	    return fetchStatHTTP(URL, us, flags);
146 	return fetchStatFTP(URL, us, flags);
147     } else {
148 	_url_seterr(URL_BAD_SCHEME);
149 	return -1;
150     }
151 }
152 
153 /*
154  * Select the appropriate protocol for the URL scheme, and return a
155  * list of files in the directory pointed to by the URL.
156  */
157 struct url_ent *
158 fetchList(struct url *URL, char *flags)
159 {
160     int direct;
161 
162     direct = (flags && strchr(flags, 'd'));
163     if (strcasecmp(URL->scheme, "file") == 0)
164 	return fetchListFile(URL, flags);
165     else if (strcasecmp(URL->scheme, "http") == 0)
166 	return fetchListHTTP(URL, flags);
167     else if (strcasecmp(URL->scheme, "ftp") == 0) {
168 	if (!direct &&
169 	    getenv("FTP_PROXY") == NULL && getenv("HTTP_PROXY") != NULL)
170 	    return fetchListHTTP(URL, flags);
171 	return fetchListFTP(URL, flags);
172     } else {
173 	_url_seterr(URL_BAD_SCHEME);
174 	return NULL;
175     }
176 }
177 
178 /*
179  * Attempt to parse the given URL; if successful, call fetchXGet().
180  */
181 FILE *
182 fetchXGetURL(char *URL, struct url_stat *us, char *flags)
183 {
184     struct url *u;
185     FILE *f;
186 
187     if ((u = fetchParseURL(URL)) == NULL)
188 	return NULL;
189 
190     f = fetchXGet(u, us, flags);
191 
192     fetchFreeURL(u);
193     return f;
194 }
195 
196 /*
197  * Attempt to parse the given URL; if successful, call fetchGet().
198  */
199 FILE *
200 fetchGetURL(char *URL, char *flags)
201 {
202     return fetchXGetURL(URL, NULL, flags);
203 }
204 
205 /*
206  * Attempt to parse the given URL; if successful, call fetchPut().
207  */
208 FILE *
209 fetchPutURL(char *URL, char *flags)
210 {
211     struct url *u;
212     FILE *f;
213 
214     if ((u = fetchParseURL(URL)) == NULL)
215 	return NULL;
216 
217     f = fetchPut(u, flags);
218 
219     fetchFreeURL(u);
220     return f;
221 }
222 
223 /*
224  * Attempt to parse the given URL; if successful, call fetchStat().
225  */
226 int
227 fetchStatURL(char *URL, struct url_stat *us, char *flags)
228 {
229     struct url *u;
230     int s;
231 
232     if ((u = fetchParseURL(URL)) == NULL)
233 	return -1;
234 
235     s = fetchStat(u, us, flags);
236 
237     fetchFreeURL(u);
238     return s;
239 }
240 
241 /*
242  * Attempt to parse the given URL; if successful, call fetchList().
243  */
244 struct url_ent *
245 fetchListURL(char *URL, char *flags)
246 {
247     struct url *u;
248     struct url_ent *ue;
249 
250     if ((u = fetchParseURL(URL)) == NULL)
251 	return NULL;
252 
253     ue = fetchList(u, flags);
254 
255     fetchFreeURL(u);
256     return ue;
257 }
258 
259 /*
260  * Make a URL
261  */
262 struct url *
263 fetchMakeURL(char *scheme, char *host, int port, char *doc,
264     char *user, char *pwd)
265 {
266     struct url *u;
267 
268     if (!scheme || (!host && !doc)) {
269 	_url_seterr(URL_MALFORMED);
270 	return NULL;
271     }
272 
273     if (port < 0 || port > 65535) {
274 	_url_seterr(URL_BAD_PORT);
275 	return NULL;
276     }
277 
278     /* allocate struct url */
279     if ((u = calloc(1, sizeof *u)) == NULL) {
280 	_fetch_syserr();
281 	return NULL;
282     }
283 
284     if ((u->doc = strdup(doc ? doc : "/")) == NULL) {
285 	_fetch_syserr();
286 	free(u);
287 	return NULL;
288     }
289 
290 #define seturl(x) snprintf(u->x, sizeof u->x, "%s", x)
291     seturl(scheme);
292     seturl(host);
293     seturl(user);
294     seturl(pwd);
295 #undef seturl
296     u->port = port;
297 
298     return u;
299 }
300 
301 /*
302  * Split an URL into components. URL syntax is:
303  * method:[//[user[:pwd]@]host[:port]]/[document]
304  * This almost, but not quite, RFC1738 URL syntax.
305  */
306 struct url *
307 fetchParseURL(char *URL)
308 {
309     char *p, *q;
310     struct url *u;
311     int i;
312 
313     /* allocate struct url */
314     if ((u = calloc(1, sizeof *u)) == NULL) {
315 	_fetch_syserr();
316 	return NULL;
317     }
318 
319     /* scheme name */
320     for (i = 0; *URL && (*URL != ':'); URL++)
321 	if (i < URL_SCHEMELEN)
322 	    u->scheme[i++] = *URL;
323     if (!URL[0] || (URL[1] != '/')) {
324 	_url_seterr(URL_BAD_SCHEME);
325 	goto ouch;
326     }
327     else URL++;
328     if (URL[1] != '/') {
329 	p = URL;
330 	goto nohost;
331     }
332     else URL += 2;
333 
334     p = strpbrk(URL, "/@");
335     if (p && *p == '@') {
336 	/* username */
337 	for (q = URL, i = 0; (*q != ':') && (*q != '@'); q++)
338 	    if (i < URL_USERLEN)
339 		u->user[i++] = *q;
340 
341 	/* password */
342 	if (*q == ':')
343 	    for (q++, i = 0; (*q != ':') && (*q != '@'); q++)
344 		if (i < URL_PWDLEN)
345 		    u->pwd[i++] = *q;
346 
347 	p++;
348     } else p = URL;
349 
350     /* hostname */
351 #ifdef INET6
352     if (*p == '[' && (q = strchr(p + 1, ']')) != NULL &&
353 	(*++q == '\0' || *q == '/' || *q == ':')) {
354 	if ((i = q - p - 2) > MAXHOSTNAMELEN)
355 	    i = MAXHOSTNAMELEN;
356 	strncpy(u->host, ++p, i);
357 	p = q;
358     } else
359 #endif
360 	for (i = 0; *p && (*p != '/') && (*p != ':'); p++)
361 	    if (i < MAXHOSTNAMELEN)
362 		u->host[i++] = *p;
363 
364     /* port */
365     if (*p == ':') {
366 	for (q = ++p; *q && (*q != '/'); q++)
367 	    if (isdigit(*q))
368 		u->port = u->port * 10 + (*q - '0');
369 	    else {
370 		/* invalid port */
371 		_url_seterr(URL_BAD_PORT);
372 		goto ouch;
373 	    }
374 	while (*p && (*p != '/'))
375 	    p++;
376     }
377 
378 nohost:
379     /* document */
380     if (!*p)
381 	p = "/";
382 
383     if ((u->doc = strdup(p)) == NULL) {
384 	_fetch_syserr();
385 	goto ouch;
386     }
387 
388     DEBUG(fprintf(stderr,
389 		  "scheme:   [\033[1m%s\033[m]\n"
390 		  "user:     [\033[1m%s\033[m]\n"
391 		  "password: [\033[1m%s\033[m]\n"
392 		  "host:     [\033[1m%s\033[m]\n"
393 		  "port:     [\033[1m%d\033[m]\n"
394 		  "document: [\033[1m%s\033[m]\n",
395 		  u->scheme, u->user, u->pwd,
396 		  u->host, u->port, u->doc));
397 
398     return u;
399 
400 ouch:
401     free(u);
402     return NULL;
403 }
404 
405 /*
406  * Free a URL
407  */
408 void
409 fetchFreeURL(struct url *u)
410 {
411     free(u->doc);
412     free(u);
413 }
414