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