Main Page Modules Data Structures File List Data Fields Globals Related Pages

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