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

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