rpm  5.4.4
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 #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 }