# rpmio/url.c

Go to the documentation of this file.

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