Propagate splint-3.0.1.7 close(2) internalState annotation throughout.
[platform/upstream/rpm.git] / rpmio / url.c
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@*/
28 int _url_iobuf_size = RPMURL_IOBUF_SIZE;
29
30 /**
31  */
32 /*@unchecked@*/
33 int _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@*/
45 urlinfo *_url_cache = NULL;
46 /*@=incondefs@*/
47
48 /**
49  */
50 /*@-incondefs@*/
51 /*@unchecked@*/
52 int _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
67 urlinfo XurlLink(urlinfo u, const char *msg, const char *file, unsigned line)
68 {
69     URLSANE(u);
70     u->nrefs++;
71 /*@-modfilesys@*/
72 URLDBGREFS(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
77 urlinfo 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
97 urlinfo XurlFree(urlinfo u, const char *msg, const char *file, unsigned line)
98 {
99     int xx;
100
101     URLSANE(u);
102 URLDBGREFS(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@*/
162 void 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
182 static 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@*/
195 static 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@*/
340 static 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
352 urltype 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) */
371 urltype 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@*/
413 int 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
510 int 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");
539 if (_url_debug)
540 fprintf(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
566 exit:
567     if (tfd)
568         (void) Fclose(tfd);
569     if (sfd)
570         (void) Fclose(sfd);
571
572     return rc;
573 }