rpm 5.2.1

- Main Page
- Related Pages
- Modules
- Data Structures
- Files

- File List
- Globals

url.c
Go to the documentation of this file.
1
5 #include "system.h"
6
7 #include <netinet/in.h>
8
9 #include <rpmmacro.h>
10 #include <rpmcb.h>
11 #include <rpmio_internal.h>
12 #ifdef WITH_NEON
13 #include <rpmdav.h>
14 #endif
15
16 #include "debug.h"
17
18 /*@access FD_t@*/ /* XXX compared with NULL */
19 /*@access urlinfo@*/
20
21 #ifndef IPPORT_FTP
22 #define IPPORT_FTP 21
23 #endif
24 #ifndef IPPORT_HTTP
25 #define IPPORT_HTTP 80
26 #endif
27 #ifndef IPPORT_HTTPS
28 #define IPPORT_HTTPS 443
29 #endif
30 #ifndef IPPORT_PGPKEYSERVER
31 #define IPPORT_PGPKEYSERVER 11371
32 #endif
33
36 /*@-redecl@*/
37 int (*urlNotify) (const urlinfo u, unsigned status)
38 /*@*/;
39 /*@=redecl@*/
40
43 /*@unchecked@*/ /*@null@*/
44 void * urlNotifyArg;
45
48 /*@unchecked@*/
49 int _url_iobuf_size = RPMURL_IOBUF_SIZE;
50
53 /*@unchecked@*/
54 int _url_debug = 0;
55
56 #define URLDBG(_f, _m, _x) if ((_url_debug | (_f)) & (_m)) fprintf _x
57
58 #define URLDBGIO(_f, _x) URLDBG((_f), RPMURL_DEBUG_IO, _x)
59 #define URLDBGREFS(_f, _x) URLDBG((_f), RPMURL_DEBUG_REFS, _x)
60
63 /*@unchecked@*/
64 /*@only@*/ /*@null@*/
65 urlinfo *_url_cache = NULL;
66
67 static void urlFini(void * _u)
68 /*@globals fileSystem, internalState @*/
69 /*@modifies _u, fileSystem, internalState @*/
70 {
71 urlinfo u =_u;
72 int xx;
73
74 if (u->ctrl) {
75 #ifndef NOTYET
76 void * fp = fdGetFp(u->ctrl);
77 if (fp) {
78 fdPush(u->ctrl, fpio, fp, -1); /* Push fpio onto stack */
79 xx = Fclose(u->ctrl);
80 } else if (fdFileno(u->ctrl) >= 0)
81 xx = fdio->close(u->ctrl);
82 #else
83 xx = Fclose(u->ctrl);
84 #endif
85
86 /*@-usereleased@*/
87 u->ctrl = (FD_t)rpmioFreePoolItem((rpmioItem)u->ctrl, "persist ctrl (urlFree)", __FILE__, __LINE__);
88 if (u->ctrl)
89 fprintf(stderr, _("warning: u %p ctrl %p nrefs != 0 (%s %s)\n"),
90 u, u->ctrl, (u->host ? u->host : ""),
91 (u->scheme ? u->scheme : ""));
92 /*@=usereleased@*/
93 }
94 if (u->data) {
95 #ifndef NOTYET
96 void * fp = fdGetFp(u->data);
97 if (fp) {
98 fdPush(u->data, fpio, fp, -1); /* Push fpio onto stack */
99 xx = Fclose(u->data);
100 } else if (fdFileno(u->data) >= 0)
101 xx = fdio->close(u->data);
102 #else
103 xx = Fclose(u->ctrl);
104 #endif
105
106 /*@-usereleased@*/
107 u->data = (FD_t)rpmioFreePoolItem((rpmioItem)u->data, "persist data (urlFree)", __FILE__, __LINE__);
108 if (u->data)
109 fprintf(stderr, _("warning: u %p data %p nrefs != 0 (%s %s)\n"),
110 u, u->data, (u->host ? u->host : ""),
111 (u->scheme ? u->scheme : ""));
112 /*@=usereleased@*/
113 }
114 #ifdef WITH_NEON
115 xx = davFree(u);
116 #endif
117 u->etag = _free(u->etag);
118 u->location = _free(u->location);
119 u->rop = _free(u->rop);
120 u->sop = _free(u->sop);
121 u->top = _free(u->top);
122 u->buf = _free(u->buf);
123 u->url = _free(u->url);
124 u->scheme = _free((void *)u->scheme);
125 u->user = _free((void *)u->user);
126 u->password = _free((void *)u->password);
127 u->host = _free((void *)u->host);
128 u->portstr = _free((void *)u->portstr);
129 u->query = _free(u->query);
130 u->fragment = _free(u->fragment);
131 u->proxyu = _free((void *)u->proxyu);
132 u->proxyh = _free((void *)u->proxyh);
133 }
134
137 /*@unchecked@*/
138 int _url_count = 0;
139
140 /*@unchecked@*/ /*@only@*/ /*@null@*/
141 rpmioPool _urlPool;
142
143 static urlinfo urlGetPool(/*@null@*/ rpmioPool pool)
144 /*@globals _urlPool, fileSystem @*/
145 /*@modifies pool, _urlPool, fileSystem @*/
146 {
147 urlinfo u;
148
149 if (_urlPool == NULL) {
150 _urlPool = rpmioNewPool("u", sizeof(*u), -1, _url_debug,
151 NULL, NULL, urlFini);
152 pool = _urlPool;
153 }
154 return (urlinfo) rpmioGetPool(pool, sizeof(*u));
155 }
156
157 urlinfo XurlNew(const char *msg, const char *fn, unsigned ln)
158 {
159 urlinfo u = urlGetPool(_urlPool);
160
161 u->proxyp = -1;
162 u->port = -1;
163 u->urltype = URL_IS_UNKNOWN;
164 u->ctrl = NULL;
165 u->data = NULL;
166 u->location = NULL;
167 u->etag = NULL;
168 u->notify = urlNotify;
169 /*@-assignexpose@*/
170 u->arg = urlNotifyArg;
171 /*@=assignexpose@*/
172 u->rop = xcalloc(1, sizeof(*u->rop));
173 u->sop = xcalloc(1, sizeof(*u->sop));
174 u->top = xcalloc(1, sizeof(*u->top));
175 u->bufAlloced = 0;
176 u->buf = NULL;
177 u->allow = RPMURL_SERVER_HASRANGE;
178 u->httpVersion = 0;
179 u->magic = URLMAGIC;
180 return (urlinfo) rpmioLinkPoolItem((rpmioItem)u, msg, fn, ln);
181 }
182
183 void urlFreeCache(void)
184 {
185 if (_url_cache) {
186 int i;
187 for (i = 0; i < _url_count; i++) {
188 if (_url_cache[i] == NULL) continue;
189 _url_cache[i] = urlFree(_url_cache[i], "_url_cache");
190 if (_url_cache[i] == NULL)
191 continue;
192 yarnPossess(_url_cache[i]->_item.use);
193 fprintf(stderr,
194 _("warning: _url_cache[%d] %p nrefs(%ld) != 1 (%s %s)\n"),
195 i, _url_cache[i], yarnPeekLock(_url_cache[i]->_item.use),
196 (_url_cache[i]->host ? _url_cache[i]->host : ""),
197 (_url_cache[i]->scheme ? _url_cache[i]->scheme : ""));
198 yarnRelease(_url_cache[i]->_item.use);
199 }
200 }
201 _url_cache = _free(_url_cache);
202 _url_count = 0;
203 }
204
205 static int urlStrcmp(/*@null@*/ const char * str1, /*@null@*/ const char * str2)
206 /*@*/
207 {
208 if (str1)
209 if (str2)
210 return strcmp(str1, str2);
211 if (str1 != str2)
212 return -1;
213 return 0;
214 }
215
216 /*@-mods@*/
217 static void urlFind(/*@null@*/ /*@in@*/ /*@out@*/ urlinfo * uret, int mustAsk)
218 /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
219 /*@modifies *uret, rpmGlobalMacroContext, fileSystem, internalState @*/
220 {
221 urlinfo u;
222 int ucx;
223 int i = 0;
224
225 if (uret == NULL)
226 return;
227
228 u = *uret;
229 URLSANE(u);
230
231 ucx = -1;
232 for (i = 0; i < _url_count; i++) {
233 urlinfo ou = NULL;
234 if (_url_cache == NULL || (ou = _url_cache[i]) == NULL) {
235 if (ucx < 0)
236 ucx = i;
237 continue;
238 }
239
240 /* Check for cache-miss condition. A cache miss is
241 * a) both items are not NULL and don't compare.
242 * b) either of the items is not NULL.
243 */
244 if (urlStrcmp(u->scheme, ou->scheme))
245 continue;
246 if (urlStrcmp(u->host, ou->host))
247 continue;
248 if (urlStrcmp(u->user, ou->user))
249 continue;
250 if (urlStrcmp(u->portstr, ou->portstr))
251 continue;
252 break; /* Found item in cache */
253 }
254
255 if (i == _url_count) {
256 if (ucx < 0) {
257 ucx = _url_count++;
258 _url_cache = xrealloc(_url_cache, sizeof(*_url_cache) * _url_count);
259 }
260 if (_url_cache) /* XXX always true */
261 _url_cache[ucx] = urlLink(u, "_url_cache (miss)");
262 u = urlFree(u, "urlSplit (urlFind miss)");
263 } else {
264 ucx = i;
265 u = urlFree(u, "urlSplit (urlFind hit)");
266 }
267
268 /* This URL is now cached. */
269
270 if (_url_cache) /* XXX always true */
271 u = urlLink(_url_cache[ucx], "_url_cache");
272 *uret = u;
273 /*@-usereleased@*/
274 u = urlFree(u, "_url_cache (urlFind)");
275 /*@=usereleased@*/
276 assert(u != NULL);
277
278 /* Zap proxy host and port in case they have been reset */
279 u->proxyp = -1;
280 u->proxyh = _free(u->proxyh);
281
282 /* Perform one-time FTP initialization */
283 if (u->urltype == URL_IS_FTP) {
284
285 if (mustAsk || (u->user != NULL && u->password == NULL)) {
286 const char * host = (u->host ? u->host : "");
287 const char * user = (u->user ? u->user : "");
288 char * prompt;
289 prompt = alloca(strlen(host) + strlen(user) + 256);
290 sprintf(prompt, _("Password for %s@%s: "), user, host);
291 u->password = _free(u->password);
292 /*@-dependenttrans -moduncon @*/
293 u->password = Getpass(prompt);
294 /*@=dependenttrans =moduncon @*/
295 if (u->password)
296 u->password = xstrdup(u->password);
297 }
298
299 if (u->proxyh == NULL) {
300 const char *proxy = rpmExpand("%{_ftpproxy}", NULL);
301 if (proxy && *proxy != '%') {
302 /*@observer@*/
303 const char * host = (u->host ? u->host : "");
304 const char *uu = (u->user ? u->user : "anonymous");
305 char *nu = xmalloc(strlen(uu) + sizeof("@") + strlen(host));
306 (void) stpcpy( stpcpy( stpcpy(nu, uu), "@"), host);
307 u->proxyu = nu;
308 u->proxyh = xstrdup(proxy);
309 }
310 proxy = _free(proxy);
311 }
312
313 if (u->proxyp < 0) {
314 const char *proxy = rpmExpand("%{_ftpport}", NULL);
315 if (proxy && *proxy != '%') {
316 char *end = NULL;
317 int port = strtol(proxy, &end, 0);
318 if (!(end && *end == '\0')) {
319 fprintf(stderr, _("error: %sport must be a number\n"),
320 (u->scheme ? u->scheme : ""));
321 return;
322 }
323 u->proxyp = port;
324 }
325 proxy = _free(proxy);
326 }
327 }
328
329 /* Perform one-time HTTP initialization */
330 if (u->urltype == URL_IS_HTTP || u->urltype == URL_IS_HTTPS || u->urltype == URL_IS_HKP) {
331
332 if (u->proxyh == NULL) {
333 const char *proxy = rpmExpand("%{_httpproxy}", NULL);
334 if (proxy && *proxy != '%')
335 u->proxyh = xstrdup(proxy);
336 proxy = _free(proxy);
337 }
338
339 if (u->proxyp < 0) {
340 const char *proxy = rpmExpand("%{_httpport}", NULL);
341 if (proxy && *proxy != '%') {
342 char *end;
343 int port = strtol(proxy, &end, 0);
344 if (!(end && *end == '\0')) {
345 fprintf(stderr, _("error: %sport must be a number\n"),
346 (u->scheme ? u->scheme : ""));
347 return;
348 }
349 u->proxyp = port;
350 }
351 proxy = _free(proxy);
352 }
353
354 }
355
356 return;
357 }
358 /*@=mods@*/
359
362 /*@observer@*/ /*@unchecked@*/
363 static struct urlstring {
364 /*@observer@*/ /*@null@*/
365 const char * leadin;
366 urltype ret;
367 } urlstrings[] = {
368 { "file://", URL_IS_PATH },
369 { "ftp://", URL_IS_FTP },
370 { "hkp://", URL_IS_HKP },
371 { "http://", URL_IS_HTTP },
372 { "https://", URL_IS_HTTPS },
373 { "-", URL_IS_DASH },
374 { NULL, URL_IS_UNKNOWN }
375 };
376
377 urltype urlIsURL(const char * url)
378 {
379 struct urlstring *us;
380
381 if (url && *url) {
382 for (us = urlstrings; us->leadin != NULL; us++) {
383 if (strncmp(url, us->leadin, strlen(us->leadin)))
384 continue;
385 return us->ret;
386 }
387 }
388
389 return URL_IS_UNKNOWN;
390 }
391
392 /* Return path portion of url (or pointer to NUL if url == NULL) */
393 urltype urlPath(const char * url, const char ** pathp)
394 {
395 const char *path;
396 int urltype;
397
398 path = url;
399 urltype = urlIsURL(url);
400 switch (urltype) {
401 case URL_IS_FTP:
402 url += sizeof("ftp://") - 1;
403 path = strchr(url, '/');
404 if (path == NULL) path = url + strlen(url);
405 break;
406 case URL_IS_PATH:
407 url += sizeof("file://") - 1;
408 path = strchr(url, '/');
409 if (path == NULL) path = url + strlen(url);
410 break;
411 case URL_IS_HKP:
412 url += sizeof("hkp://") - 1;
413 path = strchr(url, '/');
414 if (path == NULL) path = url + strlen(url);
415 break;
416 case URL_IS_HTTP:
417 url += sizeof("http://") - 1;
418 path = strchr(url, '/');
419 if (path == NULL) path = url + strlen(url);
420 break;
421 case URL_IS_HTTPS:
422 url += sizeof("https://") - 1;
423 path = strchr(url, '/');
424 if (path == NULL) path = url + strlen(url);
425 break;
426 case URL_IS_UNKNOWN:
427 if (path == NULL) path = "";
428 break;
429 case URL_IS_DASH:
430 path = "";
431 break;
432 }
433 if (pathp)
434 /*@-observertrans@*/
435 *pathp = path;
436 /*@=observertrans@*/
437 return urltype;
438 }
439
443 static const char * urlStrdup(const char * url)
444 /*@*/
445 {
446 size_t nb = strlen(url);
447 char * t = xmalloc(nb + 1 + 1);
448 const char * nurl = t;
449 while (*url != '\0')
450 *t++ = *url++;
451 *t = '\0';
452 return nurl;
453 }
454
455 /*
456 * Split URL into components. The URL can look like
457 * scheme://user:password@host:port/path
458 * or as in RFC2732 for IPv6 address
459 * service://user:password@[ip:v6:ad:dr:es:s]:port/path?query#fragment
460 */
461 /*@-modfilesys@*/
462 int urlSplit(const char * url, urlinfo *uret)
463 {
464 urlinfo u;
465 char *myurl;
466 char *s, *se, *f, *fe;
467
468 if (uret == NULL)
469 return -1;
470 if ((u = urlNew("urlSplit")) == NULL)
471 return -1;
472
473 myurl = xstrdup(url);
474 if ((se = strrchr(myurl, '#')) != NULL) {
475 *se++ = '\0';
476 u->fragment = xstrdup(se);
477 }
478 if ((se = strrchr(myurl, '?')) != NULL) {
479 *se++ = '\0';
480 u->query = xstrdup(se);
481 }
482
483 u->url = urlStrdup(myurl); /* XXX +1 byte for pesky trailing '/' */
484 u->urltype = urlIsURL(myurl);
485
486 se = s = myurl;
487 while (1) {
488 /* Point to end of next item */
489 while (*se && *se != '/') se++;
490 /* Item was scheme. Save scheme and go for the rest ...*/
491 if (*se && (se != s) && se[-1] == ':' && se[0] == '/' && se[1] == '/') {
492 se[-1] = '\0';
493 u->scheme = xstrdup(s);
494 se += 2; /* skip over "//" */
495 s = se++;
496 continue;
497 }
498
499 /* Item was everything-but-path. Continue parse on rest */
500 *se = '\0';
501 break;
502 }
503
504 /* Look for ...@host... */
505 fe = f = s;
506 while (*fe && *fe != '@') fe++;
507 if (*fe == '@') {
508 s = fe + 1;
509 *fe = '\0';
510 /* Look for user:password@host... */
511 while (fe > f && *fe != ':') fe--;
512 if (*fe == ':') {
513 *fe++ = '\0';
514 u->password = xstrdup(fe);
515 }
516 u->user = xstrdup(f);
517 }
518
519 /* Look for ...host:port or [v6addr]:port*/
520 fe = f = s;
521 if (strchr(fe, '[') && strchr(fe, ']')) {
522 fe = strchr(f, ']');
523 *f++ = '\0';
524 *fe++ = '\0';
525 }
526 assert(fe != NULL); /* XXX can't happen */
527 while (*fe && *fe != ':') fe++;
528 if (*fe == ':') {
529 *fe++ = '\0';
530 u->portstr = xstrdup(fe);
531 if (u->portstr != NULL && u->portstr[0] != '\0') {
532 char *end;
533 u->port = strtol(u->portstr, &end, 0);
534 if (!(end && *end == '\0')) {
535 rpmlog(RPMLOG_ERR, _("url port must be a number\n"));
536 myurl = _free(myurl);
537 u = urlFree(u, "urlSplit (error #3)");
538 return -1;
539 }
540 }
541 }
542 u->host = xstrdup(f);
543
544 if (u->port < 0 && u->scheme != NULL) {
545 struct servent *serv;
546 /*@-multithreaded -moduncon @*/
547 /* HACK hkp:// might lookup "pgpkeyserver" */
548 serv = getservbyname(u->scheme, "tcp");
549 /*@=multithreaded =moduncon @*/
550 if (serv != NULL)
551 u->port = (int) ntohs(serv->s_port);
552 else if (u->urltype == URL_IS_FTP)
553 u->port = IPPORT_FTP;
554 else if (u->urltype == URL_IS_HKP)
555 u->port = IPPORT_PGPKEYSERVER;
556 else if (u->urltype == URL_IS_HTTP)
557 u->port = IPPORT_HTTP;
558 else if (u->urltype == URL_IS_HTTPS)
559 u->port = IPPORT_HTTPS;
560 }
561
562 myurl = _free(myurl);
563 if (uret) {
564 *uret = u;
565 /*@-globs -mods @*/ /* FIX: rpmGlobalMacroContext not in <rpmlib.h> */
566 urlFind(uret, 0);
567 /*@=globs =mods @*/
568 }
569 return 0;
570 }
571 /*@=modfilesys@*/
572
573 int urlGetFile(const char * url, const char * dest)
574 {
575 int rc;
576 FD_t sfd = NULL;
577 FD_t tfd = NULL;
578 const char * sfuPath = NULL;
579 int urlType = urlPath(url, &sfuPath);
580 char *result;
581
582 if (*sfuPath == '\0')
583 return FTPERR_UNKNOWN;
584
585 if (dest == NULL) {
586 if ((dest = strrchr(sfuPath, '/')) != NULL)
587 dest++;
588 else
589 dest = sfuPath;
590 }
591 if (dest == NULL)
592 return FTPERR_UNKNOWN;
593
594 /*@-globs -mods@*/ /* Avoid including <rpmmacro.h> everywhere for now */
595 if (rpmExpandNumeric("%{?__urlgetfile:1}%{!?__urlgetfile:0}")) {
596 result = rpmExpand("%{__urlgetfile ", url, " ", dest, "}", NULL);
597 if (result != NULL && strcmp(result, "OK") == 0)
598 rc = 0;
599 else {
600 rpmlog(RPMLOG_DEBUG, D_("failed to fetch URL %s via external command\n"), url);
601 rc = FTPERR_UNKNOWN;
602 }
603 result = _free(result);
604 goto exit;
605 }
606 /*@=globs =mods@*/
607
608 sfd = Fopen(url, "r.ufdio");
609 if (sfd == NULL || Ferror(sfd)) {
610 rpmlog(RPMLOG_DEBUG, D_("failed to open %s: %s\n"), url, Fstrerror(sfd));
611 rc = FTPERR_UNKNOWN;
612 goto exit;
613 }
614
615 /* XXX this can fail if directory in path does not exist. */
616 tfd = Fopen(dest, "w");
617 if (_url_debug)
618 fprintf(stderr, "*** urlGetFile sfd %p %s tfd %p %s\n", sfd, url, (tfd ? tfd : NULL), dest);
619 if (tfd == NULL || Ferror(tfd)) {
620 rpmlog(RPMLOG_DEBUG, D_("failed to create %s: %s\n"), dest, Fstrerror(tfd));
621 rc = FTPERR_UNKNOWN;
622 goto exit;
623 }
624
625 switch (urlType) {
626 case URL_IS_HTTPS:
627 case URL_IS_HTTP:
628 case URL_IS_HKP:
629 case URL_IS_FTP:
630 case URL_IS_PATH:
631 case URL_IS_DASH:
632 case URL_IS_UNKNOWN:
633 if ((rc = ufdGetFile(sfd, tfd))) {
634 (void) Unlink(dest);
635 /* XXX FIXME: sfd possibly closed by copyData */
636 /*@-usereleased@*/ (void) Fclose(sfd) /*@=usereleased@*/ ;
637 }
638 sfd = NULL; /* XXX Fclose(sfd) done by ufdGetFile */
639 break;
640 default:
641 rc = FTPERR_UNKNOWN;
642 break;
643 }
644
645 exit:
646 if (tfd)
647 (void) Fclose(tfd);
648 if (sfd)
649 (void) Fclose(sfd);
650
651 return rc;
652 }