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

rpmio/rpmio.c

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

Generated at Thu Sep 6 11:32:32 2001 for rpm by doxygen1.2.8.1 written by Dimitri van Heesch, © 1997-2001