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

rpmio/rpmio.c

Go to the documentation of this file.
00001 
00005 #include "system.h"
00006 #include <stdarg.h>
00007 
00008 #ifdef  __LCLINT__
00009 typedef unsigned int            uint32_t;
00010 #define INADDR_ANY              ((uint32_t) 0x00000000)
00011 #define IPPROTO_IP              0
00012 
00013 #else   /* __LCLINT__ */
00014 
00015 #if HAVE_MACHINE_TYPES_H
00016 # include <machine/types.h>
00017 #endif
00018 
00019 #include <netinet/in.h>
00020 #include <arpa/inet.h>          /* XXX for inet_aton and HP-UX */
00021 
00022 #if HAVE_NETINET_IN_SYSTM_H
00023 # include <sys/types.h>
00024 # include <netinet/in_systm.h>
00025 #endif
00026 
00027 #if HAVE_LIBIO_H && defined(_G_IO_IO_FILE_VERSION)
00028 #define _USE_LIBIO      1
00029 #endif
00030 
00031 #endif  /* __LCLINT__ */
00032 
00033 #if !defined(HAVE_HERRNO) && defined(__hpux) /* XXX HP-UX w/o -D_XOPEN_SOURCE needs */
00034 extern int h_errno;
00035 #endif
00036 
00037 #ifndef IPPORT_FTP
00038 #define IPPORT_FTP      21
00039 #endif
00040 #ifndef IPPORT_HTTP
00041 #define IPPORT_HTTP     80
00042 #endif
00043 
00044 #if !defined(HAVE_INET_ATON)
00045 static int inet_aton(const char *cp, struct in_addr *inp)
00046 {
00047     long addr;
00048 
00049     addr = inet_addr(cp);
00050     if (addr == ((long) -1)) return 0;
00051 
00052     memcpy(inp, &addr, sizeof(addr));
00053     return 1;
00054 }
00055 #endif
00056 
00057 #if defined(USE_ALT_DNS) && USE_ALT_DNS
00058 #include "dns.h"
00059 #endif
00060 
00061 #include <rpmio_internal.h>
00062 
00063 #include "ugid.h"
00064 #include "rpmmessages.h"
00065 
00066 #include "debug.h"
00067 
00068 /*@access urlinfo @*/
00069 /*@access FDSTAT_t @*/
00070 
00071 #define FDNREFS(fd)     (fd ? ((FD_t)fd)->nrefs : -9)
00072 #define FDTO(fd)        (fd ? ((FD_t)fd)->rd_timeoutsecs : -99)
00073 #define FDCPIOPOS(fd)   (fd ? ((FD_t)fd)->fd_cpioPos : -99)
00074 
00075 #define FDONLY(fd)      assert(fdGetIo(fd) == fdio)
00076 #define GZDONLY(fd)     assert(fdGetIo(fd) == gzdio)
00077 #define BZDONLY(fd)     assert(fdGetIo(fd) == bzdio)
00078 
00079 #define UFDONLY(fd)     /* assert(fdGetIo(fd) == ufdio) */
00080 
00081 #define fdGetFILE(_fd)  ((FILE *)fdGetFp(_fd))
00082 
00083 #if _USE_LIBIO
00084 int noLibio = 0;
00085 #else
00086 int noLibio = 1;
00087 #endif
00088 
00089 #define TIMEOUT_SECS 60
00090 static int ftpTimeoutSecs = TIMEOUT_SECS;
00091 static int httpTimeoutSecs = TIMEOUT_SECS;
00092 
00093 int _ftp_debug = 0;
00094 int _rpmio_debug = 0;
00095 
00096 /* =============================================================== */
00097 
00098 static /*@observer@*/ const char * fdbg(FD_t fd)
00099 {
00100     static char buf[BUFSIZ];
00101     char *be = buf;
00102     int i;
00103 
00104 #if DYING
00105     sprintf(be, "fd %p", fd);   be += strlen(be);
00106     if (fd->rd_timeoutsecs >= 0) {
00107         sprintf(be, " secs %d", fd->rd_timeoutsecs);
00108         be += strlen(be);
00109     }
00110 #endif
00111     if (fd->bytesRemain != -1) {
00112         sprintf(be, " clen %d", (int)fd->bytesRemain);
00113         be += strlen(be);
00114      }
00115     if (fd->wr_chunked) {
00116         strcpy(be, " chunked");
00117         be += strlen(be);
00118      }
00119     *be++ = '\t';
00120     for (i = fd->nfps; i >= 0; i--) {
00121         FDSTACK_t * fps = &fd->fps[i];
00122         if (i != fd->nfps)
00123             *be++ = ' ';
00124         *be++ = '|';
00125         *be++ = ' ';
00126         if (fps->io == fdio) {
00127             sprintf(be, "FD %d fp %p", fps->fdno, fps->fp);
00128         } else if (fps->io == ufdio) {
00129             sprintf(be, "UFD %d fp %p", fps->fdno, fps->fp);
00130         } else if (fps->io == fadio) {
00131             sprintf(be, "FAD %d fp %p", fps->fdno, fps->fp);
00132         } else if (fps->io == gzdio) {
00133             sprintf(be, "GZD %p fdno %d", fps->fp, fps->fdno);
00134 #if HAVE_BZLIB_H
00135         } else if (fps->io == bzdio) {
00136             sprintf(be, "BZD %p fdno %d", fps->fp, fps->fdno);
00137 #endif
00138         } else if (fps->io == fpio) {
00139             /*@+voidabstract@*/
00140             sprintf(be, "%s %p(%d) fdno %d",
00141                 (fps->fdno < 0 ? "LIBIO" : "FP"),
00142                 fps->fp, fileno(((FILE *)fps->fp)), fps->fdno);
00143             /*@=voidabstract@*/
00144         } else {
00145             sprintf(be, "??? io %p fp %p fdno %d ???",
00146                 fps->io, fps->fp, fps->fdno);
00147         }
00148         be += strlen(be);
00149         *be = '\0';
00150     }
00151     return buf;
00152 }
00153 
00154 /* =============================================================== */
00155 off_t fdSize(FD_t fd) {
00156     struct stat sb;
00157     off_t rc = -1; 
00158 
00159 #ifdef  NOISY
00160 DBGIO(0, (stderr, "==>\tfdSize(%p) rc %ld\n", fd, (long)rc));
00161 #endif
00162     FDSANE(fd);
00163     if (fd->contentLength >= 0)
00164         rc = fd->contentLength;
00165     else switch (fd->urlType) {
00166     case URL_IS_PATH:
00167     case URL_IS_UNKNOWN:
00168         if (fstat(Fileno(fd), &sb) == 0)
00169             rc = sb.st_size;
00170         /*@fallthrough@*/
00171     case URL_IS_FTP:
00172     case URL_IS_HTTP:
00173     case URL_IS_DASH:
00174         break;
00175     }
00176     return rc;
00177 }
00178 
00179 FD_t fdDup(int fdno) {
00180     FD_t fd;
00181     int nfdno;
00182 
00183     if ((nfdno = dup(fdno)) < 0)
00184         return NULL;
00185     fd = fdNew("open (fdDup)");
00186     fdSetFdno(fd, nfdno);
00187 DBGIO(fd, (stderr, "==> fdDup(%d) fd %p %s\n", fdno, fd, fdbg(fd)));
00188     /*@-refcounttrans@*/ return fd; /*@=refcounttrans@*/
00189 }
00190 
00191 static inline /*@unused@*/ int fdSeekNot(void * cookie,  /*@unused@*/ _libio_pos_t pos,  /*@unused@*/ int whence) {
00192     FD_t fd = c2f(cookie);
00193     FDSANE(fd);         /* XXX keep gcc quiet */
00194     return -2;
00195 }
00196 
00197 #ifdef UNUSED
00198 FILE *fdFdopen(void * cookie, const char *fmode) {
00199     FD_t fd = c2f(cookie);
00200     int fdno;
00201     FILE * fp;
00202 
00203     if (fmode == NULL) return NULL;
00204     fdno = fdFileno(fd);
00205     if (fdno < 0) return NULL;
00206     fp = fdopen(fdno, fmode);
00207 DBGIO(fd, (stderr, "==> fdFdopen(%p,\"%s\") fdno %d -> fp %p fdno %d\n", cookie, fmode, fdno, fp, fileno(fp)));
00208     fd = fdFree(fd, "open (fdFdopen)");
00209     return fp;
00210 }
00211 #endif
00212 
00213 #if 0
00214 #undef  fdLink
00215 #undef  fdFree
00216 #undef  fdNew
00217 #endif
00218 
00219 /* =============================================================== */
00220 static inline FD_t XfdLink(void * cookie, const char *msg, const char *file, unsigned line) {
00221     FD_t fd;
00222 if (cookie == NULL)
00223 DBGREFS(0, (stderr, "--> fd  %p ++ %d %s at %s:%u\n", cookie, FDNREFS(cookie)+1, msg, file, line));
00224     fd = c2f(cookie);
00225     if (fd) {
00226         fd->nrefs++;
00227 DBGREFS(fd, (stderr, "--> fd  %p ++ %d %s at %s:%u %s\n", fd, fd->nrefs, msg, file, line, fdbg(fd)));
00228     }
00229     return fd;
00230 }
00231 
00232 static inline /*@null@*/ FD_t XfdFree( /*@killref@*/ FD_t fd, const char *msg, const char *file, unsigned line) {
00233 if (fd == NULL)
00234 DBGREFS(0, (stderr, "--> fd  %p -- %d %s at %s:%u\n", fd, FDNREFS(fd), msg, file, line));
00235     FDSANE(fd);
00236     if (fd) {
00237 DBGREFS(fd, (stderr, "--> fd  %p -- %d %s at %s:%u %s\n", fd, fd->nrefs, msg, file, line, fdbg(fd)));
00238         if (--fd->nrefs > 0)
00239             /*@-refcounttrans@*/ return fd; /*@=refcounttrans@*/
00240         if (fd->stats) free(fd->stats);
00241         if (fd->digest) free(fd->digest);
00242         /*@-refcounttrans@*/ free(fd); /*@=refcounttrans@*/
00243     }
00244     return NULL;
00245 }
00246 
00247 static inline /*@null@*/ FD_t XfdNew(const char *msg, const char *file, unsigned line) {
00248     FD_t fd = (FD_t) xmalloc(sizeof(struct _FD_s));
00249     if (fd == NULL)
00250         return NULL;
00251     fd->nrefs = 0;
00252     fd->flags = 0;
00253     fd->magic = FDMAGIC;
00254     fd->urlType = URL_IS_UNKNOWN;
00255 
00256     fd->nfps = 0;
00257     memset(fd->fps, 0, sizeof(fd->fps));
00258 
00259     fd->fps[0].io = fdio;
00260     fd->fps[0].fp = NULL;
00261     fd->fps[0].fdno = -1;
00262 
00263     fd->url = NULL;
00264     fd->rd_timeoutsecs = 1;     /* XXX default value used to be -1 */
00265     fd->contentLength = fd->bytesRemain = -1;
00266     fd->wr_chunked = 0;
00267     fd->syserrno = 0;
00268     fd->errcookie = NULL;
00269     fd->stats = xcalloc(1, sizeof(*fd->stats));
00270     fd->digest = NULL;
00271     gettimeofday(&fd->stats->create, NULL);
00272     fd->stats->begin = fd->stats->create;       /* structure assignment */
00273 
00274     fd->ftpFileDoneNeeded = 0;
00275     fd->firstFree = 0;
00276     fd->fileSize = 0;
00277     fd->fd_cpioPos = 0;
00278 
00279     return XfdLink(fd, msg, file, line);
00280 }
00281 
00282 ssize_t fdRead(void * cookie, /*@out@*/ char * buf, size_t count) {
00283     FD_t fd = c2f(cookie);
00284     ssize_t rc;
00285 
00286     if (fd->bytesRemain == 0) return 0; /* XXX simulate EOF */
00287 
00288     fdstat_enter(fd, FDSTAT_READ);
00289     rc = read(fdFileno(fd), buf, (count > fd->bytesRemain ? fd->bytesRemain : count));
00290     fdstat_exit(fd, FDSTAT_READ, rc);
00291 
00292     if (fd->digest && rc > 0) rpmDigestUpdate(fd->digest, buf, rc);
00293 
00294 DBGIO(fd, (stderr, "==>\tfdRead(%p,%p,%ld) rc %ld %s\n", cookie, buf, (long)count, (long)rc, fdbg(fd)));
00295 
00296     return rc;
00297 }
00298 
00299 ssize_t fdWrite(void * cookie, const char * buf, size_t count) {
00300     FD_t fd = c2f(cookie);
00301     int fdno = fdFileno(fd);
00302     ssize_t rc;
00303 
00304     if (fd->bytesRemain == 0) return 0; /* XXX simulate EOF */
00305 
00306     if (fd->digest && count > 0) rpmDigestUpdate(fd->digest, buf, count);
00307 
00308     if (fd->wr_chunked) {
00309         char chunksize[20];
00310         sprintf(chunksize, "%x\r\n", (unsigned)count);
00311         rc = write(fdno, chunksize, strlen(chunksize));
00312         if (rc == -1)   fd->syserrno = errno;
00313     }
00314     if (count == 0) return 0;
00315 
00316     fdstat_enter(fd, FDSTAT_WRITE);
00317     rc = write(fdno, buf, (count > fd->bytesRemain ? fd->bytesRemain : count));
00318     fdstat_exit(fd, FDSTAT_WRITE, rc);
00319 
00320     if (fd->wr_chunked) {
00321         int ec;
00322         ec = write(fdno, "\r\n", sizeof("\r\n")-1);
00323         if (ec == -1)   fd->syserrno = errno;
00324     }
00325 
00326 DBGIO(fd, (stderr, "==>\tfdWrite(%p,%p,%ld) rc %ld %s\n", cookie, buf, (long)count, (long)rc, fdbg(fd)));
00327 
00328     return rc;
00329 }
00330 
00331 static inline int fdSeek(void * cookie, _libio_pos_t pos, int whence) {
00332 #ifdef USE_COOKIE_SEEK_POINTER
00333     _IO_off64_t p = *pos;
00334 #else
00335     off_t p = pos;
00336 #endif
00337     FD_t fd = c2f(cookie);
00338     off_t rc;
00339 
00340     assert(fd->bytesRemain == -1);      /* XXX FIXME fadio only for now */
00341     fdstat_enter(fd, FDSTAT_SEEK);
00342     rc = lseek(fdFileno(fd), p, whence);
00343     fdstat_exit(fd, FDSTAT_SEEK, rc);
00344 
00345 DBGIO(fd, (stderr, "==>\tfdSeek(%p,%ld,%d) rc %lx %s\n", cookie, (long)p, whence, (long)rc, fdbg(fd)));
00346 
00347     return rc;
00348 }
00349 
00350 int fdClose( /*@only@*/ void * cookie) {
00351     FD_t fd;
00352     int fdno;
00353     int rc;
00354 
00355     if (cookie == NULL) return -2;
00356     fd = c2f(cookie);
00357     fdno = fdFileno(fd);
00358 
00359     fdSetFdno(fd, -1);
00360 
00361     fdstat_enter(fd, FDSTAT_CLOSE);
00362     rc = ((fdno >= 0) ? close(fdno) : -2);
00363     fdstat_exit(fd, FDSTAT_CLOSE, rc);
00364 
00365 DBGIO(fd, (stderr, "==>\tfdClose(%p) rc %lx %s\n", fd, (long)rc, fdbg(fd)));
00366 
00367     fd = fdFree(fd, "open (fdClose)");
00368     return rc;
00369 }
00370 
00371 /*@null@*/ FD_t fdOpen(const char *path, int flags, mode_t mode) {
00372     FD_t fd;
00373     int fdno;
00374 
00375     fdno = open(path, flags, mode);
00376     if (fdno < 0) return NULL;
00377     fd = fdNew("open (fdOpen)");
00378     fdSetFdno(fd, fdno);
00379     fd->flags = flags;
00380 DBGIO(fd, (stderr, "==>\tfdOpen(\"%s\",%x,0%o) %s\n", path, flags, (unsigned)mode, fdbg(fd)));
00381     /*@-refcounttrans@*/ return fd; /*@=refcounttrans@*/
00382 }
00383 
00384 static struct FDIO_s fdio_s = {
00385   fdRead, fdWrite, fdSeek, fdClose, XfdLink, XfdFree, XfdNew, fdFileno,
00386   fdOpen, NULL, fdGetFp, NULL,  mkdir, chdir, rmdir, rename, unlink
00387 };
00388 FDIO_t fdio = /*@-compmempass@*/ &fdio_s /*@=compmempass@*/ ;
00389 
00390 FDIO_t fadio;   /* XXX usually NULL, filled in when linked with rpm */
00391 
00392 int fdWritable(FD_t fd, int secs)
00393 {
00394     int fdno;
00395     fd_set wrfds;
00396     struct timeval timeout, *tvp = (secs >= 0 ? &timeout : NULL);
00397     int rc;
00398         
00399     if ((fdno = fdFileno(fd)) < 0)
00400         return -1;      /* XXX W2DO? */
00401         
00402     FD_ZERO(&wrfds);
00403     do {
00404         FD_SET(fdno, &wrfds);
00405 
00406         if (tvp) {
00407             tvp->tv_sec = secs;
00408             tvp->tv_usec = 0;
00409         }
00410         errno = 0;
00411         rc = select(fdno + 1, NULL, &wrfds, NULL, tvp);
00412 
00413 if (_rpmio_debug && !(rc == 1 && errno == 0))
00414 fprintf(stderr, "*** fdWritable fdno %d rc %d %s\n", fdno, rc, strerror(errno));
00415         if (rc < 0) {
00416             switch (errno) {
00417             case EINTR:
00418                 continue;
00419                 /*@notreached@*/ break;
00420             default:
00421                 return rc;
00422                 /*@notreached@*/ break;
00423             }
00424         }
00425         return rc;
00426     } while (1);
00427     /*@notreached@*/
00428 }
00429 
00430 int fdReadable(FD_t fd, int secs)
00431 {
00432     int fdno;
00433     fd_set rdfds;
00434     struct timeval timeout, *tvp = (secs >= 0 ? &timeout : NULL);
00435     int rc;
00436 
00437     if ((fdno = fdFileno(fd)) < 0)
00438         return -1;      /* XXX W2DO? */
00439         
00440     FD_ZERO(&rdfds);
00441     do {
00442         FD_SET(fdno, &rdfds);
00443 
00444         if (tvp) {
00445             tvp->tv_sec = secs;
00446             tvp->tv_usec = 0;
00447         }
00448         errno = 0;
00449         rc = select(fdno + 1, &rdfds, NULL, NULL, tvp);
00450 
00451         if (rc < 0) {
00452             switch (errno) {
00453             case EINTR:
00454                 continue;
00455                 /*@notreached@*/ break;
00456             default:
00457                 return rc;
00458                 /*@notreached@*/ break;
00459             }
00460         }
00461         return rc;
00462     } while (1);
00463     /*@notreached@*/
00464 }
00465 
00466 int fdFgets(FD_t fd, char * buf, size_t len)
00467 {
00468     int fdno;
00469     int secs = fd->rd_timeoutsecs;
00470     size_t nb = 0;
00471     int ec = 0;
00472     char lastchar = '\0';
00473 
00474     if ((fdno = fdFileno(fd)) < 0)
00475         return 0;       /* XXX W2DO? */
00476         
00477     do {
00478         int rc;
00479 
00480         /* Is there data to read? */
00481         rc = fdReadable(fd, secs);
00482 
00483         switch (rc) {
00484         case -1:        /* error */
00485             ec = -1;
00486             continue;
00487             /*@notreached@*/ break;
00488         case  0:        /* timeout */
00489             ec = -1;
00490             continue;
00491             /*@notreached@*/ break;
00492         default:        /* data to read */
00493             break;
00494         }
00495 
00496         errno = 0;
00497 #ifdef  NOISY
00498         rc = fdRead(fd, buf + nb, 1);
00499 #else
00500         rc = read(fdFileno(fd), buf + nb, 1);
00501 #endif
00502         if (rc < 0) {
00503             fd->syserrno = errno;
00504             switch (errno) {
00505             case EWOULDBLOCK:
00506                 continue;
00507                 /*@notreached@*/ break;
00508             default:
00509                 break;
00510             }
00511 if (_rpmio_debug)
00512 fprintf(stderr, "*** read: fd %p rc %d errno %d %s \"%s\"\n", fd, rc, errno, strerror(errno), buf);
00513             ec = -1;
00514             break;
00515         } else if (rc == 0) {
00516 if (_rpmio_debug)
00517 fprintf(stderr, "*** read: fd %p rc %d EOF errno %d %s \"%s\"\n", fd, rc, errno, strerror(errno), buf);
00518             break;
00519         } else {
00520             nb += rc;
00521             buf[nb] = '\0';
00522             lastchar = buf[nb - 1];
00523         }
00524     } while (ec == 0 && nb < len && lastchar != '\n');
00525 
00526     return (ec >= 0 ? nb : ec);
00527 }
00528 
00529 /* =============================================================== */
00530 /* Support for FTP/HTTP I/O.
00531  */
00532 const char *const ftpStrerror(int errorNumber) {
00533   switch (errorNumber) {
00534     case 0:
00535         return _("Success");
00536 
00537     case FTPERR_BAD_SERVER_RESPONSE:
00538         return _("Bad server response");
00539 
00540     case FTPERR_SERVER_IO_ERROR:
00541         return _("Server I/O error");
00542 
00543     case FTPERR_SERVER_TIMEOUT:
00544         return _("Server timeout");
00545 
00546     case FTPERR_BAD_HOST_ADDR:
00547         return _("Unable to lookup server host address");
00548 
00549     case FTPERR_BAD_HOSTNAME:
00550         return _("Unable to lookup server host name");
00551 
00552     case FTPERR_FAILED_CONNECT:
00553         return _("Failed to connect to server");
00554 
00555     case FTPERR_FAILED_DATA_CONNECT:
00556         return _("Failed to establish data connection to server");
00557 
00558     case FTPERR_FILE_IO_ERROR:
00559         return _("I/O error to local file");
00560 
00561     case FTPERR_PASSIVE_ERROR:
00562         return _("Error setting remote server to passive mode");
00563 
00564     case FTPERR_FILE_NOT_FOUND:
00565         return _("File not found on server");
00566 
00567     case FTPERR_NIC_ABORT_IN_PROGRESS:
00568         return _("Abort in progress");
00569 
00570     case FTPERR_UNKNOWN:
00571     default:
00572         return _("Unknown or unexpected error");
00573   }
00574 }
00575 
00576 const char *urlStrerror(const char *url)
00577 {
00578     const char *retstr;
00579     switch (urlIsURL(url)) {
00580     case URL_IS_FTP:
00581     case URL_IS_HTTP:
00582     {   urlinfo u;
00583 /* XXX This only works for httpReq/ftpLogin/ftpReq failures */
00584         if (urlSplit(url, &u) == 0) {
00585             retstr = ftpStrerror(u->openError);
00586         } else
00587             retstr = "Malformed URL";
00588     }   break;
00589     default:
00590         retstr = strerror(errno);
00591         break;
00592     }
00593     return retstr;
00594 }
00595 
00596 #if !defined(USE_ALT_DNS) || !USE_ALT_DNS 
00597 static int mygethostbyname(const char * host, struct in_addr * address)
00598 {
00599     struct hostent * hostinfo;
00600 
00601     hostinfo = /*@-unrecog@*/ gethostbyname(host) /*@=unrecog@*/;
00602     if (!hostinfo) return 1;
00603 
00604     memcpy(address, hostinfo->h_addr_list[0], sizeof(*address));
00605     return 0;
00606 }
00607 #endif
00608 
00609 static int getHostAddress(const char * host, struct in_addr * address)
00610 {
00611     if (isdigit(host[0])) {
00612       if (! /*@-unrecog@*/ inet_aton(host, address) /*@=unrecog@*/ ) {
00613           return FTPERR_BAD_HOST_ADDR;
00614       }
00615     } else {
00616       if (mygethostbyname(host, address)) {
00617           errno = h_errno;
00618           return FTPERR_BAD_HOSTNAME;
00619       }
00620     }
00621     
00622     return 0;
00623 }
00624 
00625 static int tcpConnect(FD_t ctrl, const char *host, int port)
00626 {
00627     struct sockaddr_in sin;
00628     int fdno = -1;
00629     int rc;
00630 
00631     sin.sin_family = AF_INET;
00632     sin.sin_port = htons(port);
00633     sin.sin_addr.s_addr = INADDR_ANY;
00634     
00635   do {
00636     if ((rc = getHostAddress(host, &sin.sin_addr)) < 0)
00637         break;
00638 
00639     if ((fdno = socket(sin.sin_family, SOCK_STREAM, IPPROTO_IP)) < 0) {
00640         rc = FTPERR_FAILED_CONNECT;
00641         break;
00642     }
00643 
00644     if (connect(fdno, (struct sockaddr *) &sin, sizeof(sin))) {
00645         rc = FTPERR_FAILED_CONNECT;
00646         break;
00647     }
00648   } while (0);
00649 
00650     if (rc < 0)
00651         goto errxit;
00652 
00653 if (_ftp_debug)
00654 fprintf(stderr,"++ connect %s:%d on fdno %d\n",
00655 /*@-unrecog@*/ inet_ntoa(sin.sin_addr) /*@=unrecog@*/ ,
00656 ntohs(sin.sin_port), fdno);
00657 
00658     fdSetFdno(ctrl, (fdno >= 0 ? fdno : -1));
00659     return 0;
00660 
00661 errxit:
00662     fdSetSyserrno(ctrl, errno, ftpStrerror(rc));
00663     if (fdno >= 0)
00664         close(fdno);
00665     return rc;
00666 }
00667 
00668 static int checkResponse(void * uu, FD_t ctrl, /*@out@*/ int *ecp, /*@out@*/ char ** str)
00669 {
00670     urlinfo u = uu;
00671     char *buf;
00672     size_t bufAlloced;
00673     int bufLength = 0; 
00674     const char *s;
00675     char *se;
00676     int ec = 0;
00677     int moretodo = 1;
00678     char errorCode[4];
00679  
00680     URLSANE(u);
00681     if (u->bufAlloced == 0 || u->buf == NULL) {
00682         u->bufAlloced = url_iobuf_size;
00683         u->buf = xcalloc(u->bufAlloced, sizeof(char));
00684     }
00685     buf = u->buf;
00686     bufAlloced = u->bufAlloced;
00687     *buf = '\0';
00688 
00689     errorCode[0] = '\0';
00690     
00691     do {
00692         int rc;
00693 
00694         /*
00695          * Read next line from server.
00696          */
00697         se = buf + bufLength;
00698         *se = '\0';
00699         rc = fdFgets(ctrl, se, (bufAlloced - bufLength));
00700         if (rc < 0) {
00701             ec = FTPERR_BAD_SERVER_RESPONSE;
00702             continue;
00703         } else if (rc == 0 || fdWritable(ctrl, 0) < 1)
00704             moretodo = 0;
00705 
00706         /*
00707          * Process next line from server.
00708          */
00709         for (s = se; *s != '\0'; s = se) {
00710                 const char *e;
00711 
00712                 while (*se && *se != '\n') se++;
00713 
00714                 if (se > s && se[-1] == '\r')
00715                    se[-1] = '\0';
00716                 if (*se == '\0')
00717                     break;
00718 
00719 if (_ftp_debug)
00720 fprintf(stderr, "<- %s\n", s);
00721 
00722                 /* HTTP: header termination on empty line */
00723                 if (*s == '\0') {
00724                     moretodo = 0;
00725                     break;
00726                 }
00727                 *se++ = '\0';
00728 
00729                 /* HTTP: look for "HTTP/1.1 123 ..." */
00730                 if (!strncmp(s, "HTTP", sizeof("HTTP")-1)) {
00731                     ctrl->contentLength = -1;
00732                     if ((e = strchr(s, '.')) != NULL) {
00733                         e++;
00734                         u->httpVersion = *e - '0';
00735                         if (u->httpVersion < 1 || u->httpVersion > 2)
00736                             ctrl->persist = u->httpVersion = 0;
00737                         else
00738                             ctrl->persist = 1;
00739                     }
00740                     if ((e = strchr(s, ' ')) != NULL) {
00741                         e++;
00742                         if (strchr("0123456789", *e))
00743                             strncpy(errorCode, e, 3);
00744                         errorCode[3] = '\0';
00745                     }
00746                     continue;
00747                 }
00748 
00749                 /* HTTP: look for "token: ..." */
00750                 for (e = s; *e && !(*e == ' ' || *e == ':'); e++)
00751                     ;
00752                 if (e > s && *e++ == ':') {
00753                     size_t ne = (e - s);
00754                     while (*e && *e == ' ') e++;
00755 #if 0
00756                     if (!strncmp(s, "Date:", ne)) {
00757                     } else
00758                     if (!strncmp(s, "Server:", ne)) {
00759                     } else
00760                     if (!strncmp(s, "Last-Modified:", ne)) {
00761                     } else
00762                     if (!strncmp(s, "ETag:", ne)) {
00763                     } else
00764 #endif
00765                     if (!strncmp(s, "Accept-Ranges:", ne)) {
00766                         if (!strcmp(e, "bytes"))
00767                             u->httpHasRange = 1;
00768                         if (!strcmp(e, "none"))
00769                             u->httpHasRange = 0;
00770                     } else
00771                     if (!strncmp(s, "Content-Length:", ne)) {
00772                         if (strchr("0123456789", *e))
00773                             ctrl->contentLength = atoi(e);
00774                     } else
00775                     if (!strncmp(s, "Connection:", ne)) {
00776                         if (!strcmp(e, "close"))
00777                             ctrl->persist = 0;
00778                     }
00779 #if 0
00780                     else
00781                     if (!strncmp(s, "Content-Type:", ne)) {
00782                     } else
00783                     if (!strncmp(s, "Transfer-Encoding:", ne)) {
00784                         if (!strcmp(e, "chunked"))
00785                             ctrl->wr_chunked = 1;
00786                         else
00787                             ctrl->wr_chunked = 0;
00788                     } else
00789                     if (!strncmp(s, "Allow:", ne)) {
00790                     }
00791 #endif
00792                     continue;
00793                 }
00794 
00795                 /* HTTP: look for "<TITLE>501 ... </TITLE>" */
00796                 if (!strncmp(s, "<TITLE>", sizeof("<TITLE>")-1))
00797                     s += sizeof("<TITLE>") - 1;
00798 
00799                 /* FTP: look for "123-" and/or "123 " */
00800                 if (strchr("0123456789", *s)) {
00801                     if (errorCode[0]) {
00802                         if (!strncmp(s, errorCode, sizeof("123")-1) && s[3] == ' ')
00803                             moretodo = 0;
00804                     } else {
00805                         strncpy(errorCode, s, sizeof("123")-1);
00806                         errorCode[3] = '\0';
00807                         if (s[3] != '-')
00808                             moretodo = 0;
00809                     }
00810                 }
00811         }
00812 
00813         if (moretodo && se > s) {
00814             bufLength = se - s - 1;
00815             if (s != buf)
00816                 memmove(buf, s, bufLength);
00817         } else {
00818             bufLength = 0;
00819         }
00820     } while (moretodo && ec == 0);
00821 
00822     if (str)    *str = buf;
00823     if (ecp)    *ecp = atoi(errorCode);
00824 
00825     return ec;
00826 }
00827 
00828 static int ftpCheckResponse(urlinfo u, /*@out@*/ char ** str)
00829 {
00830     int ec = 0;
00831     int rc;
00832 
00833     URLSANE(u);
00834     rc = checkResponse(u, u->ctrl, &ec, str);
00835 
00836     switch (ec) {
00837     case 550:
00838         return FTPERR_FILE_NOT_FOUND;
00839         /*@notreached@*/ break;
00840     case 552:
00841         return FTPERR_NIC_ABORT_IN_PROGRESS;
00842         /*@notreached@*/ break;
00843     default:
00844         if (ec >= 400 && ec <= 599) {
00845             return FTPERR_BAD_SERVER_RESPONSE;
00846         }
00847         break;
00848     }
00849     return rc;
00850 }
00851 
00852 static int ftpCommand(urlinfo u, char ** str, ...)
00853 {
00854     va_list ap;
00855     int len = 0;
00856     const char * s, * t;
00857     char * te;
00858     int rc;
00859 
00860     URLSANE(u);
00861     va_start(ap, str);
00862     while ((s = va_arg(ap, const char *)) != NULL) {
00863         if (len) len++;
00864         len += strlen(s);
00865     }
00866     len += sizeof("\r\n")-1;
00867     va_end(ap);
00868 
00869     t = te = alloca(len + 1);
00870 
00871     va_start(ap, str);
00872     while ((s = va_arg(ap, const char *)) != NULL) {
00873         if (te > t) *te++ = ' ';
00874         te = stpcpy(te, s);
00875     }
00876     te = stpcpy(te, "\r\n");
00877     va_end(ap);
00878 
00879 if (_ftp_debug)
00880 fprintf(stderr, "-> %s", t);
00881     if (fdWrite(u->ctrl, t, (te-t)) != (te-t))
00882         return FTPERR_SERVER_IO_ERROR;
00883 
00884     rc = ftpCheckResponse(u, str);
00885     return rc;
00886 }
00887 
00888 static int ftpLogin(urlinfo u)
00889 {
00890     const char * host;
00891     const char * user;
00892     const char * password;
00893     int port;
00894     int rc;
00895 
00896     URLSANE(u);
00897     u->ctrl = fdLink(u->ctrl, "open ctrl");
00898 
00899     if (((host = (u->proxyh ? u->proxyh : u->host)) == NULL)) {
00900         rc = FTPERR_BAD_HOSTNAME;
00901         goto errxit;
00902     }
00903 
00904     if ((port = (u->proxyp > 0 ? u->proxyp : u->port)) < 0) port = IPPORT_FTP;
00905 
00906     if ((user = (u->proxyu ? u->proxyu : u->user)) == NULL)
00907         user = "anonymous";
00908 
00909     if ((password = u->password) == NULL) {
00910         if (getuid()) {
00911             struct passwd * pw = getpwuid(getuid());
00912             char *myp = alloca(strlen(pw->pw_name) + sizeof("@"));
00913             strcpy(myp, pw->pw_name);
00914             strcat(myp, "@");
00915             password = myp;
00916         } else {
00917             password = "root@";
00918         }
00919     }
00920 
00921     if (fdFileno(u->ctrl) >= 0 && fdWritable(u->ctrl, 0) < 1)
00922         fdClose(u->ctrl);
00923 
00924     if (fdFileno(u->ctrl) < 0) {
00925         rc = tcpConnect(u->ctrl, host, port);
00926         if (rc < 0)
00927             goto errxit2;
00928     }
00929 
00930     if ((rc = ftpCheckResponse(u, NULL)))
00931         goto errxit;
00932 
00933     if ((rc = ftpCommand(u, NULL, "USER", user, NULL)))
00934         goto errxit;
00935 
00936     if ((rc = ftpCommand(u, NULL, "PASS", password, NULL)))
00937         goto errxit;
00938 
00939     if ((rc = ftpCommand(u, NULL, "TYPE", "I", NULL)))
00940         goto errxit;
00941 
00942     return 0;
00943 
00944 errxit:
00945     fdSetSyserrno(u->ctrl, errno, ftpStrerror(rc));
00946 errxit2:
00947     if (fdFileno(u->ctrl) >= 0)
00948         fdClose(u->ctrl);
00949     return rc;
00950 }
00951 
00952 int ftpReq(FD_t data, const char * ftpCmd, const char * ftpArg)
00953 {
00954     urlinfo u = data->url;
00955     struct sockaddr_in dataAddress;
00956     char * cmd;
00957     int cmdlen;
00958     char * passReply;
00959     char * chptr;
00960     int rc;
00961 
00962     URLSANE(u);
00963     if (ftpCmd == NULL)
00964         return FTPERR_UNKNOWN;  /* XXX W2DO? */
00965 
00966     cmdlen = strlen(ftpCmd) + (ftpArg ? 1+strlen(ftpArg) : 0) + sizeof("\r\n");
00967     chptr = cmd = alloca(cmdlen);
00968     chptr = stpcpy(chptr, ftpCmd);
00969     if (ftpArg) {
00970         *chptr++ = ' ';
00971         chptr = stpcpy(chptr, ftpArg);
00972     }
00973     chptr = stpcpy(chptr, "\r\n");
00974     cmdlen = chptr - cmd;
00975 
00976 /*
00977  * Get the ftp version of the Content-Length.
00978  */
00979     if (!strncmp(cmd, "RETR", 4)) {
00980         unsigned cl;
00981 
00982         passReply = NULL;
00983         rc = ftpCommand(u, &passReply, "SIZE", ftpArg, NULL);
00984         if (rc)
00985             goto errxit;
00986         if (sscanf(passReply, "%d %u", &rc, &cl) != 2) {
00987             rc = FTPERR_BAD_SERVER_RESPONSE;
00988             goto errxit;
00989         }
00990         rc = 0;
00991         data->contentLength = cl;
00992     }
00993 
00994     passReply = NULL;
00995     rc = ftpCommand(u, &passReply, "PASV", NULL);
00996     if (rc) {
00997         rc = FTPERR_PASSIVE_ERROR;
00998         goto errxit;
00999     }
01000 
01001     chptr = passReply;
01002     while (*chptr && *chptr != '(') chptr++;
01003     if (*chptr != '(') return FTPERR_PASSIVE_ERROR; 
01004     chptr++;
01005     passReply = chptr;
01006     while (*chptr && *chptr != ')') chptr++;
01007     if (*chptr != ')') return FTPERR_PASSIVE_ERROR;
01008     *chptr-- = '\0';
01009 
01010     while (*chptr && *chptr != ',') chptr--;
01011     if (*chptr != ',') return FTPERR_PASSIVE_ERROR;
01012     chptr--;
01013     while (*chptr && *chptr != ',') chptr--;
01014     if (*chptr != ',') return FTPERR_PASSIVE_ERROR;
01015     *chptr++ = '\0';
01016     
01017     /* now passReply points to the IP portion, and chptr points to the
01018        port number portion */
01019 
01020     {   int i, j;
01021         dataAddress.sin_family = AF_INET;
01022         if (sscanf(chptr, "%d,%d", &i, &j) != 2) {
01023             rc = FTPERR_PASSIVE_ERROR;
01024             goto errxit;
01025         }
01026         dataAddress.sin_port = htons((((unsigned)i) << 8) + j);
01027     }
01028 
01029     chptr = passReply;
01030     while (*chptr++) {
01031         if (*chptr == ',') *chptr = '.';
01032     }
01033 
01034     if (!inet_aton(passReply, &dataAddress.sin_addr)) {
01035         rc = FTPERR_PASSIVE_ERROR;
01036         goto errxit;
01037     }
01038 
01039     rc = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
01040     fdSetFdno(data, (rc >= 0 ? rc : -1));
01041     if (rc < 0) {
01042         rc = FTPERR_FAILED_CONNECT;
01043         goto errxit;
01044     }
01045     data = fdLink(data, "open data (ftpReq)");
01046 
01047     /* XXX setsockopt SO_LINGER */
01048     /* XXX setsockopt SO_KEEPALIVE */
01049     /* XXX setsockopt SO_TOS IPTOS_THROUGHPUT */
01050 
01051     while (connect(fdFileno(data), (struct sockaddr *) &dataAddress, 
01052                 sizeof(dataAddress)) < 0) {
01053         if (errno == EINTR)
01054             continue;
01055         rc = FTPERR_FAILED_DATA_CONNECT;
01056         goto errxit;
01057     }
01058 
01059 if (_ftp_debug)
01060 fprintf(stderr, "-> %s", cmd);
01061     if (fdWrite(u->ctrl, cmd, cmdlen) != cmdlen) {
01062         rc = FTPERR_SERVER_IO_ERROR;
01063         goto errxit;
01064     }
01065 
01066     if ((rc = ftpCheckResponse(u, NULL))) {
01067         goto errxit;
01068     }
01069 
01070     data->ftpFileDoneNeeded = 1;
01071     u->ctrl = fdLink(u->ctrl, "grab data (ftpReq)");
01072     u->ctrl = fdLink(u->ctrl, "open data (ftpReq)");
01073     return 0;
01074 
01075 errxit:
01076     fdSetSyserrno(u->ctrl, errno, ftpStrerror(rc));
01077     if (fdFileno(data) >= 0)
01078         fdClose(data);
01079     return rc;
01080 }
01081 
01082 /*@null@*/ static rpmCallbackFunction   urlNotify = NULL;
01083 /*@null@*/ static void *        urlNotifyData = NULL;
01084 static int                      urlNotifyCount = -1;
01085 
01086 void urlSetCallback(rpmCallbackFunction notify, void *notifyData, int notifyCount) {
01087     urlNotify = notify;
01088     urlNotifyData = notifyData;
01089     urlNotifyCount = (notifyCount >= 0) ? notifyCount : 4096;
01090 }
01091 
01092 int ufdCopy(FD_t sfd, FD_t tfd)
01093 {
01094     char buf[BUFSIZ];
01095     int itemsRead;
01096     int itemsCopied = 0;
01097     int rc = 0;
01098     int notifier = -1;
01099 
01100     if (urlNotify) {
01101         (void)(*urlNotify) (NULL, RPMCALLBACK_INST_OPEN_FILE,
01102                 0, 0, NULL, urlNotifyData);
01103     }
01104     
01105     while (1) {
01106         rc = Fread(buf, sizeof(buf[0]), sizeof(buf), sfd);
01107         if (rc < 0)
01108             break;
01109         else if (rc == 0) {
01110             rc = itemsCopied;
01111             break;
01112         }
01113         itemsRead = rc;
01114         rc = Fwrite(buf, sizeof(buf[0]), itemsRead, tfd);
01115         if (rc < 0)
01116             break;
01117         if (rc != itemsRead) {
01118             rc = FTPERR_FILE_IO_ERROR;
01119             break;
01120         }
01121 
01122         itemsCopied += itemsRead;
01123         if (urlNotify && urlNotifyCount > 0) {
01124             int n = itemsCopied/urlNotifyCount;
01125             if (n != notifier) {
01126                 (void)(*urlNotify) (NULL, RPMCALLBACK_INST_PROGRESS,
01127                         itemsCopied, 0, NULL, urlNotifyData);
01128                 notifier = n;
01129             }
01130         }
01131     }
01132 
01133     DBGIO(sfd, (stderr, "++ copied %d bytes: %s\n", itemsCopied,
01134         ftpStrerror(rc)));
01135 
01136     if (urlNotify) {
01137         (void)(*urlNotify) (NULL, RPMCALLBACK_INST_OPEN_FILE,
01138                 itemsCopied, itemsCopied, NULL, urlNotifyData);
01139     }
01140     
01141     return rc;
01142 }
01143 
01144 static int urlConnect(const char * url, /*@out@*/ urlinfo * uret)
01145 {
01146     urlinfo u;
01147     int rc = 0;
01148 
01149     if (urlSplit(url, &u) < 0)
01150         return -1;
01151 
01152     if (u->urltype == URL_IS_FTP) {
01153         FD_t fd;
01154 
01155         if ((fd = u->ctrl) == NULL) {
01156             fd = u->ctrl = fdNew("persist ctrl (urlConnect FTP)");
01157             fdSetIo(u->ctrl, ufdio);
01158         }
01159         
01160         fd->rd_timeoutsecs = ftpTimeoutSecs;
01161         fd->contentLength = fd->bytesRemain = -1;
01162         fd->url = NULL;         /* XXX FTP ctrl has not */
01163         fd->ftpFileDoneNeeded = 0;
01164         fd = fdLink(fd, "grab ctrl (urlConnect FTP)");
01165 
01166         if (fdFileno(u->ctrl) < 0) {
01167             rpmMessage(RPMMESS_DEBUG, _("logging into %s as %s, pw %s\n"),
01168                         u->host,
01169                         u->user ? u->user : "ftp",
01170                         u->password ? u->password : "(username)");
01171 
01172             if ((rc = ftpLogin(u)) < 0) {       /* XXX save ftpLogin error */
01173                 u->ctrl = fdFree(fd, "grab ctrl (urlConnect FTP)");
01174                 u->openError = rc;
01175             }
01176         }
01177     }
01178 
01179     if (uret != NULL)
01180         *uret = urlLink(u, "urlConnect");
01181     u = urlFree(u, "urlSplit (urlConnect)");    
01182 
01183     return rc;
01184 }
01185 
01186 int ufdGetFile(FD_t sfd, FD_t tfd)
01187 {
01188     int rc;
01189 
01190     FDSANE(sfd);
01191     FDSANE(tfd);
01192     rc = ufdCopy(sfd, tfd);
01193     Fclose(sfd);
01194     if (rc > 0)         /* XXX ufdCopy now returns no. bytes copied */
01195         rc = 0;
01196     return rc;
01197 }
01198 
01199 int ftpCmd(const char * cmd, const char * url, const char * arg2) {
01200     urlinfo u;
01201     int rc;
01202     const char * path;
01203 
01204     if (urlConnect(url, &u) < 0)
01205         return -1;
01206 
01207     (void) urlPath(url, &path);
01208 
01209     rc = ftpCommand(u, NULL, cmd, path, arg2, NULL);
01210     u->ctrl = fdFree(u->ctrl, "grab ctrl (ftpCmd)");
01211     return rc;
01212 }
01213 
01214 /* XXX these aren't worth the pain of including correctly */
01215 #if !defined(IAC)
01216 #define IAC     255             /* interpret as command: */
01217 #endif
01218 #if !defined(IP)
01219 #define IP      244             /* interrupt process--permanently */
01220 #endif
01221 #if !defined(DM)
01222 #define DM      242             /* data mark--for connect. cleaning */
01223 #endif
01224 #if !defined(SHUT_RDWR)
01225 #define SHUT_RDWR       1+1
01226 #endif
01227 
01228 static int ftpAbort(urlinfo u, FD_t data) {
01229     static unsigned char ipbuf[3] = { IAC, IP, IAC };
01230     FD_t ctrl;
01231     int rc;
01232     int tosecs;
01233 
01234     URLSANE(u);
01235 
01236     if (data != NULL) {
01237         data->ftpFileDoneNeeded = 0;
01238         if (fdFileno(data) >= 0)
01239             u->ctrl = fdFree(u->ctrl, "open data (ftpAbort)");
01240         u->ctrl = fdFree(u->ctrl, "grab data (ftpAbort)");
01241     }
01242     ctrl = u->ctrl;
01243 
01244     DBGIO(0, (stderr, "-> ABOR\n"));
01245 
01246     if (send(fdFileno(ctrl), ipbuf, sizeof(ipbuf), MSG_OOB) != sizeof(ipbuf)) {
01247         fdClose(ctrl);
01248         return FTPERR_SERVER_IO_ERROR;
01249     }
01250 
01251     sprintf(u->buf, "%cABOR\r\n",(char) DM);
01252     if (fdWrite(ctrl, u->buf, 7) != 7) {
01253         fdClose(ctrl);
01254         return FTPERR_SERVER_IO_ERROR;
01255     }
01256 
01257     if (data && fdFileno(data) >= 0) {
01258         /* XXX shorten data drain time wait */
01259         tosecs = data->rd_timeoutsecs;
01260         data->rd_timeoutsecs = 10;
01261         if (fdReadable(data, data->rd_timeoutsecs) > 0) {
01262             while (timedRead(data, u->buf, u->bufAlloced) > 0)
01263                 ;
01264         }
01265         data->rd_timeoutsecs = tosecs;
01266         /* XXX ftp abort needs to close the data channel to receive status */
01267         shutdown(fdFileno(data), SHUT_RDWR);
01268         close(fdFileno(data));
01269         data->fps[0].fdno = -1; /* XXX WRONG but expedient */
01270     }
01271 
01272     /* XXX shorten ctrl drain time wait */
01273     tosecs = u->ctrl->rd_timeoutsecs;
01274     u->ctrl->rd_timeoutsecs = 10;
01275     if ((rc = ftpCheckResponse(u, NULL)) == FTPERR_NIC_ABORT_IN_PROGRESS) {
01276         rc = ftpCheckResponse(u, NULL);
01277     }
01278     rc = ftpCheckResponse(u, NULL);
01279     u->ctrl->rd_timeoutsecs = tosecs;
01280 
01281     return rc;
01282 }
01283 
01284 static int ftpFileDone(urlinfo u, FD_t data)
01285 {
01286     int rc = 0;
01287 
01288     URLSANE(u);
01289     assert(data->ftpFileDoneNeeded);
01290 
01291     if (data->ftpFileDoneNeeded) {
01292         data->ftpFileDoneNeeded = 0;
01293         u->ctrl = fdFree(u->ctrl, "open data (ftpFileDone)");
01294         u->ctrl = fdFree(u->ctrl, "grab data (ftpFileDone)");
01295         rc = ftpCheckResponse(u, NULL);
01296     }
01297     return rc;
01298 }
01299 
01300 static int httpResp(urlinfo u, FD_t ctrl, /*@out@*/ char ** str)
01301 {
01302     int ec = 0;
01303     int rc;
01304 
01305     URLSANE(u);
01306     rc = checkResponse(u, ctrl, &ec, str);
01307 
01308 if (_ftp_debug && !(rc == 0 && ec == 200))
01309 fprintf(stderr, "*** httpResp: rc %d ec %d\n", rc, ec);
01310 
01311     switch (ec) {
01312     case 200:
01313         break;
01314     default:
01315         rc = FTPERR_FILE_NOT_FOUND;
01316         break;
01317     }
01318 
01319     return rc;
01320 }
01321 
01322 static int httpReq(FD_t ctrl, const char * httpCmd, const char * httpArg)
01323 {
01324     urlinfo u = ctrl->url;
01325     const char * host;
01326     const char * path;
01327     int port;
01328     int rc;
01329     char * req;
01330     size_t len;
01331     int retrying = 0;
01332 
01333     URLSANE(u);
01334     assert(ctrl != NULL);
01335 
01336     if (((host = (u->proxyh ? u->proxyh : u->host)) == NULL))
01337         return FTPERR_BAD_HOSTNAME;
01338 
01339     if ((port = (u->proxyp > 0 ? u->proxyp : u->port)) < 0) port = 80;
01340     path = (u->proxyh || u->proxyp > 0) ? u->url : httpArg;
01341 
01342 reopen:
01343     if (fdFileno(ctrl) >= 0 && (rc = fdWritable(ctrl, 0)) < 1) {
01344         fdClose(ctrl);
01345     }
01346 
01347     if (fdFileno(ctrl) < 0) {
01348         rc = tcpConnect(ctrl, host, port);
01349         if (rc < 0)
01350             goto errxit2;
01351         ctrl = fdLink(ctrl, "open ctrl (httpReq)");
01352     }
01353 
01354     len = sizeof("\
01355 req x HTTP/1.0\r\n\
01356 User-Agent: rpm/3.0.4\r\n\
01357 Host: y:z\r\n\
01358 Accept: text/plain\r\n\
01359 Transfer-Encoding: chunked\r\n\
01360 \r\n\
01361 ") + strlen(httpCmd) + strlen(path) + sizeof(VERSION) + strlen(host) + 20;
01362 
01363     req = alloca(len);
01364     *req = '\0';
01365 
01366   if (!strcmp(httpCmd, "PUT")) {
01367     sprintf(req, "\
01368 %s %s HTTP/1.%d\r\n\
01369 User-Agent: rpm/%s\r\n\
01370 Host: %s:%d\r\n\
01371 Accept: text/plain\r\n\
01372 Transfer-Encoding: chunked\r\n\
01373 \r\n\
01374 ",      httpCmd, path, (u->httpVersion ? 1 : 0), VERSION, host, port);
01375 } else {
01376     sprintf(req, "\
01377 %s %s HTTP/1.%d\r\n\
01378 User-Agent: rpm/%s\r\n\
01379 Host: %s:%d\r\n\
01380 Accept: text/plain\r\n\
01381 \r\n\
01382 ",      httpCmd, path, (u->httpVersion ? 1 : 0), VERSION, host, port);
01383 }
01384 
01385 if (_ftp_debug)
01386 fprintf(stderr, "-> %s", req);
01387 
01388     len = strlen(req);
01389     if (fdWrite(ctrl, req, len) != len) {
01390         rc = FTPERR_SERVER_IO_ERROR;
01391         goto errxit;
01392     }
01393 
01394     if (!strcmp(httpCmd, "PUT")) {
01395         ctrl->wr_chunked = 1;
01396     } else {
01397 
01398         rc = httpResp(u, ctrl, NULL);
01399 
01400         if (rc) {
01401             if (!retrying) {    /* not HTTP_OK */
01402                 retrying = 1;
01403                 fdClose(ctrl);
01404                 goto reopen;
01405             }
01406             goto errxit;
01407         }
01408     }
01409 
01410     ctrl = fdLink(ctrl, "open data (httpReq)");
01411     return 0;
01412 
01413 errxit:
01414     fdSetSyserrno(ctrl, errno, ftpStrerror(rc));
01415 errxit2:
01416     if (fdFileno(ctrl) >= 0)
01417         fdClose(ctrl);
01418     return rc;
01419 }
01420 
01421 /* XXX DYING: unused */
01422 void * ufdGetUrlinfo(FD_t fd) {
01423     FDSANE(fd);
01424     if (fd->url == NULL)
01425         return NULL;
01426     return urlLink(fd->url, "ufdGetUrlinfo");
01427 }
01428 
01429 /* =============================================================== */
01430 static ssize_t ufdRead(void * cookie, /*@out@*/ char * buf, size_t count) {
01431     FD_t fd = c2f(cookie);
01432     int bytesRead;
01433     int total;
01434 
01435     /* XXX preserve timedRead() behavior */
01436     if (fdGetIo(fd) == fdio) {
01437         struct stat sb;
01438         int fdno = fdFileno(fd);
01439         fstat(fdno, &sb);
01440         if (S_ISREG(sb.st_mode))
01441             return fdRead(fd, buf, count);
01442     }
01443 
01444     UFDONLY(fd);
01445     assert(fd->rd_timeoutsecs >= 0);
01446 
01447     for (total = 0; total < count; total += bytesRead) {
01448 
01449         int rc;
01450 
01451         bytesRead = 0;
01452 
01453         /* Is there data to read? */
01454         if (fd->bytesRemain == 0) return total; /* XXX simulate EOF */
01455         rc = fdReadable(fd, fd->rd_timeoutsecs);
01456 
01457         switch (rc) {
01458         case -1:        /* error */
01459         case  0:        /* timeout */
01460             return total;
01461             /*@notreached@*/ break;
01462         default:        /* data to read */
01463             break;
01464         }
01465 
01466         rc = fdRead(fd, buf + total, count - total);
01467 
01468         if (rc < 0) {
01469             switch (errno) {
01470             case EWOULDBLOCK:
01471                 continue;
01472                 /*@notreached@*/ break;
01473             default:
01474                 break;
01475             }
01476 if (_rpmio_debug)
01477 fprintf(stderr, "*** read: rc %d errno %d %s \"%s\"\n", rc, errno, strerror(errno), buf);
01478             return rc;
01479             /*@notreached@*/ break;
01480         } else if (rc == 0) {
01481             return total;
01482             /*@notreached@*/ break;
01483         }
01484         bytesRead = rc;
01485     }
01486 
01487     return count;
01488 }
01489 
01490 static ssize_t ufdWrite(void * cookie, const char * buf, size_t count)
01491 {
01492     FD_t fd = c2f(cookie);
01493     int bytesWritten;
01494     int total = 0;
01495 
01496 #ifdef  NOTYET
01497     if (fdGetIo(fd) == fdio) {
01498         struct stat sb;
01499         fstat(fdGetFdno(fd), &sb);
01500         if (S_ISREG(sb.st_mode))
01501             return fdWrite(fd, buf, count);
01502     }
01503 #endif
01504 
01505     UFDONLY(fd);
01506 
01507     for (total = 0; total < count; total += bytesWritten) {
01508 
01509         int rc;
01510 
01511         bytesWritten = 0;
01512 
01513         /* Is there room to write data? */
01514         if (fd->bytesRemain == 0) {
01515 fprintf(stderr, "*** ufdWrite fd %p WRITE PAST END OF CONTENT\n", fd);
01516             return total;       /* XXX simulate EOF */
01517         }
01518         rc = fdWritable(fd, 2);         /* XXX configurable? */
01519 
01520         switch (rc) {
01521         case -1:        /* error */
01522         case  0:        /* timeout */
01523             return total;
01524             /*@notreached@*/ break;
01525         default:        /* data to write */
01526             break;
01527         }
01528 
01529         rc = fdWrite(fd, buf + total, count - total);
01530 
01531         if (rc < 0) {
01532             switch (errno) {
01533             case EWOULDBLOCK:
01534                 continue;
01535                 /*@notreached@*/ break;
01536             default:
01537                 break;
01538             }
01539 if (_rpmio_debug)
01540 fprintf(stderr, "*** write: rc %d errno %d %s \"%s\"\n", rc, errno, strerror(errno), buf);
01541             return rc;
01542             /*@notreached@*/ break;
01543         } else if (rc == 0) {
01544             return total;
01545             /*@notreached@*/ break;
01546         }
01547         bytesWritten = rc;
01548     }
01549 
01550     return count;
01551 }
01552 
01553 static inline int ufdSeek(void * cookie, _libio_pos_t pos, int whence) {
01554     FD_t fd = c2f(cookie);
01555 
01556     switch (fd->urlType) {
01557     case URL_IS_UNKNOWN:
01558     case URL_IS_PATH:
01559         break;
01560     case URL_IS_DASH:
01561     case URL_IS_FTP:
01562     case URL_IS_HTTP:
01563     default:
01564         return -2;
01565         /*@notreached@*/ break;
01566     }
01567     return fdSeek(cookie, pos, whence);
01568 }
01569 
01570 int ufdClose( /*@only@*/ void * cookie)
01571 {
01572     FD_t fd = c2f(cookie);
01573 
01574     UFDONLY(fd);
01575 
01576     if (fd->url) {
01577         urlinfo u = fd->url;
01578 
01579         if (fd == u->data)
01580                 fd = u->data = fdFree(fd, "grab data (ufdClose persist)");
01581         else
01582                 fd = fdFree(fd, "grab data (ufdClose)");
01583         (void) urlFree(fd->url, "url (ufdClose)");
01584         fd->url = NULL;
01585         u->ctrl = fdFree(u->ctrl, "grab ctrl (ufdClose)");
01586 
01587         if (u->urltype == URL_IS_FTP) {
01588 
01589             /* XXX if not using libio, lose the fp from fpio */
01590             {   FILE * fp;
01591                 /*@+voidabstract@*/
01592                 fp = fdGetFILE(fd);
01593                 if (noLibio && fp)
01594                     fdSetFp(fd, NULL);
01595                 /*@=voidabstract@*/
01596             }
01597 
01598             /*
01599              * Normal FTP has 4 refs on the data fd:
01600              *  "persist data (ufdOpen FTP)"            rpmio.c:888
01601              *  "grab data (ufdOpen FTP)"               rpmio.c:892
01602              *  "open data (ftpReq)"                    ftp.c:633
01603              *  "fopencookie"                           rpmio.c:1507
01604              */
01605 
01606             /*
01607              * Normal FTP has 5 refs on the ctrl fd:
01608              *  "persist ctrl"                          url.c:176
01609              *  "grab ctrl (urlConnect FTP)"            rpmio.c:404
01610              *  "open ctrl"                             ftp.c:504
01611              *  "grab data (ftpReq)"                    ftp.c:661
01612              *  "open data (ftpReq)"                    ftp.c:662
01613              */
01614             if (fd->bytesRemain > 0) {
01615                 if (fd->ftpFileDoneNeeded) {
01616                     if (fdReadable(u->ctrl, 0) > 0)
01617                         ftpFileDone(u, fd);
01618                     else
01619                         ftpAbort(u, fd);
01620                 }
01621             } else {
01622                 int rc;
01623                 /* XXX STOR et al require close before ftpFileDone */
01624                 rc = fdClose(fd);
01625 #if 0   /* XXX error exit from ufdOpen does not have this set */
01626                 assert(fd->ftpFileDoneNeeded != 0);
01627 #endif
01628                 if (fd->ftpFileDoneNeeded)
01629                     ftpFileDone(u, fd);
01630                 return rc;
01631             }
01632         }
01633 
01634         if (!strcmp(u->service, "http")) {
01635             if (fd->wr_chunked) {
01636                 int rc;
01637             /* XXX HTTP PUT requires terminating 0 length chunk. */
01638                 (void) fdWrite(fd, NULL, 0);
01639                 fd->wr_chunked = 0;
01640             /* XXX HTTP PUT requires terminating entity-header. */
01641 if (_ftp_debug)
01642 fprintf(stderr, "-> \r\n");
01643                 (void) fdWrite(fd, "\r\n", sizeof("\r\n")-1);
01644                 rc = httpResp(u, fd, NULL);
01645             }
01646 
01647             if (fd == u->ctrl)
01648                 fd = u->ctrl = fdFree(fd, "open data (ufdClose HTTP persist ctrl)");
01649             else if (fd == u->data)
01650                 fd = u->data = fdFree(fd, "open data (ufdClose HTTP persist data)");
01651             else
01652                 fd = fdFree(fd, "open data (ufdClose HTTP)");
01653 
01654             /*
01655              * HTTP has 4 (or 5 if persistent malloc) refs on the fd:
01656              *  "persist ctrl"                          url.c:177
01657              *  "grab ctrl (ufdOpen HTTP)"              rpmio.c:924
01658              *  "grab data (ufdOpen HTTP)"              rpmio.c:928
01659              *  "open ctrl (httpReq)"                   ftp.c:382
01660              *  "open data (httpReq)"                   ftp.c:435
01661              */
01662 
01663             /* XXX if not using libio, lose the fp from fpio */
01664             {   FILE * fp;
01665                 /*@+voidabstract@*/
01666                 fp = fdGetFILE(fd);
01667                 if (noLibio && fp)
01668                     fdSetFp(fd, NULL);
01669                 /*@=voidabstract@*/
01670             }
01671 
01672             if (fd->persist && u->httpVersion &&
01673                 (fd == u->ctrl || fd == u->data) && fd->bytesRemain == 0) {
01674                 fd->contentLength = fd->bytesRemain = -1;
01675                 return 0;
01676             } else {
01677                 fd->contentLength = fd->bytesRemain = -1;
01678             }
01679         }
01680     }
01681     return fdClose(fd);
01682 }
01683 
01684 /*@null@*/ FD_t ftpOpen(const char *url, /*@unused@*/ int flags,
01685                 /*@unused@*/ mode_t mode, /*@out@*/ urlinfo *uret)
01686 {
01687     urlinfo u = NULL;
01688     FD_t fd = NULL;
01689 
01690 #if 0   /* XXX makeTempFile() heartburn */
01691     assert(!(flags & O_RDWR));
01692 #endif
01693     if (urlConnect(url, &u) < 0)
01694         goto exit;
01695 
01696     if (u->data == NULL)
01697         u->data = fdNew("persist data (ftpOpen)");
01698 
01699     if (u->data->url == NULL)
01700         fd = fdLink(u->data, "grab data (ftpOpen persist data)");
01701     else
01702         fd = fdNew("grab data (ftpOpen)");
01703 
01704     if (fd) {
01705         fdSetIo(fd, ufdio);
01706         fd->ftpFileDoneNeeded = 0;
01707         fd->rd_timeoutsecs = ftpTimeoutSecs;
01708         fd->contentLength = fd->bytesRemain = -1;
01709         fd->url = urlLink(u, "url (ufdOpen FTP)");
01710         fd->urlType = URL_IS_FTP;
01711     }
01712 
01713 exit:
01714     if (uret)
01715         *uret = u;
01716     return fd;
01717 }
01718 
01719 static /*@null@*/ FD_t httpOpen(const char *url, int flags, mode_t mode,
01720                 /*@out@*/ urlinfo *uret)
01721 {
01722     urlinfo u = NULL;
01723     FD_t fd = NULL;
01724 
01725 #if 0   /* XXX makeTempFile() heartburn */
01726     assert(!(flags & O_RDWR));
01727 #endif
01728     if (urlSplit(url, &u))
01729         goto exit;
01730 
01731     if (u->ctrl == NULL)
01732         u->ctrl = fdNew("persist ctrl (httpOpen)");
01733     if (u->ctrl->nrefs > 2 && u->data == NULL)
01734         u->data = fdNew("persist data (httpOpen)");
01735 
01736     if (u->ctrl->url == NULL)
01737         fd = fdLink(u->ctrl, "grab ctrl (httpOpen persist ctrl)");
01738     else if (u->data->url == NULL)
01739         fd = fdLink(u->data, "grab ctrl (httpOpen persist data)");
01740     else
01741         fd = fdNew("grab ctrl (httpOpen)");
01742 
01743     if (fd) {
01744         fdSetIo(fd, ufdio);
01745         fd->ftpFileDoneNeeded = 0;
01746         fd->rd_timeoutsecs = httpTimeoutSecs;
01747         fd->contentLength = fd->bytesRemain = -1;
01748         fd->url = urlLink(u, "url (httpOpen)");
01749         fd = fdLink(fd, "grab data (httpOpen)");
01750         fd->urlType = URL_IS_HTTP;
01751     }
01752 
01753 exit:
01754     if (uret)
01755         *uret = u;
01756     return fd;
01757 }
01758 
01759 static /*@null@*/ FD_t ufdOpen(const char *url, int flags, mode_t mode)
01760 {
01761     FD_t fd = NULL;
01762     const char * cmd;
01763     urlinfo u;
01764     const char * path;
01765     urltype urlType = urlPath(url, &path);
01766 
01767 if (_rpmio_debug)
01768 fprintf(stderr, "*** ufdOpen(%s,0x%x,0%o)\n", url, flags, (unsigned)mode);
01769 
01770     switch (urlType) {
01771     case URL_IS_FTP:
01772         fd = ftpOpen(url, flags, mode, &u);
01773         if (fd == NULL || u == NULL)
01774             break;
01775 
01776         /* XXX W2DO? use STOU rather than STOR to prevent clobbering */
01777         cmd = ((flags & O_WRONLY) 
01778                 ?  ((flags & O_APPEND) ? "APPE" :
01779                    ((flags & O_CREAT) ? "STOR" : "STOR"))
01780                 :  ((flags & O_CREAT) ? "STOR" : "RETR"));
01781         u->openError = ftpReq(fd, cmd, path);
01782         if (u->openError < 0) {
01783             /* XXX make sure that we can exit through ufdClose */
01784             fd = fdLink(fd, "error data (ufdOpen FTP)");
01785         } else {
01786             fd->bytesRemain = ((!strcmp(cmd, "RETR"))
01787                 ?  fd->contentLength : -1);
01788             fd->wr_chunked = 0;
01789         }
01790         break;
01791     case URL_IS_HTTP:
01792         fd = httpOpen(url, flags, mode, &u);
01793         if (fd == NULL || u == NULL)
01794             break;
01795 
01796         cmd = ((flags & O_WRONLY)
01797                 ?  ((flags & O_APPEND) ? "PUT" :
01798                    ((flags & O_CREAT) ? "PUT" : "PUT"))
01799                 : "GET");
01800         u->openError = httpReq(fd, cmd, path);
01801         if (u->openError < 0) {
01802             /* XXX make sure that we can exit through ufdClose */
01803             fd = fdLink(fd, "error ctrl (ufdOpen HTTP)");
01804             fd = fdLink(fd, "error data (ufdOpen HTTP)");
01805         } else {
01806             fd->bytesRemain = ((!strcmp(cmd, "GET"))
01807                 ?  fd->contentLength : -1);
01808             fd->wr_chunked = ((!strcmp(cmd, "PUT"))
01809                 ?  fd->wr_chunked : 0);
01810         }
01811         break;
01812     case URL_IS_DASH:
01813         assert(!(flags & O_RDWR));
01814         fd = fdDup( ((flags & O_WRONLY) ? STDOUT_FILENO : STDIN_FILENO) );
01815         if (fd) {
01816             fdSetIo(fd, ufdio);
01817             fd->rd_timeoutsecs = 600;   /* XXX W2DO? 10 mins? */
01818             fd->contentLength = fd->bytesRemain = -1;
01819         }
01820         break;
01821     case URL_IS_PATH:
01822     case URL_IS_UNKNOWN:
01823     default:
01824         fd = fdOpen(path, flags, mode);
01825         if (fd) {
01826             fdSetIo(fd, ufdio);
01827             fd->rd_timeoutsecs = 1;
01828             fd->contentLength = fd->bytesRemain = -1;
01829         }
01830         break;
01831     }
01832 
01833     if (fd == NULL) return NULL;
01834     fd->urlType = urlType;
01835     if (Fileno(fd) < 0) {
01836         ufdClose(fd);
01837         return NULL;
01838     }
01839 DBGIO(fd, (stderr, "==>\tufdOpen(\"%s\",%x,0%o) %s\n", url, flags, (unsigned)mode, fdbg(fd)));
01840     return fd;
01841 }
01842 
01843 static struct FDIO_s ufdio_s = {
01844   ufdRead, ufdWrite, ufdSeek, ufdClose, XfdLink, XfdFree, XfdNew, fdFileno,
01845   ufdOpen, NULL, fdGetFp, NULL, Mkdir, Chdir, Rmdir, Rename, Unlink
01846 };
01847 FDIO_t ufdio = /*@-compmempass@*/ &ufdio_s /*@=compmempass@*/ ;
01848 
01849 /* =============================================================== */
01850 /* Support for GZIP library.
01851  */
01852 #ifdef  HAVE_ZLIB_H
01853 
01854 #include <zlib.h>
01855 
01856 static inline /*@dependent@*/ /*@null@*/ void * gzdFileno(FD_t fd) {
01857     void * rc = NULL;
01858     int i;
01859 
01860     FDSANE(fd);
01861     for (i = fd->nfps; i >= 0; i--) {
01862         FDSTACK_t * fps = &fd->fps[i];
01863         if (fps->io != gzdio)
01864             continue;
01865         rc = fps->fp;
01866         break;
01867     }
01868     
01869     return rc;
01870 }
01871 
01872 static /*@null@*/ FD_t gzdOpen(const char *path, const