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

rpmio/rpmio.c

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

Generated on Tue Dec 21 15:36:59 2004 for rpm by doxygen1.2.18