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
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>
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
00032
00033 #if !defined(HAVE_HERRNO) && defined(__hpux)
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
00069
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)
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 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
00140 sprintf(be, "%s %p(%d) fdno %d",
00141 (fps->fdno < 0 ? "LIBIO" : "FP"),
00142 fps->fp, fileno(((FILE *)fps->fp)), fps->fdno);
00143
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
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 return fd;
00189 }
00190
00191 static inline int fdSeekNot(void * cookie, _libio_pos_t pos, int whence) {
00192 FD_t fd = c2f(cookie);
00193 FDSANE(fd);
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 FD_t XfdFree( 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 return fd;
00240 if (fd->stats) free(fd->stats);
00241 if (fd->digest) free(fd->digest);
00242 free(fd);
00243 }
00244 return NULL;
00245 }
00246
00247 static inline 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;
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;
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, char * buf, size_t count) {
00283 FD_t fd = c2f(cookie);
00284 ssize_t rc;
00285
00286 if (fd->bytesRemain == 0) return 0;
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;
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);
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( 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 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 return fd;
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 = &fdio_s ;
00389
00390 FDIO_t fadio;
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;
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 break;
00420 default:
00421 return rc;
00422 break;
00423 }
00424 }
00425 return rc;
00426 } while (1);
00427
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;
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 break;
00456 default:
00457 return rc;
00458 break;
00459 }
00460 }
00461 return rc;
00462 } while (1);
00463
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;
00476
00477 do {
00478 int rc;
00479
00480
00481 rc = fdReadable(fd, secs);
00482
00483 switch (rc) {
00484 case -1:
00485 ec = -1;
00486 continue;
00487 break;
00488 case 0:
00489 ec = -1;
00490 continue;
00491 break;
00492 default:
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 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
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
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 = gethostbyname(host) ;
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 (! inet_aton(host, address) ) {
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 inet_ntoa(sin.sin_addr) ,
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, int *ecp, 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
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
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
00723 if (*s == '\0') {
00724 moretodo = 0;
00725 break;
00726 }
00727 *se++ = '\0';
00728
00729
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
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
00796 if (!strncmp(s, "<TITLE>", sizeof("<TITLE>")-1))
00797 s += sizeof("<TITLE>") - 1;
00798
00799
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, 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 break;
00840 case 552:
00841 return FTPERR_NIC_ABORT_IN_PROGRESS;
00842 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;
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
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
01018
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
01048
01049
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 static rpmCallbackFunction urlNotify = NULL;
01083 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, 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;
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) {
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)
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
01215 #if !defined(IAC)
01216 #define IAC 255
01217 #endif
01218 #if !defined(IP)
01219 #define IP 244
01220 #endif
01221 #if !defined(DM)
01222 #define DM 242
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
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
01267 shutdown(fdFileno(data), SHUT_RDWR);
01268 close(fdFileno(data));
01269 data->fps[0].fdno = -1;
01270 }
01271
01272
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, 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) {
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
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, char * buf, size_t count) {
01431 FD_t fd = c2f(cookie);
01432 int bytesRead;
01433 int total;
01434
01435
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
01454 if (fd->bytesRemain == 0) return total;
01455 rc = fdReadable(fd, fd->rd_timeoutsecs);
01456
01457 switch (rc) {
01458 case -1:
01459 case 0:
01460 return total;
01461 break;
01462 default:
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 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 break;
01480 } else if (rc == 0) {
01481 return total;
01482 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
01514 if (fd->bytesRemain == 0) {
01515 fprintf(stderr, "*** ufdWrite fd %p WRITE PAST END OF CONTENT\n", fd);
01516 return total;
01517 }
01518 rc = fdWritable(fd, 2);
01519
01520 switch (rc) {
01521 case -1:
01522 case 0:
01523 return total;
01524 break;
01525 default:
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 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 break;
01543 } else if (rc == 0) {
01544 return total;
01545 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 break;
01566 }
01567 return fdSeek(cookie, pos, whence);
01568 }
01569
01570 int ufdClose( 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
01590 { FILE * fp;
01591
01592 fp = fdGetFILE(fd);
01593 if (noLibio && fp)
01594 fdSetFp(fd, NULL);
01595
01596 }
01597
01598
01599
01600
01601
01602
01603
01604
01605
01606
01607
01608
01609
01610
01611
01612
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
01624 rc = fdClose(fd);
01625 #if 0
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
01638 (void) fdWrite(fd, NULL, 0);
01639 fd->wr_chunked = 0;
01640
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
01656
01657
01658
01659
01660
01661
01662
01663
01664 { FILE * fp;
01665
01666 fp = fdGetFILE(fd);
01667 if (noLibio && fp)
01668 fdSetFp(fd, NULL);
01669
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 FD_t ftpOpen(const char *url, int flags,
01685 mode_t mode, urlinfo *uret)
01686 {
01687 urlinfo u = NULL;
01688 FD_t fd = NULL;
01689
01690 #if 0
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 FD_t httpOpen(const char *url, int flags, mode_t mode,
01720 urlinfo *uret)
01721 {
01722 urlinfo u = NULL;
01723 FD_t fd = NULL;
01724
01725 #if 0
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 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
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
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
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;
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 = &ufdio_s ;
01848
01849
01850
01851
01852 #ifdef HAVE_ZLIB_H
01853
01854 #include <zlib.h>
01855
01856 static inline 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 FD_t gzdOpen(const char *path, const