- Main Page
- Related Pages
- Modules
- Data Structures
- Files

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