rpm  5.2.1
rpmio.c
Go to the documentation of this file.
1 
5 #include "system.h"
6 #include <stdarg.h>
7 
8 #if defined(HAVE_MACHINE_TYPES_H)
9 # include <machine/types.h>
10 #endif
11 
12 #if defined(HAVE_SYS_SOCKET_H)
13 # include <sys/socket.h>
14 #endif
15 
16 #ifndef NI_MAXHOST
17 #define NI_MAXHOST 1025
18 #endif
19 
20 #if defined(__LCLINT__)
21 struct addrinfo
22 {
23  int ai_flags; /* Input flags. */
24  int ai_family; /* Protocol family for socket. */
25  int ai_socktype; /* Socket type. */
26  int ai_protocol; /* Protocol for socket. */
27  socklen_t ai_addrlen; /* Length of socket address. */
28  struct sockaddr *ai_addr; /* Socket address for socket. */
29  char *ai_canonname; /* Canonical name for service location. */
30  struct addrinfo *ai_next; /* Pointer to next in list. */
31 };
32 
33 /*@-exportheader -incondefs @*/
34 extern int getaddrinfo (__const char *__restrict __name,
35  __const char *__restrict __service,
36  __const struct addrinfo *__restrict __req,
37  /*@out@*/ struct addrinfo **__restrict __pai)
38  /*@modifies *__pai @*/;
39 
40 extern int getnameinfo (__const struct sockaddr *__restrict __sa,
41  socklen_t __salen, /*@out@*/ char *__restrict __host,
42  socklen_t __hostlen, /*@out@*/ char *__restrict __serv,
43  socklen_t __servlen, unsigned int __flags)
44  /*@modifies __host, __serv @*/;
45 
46 extern void freeaddrinfo (/*@only@*/ struct addrinfo *__ai)
47  /*@modifies __ai @*/;
48 /*@=exportheader =incondefs @*/
49 #else
50 #include <netdb.h> /* XXX getaddrinfo et al */
51 #endif
52 
53 #include <netinet/in.h>
54 #include <arpa/inet.h> /* XXX for inet_aton and HP-UX */
55 
56 #if defined(HAVE_NETINET_IN_SYSTM_H)
57 # include <sys/types.h>
58 # include <netinet/in_systm.h>
59 #endif
60 
61 #if defined(WITH_XZ)
62 #include <lzma.h>
63 #endif
64 
65 #include <rpmiotypes.h>
66 #include <rpmmacro.h> /* XXX rpmioAccess needs rpmCleanPath() */
67 
68 #include <rpmficl.h>
69 #include <rpmjs.h>
70 #include <rpmlua.h> /* XXX rpmioClean() calls rpmluaFree() */
71 #include <rpmperl.h>
72 #include <rpmpython.h>
73 #include <rpmruby.h>
74 #include <rpmtcl.h>
75 
76 #if defined(HAVE_LIBIO_H) && defined(_G_IO_IO_FILE_VERSION)
77 #define _USE_LIBIO 1
78 #endif
79 
80 /* XXX HP-UX w/o -D_XOPEN_SOURCE needs */
81 #if !defined(HAVE_HERRNO) && (defined(hpux) || defined(__hpux) || defined(__LCLINT__))
82 /*@unchecked@*/
83 extern int h_errno;
84 #endif
85 
86 #ifndef IPPORT_FTP
87 #define IPPORT_FTP 21
88 #endif
89 #ifndef IPPORT_HTTP
90 #define IPPORT_HTTP 80
91 #endif
92 
93 #if !defined(HAVE_INET_ATON)
94 #define inet_aton(cp,inp) rpm_inet_aton(cp,inp)
95 static int rpm_inet_aton(const char *cp, struct in_addr *inp)
96  /*@modifies *inp @*/
97 {
98  long addr;
99 
100  addr = inet_addr(cp);
101  if (addr == ((long) -1)) return 0;
102 
103  memcpy(inp, &addr, sizeof(addr));
104  return 1;
105 }
106 #endif
107 
108 #if defined(USE_ALT_DNS) && USE_ALT_DNS
109 #include "dns.h"
110 #endif
111 
112 #include <rpmio_internal.h>
113 #undef fdFileno
114 #undef fdOpen
115 #define fdOpen __fdOpen
116 #undef fdRead
117 #define fdRead __fdRead
118 #undef fdWrite
119 #define fdWrite __fdWrite
120 #undef fdClose
121 #define fdClose __fdClose
122 
123 #include <ugid.h>
124 #include <rpmcb.h>
125 #include <rpmdav.h>
126 
127 #include "debug.h"
128 
129 /*@access FILE @*/ /* XXX to permit comparison/conversion with void *. */
130 /*@access urlinfo @*/
131 /*@access FDSTAT_t @*/
132 /*@access rpmxar @*/
133 /*@access pgpDig @*/
134 
135 #define FDTO(fd) (fd ? ((FD_t)fd)->rd_timeoutsecs : -99)
136 #define FDCPIOPOS(fd) (fd ? ((FD_t)fd)->fd_cpioPos : -99)
137 
138 #define FDONLY(fd) assert(fdGetIo(fd) == fdio)
139 
140 #define UFDONLY(fd) /* assert(fdGetIo(fd) == ufdio) */
141 
142 #define fdGetFILE(_fd) ((FILE *)fdGetFp(_fd))
143 
146 /*@unchecked@*/
147 #if _USE_LIBIO
148 int noLibio = 0;
149 #else
150 int noLibio = 1;
151 #endif
152 
153 #define TIMEOUT_SECS 60
154 
157 /*@unchecked@*/
159 
162 /*@unchecked@*/
163 int _rpmio_debug = 0;
164 
167 /*@unchecked@*/
168 int _av_debug = 0;
169 
172 /*@unchecked@*/
173 int _ftp_debug = 0;
174 
177 /*@unchecked@*/
178 int _dav_debug = 0;
179 
180 /* =============================================================== */
181 
182 const char * fdbg(FD_t fd)
183 {
184  static char buf[BUFSIZ];
185  char *be = buf;
186  int i;
187 
188  buf[0] = '\0';
189  if (fd == NULL)
190  return buf;
191 
192 #ifdef DYING
193  sprintf(be, "fd %p", fd); be += strlen(be);
194  if (fd->rd_timeoutsecs >= 0) {
195  sprintf(be, " secs %d", fd->rd_timeoutsecs);
196  be += strlen(be);
197  }
198 #endif
199  if (fd->bytesRemain != -1) {
200  sprintf(be, " clen %d", (int)fd->bytesRemain);
201  be += strlen(be);
202  }
203  if (fd->wr_chunked) {
204  strcpy(be, " chunked");
205  be += strlen(be);
206  }
207  *be++ = '\t';
208  for (i = fd->nfps; i >= 0; i--) {
209  FDSTACK_t * fps = &fd->fps[i];
210  if (i != fd->nfps)
211  *be++ = ' ';
212  *be++ = '|';
213  *be++ = ' ';
214  if (fps->io == fdio) {
215  sprintf(be, "FD %d fp %p", fps->fdno, fps->fp);
216  } else if (fps->io == ufdio) {
217  sprintf(be, "UFD %d fp %p", fps->fdno, fps->fp);
218 #if defined(WITH_ZLIB)
219  } else if (fps->io == gzdio) {
220  sprintf(be, "GZD %p fdno %d", fps->fp, fps->fdno);
221 #endif
222 #if defined(WITH_BZIP2)
223  } else if (fps->io == bzdio) {
224  sprintf(be, "BZD %p fdno %d", fps->fp, fps->fdno);
225 #endif
226 #if defined(WITH_XZ)
227  } else if (fps->io == lzdio) {
228  sprintf(be, "LZD %p fdno %d", fps->fp, fps->fdno);
229  } else if (fps->io == xzdio) {
230  sprintf(be, "XZD %p fdno %d", fps->fp, fps->fdno);
231 #endif
232  } else if (fps->io == fpio) {
233  /*@+voidabstract@*/
234  sprintf(be, "%s %p(%d) fdno %d",
235  (fps->fdno < 0 ? "LIBIO" : "FP"),
236  fps->fp, fileno(((FILE *)fps->fp)), fps->fdno);
237  /*@=voidabstract@*/
238  } else {
239  sprintf(be, "??? io %p fp %p fdno %d ???",
240  fps->io, fps->fp, fps->fdno);
241  }
242  be += strlen(be);
243  *be = '\0';
244  }
245  return buf;
246 }
247 
248 /* =============================================================== */
249 FD_t fdDup(int fdno)
250 {
251  FD_t fd;
252  int nfdno;
253 
254  if ((nfdno = dup(fdno)) < 0)
255  return NULL;
256  if (fcntl(nfdno, F_SETFD, FD_CLOEXEC)) {
257  (void) close(nfdno);
258  return NULL;
259  }
260  fd = fdNew("open (fdDup)");
261  fdSetOpen(fd, "fdDup", nfdno, 0); /* XXX bogus */
262  fdSetFdno(fd, nfdno);
263 DBGIO(fd, (stderr, "==> fdDup(%d) fd %p %s\n", fdno, (fd ? fd : NULL), fdbg(fd)));
264  /*@-refcounttrans@*/ return fd; /*@=refcounttrans@*/
265 }
266 
267 static inline /*@unused@*/
268 int fdSeekNot(void * cookie,
269  /*@unused@*/ _libio_pos_t pos,
270  /*@unused@*/ int whence)
271  /*@*/
272 {
273  FD_t fd = c2f(cookie);
274  FDSANE(fd); /* XXX keep gcc quiet */
275  return -2;
276 }
277 
278 /* =============================================================== */
279 
280 static void fdFini(void * _fd)
281  /*@globals fileSystem @*/
282  /*@modifies _fd, fileSystem @*/
283 {
284  FD_t fd = _fd;
285  int i;
286 
287 assert(fd != NULL);
288  fd->opath = _free(fd->opath);
289  fd->stats = _free(fd->stats);
290  for (i = fd->ndigests - 1; i >= 0; i--) {
291  FDDIGEST_t fddig = fd->digests + i;
292  if (fddig->hashctx == NULL)
293  continue;
294  (void) rpmDigestFinal(fddig->hashctx, NULL, NULL, 0);
295  fddig->hashctx = NULL;
296  }
297  fd->ndigests = 0;
298  fd->contentType = _free(fd->contentType);
300 /*@-onlytrans@*/
301 #ifdef WITH_XAR
302  fd->xar = rpmxarFree(fd->xar, "fdFini");
303 #endif
304  fd->dig = pgpDigFree(fd->dig, "fdFini");
305 /*@=onlytrans@*/
306 }
307 
308 /*@unchecked@*/ /*@only@*/ /*@null@*/
310 
311 static FD_t fdGetPool(/*@null@*/ rpmioPool pool)
312  /*@globals _fdPool, fileSystem @*/
313  /*@modifies pool, _fdPool, fileSystem @*/
314 {
315  FD_t fd;
316 
317  if (_fdPool == NULL) {
318  _fdPool = rpmioNewPool("fd", sizeof(*fd), -1, _rpmio_debug,
319  (const char * (*)(void *))fdbg, NULL, fdFini);
320  pool = _fdPool;
321  }
322  return (FD_t) rpmioGetPool(pool, sizeof(*fd));
323 }
324 
325 /*@-incondefs@*/
326 /*@null@*/
327 FD_t XfdNew(const char * msg, const char * fn, unsigned ln)
328 {
329  FD_t fd = fdGetPool(_fdPool);
330  if (fd == NULL) /* XXX xmalloc never returns NULL */
331  return NULL;
332  fd->flags = 0;
333  fd->magic = FDMAGIC;
334  fd->urlType = URL_IS_UNKNOWN;
335 
336  fd->nfps = 0;
337  memset(fd->fps, 0, sizeof(fd->fps));
338 
339  fd->fps[0].io = ufdio;
340  fd->fps[0].fp = NULL;
341  fd->fps[0].fdno = -1;
342 
343  fd->opath = NULL;
344  fd->oflags = 0;
345  fd->omode = 0;
346  fd->url = NULL;
347 #if defined(RPM_VENDOR_MANDRIVA) /* raise-read-timeout-to-60secs */
348  fd->rd_timeoutsecs = 60; /* XXX default value used to be -1 */
349 #else
350  fd->rd_timeoutsecs = 1; /* XXX default value used to be -1 */
351 #endif
352  fd->contentLength = fd->bytesRemain = -1;
353  fd->contentType = NULL;
354  fd->contentDisposition = NULL;
355  fd->lastModified = 0;
356  fd->wr_chunked = 0;
357  fd->syserrno = 0;
358  fd->errcookie = NULL;
359  fd->stats = xcalloc(1, sizeof(*fd->stats));
360  fd->xar = NULL;
361  fd->dig = NULL;
362 
363  fd->ndigests = 0;
364  memset(fd->digests, 0, sizeof(fd->digests));
365 
366  fd->ftpFileDoneNeeded = 0;
367  fd->fd_cpioPos = 0;
368 
369  return (FD_t)rpmioLinkPoolItem((rpmioItem)fd, msg, fn, ln);
370 }
371 /*@=incondefs@*/
372 
373 static ssize_t fdRead(void * cookie, /*@out@*/ char * buf, size_t count)
374  /*@globals errno, fileSystem, internalState @*/
375  /*@modifies buf, errno, fileSystem, internalState @*/
376  /*@requires maxSet(buf) >= (count - 1) @*/
377 {
378  FD_t fd = c2f(cookie);
379  ssize_t rc;
380 
381  if (fd->bytesRemain == 0) return 0; /* XXX simulate EOF */
382 
384  /* HACK: flimsy wiring for davRead */
385  if (fd->req != NULL) {
386 #ifdef WITH_NEON
387  if (fd->req != (void *)-1)
388  rc = davRead(fd, buf, (count > (size_t)fd->bytesRemain ? (size_t)fd->bytesRemain : count));
389  else
390  rc = -1;
391 #else
392  rc = -1;
393 #endif
394  /* XXX Chunked davRead EOF. */
395  if (rc == 0)
396  fd->bytesRemain = 0;
397  } else
398  if (fd->xar != NULL) {
399 #ifdef WITH_XAR
400  rc = xarRead(fd, buf, (count > (size_t)fd->bytesRemain ? (size_t)fd->bytesRemain : count));
401 #else
402  rc = -1;
403 #endif
404  } else
405  rc = read(fdFileno(fd), buf, (count > (size_t)fd->bytesRemain ? (size_t)fd->bytesRemain : count));
406  fdstat_exit(fd, FDSTAT_READ, rc);
407 
408  if (fd->ndigests && rc > 0) fdUpdateDigests(fd, (void *)buf, rc);
409 
410 DBGIO(fd, (stderr, "==>\tfdRead(%p,%p,%ld) rc %ld %s\n", cookie, buf, (long)count, (long)rc, fdbg(fd)));
411 
412  return rc;
413 }
414 
415 static ssize_t fdWrite(void * cookie, const char * buf, size_t count)
416  /*@globals errno, fileSystem, internalState @*/
417  /*@modifies errno, fileSystem, internalState @*/
418 {
419  FD_t fd = c2f(cookie);
420  int fdno = fdFileno(fd);
421  ssize_t rc;
422 
423  if (fd->bytesRemain == 0) return 0; /* XXX simulate EOF */
424 
425  if (fd->ndigests && count > 0) fdUpdateDigests(fd, (void *)buf, count);
426 
427  if (count == 0) return 0;
428 
430  /* HACK: flimsy wiring for davWrite */
431  if (fd->req != NULL)
432 #ifdef WITH_NEON
433  if (fd->req != (void *)-1)
434  rc = davWrite(fd, buf, (count > (size_t)fd->bytesRemain ? (size_t)fd->bytesRemain : count));
435  else
436  rc = -1;
437 #else
438  rc = -1;
439 #endif
440  else
441  rc = write(fdno, buf, (count > (size_t)fd->bytesRemain ? (size_t)fd->bytesRemain : count));
442  fdstat_exit(fd, FDSTAT_WRITE, rc);
443 
444 DBGIO(fd, (stderr, "==>\tfdWrite(%p,%p,%ld) rc %ld %s\n", cookie, buf, (long)count, (long)rc, fdbg(fd)));
445 
446  return rc;
447 }
448 
449 static inline int fdSeek(void * cookie, _libio_pos_t pos, int whence)
450  /*@globals fileSystem, internalState @*/
451  /*@modifies fileSystem, internalState @*/
452 {
453 #ifdef USE_COOKIE_SEEK_POINTER
454  _IO_off64_t p = *pos;
455 #else
456  off_t p = pos;
457 #endif
458  FD_t fd = c2f(cookie);
459  off_t rc;
460 
461  assert(fd->bytesRemain == -1); /* XXX FIXME fadio only for now */
463  rc = lseek(fdFileno(fd), p, whence);
464  fdstat_exit(fd, FDSTAT_SEEK, rc);
465 
466 DBGIO(fd, (stderr, "==>\tfdSeek(%p,%ld,%d) rc %lx %s\n", cookie, (long)p, whence, (unsigned long)rc, fdbg(fd)));
467 
468  return (int) rc;
469 }
470 
471 static int fdClose( /*@only@*/ void * cookie)
472  /*@globals errno, fileSystem, systemState, internalState @*/
473  /*@modifies errno, fileSystem, systemState, internalState @*/
474 {
475  FD_t fd;
476  int fdno;
477  int rc;
478 
479  if (cookie == NULL) return -2;
480  fd = c2f(cookie);
481  fdno = fdFileno(fd);
482 
483  fdSetFdno(fd, -1);
484 
486  /* HACK: flimsy wiring for davClose */
487  if (fd->req != NULL)
488 #ifdef WITH_NEON
489  rc = davClose(fd);
490 #else
491  rc = -1;
492 #endif
493  else
494  rc = ((fdno >= 0) ? close(fdno) : -2);
495  fdstat_exit(fd, FDSTAT_CLOSE, rc);
496 
497 DBGIO(fd, (stderr, "==>\tfdClose(%p) rc %lx %s\n", (fd ? fd : NULL), (unsigned long)rc, fdbg(fd)));
498 
499  fd = fdFree(fd, "open (fdClose)");
500  return rc;
501 }
502 
503 static /*@null@*/ FD_t fdOpen(const char *path, int flags, mode_t mode)
504  /*@globals errno, fileSystem, internalState @*/
505  /*@modifies errno, fileSystem, internalState @*/
506 {
507  FD_t fd;
508  int fdno;
509 
510  fdno = open(path, flags, mode);
511  if (fdno < 0) return NULL;
512  if (fcntl(fdno, F_SETFD, FD_CLOEXEC)) {
513  (void) close(fdno);
514  return NULL;
515  }
516  fd = fdNew("open (fdOpen)");
517  fdSetOpen(fd, path, flags, mode);
518  fdSetFdno(fd, fdno);
519 assert(fd != NULL);
520  fd->flags = flags;
521 DBGIO(fd, (stderr, "==>\tfdOpen(\"%s\",%x,0%o) %s\n", path, (unsigned)flags, (unsigned)mode, fdbg(fd)));
522  /*@-refcounttrans@*/ return fd; /*@=refcounttrans@*/
523 }
524 
525 #ifdef NOTUSED
526 FILE *fdFdopen(void * cookie, const char *fmode)
527 {
528  FD_t fd = c2f(cookie);
529  int fdno;
530  FILE * fp;
531 
532  if (fmode == NULL) return NULL;
533  fdno = fdFileno(fd);
534  if (fdno < 0) return NULL;
535  fp = fdopen(fdno, fmode);
536 DBGIO(fd, (stderr, "==> fdFdopen(%p,\"%s\") fdno %d -> fp %p fdno %d\n", cookie, fmode, fdno, fp, fileno(fp)));
537  fd = fdFree(fd, "open (fdFdopen)");
538  return fp;
539 }
540 #endif
541 
542 /*@-type@*/ /* LCL: function typedefs */
543 static struct FDIO_s fdio_s = {
544  fdRead, fdWrite, fdSeek, fdClose, NULL, NULL, NULL,
545 };
546 /*@=type@*/
547 
548 FDIO_t fdio = /*@-compmempass@*/ &fdio_s /*@=compmempass@*/ ;
549 
550 int fdWritable(FD_t fd, int secs)
551 {
552  int fdno;
553  int rc;
554 #if defined(HAVE_POLL_H)
555  int msecs = (secs >= 0 ? (1000 * secs) : -1);
556  struct pollfd wrfds;
557 #else
558  struct timeval timeout, *tvp = (secs >= 0 ? &timeout : NULL);
559  fd_set wrfds;
560  FD_ZERO(&wrfds);
561 #endif
562 
563  /* HACK: flimsy wiring for davWrite */
564  if (fd->req != NULL)
565  return (fd->req == (void *)-1 ? -1 : 1);
566 
567  if ((fdno = fdFileno(fd)) < 0)
568  return -1; /* XXX W2DO? */
569 
570  do {
571 #if defined(HAVE_POLL_H)
572  wrfds.fd = fdno;
573  wrfds.events = POLLOUT;
574  wrfds.revents = 0;
575  rc = poll(&wrfds, 1, msecs);
576 #else
577  if (tvp) {
578  tvp->tv_sec = secs;
579  tvp->tv_usec = 0;
580  }
581  FD_SET(fdno, &wrfds);
582 /*@-compdef -nullpass@*/
583  rc = select(fdno + 1, NULL, &wrfds, NULL, tvp);
584 /*@=compdef =nullpass@*/
585 #endif
586 
587  /* HACK: EBADF on PUT chunked termination from ufdClose. */
588 if (_rpmio_debug && !(rc == 1 && errno == 0))
589 fprintf(stderr, "*** fdWritable fdno %d rc %d %s\n", fdno, rc, strerror(errno));
590  if (rc < 0) {
591  switch (errno) {
592  case EINTR:
593  continue;
594  /*@notreached@*/ /*@switchbreak@*/ break;
595  default:
596  return rc;
597  /*@notreached@*/ /*@switchbreak@*/ break;
598  }
599  }
600  return rc;
601  } while (1);
602  /*@notreached@*/
603 }
604 
605 int fdReadable(FD_t fd, int secs)
606 {
607  int fdno;
608  int rc;
609 #if defined(HAVE_POLL_H)
610  int msecs = (secs >= 0 ? (1000 * secs) : -1);
611  struct pollfd rdfds;
612 #else
613  struct timeval timeout, *tvp = (secs >= 0 ? &timeout : NULL);
614  fd_set rdfds;
615  FD_ZERO(&rdfds);
616 #endif
617 
618  /* HACK: flimsy wiring for davRead */
619  if (fd->req != NULL)
620  return (fd->req == (void *)-1 ? -1 : 1);
621 
622  if ((fdno = fdFileno(fd)) < 0)
623  return -1; /* XXX W2DO? */
624 
625  do {
626 #if defined(HAVE_POLL_H)
627  rdfds.fd = fdno;
628  rdfds.events = POLLIN;
629  rdfds.revents = 0;
630  rc = poll(&rdfds, 1, msecs);
631 #else
632  if (tvp) {
633  tvp->tv_sec = secs;
634  tvp->tv_usec = 0;
635  }
636  FD_SET(fdno, &rdfds);
637  /*@-compdef -nullpass@*/
638  rc = select(fdno + 1, &rdfds, NULL, NULL, tvp);
639  /*@=compdef =nullpass@*/
640 #endif
641 
642  if (rc < 0) {
643  switch (errno) {
644  case EINTR:
645  continue;
646  /*@notreached@*/ /*@switchbreak@*/ break;
647  default:
648  return rc;
649  /*@notreached@*/ /*@switchbreak@*/ break;
650  }
651  }
652  return rc;
653  } while (1);
654  /*@notreached@*/
655 }
656 
657 int fdFgets(FD_t fd, char * buf, size_t len)
658 {
659  int fdno;
660  int secs = fd->rd_timeoutsecs;
661  size_t nb = 0;
662  int ec = 0;
663  char lastchar = '\0';
664 
665  if ((fdno = fdFileno(fd)) < 0)
666  return 0; /* XXX W2DO? */
667 
668  do {
669  int rc;
670 
671  /* Is there data to read? */
672  rc = fdReadable(fd, secs);
673 
674  switch (rc) {
675  case -1: /* error */
676  ec = -1;
677  continue;
678  /*@notreached@*/ /*@switchbreak@*/ break;
679  case 0: /* timeout */
680  ec = -1;
681  continue;
682  /*@notreached@*/ /*@switchbreak@*/ break;
683  default: /* data to read */
684  /*@switchbreak@*/ break;
685  }
686 
687  errno = 0;
688 #ifdef NOISY
689  rc = fdRead(fd, buf + nb, 1);
690 #else
691  rc = (int)read(fdFileno(fd), buf + nb, 1);
692 #endif
693  if (rc < 0) {
694  fd->syserrno = errno;
695  switch (errno) {
696  case EWOULDBLOCK:
697  continue;
698  /*@notreached@*/ /*@switchbreak@*/ break;
699  default:
700  /*@switchbreak@*/ break;
701  }
702 if (_rpmio_debug)
703 fprintf(stderr, "*** read: fd %p rc %d errno %d %s \"%s\"\n", fd, rc, errno, strerror(errno), buf);
704  ec = -1;
705  break;
706  } else if (rc == 0) {
707 if (_rpmio_debug)
708 fprintf(stderr, "*** read: fd %p rc %d EOF errno %d %s \"%s\"\n", fd, rc, errno, strerror(errno), buf);
709  break;
710  } else {
711  nb += rc;
712  buf[nb] = '\0';
713  lastchar = buf[nb - 1];
714  }
715  } while (ec == 0 && nb < len && lastchar != '\n');
716 
717  return (ec >= 0 ? (int)nb : ec);
718 }
719 
720 /* =============================================================== */
721 /* Support for FTP/HTTP I/O.
722  */
723 const char * ftpStrerror(int errorNumber)
724 {
725  switch (errorNumber) {
726  case 0:
727  return _("Success");
728 
729  /* HACK error impediance match, coalesce and rename. */
730  case FTPERR_NE_ERROR:
731  return ("NE_ERROR: Generic error.");
732  case FTPERR_NE_LOOKUP:
733  return ("NE_LOOKUP: Hostname lookup failed.");
734  case FTPERR_NE_AUTH:
735  return ("NE_AUTH: Server authentication failed.");
736  case FTPERR_NE_PROXYAUTH:
737  return ("NE_PROXYAUTH: Proxy authentication failed.");
738  case FTPERR_NE_CONNECT:
739  return ("NE_CONNECT: Could not connect to server.");
740  case FTPERR_NE_TIMEOUT:
741  return ("NE_TIMEOUT: Connection timed out.");
742  case FTPERR_NE_FAILED:
743  return ("NE_FAILED: The precondition failed.");
744  case FTPERR_NE_RETRY:
745  return ("NE_RETRY: Retry request.");
746  case FTPERR_NE_REDIRECT:
747  return ("NE_REDIRECT: Redirect received.");
748 
750  return _("Bad server response");
752  return _("Server I/O error");
754  return _("Server timeout");
756  return _("Unable to lookup server host address");
757  case FTPERR_BAD_HOSTNAME:
758  return _("Unable to lookup server host name");
760  return _("Failed to connect to server");
762  return _("Failed to establish data connection to server");
764  return _("I/O error to local file");
766  return _("Error setting remote server to passive mode");
768  return _("File not found on server");
770  return _("Abort in progress");
771 
772  case FTPERR_UNKNOWN:
773  default:
774  return _("Unknown or unexpected error");
775  }
776 }
777 
778 const char *urlStrerror(const char *url)
779 {
780  const char *retstr;
781  switch (urlIsURL(url)) {
782  case URL_IS_HTTPS:
783  case URL_IS_HTTP:
784  case URL_IS_HKP:
785  case URL_IS_FTP:
786  { urlinfo u;
787 /* XXX This only works for httpReq/ftpLogin/ftpReq failures */
788  if (urlSplit(url, &u) == 0)
789  retstr = ftpStrerror(u->openError);
790  else
791  retstr = _("Malformed URL");
792  } break;
793  default:
794  retstr = strerror(errno);
795  break;
796  }
797  return retstr;
798 }
799 
800 #if !defined(HAVE_GETADDRINFO)
801 #if !defined(USE_ALT_DNS) || !USE_ALT_DNS
802 static int mygethostbyname(const char * host,
803  /*@out@*/ struct in_addr * address)
804  /*@globals h_errno @*/
805  /*@modifies *address @*/
806 {
807  struct hostent * hostinfo;
808 
809  /*@-multithreaded @*/
810  hostinfo = gethostbyname(host);
811  /*@=multithreaded @*/
812  if (!hostinfo) return 1;
813 
814  memcpy(address, hostinfo->h_addr_list[0], sizeof(*address));
815  return 0;
816 }
817 #endif
818 
819 /*@-compdef@*/ /* FIX: address->s_addr undefined. */
820 static int getHostAddress(const char * host, /*@out@*/ struct in_addr * address)
821  /*@globals errno, h_errno @*/
822  /*@modifies *address, errno @*/
823 {
824 #if 0 /* XXX workaround nss_foo module hand-off using valgrind. */
825  if (!strcmp(host, "localhost")) {
826  /*@-moduncon @*/
827  if (!inet_aton("127.0.0.1", address))
828  return FTPERR_BAD_HOST_ADDR;
829  /*@=moduncon @*/
830  } else
831 #endif
832  if (xisdigit(host[0])) {
833  /*@-moduncon @*/
834  if (!inet_aton(host, address))
835  return FTPERR_BAD_HOST_ADDR;
836  /*@=moduncon @*/
837  } else {
838  if (mygethostbyname(host, address)) {
839  errno = h_errno;
840  return FTPERR_BAD_HOSTNAME;
841  }
842  }
843 
844  return 0;
845 }
846 /*@=compdef@*/
847 #endif /* HAVE_GETADDRINFO */
848 
849 static int tcpConnect(FD_t ctrl, const char * host, int port)
850  /*@globals fileSystem, internalState @*/
851  /*@modifies ctrl, fileSystem, internalState @*/
852 {
853  int fdno = -1;
854  int rc;
855 #ifdef HAVE_GETADDRINFO
856 /*@-unrecog@*/
857  struct addrinfo hints, *res, *res0;
858  char pbuf[NI_MAXSERV];
859  int xx;
860 
861  memset(&hints, 0, sizeof(hints));
862  hints.ai_family = AF_UNSPEC;
863  hints.ai_socktype = SOCK_STREAM;
864  sprintf(pbuf, "%d", port);
865  pbuf[sizeof(pbuf)-1] = '\0';
867  if (getaddrinfo(host, pbuf, &hints, &res0) == 0) {
868  for (res = res0; res != NULL; res = res->ai_next) {
869  if ((fdno = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) < 0)
870  continue;
871  if (connect(fdno, res->ai_addr, (int)res->ai_addrlen) < 0) {
872  xx = close(fdno);
873  continue;
874  }
875  /* success */
876  rc = 0;
877  if (_ftp_debug) {
878  char hbuf[NI_MAXHOST];
879  hbuf[0] = '\0';
880  xx = getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, sizeof(hbuf),
881  NULL, 0, NI_NUMERICHOST);
882  fprintf(stderr,"++ connect [%s]:%d on fdno %d\n",
883  /*@-unrecog@*/ hbuf /*@=unrecog@*/, port, fdno);
884  }
885  break;
886  }
887  freeaddrinfo(res0);
888  }
889  if (rc < 0)
890  goto errxit;
891 /*@=unrecog@*/
892 #else /* HAVE_GETADDRINFO */
893  struct sockaddr_in sin;
894 
895  memset(&sin, 0, sizeof(sin));
896  sin.sin_family = AF_INET;
897  sin.sin_port = htons(port);
898  sin.sin_addr.s_addr = INADDR_ANY;
899 
900  do {
901  if ((rc = getHostAddress(host, &sin.sin_addr)) < 0)
902  break;
903 
904  if ((fdno = socket(sin.sin_family, SOCK_STREAM, IPPROTO_IP)) < 0) {
906  break;
907  }
908 
909  /*@-internalglobs@*/
910  if (connect(fdno, (struct sockaddr *) &sin, sizeof(sin))) {
912  break;
913  }
914  /*@=internalglobs@*/
915  } while (0);
916 
917  if (rc < 0)
918  goto errxit;
919 
920 if (_ftp_debug)
921 fprintf(stderr,"++ connect %s:%d on fdno %d\n",
922 /*@-unrecog -moduncon -evalorderuncon @*/
923 inet_ntoa(sin.sin_addr)
924 /*@=unrecog =moduncon =evalorderuncon @*/ ,
925 (int)ntohs(sin.sin_port), fdno);
926 #endif /* HAVE_GETADDRINFO */
927 
928  fdSetFdno(ctrl, (fdno >= 0 ? fdno : -1));
929  return 0;
930 
931 errxit:
932  /*@-observertrans@*/
933  fdSetSyserrno(ctrl, errno, ftpStrerror(rc));
934  /*@=observertrans@*/
935  if (fdno >= 0)
936  (void) close(fdno);
937  return rc;
938 }
939 
940 static int checkResponse(void * uu, FD_t ctrl,
941  /*@out@*/ int *ecp, /*@out@*/ char ** str)
942  /*@globals fileSystem @*/
943  /*@modifies ctrl, *ecp, *str, fileSystem @*/
944 {
945  urlinfo u = uu;
946  char *buf;
947  size_t bufAlloced;
948  int bufLength = 0;
949  const char *s;
950  char *se;
951  int ec = 0;
952  int moretodo = 1;
953  char errorCode[4];
954 
955  URLSANE(u);
956  if (u->bufAlloced == 0 || u->buf == NULL) {
958  u->buf = xcalloc(u->bufAlloced, sizeof(u->buf[0]));
959  }
960  buf = u->buf;
961  bufAlloced = u->bufAlloced;
962  *buf = '\0';
963 
964  errorCode[0] = '\0';
965 
966  do {
967  int rc;
968 
969  /*
970  * Read next line from server.
971  */
972  se = buf + bufLength;
973  *se = '\0';
974  rc = fdFgets(ctrl, se, (bufAlloced - bufLength));
975  if (rc < 0) {
977  continue;
978  } else if (rc == 0 || fdWritable(ctrl, 0) < 1)
979  moretodo = 0;
980 
981  /*
982  * Process next line from server.
983  */
984  for (s = se; *s != '\0'; s = se) {
985  const char *e;
986 
987  while (*se && *se != '\n') se++;
988 
989  if (se > s && se[-1] == '\r')
990  se[-1] = '\0';
991  if (*se == '\0')
992  /*@innerbreak@*/ break;
993 
994 if (_ftp_debug)
995 fprintf(stderr, "<- %s\n", s);
996 
997  /* HTTP: header termination on empty line */
998  if (*s == '\0') {
999  moretodo = 0;
1000  /*@innerbreak@*/ break;
1001  }
1002  *se++ = '\0';
1003 
1004  /* HTTP: look for "HTTP/1.1 123 ..." */
1005  if (!strncmp(s, "HTTP", sizeof("HTTP")-1)) {
1006  ctrl->contentLength = -1;
1007  if ((e = strchr(s, '.')) != NULL) {
1008  e++;
1009  u->httpVersion = (int)(*e - '0');
1010  if (u->httpVersion < 1 || u->httpVersion > 2)
1011  ctrl->persist = u->httpVersion = 0;
1012  else
1013  ctrl->persist = 1;
1014  }
1015  if ((e = strchr(s, ' ')) != NULL) {
1016  e++;
1017  if (strchr("0123456789", *e))
1018  strncpy(errorCode, e, 3);
1019  errorCode[3] = '\0';
1020  }
1021  /*@innercontinue@*/ continue;
1022  }
1023 
1024  /* HTTP: look for "token: ..." */
1025  for (e = s; *e && !(*e == ' ' || *e == ':'); e++)
1026  {};
1027  if (e > s && *e++ == ':') {
1028  size_t ne = (e - s);
1029  while (*e && *e == ' ') e++;
1030 #if 0
1031  if (!strncmp(s, "Date:", ne)) {
1032  } else
1033  if (!strncmp(s, "Server:", ne)) {
1034  } else
1035  if (!strncmp(s, "Last-Modified:", ne)) {
1036  } else
1037  if (!strncmp(s, "ETag:", ne)) {
1038  } else
1039 #endif
1040  if (!strncmp(s, "Accept-Ranges:", ne)) {
1041  if (!strcmp(e, "bytes"))
1043  if (!strcmp(e, "none"))
1045  } else
1046  if (!strncmp(s, "Content-Length:", ne)) {
1047  if (strchr("0123456789", *e))
1048  ctrl->contentLength = atol(e);
1049  } else
1050  if (!strncmp(s, "Connection:", ne)) {
1051  if (!strcmp(e, "close"))
1052  ctrl->persist = 0;
1053  }
1054 #if 0
1055  else
1056  if (!strncmp(s, "Content-Type:", ne)) {
1057  } else
1058  if (!strncmp(s, "Transfer-Encoding:", ne)) {
1059  if (!strcmp(e, "chunked"))
1060  ctrl->wr_chunked = 1;
1061  else
1062  ctrl->wr_chunked = 0;
1063  } else
1064  if (!strncmp(s, "Allow:", ne)) {
1065  }
1066 #endif
1067  /*@innercontinue@*/ continue;
1068  }
1069 
1070  /* HTTP: look for "<TITLE>501 ... </TITLE>" */
1071  if (!strncmp(s, "<TITLE>", sizeof("<TITLE>")-1))
1072  s += sizeof("<TITLE>") - 1;
1073 
1074  /* FTP: look for "123-" and/or "123 " */
1075  if (strchr("0123456789", *s)) {
1076  if (errorCode[0] != '\0') {
1077  if (!strncmp(s, errorCode, sizeof("123")-1) && s[3] == ' ')
1078  moretodo = 0;
1079  } else {
1080  strncpy(errorCode, s, sizeof("123")-1);
1081  errorCode[3] = '\0';
1082  if (s[3] != '-')
1083  moretodo = 0;
1084  }
1085  }
1086  }
1087 
1088  if (moretodo && se > s) {
1089  bufLength = se - s - 1;
1090  if (s != buf)
1091  memmove(buf, s, bufLength);
1092  } else {
1093  bufLength = 0;
1094  }
1095  } while (moretodo && ec == 0);
1096 
1097  if (str) *str = buf;
1098  if (ecp) *ecp = atoi(errorCode);
1099 
1100  return ec;
1101 }
1102 
1103 static int ftpCheckResponse(urlinfo u, /*@out@*/ char ** str)
1104  /*@globals fileSystem @*/
1105  /*@modifies u, *str, fileSystem @*/
1106 {
1107  int ec = 0;
1108  int rc;
1109 
1110  URLSANE(u);
1111  rc = checkResponse(u, u->ctrl, &ec, str);
1112 
1113  switch (ec) {
1114  case 550:
1115  return FTPERR_FILE_NOT_FOUND;
1116  /*@notreached@*/ break;
1117  case 552:
1119  /*@notreached@*/ break;
1120  default:
1121  if (ec >= 400 && ec <= 599) {
1123  }
1124  break;
1125  }
1126  return rc;
1127 }
1128 
1129 static int ftpCommand(urlinfo u, char ** str, ...)
1130  /*@globals fileSystem, internalState @*/
1131  /*@modifies u, *str, fileSystem, internalState @*/
1132 {
1133  va_list ap;
1134  int len = 0;
1135  const char * s, * t;
1136  char * te;
1137  int rc;
1138 
1139  URLSANE(u);
1140  va_start(ap, str);
1141  while ((s = va_arg(ap, const char *)) != NULL) {
1142  if (len) len++;
1143  len += strlen(s);
1144  }
1145  len += sizeof("\r\n")-1;
1146  va_end(ap);
1147 
1148  t = te = alloca(len + 1);
1149 
1150  va_start(ap, str);
1151  while ((s = va_arg(ap, const char *)) != NULL) {
1152  if (te > t) *te++ = ' ';
1153  te = stpcpy(te, s);
1154  }
1155  te = stpcpy(te, "\r\n");
1156  va_end(ap);
1157 
1158 if (_ftp_debug)
1159 fprintf(stderr, "-> %s", t);
1160  if (fdWrite(u->ctrl, t, (te-t)) != (te-t))
1161  return FTPERR_SERVER_IO_ERROR;
1162 
1163  rc = ftpCheckResponse(u, str);
1164  return rc;
1165 }
1166 
1167 static int ftpLogin(urlinfo u)
1168  /*@globals fileSystem, internalState @*/
1169  /*@modifies u, fileSystem, internalState @*/
1170 {
1171  const char * host;
1172  const char * user;
1173  const char * password;
1174  int port;
1175  int rc;
1176 
1177  URLSANE(u);
1178  u->ctrl = fdLink(u->ctrl, "open ctrl");
1179 
1180  if (((host = (u->proxyh ? u->proxyh : u->host)) == NULL)) {
1181  rc = FTPERR_BAD_HOSTNAME;
1182  goto errxit;
1183  }
1184 
1185  if ((port = (u->proxyp > 0 ? u->proxyp : u->port)) < 0) port = IPPORT_FTP;
1186 
1187  if ((user = (u->proxyu ? u->proxyu : u->user)) == NULL)
1188  user = "anonymous";
1189 
1190  if ((password = u->password) == NULL) {
1191  uid_t uid = getuid();
1192  struct passwd * pw;
1193  if (uid && (pw = getpwuid(uid)) != NULL) {
1194  char *myp = alloca(strlen(pw->pw_name) + sizeof("@"));
1195  strcpy(myp, pw->pw_name);
1196  strcat(myp, "@");
1197  password = myp;
1198  } else {
1199  password = "root@";
1200  }
1201  }
1202 
1203  if (fdFileno(u->ctrl) >= 0 && fdWritable(u->ctrl, 0) < 1)
1204  /*@-refcounttrans@*/ (void) fdClose(u->ctrl); /*@=refcounttrans@*/
1205 
1206 /*@-usereleased@*/
1207  if (fdFileno(u->ctrl) < 0) {
1208  rc = tcpConnect(u->ctrl, host, port);
1209  if (rc < 0)
1210  goto errxit2;
1211  }
1212 
1213  if ((rc = ftpCheckResponse(u, NULL)))
1214  goto errxit;
1215 
1216  if ((rc = ftpCommand(u, NULL, "USER", user, NULL)))
1217  goto errxit;
1218 
1219  if ((rc = ftpCommand(u, NULL, "PASS", password, NULL)))
1220  goto errxit;
1221 
1222  if ((rc = ftpCommand(u, NULL, "TYPE", "I", NULL)))
1223  goto errxit;
1224 
1225  /*@-compdef@*/
1226  return 0;
1227  /*@=compdef@*/
1228 
1229 errxit:
1230  /*@-observertrans@*/
1231  fdSetSyserrno(u->ctrl, errno, ftpStrerror(rc));
1232  /*@=observertrans@*/
1233 errxit2:
1234  if (fdFileno(u->ctrl) >= 0)
1235  /*@-refcounttrans@*/ (void) fdClose(u->ctrl); /*@=refcounttrans@*/
1236  /*@-compdef@*/
1237  return rc;
1238  /*@=compdef@*/
1239 /*@=usereleased@*/
1240 }
1241 
1242 int ftpReq(FD_t data, const char * ftpCmd, const char * ftpArg)
1243 {
1244  urlinfo u = data->url;
1245 #if !defined(HAVE_GETADDRINFO)
1246  struct sockaddr_in dataAddress;
1247 #endif /* HAVE_GETADDRINFO */
1248  char remoteIP[NI_MAXHOST];
1249  char * cmd;
1250  size_t cmdlen;
1251  char * passReply;
1252  char * chptr;
1253  int rc;
1254  int epsv;
1255  int port;
1256 
1257  remoteIP[0] = '\0';
1258  URLSANE(u);
1259  if (ftpCmd == NULL)
1260  return FTPERR_UNKNOWN; /* XXX W2DO? */
1261 
1262  cmdlen = strlen(ftpCmd) + (ftpArg ? 1+strlen(ftpArg) : 0) + sizeof("\r\n");
1263  chptr = cmd = alloca(cmdlen);
1264  chptr = stpcpy(chptr, ftpCmd);
1265  if (ftpArg) {
1266  *chptr++ = ' ';
1267  chptr = stpcpy(chptr, ftpArg);
1268  }
1269  chptr = stpcpy(chptr, "\r\n");
1270  cmdlen = chptr - cmd;
1271 
1272 /*
1273  * Get the ftp version of the Content-Length.
1274  */
1275  if (!strncmp(cmd, "RETR", 4)) {
1276  unsigned cl;
1277 
1278  passReply = NULL;
1279  rc = ftpCommand(u, &passReply, "SIZE", ftpArg, NULL);
1280  if (rc)
1281  goto errxit;
1282  if (sscanf(passReply, "%d %u", &rc, &cl) != 2) {
1284  goto errxit;
1285  }
1286  rc = 0;
1287  data->contentLength = cl;
1288  }
1289 
1290  epsv = 0;
1291  passReply = NULL;
1292 #ifdef HAVE_GETNAMEINFO
1293  rc = ftpCommand(u, &passReply, "EPSV", NULL);
1294  if (rc == 0) {
1295 #ifdef HAVE_GETADDRINFO
1296  struct sockaddr_storage ss;
1297 #else /* HAVE_GETADDRINFO */
1298  struct sockaddr_in ss;
1299 #endif /* HAVE_GETADDRINFO */
1300  socklen_t sslen = sizeof(ss);
1301 
1302  /* we need to know IP of remote host */
1303  if ((getpeername(fdFileno(c2f(u->ctrl)), (struct sockaddr *)&ss, &sslen) == 0)
1304  && (getnameinfo((struct sockaddr *)&ss, sslen,
1305  remoteIP, sizeof(remoteIP),
1306  NULL, 0, NI_NUMERICHOST) == 0))
1307  {
1308  epsv++;
1309  } else {
1310  /* abort EPSV and fall back to PASV */
1311  rc = ftpCommand(u, &passReply, "ABOR", NULL);
1312  if (rc) {
1313  rc = FTPERR_PASSIVE_ERROR;
1314  goto errxit;
1315  }
1316  }
1317  }
1318  if (epsv == 0)
1319 #endif /* HAVE_GETNAMEINFO */
1320  rc = ftpCommand(u, &passReply, "PASV", NULL);
1321  if (rc) {
1322  rc = FTPERR_PASSIVE_ERROR;
1323  goto errxit;
1324  }
1325 
1326  chptr = passReply;
1327 assert(chptr != NULL);
1328  while (*chptr && *chptr != '(') chptr++;
1329  if (*chptr != '(') return FTPERR_PASSIVE_ERROR;
1330  chptr++;
1331  passReply = chptr;
1332  while (*chptr && *chptr != ')') chptr++;
1333  if (*chptr != ')') return FTPERR_PASSIVE_ERROR;
1334  *chptr-- = '\0';
1335 
1336  if (epsv) {
1337  int i;
1338  if(sscanf(passReply,"%*c%*c%*c%d%*c",&i) != 1) {
1339  rc = FTPERR_PASSIVE_ERROR;
1340  goto errxit;
1341  }
1342  port = i;
1343  } else {
1344 
1345  while (*chptr && *chptr != ',') chptr--;
1346  if (*chptr != ',') return FTPERR_PASSIVE_ERROR;
1347  chptr--;
1348  while (*chptr && *chptr != ',') chptr--;
1349  if (*chptr != ',') return FTPERR_PASSIVE_ERROR;
1350  *chptr++ = '\0';
1351 
1352  /* now passReply points to the IP portion, and chptr points to the
1353  port number portion */
1354 
1355  { int i, j;
1356  if (sscanf(chptr, "%d,%d", &i, &j) != 2) {
1357  rc = FTPERR_PASSIVE_ERROR;
1358  goto errxit;
1359  }
1360  port = (((unsigned)i) << 8) + j;
1361  }
1362 
1363  chptr = passReply;
1364  while (*chptr++ != '\0') {
1365  if (*chptr == ',') *chptr = '.';
1366  }
1367  sprintf(remoteIP, "%s", passReply);
1368  } /* if (epsv) */
1369 
1370 #ifdef HAVE_GETADDRINFO
1371 /*@-unrecog@*/
1372  {
1373  struct addrinfo hints, *res, *res0;
1374  char pbuf[NI_MAXSERV];
1375  int xx;
1376 
1377  memset(&hints, 0, sizeof(hints));
1378  hints.ai_family = AF_UNSPEC;
1379  hints.ai_socktype = SOCK_STREAM;
1380  hints.ai_flags = AI_NUMERICHOST;
1381 #if defined(AI_IDN)
1382  hints.ai_flags |= AI_IDN;
1383 #endif
1384  sprintf(pbuf, "%d", port);
1385  pbuf[sizeof(pbuf)-1] = '\0';
1386  if (getaddrinfo(remoteIP, pbuf, &hints, &res0)) {
1387  rc = FTPERR_PASSIVE_ERROR;
1388  goto errxit;
1389  }
1390 
1391  for (res = res0; res != NULL; res = res->ai_next) {
1392  rc = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
1393  fdSetFdno(data, (rc >= 0 ? rc : -1));
1394  if (rc < 0) {
1395  if (res->ai_next)
1396  continue;
1397  else {
1398  rc = FTPERR_FAILED_CONNECT;
1399  freeaddrinfo(res0);
1400  goto errxit;
1401  }
1402  }
1403  data = fdLink(data, "open data (ftpReq)");
1404 
1405  /* XXX setsockopt SO_LINGER */
1406  /* XXX setsockopt SO_KEEPALIVE */
1407  /* XXX setsockopt SO_TOS IPTOS_THROUGHPUT */
1408 
1409  {
1410  int criterr = 0;
1411  while (connect(fdFileno(data), res->ai_addr, (int)res->ai_addrlen) < 0) {
1412  if (errno == EINTR)
1413  /*@innercontinue@*/ continue;
1414  criterr++;
1415  }
1416  if (criterr) {
1417  if (res->ai_addr) {
1418 /*@-refcounttrans@*/
1419  xx = fdClose(data);
1420 /*@=refcounttrans@*/
1421  continue;
1422  } else {
1423  rc = FTPERR_PASSIVE_ERROR;
1424  freeaddrinfo(res0);
1425  goto errxit;
1426  }
1427  }
1428  }
1429  /* success */
1430  rc = 0;
1431  break;
1432  }
1433  freeaddrinfo(res0);
1434  }
1435 /*@=unrecog@*/
1436 #else /* HAVE_GETADDRINFO */
1437  memset(&dataAddress, 0, sizeof(dataAddress));
1438  dataAddress.sin_family = AF_INET;
1439  dataAddress.sin_port = htons(port);
1440 
1441  /*@-moduncon@*/
1442  if (!inet_aton(remoteIP, &dataAddress.sin_addr)) {
1443  rc = FTPERR_PASSIVE_ERROR;
1444  goto errxit;
1445  }
1446  /*@=moduncon@*/
1447 
1448  rc = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
1449  fdSetFdno(data, (rc >= 0 ? rc : -1));
1450  if (rc < 0) {
1451  rc = FTPERR_FAILED_CONNECT;
1452  goto errxit;
1453  }
1454  data = fdLink(data, "open data (ftpReq)");
1455 
1456  /* XXX setsockopt SO_LINGER */
1457  /* XXX setsockopt SO_KEEPALIVE */
1458  /* XXX setsockopt SO_TOS IPTOS_THROUGHPUT */
1459 
1460  /*@-internalglobs@*/
1461  while (connect(fdFileno(data), (struct sockaddr *) &dataAddress,
1462  sizeof(dataAddress)) < 0)
1463  {
1464  if (errno == EINTR)
1465  continue;
1467  goto errxit;
1468  }
1469  /*@=internalglobs@*/
1470 #endif /* HAVE_GETADDRINFO */
1471 
1472 if (_ftp_debug)
1473 fprintf(stderr, "-> %s", cmd);
1474  if ((size_t)fdWrite(u->ctrl, cmd, cmdlen) != cmdlen) {
1476  goto errxit;
1477  }
1478 
1479  if ((rc = ftpCheckResponse(u, NULL))) {
1480  goto errxit;
1481  }
1482 
1483  data->ftpFileDoneNeeded = 1;
1484  u->ctrl = fdLink(u->ctrl, "grab data (ftpReq)");
1485  u->ctrl = fdLink(u->ctrl, "open data (ftpReq)");
1486  return 0;
1487 
1488 errxit:
1489  /*@-observertrans@*/
1490  fdSetSyserrno(u->ctrl, errno, ftpStrerror(rc));
1491  /*@=observertrans@*/
1492  if (fdFileno(data) >= 0)
1493  /*@-refcounttrans@*/ (void) fdClose(data); /*@=refcounttrans@*/
1494  return rc;
1495 }
1496 
1497 #ifdef DYING
1498 /*@unchecked@*/ /*@null@*/
1499 static rpmCallbackFunction _urlNotify = NULL;
1500 
1501 /*@unchecked@*/ /*@null@*/
1502 static void * _urlNotifyData = NULL;
1503 
1504 /*@unchecked@*/
1505 static int _urlNotifyCount = -1;
1506 
1507 static void urlSetCallback(rpmCallbackFunction notify, void *notifyData, int notifyCount) {
1508  _urlNotify = notify;
1509  _urlNotifyData = notifyData;
1510  _urlNotifyCount = (notifyCount >= 0) ? notifyCount : 4096;
1511 }
1512 #endif
1513 
1514 int ufdCopy(FD_t sfd, FD_t tfd)
1515 {
1516  char buf[BUFSIZ];
1517  int itemsRead;
1518  int itemsCopied = 0;
1519  int rc = 0;
1520 #ifdef DYING
1521  int notifier = -1;
1522 
1523  if (_urlNotify) {
1524  /*@-noeffectuncon @*/ /* FIX: check rc */
1525  (void)(*_urlNotify) (NULL, RPMCALLBACK_INST_OPEN_FILE,
1526  0, 0, NULL, _urlNotifyData);
1527  /*@=noeffectuncon @*/
1528  }
1529 #endif
1530 
1531  while (1) {
1532  rc = (int) Fread(buf, sizeof(buf[0]), sizeof(buf), sfd);
1533  if (rc < 0) /* XXX never happens Fread returns size_t */
1534  break;
1535  else if (rc == 0) {
1536  rc = itemsCopied;
1537  break;
1538  }
1539  itemsRead = rc;
1540  rc = (int) Fwrite(buf, sizeof(buf[0]), itemsRead, tfd);
1541  if (rc < 0) /* XXX never happens Fwrite returns size_t */
1542  break;
1543  if (rc != itemsRead) {
1544  rc = FTPERR_FILE_IO_ERROR;
1545  break;
1546  }
1547 
1548  itemsCopied += itemsRead;
1549 #ifdef DYING
1550  if (_urlNotify && _urlNotifyCount > 0) {
1551  int n = itemsCopied/_urlNotifyCount;
1552  if (n != notifier) {
1553  /*@-noeffectuncon @*/ /* FIX: check rc */
1554  (void)(*_urlNotify) (NULL, RPMCALLBACK_INST_PROGRESS,
1555  itemsCopied, 0, NULL, _urlNotifyData);
1556  /*@=noeffectuncon @*/
1557  notifier = n;
1558  }
1559  }
1560 #endif
1561  }
1562 
1563  DBGIO(sfd, (stderr, "++ copied %d bytes: %s\n", itemsCopied,
1564  ftpStrerror(rc)));
1565 
1566 #ifdef DYING
1567  if (_urlNotify) {
1568  /*@-noeffectuncon @*/ /* FIX: check rc */
1569  (void)(*_urlNotify) (NULL, RPMCALLBACK_INST_OPEN_FILE,
1570  itemsCopied, itemsCopied, NULL, _urlNotifyData);
1571  /*@=noeffectuncon @*/
1572  }
1573 #endif
1574 
1575  return rc;
1576 }
1577 
1578 static int urlConnect(const char * url, /*@out@*/ urlinfo * uret)
1579  /*@globals h_errno, fileSystem, internalState @*/
1580  /*@modifies *uret, fileSystem, internalState @*/
1581 {
1582  urlinfo u;
1583  int rc = 0;
1584 
1585  if (urlSplit(url, &u) < 0)
1586  return -1;
1587 
1588  if (u->urltype == URL_IS_FTP) {
1589  FD_t fd;
1590 
1591  if ((fd = u->ctrl) == NULL) {
1592  fd = u->ctrl = fdNew("persist ctrl (urlConnect FTP)");
1593 /*@-usereleased@*/
1594  fdSetOpen(u->ctrl, url, 0, 0);
1595  fdSetIo(u->ctrl, ufdio);
1596 /*@=usereleased@*/
1597  }
1598 
1599 assert(fd != NULL);
1601  fd->contentLength = fd->bytesRemain = -1;
1602  fd->url = NULL; /* XXX FTP ctrl has not */
1603  fd->ftpFileDoneNeeded = 0;
1604  fd = fdLink(fd, "grab ctrl (urlConnect FTP)");
1605 
1606  if (fdFileno(u->ctrl) < 0) {
1607  rpmlog(RPMLOG_DEBUG, D_("logging into %s as %s, pw %s\n"),
1608  u->host ? u->host : "???",
1609  u->user ? u->user : "ftp",
1610  u->password ? u->password : "(username)");
1611 
1612  if ((rc = ftpLogin(u)) < 0) { /* XXX save ftpLogin error */
1613  u->ctrl = fdFree(fd, "grab ctrl (urlConnect FTP)");
1614  u->openError = rc;
1615  }
1616  }
1617  }
1618 
1619  if (uret != NULL)
1620  *uret = urlLink(u, "urlConnect");
1621  u = urlFree(u, "urlSplit (urlConnect)");
1622 
1623  return rc;
1624 }
1625 
1626 int ufdGetFile(FD_t sfd, FD_t tfd)
1627 {
1628  int rc;
1629 
1630  FDSANE(sfd);
1631  FDSANE(tfd);
1632  rc = ufdCopy(sfd, tfd);
1633  (void) Fclose(sfd);
1634  if (rc > 0) /* XXX ufdCopy now returns no. bytes copied */
1635  rc = 0;
1636  return rc;
1637 }
1638 
1639 int ftpCmd(const char * cmd, const char * url, const char * arg2)
1640 {
1641  urlinfo u;
1642  int rc;
1643  const char * path;
1644 
1645  if (urlConnect(url, &u) < 0)
1646  return -1;
1647 
1648  (void) urlPath(url, &path);
1649 
1650  rc = ftpCommand(u, NULL, cmd, path, arg2, NULL);
1651  u->ctrl = fdFree(u->ctrl, "grab ctrl (ftpCmd)");
1652  return rc;
1653 }
1654 
1655 /* XXX these aren't worth the pain of including correctly */
1656 #if !defined(IAC)
1657 #define IAC ((unsigned char)255) /* interpret as command: */
1658 #endif
1659 #if !defined(IP)
1660 #define IP ((unsigned char)244) /* interrupt process--permanently */
1661 #endif
1662 #if !defined(DM)
1663 #define DM ((unsigned char)242) /* data mark--for connect. cleaning */
1664 #endif
1665 #if !defined(SHUT_RDWR)
1666 #define SHUT_RDWR 1+1
1667 #endif
1668 
1669 static int ftpAbort(urlinfo u, FD_t data)
1670  /*@globals fileSystem, internalState @*/
1671  /*@modifies u, data, fileSystem, internalState @*/
1672 {
1673  static unsigned char ipbuf[3] = { IAC, IP, IAC };
1674  FD_t ctrl;
1675  int rc;
1676  int tosecs;
1677 
1678  URLSANE(u);
1679 
1680  if (data != NULL) {
1681  data->ftpFileDoneNeeded = 0;
1682  if (fdFileno(data) >= 0)
1683  u->ctrl = fdFree(u->ctrl, "open data (ftpAbort)");
1684  u->ctrl = fdFree(u->ctrl, "grab data (ftpAbort)");
1685  }
1686  ctrl = u->ctrl;
1687 
1688  DBGIO(0, (stderr, "-> ABOR\n"));
1689 
1690 /*@-usereleased -compdef@*/
1691  if (send(fdFileno(ctrl), ipbuf, sizeof(ipbuf), MSG_OOB) != sizeof(ipbuf)) {
1692  /*@-refcounttrans@*/ (void) fdClose(ctrl); /*@=refcounttrans@*/
1693  return FTPERR_SERVER_IO_ERROR;
1694  }
1695 
1696  sprintf(u->buf, "%cABOR\r\n",(char) DM);
1697  if (fdWrite(ctrl, u->buf, 7) != 7) {
1698  /*@-refcounttrans@*/ (void) fdClose(ctrl); /*@=refcounttrans@*/
1699  return FTPERR_SERVER_IO_ERROR;
1700  }
1701 
1702  if (data && fdFileno(data) >= 0) {
1703  /* XXX shorten data drain time wait */
1704  tosecs = data->rd_timeoutsecs;
1705  data->rd_timeoutsecs = 10;
1706  if (fdReadable(data, data->rd_timeoutsecs) > 0) {
1707 /*@-infloopsuncon@*/
1708  while ((ufdio->read)(data, u->buf, u->bufAlloced) > 0)
1709  u->buf[0] = '\0';
1710 /*@=infloopsuncon@*/
1711  }
1712  data->rd_timeoutsecs = tosecs;
1713  /* XXX ftp abort needs to close the data channel to receive status */
1714  (void) shutdown(fdFileno(data), SHUT_RDWR);
1715  (void) close(fdFileno(data));
1716  data->fps[0].fdno = -1; /* XXX WRONG but expedient */
1717  }
1718 
1719  /* XXX shorten ctrl drain time wait */
1720 assert(u->ctrl != NULL);
1721  tosecs = u->ctrl->rd_timeoutsecs;
1722  u->ctrl->rd_timeoutsecs = 10;
1723  if ((rc = ftpCheckResponse(u, NULL)) == FTPERR_NIC_ABORT_IN_PROGRESS) {
1724  rc = ftpCheckResponse(u, NULL);
1725  }
1726  rc = ftpCheckResponse(u, NULL);
1727  u->ctrl->rd_timeoutsecs = tosecs;
1728 
1729  return rc;
1730 /*@=usereleased =compdef@*/
1731 }
1732 
1733 static int ftpFileDone(urlinfo u, FD_t data)
1734  /*@globals fileSystem @*/
1735  /*@modifies u, data, fileSystem @*/
1736 {
1737  int rc = 0;
1738 
1739  URLSANE(u);
1740  assert(data->ftpFileDoneNeeded);
1741 
1742  if (data->ftpFileDoneNeeded) {
1743  data->ftpFileDoneNeeded = 0;
1744  u->ctrl = fdFree(u->ctrl, "open data (ftpFileDone)");
1745  u->ctrl = fdFree(u->ctrl, "grab data (ftpFileDone)");
1746  rc = ftpCheckResponse(u, NULL);
1747  }
1748  return rc;
1749 }
1750 
1751 #ifndef WITH_NEON
1752 static int httpResp(urlinfo u, FD_t ctrl, /*@out@*/ char ** str)
1753  /*@globals fileSystem @*/
1754  /*@modifies ctrl, *str, fileSystem @*/
1755 {
1756  int ec = 0;
1757  int rc;
1758 
1759  URLSANE(u);
1760  rc = checkResponse(u, ctrl, &ec, str);
1761 
1762 if (_ftp_debug && !(rc == 0 && (ec == 200 || ec == 201)))
1763 fprintf(stderr, "*** httpResp: rc %d ec %d\n", rc, ec);
1764 
1765  switch (ec) {
1766  case 200:
1767  case 201: /* 201 Created. */
1768  break;
1769  case 204: /* HACK: if overwriting, 204 No Content. */
1770  case 403: /* 403 Forbidden. */
1771  ctrl->syserrno = EACCES; /* HACK */
1772  rc = FTPERR_UNKNOWN;
1773  break;
1774  default:
1775  rc = FTPERR_FILE_NOT_FOUND;
1776  break;
1777  }
1778  return rc;
1779 }
1780 
1781 static int httpReq(FD_t ctrl, const char * httpCmd, const char * httpArg)
1782  /*@globals h_errno, fileSystem, internalState @*/
1783  /*@modifies ctrl, fileSystem, internalState @*/
1784 {
1785  urlinfo u;
1786  const char * host;
1787  const char * path;
1788  char hthost[NI_MAXHOST];
1789  int port;
1790  int rc;
1791  char * req;
1792  size_t len;
1793  int retrying = 0;
1794 
1795 assert(ctrl != NULL);
1796  u = ctrl->url;
1797  URLSANE(u);
1798 
1799  if (((host = (u->proxyh ? u->proxyh : u->host)) == NULL))
1800  return FTPERR_BAD_HOSTNAME;
1801  if (strchr(host, ':'))
1802  sprintf(hthost, "[%s]", host);
1803  else
1804  strcpy(hthost, host);
1805 
1806  if ((port = (u->proxyp > 0 ? u->proxyp : u->port)) < 0) port = 80;
1807  path = (u->proxyh || u->proxyp > 0) ? u->url : httpArg;
1808  if (path == NULL) path = "";
1809 
1810 reopen:
1811  if (fdFileno(ctrl) >= 0 && (rc = fdWritable(ctrl, 0)) < 1) {
1812  /*@-refcounttrans@*/ (void) fdClose(ctrl); /*@=refcounttrans@*/
1813  }
1814 
1815 /*@-usereleased@*/
1816  if (fdFileno(ctrl) < 0) {
1817  rc = tcpConnect(ctrl, host, port);
1818  if (rc < 0)
1819  goto errxit2;
1820  ctrl = fdLink(ctrl, "open ctrl (httpReq)");
1821  }
1822 
1823  len = sizeof("\
1824 req x HTTP/1.0\r\n\
1825 User-Agent: rpm/3.0.4\r\n\
1826 Host: y:z\r\n\
1827 Accept: text/plain\r\n\
1828 Transfer-Encoding: chunked\r\n\
1829 \r\n\
1830 ") + strlen(httpCmd) + strlen(path) + sizeof(VERSION) + strlen(hthost) + 20;
1831 
1832  req = alloca(len);
1833  *req = '\0';
1834 
1835  if (!strcmp(httpCmd, "PUT")) {
1836  sprintf(req, "\
1837 %s %s HTTP/1.%d\r\n\
1838 User-Agent: rpm/%s\r\n\
1839 Host: %s:%d\r\n\
1840 Accept: text/plain\r\n\
1841 Transfer-Encoding: chunked\r\n\
1842 \r\n\
1843 ", httpCmd, path, (u->httpVersion ? 1 : 0), VERSION, hthost, port);
1844 } else {
1845  sprintf(req, "\
1846 %s %s HTTP/1.%d\r\n\
1847 User-Agent: rpm/%s\r\n\
1848 Host: %s:%d\r\n\
1849 Accept: text/plain\r\n\
1850 \r\n\
1851 ", httpCmd, path, (u->httpVersion ? 1 : 0), VERSION, hthost, port);
1852 }
1853 
1854 if (_ftp_debug)
1855 fprintf(stderr, "-> %s", req);
1856 
1857  len = strlen(req);
1858  if (fdWrite(ctrl, req, len) != len) {
1860  goto errxit;
1861  }
1862 
1863  if (!strcmp(httpCmd, "PUT")) {
1864  ctrl->wr_chunked = 1;
1865  } else {
1866 
1867  rc = httpResp(u, ctrl, NULL);
1868 
1869  if (rc) {
1870  if (!retrying) { /* not HTTP_OK */
1871  retrying = 1;
1872  /*@-refcounttrans@*/ (void) fdClose(ctrl); /*@=refcounttrans@*/
1873  goto reopen;
1874  }
1875  goto errxit;
1876  }
1877  }
1878 
1879  ctrl = fdLink(ctrl, "open data (httpReq)");
1880  return 0;
1881 
1882 errxit:
1883  /*@-observertrans@*/
1884  fdSetSyserrno(ctrl, errno, ftpStrerror(rc));
1885  /*@=observertrans@*/
1886 errxit2:
1887  if (fdFileno(ctrl) >= 0)
1888  /*@-refcounttrans@*/ (void) fdClose(ctrl); /*@=refcounttrans@*/
1889  return rc;
1890 /*@=usereleased@*/
1891 }
1892 #endif /* WITH_NEON */
1893 
1894 /* XXX DYING: unused */
1896 {
1897  FDSANE(fd);
1898  if (fd->url == NULL)
1899  return NULL;
1900 /*@-retexpose@*/
1901  return urlLink(fd->url, "ufdGetUrlinfo");
1902 /*@=retexpose@*/
1903 }
1904 
1905 /* =============================================================== */
1906 static ssize_t ufdRead(void * cookie, /*@out@*/ char * buf, size_t count)
1907  /*@globals fileSystem, internalState @*/
1908  /*@modifies buf, fileSystem, internalState @*/
1909  /*@requires maxSet(buf) >= (count - 1) @*/
1910 {
1911  FD_t fd = c2f(cookie);
1912  size_t bytesRead;
1913  size_t total;
1914 
1915  if (fdGetIo(fd) == fdio) {
1916  struct stat sb;
1917  int fdno = fdFileno(fd);
1918  (void) fstat(fdno, &sb);
1919  if (S_ISREG(sb.st_mode))
1920  return fdRead(fd, buf, count);
1921  }
1922 
1923  UFDONLY(fd);
1924  assert(fd->rd_timeoutsecs >= 0);
1925 
1926  for (total = 0; total < count; total += bytesRead) {
1927 
1928  int rc;
1929 
1930  bytesRead = 0;
1931 
1932  /* Is there data to read? */
1933  if (fd->bytesRemain == 0) return (ssize_t) total; /* XXX simulate EOF */
1934  rc = fdReadable(fd, fd->rd_timeoutsecs);
1935 
1936  switch (rc) {
1937  case -1: /* error */
1938  case 0: /* timeout */
1939  return (ssize_t) total;
1940  /*@notreached@*/ /*@switchbreak@*/ break;
1941  default: /* data to read */
1942  /*@switchbreak@*/ break;
1943  }
1944 
1945  rc = (int) fdRead(fd, buf + total, count - total);
1946 
1947  if (rc < 0) {
1948  switch (errno) {
1949  case EWOULDBLOCK:
1950  continue;
1951  /*@notreached@*/ /*@switchbreak@*/ break;
1952  default:
1953  /*@switchbreak@*/ break;
1954  }
1955 if (_rpmio_debug)
1956 fprintf(stderr, "*** read: rc %d errno %d %s \"%s\"\n", rc, errno, strerror(errno), buf);
1957  return rc;
1958  /*@notreached@*/ break;
1959  } else if (rc == 0) {
1960  return (ssize_t) total;
1961  /*@notreached@*/ break;
1962  }
1963  bytesRead = (size_t) rc;
1964  }
1965 
1966  return (ssize_t) count;
1967 }
1968 
1969 static ssize_t ufdWrite(void * cookie, const char * buf, size_t count)
1970  /*@globals fileSystem, internalState @*/
1971  /*@modifies fileSystem, internalState @*/
1972 {
1973  FD_t fd = c2f(cookie);
1974  size_t bytesWritten;
1975  size_t total = 0;
1976 
1977 #ifdef NOTYET
1978  if (fdGetIo(fd) == fdio) {
1979  struct stat sb;
1980  (void) fstat(fdGetFdno(fd), &sb);
1981  if (S_ISREG(sb.st_mode))
1982  return fdWrite(fd, buf, count);
1983  }
1984 #endif
1985 
1986  UFDONLY(fd);
1987 
1988  for (total = 0; total < count; total += bytesWritten) {
1989 
1990  int rc;
1991 
1992  bytesWritten = 0;
1993 
1994  /* Is there room to write data? */
1995  if (fd->bytesRemain == 0) {
1996 fprintf(stderr, "*** ufdWrite fd %p WRITE PAST END OF CONTENT\n", fd);
1997  return (ssize_t) total; /* XXX simulate EOF */
1998  }
1999  rc = fdWritable(fd, 2); /* XXX configurable? */
2000 
2001  switch (rc) {
2002  case -1: /* error */
2003  case 0: /* timeout */
2004  return (ssize_t) total;
2005  /*@notreached@*/ /*@switchbreak@*/ break;
2006  default: /* data to write */
2007  /*@switchbreak@*/ break;
2008  }
2009 
2010  rc = (int) fdWrite(fd, buf + total, count - total);
2011 
2012  if (rc < 0) {
2013  switch (errno) {
2014  case EWOULDBLOCK:
2015  continue;
2016  /*@notreached@*/ /*@switchbreak@*/ break;
2017  default:
2018  /*@switchbreak@*/ break;
2019  }
2020 if (_rpmio_debug)
2021 fprintf(stderr, "*** write: rc %d errno %d %s \"%s\"\n", rc, errno, strerror(errno), buf);
2022  return rc;
2023  /*@notreached@*/ break;
2024  } else if (rc == 0) {
2025  return (ssize_t) total;
2026  /*@notreached@*/ break;
2027  }
2028  bytesWritten = (size_t) rc;
2029  }
2030 
2031  return (ssize_t) count;
2032 }
2033 
2034 static inline int ufdSeek(void * cookie, _libio_pos_t pos, int whence)
2035  /*@globals fileSystem, internalState @*/
2036  /*@modifies fileSystem, internalState @*/
2037 {
2038  FD_t fd = c2f(cookie);
2039 
2040  switch (fd->urlType) {
2041  case URL_IS_UNKNOWN:
2042  case URL_IS_PATH:
2043  break;
2044  case URL_IS_HTTPS:
2045  case URL_IS_HTTP:
2046  case URL_IS_HKP:
2047  case URL_IS_FTP:
2048  case URL_IS_DASH:
2049  default:
2050  return -2;
2051  /*@notreached@*/ break;
2052  }
2053  return fdSeek(cookie, pos, whence);
2054 }
2055 
2056 /*@-usereleased@*/ /* LCL: fd handling is tricky here. */
2057 int ufdClose( /*@only@*/ void * cookie)
2058 {
2059  FD_t fd = c2f(cookie);
2060 
2061  UFDONLY(fd);
2062 
2063  if (fd->url) {
2064  urlinfo u = fd->url;
2065 
2066 /*@-evalorder @*/
2067  if (fd == u->data)
2068  fd = u->data = fdFree(fd, "grab data (ufdClose persist)");
2069  else
2070  fd = fdFree(fd, "grab data (ufdClose)");
2071 assert(fd != NULL);
2072  (void) urlFree(fd->url, "url (ufdClose)");
2073  fd->url = NULL;
2074  u->ctrl = fdFree(u->ctrl, "grab ctrl (ufdClose)");
2075 /*@=evalorder @*/
2076 
2077  if (u->urltype == URL_IS_FTP) {
2078 
2079  /* XXX if not using libio, lose the fp from fpio */
2080  { FILE * fp;
2081  /*@+voidabstract -nullpass@*/
2082  fp = fdGetFILE(fd);
2083  if (noLibio && fp)
2084  fdSetFp(fd, NULL);
2085  /*@=voidabstract =nullpass@*/
2086  }
2087 
2088  /*
2089  * Non-error FTP has 4 refs on the data fd:
2090  * "persist data (ufdOpen FTP)" rpmio.c:888
2091  * "grab data (ufdOpen FTP)" rpmio.c:892
2092  * "open data (ftpReq)" ftp.c:633
2093  * "fopencookie" rpmio.c:1507
2094  *
2095  * Non-error FTP has 5 refs on the ctrl fd:
2096  * "persist ctrl" url.c:176
2097  * "grab ctrl (urlConnect FTP)" rpmio.c:404
2098  * "open ctrl" ftp.c:504
2099  * "grab data (ftpReq)" ftp.c:661
2100  * "open data (ftpReq)" ftp.c:662
2101  */
2102  if (fd->bytesRemain > 0) {
2103  if (fd->ftpFileDoneNeeded) {
2104  if (fdReadable(u->ctrl, 0) > 0)
2105  (void) ftpFileDone(u, fd);
2106  else
2107  (void) ftpAbort(u, fd);
2108  }
2109  } else {
2110  int rc;
2111  /* XXX STOR et al require close before ftpFileDone */
2112  /*@-refcounttrans@*/
2113  rc = fdClose(fd);
2114  /*@=refcounttrans@*/
2115 #if 0 /* XXX error exit from ufdOpen does not have this set */
2116  assert(fd->ftpFileDoneNeeded != 0);
2117 #endif
2118  /*@-compdef@*/ /* FIX: u->data undefined */
2119  if (fd->ftpFileDoneNeeded)
2120  (void) ftpFileDone(u, fd);
2121  /*@=compdef@*/
2122  return rc;
2123  }
2124  }
2125 
2126  /* XXX Why not (u->urltype == URL_IS_HTTP) ??? */
2127  /* XXX Why not (u->urltype == URL_IS_HTTPS) ??? */
2128  /* XXX Why not (u->urltype == URL_IS_HKP) ??? */
2129  if (u->scheme != NULL
2130  && (!strncmp(u->scheme, "http", sizeof("http")-1) || !strncmp(u->scheme, "hkp", sizeof("hkp")-1)))
2131  {
2132  /*
2133  * HTTP has 4 (or 5 if persistent malloc) refs on the fd:
2134  * "persist ctrl" url.c:177
2135  * "grab ctrl (ufdOpen HTTP)" rpmio.c:924
2136  * "grab data (ufdOpen HTTP)" rpmio.c:928
2137  * "open ctrl (httpReq)" ftp.c:382
2138  * "open data (httpReq)" ftp.c:435
2139  */
2140 
2141 /*@-evalorder @*/
2142  if (fd == u->ctrl)
2143  fd = u->ctrl = fdFree(fd, "open data (ufdClose HTTP persist ctrl)");
2144  else if (fd == u->data)
2145  fd = u->data = fdFree(fd, "open data (ufdClose HTTP persist data)");
2146  else
2147  fd = fdFree(fd, "open data (ufdClose HTTP)");
2148 /*@=evalorder @*/
2149 
2150  /* XXX if not using libio, lose the fp from fpio */
2151  { FILE * fp;
2152  /*@+voidabstract -nullpass@*/
2153  fp = fdGetFILE(fd);
2154  if (noLibio && fp)
2155  fdSetFp(fd, NULL);
2156  /*@=voidabstract =nullpass@*/
2157  }
2158 
2159  /* If content remains, then don't persist. */
2160 assert(fd != NULL);
2161  if (fd->bytesRemain > 0)
2162  fd->persist = 0;
2163  fd->contentLength = fd->bytesRemain = -1;
2164 
2165  /* If persisting, then Fclose will juggle refcounts. */
2166  if (fd->persist && (fd == u->ctrl || fd == u->data))
2167  return 0;
2168  }
2169  }
2170  return fdClose(fd);
2171 }
2172 /*@=usereleased@*/
2173 
2174 /*@-nullstate@*/ /* FIX: u->{ctrl,data}->url undef after XurlLink. */
2175 /*@null@*/ FD_t ftpOpen(const char *url, /*@unused@*/ int flags,
2176  /*@unused@*/ mode_t mode, /*@out@*/ urlinfo *uret)
2177  /*@modifies *uret @*/
2178 {
2179  urlinfo u = NULL;
2180  FD_t fd = NULL;
2181 
2182 #if 0 /* XXX makeTempFile() heartburn */
2183  assert(!(flags & O_RDWR));
2184 #endif
2185  if (urlConnect(url, &u) < 0)
2186  goto exit;
2187 
2188  if (u->data == NULL)
2189  u->data = fdNew("persist data (ftpOpen)");
2190 
2191 assert(u->data != NULL);
2192 /*@-unqualifiedtrans@*/
2193  if (u->data->url == NULL)
2194  fd = u->data = fdLink(u->data, "grab data (ftpOpen persist data)");
2195  else
2196  fd = fdNew("grab data (ftpOpen)");
2197 /*@=unqualifiedtrans@*/
2198 
2199  if (fd != NULL) {
2200  fdSetOpen(fd, url, flags, mode);
2201  fdSetIo(fd, ufdio);
2202  fd->ftpFileDoneNeeded = 0;
2204  fd->contentLength = fd->bytesRemain = -1;
2205 /*@-usereleased@*/
2206  fd->url = urlLink(u, "url (ufdOpen FTP)");
2207 /*@=usereleased@*/
2208  fd->urlType = URL_IS_FTP;
2209  }
2210 
2211 exit:
2212  if (uret)
2213  *uret = u;
2214  /*@-refcounttrans@*/
2215  return fd;
2216  /*@=refcounttrans@*/
2217 }
2218 /*@=nullstate@*/
2219 
2220 static /*@null@*/ FD_t ufdOpen(const char * url, int flags, mode_t mode)
2221  /*@globals h_errno, fileSystem, internalState @*/
2222  /*@modifies fileSystem, internalState @*/
2223 {
2224  FD_t fd = NULL;
2225  const char * cmd;
2226  urlinfo u;
2227  const char * path;
2228  urltype urlType = urlPath(url, &path);
2229 
2230 if (_rpmio_debug)
2231 fprintf(stderr, "*** ufdOpen(%s,0x%x,0%o)\n", url, (unsigned)flags, (unsigned)mode);
2232 
2233 /*@-usereleased@*/
2234  switch (urlType) {
2235  case URL_IS_FTP:
2236  fd = ftpOpen(url, flags, mode, &u);
2237  if (fd == NULL || u == NULL)
2238  break;
2239 
2240  /* XXX W2DO? use STOU rather than STOR to prevent clobbering */
2241  cmd = ((flags & O_WRONLY)
2242  ? ((flags & O_APPEND) ? "APPE" :
2243  ((flags & O_CREAT) ? "STOR" : "STOR"))
2244  : ((flags & O_CREAT) ? "STOR" : "RETR"));
2245  u->openError = ftpReq(fd, cmd, path);
2246  if (u->openError < 0) {
2247  /* XXX make sure that we can exit through ufdClose */
2248  fd = fdLink(fd, "error data (ufdOpen FTP)");
2249  } else {
2250  fd->bytesRemain = ((!strcmp(cmd, "RETR"))
2251  ? fd->contentLength : -1);
2252  fd->wr_chunked = 0;
2253  }
2254  break;
2255  case URL_IS_HTTPS:
2256  case URL_IS_HTTP:
2257  case URL_IS_HKP:
2258 #ifdef WITH_NEON
2259  fd = davOpen(url, flags, mode, &u);
2260 #else
2261  fd = httpOpen(url, flags, mode, &u);
2262 #endif
2263  if (fd == NULL || u == NULL)
2264  break;
2265 
2266  cmd = ((flags & O_WRONLY)
2267  ? ((flags & O_APPEND) ? "PUT" :
2268  ((flags & O_CREAT) ? "PUT" : "PUT"))
2269  : "GET");
2270 #ifdef WITH_NEON
2271  u->openError = davReq(fd, cmd, path);
2272 #else
2273  u->openError = httpReq(fd, cmd, path);
2274 #endif
2275  if (u->openError < 0) {
2276  /* XXX make sure that we can exit through ufdClose */
2277  fd = fdLink(fd, "error ctrl (ufdOpen HTTP)");
2278  fd = fdLink(fd, "error data (ufdOpen HTTP)");
2279  } else {
2280  fd->bytesRemain = ((!strcmp(cmd, "GET"))
2281  ? fd->contentLength : -1);
2282  fd->wr_chunked = ((!strcmp(cmd, "PUT"))
2283  ? fd->wr_chunked : 0);
2284  }
2285  break;
2286  case URL_IS_DASH:
2287  assert(!(flags & O_RDWR));
2288  fd = fdDup( ((flags & O_WRONLY) ? STDOUT_FILENO : STDIN_FILENO) );
2289  if (fd) {
2290  fdSetOpen(fd, url, flags, mode);
2291  fdSetIo(fd, ufdio);
2292  fd->rd_timeoutsecs = 600; /* XXX W2DO? 10 mins? */
2293  fd->contentLength = fd->bytesRemain = -1;
2294  }
2295  break;
2296  case URL_IS_PATH:
2297  case URL_IS_UNKNOWN:
2298  default:
2299  fd = fdOpen(path, flags, mode);
2300  if (fd) {
2301  fdSetIo(fd, ufdio);
2302 #if defined(RPM_VENDOR_MANDRIVA) /* raise-read-timeout-to-60secs */
2303  fd->rd_timeoutsecs = 60;
2304 #else
2305  fd->rd_timeoutsecs = 1;
2306 #endif
2307  fd->contentLength = fd->bytesRemain = -1;
2308  }
2309  break;
2310  }
2311 
2312  if (fd == NULL) return NULL;
2313  fd->urlType = urlType;
2314  if (Fileno(fd) < 0) {
2315  (void) ufdClose(fd);
2316  return NULL;
2317  }
2318 /*@=usereleased@*/
2319 DBGIO(fd, (stderr, "==>\tufdOpen(\"%s\",%x,0%o) %s\n", url, (unsigned)flags, (unsigned)mode, fdbg(fd)));
2320  return fd;
2321 }
2322 
2323 /*@-type@*/ /* LCL: function typedefs */
2324 static struct FDIO_s ufdio_s = {
2325  ufdRead, ufdWrite, ufdSeek, ufdClose, NULL, NULL, NULL,
2326 };
2327 /*@=type@*/
2328 
2329 FDIO_t ufdio = /*@-compmempass@*/ &ufdio_s /*@=compmempass@*/ ;
2330 
2331 /* =============================================================== */
2332 /*@observer@*/
2333 static const char * getFdErrstr (FD_t fd)
2334  /*@*/
2335 {
2336  const char *errstr = NULL;
2337 
2338 #if defined(WITH_ZLIB)
2339  if (fdGetIo(fd) == gzdio) {
2340  errstr = fd->errcookie;
2341  } else
2342 #endif /* WITH_ZLIB */
2343 
2344 #if defined(WITH_BZIP2)
2345  if (fdGetIo(fd) == bzdio) {
2346  errstr = fd->errcookie;
2347  } else
2348 #endif
2349 
2350 #if defined(WITH_XZ)
2351  if (fdGetIo(fd) == lzdio) {
2352  errstr = fd->errcookie;
2353  } else
2354  if (fdGetIo(fd) == xzdio) {
2355  errstr = fd->errcookie;
2356  } else
2357 #endif
2358 
2359  {
2360  errstr = (fd->syserrno ? strerror(fd->syserrno) : "");
2361  }
2362 
2363  return errstr;
2364 }
2365 
2366 /* =============================================================== */
2367 
2368 const char *Fstrerror(FD_t fd)
2369 {
2370  if (fd == NULL)
2371  return (errno ? strerror(errno) : "");
2372  FDSANE(fd);
2373  return getFdErrstr(fd);
2374 }
2375 
2376 #define FDIOVEC(_fd, _vec) \
2377  ((fdGetIo(_fd) && fdGetIo(_fd)->_vec) ? fdGetIo(_fd)->_vec : NULL)
2378 
2379 size_t Fread(void *buf, size_t size, size_t nmemb, FD_t fd) {
2380  fdio_read_function_t _read;
2381  int rc;
2382 
2383  FDSANE(fd);
2384 DBGIO(fd, (stderr, "==> Fread(%p,%u,%u,%p) %s\n", buf, (unsigned)size, (unsigned)nmemb, (fd ? fd : NULL), fdbg(fd)));
2385 
2386  if (fdGetIo(fd) == fpio) {
2387  /*@+voidabstract -nullpass@*/
2388  rc = (int) fread(buf, size, nmemb, fdGetFILE(fd));
2389  /*@=voidabstract =nullpass@*/
2390  return (size_t) rc;
2391  }
2392 
2393  /*@-nullderef@*/
2394  _read = FDIOVEC(fd, read);
2395  /*@=nullderef@*/
2396 
2397  rc = (int) (_read ? (*_read) (fd, buf, size * nmemb) : -2);
2398  return (size_t) rc;
2399 }
2400 
2401 size_t Fwrite(const void *buf, size_t size, size_t nmemb, FD_t fd)
2402 {
2403  fdio_write_function_t _write;
2404  int rc;
2405 
2406  FDSANE(fd);
2407 DBGIO(fd, (stderr, "==> Fwrite(%p,%u,%u,%p) %s\n", buf, (unsigned)size, (unsigned)nmemb, (fd ? fd : NULL), fdbg(fd)));
2408 
2409  if (fdGetIo(fd) == fpio) {
2410  /*@+voidabstract -nullpass@*/
2411  rc = (int) fwrite(buf, size, nmemb, fdGetFILE(fd));
2412  /*@=voidabstract =nullpass@*/
2413  return (size_t) rc;
2414  }
2415 
2416  /*@-nullderef@*/
2417  _write = FDIOVEC(fd, write);
2418  /*@=nullderef@*/
2419 
2420  rc = (int) (_write ? _write(fd, buf, size * nmemb) : -2);
2421  return (size_t) rc;
2422 }
2423 
2424 int Fseek(FD_t fd, _libio_off_t offset, int whence) {
2425  fdio_seek_function_t _seek;
2426 #ifdef USE_COOKIE_SEEK_POINTER
2427  _IO_off64_t o64 = offset;
2428  _libio_pos_t pos = &o64;
2429 #else
2430  _libio_pos_t pos = offset;
2431 #endif
2432 
2433  long int rc;
2434 
2435  FDSANE(fd);
2436 DBGIO(fd, (stderr, "==> Fseek(%p,%ld,%d) %s\n", fd, (long)offset, whence, fdbg(fd)));
2437 
2438  if (fdGetIo(fd) == fpio) {
2439  FILE *fp;
2440 
2441  /*@+voidabstract -nullpass@*/
2442  fp = fdGetFILE(fd);
2443  rc = fseek(fp, (long)offset, whence);
2444  /*@=voidabstract =nullpass@*/
2445  return rc;
2446  }
2447 
2448  /*@-nullderef@*/
2449  _seek = FDIOVEC(fd, seek);
2450  /*@=nullderef@*/
2451 
2452  rc = (_seek ? _seek(fd, pos, whence) : -2);
2453  return rc;
2454 }
2455 
2456 int Fclose(FD_t fd)
2457 {
2458  int rc = 0, ec = 0;
2459 
2460  FDSANE(fd);
2461 DBGIO(fd, (stderr, "==> Fclose(%p) %s\n", (fd ? fd : NULL), fdbg(fd)));
2462 
2463 /*@-usereleased@*/
2464  fd = fdLink(fd, "Fclose");
2465  if (fd != NULL)
2466  while (fd->nfps >= 0) {
2467  FDSTACK_t * fps = &fd->fps[fd->nfps];
2468 
2469  if (fps->io == fpio) {
2470  FILE *fp;
2471  int fpno;
2472 
2473  /*@+voidabstract -nullpass@*/
2474  fp = fdGetFILE(fd);
2475  fpno = fileno(fp);
2476  /*@=voidabstract =nullpass@*/
2477  /* XXX persistent HTTP/1.1 returns the previously opened fp */
2478  if (fd->nfps > 0 && fpno == -1 &&
2479  fd->fps[fd->nfps-1].io == ufdio &&
2480  fd->fps[fd->nfps-1].fp == fp &&
2481  (fd->fps[fd->nfps-1].fdno >= 0 || fd->req != NULL))
2482  {
2483  int hadreqpersist = (fd->req != NULL);
2484 
2485  if (fp)
2486  rc = fflush(fp);
2487  fd->nfps--;
2488  /*@-refcounttrans@*/
2489  rc = ufdClose(fd);
2490  /*@=refcounttrans@*/
2491  if (fdGetFdno(fd) >= 0)
2492  break;
2493  if (!fd->persist)
2494  hadreqpersist = 0;
2495  fdSetFp(fd, NULL);
2496  fd->nfps++;
2497  if (fp) {
2498  /* HACK: flimsy Keepalive wiring. */
2499  if (hadreqpersist) {
2500 #ifdef NOTYET /* XXX not quite right yet. */
2501  (void) davDisconnect(fd);
2502  fd->req = NULL;
2503 #endif
2504  fd->nfps--;
2505 /*@-exposetrans@*/
2506  fdSetFp(fd, fp);
2507 /*@=exposetrans@*/
2508 /*@-refcounttrans@*/
2509  (void) fdClose(fd);
2510 /*@=refcounttrans@*/
2511  fdSetFp(fd, NULL);
2512  fd->nfps++;
2513 /*@-refcounttrans@*/
2514  (void) fdClose(fd);
2515 /*@=refcounttrans@*/
2516  } else
2517  rc = fclose(fp);
2518  }
2519  fdPop(fd);
2520  if (noLibio)
2521  fdSetFp(fd, NULL);
2522  } else {
2523  if (fp)
2524  rc = fclose(fp);
2525  if (fpno == -1) {
2526  fdPop(fd);
2527  fd = fdFree(fd, "fopencookie (Fclose)");
2528  }
2529  }
2530  } else {
2531  /*@-nullderef@*/
2532  fdio_close_function_t _close = FDIOVEC(fd, close);
2533  /*@=nullderef@*/
2534  rc = _close(fd);
2535  }
2536  if (fd == NULL || fd->nfps == 0) /* XXX fd != NULL ever */
2537  break;
2538  if (ec == 0 && rc)
2539  ec = rc;
2540  fdPop(fd);
2541  }
2542  fd = fdFree(fd, "Fclose");
2543  return ec;
2544 /*@=usereleased@*/
2545 }
2546 
2564 static inline void cvtfmode (const char *m,
2565  /*@out@*/ char *stdio, size_t nstdio,
2566  /*@out@*/ char *other, size_t nother,
2567  /*@out@*/ const char **end, /*@out@*/ int * f)
2568  /*@modifies *stdio, *other, *end, *f @*/
2569 {
2570  int flags = 0;
2571  char c;
2572 
2573  switch (*m) {
2574  case 'a':
2575  flags |= O_WRONLY | O_CREAT | O_APPEND;
2576  if (--nstdio > 0) *stdio++ = *m;
2577  break;
2578  case 'w':
2579  flags |= O_WRONLY | O_CREAT | O_TRUNC;
2580  if (--nstdio > 0) *stdio++ = *m;
2581  break;
2582  case 'r':
2583  flags |= O_RDONLY;
2584  if (--nstdio > 0) *stdio++ = *m;
2585  break;
2586  default:
2587  *stdio = '\0';
2588  return;
2589  /*@notreached@*/ break;
2590  }
2591  m++;
2592 
2593  while ((c = *m++) != '\0') {
2594  switch (c) {
2595  case '.':
2596  /*@switchbreak@*/ break;
2597  case '+':
2598  flags &= ~(O_RDONLY|O_WRONLY);
2599  flags |= O_RDWR;
2600  if (--nstdio > 0) *stdio++ = c;
2601  continue;
2602  /*@notreached@*/ /*@switchbreak@*/ break;
2603  case 'x': /* glibc: open file exclusively. */
2604  flags |= O_EXCL;
2605  /*@fallthrough@*/
2606  case 'm': /* glibc: mmap'd reads */
2607  case 'c': /* glibc: no cancel */
2608 #if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ >= 3
2609  if (--nstdio > 0) *stdio++ = c;
2610 #endif
2611  continue;
2612  /*@notreached@*/ /*@switchbreak@*/ break;
2613  case 'b':
2614  if (--nstdio > 0) *stdio++ = c;
2615  continue;
2616  /*@notreached@*/ /*@switchbreak@*/ break;
2617  default:
2618  if (--nother > 0) *other++ = c;
2619  continue;
2620  /*@notreached@*/ /*@switchbreak@*/ break;
2621  }
2622  break;
2623  }
2624  if (c == '\0') m--; /* one too far */
2625 
2626  *stdio = *other = '\0';
2627  if (end != NULL)
2628  *end = (*m != '\0' ? m : NULL);
2629  if (f != NULL)
2630  *f = flags;
2631 }
2632 
2633 #if _USE_LIBIO
2634 #if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ == 0
2635 /* XXX retrofit glibc-2.1.x typedef on glibc-2.0.x systems */
2636 typedef _IO_cookie_io_functions_t cookie_io_functions_t;
2637 #endif
2638 #endif
2639 
2640 FD_t Fdopen(FD_t ofd, const char *fmode)
2641 {
2642  char stdio[20], other[20], zstdio[40+1];
2643  const char *end = NULL;
2644  FDIO_t iof = NULL;
2645  FD_t fd = ofd;
2646 
2647 if (_rpmio_debug)
2648 fprintf(stderr, "*** Fdopen(%p,%s) %s\n", fd, fmode, fdbg(fd));
2649  FDSANE(fd);
2650 
2651  if (fmode == NULL)
2652  return NULL;
2653 
2654  cvtfmode(fmode, stdio, sizeof(stdio), other, sizeof(other), &end, NULL);
2655  if (stdio[0] == '\0')
2656  return NULL;
2657  zstdio[0] = '\0';
2658  (void) stpcpy( stpcpy(zstdio, stdio), other);
2659 
2660  if (end == NULL && other[0] == '\0')
2661  /*@-refcounttrans -retalias@*/ return fd; /*@=refcounttrans =retalias@*/
2662 
2663  if (end && *end) {
2664  if (!strcmp(end, "fdio")) {
2665  iof = fdio;
2666 #if defined(WITH_ZLIB)
2667  } else if (!strcmp(end, "gzdio")) {
2668  iof = gzdio;
2669  /*@-internalglobs@*/
2670  fd = iof->_fdopen(fd, zstdio);
2671  /*@=internalglobs@*/
2672 #endif
2673 #if defined(WITH_BZIP2)
2674  } else if (!strcmp(end, "bzdio")) {
2675  iof = bzdio;
2676  /*@-internalglobs@*/
2677  fd = iof->_fdopen(fd, zstdio);
2678  /*@=internalglobs@*/
2679 #endif
2680 #if defined(WITH_XZ)
2681  } else if (!strcmp(end, "lzdio")) {
2682  iof = lzdio;
2683  fd = iof->_fdopen(fd, zstdio);
2684  } else if (!strcmp(end, "xzdio")) {
2685  iof = xzdio;
2686  fd = iof->_fdopen(fd, zstdio);
2687 #endif
2688  } else if (!strcmp(end, "ufdio")) {
2689  iof = ufdio;
2690  } else if (!strcmp(end, "fpio")) {
2691  iof = fpio;
2692  if (noLibio) {
2693  int fdno = Fileno(fd);
2694  FILE * fp = fdopen(fdno, stdio);
2695 /*@+voidabstract -nullpass@*/
2696 if (_rpmio_debug)
2697 fprintf(stderr, "*** Fdopen fpio fp %p\n", (void *)fp);
2698 /*@=voidabstract =nullpass@*/
2699  if (fp == NULL)
2700  return NULL;
2701  /* XXX gzdio/bzdio use fp for private data */
2702  /*@+voidabstract@*/
2703  if (fdGetFp(fd) == NULL)
2704  fdSetFp(fd, fp);
2705  fdPush(fd, fpio, fp, fdno); /* Push fpio onto stack */
2706  /*@=voidabstract@*/
2707  }
2708  }
2709  } else if (other[0] != '\0') {
2710  for (end = other; *end && strchr("0123456789fh", *end); end++)
2711  {};
2712  if (*end == '\0') {
2713 #if defined(WITH_ZLIB)
2714  iof = gzdio;
2715  /*@-internalglobs@*/
2716  fd = iof->_fdopen(fd, zstdio);
2717  /*@=internalglobs@*/
2718 #endif
2719  }
2720  }
2721  if (iof == NULL)
2722  /*@-refcounttrans -retalias@*/ return fd; /*@=refcounttrans =retalias@*/
2723 
2724  if (!noLibio) {
2725  FILE * fp = NULL;
2726 
2727 #if _USE_LIBIO
2728  { cookie_io_functions_t ciof;
2729  ciof.read = iof->read;
2730  ciof.write = iof->write;
2731  ciof.seek = iof->seek;
2732  ciof.close = iof->close;
2733  fp = fopencookie(fd, stdio, ciof);
2734 DBGIO(fd, (stderr, "==> fopencookie(%p,\"%s\",*%p) returns fp %p\n", fd, stdio, iof, fp));
2735  }
2736 #endif
2737 
2738  if (fp) {
2739  /* XXX gzdio/bzdio use fp for private data */
2740  /*@+voidabstract -nullpass@*/
2741  if (fdGetFp(fd) == NULL)
2742  fdSetFp(fd, fp);
2743  fdPush(fd, fpio, fp, fileno(fp)); /* Push fpio onto stack */
2744  /*@=voidabstract =nullpass@*/
2745  fd = fdLink(fd, "fopencookie");
2746  }
2747  }
2748 
2749 /*@-refcounttrans -retalias -usereleased @*/
2750 DBGIO(fd, (stderr, "==> Fdopen(%p,\"%s\") returns fd %p %s\n", ofd, fmode, (fd ? fd : NULL), fdbg(fd)));
2751  return fd;
2752 /*@=refcounttrans =retalias =usereleased @*/
2753 }
2754 
2755 FD_t Fopen(const char *path, const char *_fmode)
2756 {
2757  const char * fmode = NULL;
2758  char stdio[20], other[20];
2759  const char *end = NULL;
2760  mode_t perms = 0666;
2761  int flags = 0;
2762  FD_t fd = NULL;
2763 
2764  if (path == NULL || _fmode == NULL)
2765  goto exit;
2766 /*@-globs -mods@*/
2767  fmode = rpmExpand(_fmode, NULL);
2768 /*@=globs =mods@*/
2769 
2770  stdio[0] = '\0';
2771  cvtfmode(fmode, stdio, sizeof(stdio), other, sizeof(other), &end, &flags);
2772  if (stdio[0] == '\0')
2773  goto exit;
2774 
2775  if (end == NULL || !strcmp(end, "fdio")) {
2776 if (_rpmio_debug)
2777 fprintf(stderr, "*** Fopen(%s, %s) fdio\n", path, fmode);
2778  fd = fdOpen(path, flags, perms);
2779  if (fdFileno(fd) < 0) {
2780  if (fd) (void) fdClose(fd);
2781  fd = NULL;
2782  goto exit;
2783  }
2784  } else {
2785  FILE *fp;
2786  int fdno;
2787  int isHTTP = 0;
2788 
2789  /* XXX gzdio/bzdio/lzdio through here too */
2790 
2791  switch (urlIsURL(path)) {
2792  case URL_IS_HTTPS:
2793  case URL_IS_HTTP:
2794  case URL_IS_HKP:
2795  isHTTP = 1;
2796  /*@fallthrough@*/
2797  case URL_IS_PATH:
2798  case URL_IS_DASH:
2799  case URL_IS_FTP:
2800  case URL_IS_UNKNOWN:
2801 if (_rpmio_debug)
2802 fprintf(stderr, "*** Fopen(%s, %s) ufdio\n", path, fmode);
2803  fd = ufdOpen(path, flags, perms);
2804  if (fd == NULL || !(fdFileno(fd) >= 0 || fd->req != NULL)) {
2805  if (fd) (void) fdClose(fd);
2806  fd = NULL;
2807  goto exit;
2808  }
2809  break;
2810  default:
2811 if (_rpmio_debug)
2812 fprintf(stderr, "*** Fopen(%s, %s) WTFO\n", path, fmode);
2813  if (fd) (void) fdClose(fd);
2814  fd = NULL;
2815  goto exit;
2816  /*@notreached@*/ break;
2817  }
2818 
2819  /* XXX persistent HTTP/1.1 returns the previously opened fp */
2820  if (isHTTP && ((fp = fdGetFp(fd)) != NULL) && ((fdno = fdGetFdno(fd)) >= 0 || fd->req != NULL))
2821  {
2822  /*@+voidabstract@*/
2823  fdPush(fd, fpio, fp, fileno(fp)); /* Push fpio onto stack */
2824  /*@=voidabstract@*/
2825  goto exit;
2826  }
2827  }
2828 
2829  if (fd)
2830  fd = Fdopen(fd, fmode);
2831 exit:
2832  fmode = _free(fmode);
2833  return fd;
2834 }
2835 
2836 int Fflush(FD_t fd)
2837 {
2838  void * vh;
2839  if (fd == NULL) return -1;
2840  if (fdGetIo(fd) == fpio)
2841  /*@+voidabstract -nullpass@*/
2842  return fflush(fdGetFILE(fd));
2843  /*@=voidabstract =nullpass@*/
2844 
2845  vh = fdGetFp(fd);
2846 #if defined(WITH_ZLIB)
2847  if (vh && fdGetIo(fd) == gzdio && gzdio->_flush != NULL)
2848  return (*gzdio->_flush) ((void *)fd);
2849 #endif
2850 #if defined(WITH_BZIP2)
2851  if (vh && fdGetIo(fd) == bzdio && bzdio->_flush != NULL)
2852  return (*bzdio->_flush) ((void *)fd);
2853 #endif
2854 #if defined(WITH_XZ)
2855  if (vh && fdGetIo(fd) == lzdio && lzdio->_flush != NULL)
2856  return (*lzdio->_flush) ((void *)fd);
2857  if (vh && fdGetIo(fd) == xzdio && xzdio->_flush != NULL)
2858  return (*xzdio->_flush) ((void *)fd);
2859 #endif
2860 
2861  return 0;
2862 }
2863 
2864 int Ferror(FD_t fd)
2865 {
2866  int i, rc = 0;
2867 
2868  if (fd == NULL) return -1;
2869  if (fd->req != NULL) {
2870  /* HACK: flimsy wiring for neon errors. */
2871  rc = (fd->req == (void *)-1 || fd->syserrno || fd->errcookie != NULL) ? -1 : 0;
2872  } else
2873  for (i = fd->nfps; rc == 0 && i >= 0; i--) {
2874  FDSTACK_t * fps = &fd->fps[i];
2875  int ec;
2876 
2877  if (fps->io == fpio) {
2878  /*@+voidabstract -nullpass@*/
2879  ec = ferror(fdGetFILE(fd));
2880  /*@=voidabstract =nullpass@*/
2881 #if defined(WITH_ZLIB)
2882  } else if (fps->io == gzdio) {
2883  ec = (fd->syserrno || fd->errcookie != NULL) ? -1 : 0;
2884  i--; /* XXX fdio under gzdio always has fdno == -1 */
2885 #endif
2886 #if defined(WITH_BZIP2)
2887  } else if (fps->io == bzdio) {
2888  ec = (fd->syserrno || fd->errcookie != NULL) ? -1 : 0;
2889  i--; /* XXX fdio under bzdio always has fdno == -1 */
2890 #endif
2891 #if defined(WITH_XZ)
2892  } else if (fps->io == lzdio) {
2893  ec = (fd->syserrno || fd->errcookie != NULL) ? -1 : 0;
2894  i--; /* XXX fdio under lzdio always has fdno == -1 */
2895  } else if (fps->io == xzdio) {
2896  ec = (fd->syserrno || fd->errcookie != NULL) ? -1 : 0;
2897  i--; /* XXX fdio under xzdio always has fdno == -1 */
2898 #endif
2899  } else {
2900  /* XXX need to check ufdio/gzdio/bzdio/fdio errors correctly. */
2901  ec = (fdFileno(fd) < 0 ? -1 : 0);
2902  }
2903 
2904  if (rc == 0 && ec)
2905  rc = ec;
2906  }
2907 DBGIO(fd, (stderr, "==> Ferror(%p) rc %d %s\n", fd, rc, fdbg(fd)));
2908  return rc;
2909 }
2910 
2911 int Fileno(FD_t fd)
2912 {
2913  int i, rc = -1;
2914 
2915  if (fd == NULL)
2916  return -1;
2917  if (fd->req != NULL)
2918  rc = 123456789; /* HACK: https has no steenkin fileno. */
2919  else
2920  for (i = fd->nfps ; rc == -1 && i >= 0; i--) {
2921  rc = fd->fps[i].fdno;
2922  }
2923 
2924 DBGIO(fd, (stderr, "==> Fileno(%p) rc %d %s\n", (fd ? fd : NULL), rc, fdbg(fd)));
2925  return rc;
2926 }
2927 
2928 /* XXX this is naive */
2929 int Fcntl(FD_t fd, int op, void *lip)
2930 {
2931  return fcntl(Fileno(fd), op, lip);
2932 }
2933 
2934 /* =============================================================== */
2935 /* Helper routines that may be generally useful.
2936  */
2937 int rpmioMkpath(const char * path, mode_t mode, uid_t uid, gid_t gid)
2938 {
2939  char * d, * de;
2940  int created = 0;
2941  int rc;
2942 
2943  if (path == NULL || *path == '\0')
2944  return -1;
2945  d = alloca(strlen(path)+2);
2946  de = stpcpy(d, path);
2947  de[1] = '\0';
2948  for (de = d; *de != '\0'; de++) {
2949  struct stat st;
2950  char savec;
2951 
2952  while (*de && *de != '/') de++;
2953  savec = de[1];
2954  de[1] = '\0';
2955 
2956  rc = Stat(d, &st);
2957  if (rc) {
2958  switch(errno) {
2959  default:
2960  return errno;
2961  /*@notreached@*/ /*@switchbreak@*/ break;
2962  case ENOENT:
2963  /*@switchbreak@*/ break;
2964  }
2965  rc = Mkdir(d, mode);
2966  if (rc)
2967  return errno;
2968  created = 1;
2969  if (!(uid == (uid_t) -1 && gid == (gid_t) -1)) {
2970  rc = Chown(d, uid, gid);
2971  if (rc)
2972  return errno;
2973  }
2974  } else if (!S_ISDIR(st.st_mode)) {
2975  return ENOTDIR;
2976  }
2977  de[1] = savec;
2978  }
2979  rc = 0;
2980  if (created)
2981  rpmlog(RPMLOG_DEBUG, D_("created directory(s) %s mode 0%o\n"),
2982  path, (unsigned)mode);
2983  return rc;
2984 }
2985 
2986 #define _PATH "/bin:/usr/bin:/sbin:/usr/sbin"
2987 /*@unchecked@*/ /*@observer@*/
2988 static const char *_path = _PATH;
2989 
2990 #define alloca_strdup(_s) strcpy(alloca(strlen(_s)+1), (_s))
2991 
2992 int rpmioAccess(const char * FN, const char * path, int mode)
2993 {
2994  char fn[4096];
2995  char * bn;
2996  char * r, * re;
2997  char * t, * te;
2998  int negate = 0;
2999  int rc = 0;
3000 
3001  /* Empty paths are always accessible. */
3002  if (FN == NULL || *FN == '\0')
3003  return 0;
3004 
3005  if (mode == 0)
3006  mode = X_OK;
3007 
3008  /* Strip filename out of its name space wrapper. */
3009  bn = alloca_strdup(FN);
3010  for (t = bn; t && *t; t++) {
3011  if (*t != '(')
3012  continue;
3013  *t++ = '\0';
3014 
3015  /* Permit negation on name space tests. */
3016  if (*bn == '!') {
3017  negate = 1;
3018  bn++;
3019  }
3020 
3021  /* Set access flags from name space marker. */
3022  if (strlen(bn) == 3
3023  && strchr("Rr_", bn[0]) != NULL
3024  && strchr("Ww_", bn[1]) != NULL
3025  && strchr("Xx_", bn[2]) != NULL) {
3026  mode = 0;
3027  if (strchr("Rr", bn[0]) != NULL)
3028  mode |= R_OK;
3029  if (strchr("Ww", bn[1]) != NULL)
3030  mode |= W_OK;
3031  if (strchr("Xx", bn[2]) != NULL)
3032  mode |= X_OK;
3033  if (mode == 0)
3034  mode = F_OK;
3035  } else if (!strcmp(bn, "exists"))
3036  mode = F_OK;
3037  else if (!strcmp(bn, "executable"))
3038  mode = X_OK;
3039  else if (!strcmp(bn, "readable"))
3040  mode = R_OK;
3041  else if (!strcmp(bn, "writable"))
3042  mode = W_OK;
3043 
3044  bn = t;
3045  te = bn + strlen(t) - 1;
3046  if (*te != ')') /* XXX syntax error, never exists */
3047  return 1;
3048  *te = '\0';
3049  break;
3050  }
3051 
3052  /* Empty paths are always accessible. */
3053  if (*bn == '\0')
3054  goto exit;
3055 
3056  /* Check absolute path for access. */
3057  if (*bn == '/') {
3058  rc = (Access(bn, mode) != 0 ? 1 : 0);
3059 if (_rpmio_debug)
3060 fprintf(stderr, "*** rpmioAccess(\"%s\", 0x%x) rc %d\n", bn, mode, rc);
3061  goto exit;
3062  }
3063 
3064  /* Find path to search. */
3065  if (path == NULL)
3066  path = getenv("PATH");
3067  if (path == NULL)
3068  path = _path;
3069  if (path == NULL) {
3070  rc = 1;
3071  goto exit;
3072  }
3073 
3074  /* Look for relative basename on PATH. */
3075  for (r = alloca_strdup(path); r != NULL && *r != '\0'; r = re) {
3076 
3077  /* Find next element, terminate current element. */
3078  for (re = r; (re = strchr(re, ':')) != NULL; re++) {
3079  if (!(re[1] == '/' && re[2] == '/'))
3080  /*@innerbreak@*/ break;
3081  }
3082  if (re && *re == ':')
3083  *re++ = '\0';
3084  else
3085  re = r + strlen(r);
3086 
3087  /* Expand ~/ to $HOME/ */
3088  fn[0] = '\0';
3089  t = fn;
3090  *t = '\0'; /* XXX redundant. */
3091  if (r[0] == '~' && r[1] == '/') {
3092  const char * home = getenv("HOME");
3093  if (home == NULL) /* XXX No HOME? */
3094  continue;
3095  if (strlen(home) > (sizeof(fn) - strlen(r))) /* XXX too big */
3096  continue;
3097  t = stpcpy(t, home);
3098  r++; /* skip ~ */
3099  }
3100  t = stpcpy(t, r);
3101  if (t[-1] != '/' && *bn != '/')
3102  *t++ = '/';
3103  t = stpcpy(t, bn);
3104  t = rpmCleanPath(fn);
3105  if (t == NULL) /* XXX can't happen */
3106  continue;
3107 
3108  /* Check absolute path for access. */
3109  rc = (Access(t, mode) != 0 ? 1 : 0);
3110 if (_rpmio_debug)
3111 fprintf(stderr, "*** rpmioAccess(\"%s\", 0x%x) rc %d\n", t, mode, rc);
3112  if (rc == 0)
3113  goto exit;
3114  }
3115 
3116  rc = 1;
3117 
3118 exit:
3119  if (negate)
3120  rc ^= 1;
3121  return rc;
3122 }
3123 
3124 #if defined(WITH_NSS) && !defined(__LCLINT__) /* XXX TODO: add nssDestroy */
3125 /*@-exportheader@*/
3126 extern void NSS_Shutdown(void);
3127 /*@=exportheader@*/
3128 
3129 /*@unchecked@*/
3130 int _rpmnss_init = 0;
3131 #endif
3132 
3133 void rpmioClean(void)
3134 {
3135 /*@-nestedextern@*/
3136  extern rpmioPool _urlPool;
3137  extern rpmioPool _xarPool;
3138  extern rpmioPool _digPool;
3139  extern rpmioPool _rpmiobPool;
3140 /*@-shadow@*/
3141  extern rpmioPool _mirePool;
3142  extern rpmioPool _htPool;
3143  extern rpmioPool _rpmsyckPool;
3144 /*@=shadow@*/
3145  extern rpmioPool _rpmmgPool;
3146  extern rpmioPool _rpmluavPool;
3147  extern rpmioPool _rpmluaPool;
3148  extern rpmioPool _rpmficlPool;
3149  extern rpmioPool _rpmjsPool;
3150  extern rpmioPool _rpmperlPool;
3151  extern rpmioPool _rpmpythonPool;
3152  extern rpmioPool _rpmrubyPool;
3153  extern rpmioPool _rpmtclPool;
3154 /*@=nestedextern@*/
3155 
3156 #if defined(WITH_LUA)
3157  (void) rpmluaFree(NULL);
3158 #endif
3159 #if defined(WITH_NEON)
3160  davDestroy();
3161 #endif
3162 #if defined(WITH_NSS) && !defined(__LCLINT__)
3163  if (_rpmnss_init) {
3164  (void) NSS_Shutdown();
3165  _rpmnss_init = 0;
3166  }
3167 #endif
3168  urlFreeCache();
3169 
3170  _rpmtclI = rpmtclFree(_rpmtclI);
3171  _rpmtclPool = rpmioFreePool(_rpmtclPool);
3172  _rpmrubyI = rpmrubyFree(_rpmrubyI);
3173  _rpmrubyPool = rpmioFreePool(_rpmrubyPool);
3174  _rpmpythonI = rpmpythonFree(_rpmpythonI);
3175  _rpmpythonPool = rpmioFreePool(_rpmpythonPool);
3176  _rpmperlI = rpmperlFree(_rpmperlI);
3177  _rpmperlPool = rpmioFreePool(_rpmperlPool);
3178  _rpmjsI = rpmjsFree(_rpmjsI);
3179  _rpmjsPool = rpmioFreePool(_rpmjsPool);
3180  _rpmficlI = rpmficlFree(_rpmficlI);
3181  _rpmficlPool = rpmioFreePool(_rpmficlPool);
3182  _rpmluavPool = rpmioFreePool(_rpmluavPool);
3183  _rpmluaPool = rpmioFreePool(_rpmluaPool);
3184  _mirePool = rpmioFreePool(_mirePool);
3185  _rpmmgPool = rpmioFreePool(_rpmmgPool);
3186  _htPool = rpmioFreePool(_htPool);
3187  _rpmsyckPool = rpmioFreePool(_rpmsyckPool);
3188  _rpmiobPool = rpmioFreePool(_rpmiobPool);
3189  _digPool = rpmioFreePool(_digPool);
3190  _xarPool = rpmioFreePool(_xarPool);
3191  _urlPool = rpmioFreePool(_urlPool);
3192  _fdPool = rpmioFreePool(_fdPool);
3193 
3194  rpmlogClose();
3195 }
3196 
3197 /*@-type@*/ /* LCL: function typedefs */
3198 static struct FDIO_s fpio_s = {
3199  ufdRead, ufdWrite, fdSeek, ufdClose, NULL, NULL, NULL,
3200 };
3201 /*@=type@*/
3202 
3203 FDIO_t fpio = /*@-compmempass@*/ &fpio_s /*@=compmempass@*/ ;