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