source: trunk/third/rpm/rpmio/url.c @ 19079

Revision 19079, 13.2 KB checked in by ghudson, 22 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r19078, which included commits to RCS files with non-trunk default branches.
Line 
1/** \ingroup rpmio
2 * \file rpmio/url.c
3 */
4
5#include "system.h"
6
7#include <netinet/in.h>
8
9#include <rpmmacro.h>
10#include <rpmmessages.h>
11#include <rpmio_internal.h>
12
13#include "debug.h"
14
15/*@access FD_t@*/               /* XXX compared with NULL */
16/*@access urlinfo@*/
17
18#ifndef IPPORT_FTP
19#define IPPORT_FTP      21
20#endif
21#ifndef IPPORT_HTTP
22#define IPPORT_HTTP     80
23#endif
24
25/**
26 */
27/*@unchecked@*/
28int _url_iobuf_size = RPMURL_IOBUF_SIZE;
29
30/**
31 */
32/*@unchecked@*/
33int _url_debug = 0;
34
35#define URLDBG(_f, _m, _x)      if ((_url_debug | (_f)) & (_m)) fprintf _x
36
37#define URLDBGIO(_f, _x)        URLDBG((_f), RPMURL_DEBUG_IO, _x)
38#define URLDBGREFS(_f, _x)      URLDBG((_f), RPMURL_DEBUG_REFS, _x)
39
40/**
41 */
42/*@-incondefs@*/
43/*@unchecked@*/
44/*@only@*/ /*@null@*/
45urlinfo *_url_cache = NULL;
46/*@=incondefs@*/
47
48/**
49 */
50/*@-incondefs@*/
51/*@unchecked@*/
52int _url_count = 0;
53/*@=incondefs@*/
54
55/**
56 * Wrapper to free(3), hides const compilation noise, permit NULL, return NULL.
57 * @param p             memory to free
58 * @retval              NULL always
59 */
60/*@unused@*/ static inline /*@null@*/ void *
61_free(/*@only@*/ /*@null@*/ const void * p) /*@modifies p@*/
62{
63    if (p != NULL)      free((void *)p);
64    return NULL;
65}
66
67urlinfo XurlLink(urlinfo u, const char *msg, const char *file, unsigned line)
68{
69    URLSANE(u);
70    u->nrefs++;
71/*@-modfilesys@*/
72URLDBGREFS(0, (stderr, "--> url %p ++ %d %s at %s:%u\n", u, u->nrefs, msg, file, line));
73/*@=modfilesys@*/
74    /*@-refcounttrans@*/ return u; /*@=refcounttrans@*/
75}
76
77urlinfo XurlNew(const char *msg, const char *file, unsigned line)
78{
79    urlinfo u;
80    if ((u = xmalloc(sizeof(*u))) == NULL)
81        return NULL;
82    memset(u, 0, sizeof(*u));
83    u->proxyp = -1;
84    u->port = -1;
85    u->urltype = URL_IS_UNKNOWN;
86    u->ctrl = NULL;
87    u->data = NULL;
88    u->bufAlloced = 0;
89    u->buf = NULL;
90    u->httpHasRange = 1;
91    u->httpVersion = 0;
92    u->nrefs = 0;
93    u->magic = URLMAGIC;
94    return XurlLink(u, msg, file, line);
95}
96
97urlinfo XurlFree(urlinfo u, const char *msg, const char *file, unsigned line)
98{
99    int xx;
100
101    URLSANE(u);
102URLDBGREFS(0, (stderr, "--> url %p -- %d %s at %s:%u\n", u, u->nrefs, msg, file, line));
103    if (--u->nrefs > 0)
104        /*@-refcounttrans -retalias@*/ return u; /*@=refcounttrans =retalias@*/
105    if (u->ctrl) {
106#ifndef NOTYET
107        void * fp = fdGetFp(u->ctrl);
108        /*@-branchstate@*/
109        if (fp) {
110            fdPush(u->ctrl, fpio, fp, -1);   /* Push fpio onto stack */
111            (void) Fclose(u->ctrl);
112        } else if (fdio->_fileno(u->ctrl) >= 0)
113            xx = fdio->close(u->ctrl);
114        /*@=branchstate@*/
115#else
116        (void) Fclose(u->ctrl);
117#endif
118
119        u->ctrl = fdio->_fdderef(u->ctrl, "persist ctrl (urlFree)", file, line);
120        /*@-usereleased@*/
121        if (u->ctrl)
122            fprintf(stderr, _("warning: u %p ctrl %p nrefs != 0 (%s %s)\n"),
123                        u, u->ctrl, (u->host ? u->host : ""),
124                        (u->service ? u->service : ""));
125        /*@=usereleased@*/
126    }
127    if (u->data) {
128#ifndef NOTYET
129        void * fp = fdGetFp(u->data);
130        if (fp) {
131            fdPush(u->data, fpio, fp, -1);   /* Push fpio onto stack */
132            (void) Fclose(u->data);
133        } else if (fdio->_fileno(u->data) >= 0)
134            xx = fdio->close(u->data);
135#else
136        (void) Fclose(u->ctrl);
137#endif
138
139        u->data = fdio->_fdderef(u->data, "persist data (urlFree)", file, line);
140        /*@-usereleased@*/
141        if (u->data)
142            fprintf(stderr, _("warning: u %p data %p nrefs != 0 (%s %s)\n"),
143                        u, u->data, (u->host ? u->host : ""),
144                        (u->service ? u->service : ""));
145        /*@=usereleased@*/
146    }
147    u->buf = _free(u->buf);
148    u->url = _free(u->url);
149    u->service = _free((void *)u->service);
150    u->user = _free((void *)u->user);
151    u->password = _free((void *)u->password);
152    u->host = _free((void *)u->host);
153    u->portstr = _free((void *)u->portstr);
154    u->proxyu = _free((void *)u->proxyu);
155    u->proxyh = _free((void *)u->proxyh);
156
157    /*@-refcounttrans@*/ u = _free(u); /*@-refcounttrans@*/
158    return NULL;
159}
160
161/*@-boundswrite@*/
162void urlFreeCache(void)
163{
164    if (_url_cache) {
165        int i;
166        for (i = 0; i < _url_count; i++) {
167            if (_url_cache[i] == NULL) continue;
168            _url_cache[i] = urlFree(_url_cache[i], "_url_cache");
169            if (_url_cache[i])
170                fprintf(stderr,
171                        _("warning: _url_cache[%d] %p nrefs(%d) != 1 (%s %s)\n"),
172                        i, _url_cache[i], _url_cache[i]->nrefs,
173                        (_url_cache[i]->host ? _url_cache[i]->host : ""),
174                        (_url_cache[i]->service ? _url_cache[i]->service : ""));
175        }
176    }
177    _url_cache = _free(_url_cache);
178    _url_count = 0;
179}
180/*@=boundswrite@*/
181
182static int urlStrcmp(/*@null@*/ const char * str1, /*@null@*/ const char * str2)
183        /*@*/
184{
185    if (str1)
186        if (str2)
187            return strcmp(str1, str2);
188    if (str1 != str2)
189        return -1;
190    return 0;
191}
192
193/*@-boundswrite@*/
194/*@-mods@*/
195static void urlFind(/*@null@*/ /*@in@*/ /*@out@*/ urlinfo * uret, int mustAsk)
196        /*@globals rpmGlobalMacroContext, fileSystem, internalState @*/
197        /*@modifies *uret, rpmGlobalMacroContext, fileSystem, internalState @*/
198{
199    urlinfo u;
200    int ucx;
201    int i = 0;
202
203    if (uret == NULL)
204        return;
205
206    u = *uret;
207    URLSANE(u);
208
209    ucx = -1;
210    for (i = 0; i < _url_count; i++) {
211        urlinfo ou = NULL;
212        if (_url_cache == NULL || (ou = _url_cache[i]) == NULL) {
213            if (ucx < 0)
214                ucx = i;
215            continue;
216        }
217
218        /* Check for cache-miss condition. A cache miss is
219         *    a) both items are not NULL and don't compare.
220         *    b) either of the items is not NULL.
221         */
222        if (urlStrcmp(u->service, ou->service))
223            continue;
224        if (urlStrcmp(u->host, ou->host))
225            continue;
226        if (urlStrcmp(u->user, ou->user))
227            continue;
228        if (urlStrcmp(u->portstr, ou->portstr))
229            continue;
230        break;  /* Found item in cache */
231    }
232
233    if (i == _url_count) {
234        if (ucx < 0) {
235            ucx = _url_count++;
236            _url_cache = xrealloc(_url_cache, sizeof(*_url_cache) * _url_count);
237        }
238        if (_url_cache)         /* XXX always true */
239            _url_cache[ucx] = urlLink(u, "_url_cache (miss)");
240        u = urlFree(u, "urlSplit (urlFind miss)");
241    } else {
242        ucx = i;
243        u = urlFree(u, "urlSplit (urlFind hit)");
244    }
245
246    /* This URL is now cached. */
247
248    if (_url_cache)             /* XXX always true */
249        u = urlLink(_url_cache[ucx], "_url_cache");
250    *uret = u;
251    /*@-usereleased@*/
252    u = urlFree(u, "_url_cache (urlFind)");
253    /*@=usereleased@*/
254
255    /* Zap proxy host and port in case they have been reset */
256    u->proxyp = -1;
257    u->proxyh = _free(u->proxyh);
258
259    /* Perform one-time FTP initialization */
260    if (u->urltype == URL_IS_FTP) {
261
262        if (mustAsk || (u->user != NULL && u->password == NULL)) {
263            const char * host = (u->host ? u->host : "");
264            const char * user = (u->user ? u->user : "");
265            char * prompt;
266            prompt = alloca(strlen(host) + strlen(user) + 256);
267            sprintf(prompt, _("Password for %s@%s: "), user, host);
268            u->password = _free(u->password);
269/*@-dependenttrans -moduncon @*/
270            u->password = /*@-unrecog@*/ getpass(prompt) /*@=unrecog@*/;
271/*@=dependenttrans =moduncon @*/
272            if (u->password)
273                u->password = xstrdup(u->password);
274        }
275
276        if (u->proxyh == NULL) {
277            const char *proxy = rpmExpand("%{_ftpproxy}", NULL);
278            if (proxy && *proxy != '%') {
279                /*@observer@*/ const char * host = (u->host ? u->host : "");
280                const char *uu = (u->user ? u->user : "anonymous");
281                char *nu = xmalloc(strlen(uu) + sizeof("@") + strlen(host));
282                (void) stpcpy( stpcpy( stpcpy(nu, uu), "@"), host);
283                u->proxyu = nu;
284                u->proxyh = xstrdup(proxy);
285            }
286            proxy = _free(proxy);
287        }
288
289        if (u->proxyp < 0) {
290            const char *proxy = rpmExpand("%{_ftpport}", NULL);
291            if (proxy && *proxy != '%') {
292                char *end;
293                int port = strtol(proxy, &end, 0);
294                if (!(end && *end == '\0')) {
295                    fprintf(stderr, _("error: %sport must be a number\n"),
296                        (u->service ? u->service : ""));
297                    return;
298                }
299                u->proxyp = port;
300            }
301            proxy = _free(proxy);
302        }
303    }
304
305    /* Perform one-time HTTP initialization */
306    if (u->urltype == URL_IS_HTTP) {
307
308        if (u->proxyh == NULL) {
309            const char *proxy = rpmExpand("%{_httpproxy}", NULL);
310            if (proxy && *proxy != '%')
311                u->proxyh = xstrdup(proxy);
312            proxy = _free(proxy);
313        }
314
315        if (u->proxyp < 0) {
316            const char *proxy = rpmExpand("%{_httpport}", NULL);
317            if (proxy && *proxy != '%') {
318                char *end;
319                int port = strtol(proxy, &end, 0);
320                if (!(end && *end == '\0')) {
321                    fprintf(stderr, _("error: %sport must be a number\n"),
322                        (u->service ? u->service : ""));
323                    return;
324                }
325                u->proxyp = port;
326            }
327            proxy = _free(proxy);
328        }
329
330    }
331
332    return;
333}
334/*@=mods@*/
335/*@=boundswrite@*/
336
337/**
338 */
339/*@observer@*/ /*@unchecked@*/
340static struct urlstring {
341/*@observer@*/ /*@null@*/
342    const char * leadin;
343    urltype     ret;
344} urlstrings[] = {
345    { "file://",        URL_IS_PATH },
346    { "ftp://",         URL_IS_FTP },
347    { "http://",        URL_IS_HTTP },
348    { "-",              URL_IS_DASH },
349    { NULL,             URL_IS_UNKNOWN }
350};
351
352urltype urlIsURL(const char * url)
353{
354    struct urlstring *us;
355
356/*@-boundsread@*/
357    if (url && *url) {
358        for (us = urlstrings; us->leadin != NULL; us++) {
359            if (strncmp(url, us->leadin, strlen(us->leadin)))
360                continue;
361            return us->ret;
362        }
363    }
364/*@=boundsread@*/
365
366    return URL_IS_UNKNOWN;
367}
368
369/*@-boundswrite@*/
370/* Return path portion of url (or pointer to NUL if url == NULL) */
371urltype urlPath(const char * url, const char ** pathp)
372{
373    const char *path;
374    int urltype;
375
376    path = url;
377    urltype = urlIsURL(url);
378    /*@-branchstate@*/
379    switch (urltype) {
380    case URL_IS_FTP:
381        url += sizeof("ftp://") - 1;
382        path = strchr(url, '/');
383        if (path == NULL) path = url + strlen(url);
384        break;
385    case URL_IS_HTTP:
386    case URL_IS_PATH:
387        url += sizeof("file://") - 1;
388        path = strchr(url, '/');
389        if (path == NULL) path = url + strlen(url);
390        break;
391    case URL_IS_UNKNOWN:
392        if (path == NULL) path = "";
393        break;
394    case URL_IS_DASH:
395        path = "";
396        break;
397    }
398    /*@=branchstate@*/
399    if (pathp)
400        /*@-observertrans@*/
401        *pathp = path;
402        /*@=observertrans@*/
403    return urltype;
404}
405/*@=boundswrite@*/
406
407/*
408 * Split URL into components. The URL can look like
409 *      service://user:password@host:port/path
410 */
411/*@-bounds@*/
412/*@-modfilesys@*/
413int urlSplit(const char * url, urlinfo *uret)
414{
415    urlinfo u;
416    char *myurl;
417    char *s, *se, *f, *fe;
418
419    if (uret == NULL)
420        return -1;
421    if ((u = urlNew("urlSplit")) == NULL)
422        return -1;
423
424    if ((se = s = myurl = xstrdup(url)) == NULL) {
425        u = urlFree(u, "urlSplit (error #1)");
426        return -1;
427    }
428
429    u->url = xstrdup(url);
430    u->urltype = urlIsURL(url);
431
432    while (1) {
433        /* Point to end of next item */
434        while (*se && *se != '/') se++;
435        /* Item was service. Save service and go for the rest ...*/
436        if (*se && (se != s) && se[-1] == ':' && se[0] == '/' && se[1] == '/') {
437                se[-1] = '\0';
438            u->service = xstrdup(s);
439            se += 2;    /* skip over "//" */
440            s = se++;
441            continue;
442        }
443       
444        /* Item was everything-but-path. Continue parse on rest */
445        *se = '\0';
446        break;
447    }
448
449    /* Look for ...@host... */
450    fe = f = s;
451    while (*fe && *fe != '@') fe++;
452    /*@-branchstate@*/
453    if (*fe == '@') {
454        s = fe + 1;
455        *fe = '\0';
456        /* Look for user:password@host... */
457        while (fe > f && *fe != ':') fe--;
458        if (*fe == ':') {
459            *fe++ = '\0';
460            u->password = xstrdup(fe);
461        }
462        u->user = xstrdup(f);
463    }
464    /*@=branchstate@*/
465
466    /* Look for ...host:port */
467    fe = f = s;
468    while (*fe && *fe != ':') fe++;
469    if (*fe == ':') {
470        *fe++ = '\0';
471        u->portstr = xstrdup(fe);
472        if (u->portstr != NULL && u->portstr[0] != '\0') {
473            char *end;
474            u->port = strtol(u->portstr, &end, 0);
475            if (!(end && *end == '\0')) {
476                rpmMessage(RPMMESS_ERROR, _("url port must be a number\n"));
477                myurl = _free(myurl);
478                u = urlFree(u, "urlSplit (error #3)");
479                return -1;
480            }
481        }
482    }
483    u->host = xstrdup(f);
484
485    if (u->port < 0 && u->service != NULL) {
486        struct servent *serv;
487        /*@-multithreaded -moduncon @*/
488        serv = getservbyname(u->service, "tcp");
489        /*@=multithreaded =moduncon @*/
490        if (serv != NULL)
491            u->port = ntohs(serv->s_port);
492        else if (u->urltype == URL_IS_FTP)
493            u->port = IPPORT_FTP;
494        else if (u->urltype == URL_IS_HTTP)
495            u->port = IPPORT_HTTP;
496    }
497
498    myurl = _free(myurl);
499    if (uret) {
500        *uret = u;
501/*@-globs -mods @*/ /* FIX: rpmGlobalMacroContext not in <rpmlib.h> */
502        urlFind(uret, 0);
503/*@=globs =mods @*/
504    }
505    return 0;
506}
507/*@=modfilesys@*/
508/*@=bounds@*/
509
510int urlGetFile(const char * url, const char * dest)
511{
512    int rc;
513    FD_t sfd = NULL;
514    FD_t tfd = NULL;
515    const char * sfuPath = NULL;
516    int urlType = urlPath(url, &sfuPath);
517
518    if (*sfuPath == '\0')
519        return FTPERR_UNKNOWN;
520       
521    sfd = Fopen(url, "r.ufdio");
522    if (sfd == NULL || Ferror(sfd)) {
523        rpmMessage(RPMMESS_DEBUG, _("failed to open %s: %s\n"), url, Fstrerror(sfd));
524        rc = FTPERR_UNKNOWN;
525        goto exit;
526    }
527
528    if (dest == NULL) {
529        if ((dest = strrchr(sfuPath, '/')) != NULL)
530            dest++;
531        else
532            dest = sfuPath;
533    }
534
535    if (dest == NULL)
536        return FTPERR_UNKNOWN;
537
538    tfd = Fopen(dest, "w.ufdio");
539if (_url_debug)
540fprintf(stderr, "*** urlGetFile sfd %p %s tfd %p %s\n", sfd, url, (tfd ? tfd : NULL), dest);
541    if (tfd == NULL || Ferror(tfd)) {
542        /* XXX Fstrerror */
543        rpmMessage(RPMMESS_DEBUG, _("failed to create %s: %s\n"), dest, Fstrerror(tfd));
544        rc = FTPERR_UNKNOWN;
545        goto exit;
546    }
547
548    switch (urlType) {
549    case URL_IS_FTP:
550    case URL_IS_HTTP:
551    case URL_IS_PATH:
552    case URL_IS_DASH:
553    case URL_IS_UNKNOWN:
554        if ((rc = ufdGetFile(sfd, tfd))) {
555            (void) Unlink(dest);
556            /* XXX FIXME: sfd possibly closed by copyData */
557            /*@-usereleased@*/ (void) Fclose(sfd) /*@=usereleased@*/ ;
558        }
559        sfd = NULL;     /* XXX Fclose(sfd) done by ufdGetFile */
560        break;
561    default:
562        rc = FTPERR_UNKNOWN;
563        break;
564    }
565
566exit:
567    if (tfd)
568        (void) Fclose(tfd);
569    if (sfd)
570        (void) Fclose(sfd);
571
572    return rc;
573}
Note: See TracBrowser for help on using the repository browser.