rpm 5.3.12
rpmio/url.c
Go to the documentation of this file.
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 }