rpm 5.3.7

rpmio/rpmio.c

Go to the documentation of this file.
00001 
00005 #include "system.h"
00006 #include <stdarg.h>
00007 
00008 #if defined(HAVE_MACHINE_TYPES_H)
00009 # include <machine/types.h>
00010 #endif
00011 
00012 #if defined(HAVE_SYS_SOCKET_H)
00013 # include <sys/socket.h>
00014 #endif
00015 
00016 #ifndef NI_MAXHOST
00017 #define NI_MAXHOST      1025
00018 #endif
00019 
00020 #if defined(__LCLINT__)
00021 struct addrinfo
00022 {
00023   int ai_flags;                 /* Input flags.  */
00024   int ai_family;                /* Protocol family for socket.  */
00025   int ai_socktype;              /* Socket type.  */
00026   int ai_protocol;              /* Protocol for socket.  */
00027   socklen_t ai_addrlen;         /* Length of socket address.  */
00028   struct sockaddr *ai_addr;     /* Socket address for socket.  */
00029   char *ai_canonname;           /* Canonical name for service location.  */
00030   struct addrinfo *ai_next;     /* Pointer to next in list.  */
00031 };
00032 
00033 /*@-exportheader -incondefs @*/
00034 extern int getaddrinfo (__const char *__restrict __name,
00035                         __const char *__restrict __service,
00036                         __const struct addrinfo *__restrict __req,
00037                         /*@out@*/ struct addrinfo **__restrict __pai)
00038         /*@modifies *__pai @*/;
00039 
00040 extern int getnameinfo (__const struct sockaddr *__restrict __sa,
00041                         socklen_t __salen, /*@out@*/ char *__restrict __host,
00042                         socklen_t __hostlen, /*@out@*/ char *__restrict __serv,
00043                         socklen_t __servlen, unsigned int __flags)
00044         /*@modifies __host, __serv @*/;
00045 
00046 extern void freeaddrinfo (/*@only@*/ struct addrinfo *__ai)
00047         /*@modifies __ai @*/;
00048 /*@=exportheader =incondefs @*/
00049 #else
00050 #include <netdb.h>              /* XXX getaddrinfo et al */
00051 #endif
00052 
00053 #include <netinet/in.h>
00054 #include <arpa/inet.h>          /* XXX for inet_aton and HP-UX */
00055 
00056 #if defined(HAVE_NETINET_IN_SYSTM_H)
00057 # include <sys/types.h>
00058 # include <netinet/in_systm.h>
00059 #endif
00060 
00061 #if defined(WITH_XZ)
00062 #include <lzma.h>
00063 #endif
00064 
00065 #include <rpmiotypes.h>
00066 #include <rpmmacro.h>           /* XXX rpmioAccess needs rpmCleanPath() */
00067 
00068 #include <rpmaug.h>
00069 #include <rpmficl.h>
00070 #include <rpmjs.h>
00071 #include <rpmlua.h>             /* XXX rpmioClean() calls rpmluaFree() */
00072 #include <rpmnix.h>
00073 #include <rpmperl.h>
00074 #include <rpmpython.h>
00075 #include <rpmruby.h>
00076 #include <rpmsql.h>
00077 #include <rpmsquirrel.h>
00078 #include <rpmtcl.h>
00079 
00080 #define _RPMHKP_INTERNAL        /* XXX awol/crl bloom filters */
00081 #include <rpmhkp.h>
00082 
00083 #include <rpmsm.h>
00084 #include <rpmsp.h>
00085 #include <rpmsx.h>
00086 
00087 #if defined(HAVE_LIBIO_H) && defined(_G_IO_IO_FILE_VERSION)
00088 #define _USE_LIBIO      1
00089 #endif
00090 
00091 /* XXX HP-UX w/o -D_XOPEN_SOURCE needs */
00092 #if !defined(HAVE_HERRNO) && (defined(hpux) || defined(__hpux) || defined(__LCLINT__))
00093 /*@unchecked@*/
00094 extern int h_errno;
00095 #endif
00096 
00097 #ifndef IPPORT_FTP
00098 #define IPPORT_FTP      21
00099 #endif
00100 #ifndef IPPORT_HTTP
00101 #define IPPORT_HTTP     80
00102 #endif
00103 
00104 #if !defined(HAVE_INET_ATON)
00105 #define inet_aton(cp,inp) rpm_inet_aton(cp,inp)
00106 static int rpm_inet_aton(const char *cp, struct in_addr *inp)
00107         /*@modifies *inp @*/
00108 {
00109     long addr;
00110 
00111     addr = inet_addr(cp);
00112     if (addr == ((long) -1)) return 0;
00113 
00114     memcpy(inp, &addr, sizeof(addr));
00115     return 1;
00116 }
00117 #endif
00118 
00119 #if defined(USE_ALT_DNS) && USE_ALT_DNS
00120 #include "dns.h"
00121 #endif
00122 
00123 #include <rpmio_internal.h>
00124 #undef  fdFileno
00125 #undef  fdOpen
00126 #define fdOpen  __fdOpen
00127 #undef  fdRead
00128 #define fdRead  __fdRead
00129 #undef  fdWrite
00130 #define fdWrite __fdWrite
00131 #undef  fdClose
00132 #define fdClose __fdClose
00133 
00134 #include <ugid.h>
00135 #include <rpmcb.h>
00136 #include <rpmdav.h>
00137 
00138 #include "debug.h"
00139 
00140 /*@access FILE @*/      /* XXX to permit comparison/conversion with void *. */
00141 /*@access urlinfo @*/
00142 /*@access FDSTAT_t @*/
00143 /*@access rpmxar @*/
00144 /*@access pgpDig @*/
00145 
00146 #define FDTO(fd)        (fd ? ((FD_t)fd)->rd_timeoutsecs : -99)
00147 #define FDCPIOPOS(fd)   (fd ? ((FD_t)fd)->fd_cpioPos : -99)
00148 
00149 #define FDONLY(fd)      assert(fdGetIo(fd) == fdio)
00150 
00151 #define UFDONLY(fd)     /* assert(fdGetIo(fd) == ufdio) */
00152 
00153 #define fdGetFILE(_fd)  ((FILE *)fdGetFp(_fd))
00154 
00157 /*@unchecked@*/
00158 #if _USE_LIBIO
00159 int noLibio = 0;
00160 #else
00161 int noLibio = 1;
00162 #endif
00163 
00164 #define TIMEOUT_SECS 60
00165 
00168 /*@unchecked@*/
00169 static int ftpTimeoutSecs = TIMEOUT_SECS;
00170 
00173 /*@unchecked@*/
00174 int _rpmio_debug = 0;
00175 
00178 /*@unchecked@*/
00179 int _av_debug = 0;
00180 
00183 /*@unchecked@*/
00184 int _ftp_debug = 0;
00185 
00188 /*@unchecked@*/
00189 int _dav_debug = 0;
00190 
00191 /* =============================================================== */
00192 
00193 const char * fdbg(FD_t fd)
00194 {
00195     static char buf[BUFSIZ];
00196     char *be = buf;
00197     int i;
00198 
00199     buf[0] = '\0';
00200     if (fd == NULL)
00201         return buf;
00202 
00203 #ifdef DYING
00204     sprintf(be, "fd %p", fd);   be += strlen(be);
00205     if (fd->rd_timeoutsecs >= 0) {
00206         sprintf(be, " secs %d", fd->rd_timeoutsecs);
00207         be += strlen(be);
00208     }
00209 #endif
00210     if (fd->bytesRemain != -1) {
00211         sprintf(be, " clen %d", (int)fd->bytesRemain);
00212         be += strlen(be);
00213     }
00214     if (fd->wr_chunked) {
00215         strcpy(be, " chunked");
00216         be += strlen(be);
00217     }
00218     *be++ = '\t';
00219     for (i = fd->nfps; i >= 0; i--) {
00220         FDSTACK_t * fps = &fd->fps[i];
00221         if (i != fd->nfps)
00222             *be++ = ' ';
00223         *be++ = '|';
00224         *be++ = ' ';
00225         if (fps->io == fdio) {
00226             sprintf(be, "FD %d fp %p", fps->fdno, fps->fp);
00227         } else if (fps->io == ufdio) {
00228             sprintf(be, "UFD %d fp %p", fps->fdno, fps->fp);
00229 #if defined(WITH_ZLIB)
00230         } else if (fps->io == gzdio) {
00231             sprintf(be, "GZD %p fdno %d", fps->fp, fps->fdno);
00232 #endif
00233 #if defined(WITH_BZIP2)
00234         } else if (fps->io == bzdio) {
00235             sprintf(be, "BZD %p fdno %d", fps->fp, fps->fdno);
00236 #endif
00237 #if defined(WITH_XZ)
00238         } else if (fps->io == lzdio) {
00239             sprintf(be, "LZD %p fdno %d", fps->fp, fps->fdno);
00240         } else if (fps->io == xzdio) {
00241             sprintf(be, "XZD %p fdno %d", fps->fp, fps->fdno);
00242 #endif
00243         } else if (fps->io == fpio) {
00244             /*@+voidabstract@*/
00245             sprintf(be, "%s %p(%d) fdno %d",
00246                 (fps->fdno < 0 ? "LIBIO" : "FP"),
00247                 fps->fp, fileno(((FILE *)fps->fp)), fps->fdno);
00248             /*@=voidabstract@*/
00249         } else {
00250             sprintf(be, "??? io %p fp %p fdno %d ???",
00251                 fps->io, fps->fp, fps->fdno);
00252         }
00253         be += strlen(be);
00254         *be = '\0';
00255     }
00256     return buf;
00257 }
00258 
00259 /* =============================================================== */
00260 FD_t fdDup(int fdno)
00261 {
00262     FD_t fd;
00263     int nfdno;
00264 
00265     if ((nfdno = dup(fdno)) < 0)
00266         return NULL;
00267     if (fcntl(nfdno, F_SETFD, FD_CLOEXEC)) {
00268         (void) close(nfdno);
00269         return NULL;
00270     }
00271     fd = fdNew("open (fdDup)");
00272     fdSetOpen(fd, "fdDup", nfdno, 0);   /* XXX bogus */
00273     fdSetFdno(fd, nfdno);
00274 DBGIO(fd, (stderr, "<-- fdDup(%d) fd %p %s\n", fdno, (fd ? fd : NULL), fdbg(fd)));
00275     /*@-refcounttrans@*/ return fd; /*@=refcounttrans@*/
00276 }
00277 
00278 static inline /*@unused@*/
00279 int fdSeekNot(void * cookie,
00280                 /*@unused@*/ _libio_pos_t pos,
00281                 /*@unused@*/ int whence)
00282         /*@*/
00283 {
00284     FD_t fd = c2f(cookie);
00285     FDSANE(fd);         /* XXX keep gcc quiet */
00286     return -2;
00287 }
00288 
00289 /* =============================================================== */
00290 
00291 static void fdFini(void * _fd)
00292         /*@globals fileSystem @*/
00293         /*@modifies _fd, fileSystem @*/
00294 {
00295     FD_t fd = _fd;
00296     int i;
00297 
00298 assert(fd != NULL);
00299     fd->opath = _free(fd->opath);
00300     fd->stats = _free(fd->stats);
00301     if (fd->ndigests > 0)
00302     for (i = fd->ndigests - 1; i >= 0; i--) {
00303         DIGEST_CTX ctx = fd->digests[i];
00304         if (ctx == NULL)
00305             continue;
00306         (void) rpmDigestFinal(ctx, NULL, NULL, 0);
00307         fd->digests[i] = NULL;
00308     }
00309     fd->digests = _free(fd->digests);
00310     fd->ndigests = 0;
00311     fd->contentType = _free(fd->contentType);
00312     fd->contentDisposition = _free(fd->contentDisposition);
00313 /*@-onlytrans@*/
00314 #ifdef WITH_XAR
00315     fd->xar = rpmxarFree(fd->xar, "fdFini");
00316 #endif
00317 #ifdef WITH_NEON
00318 #ifndef NOTYET
00319 if (fd->req != NULL)
00320 fprintf(stderr, "*** %s: fd->req %p\n", __FUNCTION__, fd->req);
00321 fd->req = NULL;
00322 #else
00323 assert(fd->req == NULL);
00324 #endif
00325 #endif
00326     fd->dig = pgpDigFree(fd->dig);
00327 /*@=onlytrans@*/
00328 }
00329 
00330 /*@unchecked@*/ /*@only@*/ /*@null@*/
00331 rpmioPool _fdPool;
00332 
00333 static FD_t fdGetPool(/*@null@*/ rpmioPool pool)
00334         /*@globals _fdPool, fileSystem @*/
00335         /*@modifies pool, _fdPool, fileSystem @*/
00336 {
00337     FD_t fd;
00338 
00339     if (_fdPool == NULL) {
00340         _fdPool = rpmioNewPool("fd", sizeof(*fd), -1, _rpmio_debug,
00341                 (char * (*)(void *))fdbg, NULL, fdFini);
00342         pool = _fdPool;
00343     }
00344     return (FD_t) rpmioGetPool(pool, sizeof(*fd));
00345 }
00346 
00347 /*@-incondefs@*/
00348 /*@null@*/
00349 FD_t XfdNew(const char * msg, const char * fn, unsigned ln)
00350 {
00351     FD_t fd = fdGetPool(_fdPool);
00352     if (fd == NULL) /* XXX xmalloc never returns NULL */
00353         return NULL;
00354     fd->flags = 0;
00355     fd->magic = FDMAGIC;
00356 
00357     fd->nfps = 0;
00358     memset(fd->fps, 0, sizeof(fd->fps));
00359 
00360     fd->fps[0].io = ufdio;
00361     fd->fps[0].fp = NULL;
00362     fd->fps[0].fdno = -1;
00363 
00364     fd->u = NULL;
00365     fd->req = NULL;
00366     fd->rd_timeoutsecs = 1;     /* XXX default value used to be -1 */
00367     fd->bytesRemain = -1;
00368     fd->contentLength = -1;
00369     fd->persist = 0;
00370     fd->wr_chunked = 0;
00371 
00372     fd->syserrno = 0;
00373     fd->errcookie = NULL;
00374 
00375     fd->opath = NULL;
00376     fd->oflags = 0;
00377     fd->omode = 0;
00378 
00379     fd->xar = NULL;
00380     fd->dig = NULL;
00381     fd->stats = xcalloc(1, sizeof(*fd->stats));
00382     fd->ndigests = 0;
00383     fd->digests = NULL;
00384 
00385     fd->contentType = NULL;
00386     fd->contentDisposition = NULL;
00387     fd->lastModified = 0;
00388     fd->ftpFileDoneNeeded = 0;
00389     fd->fd_cpioPos = 0;
00390 
00391     return (FD_t)rpmioLinkPoolItem((rpmioItem)fd, msg, fn, ln);
00392 }
00393 /*@=incondefs@*/
00394 
00395 static ssize_t fdRead(void * cookie, /*@out@*/ char * buf, size_t count)
00396         /*@globals errno, fileSystem, internalState @*/
00397         /*@modifies buf, errno, fileSystem, internalState @*/
00398         /*@requires maxSet(buf) >= (count - 1) @*/
00399 {
00400     FD_t fd = c2f(cookie);
00401     ssize_t rc;
00402 
00403     if (fd->bytesRemain == 0) return 0; /* XXX simulate EOF */
00404 
00405     fdstat_enter(fd, FDSTAT_READ);
00406     /* HACK: flimsy wiring for davRead */
00407     if (fd->req != NULL) {
00408 #ifdef WITH_NEON
00409         if (fd->req != (void *)-1)
00410             rc = davRead(fd, buf, (count > (size_t)fd->bytesRemain ? (size_t)fd->bytesRemain : count));
00411         else
00412             rc = -1;
00413 #else
00414         rc = -1;
00415 #endif
00416         /* XXX Chunked davRead EOF. */
00417         if (rc == 0)
00418             fd->bytesRemain = 0;
00419     } else
00420     if (fd->xar != NULL) {
00421 #ifdef WITH_XAR
00422         rc = xarRead(fd, buf, (count > (size_t)fd->bytesRemain ? (size_t)fd->bytesRemain : count));
00423 #else
00424         rc = -1;
00425 #endif
00426     } else
00427         rc = read(fdFileno(fd), buf, (count > (size_t)fd->bytesRemain ? (size_t)fd->bytesRemain : count));
00428     fdstat_exit(fd, FDSTAT_READ, rc);
00429 
00430     if (fd->ndigests > 0 && rc > 0) fdUpdateDigests(fd, (void *)buf, rc);
00431 
00432 DBGIO(fd, (stderr, "<--\tfdRead(%p,%p,%ld) rc %ld %s\n", cookie, buf, (long)count, (long)rc, fdbg(fd)));
00433 
00434     return rc;
00435 }
00436 
00437 static ssize_t fdWrite(void * cookie, const char * buf, size_t count)
00438         /*@globals errno, fileSystem, internalState @*/
00439         /*@modifies errno, fileSystem, internalState @*/
00440 {
00441     FD_t fd = c2f(cookie);
00442     int fdno = fdFileno(fd);
00443     ssize_t rc;
00444 
00445     if (fd->bytesRemain == 0) return 0; /* XXX simulate EOF */
00446 
00447     if (fd->ndigests > 0 && count > 0) fdUpdateDigests(fd, (void *)buf, count);
00448 
00449     if (count == 0) return 0;
00450 
00451     fdstat_enter(fd, FDSTAT_WRITE);
00452     /* HACK: flimsy wiring for davWrite */
00453     if (fd->req != NULL)
00454 #ifdef WITH_NEON
00455         if (fd->req != (void *)-1)
00456             rc = davWrite(fd, buf, (count > (size_t)fd->bytesRemain ? (size_t)fd->bytesRemain : count));
00457         else
00458             rc = -1;
00459 #else
00460         rc = -1;
00461 #endif
00462     else
00463         rc = write(fdno, buf, (count > (size_t)fd->bytesRemain ? (size_t)fd->bytesRemain : count));
00464     fdstat_exit(fd, FDSTAT_WRITE, rc);
00465 
00466 DBGIO(fd, (stderr, "<--\tfdWrite(%p,%p,%ld) rc %ld %s\n", cookie, buf, (long)count, (long)rc, fdbg(fd)));
00467 
00468     return rc;
00469 }
00470 
00471 static int fdSeek(void * cookie, _libio_pos_t pos, int whence)
00472         /*@globals fileSystem, internalState @*/
00473         /*@modifies fileSystem, internalState @*/
00474 {
00475 #ifdef USE_COOKIE_SEEK_POINTER
00476     _IO_off64_t p = *pos;
00477 #else
00478     off_t p = pos;
00479 #endif
00480     FD_t fd = c2f(cookie);
00481     off_t rc;
00482 
00483     assert(fd->bytesRemain == -1);      /* XXX FIXME fadio only for now */
00484     fdstat_enter(fd, FDSTAT_SEEK);
00485     rc = lseek(fdFileno(fd), p, whence);
00486     fdstat_exit(fd, FDSTAT_SEEK, rc);
00487 
00488 DBGIO(fd, (stderr, "<--\tfdSeek(%p,%ld,%d) rc %lx %s\n", cookie, (long)p, whence, (unsigned long)rc, fdbg(fd)));
00489 
00490     return (int) rc;
00491 }
00492 
00493 static int fdClose( /*@only@*/ void * cookie)
00494         /*@globals errno, fileSystem, systemState, internalState @*/
00495         /*@modifies errno, fileSystem, systemState, internalState @*/
00496 {
00497     FD_t fd;
00498     int fdno;
00499     int rc;
00500 
00501     if (cookie == NULL) return -2;
00502     fd = c2f(cookie);
00503     fdno = fdFileno(fd);
00504 
00505     fdSetFdno(fd, -1);
00506 
00507     fdstat_enter(fd, FDSTAT_CLOSE);
00508     /* HACK: flimsy wiring for davClose */
00509     if (fd->req != NULL)
00510 #ifdef WITH_NEON
00511         rc = davClose(fd);
00512 #else
00513         rc = -1;
00514 #endif
00515     else
00516         rc = ((fdno >= 0) ? close(fdno) : -2);
00517     fdstat_exit(fd, FDSTAT_CLOSE, rc);
00518 
00519 DBGIO(fd, (stderr, "<--\tfdClose(%p) rc %lx %s\n", (fd ? fd : NULL), (unsigned long)rc, fdbg(fd)));
00520 
00521     fd = fdFree(fd, "open (fdClose)");
00522     return rc;
00523 }
00524 
00525 static /*@null@*/ FD_t fdOpen(const char *path, int flags, mode_t mode)
00526         /*@globals errno, fileSystem, internalState @*/
00527         /*@modifies errno, fileSystem, internalState @*/
00528 {
00529     FD_t fd;
00530     int fdno;
00531 
00532     fdno = open(path, flags, mode);
00533     if (fdno < 0) return NULL;
00534     if (fcntl(fdno, F_SETFD, FD_CLOEXEC)) {
00535         (void) close(fdno);
00536         return NULL;
00537     }
00538     fd = fdNew("open (fdOpen)");
00539     fdSetOpen(fd, path, flags, mode);
00540     fdSetFdno(fd, fdno);
00541 assert(fd != NULL);
00542     fd->flags = flags;
00543 DBGIO(fd, (stderr, "<--\tfdOpen(\"%s\",%x,0%o) %s\n", path, (unsigned)flags, (unsigned)mode, fdbg(fd)));
00544     /*@-refcounttrans@*/ return fd; /*@=refcounttrans@*/
00545 }
00546 
00547 #ifdef NOTUSED
00548 FILE *fdFdopen(void * cookie, const char *fmode)
00549 {
00550     FD_t fd = c2f(cookie);
00551     int fdno;
00552     FILE * fp;
00553 
00554     if (fmode == NULL) return NULL;
00555     fdno = fdFileno(fd);
00556     if (fdno < 0) return NULL;
00557     fp = fdopen(fdno, fmode);
00558 DBGIO(fd, (stderr, "<-- fdFdopen(%p,\"%s\") fdno %d -> fp %p fdno %d\n", cookie, fmode, fdno, fp, fileno(fp)));
00559     fd = fdFree(fd, "open (fdFdopen)");
00560     return fp;
00561 }
00562 #endif
00563 
00564 /*@-type@*/ /* LCL: function typedefs */
00565 static struct FDIO_s fdio_s = {
00566   fdRead, fdWrite, fdSeek, fdClose, NULL, NULL, NULL,
00567 };
00568 /*@=type@*/
00569 
00570 FDIO_t fdio = /*@-compmempass@*/ &fdio_s /*@=compmempass@*/ ;
00571 
00572 int fdWritable(FD_t fd, int secs)
00573 {
00574     int fdno;
00575     int rc;
00576 #if defined(HAVE_POLL_H)
00577     int msecs = (secs >= 0 ? (1000 * secs) : -1);
00578     struct pollfd wrfds;
00579 #else
00580     struct timeval timeout, *tvp = (secs >= 0 ? &timeout : NULL);
00581     fd_set wrfds;
00582     FD_ZERO(&wrfds);
00583 #endif
00584         
00585     /* HACK: flimsy wiring for davWrite */
00586     if (fd->req != NULL)
00587         return (fd->req == (void *)-1 ? -1 : 1);
00588 
00589     if ((fdno = fdFileno(fd)) < 0)
00590         return -1;      /* XXX W2DO? */
00591         
00592     do {
00593 #if defined(HAVE_POLL_H)
00594         wrfds.fd = fdno;
00595         wrfds.events = POLLOUT;
00596         wrfds.revents = 0;
00597         rc = poll(&wrfds, 1, msecs);
00598 #else
00599         if (tvp) {
00600             tvp->tv_sec = secs;
00601             tvp->tv_usec = 0;
00602         }
00603         FD_SET(fdno, &wrfds);
00604 /*@-compdef -nullpass@*/
00605         rc = select(fdno + 1, NULL, &wrfds, NULL, tvp);
00606 /*@=compdef =nullpass@*/
00607 #endif
00608 
00609         /* HACK: EBADF on PUT chunked termination from ufdClose. */
00610 if (_rpmio_debug && !(rc == 1 && errno == 0))
00611 fprintf(stderr, "*** fdWritable fdno %d rc %d %s\n", fdno, rc, strerror(errno));
00612         if (rc < 0) {
00613             switch (errno) {
00614             case EINTR:
00615                 continue;
00616                 /*@notreached@*/ /*@switchbreak@*/ break;
00617             default:
00618                 return rc;
00619                 /*@notreached@*/ /*@switchbreak@*/ break;
00620             }
00621         }
00622         return rc;
00623     } while (1);
00624     /*@notreached@*/
00625 }
00626 
00627 int fdReadable(FD_t fd, int secs)
00628 {
00629     int fdno;
00630     int rc;
00631 #if defined(HAVE_POLL_H)
00632     int msecs = (secs >= 0 ? (1000 * secs) : -1);
00633     struct pollfd rdfds;
00634 #else
00635     struct timeval timeout, *tvp = (secs >= 0 ? &timeout : NULL);
00636     fd_set rdfds;
00637     FD_ZERO(&rdfds);
00638 #endif
00639 
00640     /* HACK: flimsy wiring for davRead */
00641     if (fd->req != NULL)
00642         return (fd->req == (void *)-1 ? -1 : 1);
00643 
00644     if ((fdno = fdFileno(fd)) < 0)
00645         return -1;      /* XXX W2DO? */
00646         
00647     do {
00648 #if defined(HAVE_POLL_H)
00649         rdfds.fd = fdno;
00650         rdfds.events = POLLIN;
00651         rdfds.revents = 0;
00652         rc = poll(&rdfds, 1, msecs);
00653 #else
00654         if (tvp) {
00655             tvp->tv_sec = secs;
00656             tvp->tv_usec = 0;
00657         }
00658         FD_SET(fdno, &rdfds);
00659         /*@-compdef -nullpass@*/
00660         rc = select(fdno + 1, &rdfds, NULL, NULL, tvp);
00661         /*@=compdef =nullpass@*/
00662 #endif
00663 
00664         if (rc < 0) {
00665             switch (errno) {
00666             case EINTR:
00667                 continue;
00668                 /*@notreached@*/ /*@switchbreak@*/ break;
00669             default:
00670                 return rc;
00671                 /*@notreached@*/ /*@switchbreak@*/ break;
00672             }
00673         }
00674         return rc;
00675     } while (1);
00676     /*@notreached@*/
00677 }
00678 
00679 int fdFgets(FD_t fd, char * buf, size_t len)
00680 {
00681     int fdno;
00682     int secs = fd->rd_timeoutsecs;
00683     size_t nb = 0;
00684     int ec = 0;
00685     char lastchar = '\0';
00686 
00687     if ((fdno = fdFileno(fd)) < 0)
00688         return 0;       /* XXX W2DO? */
00689         
00690     do {
00691         int rc;
00692 
00693         /* Is there data to read? */
00694         rc = fdReadable(fd, secs);
00695 
00696         switch (rc) {
00697         case -1:        /* error */
00698             ec = -1;
00699             continue;
00700             /*@notreached@*/ /*@switchbreak@*/ break;
00701         case  0:        /* timeout */
00702             ec = -1;
00703             continue;
00704             /*@notreached@*/ /*@switchbreak@*/ break;
00705         default:        /* data to read */
00706             /*@switchbreak@*/ break;
00707         }
00708 
00709         errno = 0;
00710 #ifdef  NOISY
00711         rc = fdRead(fd, buf + nb, 1);
00712 #else
00713         rc = (int)read(fdFileno(fd), buf + nb, 1);
00714 #endif
00715         if (rc < 0) {
00716             fd->syserrno = errno;
00717             switch (errno) {
00718             case EWOULDBLOCK:
00719                 continue;
00720                 /*@notreached@*/ /*@switchbreak@*/ break;
00721             default:
00722                 /*@switchbreak@*/ break;
00723             }
00724 if (_rpmio_debug)
00725 fprintf(stderr, "*** read: fd %p rc %d errno %d %s \"%s\"\n", fd, rc, errno, strerror(errno), buf);
00726             ec = -1;
00727             break;
00728         } else if (rc == 0) {
00729 if (_rpmio_debug)
00730 fprintf(stderr, "*** read: fd %p rc %d EOF errno %d %s \"%s\"\n", fd, rc, errno, strerror(errno), buf);
00731             break;
00732         } else {
00733             nb += rc;
00734             buf[nb] = '\0';
00735             lastchar = buf[nb - 1];
00736         }
00737     } while (ec == 0 && nb < len && lastchar != '\n');
00738 
00739     return (ec >= 0 ? (int)nb : ec);
00740 }
00741 
00742 /* =============================================================== */
00743 /* Support for FTP/HTTP I/O.
00744  */
00745 const char * ftpStrerror(int errorNumber)
00746 {
00747     switch (errorNumber) {
00748     case 0:
00749         return _("Success");
00750 
00751     /* HACK error impediance match, coalesce and rename. */
00752     case FTPERR_NE_ERROR:
00753         return ("NE_ERROR: Generic error.");
00754     case FTPERR_NE_LOOKUP:
00755         return ("NE_LOOKUP: Hostname lookup failed.");
00756     case FTPERR_NE_AUTH:
00757         return ("NE_AUTH: Server authentication failed.");
00758     case FTPERR_NE_PROXYAUTH:
00759         return ("NE_PROXYAUTH: Proxy authentication failed.");
00760     case FTPERR_NE_CONNECT:
00761         return ("NE_CONNECT: Could not connect to server.");
00762     case FTPERR_NE_TIMEOUT:
00763         return ("NE_TIMEOUT: Connection timed out.");
00764     case FTPERR_NE_FAILED:
00765         return ("NE_FAILED: The precondition failed.");
00766     case FTPERR_NE_RETRY:
00767         return ("NE_RETRY: Retry request.");
00768     case FTPERR_NE_REDIRECT:
00769         return ("NE_REDIRECT: Redirect received.");
00770 
00771     case FTPERR_BAD_SERVER_RESPONSE:
00772         return _("Bad server response");
00773     case FTPERR_SERVER_IO_ERROR:
00774         return _("Server I/O error");
00775     case FTPERR_SERVER_TIMEOUT:
00776         return _("Server timeout");
00777     case FTPERR_BAD_HOST_ADDR:
00778         return _("Unable to lookup server host address");
00779     case FTPERR_BAD_HOSTNAME:
00780         return _("Unable to lookup server host name");
00781     case FTPERR_FAILED_CONNECT:
00782         return _("Failed to connect to server");
00783     case FTPERR_FAILED_DATA_CONNECT:
00784         return _("Failed to establish data connection to server");
00785     case FTPERR_FILE_IO_ERROR:
00786         return _("I/O error to local file");
00787     case FTPERR_PASSIVE_ERROR:
00788         return _("Error setting remote server to passive mode");
00789     case FTPERR_FILE_NOT_FOUND:
00790         return _("File not found on server");
00791     case FTPERR_NIC_ABORT_IN_PROGRESS:
00792         return _("Abort in progress");
00793 
00794     case FTPERR_UNKNOWN:
00795     default:
00796         return _("Unknown or unexpected error");
00797     }
00798 }
00799 
00800 const char *urlStrerror(const char *url)
00801 {
00802     const char *retstr;
00803     switch (urlIsURL(url)) {
00804     case URL_IS_HTTPS:
00805     case URL_IS_HTTP:
00806     case URL_IS_HKP:
00807     case URL_IS_FTP:
00808     {   urlinfo u;
00809 /* XXX This only works for httpReq/ftpLogin/ftpReq failures */
00810         if (urlSplit(url, &u) == 0)
00811             retstr = ftpStrerror(u->openError);
00812         else
00813             retstr = _("Malformed URL");
00814     }   break;
00815     default:
00816         retstr = strerror(errno);
00817         break;
00818     }
00819     return retstr;
00820 }
00821 
00822 #if !defined(HAVE_GETADDRINFO)
00823 #if !defined(USE_ALT_DNS) || !USE_ALT_DNS
00824 static int mygethostbyname(const char * host,
00825                 /*@out@*/ struct in_addr * address)
00826         /*@globals h_errno @*/
00827         /*@modifies *address @*/
00828 {
00829     struct hostent * hostinfo;
00830 
00831     /*@-multithreaded @*/
00832     hostinfo = gethostbyname(host);
00833     /*@=multithreaded @*/
00834     if (!hostinfo) return 1;
00835 
00836     memcpy(address, hostinfo->h_addr_list[0], sizeof(*address));
00837     return 0;
00838 }
00839 #endif
00840 
00841 /*@-compdef@*/  /* FIX: address->s_addr undefined. */
00842 static int getHostAddress(const char * host, /*@out@*/ struct in_addr * address)
00843         /*@globals errno, h_errno @*/
00844         /*@modifies *address, errno @*/
00845 {
00846 #if 0   /* XXX workaround nss_foo module hand-off using valgrind. */
00847     if (!strcmp(host, "localhost")) {
00848         /*@-moduncon @*/
00849         if (!inet_aton("127.0.0.1", address))
00850             return FTPERR_BAD_HOST_ADDR;
00851         /*@=moduncon @*/
00852     } else
00853 #endif
00854     if (xisdigit(host[0])) {
00855         /*@-moduncon @*/
00856         if (!inet_aton(host, address))
00857             return FTPERR_BAD_HOST_ADDR;
00858         /*@=moduncon @*/
00859     } else {
00860         if (mygethostbyname(host, address)) {
00861             errno = h_errno;
00862             return FTPERR_BAD_HOSTNAME;
00863         }
00864     }
00865 
00866     return 0;
00867 }
00868 /*@=compdef@*/
00869 #endif  /* HAVE_GETADDRINFO */
00870 
00871 static int tcpConnect(FD_t ctrl, const char * host, int port)
00872         /*@globals fileSystem, internalState @*/
00873         /*@modifies ctrl, fileSystem, internalState @*/
00874 {
00875     int fdno = -1;
00876     int rc;
00877 #ifdef  HAVE_GETADDRINFO
00878 /*@-unrecog@*/
00879     struct addrinfo hints, *res, *res0;
00880 #ifndef NI_MAXSERV
00881 #define NI_MAXSERV      32
00882 #endif
00883     char pbuf[NI_MAXSERV];
00884     int xx;
00885 
00886     memset(&hints, 0, sizeof(hints));
00887     hints.ai_family = AF_UNSPEC;
00888     hints.ai_socktype = SOCK_STREAM;
00889     sprintf(pbuf, "%d", port);
00890     pbuf[sizeof(pbuf)-1] = '\0';
00891     rc = FTPERR_FAILED_CONNECT;
00892     if (getaddrinfo(host, pbuf, &hints, &res0) == 0) {
00893         for (res = res0; res != NULL; res = res->ai_next) {
00894             if ((fdno = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) < 0)
00895                 continue;
00896             if (connect(fdno, res->ai_addr, (int)res->ai_addrlen) < 0) {
00897                 xx = close(fdno);
00898                 continue;
00899             }
00900             /* success */
00901             rc = 0;
00902             if (_ftp_debug) {
00903                 char hbuf[NI_MAXHOST];
00904                 hbuf[0] = '\0';
00905                 xx = getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, sizeof(hbuf),
00906                                 NULL, 0, NI_NUMERICHOST);
00907                 fprintf(stderr,"++ connect [%s]:%d on fdno %d\n",
00908                                 /*@-unrecog@*/ hbuf /*@=unrecog@*/, port, fdno);
00909             }
00910             break;
00911         }
00912         freeaddrinfo(res0);
00913     }
00914     if (rc < 0)
00915         goto errxit;
00916 /*@=unrecog@*/
00917 #else   /* HAVE_GETADDRINFO */
00918     struct sockaddr_in sin;
00919 
00920     memset(&sin, 0, sizeof(sin));
00921     sin.sin_family = AF_INET;
00922     sin.sin_port = htons(port);
00923     sin.sin_addr.s_addr = INADDR_ANY;
00924 
00925   do {
00926     if ((rc = getHostAddress(host, &sin.sin_addr)) < 0)
00927         break;
00928 
00929     if ((fdno = socket(sin.sin_family, SOCK_STREAM, IPPROTO_IP)) < 0) {
00930         rc = FTPERR_FAILED_CONNECT;
00931         break;
00932     }
00933 
00934     /*@-internalglobs@*/
00935     if (connect(fdno, (struct sockaddr *) &sin, sizeof(sin))) {
00936         rc = FTPERR_FAILED_CONNECT;
00937         break;
00938     }
00939     /*@=internalglobs@*/
00940   } while (0);
00941 
00942     if (rc < 0)
00943         goto errxit;
00944 
00945 if (_ftp_debug)
00946 fprintf(stderr,"++ connect %s:%d on fdno %d\n",
00947 /*@-unrecog -moduncon -evalorderuncon @*/
00948 inet_ntoa(sin.sin_addr)
00949 /*@=unrecog =moduncon =evalorderuncon @*/ ,
00950 (int)ntohs(sin.sin_port), fdno);
00951 #endif  /* HAVE_GETADDRINFO */
00952 
00953     fdSetFdno(ctrl, (fdno >= 0 ? fdno : -1));
00954     return 0;
00955 
00956 errxit:
00957     /*@-observertrans@*/
00958     fdSetSyserrno(ctrl, errno, ftpStrerror(rc));
00959     /*@=observertrans@*/
00960     if (fdno >= 0)
00961         (void) close(fdno);
00962     return rc;
00963 }
00964 
00965 static int checkResponse(void * uu, FD_t ctrl,
00966                 /*@out@*/ int *ecp, /*@out@*/ char ** str)
00967         /*@globals fileSystem @*/
00968         /*@modifies ctrl, *ecp, *str, fileSystem @*/
00969 {
00970     urlinfo u = uu;
00971     char *buf;
00972     size_t bufAlloced;
00973     int bufLength = 0;
00974     const char *s;
00975     char *se;
00976     int ec = 0;
00977     int moretodo = 1;
00978     char errorCode[4];
00979 
00980     URLSANE(u);
00981     if (u->bufAlloced == 0 || u->buf == NULL) {
00982         u->bufAlloced = _url_iobuf_size;
00983         u->buf = xcalloc(u->bufAlloced, sizeof(u->buf[0]));
00984     }
00985     buf = u->buf;
00986     bufAlloced = u->bufAlloced;
00987     *buf = '\0';
00988 
00989     errorCode[0] = '\0';
00990 
00991     do {
00992         int rc;
00993 
00994         /*
00995          * Read next line from server.
00996          */
00997         se = buf + bufLength;
00998         *se = '\0';
00999         rc = fdFgets(ctrl, se, (bufAlloced - bufLength));
01000         if (rc < 0) {
01001             ec = FTPERR_BAD_SERVER_RESPONSE;
01002             continue;
01003         } else if (rc == 0 || fdWritable(ctrl, 0) < 1)
01004             moretodo = 0;
01005 
01006         /*
01007          * Process next line from server.
01008          */
01009         for (s = se; *s != '\0'; s = se) {
01010                 const char *e;
01011 
01012                 while (*se && *se != '\n') se++;
01013 
01014                 if (se > s && se[-1] == '\r')
01015                    se[-1] = '\0';
01016                 if (*se == '\0')
01017                     /*@innerbreak@*/ break;
01018 
01019 if (_ftp_debug)
01020 fprintf(stderr, "<- %s\n", s);
01021 
01022                 /* HTTP: header termination on empty line */
01023                 if (*s == '\0') {
01024                     moretodo = 0;
01025                     /*@innerbreak@*/ break;
01026                 }
01027                 *se++ = '\0';
01028 
01029                 /* HTTP: look for "HTTP/1.1 123 ..." */
01030                 if (!strncmp(s, "HTTP", sizeof("HTTP")-1)) {
01031                     ctrl->contentLength = -1;
01032                     if ((e = strchr(s, '.')) != NULL) {
01033                         e++;
01034                         u->httpVersion = (int)(*e - '0');
01035                         if (u->httpVersion < 1 || u->httpVersion > 2)
01036                             ctrl->persist = u->httpVersion = 0;
01037                         else
01038                             ctrl->persist = 1;
01039                     }
01040                     if ((e = strchr(s, ' ')) != NULL) {
01041                         e++;
01042                         if (strchr("0123456789", *e))
01043                             strncpy(errorCode, e, 3);
01044                         errorCode[3] = '\0';
01045                     }
01046                     /*@innercontinue@*/ continue;
01047                 }
01048 
01049                 /* HTTP: look for "token: ..." */
01050                 for (e = s; *e && !(*e == ' ' || *e == ':'); e++)
01051                     {};
01052                 if (e > s && *e++ == ':') {
01053                     size_t ne = (e - s);
01054                     while (*e && *e == ' ') e++;
01055 #if 0
01056                     if (!strncmp(s, "Date:", ne)) {
01057                     } else
01058                     if (!strncmp(s, "Server:", ne)) {
01059                     } else
01060                     if (!strncmp(s, "Last-Modified:", ne)) {
01061                     } else
01062                     if (!strncmp(s, "ETag:", ne)) {
01063                     } else
01064 #endif
01065                     if (!strncmp(s, "Accept-Ranges:", ne)) {
01066                         if (!strcmp(e, "bytes"))
01067                             u->allow |= RPMURL_SERVER_HASRANGE;
01068                         if (!strcmp(e, "none"))
01069                             u->allow &= ~RPMURL_SERVER_HASRANGE;
01070                     } else
01071                     if (!strncmp(s, "Content-Length:", ne)) {
01072                         if (strchr("0123456789", *e))
01073                             ctrl->contentLength = atol(e);
01074                     } else
01075                     if (!strncmp(s, "Connection:", ne)) {
01076                         if (!strcmp(e, "close"))
01077                             ctrl->persist = 0;
01078                     }
01079 #if 0
01080                     else
01081                     if (!strncmp(s, "Content-Type:", ne)) {
01082                     } else
01083                     if (!strncmp(s, "Transfer-Encoding:", ne)) {
01084                         if (!strcmp(e, "chunked"))
01085                             ctrl->wr_chunked = 1;
01086                         else
01087                             ctrl->wr_chunked = 0;
01088                     } else
01089                     if (!strncmp(s, "Allow:", ne)) {
01090                     }
01091 #endif
01092                     /*@innercontinue@*/ continue;
01093                 }
01094 
01095                 /* HTTP: look for "<TITLE>501 ... </TITLE>" */
01096                 if (!strncmp(s, "<TITLE>", sizeof("<TITLE>")-1))
01097                     s += sizeof("<TITLE>") - 1;
01098 
01099                 /* FTP: look for "123-" and/or "123 " */
01100                 if (strchr("0123456789", *s)) {
01101                     if (errorCode[0] != '\0') {
01102                         if (!strncmp(s, errorCode, sizeof("123")-1) && s[3] == ' ')
01103                             moretodo = 0;
01104                     } else {
01105                         strncpy(errorCode, s, sizeof("123")-1);
01106                         errorCode[3] = '\0';
01107                         if (s[3] != '-')
01108                             moretodo = 0;
01109                     }
01110                 }
01111         }
01112 
01113         if (moretodo && se > s) {
01114             bufLength = se - s - 1;
01115             if (s != buf)
01116                 memmove(buf, s, bufLength);
01117         } else {
01118             bufLength = 0;
01119         }
01120     } while (moretodo && ec == 0);
01121 
01122     if (str)    *str = buf;
01123     if (ecp)    *ecp = atoi(errorCode);
01124 
01125     return ec;
01126 }
01127 
01128 static int ftpCheckResponse(urlinfo u, /*@out@*/ char ** str)
01129         /*@globals fileSystem @*/
01130         /*@modifies u, *str, fileSystem @*/
01131 {
01132     int ec = 0;
01133     int rc;
01134 
01135     URLSANE(u);
01136     rc = checkResponse(u, u->ctrl, &ec, str);
01137 
01138     switch (ec) {
01139     case 550:
01140         return FTPERR_FILE_NOT_FOUND;
01141         /*@notreached@*/ break;
01142     case 552:
01143         return FTPERR_NIC_ABORT_IN_PROGRESS;
01144         /*@notreached@*/ break;
01145     default:
01146         if (ec >= 400 && ec <= 599) {
01147             return FTPERR_BAD_SERVER_RESPONSE;
01148         }
01149         break;
01150     }
01151     return rc;
01152 }
01153 
01154 static int ftpCommand(urlinfo u, char ** str, ...)
01155         /*@globals fileSystem, internalState @*/
01156         /*@modifies u, *str, fileSystem, internalState @*/
01157 {
01158     va_list ap;
01159     int len = 0;
01160     const char * s, * t;
01161     char * te;
01162     int rc;
01163 
01164     URLSANE(u);
01165     va_start(ap, str);
01166     while ((s = va_arg(ap, const char *)) != NULL) {
01167         if (len) len++;
01168         len += strlen(s);
01169     }
01170     len += sizeof("\r\n")-1;
01171     va_end(ap);
01172 
01173     t = te = alloca(len + 1);
01174 
01175     va_start(ap, str);
01176     while ((s = va_arg(ap, const char *)) != NULL) {
01177         if (te > t) *te++ = ' ';
01178         te = stpcpy(te, s);
01179     }
01180     te = stpcpy(te, "\r\n");
01181     va_end(ap);
01182 
01183 if (_ftp_debug)
01184 fprintf(stderr, "-> %s", t);
01185     if (fdWrite(u->ctrl, t, (te-t)) != (te-t))
01186         return FTPERR_SERVER_IO_ERROR;
01187 
01188     rc = ftpCheckResponse(u, str);
01189     return rc;
01190 }
01191 
01192 static int ftpLogin(urlinfo u)
01193         /*@globals fileSystem, internalState @*/
01194         /*@modifies u, fileSystem, internalState @*/
01195 {
01196     const char * host;
01197     const char * user;
01198     const char * password;
01199     int port;
01200     int rc;
01201 
01202     URLSANE(u);
01203     u->ctrl = fdLink(u->ctrl, "open ctrl");
01204 
01205     if (((host = (u->proxyh ? u->proxyh : u->host)) == NULL)) {
01206         rc = FTPERR_BAD_HOSTNAME;
01207         goto errxit;
01208     }
01209 
01210     if ((port = (u->proxyp > 0 ? u->proxyp : u->port)) < 0) port = IPPORT_FTP;
01211 
01212     if ((user = (u->proxyu ? u->proxyu : u->user)) == NULL)
01213         user = "anonymous";
01214 
01215     if ((password = u->password) == NULL) {
01216         uid_t uid = getuid();
01217         struct passwd * pw;
01218         if (uid && (pw = getpwuid(uid)) != NULL) {
01219             char *myp = alloca(strlen(pw->pw_name) + sizeof("@"));
01220             strcpy(myp, pw->pw_name);
01221             strcat(myp, "@");
01222             password = myp;
01223         } else {
01224             password = "root@";
01225         }
01226     }
01227 
01228     if (fdFileno(u->ctrl) >= 0 && fdWritable(u->ctrl, 0) < 1)
01229         /*@-refcounttrans@*/ (void) fdClose(u->ctrl); /*@=refcounttrans@*/
01230 
01231 /*@-usereleased@*/
01232     if (fdFileno(u->ctrl) < 0) {
01233         rc = tcpConnect(u->ctrl, host, port);
01234         if (rc < 0)
01235             goto errxit2;
01236     }
01237 
01238     if ((rc = ftpCheckResponse(u, NULL)))
01239         goto errxit;
01240 
01241     if ((rc = ftpCommand(u, NULL, "USER", user, NULL)))
01242         goto errxit;
01243 
01244     if ((rc = ftpCommand(u, NULL, "PASS", password, NULL)))
01245         goto errxit;
01246 
01247     if ((rc = ftpCommand(u, NULL, "TYPE", "I", NULL)))
01248         goto errxit;
01249 
01250     /*@-compdef@*/
01251     return 0;
01252     /*@=compdef@*/
01253 
01254 errxit:
01255     /*@-observertrans@*/
01256     fdSetSyserrno(u->ctrl, errno, ftpStrerror(rc));
01257     /*@=observertrans@*/
01258 errxit2:
01259     if (fdFileno(u->ctrl) >= 0)
01260         /*@-refcounttrans@*/ (void) fdClose(u->ctrl); /*@=refcounttrans@*/
01261     /*@-compdef@*/
01262     return rc;
01263     /*@=compdef@*/
01264 /*@=usereleased@*/
01265 }
01266 
01267 int ftpReq(FD_t data, const char * ftpCmd, const char * ftpArg)
01268 {
01269     urlinfo u = data->u;
01270 #if !defined(HAVE_GETADDRINFO)
01271     struct sockaddr_in dataAddress;
01272 #endif  /* HAVE_GETADDRINFO */
01273     char remoteIP[NI_MAXHOST];
01274     char * cmd;
01275     size_t cmdlen;
01276     char * passReply;
01277     char * chptr;
01278     int rc;
01279     int epsv;
01280     int port;
01281 
01282     remoteIP[0] = '\0';
01283     URLSANE(u);
01284     if (ftpCmd == NULL)
01285         return FTPERR_UNKNOWN;  /* XXX W2DO? */
01286 
01287     cmdlen = strlen(ftpCmd) + (ftpArg ? 1+strlen(ftpArg) : 0) + sizeof("\r\n");
01288     chptr = cmd = alloca(cmdlen);
01289     chptr = stpcpy(chptr, ftpCmd);
01290     if (ftpArg) {
01291         *chptr++ = ' ';
01292         chptr = stpcpy(chptr, ftpArg);
01293     }
01294     chptr = stpcpy(chptr, "\r\n");
01295     cmdlen = chptr - cmd;
01296 
01297 /*
01298  * Get the ftp version of the Content-Length.
01299  */
01300     if (!strncmp(cmd, "RETR", 4)) {
01301         unsigned cl;
01302 
01303         passReply = NULL;
01304         rc = ftpCommand(u, &passReply, "SIZE", ftpArg, NULL);
01305         if (rc)
01306             goto errxit;
01307         if (sscanf(passReply, "%d %u", &rc, &cl) != 2) {
01308             rc = FTPERR_BAD_SERVER_RESPONSE;
01309             goto errxit;
01310         }
01311         rc = 0;
01312         data->contentLength = cl;
01313     }
01314 
01315     epsv = 0;
01316     passReply = NULL;
01317 #ifdef HAVE_GETNAMEINFO
01318     rc = ftpCommand(u, &passReply, "EPSV", NULL);
01319     if (rc == 0) {
01320 #ifdef HAVE_GETADDRINFO
01321         struct sockaddr_storage ss;
01322 #else /* HAVE_GETADDRINFO */
01323         struct sockaddr_in ss;
01324 #endif /* HAVE_GETADDRINFO */
01325         socklen_t sslen = sizeof(ss);
01326 
01327         /* we need to know IP of remote host */
01328         if ((getpeername(fdFileno(c2f(u->ctrl)), (struct sockaddr *)&ss, &sslen) == 0)
01329          && (getnameinfo((struct sockaddr *)&ss, sslen,
01330                         remoteIP, sizeof(remoteIP),
01331                         NULL, 0, NI_NUMERICHOST) == 0))
01332         {
01333                 epsv++;
01334         } else {
01335                 /* abort EPSV and fall back to PASV */
01336                 rc = ftpCommand(u, &passReply, "ABOR", NULL);
01337                 if (rc) {
01338                     rc = FTPERR_PASSIVE_ERROR;
01339                     goto errxit;
01340                 }
01341         }
01342     }
01343    if (epsv == 0)
01344 #endif /* HAVE_GETNAMEINFO */
01345         rc = ftpCommand(u, &passReply, "PASV", NULL);
01346     if (rc) {
01347         rc = FTPERR_PASSIVE_ERROR;
01348         goto errxit;
01349     }
01350 
01351     chptr = passReply;
01352 assert(chptr != NULL);
01353     while (*chptr && *chptr != '(') chptr++;
01354     if (*chptr != '(') return FTPERR_PASSIVE_ERROR;
01355     chptr++;
01356     passReply = chptr;
01357     while (*chptr && *chptr != ')') chptr++;
01358     if (*chptr != ')') return FTPERR_PASSIVE_ERROR;
01359     *chptr-- = '\0';
01360 
01361   if (epsv) {
01362         int i;
01363         if(sscanf(passReply,"%*c%*c%*c%d%*c",&i) != 1) {
01364            rc = FTPERR_PASSIVE_ERROR;
01365            goto errxit;
01366         }
01367         port = i;
01368   } else {
01369 
01370     while (*chptr && *chptr != ',') chptr--;
01371     if (*chptr != ',') return FTPERR_PASSIVE_ERROR;
01372     chptr--;
01373     while (*chptr && *chptr != ',') chptr--;
01374     if (*chptr != ',') return FTPERR_PASSIVE_ERROR;
01375     *chptr++ = '\0';
01376 
01377     /* now passReply points to the IP portion, and chptr points to the
01378        port number portion */
01379 
01380     {   int i, j;
01381         if (sscanf(chptr, "%d,%d", &i, &j) != 2) {
01382             rc = FTPERR_PASSIVE_ERROR;
01383             goto errxit;
01384         }
01385         port = (((unsigned)i) << 8) + j;
01386     }
01387 
01388     chptr = passReply;
01389     while (*chptr++ != '\0') {
01390         if (*chptr == ',') *chptr = '.';
01391     }
01392     sprintf(remoteIP, "%s", passReply);
01393   } /* if (epsv) */
01394 
01395 #ifdef HAVE_GETADDRINFO
01396 /*@-unrecog@*/
01397     {
01398         struct addrinfo hints, *res, *res0;
01399         char pbuf[NI_MAXSERV];
01400         int xx;
01401 
01402         memset(&hints, 0, sizeof(hints));
01403         hints.ai_family = AF_UNSPEC;
01404         hints.ai_socktype = SOCK_STREAM;
01405         hints.ai_flags = AI_NUMERICHOST;
01406 #if defined(AI_IDN)
01407         hints.ai_flags |= AI_IDN;
01408 #endif
01409         sprintf(pbuf, "%d", port);
01410         pbuf[sizeof(pbuf)-1] = '\0';
01411         if (getaddrinfo(remoteIP, pbuf, &hints, &res0)) {
01412             rc = FTPERR_PASSIVE_ERROR;
01413             goto errxit;
01414         }
01415 
01416         for (res = res0; res != NULL; res = res->ai_next) {
01417             rc = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
01418             fdSetFdno(data, (rc >= 0 ? rc : -1));
01419             if (rc < 0) {
01420                 if (res->ai_next)
01421                     continue;
01422                 else {
01423                     rc = FTPERR_FAILED_CONNECT;
01424                     freeaddrinfo(res0);
01425                     goto errxit;
01426                 }
01427             }
01428             data = fdLink(data, "open data (ftpReq)");
01429 
01430             /* XXX setsockopt SO_LINGER */
01431             /* XXX setsockopt SO_KEEPALIVE */
01432             /* XXX setsockopt SO_TOS IPTOS_THROUGHPUT */
01433 
01434             {
01435                 int criterr = 0;
01436                 while (connect(fdFileno(data), res->ai_addr, (int)res->ai_addrlen) < 0) {
01437                     if (errno == EINTR)
01438                         /*@innercontinue@*/ continue;
01439                     criterr++;
01440                 }
01441                 if (criterr) {
01442                     if (res->ai_addr) {
01443 /*@-refcounttrans@*/
01444                         xx = fdClose(data);
01445 /*@=refcounttrans@*/
01446                         continue;
01447                     } else {
01448                         rc = FTPERR_PASSIVE_ERROR;
01449                         freeaddrinfo(res0);
01450                         goto errxit;
01451                     }
01452                 }
01453             }
01454             /* success */
01455             rc = 0;
01456             break;
01457         }
01458         freeaddrinfo(res0);
01459     }
01460 /*@=unrecog@*/
01461 #else /* HAVE_GETADDRINFO */
01462     memset(&dataAddress, 0, sizeof(dataAddress));
01463     dataAddress.sin_family = AF_INET;
01464     dataAddress.sin_port = htons(port);
01465 
01466     /*@-moduncon@*/
01467     if (!inet_aton(remoteIP, &dataAddress.sin_addr)) {
01468         rc = FTPERR_PASSIVE_ERROR;
01469         goto errxit;
01470     }
01471     /*@=moduncon@*/
01472 
01473     rc = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
01474     fdSetFdno(data, (rc >= 0 ? rc : -1));
01475     if (rc < 0) {
01476         rc = FTPERR_FAILED_CONNECT;
01477         goto errxit;
01478     }
01479     data = fdLink(data, "open data (ftpReq)");
01480 
01481     /* XXX setsockopt SO_LINGER */
01482     /* XXX setsockopt SO_KEEPALIVE */
01483     /* XXX setsockopt SO_TOS IPTOS_THROUGHPUT */
01484 
01485     /*@-internalglobs@*/
01486     while (connect(fdFileno(data), (struct sockaddr *) &dataAddress,
01487                 sizeof(dataAddress)) < 0)
01488     {
01489         if (errno == EINTR)
01490             continue;
01491         rc = FTPERR_FAILED_DATA_CONNECT;
01492         goto errxit;
01493     }
01494     /*@=internalglobs@*/
01495 #endif /* HAVE_GETADDRINFO */
01496 
01497 if (_ftp_debug)
01498 fprintf(stderr, "-> %s", cmd);
01499     if ((size_t)fdWrite(u->ctrl, cmd, cmdlen) != cmdlen) {
01500         rc = FTPERR_SERVER_IO_ERROR;
01501         goto errxit;
01502     }
01503 
01504     if ((rc = ftpCheckResponse(u, NULL))) {
01505         goto errxit;
01506     }
01507 
01508     data->ftpFileDoneNeeded = 1;
01509     u->ctrl = fdLink(u->ctrl, "grab data (ftpReq)");
01510     u->ctrl = fdLink(u->ctrl, "open data (ftpReq)");
01511     return 0;
01512 
01513 errxit:
01514     /*@-observertrans@*/
01515     fdSetSyserrno(u->ctrl, errno, ftpStrerror(rc));
01516     /*@=observertrans@*/
01517     if (fdFileno(data) >= 0)
01518         /*@-refcounttrans@*/ (void) fdClose(data); /*@=refcounttrans@*/
01519     return rc;
01520 }
01521 
01522 #ifdef  DYING
01523 /*@unchecked@*/ /*@null@*/
01524 static rpmCallbackFunction      _urlNotify = NULL;
01525 
01526 /*@unchecked@*/ /*@null@*/
01527 static void *                   _urlNotifyData = NULL;
01528 
01529 /*@unchecked@*/
01530 static int                      _urlNotifyCount = -1;
01531 
01532 static void urlSetCallback(rpmCallbackFunction notify, void *notifyData, int notifyCount) {
01533     _urlNotify = notify;
01534     _urlNotifyData = notifyData;
01535     _urlNotifyCount = (notifyCount >= 0) ? notifyCount : 4096;
01536 }
01537 #endif
01538 
01539 int ufdCopy(FD_t sfd, FD_t tfd)
01540 {
01541     char buf[BUFSIZ];
01542     int itemsRead;
01543     int itemsCopied = 0;
01544     int rc = 0;
01545 #ifdef  DYING
01546     int notifier = -1;
01547 
01548     if (_urlNotify) {
01549         /*@-noeffectuncon @*/ /* FIX: check rc */
01550         (void)(*_urlNotify) (NULL, RPMCALLBACK_INST_OPEN_FILE,
01551                 0, 0, NULL, _urlNotifyData);
01552         /*@=noeffectuncon @*/
01553     }
01554 #endif
01555 
01556     while (1) {
01557         rc = (int) Fread(buf, sizeof(buf[0]), sizeof(buf), sfd);
01558         if (rc < 0)     /* XXX never happens Fread returns size_t */
01559             break;
01560         else if (rc == 0) {
01561             rc = itemsCopied;
01562             break;
01563         }
01564         itemsRead = rc;
01565         rc = (int) Fwrite(buf, sizeof(buf[0]), itemsRead, tfd);
01566         if (rc < 0)     /* XXX never happens Fwrite returns size_t */
01567             break;
01568         if (rc != itemsRead) {
01569             rc = FTPERR_FILE_IO_ERROR;
01570             break;
01571         }
01572 
01573         itemsCopied += itemsRead;
01574 #ifdef  DYING
01575         if (_urlNotify && _urlNotifyCount > 0) {
01576             int n = itemsCopied/_urlNotifyCount;
01577             if (n != notifier) {
01578                 /*@-noeffectuncon @*/ /* FIX: check rc */
01579                 (void)(*_urlNotify) (NULL, RPMCALLBACK_INST_PROGRESS,
01580                         itemsCopied, 0, NULL, _urlNotifyData);
01581                 /*@=noeffectuncon @*/
01582                 notifier = n;
01583             }
01584         }
01585 #endif
01586     }
01587 
01588     DBGIO(sfd, (stderr, "++ copied %d bytes: %s\n", itemsCopied,
01589         ftpStrerror(rc)));
01590 
01591 #ifdef  DYING
01592     if (_urlNotify) {
01593         /*@-noeffectuncon @*/ /* FIX: check rc */
01594         (void)(*_urlNotify) (NULL, RPMCALLBACK_INST_OPEN_FILE,
01595                 itemsCopied, itemsCopied, NULL, _urlNotifyData);
01596         /*@=noeffectuncon @*/
01597     }
01598 #endif
01599 
01600     return rc;
01601 }
01602 
01603 static int urlConnect(const char * url, /*@out@*/ urlinfo * uret)
01604         /*@globals h_errno, fileSystem, internalState @*/
01605         /*@modifies *uret, fileSystem, internalState @*/
01606 {
01607     urlinfo u;
01608     int rc = 0;
01609 
01610     if (urlSplit(url, &u) < 0)
01611         return -1;
01612 
01613     if (urlType(u) == URL_IS_FTP) {
01614         FD_t fd;
01615 
01616         if ((fd = u->ctrl) == NULL) {
01617             fd = u->ctrl = fdNew("persist ctrl (urlConnect FTP)");
01618 /*@-usereleased@*/
01619             fdSetOpen(u->ctrl, url, 0, 0);
01620             fdSetIo(u->ctrl, ufdio);
01621 /*@=usereleased@*/
01622         }
01623         
01624 assert(fd != NULL);
01625         fd->rd_timeoutsecs = ftpTimeoutSecs;
01626         fd->contentLength = fd->bytesRemain = -1;
01627         fd->u = NULL;           /* XXX FTP ctrl has not */
01628         fd->ftpFileDoneNeeded = 0;
01629         fd = fdLink(fd, "grab ctrl (urlConnect FTP)");
01630 
01631         if (fdFileno(u->ctrl) < 0) {
01632             rpmlog(RPMLOG_DEBUG, D_("logging into %s as %s, pw %s\n"),
01633                         u->host ? u->host : "???",
01634                         u->user ? u->user : "ftp",
01635                         u->password ? u->password : "(username)");
01636 
01637             if ((rc = ftpLogin(u)) < 0) {       /* XXX save ftpLogin error */
01638                 u->ctrl = fdFree(fd, "grab ctrl (urlConnect FTP)");
01639                 u->openError = rc;
01640             }
01641         }
01642     }
01643 
01644     if (uret != NULL)
01645         *uret = urlLink(u, "urlConnect");
01646     u = urlFree(u, "urlSplit (urlConnect)");    
01647 
01648     return rc;
01649 }
01650 
01651 int ufdGetFile(FD_t sfd, FD_t tfd)
01652 {
01653     int rc;
01654 
01655     FDSANE(sfd);
01656     FDSANE(tfd);
01657     rc = ufdCopy(sfd, tfd);
01658     (void) Fclose(sfd);
01659     if (rc > 0)         /* XXX ufdCopy now returns no. bytes copied */
01660         rc = 0;
01661     return rc;
01662 }
01663 
01664 int ftpCmd(const char * cmd, const char * url, const char * arg2)
01665 {
01666     urlinfo u;
01667     int rc;
01668     const char * path;
01669 
01670     if (urlConnect(url, &u) < 0)
01671         return -1;
01672 
01673     (void) urlPath(url, &path);
01674 
01675     rc = ftpCommand(u, NULL, cmd, path, arg2, NULL);
01676     u->ctrl = fdFree(u->ctrl, "grab ctrl (ftpCmd)");
01677     return rc;
01678 }
01679 
01680 /* XXX these aren't worth the pain of including correctly */
01681 #if !defined(IAC)
01682 #define IAC     ((unsigned char)255)    /* interpret as command: */
01683 #endif
01684 #if !defined(IP)
01685 #define IP      ((unsigned char)244)    /* interrupt process--permanently */
01686 #endif
01687 #if !defined(DM)
01688 #define DM      ((unsigned char)242)    /* data mark--for connect. cleaning */
01689 #endif
01690 #if !defined(SHUT_RDWR)
01691 #define SHUT_RDWR       1+1
01692 #endif
01693 
01694 static int ftpAbort(urlinfo u, FD_t data)
01695         /*@globals fileSystem, internalState @*/
01696         /*@modifies u, data, fileSystem, internalState @*/
01697 {
01698     static unsigned char ipbuf[3] = { IAC, IP, IAC };
01699     FD_t ctrl;
01700     int rc;
01701     int tosecs;
01702 
01703     URLSANE(u);
01704 
01705     if (data != NULL) {
01706         data->ftpFileDoneNeeded = 0;
01707         if (fdFileno(data) >= 0)
01708             u->ctrl = fdFree(u->ctrl, "open data (ftpAbort)");
01709         u->ctrl = fdFree(u->ctrl, "grab data (ftpAbort)");
01710     }
01711     ctrl = u->ctrl;
01712 
01713     DBGIO(0, (stderr, "-> ABOR\n"));
01714 
01715 /*@-usereleased -compdef@*/
01716     if (send(fdFileno(ctrl), ipbuf, sizeof(ipbuf), MSG_OOB) != sizeof(ipbuf)) {
01717         /*@-refcounttrans@*/ (void) fdClose(ctrl); /*@=refcounttrans@*/
01718         return FTPERR_SERVER_IO_ERROR;
01719     }
01720 
01721     sprintf(u->buf, "%cABOR\r\n",(char) DM);
01722     if (fdWrite(ctrl, u->buf, 7) != 7) {
01723         /*@-refcounttrans@*/ (void) fdClose(ctrl); /*@=refcounttrans@*/
01724         return FTPERR_SERVER_IO_ERROR;
01725     }
01726 
01727     if (data && fdFileno(data) >= 0) {
01728         /* XXX shorten data drain time wait */
01729         tosecs = data->rd_timeoutsecs;
01730         data->rd_timeoutsecs = 10;
01731         if (fdReadable(data, data->rd_timeoutsecs) > 0) {
01732 /*@-infloopsuncon@*/
01733             while ((ufdio->read)(data, u->buf, u->bufAlloced) > 0)
01734                 u->buf[0] = '\0';
01735 /*@=infloopsuncon@*/
01736         }
01737         data->rd_timeoutsecs = tosecs;
01738         /* XXX ftp abort needs to close the data channel to receive status */
01739         (void) shutdown(fdFileno(data), SHUT_RDWR);
01740         (void) close(fdFileno(data));
01741         data->fps[0].fdno = -1; /* XXX WRONG but expedient */
01742     }
01743 
01744     /* XXX shorten ctrl drain time wait */
01745 assert(u->ctrl != NULL);
01746     tosecs = u->ctrl->rd_timeoutsecs;
01747     u->ctrl->rd_timeoutsecs = 10;
01748     if ((rc = ftpCheckResponse(u, NULL)) == FTPERR_NIC_ABORT_IN_PROGRESS) {
01749         rc = ftpCheckResponse(u, NULL);
01750     }
01751     rc = ftpCheckResponse(u, NULL);
01752     u->ctrl->rd_timeoutsecs = tosecs;
01753 
01754     return rc;
01755 /*@=usereleased =compdef@*/
01756 }
01757 
01758 static int ftpFileDone(urlinfo u, FD_t data)
01759         /*@globals fileSystem @*/
01760         /*@modifies u, data, fileSystem @*/
01761 {
01762     int rc = 0;
01763 
01764     URLSANE(u);
01765     assert(data->ftpFileDoneNeeded);
01766 
01767     if (data->ftpFileDoneNeeded) {
01768         data->ftpFileDoneNeeded = 0;
01769         u->ctrl = fdFree(u->ctrl, "open data (ftpFileDone)");
01770         u->ctrl = fdFree(u->ctrl, "grab data (ftpFileDone)");
01771         rc = ftpCheckResponse(u, NULL);
01772     }
01773     return rc;
01774 }
01775 
01776 #ifndef WITH_NEON
01777 static int httpResp(urlinfo u, FD_t ctrl, /*@out@*/ char ** str)
01778         /*@globals fileSystem @*/
01779         /*@modifies ctrl, *str, fileSystem @*/
01780 {
01781     int ec = 0;
01782     int rc;
01783 
01784     URLSANE(u);
01785     rc = checkResponse(u, ctrl, &ec, str);
01786 
01787 if (_ftp_debug && !(rc == 0 && (ec == 200 || ec == 201)))
01788 fprintf(stderr, "*** httpResp: rc %d ec %d\n", rc, ec);
01789 
01790     switch (ec) {
01791     case 200:
01792     case 201:                   /* 201 Created. */
01793         break;
01794     case 204:                   /* HACK: if overwriting, 204 No Content. */
01795     case 403:                   /* 403 Forbidden. */
01796         ctrl->syserrno = EACCES;        /* HACK */
01797         rc = FTPERR_UNKNOWN;
01798         break;
01799     default:
01800         rc = FTPERR_FILE_NOT_FOUND;
01801         break;
01802     }
01803     return rc;
01804 }
01805 
01806 static int httpReq(FD_t ctrl, const char * httpCmd, const char * httpArg)
01807         /*@globals h_errno, fileSystem, internalState @*/
01808         /*@modifies ctrl, fileSystem, internalState @*/
01809 {
01810     urlinfo u;
01811     const char * host;
01812     const char * path;
01813     char hthost[NI_MAXHOST];
01814     int port;
01815     int rc;
01816     char * req;
01817     size_t len;
01818     int retrying = 0;
01819 
01820 assert(ctrl != NULL);
01821     u = ctrl->u;
01822     URLSANE(u);
01823 
01824     if (((host = (u->proxyh ? u->proxyh : u->host)) == NULL))
01825         return FTPERR_BAD_HOSTNAME;
01826     if (strchr(host, ':'))
01827         sprintf(hthost, "[%s]", host);
01828     else
01829         strcpy(hthost, host);
01830 
01831     if ((port = (u->proxyp > 0 ? u->proxyp : u->port)) < 0) port = 80;
01832     path = (u->proxyh || u->proxyp > 0) ? u->url : httpArg;
01833     if (path == NULL) path = "";
01834 
01835 reopen:
01836     if (fdFileno(ctrl) >= 0 && (rc = fdWritable(ctrl, 0)) < 1) {
01837         /*@-refcounttrans@*/ (void) fdClose(ctrl); /*@=refcounttrans@*/
01838     }
01839 
01840 /*@-usereleased@*/
01841     if (fdFileno(ctrl) < 0) {
01842         rc = tcpConnect(ctrl, host, port);
01843         if (rc < 0)
01844             goto errxit2;
01845         ctrl = fdLink(ctrl, "open ctrl (httpReq)");
01846     }
01847 
01848     len = sizeof("\
01849 req x HTTP/1.0\r\n\
01850 User-Agent: rpm/3.0.4\r\n\
01851 Host: y:z\r\n\
01852 Accept: text/plain\r\n\
01853 Transfer-Encoding: chunked\r\n\
01854 \r\n\
01855 ") + strlen(httpCmd) + strlen(path) + sizeof(VERSION) + strlen(hthost) + 20;
01856 
01857     req = alloca(len);
01858     *req = '\0';
01859 
01860   if (!strcmp(httpCmd, "PUT")) {
01861     sprintf(req, "\
01862 %s %s HTTP/1.%d\r\n\
01863 User-Agent: rpm/%s\r\n\
01864 Host: %s:%d\r\n\
01865 Accept: text/plain\r\n\
01866 Transfer-Encoding: chunked\r\n\
01867 \r\n\
01868 ",      httpCmd, path, (u->httpVersion ? 1 : 0), VERSION, hthost, port);
01869 } else {
01870     sprintf(req, "\
01871 %s %s HTTP/1.%d\r\n\
01872 User-Agent: rpm/%s\r\n\
01873 Host: %s:%d\r\n\
01874 Accept: text/plain\r\n\
01875 \r\n\
01876 ",      httpCmd, path, (u->httpVersion ? 1 : 0), VERSION, hthost, port);
01877 }
01878 
01879 if (_ftp_debug)
01880 fprintf(stderr, "-> %s", req);
01881 
01882     len = strlen(req);
01883     if (fdWrite(ctrl, req, len) != (ssize_t)len) {
01884         rc = FTPERR_SERVER_IO_ERROR;
01885         goto errxit;
01886     }
01887 
01888     if (!strcmp(httpCmd, "PUT")) {
01889         ctrl->wr_chunked = 1;
01890     } else {
01891 
01892         rc = httpResp(u, ctrl, NULL);
01893 
01894         if (rc) {
01895             if (!retrying) {    /* not HTTP_OK */
01896                 retrying = 1;
01897                 /*@-refcounttrans@*/ (void) fdClose(ctrl); /*@=refcounttrans@*/
01898                 goto reopen;
01899             }
01900             goto errxit;
01901         }
01902     }
01903 
01904     ctrl = fdLink(ctrl, "open data (httpReq)");
01905     return 0;
01906 
01907 errxit:
01908     /*@-observertrans@*/
01909     fdSetSyserrno(ctrl, errno, ftpStrerror(rc));
01910     /*@=observertrans@*/
01911 errxit2:
01912     if (fdFileno(ctrl) >= 0)
01913         /*@-refcounttrans@*/ (void) fdClose(ctrl); /*@=refcounttrans@*/
01914     return rc;
01915 /*@=usereleased@*/
01916 }
01917 #endif /* WITH_NEON */
01918 
01919 /* XXX DYING: unused */
01920 void * ufdGetUrlinfo(FD_t fd)
01921 {
01922     FDSANE(fd);
01923     if (fd->u == NULL)
01924         return NULL;
01925 /*@-retexpose@*/
01926     return urlLink(fd->u, "ufdGetUrlinfo");
01927 /*@=retexpose@*/
01928 }
01929 
01930 /* =============================================================== */
01931 static ssize_t ufdRead(void * cookie, /*@out@*/ char * buf, size_t count)
01932         /*@globals fileSystem, internalState @*/
01933         /*@modifies buf, fileSystem, internalState @*/
01934         /*@requires maxSet(buf) >= (count - 1) @*/
01935 {
01936     FD_t fd = c2f(cookie);
01937     size_t bytesRead;
01938     size_t total;
01939 
01940     if (fdGetIo(fd) == fdio) {
01941         struct stat sb;
01942         int fdno = fdFileno(fd);
01943         (void) fstat(fdno, &sb);
01944         if (S_ISREG(sb.st_mode))
01945             return fdRead(fd, buf, count);
01946     }
01947 
01948     UFDONLY(fd);
01949     assert(fd->rd_timeoutsecs >= 0);
01950 
01951     for (total = 0; total < count; total += bytesRead) {
01952 
01953         int rc;
01954 
01955         bytesRead = 0;
01956 
01957         /* Is there data to read? */
01958         if (fd->bytesRemain == 0) return (ssize_t) total; /* XXX simulate EOF */
01959         rc = fdReadable(fd, fd->rd_timeoutsecs);
01960 
01961         switch (rc) {
01962         case -1:        /* error */
01963         case  0:        /* timeout */
01964             return (ssize_t) total;
01965             /*@notreached@*/ /*@switchbreak@*/ break;
01966         default:        /* data to read */
01967             /*@switchbreak@*/ break;
01968         }
01969 
01970         rc = (int) fdRead(fd, buf + total, count - total);
01971 
01972         if (rc < 0) {
01973             switch (errno) {
01974             case EWOULDBLOCK:
01975                 continue;
01976                 /*@notreached@*/ /*@switchbreak@*/ break;
01977             default:
01978                 /*@switchbreak@*/ break;
01979             }
01980 if (_rpmio_debug)
01981 fprintf(stderr, "*** read: rc %d errno %d %s \"%s\"\n", rc, errno, strerror(errno), buf);
01982             return rc;
01983             /*@notreached@*/ break;
01984         } else if (rc == 0) {
01985             return (ssize_t) total;
01986             /*@notreached@*/ break;
01987         }
01988         bytesRead = (size_t) rc;
01989     }
01990 
01991     return (ssize_t) count;
01992 }
01993 
01994 static ssize_t ufdWrite(void * cookie, const char * buf, size_t count)
01995         /*@globals fileSystem, internalState @*/
01996         /*@modifies fileSystem, internalState @*/
01997 {
01998     FD_t fd = c2f(cookie);
01999     size_t bytesWritten;
02000     size_t total = 0;
02001 
02002 #ifdef  NOTYET
02003     if (fdGetIo(fd) == fdio) {
02004         struct stat sb;
02005         (void) fstat(fdGetFdno(fd), &sb);
02006         if (S_ISREG(sb.st_mode))
02007             return fdWrite(fd, buf, count);
02008     }
02009 #endif
02010 
02011     UFDONLY(fd);
02012 
02013     for (total = 0; total < count; total += bytesWritten) {
02014 
02015         int rc;
02016 
02017         bytesWritten = 0;
02018 
02019         /* Is there room to write data? */
02020         if (fd->bytesRemain == 0) {
02021 fprintf(stderr, "*** ufdWrite fd %p WRITE PAST END OF CONTENT\n", fd);
02022             return (ssize_t) total;     /* XXX simulate EOF */
02023         }
02024         rc = fdWritable(fd, 2);         /* XXX configurable? */
02025 
02026         switch (rc) {
02027         case -1:        /* error */
02028         case  0:        /* timeout */
02029             return (ssize_t) total;
02030             /*@notreached@*/ /*@switchbreak@*/ break;
02031         default:        /* data to write */
02032             /*@switchbreak@*/ break;
02033         }
02034 
02035         rc = (int) fdWrite(fd, buf + total, count - total);
02036 
02037         if (rc < 0) {
02038             switch (errno) {
02039             case EWOULDBLOCK:
02040                 continue;
02041                 /*@notreached@*/ /*@switchbreak@*/ break;
02042             default:
02043                 /*@switchbreak@*/ break;
02044             }
02045 if (_rpmio_debug)
02046 fprintf(stderr, "*** write: rc %d errno %d %s \"%s\"\n", rc, errno, strerror(errno), buf);
02047             return rc;
02048             /*@notreached@*/ break;
02049         } else if (rc == 0) {
02050             return (ssize_t) total;
02051             /*@notreached@*/ break;
02052         }
02053         bytesWritten = (size_t) rc;
02054     }
02055 
02056     return (ssize_t) count;
02057 }
02058 
02059 static int ufdSeek(void * cookie, _libio_pos_t pos, int whence)
02060         /*@globals fileSystem, internalState @*/
02061         /*@modifies fileSystem, internalState @*/
02062 {
02063     FD_t fd = c2f(cookie);
02064 
02065     switch (urlType(fd->u)) {
02066     case URL_IS_UNKNOWN:
02067     case URL_IS_PATH:
02068         break;
02069     case URL_IS_HTTPS:
02070     case URL_IS_HTTP:
02071     case URL_IS_HKP:
02072     case URL_IS_FTP:
02073     case URL_IS_DASH:
02074     default:
02075         return -2;
02076         /*@notreached@*/ break;
02077     }
02078     return fdSeek(cookie, pos, whence);
02079 }
02080 
02081 /*@-usereleased@*/      /* LCL: fd handling is tricky here. */
02082 int ufdClose( /*@only@*/ void * cookie)
02083 {
02084     FD_t fd = c2f(cookie);
02085 
02086     UFDONLY(fd);
02087 
02088     if (fd->u) {
02089         urlinfo u = fd->u;
02090 
02091 /*@-evalorder @*/
02092         if (fd == u->data)
02093             fd = u->data = fdFree(fd, "grab data (ufdClose persist)");
02094         else
02095             fd = fdFree(fd, "grab data (ufdClose)");
02096 assert(fd != NULL);
02097         (void) urlFree(fd->u, "url (ufdClose)");
02098         fd->u = NULL;
02099         u->ctrl = fdFree(u->ctrl, "grab ctrl (ufdClose)");
02100 /*@=evalorder @*/
02101 
02102         if (urlType(u) == URL_IS_FTP) {
02103 
02104             /* XXX if not using libio, lose the fp from fpio */
02105             {   FILE * fp;
02106                 /*@+voidabstract -nullpass@*/
02107                 fp = fdGetFILE(fd);
02108                 if (noLibio && fp)
02109                     fdSetFp(fd, NULL);
02110                 /*@=voidabstract =nullpass@*/
02111             }
02112 
02113             /*
02114              * Non-error FTP has 4 refs on the data fd:
02115              *  "persist data (ufdOpen FTP)"            rpmio.c:888
02116              *  "grab data (ufdOpen FTP)"               rpmio.c:892
02117              *  "open data (ftpReq)"                    ftp.c:633
02118              *  "fopencookie"                           rpmio.c:1507
02119              *
02120              * Non-error FTP has 5 refs on the ctrl fd:
02121              *  "persist ctrl"                          url.c:176
02122              *  "grab ctrl (urlConnect FTP)"            rpmio.c:404
02123              *  "open ctrl"                             ftp.c:504
02124              *  "grab data (ftpReq)"                    ftp.c:661
02125              *  "open data (ftpReq)"                    ftp.c:662
02126              */
02127             if (fd->bytesRemain > 0) {
02128                 if (fd->ftpFileDoneNeeded) {
02129                     if (fdReadable(u->ctrl, 0) > 0)
02130                         (void) ftpFileDone(u, fd);
02131                     else
02132                         (void) ftpAbort(u, fd);
02133                 }
02134             } else {
02135                 int rc;
02136                 /* XXX STOR et al require close before ftpFileDone */
02137                 /*@-refcounttrans@*/
02138                 rc = fdClose(fd);
02139                 /*@=refcounttrans@*/
02140 #if 0   /* XXX error exit from ufdOpen does not have this set */
02141                 assert(fd->ftpFileDoneNeeded != 0);
02142 #endif
02143                 /*@-compdef@*/ /* FIX: u->data undefined */
02144                 if (fd->ftpFileDoneNeeded)
02145                     (void) ftpFileDone(u, fd);
02146                 /*@=compdef@*/
02147                 return rc;
02148             }
02149         }
02150 
02151         /* XXX Why not (u->urltype == URL_IS_HTTP) ??? */
02152         /* XXX Why not (u->urltype == URL_IS_HTTPS) ??? */
02153         /* XXX Why not (u->urltype == URL_IS_HKP) ??? */
02154         if (u->scheme != NULL
02155          && (!strncmp(u->scheme, "http", sizeof("http")-1) || !strncmp(u->scheme, "hkp", sizeof("hkp")-1)))
02156         {
02157             /*
02158              * HTTP has 4 (or 5 if persistent malloc) refs on the fd:
02159              *  "persist ctrl"                          url.c:177
02160              *  "grab ctrl (ufdOpen HTTP)"              rpmio.c:924
02161              *  "grab data (ufdOpen HTTP)"              rpmio.c:928
02162              *  "open ctrl (httpReq)"                   ftp.c:382
02163              *  "open data (httpReq)"                   ftp.c:435
02164              */
02165 
02166 /*@-evalorder @*/
02167             if (fd == u->ctrl)
02168                 fd = u->ctrl = fdFree(fd, "open data (ufdClose HTTP persist ctrl)");
02169             else if (fd == u->data)
02170                 fd = u->data = fdFree(fd, "open data (ufdClose HTTP persist data)");
02171             else
02172                 fd = fdFree(fd, "open data (ufdClose HTTP)");
02173 /*@=evalorder @*/
02174 
02175             /* XXX if not using libio, lose the fp from fpio */
02176             {   FILE * fp;
02177                 /*@+voidabstract -nullpass@*/
02178                 fp = fdGetFILE(fd);
02179                 if (noLibio && fp)
02180                     fdSetFp(fd, NULL);
02181                 /*@=voidabstract =nullpass@*/
02182             }
02183 
02184             /* If content remains, then don't persist. */
02185 assert(fd != NULL);
02186             if (fd->bytesRemain > 0) {
02187                 fd->persist = 0;
02188             }
02189             fd->contentLength = fd->bytesRemain = -1;
02190 
02191             /* If persisting, then Fclose will juggle refcounts. */
02192             if (fd->persist && (fd == u->ctrl || fd == u->data))
02193                 return 0;
02194         }
02195     }
02196     return fdClose(fd);
02197 }
02198 /*@=usereleased@*/
02199 
02200 /*@-nullstate@*/        /* FIX: u->{ctrl,data}->u undef after XurlLink. */
02201 /*@null@*/ FD_t ftpOpen(const char *url, /*@unused@*/ int flags,
02202                 /*@unused@*/ mode_t mode, /*@out@*/ urlinfo *uret)
02203         /*@modifies *uret @*/
02204 {
02205     urlinfo u = NULL;
02206     FD_t fd = NULL;
02207 
02208 #if 0   /* XXX makeTempFile() heartburn */
02209     assert(!(flags & O_RDWR));
02210 #endif
02211     if (urlConnect(url, &u) < 0)
02212         goto exit;
02213 
02214     if (u->data == NULL)
02215         u->data = fdNew("persist data (ftpOpen)");
02216 
02217 assert(u->data != NULL);
02218 /*@-unqualifiedtrans@*/
02219     if (u->data->u == NULL)
02220         fd = u->data = fdLink(u->data, "grab data (ftpOpen persist data)");
02221     else
02222         fd = fdNew("grab data (ftpOpen)");
02223 /*@=unqualifiedtrans@*/
02224 
02225     if (fd != NULL) {
02226         fdSetOpen(fd, url, flags, mode);
02227         fdSetIo(fd, ufdio);
02228         fd->ftpFileDoneNeeded = 0;
02229         fd->rd_timeoutsecs = ftpTimeoutSecs;
02230         fd->contentLength = fd->bytesRemain = -1;
02231 /*@-usereleased@*/
02232         fd->u = urlLink(u, "url (ufdOpen FTP)");
02233 /*@=usereleased@*/
02234     }
02235 
02236 exit:
02237     if (uret)
02238         *uret = u;
02239     /*@-refcounttrans@*/
02240     return fd;
02241     /*@=refcounttrans@*/
02242 }
02243 /*@=nullstate@*/
02244 
02245 static /*@null@*/ FD_t ufdOpen(const char * url, int flags, mode_t mode)
02246         /*@globals h_errno, fileSystem, internalState @*/
02247         /*@modifies fileSystem, internalState @*/
02248 {
02249     FD_t fd = NULL;
02250     const char * cmd;
02251     urlinfo u;
02252     const char * path;
02253     urltype ut = urlPath(url, &path);
02254 
02255 if (_rpmio_debug)
02256 fprintf(stderr, "*** ufdOpen(%s,0x%x,0%o)\n", url, (unsigned)flags, (unsigned)mode);
02257 
02258 /*@-usereleased@*/
02259     switch (ut) {
02260     case URL_IS_FTP:
02261         fd = ftpOpen(url, flags, mode, &u);
02262         if (fd == NULL || u == NULL)
02263             break;
02264 
02265         /* XXX W2DO? use STOU rather than STOR to prevent clobbering */
02266         cmd = ((flags & O_WRONLY)
02267                 ?  ((flags & O_APPEND) ? "APPE" :
02268                    ((flags & O_CREAT) ? "STOR" : "STOR"))
02269                 :  ((flags & O_CREAT) ? "STOR" : "RETR"));
02270         u->openError = ftpReq(fd, cmd, path);
02271         if (u->openError < 0) {
02272             /* XXX make sure that we can exit through ufdClose */
02273             fd = fdLink(fd, "error data (ufdOpen FTP)");
02274         } else {
02275             fd->bytesRemain = ((!strcmp(cmd, "RETR"))
02276                 ?  fd->contentLength : -1);
02277             fd->wr_chunked = 0;
02278         }
02279         break;
02280     case URL_IS_HTTPS:
02281     case URL_IS_HTTP:
02282     case URL_IS_HKP:
02283 #ifdef WITH_NEON
02284         fd = davOpen(url, flags, mode, &u);
02285 #else
02286         fd = httpOpen(url, flags, mode, &u);
02287 #endif
02288         if (fd == NULL || u == NULL)
02289             break;
02290 
02291         cmd = ((flags & O_WRONLY)
02292                 ?  ((flags & O_APPEND) ? "PUT" :
02293                    ((flags & O_CREAT) ? "PUT" : "PUT"))
02294                 : "GET");
02295 #ifdef WITH_NEON
02296         u->openError = davReq(fd, cmd, path);
02297 #else
02298         u->openError = httpReq(fd, cmd, path);
02299 #endif
02300         if (u->openError < 0) {
02301             /* XXX make sure that we can exit through ufdClose */
02302             fd = fdLink(fd, "error ctrl (ufdOpen HTTP)");
02303             fd = fdLink(fd, "error data (ufdOpen HTTP)");
02304         } else {
02305             fd->bytesRemain = ((!strcmp(cmd, "GET"))
02306                 ?  fd->contentLength : -1);
02307             fd->wr_chunked = ((!strcmp(cmd, "PUT"))
02308                 ?  fd->wr_chunked : 0);
02309         }
02310         break;
02311     case URL_IS_DASH:
02312         assert(!(flags & O_RDWR));
02313         fd = fdDup( ((flags & O_WRONLY) ? STDOUT_FILENO : STDIN_FILENO) );
02314         if (fd) {
02315             fdSetOpen(fd, url, flags, mode);
02316             fdSetIo(fd, ufdio);
02317             fd->rd_timeoutsecs = 600;   /* XXX W2DO? 10 mins? */
02318             fd->contentLength = fd->bytesRemain = -1;
02319         }
02320         break;
02321     case URL_IS_PATH:
02322     case URL_IS_UNKNOWN:
02323     default:
02324         fd = fdOpen(path, flags, mode);
02325         if (fd) {
02326             fdSetIo(fd, ufdio);
02327             fd->rd_timeoutsecs = 1;
02328             fd->contentLength = fd->bytesRemain = -1;
02329         }
02330         break;
02331     }
02332 
02333     if (fd == NULL) return NULL;
02334     if (Fileno(fd) < 0) {
02335         (void) ufdClose(fd);
02336         return NULL;
02337     }
02338 /*@=usereleased@*/
02339 DBGIO(fd, (stderr, "<--\tufdOpen(\"%s\",%x,0%o) %s\n", url, (unsigned)flags, (unsigned)mode, fdbg(fd)));
02340     return fd;
02341 }
02342 
02343 /*@-type@*/ /* LCL: function typedefs */
02344 static struct FDIO_s ufdio_s = {
02345   ufdRead, ufdWrite, ufdSeek, ufdClose, NULL, NULL, NULL,
02346 };
02347 /*@=type@*/
02348 
02349 FDIO_t ufdio = /*@-compmempass@*/ &ufdio_s /*@=compmempass@*/ ;
02350 
02351 /* =============================================================== */
02352 /*@observer@*/
02353 static const char * getFdErrstr (FD_t fd)
02354         /*@*/
02355 {
02356     const char *errstr = NULL;
02357 
02358 #if defined(WITH_ZLIB)
02359     if (fdGetIo(fd) == gzdio) {
02360         errstr = fd->errcookie;
02361     } else
02362 #endif  /* WITH_ZLIB */
02363 
02364 #if defined(WITH_BZIP2)
02365     if (fdGetIo(fd) == bzdio) {
02366         errstr = fd->errcookie;
02367     } else
02368 #endif
02369 
02370 #if defined(WITH_XZ)
02371     if (fdGetIo(fd) == lzdio) {
02372         errstr = fd->errcookie;
02373     } else
02374     if (fdGetIo(fd) == xzdio) {
02375         errstr = fd->errcookie;
02376     } else
02377 #endif
02378 
02379     {
02380         errstr = (fd->syserrno ? strerror(fd->syserrno) : "");
02381     }
02382 
02383     return errstr;
02384 }
02385 
02386 /* =============================================================== */
02387 
02388 const char *Fstrerror(FD_t fd)
02389 {
02390     if (fd == NULL)
02391         return (errno ? strerror(errno) : "");
02392     FDSANE(fd);
02393     return getFdErrstr(fd);
02394 }
02395 
02396 #define FDIOVEC(_fd, _vec)      \
02397   ((fdGetIo(_fd) && fdGetIo(_fd)->_vec) ? fdGetIo(_fd)->_vec : NULL)
02398 
02399 size_t Fread(void *buf, size_t size, size_t nmemb, FD_t fd) {
02400     fdio_read_function_t _read;
02401     int rc;
02402 
02403     FDSANE(fd);
02404 DBGIO(fd, (stderr, "==> Fread(%p,%u,%u,%p) %s\n", buf, (unsigned)size, (unsigned)nmemb, (fd ? fd : NULL), fdbg(fd)));
02405 
02406     if (fdGetIo(fd) == fpio) {
02407         /*@+voidabstract -nullpass@*/
02408         rc = (int) fread(buf, size, nmemb, fdGetFILE(fd));
02409         /*@=voidabstract =nullpass@*/
02410         return (size_t) rc;
02411     }
02412 
02413     /*@-nullderef@*/
02414     _read = FDIOVEC(fd, read);
02415     /*@=nullderef@*/
02416 
02417     rc = (int) (_read ? (*_read) (fd, buf, size * nmemb) : -2);
02418     return (size_t) rc;
02419 }
02420 
02421 size_t Fwrite(const void *buf, size_t size, size_t nmemb, FD_t fd)
02422 {
02423     fdio_write_function_t _write;
02424     int rc;
02425 
02426     FDSANE(fd);
02427 DBGIO(fd, (stderr, "==> Fwrite(%p,%u,%u,%p) %s\n", buf, (unsigned)size, (unsigned)nmemb, (fd ? fd : NULL), fdbg(fd)));
02428 
02429     if (fdGetIo(fd) == fpio) {
02430         /*@+voidabstract -nullpass@*/
02431         rc = (int) fwrite(buf, size, nmemb, fdGetFILE(fd));
02432         /*@=voidabstract =nullpass@*/
02433         return (size_t) rc;
02434     }
02435 
02436     /*@-nullderef@*/
02437     _write = FDIOVEC(fd, write);
02438     /*@=nullderef@*/
02439 
02440     rc = (int) (_write ? _write(fd, buf, size * nmemb) : -2);
02441     return (size_t) rc;
02442 }
02443 
02444 int Fseek(FD_t fd, _libio_off_t offset, int whence)
02445 {
02446     fdio_seek_function_t _seek;
02447 #ifdef USE_COOKIE_SEEK_POINTER
02448     _IO_off64_t o64 = offset;
02449     _libio_pos_t pos = &o64;
02450 #else
02451     _libio_pos_t pos = offset;
02452 #endif
02453 
02454     long int rc;
02455 
02456     FDSANE(fd);
02457 DBGIO(fd, (stderr, "==> Fseek(%p,%ld,%d) %s\n", fd, (long)offset, whence, fdbg(fd)));
02458 
02459     if (fdGetIo(fd) == fpio)
02460         return fseek(fdGetFILE(fd), (long)offset, whence);
02461 
02462     /*@-nullderef@*/
02463     _seek = FDIOVEC(fd, seek);
02464     /*@=nullderef@*/
02465 
02466     rc = (_seek ? _seek(fd, pos, whence) : -2);
02467     return rc;
02468 }
02469 
02470 long Ftell(FD_t fd)
02471 {
02472     long int rc = -2;
02473 
02474     FDSANE(fd);
02475 
02476     if (fdGetIo(fd) == fpio)
02477         rc = ftell(fdGetFILE(fd));
02478     else
02479         errno = EBADF;
02480 DBGIO(fd, (stderr, "<== Ftell(%p) rc %ld %s\n", fd, rc, fdbg(fd)));
02481     return rc;
02482 }
02483 
02484 void Rewind(FD_t fd)
02485 {
02486     FDSANE(fd);
02487 DBGIO(fd, (stderr, "==> Rewind(%p) %s\n", fd, fdbg(fd)));
02488 
02489     if (fdGetIo(fd) == fpio)
02490         rewind(fdGetFILE(fd));
02491 }
02492 
02493 int Fgetpos(FD_t fd, fpos_t *pos)
02494 {
02495     int rc = -2;
02496 
02497     FDSANE(fd);
02498 
02499     if (fdGetIo(fd) == fpio)
02500         rc = fgetpos(fdGetFILE(fd), pos);
02501     else
02502         errno = EBADF;
02503 DBGIO(fd, (stderr, "<== Fgetpos(%p,%p) rc %d %s\n", fd, pos, rc, fdbg(fd)));
02504     return rc;
02505 }
02506 
02507 int Fsetpos(FD_t fd, fpos_t *pos)
02508 {
02509     int rc = -2;
02510 
02511     FDSANE(fd);
02512 
02513     if (fdGetIo(fd) == fpio)
02514         return fgetpos(fdGetFILE(fd), pos);
02515 
02516     errno = EBADF;
02517 DBGIO(fd, (stderr, "<== Fsetpos(%p,%p) rc %d %s\n", fd, pos, rc, fdbg(fd)));
02518     return rc;
02519 }
02520 
02521 int Fclose(FD_t fd)
02522 {
02523     int rc = 0, ec = 0;
02524 
02525     FDSANE(fd);
02526 DBGIO(fd, (stderr, "==> Fclose(%p) %s\n", (fd ? fd : NULL), fdbg(fd)));
02527 
02528 /*@-usereleased@*/
02529     fd = fdLink(fd, "Fclose");
02530     if (fd != NULL)
02531     while (fd->nfps >= 0) {
02532         FDSTACK_t * fps = &fd->fps[fd->nfps];
02533         
02534         if (fps->io == fpio) {
02535             FILE *fp;
02536             int fpno;
02537 
02538             /*@+voidabstract -nullpass@*/
02539             fp = fdGetFILE(fd);
02540             fpno = fileno(fp);
02541             /*@=voidabstract =nullpass@*/
02542         /* XXX persistent HTTP/1.1 returns the previously opened fp */
02543             if (fd->nfps > 0 && fpno == -1 &&
02544                 fd->fps[fd->nfps-1].io == ufdio &&
02545                 fd->fps[fd->nfps-1].fp == fp &&
02546                 (fd->fps[fd->nfps-1].fdno >= 0 || fd->req != NULL))
02547             {
02548                 int hadreqpersist = (fd->req != NULL);
02549 
02550                 if (fp)
02551                     rc = fflush(fp);
02552                 fd->nfps--;
02553                 /*@-refcounttrans@*/
02554                 rc = ufdClose(fd);
02555                 /*@=refcounttrans@*/
02556                 if (fdGetFdno(fd) >= 0)
02557                     break;
02558                 if (!fd->persist)
02559                     hadreqpersist = 0;
02560                 fdSetFp(fd, NULL);
02561                 fd->nfps++;
02562                 if (fp) {
02563                     /* HACK: flimsy Keepalive wiring. */
02564                     if (hadreqpersist) {
02565 #ifdef  NOTYET  /* XXX not quite right yet. */
02566                         (void) davDisconnect(fd);
02567                         fd->req = NULL;
02568 #endif
02569                         fd->nfps--;
02570 /*@-exposetrans@*/
02571                         fdSetFp(fd, fp);
02572 /*@=exposetrans@*/
02573 /*@-refcounttrans@*/
02574                         (void) fdClose(fd);
02575 /*@=refcounttrans@*/
02576                         fdSetFp(fd, NULL);
02577                         fd->nfps++;
02578 /*@-refcounttrans@*/
02579                         (void) fdClose(fd);
02580 /*@=refcounttrans@*/
02581                     } else
02582                         rc = fclose(fp);
02583                 }
02584                 fdPop(fd);
02585                 if (noLibio)
02586                     fdSetFp(fd, NULL);
02587             } else {
02588                 if (fp)
02589                     rc = fclose(fp);
02590                 if (fpno == -1) {
02591                     fd = fdFree(fd, "fopencookie (Fclose)");
02592                     fdPop(fd);
02593                 }
02594             }
02595         } else {
02596             /*@-nullderef@*/
02597             fdio_close_function_t _close = FDIOVEC(fd, close);
02598             /*@=nullderef@*/
02599             rc = _close(fd);
02600         }
02601         if (fd == NULL || fd->nfps == 0)        /* XXX fd != NULL ever */
02602             break;
02603         if (ec == 0 && rc)
02604             ec = rc;
02605         fdPop(fd);
02606     }
02607     fd = fdFree(fd, "Fclose");
02608     return ec;
02609 /*@=usereleased@*/
02610 }
02611 
02629 static inline void cvtfmode (const char *m,
02630                                 /*@out@*/ char *stdio, size_t nstdio,
02631                                 /*@out@*/ char *other, size_t nother,
02632                                 /*@out@*/ const char **end, /*@out@*/ int * f)
02633         /*@modifies *stdio, *other, *end, *f @*/
02634 {
02635     int flags = 0;
02636     char c;
02637 
02638     switch (*m) {
02639     case 'a':
02640         flags |= O_WRONLY | O_CREAT | O_APPEND;
02641         if (--nstdio > 0) *stdio++ = *m;
02642         break;
02643     case 'w':
02644         flags |= O_WRONLY | O_CREAT | O_TRUNC;
02645         if (--nstdio > 0) *stdio++ = *m;
02646         break;
02647     case 'r':
02648         flags |= O_RDONLY;
02649         if (--nstdio > 0) *stdio++ = *m;
02650         break;
02651     default:
02652         *stdio = '\0';
02653         return;
02654         /*@notreached@*/ break;
02655     }
02656     m++;
02657 
02658     while ((c = *m++) != '\0') {
02659         switch (c) {
02660         case '.':
02661             /*@switchbreak@*/ break;
02662         case '+':
02663             flags &= ~(O_RDONLY|O_WRONLY);
02664             flags |= O_RDWR;
02665             if (--nstdio > 0) *stdio++ = c;
02666             continue;
02667             /*@notreached@*/ /*@switchbreak@*/ break;
02668         case 'x':       /* glibc: open file exclusively. */
02669             flags |= O_EXCL;
02670             /*@fallthrough@*/
02671         case 'm':       /* glibc: mmap'd reads */
02672         case 'c':       /* glibc: no cancel */
02673 #if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ >= 3
02674             if (--nstdio > 0) *stdio++ = c;
02675 #endif
02676             continue;
02677             /*@notreached@*/ /*@switchbreak@*/ break;
02678         case 'b':
02679             if (--nstdio > 0) *stdio++ = c;
02680             continue;
02681             /*@notreached@*/ /*@switchbreak@*/ break;
02682         default:
02683             if (--nother > 0) *other++ = c;
02684             continue;
02685             /*@notreached@*/ /*@switchbreak@*/ break;
02686         }
02687         break;
02688     }
02689     if (c == '\0')      m--;    /* one too far */
02690 
02691     *stdio = *other = '\0';
02692     if (end != NULL)
02693         *end = (*m != '\0' ? m : NULL);
02694     if (f != NULL)
02695         *f = flags;
02696 }
02697 
02698 #if _USE_LIBIO
02699 #if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ == 0
02700 /* XXX retrofit glibc-2.1.x typedef on glibc-2.0.x systems */
02701 typedef _IO_cookie_io_functions_t cookie_io_functions_t;
02702 #endif
02703 #endif
02704 
02705 FD_t Fdopen(FD_t ofd, const char *fmode)
02706 {
02707     char stdio[20], other[20], zstdio[40+1];
02708     const char *end = NULL;
02709     FDIO_t iof = NULL;
02710     FD_t fd = ofd;
02711 
02712 if (_rpmio_debug)
02713 fprintf(stderr, "*** Fdopen(%p,%s) %s\n", fd, fmode, fdbg(fd));
02714     FDSANE(fd);
02715 
02716     if (fmode == NULL)
02717         return NULL;
02718 
02719     cvtfmode(fmode, stdio, sizeof(stdio), other, sizeof(other), &end, NULL);
02720     if (stdio[0] == '\0')
02721         return NULL;
02722     zstdio[0] = '\0';
02723     (void) stpcpy( stpcpy(zstdio, stdio), other);
02724 
02725     if (end == NULL && other[0] == '\0')
02726         /*@-refcounttrans -retalias@*/ return fd; /*@=refcounttrans =retalias@*/
02727 
02728     if (end && *end) {
02729         if (!strcmp(end, "fdio")) {
02730             iof = fdio;
02731 #if defined(WITH_ZLIB)
02732         } else if (!strcmp(end, "gzdio")) {
02733             iof = gzdio;
02734             /*@-internalglobs@*/
02735             fd = iof->_fdopen(fd, zstdio);
02736             /*@=internalglobs@*/
02737 #endif
02738 #if defined(WITH_BZIP2)
02739         } else if (!strcmp(end, "bzdio")) {
02740             iof = bzdio;
02741             /*@-internalglobs@*/
02742             fd = iof->_fdopen(fd, zstdio);
02743             /*@=internalglobs@*/
02744 #endif
02745 #if defined(WITH_XZ)
02746         } else if (!strcmp(end, "lzdio")) {
02747             iof = lzdio;
02748             fd = iof->_fdopen(fd, zstdio);
02749         } else if (!strcmp(end, "xzdio")) {
02750             iof = xzdio;
02751             fd = iof->_fdopen(fd, zstdio);
02752 #endif
02753         } else if (!strcmp(end, "ufdio")) {
02754             iof = ufdio;
02755         } else if (!strcmp(end, "fpio")) {
02756             iof = fpio;
02757             if (noLibio) {
02758                 int fdno = Fileno(fd);
02759                 FILE * fp = fdopen(fdno, stdio);
02760 /*@+voidabstract -nullpass@*/
02761 if (_rpmio_debug)
02762 fprintf(stderr, "*** Fdopen fpio fp %p\n", (void *)fp);
02763 /*@=voidabstract =nullpass@*/
02764                 if (fp == NULL)
02765                     return NULL;
02766                 /* XXX gzdio/bzdio use fp for private data */
02767                 /*@+voidabstract@*/
02768                 if (fdGetFp(fd) == NULL)
02769                     fdSetFp(fd, fp);
02770                 fdPush(fd, fpio, fp, fdno);     /* Push fpio onto stack */
02771                 /*@=voidabstract@*/
02772             }
02773         }
02774     } else if (other[0] != '\0') {
02775         for (end = other; *end && strchr("0123456789fh", *end); end++)
02776             {};
02777         if (*end == '\0') {
02778 #if defined(WITH_ZLIB)
02779             iof = gzdio;
02780             /*@-internalglobs@*/
02781             fd = iof->_fdopen(fd, zstdio);
02782             /*@=internalglobs@*/
02783 #endif
02784         }
02785     }
02786     if (iof == NULL)
02787         /*@-refcounttrans -retalias@*/ return fd; /*@=refcounttrans =retalias@*/
02788 
02789     if (!noLibio) {
02790         FILE * fp = NULL;
02791 
02792 #if _USE_LIBIO
02793         {   cookie_io_functions_t ciof;
02794             ciof.read = iof->read;
02795             ciof.write = iof->write;
02796             ciof.seek = iof->seek;
02797             ciof.close = iof->close;
02798             fp = fopencookie(fd, stdio, ciof);
02799 DBGIO(fd, (stderr, "<-- fopencookie(%p,\"%s\",*%p) returns fp %p\n", fd, stdio, iof, fp));
02800         }
02801 #endif
02802 
02803         if (fp) {
02804             /* XXX gzdio/bzdio use fp for private data */
02805             /*@+voidabstract -nullpass@*/
02806             if (fdGetFp(fd) == NULL)
02807                 fdSetFp(fd, fp);
02808             fdPush(fd, fpio, fp, fileno(fp));   /* Push fpio onto stack */
02809             /*@=voidabstract =nullpass@*/
02810             fd = fdLink(fd, "fopencookie");
02811         }
02812     }
02813 
02814 /*@-refcounttrans -retalias -usereleased @*/
02815 DBGIO(fd, (stderr, "<== Fdopen(%p,\"%s\") returns fd %p %s\n", ofd, fmode, (fd ? fd : NULL), fdbg(fd)));
02816     return fd;
02817 /*@=refcounttrans =retalias =usereleased @*/
02818 }
02819 
02820 FD_t Fopen(const char *path, const char *_fmode)
02821 {
02822     const char * fmode = NULL;
02823     char stdio[20], other[20];
02824     const char *end = NULL;
02825     mode_t perms = 0666;
02826     int flags = 0;
02827     FD_t fd = NULL;
02828 
02829     if (path == NULL || _fmode == NULL)
02830         goto exit;
02831 /*@-globs -mods@*/
02832     fmode = rpmExpand(_fmode, NULL);
02833 /*@=globs =mods@*/
02834 
02835 if (_rpmio_debug)
02836 fprintf(stderr, "==> Fopen(%s, %s)\n", path, fmode);
02837 
02838     stdio[0] = '\0';
02839     cvtfmode(fmode, stdio, sizeof(stdio), other, sizeof(other), &end, &flags);
02840     if (stdio[0] == '\0')
02841         goto exit;
02842 
02843     if (end == NULL || !strcmp(end, "fdio")) {
02844         fd = fdOpen(path, flags, perms);
02845         if (fdFileno(fd) < 0) {
02846             if (fd) (void) fdClose(fd);
02847             fd = NULL;
02848             goto exit;
02849         }
02850     } else {
02851         FILE *fp;
02852         int fdno;
02853         int isHTTP = 0;
02854 
02855         /* XXX gzdio/bzdio/lzdio through here too */
02856 
02857         switch (urlIsURL(path)) {
02858         case URL_IS_HTTPS:
02859         case URL_IS_HTTP:
02860         case URL_IS_HKP:
02861             isHTTP = 1;
02862             /*@fallthrough@*/
02863         case URL_IS_PATH:
02864         case URL_IS_DASH:
02865         case URL_IS_FTP:
02866         case URL_IS_UNKNOWN:
02867             fd = ufdOpen(path, flags, perms);
02868             if (fd == NULL || !(fdFileno(fd) >= 0 || fd->req != NULL)) {
02869                 if (fd) (void) fdClose(fd);
02870                 fd = NULL;
02871                 goto exit;
02872             }
02873             break;
02874         default:
02875             if (fd) (void) fdClose(fd);
02876             fd = NULL;
02877             goto exit;
02878             /*@notreached@*/ break;
02879         }
02880 
02881         /* XXX persistent HTTP/1.1 returns the previously opened fp */
02882         if (isHTTP && ((fp = fdGetFp(fd)) != NULL) && ((fdno = fdGetFdno(fd)) >= 0 || fd->req != NULL))
02883         {
02884             /*@+voidabstract@*/
02885             fdPush(fd, fpio, fp, fileno(fp));   /* Push fpio onto stack */
02886             /*@=voidabstract@*/
02887             goto exit;
02888         }
02889     }
02890 
02891     if (fd)
02892         fd = Fdopen(fd, fmode);
02893 exit:
02894 
02895 if (_rpmio_debug)
02896 fprintf(stderr, "<== Fopen(%s, %s) fd %p\n", path, fmode, fd);
02897     fmode = _free(fmode);
02898     return fd;
02899 }
02900 
02901 int Fflush(FD_t fd)
02902 {
02903     void * vh;
02904     if (fd == NULL) return -1;
02905     if (fdGetIo(fd) == fpio)
02906         /*@+voidabstract -nullpass@*/
02907         return fflush(fdGetFILE(fd));
02908         /*@=voidabstract =nullpass@*/
02909 
02910     vh = fdGetFp(fd);
02911 #if defined(WITH_ZLIB)
02912     if (vh && fdGetIo(fd) == gzdio && gzdio->_flush != NULL)
02913         return (*gzdio->_flush) ((void *)fd);
02914 #endif
02915 #if defined(WITH_BZIP2)
02916     if (vh && fdGetIo(fd) == bzdio && bzdio->_flush != NULL)
02917         return (*bzdio->_flush) ((void *)fd);
02918 #endif
02919 #if defined(WITH_XZ)
02920     if (vh && fdGetIo(fd) == lzdio && lzdio->_flush != NULL)
02921         return (*lzdio->_flush) ((void *)fd);
02922     if (vh && fdGetIo(fd) == xzdio && xzdio->_flush != NULL)
02923         return (*xzdio->_flush) ((void *)fd);
02924 #endif
02925 
02926     return 0;
02927 }
02928 
02929 int Ferror(FD_t fd)
02930 {
02931     int i, rc = 0;
02932 
02933     if (fd == NULL) return -1;
02934     if (fd->req != NULL) {
02935         /* HACK: flimsy wiring for neon errors. */
02936         rc = (fd->req == (void *)-1 || fd->syserrno  || fd->errcookie != NULL) ? -1 : 0;
02937     } else
02938     for (i = fd->nfps; rc == 0 && i >= 0; i--) {
02939         FDSTACK_t * fps = &fd->fps[i];
02940         int ec;
02941         
02942         if (fps->io == fpio) {
02943             /*@+voidabstract -nullpass@*/
02944             ec = ferror(fdGetFILE(fd));
02945             /*@=voidabstract =nullpass@*/
02946 #if defined(WITH_ZLIB)
02947         } else if (fps->io == gzdio) {
02948             ec = (fd->syserrno || fd->errcookie != NULL) ? -1 : 0;
02949             i--;        /* XXX fdio under gzdio always has fdno == -1 */
02950 #endif
02951 #if defined(WITH_BZIP2)
02952         } else if (fps->io == bzdio) {
02953             ec = (fd->syserrno  || fd->errcookie != NULL) ? -1 : 0;
02954             i--;        /* XXX fdio under bzdio always has fdno == -1 */
02955 #endif
02956 #if defined(WITH_XZ)
02957         } else if (fps->io == lzdio) {
02958             ec = (fd->syserrno  || fd->errcookie != NULL) ? -1 : 0;
02959             i--;        /* XXX fdio under lzdio always has fdno == -1 */
02960         } else if (fps->io == xzdio) {
02961             ec = (fd->syserrno  || fd->errcookie != NULL) ? -1 : 0;
02962             i--;        /* XXX fdio under xzdio always has fdno == -1 */
02963 #endif
02964         } else {
02965         /* XXX need to check ufdio/gzdio/bzdio/fdio errors correctly. */
02966             ec = (fdFileno(fd) < 0 ? -1 : 0);
02967         }
02968 
02969         if (rc == 0 && ec)
02970             rc = ec;
02971     }
02972 DBGIO(fd, (stderr, "<== Ferror(%p) rc %d %s\n", fd, rc, fdbg(fd)));
02973     return rc;
02974 }
02975 
02976 int Fileno(FD_t fd)
02977 {
02978     int i, rc = -1;
02979 
02980     if (fd == NULL)
02981         return -1;
02982     if (fd->req != NULL)
02983         rc = 123456789; /* HACK: https has no steenkin fileno. */
02984     else
02985     for (i = fd->nfps ; rc == -1 && i >= 0; i--) {
02986         rc = fd->fps[i].fdno;
02987     }
02988 
02989 DBGIO(fd, (stderr, "<== Fileno(%p) rc %d %s\n", (fd ? fd : NULL), rc, fdbg(fd)));
02990     return rc;
02991 }
02992 
02993 /* XXX this is naive */
02994 int Fcntl(FD_t fd, int op, void *lip)
02995 {
02996     return fcntl(Fileno(fd), op, lip);
02997 }
02998 
02999 /* =============================================================== */
03000 /* Helper routines that may be generally useful.
03001  */
03002 int rpmioMkpath(const char * path, mode_t mode, uid_t uid, gid_t gid)
03003 {
03004     char * d, * de;
03005     int created = 0;
03006     int rc;
03007 
03008     if (path == NULL || *path == '\0')
03009         return -1;
03010     d = alloca(strlen(path)+2);
03011     de = stpcpy(d, path);
03012     de[1] = '\0';
03013     for (de = d; *de != '\0'; de++) {
03014         struct stat st;
03015         char savec;
03016 
03017         while (*de && *de != '/') de++;
03018         savec = de[1];
03019         de[1] = '\0';
03020 
03021         rc = Stat(d, &st);
03022         if (rc) {
03023             switch(errno) {
03024             default:
03025                 return errno;
03026                 /*@notreached@*/ /*@switchbreak@*/ break;
03027             case ENOENT:
03028                 /*@switchbreak@*/ break;
03029             }
03030             rc = Mkdir(d, mode);
03031             if (rc)
03032                 return errno;
03033             created = 1;
03034             if (!(uid == (uid_t) -1 && gid == (gid_t) -1)) {
03035                 rc = Chown(d, uid, gid);
03036                 if (rc)
03037                     return errno;
03038             }
03039         } else if (!S_ISDIR(st.st_mode)) {
03040             return ENOTDIR;
03041         }
03042         de[1] = savec;
03043     }
03044     rc = 0;
03045     if (created)
03046         rpmlog(RPMLOG_DEBUG, D_("created directory(s) %s mode 0%o\n"),
03047                         path, (unsigned)mode);
03048     return rc;
03049 }
03050 
03051 #define _PATH   "/bin:/usr/bin:/sbin:/usr/sbin"
03052 /*@unchecked@*/ /*@observer@*/
03053 static const char *_path = _PATH;
03054 
03055 #define alloca_strdup(_s)       strcpy(alloca(strlen(_s)+1), (_s))
03056 
03057 int rpmioAccess(const char * FN, const char * path, int mode)
03058 {
03059     char fn[4096];
03060     char * bn;
03061     char * r, * re;
03062     char * t, * te;
03063     int negate = 0;
03064     int rc = 0;
03065 
03066     /* Empty paths are always accessible. */
03067     if (FN == NULL || *FN == '\0')
03068         return 0;
03069 
03070     if (mode == 0)
03071         mode = X_OK;
03072 
03073     /* Strip filename out of its name space wrapper. */
03074     bn = alloca_strdup(FN);
03075     for (t = bn; t && *t; t++) {
03076         if (*t != '(')
03077             continue;
03078         *t++ = '\0';
03079 
03080         /* Permit negation on name space tests. */
03081         if (*bn == '!') {
03082             negate = 1;
03083             bn++;
03084         }
03085 
03086         /* Set access flags from name space marker. */
03087         if (strlen(bn) == 3
03088          && strchr("Rr_", bn[0]) != NULL
03089          && strchr("Ww_", bn[1]) != NULL
03090          && strchr("Xx_", bn[2]) != NULL) {
03091             mode = 0;
03092             if (strchr("Rr", bn[0]) != NULL)
03093                 mode |= R_OK;
03094             if (strchr("Ww", bn[1]) != NULL)
03095                 mode |= W_OK;
03096             if (strchr("Xx", bn[2]) != NULL)
03097                 mode |= X_OK;
03098             if (mode == 0)
03099                 mode = F_OK;
03100         } else if (!strcmp(bn, "exists"))
03101             mode = F_OK;
03102         else if (!strcmp(bn, "executable"))
03103             mode = X_OK;
03104         else if (!strcmp(bn, "readable"))
03105             mode = R_OK;
03106         else if (!strcmp(bn, "writable"))
03107             mode = W_OK;
03108 
03109         bn = t;
03110         te = bn + strlen(t) - 1;
03111         if (*te != ')')         /* XXX syntax error, never exists */
03112             return 1;
03113         *te = '\0';
03114         break;
03115     }
03116 
03117     /* Empty paths are always accessible. */
03118     if (*bn == '\0')
03119         goto exit;
03120 
03121     /* Check absolute path for access. */
03122     if (*bn == '/') {
03123         rc = (Access(bn, mode) != 0 ? 1 : 0);
03124 if (_rpmio_debug)
03125 fprintf(stderr, "*** rpmioAccess(\"%s\", 0x%x) rc %d\n", bn, mode, rc);
03126         goto exit;
03127     }
03128 
03129     /* Find path to search. */
03130     if (path == NULL)
03131         path = getenv("PATH");
03132     if (path == NULL)
03133         path = _path;
03134     if (path == NULL) {
03135         rc = 1;
03136         goto exit;
03137     }
03138 
03139     /* Look for relative basename on PATH. */
03140     for (r = alloca_strdup(path); r != NULL && *r != '\0'; r = re) {
03141 
03142         /* Find next element, terminate current element. */
03143         for (re = r; (re = strchr(re, ':')) != NULL; re++) {
03144             if (!(re[1] == '/' && re[2] == '/'))
03145                 /*@innerbreak@*/ break;
03146         }
03147         if (re && *re == ':')
03148             *re++ = '\0';
03149         else
03150             re = r + strlen(r);
03151 
03152         /* Expand ~/ to $HOME/ */
03153         fn[0] = '\0';
03154         t = fn;
03155         *t = '\0';      /* XXX redundant. */
03156         if (r[0] == '~' && r[1] == '/') {
03157             const char * home = getenv("HOME");
03158             if (home == NULL)   /* XXX No HOME? */
03159                 continue;
03160             if (strlen(home) > (sizeof(fn) - strlen(r))) /* XXX too big */
03161                 continue;
03162             t = stpcpy(t, home);
03163             r++;        /* skip ~ */
03164         }
03165         t = stpcpy(t, r);
03166         if (t[-1] != '/' && *bn != '/')
03167             *t++ = '/';
03168         t = stpcpy(t, bn);
03169         t = rpmCleanPath(fn);
03170         if (t == NULL)  /* XXX can't happen */
03171             continue;
03172 
03173         /* Check absolute path for access. */
03174         rc = (Access(t, mode) != 0 ? 1 : 0);
03175 if (_rpmio_debug)
03176 fprintf(stderr, "*** rpmioAccess(\"%s\", 0x%x) rc %d\n", t, mode, rc);
03177         if (rc == 0)
03178             goto exit;
03179     }
03180 
03181     rc = 1;
03182 
03183 exit:
03184     if (negate)
03185         rc ^= 1;
03186     return rc;
03187 }
03188 
03189 #if defined(WITH_NSS) && !defined(__LCLINT__)   /* XXX TODO: add nssDestroy */
03190 /*@-exportheader@*/
03191 extern void NSS_Shutdown(void);
03192 /*@=exportheader@*/
03193 
03194 /*@unchecked@*/
03195 int _rpmnss_init = 0;
03196 #endif
03197 
03198 void rpmioClean(void)
03199 {
03200 /*@-nestedextern@*/
03201     extern rpmioPool _avxPool;
03202     extern rpmioPool _urlPool;
03203     extern rpmioPool _xarPool;
03204     extern rpmioPool _digPool;
03205     extern rpmioPool _rpmiobPool;
03206     extern rpmioPool _rpmvcPool;
03207     extern rpmioPool _rpmvtPool;
03208 /*@-shadow@*/
03209     extern rpmioPool _mirePool;
03210     extern rpmioPool _rpmbfPool;
03211     extern rpmioPool _rpmhkpPool;
03212     extern rpmioPool _htmlPool;
03213     extern rpmioPool _htPool;
03214     extern rpmioPool _ctxPool;
03215     extern rpmioPool _rpmsmPool;
03216     extern rpmioPool _rpmspPool;
03217     extern rpmioPool _rpmsxPool;
03218     extern rpmioPool _rpmsyckPool;
03219 /*@=shadow@*/
03220 
03221     extern rpmioPool _rpmbagPool;
03222 
03223     extern rpmioPool _rpmaugPool;
03224     extern rpmioPool _rpmcudfPool;
03225     extern rpmioPool _rpmficlPool;
03226     extern rpmioPool _rpmjsPool;
03227     extern rpmioPool _rpmluavPool;
03228     extern rpmioPool _rpmluaPool;
03229     extern rpmioPool _rpmmgPool;
03230 #ifdef  NOTYET
03231     extern rpmioPool _rpmnixPool;
03232 #endif
03233     extern rpmioPool _rpmperlPool;
03234     extern rpmioPool _rpmpythonPool;
03235     extern rpmioPool _rpmrubyPool;
03236     extern rpmioPool _rpmsqlPool;
03237     extern rpmioPool _rpmsquirrelPool;
03238     extern rpmioPool _rpmtclPool;
03239 /*@=nestedextern@*/
03240 
03241 #if defined(WITH_LUA)
03242     (void) rpmluaFree(NULL);
03243 #endif
03244 #if defined(WITH_NEON)
03245     davDestroy();
03246 #endif
03247 #if defined(WITH_NSS) && !defined(__LCLINT__)
03248     if (_rpmnss_init) {
03249         (void) NSS_Shutdown();
03250         _rpmnss_init = 0;
03251     }
03252 #endif
03253     urlFreeCache();
03254 
03255     _rpmtclI = rpmtclFree(_rpmtclI);
03256     _rpmtclPool = rpmioFreePool(_rpmtclPool);
03257     _rpmsquirrelI = rpmsquirrelFree(_rpmsquirrelI);
03258     _rpmsquirrelPool = rpmioFreePool(_rpmsquirrelPool);
03259     _rpmsqlI = rpmsqlFree(_rpmsqlI);
03260     _rpmsqlPool = rpmioFreePool(_rpmsqlPool);
03261     _rpmrubyI = rpmrubyFree(_rpmrubyI);
03262     _rpmrubyPool = rpmioFreePool(_rpmrubyPool);
03263     _rpmpythonI = rpmpythonFree(_rpmpythonI);
03264     _rpmpythonPool = rpmioFreePool(_rpmpythonPool);
03265     _rpmperlI = rpmperlFree(_rpmperlI);
03266     _rpmperlPool = rpmioFreePool(_rpmperlPool);
03267     _rpmjsI = rpmjsFree(_rpmjsI);
03268     _rpmjsPool = rpmioFreePool(_rpmjsPool);
03269     _rpmficlI = rpmficlFree(_rpmficlI);
03270     _rpmficlPool = rpmioFreePool(_rpmficlPool);
03271 
03272     _rpmaugI = rpmaugFree(_rpmaugI);
03273     _rpmaugPool = rpmioFreePool(_rpmaugPool);
03274 
03275     _rpmbagPool = rpmioFreePool(_rpmbagPool);
03276 
03277 #ifdef  NOTYET  /* XXX FIXME: dig out the recursion deadlock. */
03278     _rpmnixI = rpmnixFree(_rpmnixI);
03279     _rpmnixPool = rpmioFreePool(_rpmnixPool);
03280 #endif
03281 
03282     _rpmcudfPool = rpmioFreePool(_rpmcudfPool);
03283     _rpmluavPool = rpmioFreePool(_rpmluavPool);
03284     _rpmluaPool = rpmioFreePool(_rpmluaPool);
03285 
03286     _rpmhkpI = rpmhkpFree(_rpmhkpI);
03287     _rpmhkpPool = rpmioFreePool(_rpmhkpPool);
03288     _rpmhkp_awol.bf = rpmbfFree(_rpmhkp_awol.bf);
03289     _rpmhkp_crl.bf = rpmbfFree(_rpmhkp_crl.bf);
03290 
03291     _rpmvcPool = rpmioFreePool(_rpmvcPool);
03292     _rpmvtPool = rpmioFreePool(_rpmvtPool);
03293 
03294     _rpmsmI = rpmsmFree(_rpmsmI);
03295     _rpmsmPool = rpmioFreePool(_rpmsmPool);
03296     _rpmspPool = rpmioFreePool(_rpmspPool);
03297     _rpmsxI = rpmsxFree(_rpmsxI);
03298     _rpmsxPool = rpmioFreePool(_rpmsxPool);
03299 
03300     _htmlPool = rpmioFreePool(_htmlPool);
03301     _mirePool = rpmioFreePool(_mirePool);
03302     _rpmmgPool = rpmioFreePool(_rpmmgPool);
03303     _rpmbfPool = rpmioFreePool(_rpmbfPool);
03304     _htPool = rpmioFreePool(_htPool);
03305     _ctxPool = rpmioFreePool(_ctxPool);
03306     _rpmsyckPool = rpmioFreePool(_rpmsyckPool);
03307     _rpmiobPool = rpmioFreePool(_rpmiobPool);
03308     _digPool = rpmioFreePool(_digPool);
03309     _xarPool = rpmioFreePool(_xarPool);
03310     _avxPool = rpmioFreePool(_avxPool);
03311     _urlPool = rpmioFreePool(_urlPool);
03312     _fdPool = rpmioFreePool(_fdPool);
03313 
03314     rpmlogClose();
03315 }
03316 
03317 /*@-type@*/ /* LCL: function typedefs */
03318 static struct FDIO_s fpio_s = {
03319   ufdRead, ufdWrite, fdSeek, ufdClose, NULL, NULL, NULL,
03320 };
03321 /*@=type@*/
03322 
03323 FDIO_t fpio = /*@-compmempass@*/ &fpio_s /*@=compmempass@*/ ;