rpm 5.3.12
|
00001 00005 #include "system.h" 00006 00007 #include <netinet/in.h> 00008 00009 #include <rpmmacro.h> 00010 #include <rpmcb.h> 00011 #include <rpmio_internal.h> 00012 #ifdef WITH_NEON 00013 #include <rpmdav.h> 00014 #endif 00015 00016 #include "debug.h" 00017 00018 /*@access FD_t@*/ /* XXX compared with NULL */ 00019 /*@access urlinfo@*/ 00020 00021 #ifndef IPPORT_FTP 00022 #define IPPORT_FTP 21 00023 #endif 00024 #ifndef IPPORT_HTTP 00025 #define IPPORT_HTTP 80 00026 #endif 00027 #ifndef IPPORT_HTTPS 00028 #define IPPORT_HTTPS 443 00029 #endif 00030 #ifndef IPPORT_PGPKEYSERVER 00031 #define IPPORT_PGPKEYSERVER 11371 00032 #endif 00033 00036 /*@-redecl@*/ 00037 int (*urlNotify) (const urlinfo u, unsigned status) 00038 /*@*/; 00039 /*@=redecl@*/ 00040 00043 /*@unchecked@*/ /*@null@*/ 00044 void * urlNotifyArg; 00045 00048 /*@unchecked@*/ 00049 int _url_iobuf_size = RPMURL_IOBUF_SIZE; 00050 00053 /*@unchecked@*/ 00054 int _url_debug = 0; 00055 00056 #define URLDBG(_f, _m, _x) if ((_url_debug | (_f)) & (_m)) fprintf _x 00057 00058 #define URLDBGIO(_f, _x) URLDBG((_f), RPMURL_DEBUG_IO, _x) 00059 #define URLDBGREFS(_f, _x) URLDBG((_f), RPMURL_DEBUG_REFS, _x) 00060 00063 /*@unchecked@*/ 00064 /*@only@*/ /*@null@*/ 00065 urlinfo *_url_cache = NULL; 00066 00067 static void urlFini(void * _u) 00068 /*@globals fileSystem, internalState @*/ 00069 /*@modifies _u, fileSystem, internalState @*/ 00070 { 00071 urlinfo u =_u; 00072 int xx; 00073 00074 if (u->ctrl) { 00075 #ifndef NOTYET 00076 void * fp = fdGetFp(u->ctrl); 00077 if (fp) { 00078 fdPush(u->ctrl, fpio, fp, -1); /* Push fpio onto stack */ 00079 xx = Fclose(u->ctrl); 00080 } else if (fdFileno(u->ctrl) >= 0) 00081 xx = fdio->close(u->ctrl); 00082 #else 00083 xx = Fclose(u->ctrl); 00084 #endif 00085 00086 /*@-usereleased@*/ 00087 u->ctrl = (FD_t)rpmioFreePoolItem((rpmioItem)u->ctrl, "persist ctrl (urlFree)", __FILE__, __LINE__); 00088 if (u->ctrl) 00089 fprintf(stderr, _("warning: u %p ctrl %p nrefs != 0 (%s %s)\n"), 00090 u, u->ctrl, (u->host ? u->host : ""), 00091 (u->scheme ? u->scheme : "")); 00092 /*@=usereleased@*/ 00093 } 00094 if (u->data) { 00095 #ifndef NOTYET 00096 void * fp = fdGetFp(u->data); 00097 if (fp) { 00098 fdPush(u->data, fpio, fp, -1); /* Push fpio onto stack */ 00099 xx = Fclose(u->data); 00100 } else if (fdFileno(u->data) >= 0) 00101 xx = fdio->close(u->data); 00102 #else 00103 xx = Fclose(u->ctrl); 00104 #endif 00105 00106 /*@-usereleased@*/ 00107 u->data = (FD_t)rpmioFreePoolItem((rpmioItem)u->data, "persist data (urlFree)", __FILE__, __LINE__); 00108 if (u->data) 00109 fprintf(stderr, _("warning: u %p data %p nrefs != 0 (%s %s)\n"), 00110 u, u->data, (u->host ? u->host : ""), 00111 (u->scheme ? u->scheme : "")); 00112 /*@=usereleased@*/ 00113 } 00114 #ifdef WITH_NEON 00115 xx = davFree(u); 00116 #endif 00117 u->etag = _free(u->etag); 00118 u->location = _free(u->location); 00119 u->rop = _free(u->rop); 00120 u->sop = _free(u->sop); 00121 u->top = _free(u->top); 00122 u->buf = _free(u->buf); 00123 u->url = _free(u->url); 00124 u->scheme = _free((void *)u->scheme); 00125 u->user = _free((void *)u->user); 00126 u->password = _free((void *)u->password); 00127 u->host = _free((void *)u->host); 00128 u->portstr = _free((void *)u->portstr); 00129 u->query = _free(u->query); 00130 u->fragment = _free(u->fragment); 00131 u->proxyu = _free((void *)u->proxyu); 00132 u->proxyh = _free((void *)u->proxyh); 00133 } 00134 00137 /*@unchecked@*/ 00138 int _url_count = 0; 00139 00140 /*@unchecked@*/ /*@only@*/ /*@null@*/ 00141 rpmioPool _urlPool; 00142 00143 static urlinfo urlGetPool(/*@null@*/ rpmioPool pool) 00144 /*@globals _urlPool, fileSystem @*/ 00145 /*@modifies pool, _urlPool, fileSystem @*/ 00146 { 00147 urlinfo u; 00148 00149 if (_urlPool == NULL) { 00150 _urlPool = rpmioNewPool("u", sizeof(*u), -1, _url_debug, 00151 NULL, NULL, urlFini); 00152 pool = _urlPool; 00153 } 00154 u = (urlinfo) rpmioGetPool(pool, sizeof(*u)); 00155 memset(((char *)u)+sizeof(u->_item), 0, sizeof(*u)-sizeof(u->_item)); 00156 return u; 00157 } 00158 00159 urlinfo XurlNew(const char *msg, const char *fn, unsigned ln) 00160 { 00161 urlinfo u = urlGetPool(_urlPool); 00162 00163 u->proxyp = -1; 00164 u->port = -1; 00165 u->ut = URL_IS_UNKNOWN; 00166 u->ctrl = NULL; 00167 u->data = NULL; 00168 u->location = NULL; 00169 u->etag = NULL; 00170 u->notify = urlNotify; 00171 /*@-assignexpose@*/ 00172 u->arg = urlNotifyArg; 00173 /*@=assignexpose@*/ 00174 u->rop = xcalloc(1, sizeof(*u->rop)); 00175 u->sop = xcalloc(1, sizeof(*u->sop)); 00176 u->top = xcalloc(1, sizeof(*u->top)); 00177 u->bufAlloced = 0; 00178 u->buf = NULL; 00179 u->allow = RPMURL_SERVER_HASRANGE; 00180 u->httpVersion = 0; 00181 u->magic = URLMAGIC; 00182 return (urlinfo) rpmioLinkPoolItem((rpmioItem)u, msg, fn, ln); 00183 } 00184 00185 void urlFreeCache(void) 00186 { 00187 if (_url_cache) { 00188 int i; 00189 for (i = 0; i < _url_count; i++) { 00190 if (_url_cache[i] == NULL) continue; 00191 _url_cache[i] = urlFree(_url_cache[i], "_url_cache"); 00192 if (_url_cache[i] == NULL) 00193 continue; 00194 yarnPossess(_url_cache[i]->_item.use); 00195 fprintf(stderr, 00196 _("warning: _url_cache[%d] %p nrefs(%ld) != 1 (%s %s)\n"), 00197 i, _url_cache[i], yarnPeekLock(_url_cache[i]->_item.use), 00198 (_url_cache[i]->host ? _url_cache[i]->host : ""), 00199 (_url_cache[i]->scheme ? _url_cache[i]->scheme : "")); 00200 yarnRelease(_url_cache[i]->_item.use); 00201 } 00202 } 00203 _url_cache = _free(_url_cache); 00204 _url_count = 0; 00205 } 00206 00207 static int urlStrcmp(/*@null@*/ const char * str1, /*@null@*/ const char * str2) 00208 /*@*/ 00209 { 00210 if (str1) 00211 if (str2) 00212 return strcmp(str1, str2); 00213 if (str1 != str2) 00214 return -1; 00215 return 0; 00216 } 00217 00218 /*@-mods@*/ 00219 static void urlFind(/*@null@*/ /*@in@*/ /*@out@*/ urlinfo * uret, int mustAsk) 00220 /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/ 00221 /*@modifies *uret, rpmGlobalMacroContext, fileSystem, internalState @*/ 00222 { 00223 urlinfo u; 00224 int ucx; 00225 int i = 0; 00226 00227 if (uret == NULL) 00228 return; 00229 00230 u = *uret; 00231 URLSANE(u); 00232 00233 ucx = -1; 00234 for (i = 0; i < _url_count; i++) { 00235 urlinfo ou = NULL; 00236 if (_url_cache == NULL || (ou = _url_cache[i]) == NULL) { 00237 if (ucx < 0) 00238 ucx = i; 00239 continue; 00240 } 00241 00242 /* Check for cache-miss condition. A cache miss is 00243 * a) both items are not NULL and don't compare. 00244 * b) either of the items is not NULL. 00245 */ 00246 if (urlStrcmp(u->scheme, ou->scheme)) 00247 continue; 00248 if (urlStrcmp(u->host, ou->host)) 00249 continue; 00250 if (urlStrcmp(u->user, ou->user)) 00251 continue; 00252 if (urlStrcmp(u->portstr, ou->portstr)) 00253 continue; 00254 break; /* Found item in cache */ 00255 } 00256 00257 if (i == _url_count) { 00258 if (ucx < 0) { 00259 ucx = _url_count++; 00260 _url_cache = xrealloc(_url_cache, sizeof(*_url_cache) * _url_count); 00261 } 00262 if (_url_cache) /* XXX always true */ 00263 _url_cache[ucx] = urlLink(u, "_url_cache (miss)"); 00264 u = urlFree(u, "urlSplit (urlFind miss)"); 00265 } else { 00266 ucx = i; 00267 u = urlFree(u, "urlSplit (urlFind hit)"); 00268 } 00269 00270 /* This URL is now cached. */ 00271 00272 if (_url_cache) /* XXX always true */ 00273 u = urlLink(_url_cache[ucx], "_url_cache"); 00274 *uret = u; 00275 /*@-usereleased@*/ 00276 u = urlFree(u, "_url_cache (urlFind)"); 00277 /*@=usereleased@*/ 00278 assert(u != NULL); 00279 00280 /* Zap proxy host and port in case they have been reset */ 00281 u->proxyp = -1; 00282 u->proxyh = _free(u->proxyh); 00283 00284 /* Perform one-time FTP initialization */ 00285 if (u->ut == URL_IS_FTP) { 00286 00287 if (mustAsk || (u->user != NULL && u->password == NULL)) { 00288 const char * host = (u->host ? u->host : ""); 00289 const char * user = (u->user ? u->user : ""); 00290 char * prompt; 00291 prompt = alloca(strlen(host) + strlen(user) + 256); 00292 sprintf(prompt, _("Password for %s@%s: "), user, host); 00293 u->password = _free(u->password); 00294 /*@-dependenttrans -moduncon @*/ 00295 u->password = Getpass(prompt); 00296 /*@=dependenttrans =moduncon @*/ 00297 if (u->password) 00298 u->password = xstrdup(u->password); 00299 } 00300 00301 if (u->proxyh == NULL) { 00302 const char *proxy = rpmExpand("%{_ftpproxy}", NULL); 00303 if (proxy && *proxy != '%') { 00304 /*@observer@*/ 00305 const char * host = (u->host ? u->host : ""); 00306 const char *uu = (u->user ? u->user : "anonymous"); 00307 char *nu = xmalloc(strlen(uu) + sizeof("@") + strlen(host)); 00308 (void) stpcpy( stpcpy( stpcpy(nu, uu), "@"), host); 00309 u->proxyu = nu; 00310 u->proxyh = xstrdup(proxy); 00311 } 00312 proxy = _free(proxy); 00313 } 00314 00315 if (u->proxyp < 0) { 00316 const char *proxy = rpmExpand("%{_ftpport}", NULL); 00317 if (proxy && *proxy != '%') { 00318 char *end = NULL; 00319 int port = strtol(proxy, &end, 0); 00320 if (!(end && *end == '\0')) { 00321 fprintf(stderr, _("error: %sport must be a number\n"), 00322 (u->scheme ? u->scheme : "")); 00323 return; 00324 } 00325 u->proxyp = port; 00326 } 00327 proxy = _free(proxy); 00328 } 00329 } 00330 00331 /* Perform one-time HTTP initialization */ 00332 if (u->ut == URL_IS_HTTP || u->ut == URL_IS_HTTPS || u->ut == URL_IS_HKP) { 00333 00334 if (u->proxyh == NULL) { 00335 const char *proxy = rpmExpand("%{_httpproxy}", NULL); 00336 if (proxy && *proxy != '%') 00337 u->proxyh = xstrdup(proxy); 00338 proxy = _free(proxy); 00339 } 00340 00341 if (u->proxyp < 0) { 00342 const char *proxy = rpmExpand("%{_httpport}", NULL); 00343 if (proxy && *proxy != '%') { 00344 char *end; 00345 int port = strtol(proxy, &end, 0); 00346 if (!(end && *end == '\0')) { 00347 fprintf(stderr, _("error: %sport must be a number\n"), 00348 (u->scheme ? u->scheme : "")); 00349 return; 00350 } 00351 u->proxyp = port; 00352 } 00353 proxy = _free(proxy); 00354 } 00355 00356 } 00357 00358 return; 00359 } 00360 /*@=mods@*/ 00361 00364 /*@observer@*/ /*@unchecked@*/ 00365 static struct urlstring { 00366 /*@observer@*/ /*@null@*/ 00367 const char *leadin; 00368 size_t len; 00369 urltype ret; 00370 } urlstrings[] = { 00371 { "file://", sizeof("file://")-1, URL_IS_PATH }, 00372 { "ftp://", sizeof("ftp://")-1, URL_IS_FTP }, 00373 { "hkp://", sizeof("hkp://")-1, URL_IS_HKP }, 00374 { "http://", sizeof("http://")-1, URL_IS_HTTP }, 00375 { "https://", sizeof("https://")-1, URL_IS_HTTPS }, 00376 { "-", sizeof("-")-1, URL_IS_DASH }, 00377 { NULL, 0, URL_IS_UNKNOWN } 00378 }; 00379 00380 urltype urlIsURL(const char * url) 00381 { 00382 struct urlstring *us; 00383 int ut = URL_IS_UNKNOWN;; 00384 00385 if (url && *url && *url != '/') 00386 for (us = urlstrings; us->leadin != NULL; us++) { 00387 if (strncmp(url, us->leadin, us->len)) 00388 continue; 00389 ut = us->ret; 00390 break; 00391 } 00392 return ut; 00393 } 00394 00395 urltype urlType(void * _u) 00396 { 00397 return (_u != NULL ? ((urlinfo)_u)->ut : URL_IS_UNKNOWN); 00398 } 00399 00400 /* Return path portion of url (or pointer to NUL if url == NULL) */ 00401 urltype urlPath(const char * url, const char ** pathp) 00402 { 00403 static const char empty[] = ""; 00404 const char *path = (url ? url : empty); 00405 int ut = URL_IS_UNKNOWN; 00406 00407 if (*path != '\0' && *path != '/') { 00408 struct urlstring *us; 00409 for (us = urlstrings; us->leadin != NULL; us++) { 00410 if (strncmp(url, us->leadin, us->len)) 00411 continue; 00412 if ((path = strchr(url+us->len, '/')) == NULL) 00413 path = empty; 00414 ut = us->ret; 00415 break; 00416 } 00417 } 00418 /*@-observertrans@*/ 00419 if (pathp) 00420 *pathp = path; 00421 /*@=observertrans@*/ 00422 return ut; 00423 } 00424 00428 static const char * urlStrdup(const char * url) 00429 /*@*/ 00430 { 00431 size_t nb = strlen(url); 00432 char * t = xmalloc(nb + 1 + 1); 00433 const char * nurl = t; 00434 while (*url != '\0') 00435 *t++ = *url++; 00436 *t = '\0'; 00437 return nurl; 00438 } 00439 00440 /* 00441 * Split URL into components. The URL can look like 00442 * scheme://user:password@host:port/path 00443 * or as in RFC2732 for IPv6 address 00444 * service://user:password@[ip:v6:ad:dr:es:s]:port/path?query#fragment 00445 */ 00446 /*@-modfilesys@*/ 00447 int urlSplit(const char * url, urlinfo *uret) 00448 { 00449 urlinfo u; 00450 char *myurl; 00451 char *s, *se, *f, *fe; 00452 00453 if (uret == NULL) 00454 return -1; 00455 if ((u = urlNew("urlSplit")) == NULL) 00456 return -1; 00457 00458 myurl = xstrdup(url); 00459 if ((se = strrchr(myurl, '#')) != NULL) { 00460 *se++ = '\0'; 00461 u->fragment = xstrdup(se); 00462 } 00463 if ((se = strrchr(myurl, '?')) != NULL) { 00464 *se++ = '\0'; 00465 u->query = xstrdup(se); 00466 } 00467 00468 u->url = urlStrdup(myurl); /* XXX +1 byte for pesky trailing '/' */ 00469 u->ut = urlIsURL(myurl); 00470 00471 se = s = myurl; 00472 while (1) { 00473 /* Point to end of next item */ 00474 while (*se && *se != '/') se++; 00475 /* Item was scheme. Save scheme and go for the rest ...*/ 00476 if (*se && (se != s) && se[-1] == ':' && se[0] == '/' && se[1] == '/') { 00477 se[-1] = '\0'; 00478 u->scheme = xstrdup(s); 00479 se += 2; /* skip over "//" */ 00480 s = se++; 00481 continue; 00482 } 00483 00484 /* Item was everything-but-path. Continue parse on rest */ 00485 *se = '\0'; 00486 break; 00487 } 00488 00489 /* Look for ...@host... */ 00490 fe = f = s; 00491 while (*fe && *fe != '@') fe++; 00492 if (*fe == '@') { 00493 s = fe + 1; 00494 *fe = '\0'; 00495 /* Look for user:password@host... */ 00496 while (fe > f && *fe != ':') fe--; 00497 if (*fe == ':') { 00498 *fe++ = '\0'; 00499 u->password = xstrdup(fe); 00500 } 00501 u->user = xstrdup(f); 00502 } 00503 00504 /* Look for ...host:port or [v6addr]:port*/ 00505 fe = f = s; 00506 if (strchr(fe, '[') && strchr(fe, ']')) { 00507 fe = strchr(f, ']'); 00508 *f++ = '\0'; 00509 *fe++ = '\0'; 00510 } 00511 assert(fe != NULL); /* XXX can't happen */ 00512 while (*fe && *fe != ':') fe++; 00513 if (*fe == ':') { 00514 *fe++ = '\0'; 00515 u->portstr = xstrdup(fe); 00516 if (u->portstr != NULL && u->portstr[0] != '\0') { 00517 char *end; 00518 u->port = strtol(u->portstr, &end, 0); 00519 if (!(end && *end == '\0')) { 00520 rpmlog(RPMLOG_ERR, _("url port must be a number\n")); 00521 myurl = _free(myurl); 00522 u = urlFree(u, "urlSplit (error #3)"); 00523 return -1; 00524 } 00525 } 00526 } 00527 u->host = xstrdup(f); 00528 00529 if (u->port < 0 && u->scheme != NULL) { 00530 struct servent *serv; 00531 /*@-multithreaded -moduncon @*/ 00532 /* HACK hkp:// might lookup "pgpkeyserver" */ 00533 serv = getservbyname(u->scheme, "tcp"); 00534 /*@=multithreaded =moduncon @*/ 00535 if (serv != NULL) 00536 u->port = (int) ntohs(serv->s_port); 00537 else if (u->ut == URL_IS_FTP) 00538 u->port = IPPORT_FTP; 00539 else if (u->ut == URL_IS_HKP) 00540 u->port = IPPORT_PGPKEYSERVER; 00541 else if (u->ut == URL_IS_HTTP) 00542 u->port = IPPORT_HTTP; 00543 else if (u->ut == URL_IS_HTTPS) 00544 u->port = IPPORT_HTTPS; 00545 } 00546 00547 myurl = _free(myurl); 00548 if (uret) { 00549 *uret = u; 00550 /*@-globs -mods @*/ /* FIX: rpmGlobalMacroContext not in <rpmlib.h> */ 00551 urlFind(uret, 0); 00552 /*@=globs =mods @*/ 00553 } 00554 return 0; 00555 } 00556 /*@=modfilesys@*/ 00557 00558 int urlGetFile(const char * url, const char * dest) 00559 { 00560 int rc; 00561 FD_t sfd = NULL; 00562 FD_t tfd = NULL; 00563 const char * sfuPath = NULL; 00564 int urlType = urlPath(url, &sfuPath); 00565 char *result; 00566 00567 if (*sfuPath == '\0') 00568 return FTPERR_UNKNOWN; 00569 00570 if (dest == NULL) { 00571 if ((dest = strrchr(sfuPath, '/')) != NULL) 00572 dest++; 00573 else 00574 dest = sfuPath; 00575 } 00576 if (dest == NULL) 00577 return FTPERR_UNKNOWN; 00578 00579 /*@-globs -mods@*/ /* Avoid including <rpmmacro.h> everywhere for now */ 00580 if (rpmExpandNumeric("%{?__urlgetfile:1}%{!?__urlgetfile:0}")) { 00581 result = rpmExpand("%{__urlgetfile ", url, " ", dest, "}", NULL); 00582 if (result != NULL && strcmp(result, "OK") == 0) 00583 rc = 0; 00584 else { 00585 rpmlog(RPMLOG_DEBUG, D_("failed to fetch URL %s via external command\n"), url); 00586 rc = FTPERR_UNKNOWN; 00587 } 00588 result = _free(result); 00589 goto exit; 00590 } 00591 /*@=globs =mods@*/ 00592 00593 sfd = Fopen(url, "r.ufdio"); 00594 if (sfd == NULL || Ferror(sfd)) { 00595 rpmlog(RPMLOG_DEBUG, D_("failed to open %s: %s\n"), url, Fstrerror(sfd)); 00596 rc = FTPERR_UNKNOWN; 00597 goto exit; 00598 } 00599 00600 /* XXX this can fail if directory in path does not exist. */ 00601 tfd = Fopen(dest, "w"); 00602 if (_url_debug) 00603 fprintf(stderr, "*** urlGetFile sfd %p %s tfd %p %s\n", sfd, url, (tfd ? tfd : NULL), dest); 00604 if (tfd == NULL || Ferror(tfd)) { 00605 rpmlog(RPMLOG_DEBUG, D_("failed to create %s: %s\n"), dest, Fstrerror(tfd)); 00606 rc = FTPERR_UNKNOWN; 00607 goto exit; 00608 } 00609 00610 switch (urlType) { 00611 case URL_IS_HTTPS: 00612 case URL_IS_HTTP: 00613 case URL_IS_HKP: 00614 case URL_IS_FTP: 00615 case URL_IS_PATH: 00616 case URL_IS_DASH: 00617 case URL_IS_UNKNOWN: 00618 if ((rc = ufdGetFile(sfd, tfd))) { 00619 (void) Unlink(dest); 00620 /* XXX FIXME: sfd possibly closed by copyData */ 00621 /*@-usereleased@*/ (void) Fclose(sfd) /*@=usereleased@*/ ; 00622 } 00623 sfd = NULL; /* XXX Fclose(sfd) done by ufdGetFile */ 00624 break; 00625 default: 00626 rc = FTPERR_UNKNOWN; 00627 break; 00628 } 00629 00630 exit: 00631 if (tfd) 00632 (void) Fclose(tfd); 00633 if (sfd) 00634 (void) Fclose(sfd); 00635 00636 return rc; 00637 }