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