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 int 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 57 static 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 */ 72 FILE * 73 fetchXGet(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 */ 97 FILE * 98 fetchGet(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 */ 107 FILE * 108 fetchPut(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 */ 132 int 133 fetchStat(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 */ 157 struct url_ent * 158 fetchList(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 */ 181 FILE * 182 fetchXGetURL(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 */ 199 FILE * 200 fetchGetURL(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 */ 208 FILE * 209 fetchPutURL(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 */ 226 int 227 fetchStatURL(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 */ 244 struct url_ent * 245 fetchListURL(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 */ 262 struct url * 263 fetchMakeURL(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 */ 306 struct url * 307 fetchParseURL(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 378 nohost: 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 400 ouch: 401 free(u); 402 return NULL; 403 } 404 405 /* 406 * Free a URL 407 */ 408 void 409 fetchFreeURL(struct url *u) 410 { 411 free(u->doc); 412 free(u); 413 } 414