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

Generated on Wed Mar 13 15:34:51 2002 for rpm by doxygen1.2.14 written by Dimitri van Heesch, © 1997-2002