rpm  5.2.1
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@*/
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@*/
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@*/
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;
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;
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)
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 }