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

Generated on Sun Feb 2 23:32:05 2003 for rpm by doxygen1.2.18