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