fetch.c revision 63340
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: head/lib/libfetch/fetch.c 63340 2000-07-17 21:25:00Z des $ 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 43int fetchLastErrCode; 44char fetchLastErrString[MAXERRSTRING]; 45int fetchTimeout; 46int 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 57static 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 */ 72FILE * 73fetchXGet(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 */ 97FILE * 98fetchGet(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 */ 107FILE * 108fetchPut(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 */ 132int 133fetchStat(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 */ 157struct url_ent * 158fetchList(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 */ 181FILE * 182fetchXGetURL(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 */ 199FILE * 200fetchGetURL(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 */ 208FILE * 209fetchPutURL(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 */ 226int 227fetchStatURL(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 */ 244struct url_ent * 245fetchListURL(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 */ 262struct url * 263fetchMakeURL(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 */ 306struct url * 307fetchParseURL(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 378nohost: 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 400ouch: 401 free(u); 402 return NULL; 403} 404 405/* 406 * Free a URL 407 */ 408void 409fetchFreeURL(struct url *u) 410{ 411 free(u->doc); 412 free(u); 413} 414