xref: /freebsd/lib/libfetch/fetch.c (revision 2ad872c5794e4c26fdf6ed219ad3f09ca0d5304a)
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  *	$Id: fetch.c,v 1.7 1998/12/16 10:24:54 des Exp $
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 
45 
46 /*** Local data **************************************************************/
47 
48 /*
49  * Error messages for parser errors
50  */
51 #define URL_MALFORMED		1
52 #define URL_BAD_SCHEME		2
53 #define URL_BAD_PORT		3
54 static struct fetcherr _url_errlist[] = {
55     { URL_MALFORMED,	FETCH_URL,	"Malformed URL" },
56     { URL_BAD_SCHEME,	FETCH_URL,	"Invalid URL scheme" },
57     { URL_BAD_PORT,	FETCH_URL,	"Invalid server port" },
58     { -1,		FETCH_UNKNOWN,	"Unknown parser error" }
59 };
60 
61 
62 /*** Public API **************************************************************/
63 
64 /*
65  * Select the appropriate protocol for the URL scheme, and return a
66  * read-only stream connected to the document referenced by the URL.
67  */
68 FILE *
69 fetchGet(struct url *URL, char *flags)
70 {
71     if (strcasecmp(URL->scheme, "file") == 0)
72 	return fetchGetFile(URL, flags);
73     else if (strcasecmp(URL->scheme, "http") == 0)
74 	return fetchGetHTTP(URL, flags);
75     else if (strcasecmp(URL->scheme, "ftp") == 0)
76 	return fetchGetFTP(URL, flags);
77     else {
78 	_url_seterr(URL_BAD_SCHEME);
79 	return NULL;
80     }
81 }
82 
83 /*
84  * Select the appropriate protocol for the URL scheme, and return a
85  * write-only stream connected to the document referenced by the URL.
86  */
87 FILE *
88 fetchPut(struct url *URL, char *flags)
89 {
90     if (strcasecmp(URL->scheme, "file") == 0)
91 	return fetchPutFile(URL, flags);
92     else if (strcasecmp(URL->scheme, "http") == 0)
93 	return fetchPutHTTP(URL, flags);
94     else if (strcasecmp(URL->scheme, "ftp") == 0)
95 	return fetchPutFTP(URL, flags);
96     else {
97 	_url_seterr(URL_BAD_SCHEME);
98 	return NULL;
99     }
100 }
101 
102 /*
103  * Select the appropriate protocol for the URL scheme, and return the
104  * size of the document referenced by the URL if it exists.
105  */
106 int
107 fetchStat(struct url *URL, struct url_stat *us, char *flags)
108 {
109     if (strcasecmp(URL->scheme, "file") == 0)
110 	return fetchStatFile(URL, us, flags);
111     else if (strcasecmp(URL->scheme, "http") == 0)
112 	return fetchStatHTTP(URL, us, flags);
113     else if (strcasecmp(URL->scheme, "ftp") == 0)
114 	return fetchStatFTP(URL, us, flags);
115     else {
116 	_url_seterr(URL_BAD_SCHEME);
117 	return -1;
118     }
119 }
120 
121 /*
122  * Select the appropriate protocol for the URL scheme, and return a
123  * list of files in the directory pointed to by the URL.
124  */
125 struct url_ent *
126 fetchList(struct url *URL, char *flags)
127 {
128     if (strcasecmp(URL->scheme, "file") == 0)
129 	return fetchListFile(URL, flags);
130     else if (strcasecmp(URL->scheme, "http") == 0)
131 	return fetchListHTTP(URL, flags);
132     else if (strcasecmp(URL->scheme, "ftp") == 0)
133 	return fetchListFTP(URL, flags);
134     else {
135 	_url_seterr(URL_BAD_SCHEME);
136 	return NULL;
137     }
138 }
139 
140 /*
141  * Attempt to parse the given URL; if successful, call fetchGet().
142  */
143 FILE *
144 fetchGetURL(char *URL, char *flags)
145 {
146     struct url *u;
147     FILE *f;
148 
149     if ((u = fetchParseURL(URL)) == NULL)
150 	return NULL;
151 
152     f = fetchGet(u, flags);
153 
154     free(u);
155     return f;
156 }
157 
158 
159 /*
160  * Attempt to parse the given URL; if successful, call fetchPut().
161  */
162 FILE *
163 fetchPutURL(char *URL, char *flags)
164 {
165     struct url *u;
166     FILE *f;
167 
168     if ((u = fetchParseURL(URL)) == NULL)
169 	return NULL;
170 
171     f = fetchPut(u, flags);
172 
173     free(u);
174     return f;
175 }
176 
177 /*
178  * Attempt to parse the given URL; if successful, call fetchStat().
179  */
180 int
181 fetchStatURL(char *URL, struct url_stat *us, char *flags)
182 {
183     struct url *u;
184     int s;
185 
186     if ((u = fetchParseURL(URL)) == NULL)
187 	return -1;
188 
189     s = fetchStat(u, us, flags);
190 
191     free(u);
192     return s;
193 }
194 
195 /*
196  * Attempt to parse the given URL; if successful, call fetchList().
197  */
198 struct url_ent *
199 fetchListURL(char *URL, char *flags)
200 {
201     struct url *u;
202     struct url_ent *ue;
203 
204     if ((u = fetchParseURL(URL)) == NULL)
205 	return NULL;
206 
207     ue = fetchList(u, flags);
208 
209     free(u);
210     return ue;
211 }
212 
213 /*
214  * Split an URL into components. URL syntax is:
215  * method:[//[user[:pwd]@]host[:port]]/[document]
216  * This almost, but not quite, RFC1738 URL syntax.
217  */
218 struct url *
219 fetchParseURL(char *URL)
220 {
221     char *p, *q;
222     struct url *u;
223     int i;
224 
225     /* allocate struct url */
226     if ((u = calloc(1, sizeof(struct url))) == NULL) {
227 	errno = ENOMEM;
228 	_fetch_syserr();
229 	return NULL;
230     }
231 
232     /* scheme name */
233     for (i = 0; *URL && (*URL != ':'); URL++)
234 	if (i < URL_SCHEMELEN)
235 	    u->scheme[i++] = *URL;
236     if (!URL[0] || (URL[1] != '/')) {
237 	_url_seterr(URL_BAD_SCHEME);
238 	goto ouch;
239     }
240     else URL++;
241     if (URL[1] != '/') {
242 	p = URL;
243 	goto nohost;
244     }
245     else URL += 2;
246 
247     p = strpbrk(URL, "/@");
248     if (p && *p == '@') {
249 	/* username */
250 	for (q = URL, i = 0; (*q != ':') && (*q != '@'); q++)
251 	    if (i < URL_USERLEN)
252 		u->user[i++] = *q;
253 
254 	/* password */
255 	if (*q == ':')
256 	    for (q++, i = 0; (*q != ':') && (*q != '@'); q++)
257 		if (i < URL_PWDLEN)
258 		    u->pwd[i++] = *q;
259 
260 	p++;
261     } else p = URL;
262 
263     /* hostname */
264     for (i = 0; *p && (*p != '/') && (*p != ':'); p++)
265 	if (i < MAXHOSTNAMELEN)
266 	    u->host[i++] = *p;
267 
268     /* port */
269     if (*p == ':') {
270 	for (q = ++p; *q && (*q != '/'); q++)
271 	    if (isdigit(*q))
272 		u->port = u->port * 10 + (*q - '0');
273 	    else {
274 		/* invalid port */
275 		_url_seterr(URL_BAD_PORT);
276 		goto ouch;
277 	    }
278 	while (*p && (*p != '/'))
279 	    p++;
280     }
281 
282 nohost:
283     /* document */
284     if (*p) {
285 	struct url *t;
286 	t = realloc(u, sizeof(*u)+strlen(p)-1);
287 	if (t == NULL) {
288 	    errno = ENOMEM;
289 	    _fetch_syserr();
290 	    goto ouch;
291 	}
292 	u = t;
293 	strcpy(u->doc, p);
294     } else {
295 	u->doc[0] = '/';
296 	u->doc[1] = 0;
297     }
298 
299     DEBUG(fprintf(stderr,
300 		  "scheme:   [\033[1m%s\033[m]\n"
301 		  "user:     [\033[1m%s\033[m]\n"
302 		  "password: [\033[1m%s\033[m]\n"
303 		  "host:     [\033[1m%s\033[m]\n"
304 		  "port:     [\033[1m%d\033[m]\n"
305 		  "document: [\033[1m%s\033[m]\n",
306 		  u->scheme, u->user, u->pwd,
307 		  u->host, u->port, u->doc));
308 
309     return u;
310 
311 ouch:
312     free(u);
313     return NULL;
314 }
315