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