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