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