rpm 5.2.1

- Main Page
- Related Pages
- Modules
- Data Structures
- Files

- File List
- Globals

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