rpm
5.2.1
|
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 return (urlinfo) rpmioGetPool(pool, sizeof(*u)); 00155 } 00156 00157 urlinfo XurlNew(const char *msg, const char *fn, unsigned ln) 00158 { 00159 urlinfo u = urlGetPool(_urlPool); 00160 00161 u->proxyp = -1; 00162 u->port = -1; 00163 u->urltype = URL_IS_UNKNOWN; 00164 u->ctrl = NULL; 00165 u->data = NULL; 00166 u->location = NULL; 00167 u->etag = NULL; 00168 u->notify = urlNotify; 00169 /*@-assignexpose@*/ 00170 u->arg = urlNotifyArg; 00171 /*@=assignexpose@*/ 00172 u->rop = xcalloc(1, sizeof(*u->rop)); 00173 u->sop = xcalloc(1, sizeof(*u->sop)); 00174 u->top = xcalloc(1, sizeof(*u->top)); 00175 u->bufAlloced = 0; 00176 u->buf = NULL; 00177 u->allow = RPMURL_SERVER_HASRANGE; 00178 u->httpVersion = 0; 00179 u->magic = URLMAGIC; 00180 return (urlinfo) rpmioLinkPoolItem((rpmioItem)u, msg, fn, ln); 00181 } 00182 00183 void urlFreeCache(void) 00184 { 00185 if (_url_cache) { 00186 int i; 00187 for (i = 0; i < _url_count; i++) { 00188 if (_url_cache[i] == NULL) continue; 00189 _url_cache[i] = urlFree(_url_cache[i], "_url_cache"); 00190 if (_url_cache[i] == NULL) 00191 continue; 00192 yarnPossess(_url_cache[i]->_item.use); 00193 fprintf(stderr, 00194 _("warning: _url_cache[%d] %p nrefs(%ld) != 1 (%s %s)\n"), 00195 i, _url_cache[i], yarnPeekLock(_url_cache[i]->_item.use), 00196 (_url_cache[i]->host ? _url_cache[i]->host : ""), 00197 (_url_cache[i]->scheme ? _url_cache[i]->scheme : "")); 00198 yarnRelease(_url_cache[i]->_item.use); 00199 } 00200 } 00201 _url_cache = _free(_url_cache); 00202 _url_count = 0; 00203 } 00204 00205 static int urlStrcmp(/*@null@*/ const char * str1, /*@null@*/ const char * str2) 00206 /*@*/ 00207 { 00208 if (str1) 00209 if (str2) 00210 return strcmp(str1, str2); 00211 if (str1 != str2) 00212 return -1; 00213 return 0; 00214 } 00215 00216 /*@-mods@*/ 00217 static void urlFind(/*@null@*/ /*@in@*/ /*@out@*/ urlinfo * uret, int mustAsk) 00218 /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/ 00219 /*@modifies *uret, rpmGlobalMacroContext, fileSystem, internalState @*/ 00220 { 00221 urlinfo u; 00222 int ucx; 00223 int i = 0; 00224 00225 if (uret == NULL) 00226 return; 00227 00228 u = *uret; 00229 URLSANE(u); 00230 00231 ucx = -1; 00232 for (i = 0; i < _url_count; i++) { 00233 urlinfo ou = NULL; 00234 if (_url_cache == NULL || (ou = _url_cache[i]) == NULL) { 00235 if (ucx < 0) 00236 ucx = i; 00237 continue; 00238 } 00239 00240 /* Check for cache-miss condition. A cache miss is 00241 * a) both items are not NULL and don't compare. 00242 * b) either of the items is not NULL. 00243 */ 00244 if (urlStrcmp(u->scheme, ou->scheme)) 00245 continue; 00246 if (urlStrcmp(u->host, ou->host)) 00247 continue; 00248 if (urlStrcmp(u->user, ou->user)) 00249 continue; 00250 if (urlStrcmp(u->portstr, ou->portstr)) 00251 continue; 00252 break; /* Found item in cache */ 00253 } 00254 00255 if (i == _url_count) { 00256 if (ucx < 0) { 00257 ucx = _url_count++; 00258 _url_cache = xrealloc(_url_cache, sizeof(*_url_cache) * _url_count); 00259 } 00260 if (_url_cache) /* XXX always true */ 00261 _url_cache[ucx] = urlLink(u, "_url_cache (miss)"); 00262 u = urlFree(u, "urlSplit (urlFind miss)"); 00263 } else { 00264 ucx = i; 00265 u = urlFree(u, "urlSplit (urlFind hit)"); 00266 } 00267 00268 /* This URL is now cached. */ 00269 00270 if (_url_cache) /* XXX always true */ 00271 u = urlLink(_url_cache[ucx], "_url_cache"); 00272 *uret = u; 00273 /*@-usereleased@*/ 00274 u = urlFree(u, "_url_cache (urlFind)"); 00275 /*@=usereleased@*/ 00276 assert(u != NULL); 00277 00278 /* Zap proxy host and port in case they have been reset */ 00279 u->proxyp = -1; 00280 u->proxyh = _free(u->proxyh); 00281 00282 /* Perform one-time FTP initialization */ 00283 if (u->urltype == URL_IS_FTP) { 00284 00285 if (mustAsk || (u->user != NULL && u->password == NULL)) { 00286 const char * host = (u->host ? u->host : ""); 00287 const char * user = (u->user ? u->user : ""); 00288 char * prompt; 00289 prompt = alloca(strlen(host) + strlen(user) + 256); 00290 sprintf(prompt, _("Password for %s@%s: "), user, host); 00291 u->password = _free(u->password); 00292 /*@-dependenttrans -moduncon @*/ 00293 u->password = Getpass(prompt); 00294 /*@=dependenttrans =moduncon @*/ 00295 if (u->password) 00296 u->password = xstrdup(u->password); 00297 } 00298 00299 if (u->proxyh == NULL) { 00300 const char *proxy = rpmExpand("%{_ftpproxy}", NULL); 00301 if (proxy && *proxy != '%') { 00302 /*@observer@*/ 00303 const char * host = (u->host ? u->host : ""); 00304 const char *uu = (u->user ? u->user : "anonymous"); 00305 char *nu = xmalloc(strlen(uu) + sizeof("@") + strlen(host)); 00306 (void) stpcpy( stpcpy( stpcpy(nu, uu), "@"), host); 00307 u->proxyu = nu; 00308 u->proxyh = xstrdup(proxy); 00309 } 00310 proxy = _free(proxy); 00311 } 00312 00313 if (u->proxyp < 0) { 00314 const char *proxy = rpmExpand("%{_ftpport}", NULL); 00315 if (proxy && *proxy != '%') { 00316 char *end = NULL; 00317 int port = strtol(proxy, &end, 0); 00318 if (!(end && *end == '\0')) { 00319 fprintf(stderr, _("error: %sport must be a number\n"), 00320 (u->scheme ? u->scheme : "")); 00321 return; 00322 } 00323 u->proxyp = port; 00324 } 00325 proxy = _free(proxy); 00326 } 00327 } 00328 00329 /* Perform one-time HTTP initialization */ 00330 if (u->urltype == URL_IS_HTTP || u->urltype == URL_IS_HTTPS || u->urltype == URL_IS_HKP) { 00331 00332 if (u->proxyh == NULL) { 00333 const char *proxy = rpmExpand("%{_httpproxy}", NULL); 00334 if (proxy && *proxy != '%') 00335 u->proxyh = xstrdup(proxy); 00336 proxy = _free(proxy); 00337 } 00338 00339 if (u->proxyp < 0) { 00340 const char *proxy = rpmExpand("%{_httpport}", NULL); 00341 if (proxy && *proxy != '%') { 00342 char *end; 00343 int port = strtol(proxy, &end, 0); 00344 if (!(end && *end == '\0')) { 00345 fprintf(stderr, _("error: %sport must be a number\n"), 00346 (u->scheme ? u->scheme : "")); 00347 return; 00348 } 00349 u->proxyp = port; 00350 } 00351 proxy = _free(proxy); 00352 } 00353 00354 } 00355 00356 return; 00357 } 00358 /*@=mods@*/ 00359 00362 /*@observer@*/ /*@unchecked@*/ 00363 static struct urlstring { 00364 /*@observer@*/ /*@null@*/ 00365 const char * leadin; 00366 urltype ret; 00367 } urlstrings[] = { 00368 { "file://", URL_IS_PATH }, 00369 { "ftp://", URL_IS_FTP }, 00370 { "hkp://", URL_IS_HKP }, 00371 { "http://", URL_IS_HTTP }, 00372 { "https://", URL_IS_HTTPS }, 00373 { "-", URL_IS_DASH }, 00374 { NULL, URL_IS_UNKNOWN } 00375 }; 00376 00377 urltype urlIsURL(const char * url) 00378 { 00379 struct urlstring *us; 00380 00381 if (url && *url) { 00382 for (us = urlstrings; us->leadin != NULL; us++) { 00383 if (strncmp(url, us->leadin, strlen(us->leadin))) 00384 continue; 00385 return us->ret; 00386 } 00387 } 00388 00389 return URL_IS_UNKNOWN; 00390 } 00391 00392 /* Return path portion of url (or pointer to NUL if url == NULL) */ 00393 urltype urlPath(const char * url, const char ** pathp) 00394 { 00395 const char *path; 00396 int urltype; 00397 00398 path = url; 00399 urltype = urlIsURL(url); 00400 switch (urltype) { 00401 case URL_IS_FTP: 00402 url += sizeof("ftp://") - 1; 00403 path = strchr(url, '/'); 00404 if (path == NULL) path = url + strlen(url); 00405 break; 00406 case URL_IS_PATH: 00407 url += sizeof("file://") - 1; 00408 path = strchr(url, '/'); 00409 if (path == NULL) path = url + strlen(url); 00410 break; 00411 case URL_IS_HKP: 00412 url += sizeof("hkp://") - 1; 00413 path = strchr(url, '/'); 00414 if (path == NULL) path = url + strlen(url); 00415 break; 00416 case URL_IS_HTTP: 00417 url += sizeof("http://") - 1; 00418 path = strchr(url, '/'); 00419 if (path == NULL) path = url + strlen(url); 00420 break; 00421 case URL_IS_HTTPS: 00422 url += sizeof("https://") - 1; 00423 path = strchr(url, '/'); 00424 if (path == NULL) path = url + strlen(url); 00425 break; 00426 case URL_IS_UNKNOWN: 00427 if (path == NULL) path = ""; 00428 break; 00429 case URL_IS_DASH: 00430 path = ""; 00431 break; 00432 } 00433 if (pathp) 00434 /*@-observertrans@*/ 00435 *pathp = path; 00436 /*@=observertrans@*/ 00437 return urltype; 00438 } 00439 00443 static const char * urlStrdup(const char * url) 00444 /*@*/ 00445 { 00446 size_t nb = strlen(url); 00447 char * t = xmalloc(nb + 1 + 1); 00448 const char * nurl = t; 00449 while (*url != '\0') 00450 *t++ = *url++; 00451 *t = '\0'; 00452 return nurl; 00453 } 00454 00455 /* 00456 * Split URL into components. The URL can look like 00457 * scheme://user:password@host:port/path 00458 * or as in RFC2732 for IPv6 address 00459 * service://user:password@[ip:v6:ad:dr:es:s]:port/path?query#fragment 00460 */ 00461 /*@-modfilesys@*/ 00462 int urlSplit(const char * url, urlinfo *uret) 00463 { 00464 urlinfo u; 00465 char *myurl; 00466 char *s, *se, *f, *fe; 00467 00468 if (uret == NULL) 00469 return -1; 00470 if ((u = urlNew("urlSplit")) == NULL) 00471 return -1; 00472 00473 myurl = xstrdup(url); 00474 if ((se = strrchr(myurl, '#')) != NULL) { 00475 *se++ = '\0'; 00476 u->fragment = xstrdup(se); 00477 } 00478 if ((se = strrchr(myurl, '?')) != NULL) { 00479 *se++ = '\0'; 00480 u->query = xstrdup(se); 00481 } 00482 00483 u->url = urlStrdup(myurl); /* XXX +1 byte for pesky trailing '/' */ 00484 u->urltype = urlIsURL(myurl); 00485 00486 se = s = myurl; 00487 while (1) { 00488 /* Point to end of next item */ 00489 while (*se && *se != '/') se++; 00490 /* Item was scheme. Save scheme and go for the rest ...*/ 00491 if (*se && (se != s) && se[-1] == ':' && se[0] == '/' && se[1] == '/') { 00492 se[-1] = '\0'; 00493 u->scheme = xstrdup(s); 00494 se += 2; /* skip over "//" */ 00495 s = se++; 00496 continue; 00497 } 00498 00499 /* Item was everything-but-path. Continue parse on rest */ 00500 *se = '\0'; 00501 break; 00502 } 00503 00504 /* Look for ...@host... */ 00505 fe = f = s; 00506 while (*fe && *fe != '@') fe++; 00507 if (*fe == '@') { 00508 s = fe + 1; 00509 *fe = '\0'; 00510 /* Look for user:password@host... */ 00511 while (fe > f && *fe != ':') fe--; 00512 if (*fe == ':') { 00513 *fe++ = '\0'; 00514 u->password = xstrdup(fe); 00515 } 00516 u->user = xstrdup(f); 00517 } 00518 00519 /* Look for ...host:port or [v6addr]:port*/ 00520 fe = f = s; 00521 if (strchr(fe, '[') && strchr(fe, ']')) { 00522 fe = strchr(f, ']'); 00523 *f++ = '\0'; 00524 *fe++ = '\0'; 00525 } 00526 assert(fe != NULL); /* XXX can't happen */ 00527 while (*fe && *fe != ':') fe++; 00528 if (*fe == ':') { 00529 *fe++ = '\0'; 00530 u->portstr = xstrdup(fe); 00531 if (u->portstr != NULL && u->portstr[0] != '\0') { 00532 char *end; 00533 u->port = strtol(u->portstr, &end, 0); 00534 if (!(end && *end == '\0')) { 00535 rpmlog(RPMLOG_ERR, _("url port must be a number\n")); 00536 myurl = _free(myurl); 00537 u = urlFree(u, "urlSplit (error #3)"); 00538 return -1; 00539 } 00540 } 00541 } 00542 u->host = xstrdup(f); 00543 00544 if (u->port < 0 && u->scheme != NULL) { 00545 struct servent *serv; 00546 /*@-multithreaded -moduncon @*/ 00547 /* HACK hkp:// might lookup "pgpkeyserver" */ 00548 serv = getservbyname(u->scheme, "tcp"); 00549 /*@=multithreaded =moduncon @*/ 00550 if (serv != NULL) 00551 u->port = (int) ntohs(serv->s_port); 00552 else if (u->urltype == URL_IS_FTP) 00553 u->port = IPPORT_FTP; 00554 else if (u->urltype == URL_IS_HKP) 00555 u->port = IPPORT_PGPKEYSERVER; 00556 else if (u->urltype == URL_IS_HTTP) 00557 u->port = IPPORT_HTTP; 00558 else if (u->urltype == URL_IS_HTTPS) 00559 u->port = IPPORT_HTTPS; 00560 } 00561 00562 myurl = _free(myurl); 00563 if (uret) { 00564 *uret = u; 00565 /*@-globs -mods @*/ /* FIX: rpmGlobalMacroContext not in <rpmlib.h> */ 00566 urlFind(uret, 0); 00567 /*@=globs =mods @*/ 00568 } 00569 return 0; 00570 } 00571 /*@=modfilesys@*/ 00572 00573 int urlGetFile(const char * url, const char * dest) 00574 { 00575 int rc; 00576 FD_t sfd = NULL; 00577 FD_t tfd = NULL; 00578 const char * sfuPath = NULL; 00579 int urlType = urlPath(url, &sfuPath); 00580 char *result; 00581 00582 if (*sfuPath == '\0') 00583 return FTPERR_UNKNOWN; 00584 00585 if (dest == NULL) { 00586 if ((dest = strrchr(sfuPath, '/')) != NULL) 00587 dest++; 00588 else 00589 dest = sfuPath; 00590 } 00591 if (dest == NULL) 00592 return FTPERR_UNKNOWN; 00593 00594 /*@-globs -mods@*/ /* Avoid including <rpmmacro.h> everywhere for now */ 00595 if (rpmExpandNumeric("%{?__urlgetfile:1}%{!?__urlgetfile:0}")) { 00596 result = rpmExpand("%{__urlgetfile ", url, " ", dest, "}", NULL); 00597 if (result != NULL && strcmp(result, "OK") == 0) 00598 rc = 0; 00599 else { 00600 rpmlog(RPMLOG_DEBUG, D_("failed to fetch URL %s via external command\n"), url); 00601 rc = FTPERR_UNKNOWN; 00602 } 00603 result = _free(result); 00604 goto exit; 00605 } 00606 /*@=globs =mods@*/ 00607 00608 sfd = Fopen(url, "r.ufdio"); 00609 if (sfd == NULL || Ferror(sfd)) { 00610 rpmlog(RPMLOG_DEBUG, D_("failed to open %s: %s\n"), url, Fstrerror(sfd)); 00611 rc = FTPERR_UNKNOWN; 00612 goto exit; 00613 } 00614 00615 /* XXX this can fail if directory in path does not exist. */ 00616 tfd = Fopen(dest, "w"); 00617 if (_url_debug) 00618 fprintf(stderr, "*** urlGetFile sfd %p %s tfd %p %s\n", sfd, url, (tfd ? tfd : NULL), dest); 00619 if (tfd == NULL || Ferror(tfd)) { 00620 rpmlog(RPMLOG_DEBUG, D_("failed to create %s: %s\n"), dest, Fstrerror(tfd)); 00621 rc = FTPERR_UNKNOWN; 00622 goto exit; 00623 } 00624 00625 switch (urlType) { 00626 case URL_IS_HTTPS: 00627 case URL_IS_HTTP: 00628 case URL_IS_HKP: 00629 case URL_IS_FTP: 00630 case URL_IS_PATH: 00631 case URL_IS_DASH: 00632 case URL_IS_UNKNOWN: 00633 if ((rc = ufdGetFile(sfd, tfd))) { 00634 (void) Unlink(dest); 00635 /* XXX FIXME: sfd possibly closed by copyData */ 00636 /*@-usereleased@*/ (void) Fclose(sfd) /*@=usereleased@*/ ; 00637 } 00638 sfd = NULL; /* XXX Fclose(sfd) done by ufdGetFile */ 00639 break; 00640 default: 00641 rc = FTPERR_UNKNOWN; 00642 break; 00643 } 00644 00645 exit: 00646 if (tfd) 00647 (void) Fclose(tfd); 00648 if (sfd) 00649 (void) Fclose(sfd); 00650 00651 return rc; 00652 }