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

rpmio/rpmio.c

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

Generated on Fri Apr 4 14:39:42 2003 for rpm by doxygen1.2.14 written by Dimitri van Heesch, © 1997-2002