fetch.c revision 60924
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 60924 2000-05-25 16:24:31Z 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; 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 56static 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 */ 70FILE * 71fetchGet(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 */ 95FILE * 96fetchPut(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 */ 120int 121fetchStat(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 */ 145struct url_ent * 146fetchList(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 */ 169FILE * 170fetchGetURL(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 */ 188FILE * 189fetchPutURL(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 */ 206int 207fetchStatURL(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 */ 224struct url_ent * 225fetchListURL(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 * Split an URL into components. URL syntax is: 241 * method:[//[user[:pwd]@]host[:port]]/[document] 242 * This almost, but not quite, RFC1738 URL syntax. 243 */ 244struct url * 245fetchParseURL(char *URL) 246{ 247 char *p, *q; 248 struct url *u; 249 int i; 250 251 /* allocate struct url */ 252 if ((u = calloc(1, sizeof *u)) == NULL) { 253 errno = ENOMEM; 254 _fetch_syserr(); 255 return NULL; 256 } 257 258 /* scheme name */ 259 for (i = 0; *URL && (*URL != ':'); URL++) 260 if (i < URL_SCHEMELEN) 261 u->scheme[i++] = *URL; 262 if (!URL[0] || (URL[1] != '/')) { 263 _url_seterr(URL_BAD_SCHEME); 264 goto ouch; 265 } 266 else URL++; 267 if (URL[1] != '/') { 268 p = URL; 269 goto nohost; 270 } 271 else URL += 2; 272 273 p = strpbrk(URL, "/@"); 274 if (p && *p == '@') { 275 /* username */ 276 for (q = URL, i = 0; (*q != ':') && (*q != '@'); q++) 277 if (i < URL_USERLEN) 278 u->user[i++] = *q; 279 280 /* password */ 281 if (*q == ':') 282 for (q++, i = 0; (*q != ':') && (*q != '@'); q++) 283 if (i < URL_PWDLEN) 284 u->pwd[i++] = *q; 285 286 p++; 287 } else p = URL; 288 289 /* hostname */ 290#ifdef INET6 291 if (*p == '[' && (q = strchr(p + 1, ']')) != NULL && 292 (*++q == '\0' || *q == '/' || *q == ':')) { 293 if ((i = q - p - 2) > MAXHOSTNAMELEN) 294 i = MAXHOSTNAMELEN; 295 strncpy(u->host, ++p, i); 296 p = q; 297 } else 298#endif 299 for (i = 0; *p && (*p != '/') && (*p != ':'); p++) 300 if (i < MAXHOSTNAMELEN) 301 u->host[i++] = *p; 302 303 /* port */ 304 if (*p == ':') { 305 for (q = ++p; *q && (*q != '/'); q++) 306 if (isdigit(*q)) 307 u->port = u->port * 10 + (*q - '0'); 308 else { 309 /* invalid port */ 310 _url_seterr(URL_BAD_PORT); 311 goto ouch; 312 } 313 while (*p && (*p != '/')) 314 p++; 315 } 316 317nohost: 318 /* document */ 319 if (!*p) 320 p = "/"; 321 322 if ((u->doc = strdup(p)) == NULL) { 323 errno = ENOMEM; 324 _fetch_syserr(); 325 goto ouch; 326 } 327 328 DEBUG(fprintf(stderr, 329 "scheme: [\033[1m%s\033[m]\n" 330 "user: [\033[1m%s\033[m]\n" 331 "password: [\033[1m%s\033[m]\n" 332 "host: [\033[1m%s\033[m]\n" 333 "port: [\033[1m%d\033[m]\n" 334 "document: [\033[1m%s\033[m]\n", 335 u->scheme, u->user, u->pwd, 336 u->host, u->port, u->doc)); 337 338 return u; 339 340ouch: 341 free(u); 342 return NULL; 343} 344 345/* 346 * Free a URL 347 */ 348void 349fetchFreeURL(struct url *u) 350{ 351 free(u->doc); 352 free(u); 353} 354