1 1.2 wiz /* $NetBSD: fetch.c,v 1.2 2026/04/11 10:42:32 wiz Exp $ */ 2 1.1 joerg /*- 3 1.2 wiz * Copyright (c) 1998-2004 Dag-Erling Codan Smrgrav 4 1.1 joerg * Copyright (c) 2008 Joerg Sonnenberger <joerg (at) NetBSD.org> 5 1.1 joerg * All rights reserved. 6 1.1 joerg * 7 1.1 joerg * Redistribution and use in source and binary forms, with or without 8 1.1 joerg * modification, are permitted provided that the following conditions 9 1.1 joerg * are met: 10 1.1 joerg * 1. Redistributions of source code must retain the above copyright 11 1.1 joerg * notice, this list of conditions and the following disclaimer 12 1.1 joerg * in this position and unchanged. 13 1.1 joerg * 2. Redistributions in binary form must reproduce the above copyright 14 1.1 joerg * notice, this list of conditions and the following disclaimer in the 15 1.1 joerg * documentation and/or other materials provided with the distribution. 16 1.1 joerg * 3. The name of the author may not be used to endorse or promote products 17 1.1 joerg * derived from this software without specific prior written permission 18 1.1 joerg * 19 1.1 joerg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 20 1.1 joerg * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21 1.1 joerg * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 1.1 joerg * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 23 1.1 joerg * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 1.1 joerg * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 1.1 joerg * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 1.1 joerg * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 1.1 joerg * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28 1.1 joerg * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 1.1 joerg * 30 1.1 joerg * $FreeBSD: fetch.c,v 1.41 2007/12/19 00:26:36 des Exp $ 31 1.1 joerg */ 32 1.1 joerg 33 1.1 joerg #if HAVE_CONFIG_H 34 1.1 joerg #include "config.h" 35 1.1 joerg #endif 36 1.1 joerg #ifndef NETBSD 37 1.1 joerg #include <nbcompat.h> 38 1.1 joerg #endif 39 1.1 joerg 40 1.1 joerg #include <ctype.h> 41 1.1 joerg #include <errno.h> 42 1.1 joerg #include <stdio.h> 43 1.1 joerg #include <stdlib.h> 44 1.1 joerg #include <string.h> 45 1.1 joerg 46 1.1 joerg #include "fetch.h" 47 1.1 joerg #include "common.h" 48 1.1 joerg 49 1.1 joerg auth_t fetchAuthMethod; 50 1.1 joerg int fetchLastErrCode; 51 1.1 joerg char fetchLastErrString[MAXERRSTRING]; 52 1.1 joerg int fetchTimeout; 53 1.2 wiz volatile int fetchRestartCalls = 1; 54 1.1 joerg int fetchDebug; 55 1.1 joerg 56 1.1 joerg 57 1.1 joerg /*** Local data **************************************************************/ 58 1.1 joerg 59 1.1 joerg /* 60 1.1 joerg * Error messages for parser errors 61 1.1 joerg */ 62 1.1 joerg #define URL_MALFORMED 1 63 1.1 joerg #define URL_BAD_SCHEME 2 64 1.1 joerg #define URL_BAD_PORT 3 65 1.1 joerg static struct fetcherr url_errlist[] = { 66 1.1 joerg { URL_MALFORMED, FETCH_URL, "Malformed URL" }, 67 1.1 joerg { URL_BAD_SCHEME, FETCH_URL, "Invalid URL scheme" }, 68 1.1 joerg { URL_BAD_PORT, FETCH_URL, "Invalid server port" }, 69 1.1 joerg { -1, FETCH_UNKNOWN, "Unknown parser error" } 70 1.1 joerg }; 71 1.1 joerg 72 1.1 joerg 73 1.1 joerg /*** Public API **************************************************************/ 74 1.1 joerg 75 1.1 joerg /* 76 1.1 joerg * Select the appropriate protocol for the URL scheme, and return a 77 1.1 joerg * read-only stream connected to the document referenced by the URL. 78 1.1 joerg * Also fill out the struct url_stat. 79 1.1 joerg */ 80 1.1 joerg fetchIO * 81 1.1 joerg fetchXGet(struct url *URL, struct url_stat *us, const char *flags) 82 1.1 joerg { 83 1.1 joerg 84 1.1 joerg if (us != NULL) { 85 1.1 joerg us->size = -1; 86 1.1 joerg us->atime = us->mtime = 0; 87 1.1 joerg } 88 1.1 joerg if (strcasecmp(URL->scheme, SCHEME_FILE) == 0) 89 1.1 joerg return (fetchXGetFile(URL, us, flags)); 90 1.1 joerg else if (strcasecmp(URL->scheme, SCHEME_FTP) == 0) 91 1.1 joerg return (fetchXGetFTP(URL, us, flags)); 92 1.1 joerg else if (strcasecmp(URL->scheme, SCHEME_HTTP) == 0) 93 1.1 joerg return (fetchXGetHTTP(URL, us, flags)); 94 1.1 joerg else if (strcasecmp(URL->scheme, SCHEME_HTTPS) == 0) 95 1.1 joerg return (fetchXGetHTTP(URL, us, flags)); 96 1.1 joerg url_seterr(URL_BAD_SCHEME); 97 1.1 joerg return (NULL); 98 1.1 joerg } 99 1.1 joerg 100 1.1 joerg /* 101 1.1 joerg * Select the appropriate protocol for the URL scheme, and return a 102 1.1 joerg * read-only stream connected to the document referenced by the URL. 103 1.1 joerg */ 104 1.1 joerg fetchIO * 105 1.1 joerg fetchGet(struct url *URL, const char *flags) 106 1.1 joerg { 107 1.1 joerg return (fetchXGet(URL, NULL, flags)); 108 1.1 joerg } 109 1.1 joerg 110 1.1 joerg /* 111 1.1 joerg * Select the appropriate protocol for the URL scheme, and return a 112 1.1 joerg * write-only stream connected to the document referenced by the URL. 113 1.1 joerg */ 114 1.1 joerg fetchIO * 115 1.1 joerg fetchPut(struct url *URL, const char *flags) 116 1.1 joerg { 117 1.1 joerg 118 1.1 joerg if (strcasecmp(URL->scheme, SCHEME_FILE) == 0) 119 1.1 joerg return (fetchPutFile(URL, flags)); 120 1.1 joerg else if (strcasecmp(URL->scheme, SCHEME_FTP) == 0) 121 1.1 joerg return (fetchPutFTP(URL, flags)); 122 1.1 joerg else if (strcasecmp(URL->scheme, SCHEME_HTTP) == 0) 123 1.1 joerg return (fetchPutHTTP(URL, flags)); 124 1.1 joerg else if (strcasecmp(URL->scheme, SCHEME_HTTPS) == 0) 125 1.1 joerg return (fetchPutHTTP(URL, flags)); 126 1.1 joerg url_seterr(URL_BAD_SCHEME); 127 1.1 joerg return (NULL); 128 1.1 joerg } 129 1.1 joerg 130 1.1 joerg /* 131 1.1 joerg * Select the appropriate protocol for the URL scheme, and return the 132 1.1 joerg * size of the document referenced by the URL if it exists. 133 1.1 joerg */ 134 1.1 joerg int 135 1.1 joerg fetchStat(struct url *URL, struct url_stat *us, const char *flags) 136 1.1 joerg { 137 1.1 joerg 138 1.1 joerg if (us != NULL) { 139 1.1 joerg us->size = -1; 140 1.1 joerg us->atime = us->mtime = 0; 141 1.1 joerg } 142 1.1 joerg if (strcasecmp(URL->scheme, SCHEME_FILE) == 0) 143 1.1 joerg return (fetchStatFile(URL, us, flags)); 144 1.1 joerg else if (strcasecmp(URL->scheme, SCHEME_FTP) == 0) 145 1.1 joerg return (fetchStatFTP(URL, us, flags)); 146 1.1 joerg else if (strcasecmp(URL->scheme, SCHEME_HTTP) == 0) 147 1.1 joerg return (fetchStatHTTP(URL, us, flags)); 148 1.1 joerg else if (strcasecmp(URL->scheme, SCHEME_HTTPS) == 0) 149 1.1 joerg return (fetchStatHTTP(URL, us, flags)); 150 1.1 joerg url_seterr(URL_BAD_SCHEME); 151 1.1 joerg return (-1); 152 1.1 joerg } 153 1.1 joerg 154 1.1 joerg /* 155 1.1 joerg * Select the appropriate protocol for the URL scheme, and return a 156 1.1 joerg * list of files in the directory pointed to by the URL. 157 1.1 joerg */ 158 1.1 joerg int 159 1.1 joerg fetchList(struct url_list *ue, struct url *URL, const char *pattern, 160 1.1 joerg const char *flags) 161 1.1 joerg { 162 1.1 joerg 163 1.1 joerg if (strcasecmp(URL->scheme, SCHEME_FILE) == 0) 164 1.1 joerg return (fetchListFile(ue, URL, pattern, flags)); 165 1.1 joerg else if (strcasecmp(URL->scheme, SCHEME_FTP) == 0) 166 1.1 joerg return (fetchListFTP(ue, URL, pattern, flags)); 167 1.1 joerg else if (strcasecmp(URL->scheme, SCHEME_HTTP) == 0) 168 1.1 joerg return (fetchListHTTP(ue, URL, pattern, flags)); 169 1.1 joerg else if (strcasecmp(URL->scheme, SCHEME_HTTPS) == 0) 170 1.1 joerg return (fetchListHTTP(ue, URL, pattern, flags)); 171 1.1 joerg url_seterr(URL_BAD_SCHEME); 172 1.1 joerg return -1; 173 1.1 joerg } 174 1.1 joerg 175 1.1 joerg /* 176 1.1 joerg * Attempt to parse the given URL; if successful, call fetchXGet(). 177 1.1 joerg */ 178 1.1 joerg fetchIO * 179 1.1 joerg fetchXGetURL(const char *URL, struct url_stat *us, const char *flags) 180 1.1 joerg { 181 1.1 joerg struct url *u; 182 1.1 joerg fetchIO *f; 183 1.1 joerg 184 1.1 joerg if ((u = fetchParseURL(URL)) == NULL) 185 1.1 joerg return (NULL); 186 1.1 joerg 187 1.1 joerg f = fetchXGet(u, us, flags); 188 1.1 joerg 189 1.1 joerg fetchFreeURL(u); 190 1.1 joerg return (f); 191 1.1 joerg } 192 1.1 joerg 193 1.1 joerg /* 194 1.1 joerg * Attempt to parse the given URL; if successful, call fetchGet(). 195 1.1 joerg */ 196 1.1 joerg fetchIO * 197 1.1 joerg fetchGetURL(const char *URL, const char *flags) 198 1.1 joerg { 199 1.1 joerg return (fetchXGetURL(URL, NULL, flags)); 200 1.1 joerg } 201 1.1 joerg 202 1.1 joerg /* 203 1.1 joerg * Attempt to parse the given URL; if successful, call fetchPut(). 204 1.1 joerg */ 205 1.1 joerg fetchIO * 206 1.1 joerg fetchPutURL(const char *URL, const char *flags) 207 1.1 joerg { 208 1.1 joerg struct url *u; 209 1.1 joerg fetchIO *f; 210 1.1 joerg 211 1.1 joerg if ((u = fetchParseURL(URL)) == NULL) 212 1.1 joerg return (NULL); 213 1.1 joerg 214 1.1 joerg f = fetchPut(u, flags); 215 1.1 joerg 216 1.1 joerg fetchFreeURL(u); 217 1.1 joerg return (f); 218 1.1 joerg } 219 1.1 joerg 220 1.1 joerg /* 221 1.1 joerg * Attempt to parse the given URL; if successful, call fetchStat(). 222 1.1 joerg */ 223 1.1 joerg int 224 1.1 joerg fetchStatURL(const char *URL, struct url_stat *us, const char *flags) 225 1.1 joerg { 226 1.1 joerg struct url *u; 227 1.1 joerg int s; 228 1.1 joerg 229 1.1 joerg if ((u = fetchParseURL(URL)) == NULL) 230 1.1 joerg return (-1); 231 1.1 joerg 232 1.1 joerg s = fetchStat(u, us, flags); 233 1.1 joerg 234 1.1 joerg fetchFreeURL(u); 235 1.1 joerg return (s); 236 1.1 joerg } 237 1.1 joerg 238 1.1 joerg /* 239 1.1 joerg * Attempt to parse the given URL; if successful, call fetchList(). 240 1.1 joerg */ 241 1.1 joerg int 242 1.1 joerg fetchListURL(struct url_list *ue, const char *URL, const char *pattern, 243 1.1 joerg const char *flags) 244 1.1 joerg { 245 1.1 joerg struct url *u; 246 1.1 joerg int rv; 247 1.1 joerg 248 1.1 joerg if ((u = fetchParseURL(URL)) == NULL) 249 1.1 joerg return -1; 250 1.1 joerg 251 1.1 joerg rv = fetchList(ue, u, pattern, flags); 252 1.1 joerg 253 1.1 joerg fetchFreeURL(u); 254 1.1 joerg return rv; 255 1.1 joerg } 256 1.1 joerg 257 1.1 joerg /* 258 1.1 joerg * Make a URL 259 1.1 joerg */ 260 1.1 joerg struct url * 261 1.1 joerg fetchMakeURL(const char *scheme, const char *host, int port, const char *doc, 262 1.1 joerg const char *user, const char *pwd) 263 1.1 joerg { 264 1.1 joerg struct url *u; 265 1.1 joerg 266 1.1 joerg if (!scheme || (!host && !doc)) { 267 1.1 joerg url_seterr(URL_MALFORMED); 268 1.1 joerg return (NULL); 269 1.1 joerg } 270 1.1 joerg 271 1.1 joerg if (port < 0 || port > 65535) { 272 1.1 joerg url_seterr(URL_BAD_PORT); 273 1.1 joerg return (NULL); 274 1.1 joerg } 275 1.1 joerg 276 1.1 joerg /* allocate struct url */ 277 1.1 joerg if ((u = calloc(1, sizeof(*u))) == NULL) { 278 1.1 joerg fetch_syserr(); 279 1.1 joerg return (NULL); 280 1.1 joerg } 281 1.1 joerg 282 1.1 joerg if ((u->doc = strdup(doc ? doc : "/")) == NULL) { 283 1.1 joerg fetch_syserr(); 284 1.1 joerg free(u); 285 1.1 joerg return (NULL); 286 1.1 joerg } 287 1.1 joerg 288 1.1 joerg #define seturl(x) snprintf(u->x, sizeof(u->x), "%s", x) 289 1.1 joerg seturl(scheme); 290 1.1 joerg seturl(host); 291 1.1 joerg seturl(user); 292 1.1 joerg seturl(pwd); 293 1.1 joerg #undef seturl 294 1.1 joerg u->port = port; 295 1.1 joerg 296 1.1 joerg return (u); 297 1.1 joerg } 298 1.1 joerg 299 1.1 joerg int 300 1.1 joerg fetch_urlpath_safe(char x) 301 1.1 joerg { 302 1.1 joerg if ((x >= '0' && x <= '9') || (x >= 'A' && x <= 'Z') || 303 1.1 joerg (x >= 'a' && x <= 'z')) 304 1.1 joerg return 1; 305 1.1 joerg 306 1.1 joerg switch (x) { 307 1.1 joerg case '$': 308 1.1 joerg case '-': 309 1.1 joerg case '_': 310 1.1 joerg case '.': 311 1.1 joerg case '+': 312 1.1 joerg case '!': 313 1.1 joerg case '*': 314 1.1 joerg case '\'': 315 1.1 joerg case '(': 316 1.1 joerg case ')': 317 1.1 joerg case ',': 318 1.1 joerg /* The following are allowed in segment and path components: */ 319 1.1 joerg case '?': 320 1.1 joerg case ':': 321 1.1 joerg case '@': 322 1.1 joerg case '&': 323 1.1 joerg case '=': 324 1.1 joerg case '/': 325 1.1 joerg case ';': 326 1.1 joerg /* If something is already quoted... */ 327 1.1 joerg case '%': 328 1.1 joerg return 1; 329 1.1 joerg default: 330 1.1 joerg return 0; 331 1.1 joerg } 332 1.1 joerg } 333 1.1 joerg 334 1.1 joerg /* 335 1.1 joerg * Copy an existing URL. 336 1.1 joerg */ 337 1.1 joerg struct url * 338 1.1 joerg fetchCopyURL(const struct url *src) 339 1.1 joerg { 340 1.1 joerg struct url *dst; 341 1.1 joerg char *doc; 342 1.1 joerg 343 1.1 joerg /* allocate struct url */ 344 1.1 joerg if ((dst = malloc(sizeof(*dst))) == NULL) { 345 1.1 joerg fetch_syserr(); 346 1.1 joerg return (NULL); 347 1.1 joerg } 348 1.1 joerg if ((doc = strdup(src->doc)) == NULL) { 349 1.1 joerg fetch_syserr(); 350 1.1 joerg free(dst); 351 1.1 joerg return (NULL); 352 1.1 joerg } 353 1.1 joerg *dst = *src; 354 1.1 joerg dst->doc = doc; 355 1.1 joerg 356 1.1 joerg return dst; 357 1.1 joerg } 358 1.1 joerg 359 1.1 joerg /* 360 1.1 joerg * Split an URL into components. URL syntax is: 361 1.1 joerg * [method:/][/[user[:pwd]@]host[:port]/][document] 362 1.1 joerg * This almost, but not quite, RFC1738 URL syntax. 363 1.1 joerg */ 364 1.1 joerg struct url * 365 1.1 joerg fetchParseURL(const char *URL) 366 1.1 joerg { 367 1.1 joerg const char *p, *q; 368 1.1 joerg struct url *u; 369 1.1 joerg size_t i, count; 370 1.1 joerg int pre_quoted; 371 1.1 joerg 372 1.1 joerg /* allocate struct url */ 373 1.1 joerg if ((u = calloc(1, sizeof(*u))) == NULL) { 374 1.1 joerg fetch_syserr(); 375 1.1 joerg return (NULL); 376 1.1 joerg } 377 1.1 joerg 378 1.1 joerg if (*URL == '/') { 379 1.1 joerg pre_quoted = 0; 380 1.1 joerg strcpy(u->scheme, SCHEME_FILE); 381 1.1 joerg p = URL; 382 1.1 joerg goto quote_doc; 383 1.1 joerg } 384 1.1 joerg if (strncmp(URL, "file:", 5) == 0) { 385 1.1 joerg pre_quoted = 1; 386 1.1 joerg strcpy(u->scheme, SCHEME_FILE); 387 1.1 joerg URL += 5; 388 1.1 joerg if (URL[0] != '/' || URL[1] != '/' || URL[2] != '/') { 389 1.1 joerg url_seterr(URL_MALFORMED); 390 1.1 joerg goto ouch; 391 1.1 joerg } 392 1.1 joerg p = URL + 2; 393 1.1 joerg goto quote_doc; 394 1.1 joerg } 395 1.1 joerg if (strncmp(URL, "http:", 5) == 0 || 396 1.1 joerg strncmp(URL, "https:", 6) == 0) { 397 1.1 joerg pre_quoted = 1; 398 1.1 joerg if (URL[4] == ':') { 399 1.1 joerg strcpy(u->scheme, SCHEME_HTTP); 400 1.1 joerg URL += 5; 401 1.1 joerg } else { 402 1.1 joerg strcpy(u->scheme, SCHEME_HTTPS); 403 1.1 joerg URL += 6; 404 1.1 joerg } 405 1.1 joerg 406 1.1 joerg if (URL[0] != '/' || URL[1] != '/') { 407 1.1 joerg url_seterr(URL_MALFORMED); 408 1.1 joerg goto ouch; 409 1.1 joerg } 410 1.1 joerg URL += 2; 411 1.1 joerg p = URL; 412 1.2 wiz goto find_user; 413 1.1 joerg } 414 1.1 joerg if (strncmp(URL, "ftp:", 4) == 0) { 415 1.1 joerg pre_quoted = 1; 416 1.1 joerg strcpy(u->scheme, SCHEME_FTP); 417 1.1 joerg URL += 4; 418 1.1 joerg if (URL[0] != '/' || URL[1] != '/') { 419 1.1 joerg url_seterr(URL_MALFORMED); 420 1.1 joerg goto ouch; 421 1.1 joerg } 422 1.1 joerg URL += 2; 423 1.1 joerg p = URL; 424 1.1 joerg goto find_user; 425 1.1 joerg } 426 1.1 joerg 427 1.1 joerg url_seterr(URL_BAD_SCHEME); 428 1.1 joerg goto ouch; 429 1.1 joerg 430 1.1 joerg find_user: 431 1.1 joerg p = strpbrk(URL, "/@"); 432 1.1 joerg if (p != NULL && *p == '@') { 433 1.1 joerg /* username */ 434 1.1 joerg for (q = URL, i = 0; (*q != ':') && (*q != '@'); q++) { 435 1.1 joerg if (i < URL_USERLEN) 436 1.1 joerg u->user[i++] = *q; 437 1.1 joerg } 438 1.1 joerg 439 1.1 joerg /* password */ 440 1.1 joerg if (*q == ':') { 441 1.2 wiz for (q++, i = 0; (*q != '@'); q++) 442 1.1 joerg if (i < URL_PWDLEN) 443 1.1 joerg u->pwd[i++] = *q; 444 1.1 joerg } 445 1.1 joerg 446 1.1 joerg p++; 447 1.1 joerg } else { 448 1.1 joerg p = URL; 449 1.1 joerg } 450 1.1 joerg 451 1.1 joerg /* hostname */ 452 1.1 joerg #ifdef INET6 453 1.1 joerg if (*p == '[' && (q = strchr(p + 1, ']')) != NULL && 454 1.1 joerg (*++q == '\0' || *q == '/' || *q == ':')) { 455 1.1 joerg if ((i = q - p - 2) > URL_HOSTLEN) 456 1.1 joerg i = URL_HOSTLEN; 457 1.1 joerg strncpy(u->host, ++p, i); 458 1.1 joerg p = q; 459 1.1 joerg } else 460 1.1 joerg #endif 461 1.1 joerg for (i = 0; *p && (*p != '/') && (*p != ':'); p++) 462 1.1 joerg if (i < URL_HOSTLEN) 463 1.1 joerg u->host[i++] = *p; 464 1.1 joerg 465 1.1 joerg /* port */ 466 1.1 joerg if (*p == ':') { 467 1.1 joerg for (q = ++p; *q && (*q != '/'); q++) 468 1.1 joerg if (isdigit((unsigned char)*q)) 469 1.1 joerg u->port = u->port * 10 + (*q - '0'); 470 1.1 joerg else { 471 1.1 joerg /* invalid port */ 472 1.1 joerg url_seterr(URL_BAD_PORT); 473 1.1 joerg goto ouch; 474 1.1 joerg } 475 1.1 joerg p = q; 476 1.1 joerg } 477 1.1 joerg 478 1.1 joerg /* document */ 479 1.1 joerg if (!*p) 480 1.1 joerg p = "/"; 481 1.1 joerg 482 1.1 joerg quote_doc: 483 1.1 joerg count = 1; 484 1.1 joerg for (i = 0; p[i] != '\0'; ++i) { 485 1.1 joerg if ((!pre_quoted && p[i] == '%') || 486 1.1 joerg !fetch_urlpath_safe(p[i])) 487 1.1 joerg count += 3; 488 1.1 joerg else 489 1.1 joerg ++count; 490 1.1 joerg } 491 1.1 joerg 492 1.1 joerg if ((u->doc = malloc(count)) == NULL) { 493 1.1 joerg fetch_syserr(); 494 1.1 joerg goto ouch; 495 1.1 joerg } 496 1.1 joerg for (i = 0; *p != '\0'; ++p) { 497 1.1 joerg if ((!pre_quoted && *p == '%') || 498 1.1 joerg !fetch_urlpath_safe(*p)) { 499 1.1 joerg u->doc[i++] = '%'; 500 1.1 joerg if ((unsigned char)*p < 160) 501 1.1 joerg u->doc[i++] = '0' + ((unsigned char)*p) / 16; 502 1.1 joerg else 503 1.1 joerg u->doc[i++] = 'a' - 10 + ((unsigned char)*p) / 16; 504 1.2 wiz if ((unsigned char)*p % 16 < 10) 505 1.1 joerg u->doc[i++] = '0' + ((unsigned char)*p) % 16; 506 1.1 joerg else 507 1.1 joerg u->doc[i++] = 'a' - 10 + ((unsigned char)*p) % 16; 508 1.1 joerg } else 509 1.1 joerg u->doc[i++] = *p; 510 1.1 joerg } 511 1.1 joerg u->doc[i] = '\0'; 512 1.1 joerg 513 1.1 joerg return (u); 514 1.1 joerg 515 1.1 joerg ouch: 516 1.1 joerg free(u); 517 1.1 joerg return (NULL); 518 1.1 joerg } 519 1.1 joerg 520 1.1 joerg /* 521 1.1 joerg * Free a URL 522 1.1 joerg */ 523 1.1 joerg void 524 1.1 joerg fetchFreeURL(struct url *u) 525 1.1 joerg { 526 1.1 joerg free(u->doc); 527 1.1 joerg free(u); 528 1.1 joerg } 529 1.1 joerg 530 1.1 joerg static char 531 1.1 joerg xdigit2digit(char digit) 532 1.1 joerg { 533 1.1 joerg digit = tolower((unsigned char)digit); 534 1.1 joerg if (digit >= 'a' && digit <= 'f') 535 1.1 joerg digit = digit - 'a' + 10; 536 1.1 joerg else 537 1.1 joerg digit = digit - '0'; 538 1.1 joerg 539 1.1 joerg return digit; 540 1.1 joerg } 541 1.1 joerg 542 1.1 joerg /* 543 1.1 joerg * Unquote whole URL. 544 1.1 joerg * Skips optional parts like query or fragment identifier. 545 1.1 joerg */ 546 1.1 joerg char * 547 1.1 joerg fetchUnquotePath(struct url *url) 548 1.1 joerg { 549 1.1 joerg char *unquoted; 550 1.1 joerg const char *iter; 551 1.1 joerg size_t i; 552 1.1 joerg 553 1.1 joerg if ((unquoted = malloc(strlen(url->doc) + 1)) == NULL) 554 1.1 joerg return NULL; 555 1.1 joerg 556 1.1 joerg for (i = 0, iter = url->doc; *iter != '\0'; ++iter) { 557 1.1 joerg if (*iter == '#' || *iter == '?') 558 1.1 joerg break; 559 1.1 joerg if (iter[0] != '%' || 560 1.1 joerg !isxdigit((unsigned char)iter[1]) || 561 1.1 joerg !isxdigit((unsigned char)iter[2])) { 562 1.1 joerg unquoted[i++] = *iter; 563 1.1 joerg continue; 564 1.1 joerg } 565 1.1 joerg unquoted[i++] = xdigit2digit(iter[1]) * 16 + 566 1.1 joerg xdigit2digit(iter[2]); 567 1.1 joerg iter += 2; 568 1.1 joerg } 569 1.1 joerg unquoted[i] = '\0'; 570 1.1 joerg return unquoted; 571 1.1 joerg } 572 1.1 joerg 573 1.1 joerg 574 1.1 joerg /* 575 1.1 joerg * Extract the file name component of a URL. 576 1.1 joerg */ 577 1.1 joerg char * 578 1.1 joerg fetchUnquoteFilename(struct url *url) 579 1.1 joerg { 580 1.1 joerg char *unquoted, *filename; 581 1.1 joerg const char *last_slash; 582 1.1 joerg 583 1.1 joerg if ((unquoted = fetchUnquotePath(url)) == NULL) 584 1.1 joerg return NULL; 585 1.1 joerg 586 1.1 joerg if ((last_slash = strrchr(unquoted, '/')) == NULL) 587 1.1 joerg return unquoted; 588 1.1 joerg filename = strdup(last_slash + 1); 589 1.1 joerg free(unquoted); 590 1.1 joerg return filename; 591 1.1 joerg } 592 1.1 joerg 593 1.1 joerg char * 594 1.1 joerg fetchStringifyURL(const struct url *url) 595 1.1 joerg { 596 1.1 joerg size_t total; 597 1.1 joerg char *doc; 598 1.1 joerg 599 1.1 joerg /* scheme :// user : pwd @ host :port doc */ 600 1.1 joerg total = strlen(url->scheme) + 3 + strlen(url->user) + 1 + 601 1.1 joerg strlen(url->pwd) + 1 + strlen(url->host) + 6 + strlen(url->doc) + 1; 602 1.1 joerg if ((doc = malloc(total)) == NULL) 603 1.1 joerg return NULL; 604 1.1 joerg if (url->port != 0) 605 1.1 joerg snprintf(doc, total, "%s%s%s%s%s%s%s:%d%s", 606 1.1 joerg url->scheme, 607 1.1 joerg url->scheme[0] != '\0' ? "://" : "", 608 1.1 joerg url->user, 609 1.1 joerg url->pwd[0] != '\0' ? ":" : "", 610 1.1 joerg url->pwd, 611 1.1 joerg url->user[0] != '\0' || url->pwd[0] != '\0' ? "@" : "", 612 1.1 joerg url->host, 613 1.1 joerg (int)url->port, 614 1.1 joerg url->doc); 615 1.1 joerg else { 616 1.1 joerg snprintf(doc, total, "%s%s%s%s%s%s%s%s", 617 1.1 joerg url->scheme, 618 1.1 joerg url->scheme[0] != '\0' ? "://" : "", 619 1.1 joerg url->user, 620 1.1 joerg url->pwd[0] != '\0' ? ":" : "", 621 1.1 joerg url->pwd, 622 1.1 joerg url->user[0] != '\0' || url->pwd[0] != '\0' ? "@" : "", 623 1.1 joerg url->host, 624 1.1 joerg url->doc); 625 1.1 joerg } 626 1.1 joerg return doc; 627 1.1 joerg } 628