Main Page | Modules | Data Structures | File List | Data Fields | Globals | Related Pages

rpmio/rpmio.c

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

Generated on Tue Jun 12 17:56:14 2007 for rpm by doxygen 1.3.5