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