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

rpmio/rpmio.c

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

Generated on Sun Oct 26 13:02:03 2003 for rpm by doxygen1.2.18