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

Generated on Tue Feb 21 14:28:56 2012 for rpm by  doxygen 1.4.7