10 typedef unsigned int uint32_t;
11 #define INADDR_ANY ((uint32_t) 0x00000000)
14 #else /* __LCLINT__ */
16 #if HAVE_MACHINE_TYPES_H
17 # include <machine/types.h>
20 #include <netinet/in.h>
21 #include <arpa/inet.h> /* XXX for inet_aton and HP-UX */
23 #if HAVE_NETINET_IN_SYSTM_H
24 # include <sys/types.h>
25 # include <netinet/in_systm.h>
28 #if HAVE_LIBIO_H && defined(_IO_BAD_SEEN)
32 #endif /* __LCLINT__ */
34 #if HAVE_HERRNO && defined(__hpux) /* XXX HP-UX w/o -D_XOPEN_SOURCE needs */
42 #define IPPORT_HTTP 80
46 #if !defined(HAVE_INET_ATON)
47 static int inet_aton(const char *cp, struct in_addr *inp)
52 if (addr == ((long) -1)) return 0;
54 memcpy(inp, &addr, sizeof(addr));
59 #if defined(USE_ALT_DNS) && USE_ALT_DNS
64 #include <rpmio_internal.h>
67 #define FDNREFS(fd) (fd ? ((FD_t)fd)->nrefs : -9)
68 #define FDTO(fd) (fd ? ((FD_t)fd)->rd_timeoutsecs : -99)
69 #define FDCPIOPOS(fd) (fd ? ((FD_t)fd)->fd_cpioPos : -99)
71 #define FDONLY(fd) assert(fdGetIo(fd) == fdio)
72 #define GZDONLY(fd) assert(fdGetIo(fd) == gzdio)
73 #define BZDONLY(fd) assert(fdGetIo(fd) == bzdio)
75 #define UFDONLY(fd) /* assert(fdGetIo(fd) == ufdio) */
77 #define fdGetFILE(_fd) ((FILE *)fdGetFp(_fd))
87 #define TIMEOUT_SECS 60
88 static int ftpTimeoutSecs = TIMEOUT_SECS;
89 static int httpTimeoutSecs = TIMEOUT_SECS;
93 #define DBG(_f, _m, _x) \
94 if ((_rpmio_debug | ((_f) ? ((FD_t)(_f))->flags : 0)) & (_m)) fprintf _x
96 #define DBGIO(_f, _x) DBG((_f), RPMIO_DEBUG_IO, _x)
97 #define DBGREFS(_f, _x) DBG((_f), RPMIO_DEBUG_REFS, _x)
99 /* =============================================================== */
101 static /*@observer@*/ const char * fdbg(FD_t fd)
103 static char buf[BUFSIZ];
108 sprintf(be, "fd %p", fd); be += strlen(be);
109 if (fd->rd_timeoutsecs >= 0) {
110 sprintf(be, " secs %d", fd->rd_timeoutsecs);
114 if (fd->bytesRemain != -1) {
115 sprintf(be, " clen %d", (int)fd->bytesRemain);
118 if (fd->wr_chunked) {
119 strcpy(be, " chunked");
123 for (i = fd->nfps; i >= 0; i--) {
124 FDSTACK_t * fps = &fd->fps[i];
129 if (fps->io == fdio) {
130 sprintf(be, "FD %d fp %p", fps->fdno, fps->fp);
131 } else if (fps->io == ufdio) {
132 sprintf(be, "UFD %d fp %p", fps->fdno, fps->fp);
133 } else if (fps->io == fadio) {
134 sprintf(be, "FAD %d fp %p", fps->fdno, fps->fp);
135 } else if (fps->io == gzdio) {
136 sprintf(be, "GZD %p fdno %d", fps->fp, fps->fdno);
138 } else if (fps->io == bzdio) {
139 sprintf(be, "BZD %p fdno %d", fps->fp, fps->fdno);
141 } else if (fps->io == fpio) {
142 sprintf(be, "%s %p(%d) fdno %d",
143 (fps->fdno < 0 ? "LIBIO" : "FP"),
144 fps->fp, fileno(((FILE *)fps->fp)), fps->fdno);
146 sprintf(be, "??? io %p fp %p fdno %d ???",
147 fps->io, fps->fp, fps->fdno);
155 /* =============================================================== */
156 off_t fdSize(FD_t fd) {
161 DBGIO(0, (stderr, "==>\tfdSize(%p) rc %ld\n", fd, (long)rc));
164 if (fd->contentLength >= 0)
165 rc = fd->contentLength;
166 else switch (fd->urlType) {
169 if (fstat(Fileno(fd), &sb) == 0)
180 FD_t fdDup(int fdno) {
184 if ((nfdno = dup(fdno)) < 0)
186 fd = fdNew("open (fdDup)");
187 fdSetFdno(fd, nfdno);
188 DBGIO(fd, (stderr, "==> fdDup(%d) fd %p %s\n", fdno, fd, fdbg(fd)));
192 #ifdef USE_COOKIE_SEEK_POINTER
193 static inline int fdSeekNot(void * cookie, /*@unused@*/ _IO_off64_t *pos, /*@unused@*/ int whence) {
195 static inline int fdSeekNot(void * cookie, /*@unused@*/ off_t pos, /*@unused@*/ int whence) {
197 FD_t fd = c2f(cookie);
198 FDSANE(fd); /* XXX keep gcc quiet */
203 FILE *fdFdopen(void * cookie, const char *fmode) {
204 FD_t fd = c2f(cookie);
208 if (fmode == NULL) return NULL;
210 if (fdno < 0) return NULL;
211 fp = fdopen(fdno, fmode);
212 DBGIO(fd, (stderr, "==> fdFdopen(%p,\"%s\") fdno %d -> fp %p fdno %d\n", cookie, fmode, fdno, fp, fileno(fp)));
213 fd = fdFree(fd, "open (fdFdopen)");
230 /* =============================================================== */
231 static inline FD_t XfdLink(void * cookie, const char *msg, const char *file, unsigned line) {
234 DBGREFS(0, (stderr, "--> fd %p ++ %d %s at %s:%u\n", cookie, FDNREFS(cookie)+1, msg, file, line));
238 DBGREFS(fd, (stderr, "--> fd %p ++ %d %s at %s:%u %s\n", fd, fd->nrefs, msg, file, line, fdbg(fd)));
243 static inline /*@null@*/ FD_t XfdFree( /*@killref@*/ FD_t fd, const char *msg, const char *file, unsigned line) {
245 DBGREFS(0, (stderr, "--> fd %p -- %d %s at %s:%u\n", fd, FDNREFS(fd), msg, file, line));
248 DBGREFS(fd, (stderr, "--> fd %p -- %d %s at %s:%u %s\n", fd, fd->nrefs, msg, file, line, fdbg(fd)));
250 /*@-refcounttrans@*/ return fd; /*@=refcounttrans@*/
251 if (fd->stats) free(fd->stats);
252 /*@-refcounttrans@*/ free(fd); /*@=refcounttrans@*/
257 static inline /*@null@*/ FD_t XfdNew(const char *msg, const char *file, unsigned line) {
258 FD_t fd = (FD_t) xmalloc(sizeof(struct _FD_s));
264 fd->urlType = URL_IS_UNKNOWN;
267 memset(fd->fps, 0, sizeof(fd->fps));
269 fd->fps[0].io = fdio;
270 fd->fps[0].fp = NULL;
271 fd->fps[0].fdno = -1;
274 fd->rd_timeoutsecs = 1; /* XXX default value used to be -1 */
275 fd->contentLength = fd->bytesRemain = -1;
278 fd->errcookie = NULL;
279 fd->stats = calloc(1, sizeof(FDSTAT_t));
280 gettimeofday(&fd->stats->create, NULL);
281 fd->stats->begin = fd->stats->create; /* structure assignment */
283 fd->ftpFileDoneNeeded = 0;
288 return XfdLink(fd, msg, file, line);
291 static inline ssize_t fdRead(void * cookie, /*@out@*/ char * buf, size_t count) {
292 FD_t fd = c2f(cookie);
295 if (fd->bytesRemain == 0) return 0; /* XXX simulate EOF */
297 fdstat_enter(fd, FDSTAT_READ);
298 rc = read(fdFileno(fd), buf, (count > fd->bytesRemain ? fd->bytesRemain : count));
299 fdstat_exit(fd, FDSTAT_READ, rc);
301 DBGIO(fd, (stderr, "==>\tfdRead(%p,%p,%ld) rc %ld %s\n", cookie, buf, (long)count, (long)rc, fdbg(fd)));
306 static inline ssize_t fdWrite(void * cookie, const char * buf, size_t count) {
307 FD_t fd = c2f(cookie);
308 int fdno = fdFileno(fd);
311 if (fd->bytesRemain == 0) return 0; /* XXX simulate EOF */
312 if (fd->wr_chunked) {
314 sprintf(chunksize, "%x\r\n", (unsigned)count);
315 rc = write(fdno, chunksize, strlen(chunksize));
316 if (rc == -1) fd->syserrno = errno;
318 if (count == 0) return 0;
320 fdstat_enter(fd, FDSTAT_WRITE);
321 rc = write(fdno, buf, (count > fd->bytesRemain ? fd->bytesRemain : count));
322 fdstat_exit(fd, FDSTAT_WRITE, rc);
324 if (fd->wr_chunked) {
326 ec = write(fdno, "\r\n", sizeof("\r\n")-1);
327 if (ec == -1) fd->syserrno = errno;
330 DBGIO(fd, (stderr, "==>\tfdWrite(%p,%p,%ld) rc %ld %s\n", cookie, buf, (long)count, (long)rc, fdbg(fd)));
335 #ifdef USE_COOKIE_SEEK_POINTER
336 static inline int fdSeek(void * cookie, _IO_off64_t *pos, int whence) {
337 _IO_off64_t p = *pos;
339 static inline int fdSeek(void * cookie, off_t p, int whence) {
341 FD_t fd = c2f(cookie);
344 assert(fd->bytesRemain == -1); /* XXX FIXME fadio only for now */
345 fdstat_enter(fd, FDSTAT_SEEK);
346 rc = lseek(fdFileno(fd), p, whence);
347 fdstat_exit(fd, FDSTAT_SEEK, rc);
349 DBGIO(fd, (stderr, "==>\tfdSeek(%p,%ld,%d) rc %lx %s\n", cookie, (long)p, whence, (long)rc, fdbg(fd)));
354 static inline int fdClose( /*@only@*/ void * cookie) {
359 if (cookie == NULL) return -2;
365 fdstat_enter(fd, FDSTAT_CLOSE);
366 rc = ((fdno >= 0) ? close(fdno) : -2);
367 fdstat_exit(fd, FDSTAT_CLOSE, rc);
369 DBGIO(fd, (stderr, "==>\tfdClose(%p) rc %lx %s\n", fd, (long)rc, fdbg(fd)));
371 fd = fdFree(fd, "open (fdClose)");
375 static inline /*@null@*/ FD_t fdOpen(const char *path, int flags, mode_t mode) {
379 fdno = open(path, flags, mode);
380 if (fdno < 0) return NULL;
381 fd = fdNew("open (fdOpen)");
384 DBGIO(fd, (stderr, "==>\tfdOpen(\"%s\",%x,0%o) %s\n", path, flags, (unsigned)mode, fdbg(fd)));
388 static struct FDIO_s fdio_s = {
389 fdRead, fdWrite, fdSeek, fdClose, XfdLink, XfdFree, XfdNew, fdFileno,
390 fdOpen, NULL, fdGetFp, NULL, mkdir, chdir, rmdir, rename, unlink
392 FDIO_t fdio = /*@-compmempass@*/ &fdio_s /*@=compmempass@*/ ;
394 int fdWritable(FD_t fd, int secs)
398 struct timeval timeout, *tvp = (secs >= 0 ? &timeout : NULL);
401 if ((fdno = fdFileno(fd)) < 0)
402 return -1; /* XXX W2DO? */
406 FD_SET(fdno, &wrfds);
413 rc = select(fdno + 1, NULL, &wrfds, NULL, tvp);
415 if (_rpmio_debug && !(rc == 1 && errno == 0))
416 fprintf(stderr, "*** fdWritable fdno %d rc %d %s\n", fdno, rc, strerror(errno));
421 /*@notreached@*/ break;
424 /*@notreached@*/ break;
432 int fdReadable(FD_t fd, int secs)
436 struct timeval timeout, *tvp = (secs >= 0 ? &timeout : NULL);
439 if ((fdno = fdFileno(fd)) < 0)
440 return -1; /* XXX W2DO? */
444 FD_SET(fdno, &rdfds);
451 rc = select(fdno + 1, &rdfds, NULL, NULL, tvp);
457 /*@notreached@*/ break;
460 /*@notreached@*/ break;
468 static int fdFgets(FD_t fd, char * buf, size_t len)
471 int secs = fd->rd_timeoutsecs;
474 char lastchar = '\0';
476 if ((fdno = fdFileno(fd)) < 0)
477 return 0; /* XXX W2DO? */
482 /* Is there data to read? */
483 rc = fdReadable(fd, secs);
489 /*@notreached@*/ break;
490 case 0: /* timeout */
493 /*@notreached@*/ break;
494 default: /* data to read */
500 rc = fdRead(fd, buf + nb, 1);
502 rc = read(fdFileno(fd), buf + nb, 1);
505 fd->syserrno = errno;
509 /*@notreached@*/ break;
514 fprintf(stderr, "*** read: fd %p rc %d errno %d %s \"%s\"\n", fd, rc, errno, strerror(errno), buf);
517 } else if (rc == 0) {
519 fprintf(stderr, "*** read: fd %p rc %d EOF errno %d %s \"%s\"\n", fd, rc, errno, strerror(errno), buf);
524 lastchar = buf[nb - 1];
526 } while (ec == 0 && nb < len && lastchar != '\n');
528 return (ec >= 0 ? nb : ec);
531 /* =============================================================== */
532 /* Support for FTP/HTTP I/O.
534 const char *const ftpStrerror(int errorNumber) {
535 switch (errorNumber) {
539 case FTPERR_BAD_SERVER_RESPONSE:
540 return _("Bad server response");
542 case FTPERR_SERVER_IO_ERROR:
543 return _("Server IO error");
545 case FTPERR_SERVER_TIMEOUT:
546 return _("Server timeout");
548 case FTPERR_BAD_HOST_ADDR:
549 return _("Unable to lookup server host address");
551 case FTPERR_BAD_HOSTNAME:
552 return _("Unable to lookup server host name");
554 case FTPERR_FAILED_CONNECT:
555 return _("Failed to connect to server");
557 case FTPERR_FAILED_DATA_CONNECT:
558 return _("Failed to establish data connection to server");
560 case FTPERR_FILE_IO_ERROR:
561 return _("IO error to local file");
563 case FTPERR_PASSIVE_ERROR:
564 return _("Error setting remote server to passive mode");
566 case FTPERR_FILE_NOT_FOUND:
567 return _("File not found on server");
569 case FTPERR_NIC_ABORT_IN_PROGRESS:
570 return _("Abort in progress");
574 return _("Unknown or unexpected error");
578 const char *urlStrerror(const char *url)
581 switch (urlIsURL(url)) {
585 /* XXX This only works for httpReq/ftpLogin/ftpReq failures */
586 if (urlSplit(url, &u) == 0) {
587 retstr = ftpStrerror(u->openError);
589 retstr = "Malformed URL";
592 retstr = strerror(errno);
598 #if !defined(USE_ALT_DNS) || !USE_ALT_DNS
599 static int mygethostbyname(const char * host, struct in_addr * address)
601 struct hostent * hostinfo;
603 hostinfo = /*@-unrecog@*/ gethostbyname(host) /*@=unrecog@*/;
604 if (!hostinfo) return 1;
606 memcpy(address, hostinfo->h_addr_list[0], sizeof(*address));
611 static int getHostAddress(const char * host, struct in_addr * address)
613 if (isdigit(host[0])) {
614 if (! /*@-unrecog@*/ inet_aton(host, address) /*@=unrecog@*/ ) {
615 return FTPERR_BAD_HOST_ADDR;
618 if (mygethostbyname(host, address)) {
620 return FTPERR_BAD_HOSTNAME;
627 static int tcpConnect(FD_t ctrl, const char *host, int port)
629 struct sockaddr_in sin;
633 sin.sin_family = AF_INET;
634 sin.sin_port = htons(port);
635 sin.sin_addr.s_addr = INADDR_ANY;
638 if ((rc = getHostAddress(host, &sin.sin_addr)) < 0)
641 if ((fdno = socket(sin.sin_family, SOCK_STREAM, IPPROTO_IP)) < 0) {
642 rc = FTPERR_FAILED_CONNECT;
646 if (connect(fdno, (struct sockaddr *) &sin, sizeof(sin))) {
647 rc = FTPERR_FAILED_CONNECT;
656 fprintf(stderr,"++ connect %s:%d on fdno %d\n",
657 /*@-unrecog@*/ inet_ntoa(sin.sin_addr) /*@=unrecog@*/ ,
658 ntohs(sin.sin_port), fdno);
660 fdSetFdno(ctrl, (fdno >= 0 ? fdno : -1));
664 fdSetSyserrno(ctrl, errno, ftpStrerror(rc));
670 static int checkResponse(void * uu, FD_t ctrl, /*@out@*/ int *ecp, /*@out@*/ char ** str)
683 if (u->bufAlloced == 0 || u->buf == NULL) {
684 u->bufAlloced = url_iobuf_size;
685 u->buf = xcalloc(u->bufAlloced, sizeof(char));
688 bufAlloced = u->bufAlloced;
697 * Read next line from server.
699 se = buf + bufLength;
701 rc = fdFgets(ctrl, se, (bufAlloced - bufLength));
703 ec = FTPERR_BAD_SERVER_RESPONSE;
705 } else if (rc == 0 || fdWritable(ctrl, 0) < 1)
709 * Process next line from server.
711 for (s = se; *s != '\0'; s = se) {
714 while (*se && *se != '\n') se++;
716 if (se > s && se[-1] == '\r')
722 fprintf(stderr, "<- %s\n", s);
724 /* HTTP: header termination on empty line */
731 /* HTTP: look for "HTTP/1.1 123 ..." */
732 if (!strncmp(s, "HTTP", sizeof("HTTP")-1)) {
733 ctrl->contentLength = -1;
734 if ((e = strchr(s, '.')) != NULL) {
736 u->httpVersion = *e - '0';
737 if (u->httpVersion < 1 || u->httpVersion > 2)
738 ctrl->persist = u->httpVersion = 0;
742 if ((e = strchr(s, ' ')) != NULL) {
744 if (strchr("0123456789", *e))
745 strncpy(errorCode, e, 3);
751 /* HTTP: look for "token: ..." */
752 for (e = s; *e && !(*e == ' ' || *e == ':'); e++)
754 if (e > s && *e++ == ':') {
756 while (*e && *e == ' ') e++;
758 if (!strncmp(s, "Date:", ne)) {
760 if (!strncmp(s, "Server:", ne)) {
762 if (!strncmp(s, "Last-Modified:", ne)) {
764 if (!strncmp(s, "ETag:", ne)) {
767 if (!strncmp(s, "Accept-Ranges:", ne)) {
768 if (!strcmp(e, "bytes"))
770 if (!strcmp(e, "none"))
773 if (!strncmp(s, "Content-Length:", ne)) {
774 if (strchr("0123456789", *e))
775 ctrl->contentLength = atoi(e);
777 if (!strncmp(s, "Connection:", ne)) {
778 if (!strcmp(e, "close"))
782 if (!strncmp(s, "Content-Type:", ne)) {
784 if (!strncmp(s, "Transfer-Encoding:", ne)) {
785 if (!strcmp(e, "chunked"))
786 ctrl->wr_chunked = 1;
788 ctrl->wr_chunked = 0;
790 if (!strncmp(s, "Allow:", ne)) {
797 /* HTTP: look for "<TITLE>501 ... </TITLE>" */
798 if (!strncmp(s, "<TITLE>", sizeof("<TITLE>")-1))
799 s += sizeof("<TITLE>") - 1;
801 /* FTP: look for "123-" and/or "123 " */
802 if (strchr("0123456789", *s)) {
804 if (!strncmp(s, errorCode, sizeof("123")-1) && s[3] == ' ')
807 strncpy(errorCode, s, sizeof("123")-1);
815 if (moretodo && se > s) {
816 bufLength = se - s - 1;
818 memmove(buf, s, bufLength);
822 } while (moretodo && ec == 0);
825 if (ecp) *ecp = atoi(errorCode);
830 static int ftpCheckResponse(urlinfo u, /*@out@*/ char ** str)
836 rc = checkResponse(u, u->ctrl, &ec, str);
840 return FTPERR_FILE_NOT_FOUND;
841 /*@notreached@*/ break;
843 return FTPERR_NIC_ABORT_IN_PROGRESS;
844 /*@notreached@*/ break;
846 if (ec >= 400 && ec <= 599) {
847 return FTPERR_BAD_SERVER_RESPONSE;
854 static int ftpCommand(urlinfo u, char ** str, ...)
864 while ((s = va_arg(ap, const char *)) != NULL) {
868 len += sizeof("\r\n")-1;
871 t = te = alloca(len + 1);
874 while ((s = va_arg(ap, const char *)) != NULL) {
875 if (te > t) *te++ = ' ';
878 te = stpcpy(te, "\r\n");
882 fprintf(stderr, "-> %s", t);
883 if (fdWrite(u->ctrl, t, (te-t)) != (te-t))
884 return FTPERR_SERVER_IO_ERROR;
886 rc = ftpCheckResponse(u, str);
890 static int ftpLogin(urlinfo u)
894 const char * password;
899 u->ctrl = fdLink(u->ctrl, "open ctrl");
901 if (((host = (u->proxyh ? u->proxyh : u->host)) == NULL)) {
902 rc = FTPERR_BAD_HOSTNAME;
906 if ((port = (u->proxyp > 0 ? u->proxyp : u->port)) < 0) port = IPPORT_FTP;
908 if ((user = (u->proxyu ? u->proxyu : u->user)) == NULL)
911 if ((password = u->password) == NULL) {
913 struct passwd * pw = getpwuid(getuid());
914 char *myp = alloca(strlen(pw->pw_name) + sizeof("@"));
915 strcpy(myp, pw->pw_name);
923 if (fdFileno(u->ctrl) >= 0 && fdWritable(u->ctrl, 0) < 1)
926 if (fdFileno(u->ctrl) < 0) {
927 rc = tcpConnect(u->ctrl, host, port);
932 if ((rc = ftpCheckResponse(u, NULL)))
935 if ((rc = ftpCommand(u, NULL, "USER", user, NULL)))
938 if ((rc = ftpCommand(u, NULL, "PASS", password, NULL)))
941 if ((rc = ftpCommand(u, NULL, "TYPE", "I", NULL)))
947 fdSetSyserrno(u->ctrl, errno, ftpStrerror(rc));
949 if (fdFileno(u->ctrl) >= 0)
954 static int ftpReq(FD_t data, const char * ftpCmd, const char * ftpArg)
956 urlinfo u = data->url;
957 struct sockaddr_in dataAddress;
966 return FTPERR_UNKNOWN; /* XXX W2DO? */
968 cmdlen = strlen(ftpCmd) + (ftpArg ? 1+strlen(ftpArg) : 0) + sizeof("\r\n");
969 chptr = cmd = alloca(cmdlen);
970 chptr = stpcpy(chptr, ftpCmd);
973 chptr = stpcpy(chptr, ftpArg);
975 chptr = stpcpy(chptr, "\r\n");
976 cmdlen = chptr - cmd;
979 * Get the ftp version of the Content-Length.
981 if (!strncmp(cmd, "RETR", 4)) {
985 rc = ftpCommand(u, &passReply, "SIZE", ftpArg, NULL);
988 if (sscanf(passReply, "%d %u", &rc, &cl) != 2) {
989 rc = FTPERR_BAD_SERVER_RESPONSE;
993 data->contentLength = cl;
997 rc = ftpCommand(u, &passReply, "PASV", NULL);
999 rc = FTPERR_PASSIVE_ERROR;
1004 while (*chptr && *chptr != '(') chptr++;
1005 if (*chptr != '(') return FTPERR_PASSIVE_ERROR;
1008 while (*chptr && *chptr != ')') chptr++;
1009 if (*chptr != ')') return FTPERR_PASSIVE_ERROR;
1012 while (*chptr && *chptr != ',') chptr--;
1013 if (*chptr != ',') return FTPERR_PASSIVE_ERROR;
1015 while (*chptr && *chptr != ',') chptr--;
1016 if (*chptr != ',') return FTPERR_PASSIVE_ERROR;
1019 /* now passReply points to the IP portion, and chptr points to the
1020 port number portion */
1023 dataAddress.sin_family = AF_INET;
1024 if (sscanf(chptr, "%d,%d", &i, &j) != 2) {
1025 rc = FTPERR_PASSIVE_ERROR;
1028 dataAddress.sin_port = htons((((unsigned)i) << 8) + j);
1033 if (*chptr == ',') *chptr = '.';
1036 if (!inet_aton(passReply, &dataAddress.sin_addr)) {
1037 rc = FTPERR_PASSIVE_ERROR;
1041 rc = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
1042 fdSetFdno(data, (rc >= 0 ? rc : -1));
1044 rc = FTPERR_FAILED_CONNECT;
1047 data = fdLink(data, "open data (ftpReq)");
1049 /* XXX setsockopt SO_LINGER */
1050 /* XXX setsockopt SO_KEEPALIVE */
1051 /* XXX setsockopt SO_TOS IPTOS_THROUGHPUT */
1053 while (connect(fdFileno(data), (struct sockaddr *) &dataAddress,
1054 sizeof(dataAddress)) < 0) {
1057 rc = FTPERR_FAILED_DATA_CONNECT;
1062 fprintf(stderr, "-> %s", cmd);
1063 if (fdWrite(u->ctrl, cmd, cmdlen) != cmdlen) {
1064 rc = FTPERR_SERVER_IO_ERROR;
1068 if ((rc = ftpCheckResponse(u, NULL))) {
1072 data->ftpFileDoneNeeded = 1;
1073 u->ctrl = fdLink(u->ctrl, "grab data (ftpReq)");
1074 u->ctrl = fdLink(u->ctrl, "open data (ftpReq)");
1078 fdSetSyserrno(u->ctrl, errno, ftpStrerror(rc));
1079 if (fdFileno(data) >= 0)
1084 static int urlConnect(const char * url, /*@out@*/ urlinfo * uret)
1089 if (urlSplit(url, &u) < 0)
1092 if (u->urltype == URL_IS_FTP) {
1095 if ((fd = u->ctrl) == NULL) {
1096 fd = u->ctrl = fdNew("persist ctrl (urlConnect FTP)");
1097 fdSetIo(u->ctrl, ufdio);
1100 fd->rd_timeoutsecs = ftpTimeoutSecs;
1101 fd->contentLength = fd->bytesRemain = -1;
1102 fd->url = NULL; /* XXX FTP ctrl has not */
1103 fd->ftpFileDoneNeeded = 0;
1104 fd = fdLink(fd, "grab ctrl (urlConnect FTP)");
1106 if (fdFileno(u->ctrl) < 0) {
1107 rpmMessage(RPMMESS_DEBUG, _("logging into %s as %s, pw %s\n"),
1109 u->user ? u->user : "ftp",
1110 u->password ? u->password : "(username)");
1112 if ((rc = ftpLogin(u)) < 0) { /* XXX save ftpLogin error */
1113 u->ctrl = fdFree(fd, "grab ctrl (urlConnect FTP)");
1120 *uret = urlLink(u, "urlConnect");
1121 u = urlFree(u, "urlSplit (urlConnect)");
1126 /*@null@*/ static rpmCallbackFunction urlNotify = NULL;
1127 /*@null@*/ static void * urlNotifyData = NULL;
1128 static int urlNotifyCount = -1;
1130 void urlSetCallback(rpmCallbackFunction notify, void *notifyData, int notifyCount) {
1132 urlNotifyData = notifyData;
1133 urlNotifyCount = (notifyCount >= 0) ? notifyCount : 4096;
1136 int ufdCopy(FD_t sfd, FD_t tfd)
1140 int itemsCopied = 0;
1145 (void)(*urlNotify) (NULL, RPMCALLBACK_INST_OPEN_FILE,
1146 0, 0, NULL, urlNotifyData);
1150 rc = Fread(buf, sizeof(buf[0]), sizeof(buf), sfd);
1158 rc = Fwrite(buf, sizeof(buf[0]), itemsRead, tfd);
1161 if (rc != itemsRead) {
1162 rc = FTPERR_FILE_IO_ERROR;
1166 itemsCopied += itemsRead;
1167 if (urlNotify && urlNotifyCount > 0) {
1168 int n = itemsCopied/urlNotifyCount;
1169 if (n != notifier) {
1170 (void)(*urlNotify) (NULL, RPMCALLBACK_INST_PROGRESS,
1171 itemsCopied, 0, NULL, urlNotifyData);
1177 DBGIO(sfd, (stderr, "++ copied %d bytes: %s\n", itemsCopied,
1181 (void)(*urlNotify) (NULL, RPMCALLBACK_INST_OPEN_FILE,
1182 itemsCopied, itemsCopied, NULL, urlNotifyData);
1188 int ufdGetFile(FD_t sfd, FD_t tfd)
1194 rc = ufdCopy(sfd, tfd);
1196 if (rc > 0) /* XXX ufdCopy now returns no. bytes copied */
1201 static int ftpCmd(const char * cmd, const char * url, const char * arg2) {
1206 if (urlConnect(url, &u) < 0)
1209 (void) urlPath(url, &path);
1211 rc = ftpCommand(u, NULL, cmd, path, arg2, NULL);
1212 u->ctrl = fdFree(u->ctrl, "grab ctrl (ftpCmd)");
1216 static int ftpMkdir(const char * path, /*@unused@*/ mode_t mode) {
1218 if ((rc = ftpCmd("MKD", path, NULL)) != 0)
1222 sprintf(buf, " 0%o", mode);
1223 (void) ftpCmd("SITE CHMOD", path, buf);
1229 static int ftpChdir(const char * path) {
1230 return ftpCmd("CWD", path, NULL);
1233 static int ftpRmdir(const char * path) {
1234 return ftpCmd("RMD", path, NULL);
1237 static int ftpRename(const char * oldpath, const char * newpath) {
1239 if ((rc = ftpCmd("RNFR", oldpath, NULL)) != 0)
1241 return ftpCmd("RNTO", newpath, NULL);
1244 static int ftpUnlink(const char * path) {
1245 return ftpCmd("DELE", path, NULL);
1248 /* XXX these aren't worth the pain of including correctly */
1250 #define IAC 255 /* interpret as command: */
1253 #define IP 244 /* interrupt process--permanently */
1256 #define DM 242 /* data mark--for connect. cleaning */
1258 #if !defined(SHUT_RDWR)
1259 #define SHUT_RDWR 1+1
1262 static int ftpAbort(urlinfo u, FD_t data) {
1263 static unsigned char ipbuf[3] = { IAC, IP, IAC };
1271 data->ftpFileDoneNeeded = 0;
1272 if (fdFileno(data) >= 0)
1273 u->ctrl = fdFree(u->ctrl, "open data (ftpAbort)");
1274 u->ctrl = fdFree(u->ctrl, "grab data (ftpAbort)");
1278 DBGIO(0, (stderr, "-> ABOR\n"));
1280 if (send(fdFileno(ctrl), ipbuf, sizeof(ipbuf), MSG_OOB) != sizeof(ipbuf)) {
1282 return FTPERR_SERVER_IO_ERROR;
1285 sprintf(u->buf, "%cABOR\r\n",(char) DM);
1286 if (fdWrite(ctrl, u->buf, 7) != 7) {
1288 return FTPERR_SERVER_IO_ERROR;
1291 if (data && fdFileno(data) >= 0) {
1292 /* XXX shorten data drain time wait */
1293 tosecs = data->rd_timeoutsecs;
1294 data->rd_timeoutsecs = 10;
1295 if (fdReadable(data, data->rd_timeoutsecs) > 0) {
1296 while (timedRead(data, u->buf, u->bufAlloced) > 0)
1299 data->rd_timeoutsecs = tosecs;
1300 /* XXX ftp abort needs to close the data channel to receive status */
1301 shutdown(fdFileno(data), SHUT_RDWR);
1302 close(fdFileno(data));
1303 data->fps[0].fdno = -1; /* XXX WRONG but expedient */
1306 /* XXX shorten ctrl drain time wait */
1307 tosecs = u->ctrl->rd_timeoutsecs;
1308 u->ctrl->rd_timeoutsecs = 10;
1309 if ((rc = ftpCheckResponse(u, NULL)) == FTPERR_NIC_ABORT_IN_PROGRESS) {
1310 rc = ftpCheckResponse(u, NULL);
1312 rc = ftpCheckResponse(u, NULL);
1313 u->ctrl->rd_timeoutsecs = tosecs;
1318 static int ftpFileDone(urlinfo u, FD_t data)
1323 assert(data->ftpFileDoneNeeded);
1325 if (data->ftpFileDoneNeeded) {
1326 data->ftpFileDoneNeeded = 0;
1327 u->ctrl = fdFree(u->ctrl, "open data (ftpFileDone)");
1328 u->ctrl = fdFree(u->ctrl, "grab data (ftpFileDone)");
1329 rc = ftpCheckResponse(u, NULL);
1334 static int httpResp(urlinfo u, FD_t ctrl, /*@out@*/ char ** str)
1340 rc = checkResponse(u, ctrl, &ec, str);
1342 if (_ftp_debug && !(rc == 0 && ec == 200))
1343 fprintf(stderr, "*** httpResp: rc %d ec %d\n", rc, ec);
1349 rc = FTPERR_FILE_NOT_FOUND;
1356 static int httpReq(FD_t ctrl, const char * httpCmd, const char * httpArg)
1358 urlinfo u = ctrl->url;
1368 assert(ctrl != NULL);
1370 if (((host = (u->proxyh ? u->proxyh : u->host)) == NULL))
1371 return FTPERR_BAD_HOSTNAME;
1373 if ((port = (u->proxyp > 0 ? u->proxyp : u->port)) < 0) port = 80;
1374 path = (u->proxyh || u->proxyp > 0) ? u->url : httpArg;
1377 if (fdFileno(ctrl) >= 0 && (rc = fdWritable(ctrl, 0)) < 1) {
1381 if (fdFileno(ctrl) < 0) {
1382 rc = tcpConnect(ctrl, host, port);
1385 ctrl = fdLink(ctrl, "open ctrl (httpReq)");
1390 User-Agent: rpm/3.0.4\r\n\
1392 Accept: text/plain\r\n\
1393 Transfer-Encoding: chunked\r\n\
1395 ") + strlen(httpCmd) + strlen(path) + sizeof(VERSION) + strlen(host) + 20;
1400 if (!strcmp(httpCmd, "PUT")) {
1402 %s %s HTTP/1.%d\r\n\
1403 User-Agent: rpm/%s\r\n\
1405 Accept: text/plain\r\n\
1406 Transfer-Encoding: chunked\r\n\
1408 ", httpCmd, path, (u->httpVersion ? 1 : 0), VERSION, host, port);
1411 %s %s HTTP/1.%d\r\n\
1412 User-Agent: rpm/%s\r\n\
1414 Accept: text/plain\r\n\
1416 ", httpCmd, path, (u->httpVersion ? 1 : 0), VERSION, host, port);
1420 fprintf(stderr, "-> %s", req);
1423 if (fdWrite(ctrl, req, len) != len) {
1424 rc = FTPERR_SERVER_IO_ERROR;
1428 if (!strcmp(httpCmd, "PUT")) {
1429 ctrl->wr_chunked = 1;
1432 rc = httpResp(u, ctrl, NULL);
1435 if (!retrying) { /* not HTTP_OK */
1444 ctrl = fdLink(ctrl, "open data (httpReq)");
1448 fdSetSyserrno(ctrl, errno, ftpStrerror(rc));
1450 if (fdFileno(ctrl) >= 0)
1455 /* XXX DYING: unused */
1456 void * ufdGetUrlinfo(FD_t fd) {
1458 if (fd->url == NULL)
1460 return urlLink(fd->url, "ufdGetUrlinfo");
1463 /* =============================================================== */
1464 static ssize_t ufdRead(void * cookie, /*@out@*/ char * buf, size_t count) {
1465 FD_t fd = c2f(cookie);
1469 /* XXX preserve timedRead() behavior */
1470 if (fdGetIo(fd) == fdio) {
1472 int fdno = fdFileno(fd);
1474 if (S_ISREG(sb.st_mode))
1475 return fdRead(fd, buf, count);
1479 assert(fd->rd_timeoutsecs >= 0);
1481 for (total = 0; total < count; total += bytesRead) {
1487 /* Is there data to read? */
1488 if (fd->bytesRemain == 0) return total; /* XXX simulate EOF */
1489 rc = fdReadable(fd, fd->rd_timeoutsecs);
1492 case -1: /* error */
1493 case 0: /* timeout */
1495 /*@notreached@*/ break;
1496 default: /* data to read */
1500 rc = fdRead(fd, buf + total, count - total);
1506 /*@notreached@*/ break;
1511 fprintf(stderr, "*** read: rc %d errno %d %s \"%s\"\n", rc, errno, strerror(errno), buf);
1513 /*@notreached@*/ break;
1514 } else if (rc == 0) {
1516 /*@notreached@*/ break;
1524 static ssize_t ufdWrite(void * cookie, const char * buf, size_t count)
1526 FD_t fd = c2f(cookie);
1531 if (fdGetIo(fd) == fdio) {
1533 fstat(fdGetFdno(fd), &sb);
1534 if (S_ISREG(sb.st_mode))
1535 return fdWrite(fd, buf, count);
1541 for (total = 0; total < count; total += bytesWritten) {
1547 /* Is there room to write data? */
1548 if (fd->bytesRemain == 0) {
1549 fprintf(stderr, "*** ufdWrite fd %p WRITE PAST END OF CONTENT\n", fd);
1550 return total; /* XXX simulate EOF */
1552 rc = fdWritable(fd, 2); /* XXX configurable? */
1555 case -1: /* error */
1556 case 0: /* timeout */
1558 /*@notreached@*/ break;
1559 default: /* data to write */
1563 rc = fdWrite(fd, buf + total, count - total);
1569 /*@notreached@*/ break;
1574 fprintf(stderr, "*** write: rc %d errno %d %s \"%s\"\n", rc, errno, strerror(errno), buf);
1576 /*@notreached@*/ break;
1577 } else if (rc == 0) {
1579 /*@notreached@*/ break;
1587 #ifdef USE_COOKIE_SEEK_POINTER
1588 static inline int ufdSeek(void * cookie, _IO_off64_t *pos, int whence) {
1590 static inline int ufdSeek(void * cookie, off_t pos, int whence) {
1592 FD_t fd = c2f(cookie);
1594 switch (fd->urlType) {
1595 case URL_IS_UNKNOWN:
1603 /*@notreached@*/ break;
1605 return fdSeek(cookie, pos, whence);
1608 static int ufdClose( /*@only@*/ void * cookie)
1610 FD_t fd = c2f(cookie);
1615 urlinfo u = fd->url;
1618 fd = u->data = fdFree(fd, "grab data (ufdClose persist)");
1620 fd = fdFree(fd, "grab data (ufdClose)");
1621 (void) urlFree(fd->url, "url (ufdClose)");
1623 u->ctrl = fdFree(u->ctrl, "grab ctrl (ufdClose)");
1625 if (u->urltype == URL_IS_FTP) {
1627 /* XXX if not using libio, lose the fp from fpio */
1628 { FILE * fp = fdGetFILE(fd);
1634 * Normal FTP has 4 refs on the data fd:
1635 * "persist data (ufdOpen FTP)" rpmio.c:888
1636 * "grab data (ufdOpen FTP)" rpmio.c:892
1637 * "open data (ftpReq)" ftp.c:633
1638 * "fopencookie" rpmio.c:1507
1642 * Normal FTP has 5 refs on the ctrl fd:
1643 * "persist ctrl" url.c:176
1644 * "grab ctrl (urlConnect FTP)" rpmio.c:404
1645 * "open ctrl" ftp.c:504
1646 * "grab data (ftpReq)" ftp.c:661
1647 * "open data (ftpReq)" ftp.c:662
1649 if (fd->bytesRemain > 0) {
1650 if (fd->ftpFileDoneNeeded) {
1651 if (fdReadable(u->ctrl, 0) > 0)
1658 /* XXX STOR et al require close before ftpFileDone */
1660 #if 0 /* XXX error exit from ufdOpen does not have this set */
1661 assert(fd->ftpFileDoneNeeded != 0);
1663 if (fd->ftpFileDoneNeeded)
1669 if (!strcmp(u->service, "http")) {
1670 if (fd->wr_chunked) {
1672 /* XXX HTTP PUT requires terminating 0 length chunk. */
1673 fdWrite(fd, NULL, 0);
1675 /* XXX HTTP PUT requires terminating entity-header. */
1677 fprintf(stderr, "-> \r\n");
1678 (void) fdWrite(fd, "\r\n", sizeof("\r\n")-1);
1679 rc = httpResp(u, fd, NULL);
1683 fd = u->ctrl = fdFree(fd, "open data (ufdClose HTTP persist ctrl)");
1684 else if (fd == u->data)
1685 fd = u->data = fdFree(fd, "open data (ufdClose HTTP persist data)");
1687 fd = fdFree(fd, "open data (ufdClose HTTP)");
1690 * HTTP has 4 (or 5 if persistent malloc) refs on the fd:
1691 * "persist ctrl" url.c:177
1692 * "grab ctrl (ufdOpen HTTP)" rpmio.c:924
1693 * "grab data (ufdOpen HTTP)" rpmio.c:928
1694 * "open ctrl (httpReq)" ftp.c:382
1695 * "open data (httpReq)" ftp.c:435
1698 /* XXX if not using libio, lose the fp from fpio */
1699 { FILE * fp = fdGetFILE(fd);
1704 if (fd->persist && u->httpVersion &&
1705 (fd == u->ctrl || fd == u->data) && fd->bytesRemain == 0) {
1706 fd->contentLength = fd->bytesRemain = -1;
1709 fd->contentLength = fd->bytesRemain = -1;
1716 static /*@null@*/ FD_t ftpOpen(const char *url, /*@unused@*/ int flags,
1717 /*@unused@*/ mode_t mode, /*@out@*/ urlinfo *uret)
1722 #if 0 /* XXX makeTempFile() heartburn */
1723 assert(!(flags & O_RDWR));
1725 if (urlConnect(url, &u) < 0)
1728 if (u->data == NULL)
1729 u->data = fdNew("persist data (ftpOpen)");
1731 if (u->data->url == NULL)
1732 fd = fdLink(u->data, "grab data (ftpOpen persist data)");
1734 fd = fdNew("grab data (ftpOpen)");
1738 fd->ftpFileDoneNeeded = 0;
1739 fd->rd_timeoutsecs = ftpTimeoutSecs;
1740 fd->contentLength = fd->bytesRemain = -1;
1741 fd->url = urlLink(u, "url (ufdOpen FTP)");
1742 fd->urlType = URL_IS_FTP;
1751 static /*@null@*/ FD_t httpOpen(const char *url, int flags, mode_t mode,
1752 /*@out@*/ urlinfo *uret)
1757 #if 0 /* XXX makeTempFile() heartburn */
1758 assert(!(flags & O_RDWR));
1760 if (urlSplit(url, &u))
1763 if (u->ctrl == NULL)
1764 u->ctrl = fdNew("persist ctrl (httpOpen)");
1765 if (u->ctrl->nrefs > 2 && u->data == NULL)
1766 u->data = fdNew("persist data (httpOpen)");
1768 if (u->ctrl->url == NULL)
1769 fd = fdLink(u->ctrl, "grab ctrl (httpOpen persist ctrl)");
1770 else if (u->data->url == NULL)
1771 fd = fdLink(u->data, "grab ctrl (httpOpen persist data)");
1773 fd = fdNew("grab ctrl (httpOpen)");
1777 fd->ftpFileDoneNeeded = 0;
1778 fd->rd_timeoutsecs = httpTimeoutSecs;
1779 fd->contentLength = fd->bytesRemain = -1;
1780 fd->url = urlLink(u, "url (httpOpen)");
1781 fd = fdLink(fd, "grab data (httpOpen)");
1782 fd->urlType = URL_IS_HTTP;
1791 static /*@null@*/ FD_t ufdOpen(const char *url, int flags, mode_t mode)
1797 urltype urlType = urlPath(url, &path);
1800 fprintf(stderr, "*** ufdOpen(%s,0x%x,0%o)\n", url, flags, (unsigned)mode);
1804 fd = ftpOpen(url, flags, mode, &u);
1805 if (fd == NULL || u == NULL)
1808 /* XXX W2DO? use STOU rather than STOR to prevent clobbering */
1809 cmd = ((flags & O_WRONLY)
1810 ? ((flags & O_APPEND) ? "APPE" :
1811 ((flags & O_CREAT) ? "STOR" : "STOR"))
1812 : ((flags & O_CREAT) ? "STOR" : "RETR"));
1813 u->openError = ftpReq(fd, cmd, path);
1814 if (u->openError < 0) {
1815 /* XXX make sure that we can exit through ufdClose */
1816 fd = fdLink(fd, "error data (ufdOpen FTP)");
1818 fd->bytesRemain = ((!strcmp(cmd, "RETR"))
1819 ? fd->contentLength : -1);
1824 fd = httpOpen(url, flags, mode, &u);
1825 if (fd == NULL || u == NULL)
1828 cmd = ((flags & O_WRONLY)
1829 ? ((flags & O_APPEND) ? "PUT" :
1830 ((flags & O_CREAT) ? "PUT" : "PUT"))
1832 u->openError = httpReq(fd, cmd, path);
1833 if (u->openError < 0) {
1834 /* XXX make sure that we can exit through ufdClose */
1835 fd = fdLink(fd, "error ctrl (ufdOpen HTTP)");
1836 fd = fdLink(fd, "error data (ufdOpen HTTP)");
1838 fd->bytesRemain = ((!strcmp(cmd, "GET"))
1839 ? fd->contentLength : -1);
1840 fd->wr_chunked = ((!strcmp(cmd, "PUT"))
1841 ? fd->wr_chunked : 0);
1845 assert(!(flags & O_RDWR));
1846 fd = fdDup( ((flags & O_WRONLY) ? STDOUT_FILENO : STDIN_FILENO) );
1849 fd->rd_timeoutsecs = 600; /* XXX W2DO? 10 mins? */
1850 fd->contentLength = fd->bytesRemain = -1;
1854 case URL_IS_UNKNOWN:
1856 fd = fdOpen(path, flags, mode);
1859 fd->rd_timeoutsecs = 1;
1860 fd->contentLength = fd->bytesRemain = -1;
1865 if (fd == NULL) return NULL;
1866 fd->urlType = urlType;
1867 if (Fileno(fd) < 0) {
1871 DBGIO(fd, (stderr, "==>\tufdOpen(\"%s\",%x,0%o) %s\n", url, flags, (unsigned)mode, fdbg(fd)));
1875 static struct FDIO_s ufdio_s = {
1876 ufdRead, ufdWrite, ufdSeek, ufdClose, XfdLink, XfdFree, XfdNew, fdFileno,
1877 ufdOpen, NULL, fdGetFp, NULL, Mkdir, Chdir, Rmdir, Rename, Unlink
1879 FDIO_t ufdio = /*@-compmempass@*/ &ufdio_s /*@=compmempass@*/ ;
1881 /* =============================================================== */
1882 /* Support for GZIP library.
1888 static inline /*@dependent@*/ /*@null@*/ void * gzdFileno(FD_t fd) {
1893 for (i = fd->nfps; i >= 0; i--) {
1894 FDSTACK_t * fps = &fd->fps[i];
1895 if (fps->io != gzdio)
1904 static /*@null@*/ FD_t gzdOpen(const char *path, const char *fmode) {
1907 if ((gzfile = gzopen(path, fmode)) == NULL)
1909 fd = fdNew("open (gzdOpen)");
1910 fdPop(fd); fdPush(fd, gzdio, gzfile, -1);
1912 DBGIO(fd, (stderr, "==>\tgzdOpen(\"%s\", \"%s\") fd %p %s\n", path, fmode, fd, fdbg(fd)));
1913 return fdLink(fd, "gzdOpen");
1916 static /*@null@*/ FD_t gzdFdopen(void * cookie, const char *fmode) {
1917 FD_t fd = c2f(cookie);
1921 if (fmode == NULL) return NULL;
1922 fdno = fdFileno(fd);
1923 fdSetFdno(fd, -1); /* XXX skip the fdio close */
1924 if (fdno < 0) return NULL;
1925 gzfile = gzdopen(fdno, fmode);
1926 if (gzfile == NULL) return NULL;
1928 fdPush(fd, gzdio, gzfile, fdno); /* Push gzdio onto stack */
1930 return fdLink(fd, "gzdFdopen");
1933 static int gzdFlush(FD_t fd) {
1934 return gzflush(gzdFileno(fd), Z_SYNC_FLUSH); /* XXX W2DO? */
1937 /* =============================================================== */
1938 static ssize_t gzdRead(void * cookie, /*@out@*/ char * buf, size_t count) {
1939 FD_t fd = c2f(cookie);
1943 if (fd->bytesRemain == 0) return 0; /* XXX simulate EOF */
1944 gzfile = gzdFileno(fd);
1945 fdstat_enter(fd, FDSTAT_READ);
1946 rc = gzread(gzfile, buf, count);
1947 DBGIO(fd, (stderr, "==>\tgzdRead(%p,%p,%u) rc %lx %s\n", cookie, buf, (unsigned)count, (long)rc, fdbg(fd)));
1950 fd->errcookie = gzerror(gzfile, &zerror);
1951 if (zerror == Z_ERRNO) {
1952 fd->syserrno = errno;
1953 fd->errcookie = strerror(fd->syserrno);
1955 } else if (rc >= 0) {
1956 fdstat_exit(fd, FDSTAT_READ, rc);
1961 static ssize_t gzdWrite(void * cookie, const char * buf, size_t count) {
1962 FD_t fd = c2f(cookie);
1966 if (fd->bytesRemain == 0) return 0; /* XXX simulate EOF */
1967 gzfile = gzdFileno(fd);
1968 fdstat_enter(fd, FDSTAT_WRITE);
1969 rc = gzwrite(gzfile, (void *)buf, count);
1970 DBGIO(fd, (stderr, "==>\tgzdWrite(%p,%p,%u) rc %lx %s\n", cookie, buf, (unsigned)count, (long)rc, fdbg(fd)));
1973 fd->errcookie = gzerror(gzfile, &zerror);
1974 if (zerror == Z_ERRNO) {
1975 fd->syserrno = errno;
1976 fd->errcookie = strerror(fd->syserrno);
1978 } else if (rc > 0) {
1979 fdstat_exit(fd, FDSTAT_WRITE, rc);
1984 /* XXX zlib-1.0.4 has not */
1985 #ifdef USE_COOKIE_SEEK_POINTER
1986 static inline int gzdSeek(void * cookie, _IO_off64_t *pos, int whence) {
1987 _IO_off64_t p = *pos;
1989 static inline int gzdSeek(void * cookie, off_t p, int whence) {
1993 FD_t fd = c2f(cookie);
1996 assert(fd->bytesRemain == -1); /* XXX FIXME */
1997 gzfile = gzdFileno(fd);
1998 fdstat_enter(fd, FDSTAT_SEEK);
1999 rc = gzseek(gzfile, p, whence);
2000 DBGIO(fd, (stderr, "==>\tgzdSeek(%p,%ld,%d) rc %lx %s\n", cookie, (long)p, whence, (long)rc, fdbg(fd)));
2003 fd->errcookie = gzerror(gzfile, &zerror);
2004 if (zerror == Z_ERRNO) {
2005 fd->syserrno = errno;
2006 fd->errcookie = strerror(fd->syserrno);
2008 } else if (rc >= 0) {
2009 fdstat_exit(fd, FDSTAT_SEEK, rc);
2017 static int gzdClose( /*@only@*/ void * cookie) {
2018 FD_t fd = c2f(cookie);
2022 gzfile = gzdFileno(fd);
2024 if (gzfile == NULL) return -2;
2025 fdstat_enter(fd, FDSTAT_CLOSE);
2026 rc = gzclose(gzfile);
2028 /* XXX TODO: preserve fd if errors */
2031 DBGIO(fd, (stderr, "==>\tgzdClose(%p) zerror %d %s\n", cookie, rc, fdbg(fd)));
2033 fd->errcookie = gzerror(gzfile, &rc);
2034 if (rc == Z_ERRNO) {
2035 fd->syserrno = errno;
2036 fd->errcookie = strerror(fd->syserrno);
2038 } else if (rc >= 0) {
2039 fdstat_exit(fd, FDSTAT_CLOSE, rc);
2043 DBGIO(fd, (stderr, "==>\tgzdClose(%p) rc %lx %s\n", cookie, (long)rc, fdbg(fd)));
2045 if (_rpmio_debug || rpmIsDebug()) fdstat_print(fd, "GZDIO", stderr);
2047 fd = fdFree(fd, "open (gzdClose)");
2051 static struct FDIO_s gzdio_s = {
2052 gzdRead, gzdWrite, gzdSeek, gzdClose, XfdLink, XfdFree, XfdNew, fdFileno,
2053 NULL, gzdOpen, gzdFileno, gzdFlush, NULL, NULL, NULL, NULL, NULL
2055 FDIO_t gzdio = /*@-compmempass@*/ &gzdio_s /*@=compmempass@*/ ;
2057 #endif /* HAVE_ZLIB_H */
2059 /* =============================================================== */
2060 /* Support for BZIP2 library.
2067 # define bzopen BZ2_bzopen
2068 # define bzclose BZ2_bzclose
2069 # define bzdopen BZ2_bzdopen
2070 # define bzerror BZ2_bzerror
2071 # define bzflush BZ2_bzflush
2072 # define bzread BZ2_bzread
2073 # define bzwrite BZ2_bzwrite
2074 #endif /* HAVE_BZ2_1_0 */
2076 static inline /*@dependent@*/ /*@null@*/ void * bzdFileno(FD_t fd) {
2081 for (i = fd->nfps; i >= 0; i--) {
2082 FDSTACK_t * fps = &fd->fps[i];
2083 if (fps->io != bzdio)
2092 static /*@null@*/ FD_t bzdOpen(const char *path, const char *mode) {
2095 if ((bzfile = bzopen(path, mode)) == NULL)
2097 fd = fdNew("open (bzdOpen)");
2098 fdPop(fd); fdPush(fd, bzdio, bzfile, -1);
2099 return fdLink(fd, "bzdOpen");
2102 static /*@null@*/ FD_t bzdFdopen(void * cookie, const char * fmode) {
2103 FD_t fd = c2f(cookie);
2107 if (fmode == NULL) return NULL;
2108 fdno = fdFileno(fd);
2109 fdSetFdno(fd, -1); /* XXX skip the fdio close */
2110 if (fdno < 0) return NULL;
2111 bzfile = bzdopen(fdno, fmode);
2112 if (bzfile == NULL) return NULL;
2114 fdPush(fd, bzdio, bzfile, fdno); /* Push bzdio onto stack */
2116 return fdLink(fd, "bzdFdopen");
2119 static int bzdFlush(FD_t fd) {
2120 return bzflush(bzdFileno(fd));
2123 /* =============================================================== */
2124 static ssize_t bzdRead(void * cookie, /*@out@*/ char * buf, size_t count) {
2125 FD_t fd = c2f(cookie);
2129 if (fd->bytesRemain == 0) return 0; /* XXX simulate EOF */
2130 bzfile = bzdFileno(fd);
2131 fdstat_enter(fd, FDSTAT_READ);
2132 rc = bzread(bzfile, buf, count);
2135 fd->errcookie = bzerror(bzfile, &zerror);
2136 } else if (rc >= 0) {
2137 fdstat_exit(fd, FDSTAT_READ, rc);
2142 static ssize_t bzdWrite(void * cookie, const char * buf, size_t count) {
2143 FD_t fd = c2f(cookie);
2147 if (fd->bytesRemain == 0) return 0; /* XXX simulate EOF */
2148 bzfile = bzdFileno(fd);
2149 fdstat_enter(fd, FDSTAT_WRITE);
2150 rc = bzwrite(bzfile, (void *)buf, count);
2153 fd->errcookie = bzerror(bzfile, &zerror);
2154 } else if (rc > 0) {
2155 fdstat_exit(fd, FDSTAT_WRITE, rc);
2160 #ifdef USE_COOKIE_SEEK_POINTER
2161 static inline int bzdSeek(void * cookie, _IO_off64_t *pos, int whence) {
2163 static inline int bzdSeek(void * cookie, off_t p, int whence) {
2165 FD_t fd = c2f(cookie);
2171 static int bzdClose( /*@only@*/ void * cookie) {
2172 FD_t fd = c2f(cookie);
2176 bzfile = bzdFileno(fd);
2178 if (bzfile == NULL) return -2;
2179 fdstat_enter(fd, FDSTAT_CLOSE);
2181 rc = 0; /* XXX FIXME */
2183 /* XXX TODO: preserve fd if errors */
2188 fd->errcookie = bzerror(bzfile, &zerror);
2189 } else if (rc >= 0) {
2190 fdstat_exit(fd, FDSTAT_CLOSE, rc);
2194 DBGIO(fd, (stderr, "==>\tbzdClose(%p) rc %lx %s\n", cookie, (long)rc, fdbg(fd)));
2196 if (_rpmio_debug || rpmIsDebug()) fdstat_print(fd, "BZDIO", stderr);
2198 fd = fdFree(fd, "open (bzdClose)");
2202 static struct FDIO_s bzdio_s = {
2203 bzdRead, bzdWrite, bzdSeek, bzdClose, XfdLink, XfdFree, XfdNew, fdFileno,
2204 NULL, bzdOpen, bzdFileno, bzdFlush, NULL, NULL, NULL, NULL, NULL
2206 FDIO_t bzdio = /*@-compmempass@*/ &bzdio_s /*@=compmempass@*/ ;
2208 #endif /* HAVE_BZLIB_H */
2210 /* =============================================================== */
2211 /*@observer@*/ static const char * getFdErrstr (FD_t fd) {
2212 const char *errstr = NULL;
2215 if (fdGetIo(fd) == gzdio) {
2216 errstr = fd->errcookie;
2218 #endif /* HAVE_ZLIB_H */
2221 if (fdGetIo(fd) == bzdio) {
2222 errstr = fd->errcookie;
2224 #endif /* HAVE_BZLIB_H */
2227 errstr = strerror(fd->syserrno);
2233 /* =============================================================== */
2235 const char *Fstrerror(FD_t fd) {
2237 return strerror(errno);
2239 return getFdErrstr(fd);
2242 #define FDIOVEC(_fd, _vec) \
2243 ((fdGetIo(_fd) && fdGetIo(_fd)->_vec) ? fdGetIo(_fd)->_vec : NULL)
2245 size_t Fread(void *buf, size_t size, size_t nmemb, FD_t fd) {
2246 fdio_read_function_t *_read;
2251 *(char *)buf = '\0';
2253 DBGIO(fd, (stderr, "==> Fread(%p,%u,%u,%p) %s\n", buf, (unsigned)size, (unsigned)nmemb, fd, fdbg(fd)));
2255 if (fdGetIo(fd) == fpio) {
2256 rc = fread(buf, size, nmemb, fdGetFILE(fd));
2260 _read = FDIOVEC(fd, read);
2262 rc = (_read ? (*_read) (fd, buf, size * nmemb) : -2);
2266 size_t Fwrite(const void *buf, size_t size, size_t nmemb, FD_t fd) {
2267 fdio_write_function_t *_write;
2271 DBGIO(fd, (stderr, "==> Fwrite(%p,%u,%u,%p) %s\n", buf, (unsigned)size, (unsigned)nmemb, fd, fdbg(fd)));
2273 if (fdGetIo(fd) == fpio) {
2274 rc = fwrite(buf, size, nmemb, fdGetFILE(fd));
2278 _write = FDIOVEC(fd, write);
2280 rc = (_write ? _write(fd, buf, size * nmemb) : -2);
2284 #ifdef USE_COOKIE_SEEK_POINTER
2285 int Fseek(FD_t fd, _IO_off64_t offset, int whence) {
2287 int Fseek(FD_t fd, off_t offset, int whence) {
2289 fdio_seek_function_t *_seek;
2293 DBGIO(fd, (stderr, "==> Fseek(%p,%ld,%d) %s\n", fd, (long)offset, whence, fdbg(fd)));
2295 if (fdGetIo(fd) == fpio) {
2299 rc = fseek(f, offset, whence);
2303 _seek = FDIOVEC(fd, seek);
2305 #ifdef USE_COOKIE_SEEK_POINTER
2306 rc = (_seek ? _seek(fd, &offset, whence) : -2);
2308 rc = (_seek ? _seek(fd, offset, whence) : -2);
2313 int Fclose(FD_t fd) {
2317 DBGIO(fd, (stderr, "==> Fclose(%p) %s\n", fd, fdbg(fd)));
2319 fd = fdLink(fd, "Fclose");
2320 while (fd->nfps >= 0) {
2321 FDSTACK_t * fps = &fd->fps[fd->nfps];
2323 if (fps->io == fpio) {
2324 FILE *fp = fdGetFILE(fd);
2325 int fpno = fileno(fp);
2327 /* XXX persistent HTTP/1.1 returns the previously opened fp */
2328 if (fd->nfps > 0 && fpno == -1 &&
2329 fd->fps[fd->nfps-1].io == ufdio &&
2330 fd->fps[fd->nfps-1].fp == fp &&
2331 fd->fps[fd->nfps-1].fdno >= 0)
2336 if (fdGetFdno(fd) >= 0)
2347 fd = fdFree(fd, "fopencookie (Fclose)");
2352 fdio_close_function_t * _close = FDIOVEC(fd, close);
2361 fd = fdFree(fd, "Fclose");
2366 * Convert stdio fmode to open(2) mode, filtering out zlib/bzlib flags.
2367 * returns stdio[0] = '\0' on error.
2369 * gzopen: [0-9] is compession level
2370 * gzopen: 'f' is filtered (Z_FILTERED)
2371 * gzopen: 'h' is Huffman encoding (Z_HUFFMAN_ONLY)
2372 * bzopen: [1-9] is block size (modulo 100K)
2373 * bzopen: 's' is smallmode
2374 * HACK: '.' terminates, rest is type of I/O
2376 static inline void cvtfmode (const char *m,
2377 /*@out@*/ char *stdio, size_t nstdio,
2378 /*@out@*/ char *other, size_t nother,
2379 /*@out@*/ const char **end, /*@out@*/ int * f)
2386 flags |= O_WRONLY | O_CREAT | O_APPEND;
2387 if (--nstdio > 0) *stdio++ = *m;
2390 flags |= O_WRONLY | O_CREAT | O_TRUNC;
2391 if (--nstdio > 0) *stdio++ = *m;
2395 if (--nstdio > 0) *stdio++ = *m;
2400 /*@notreached@*/ break;
2404 while ((c = *m++) != '\0') {
2409 flags &= ~(O_RDONLY|O_WRONLY);
2411 if (--nstdio > 0) *stdio++ = c;
2414 if (--nstdio > 0) *stdio++ = c;
2418 if (--nstdio > 0) *stdio++ = c;
2421 if (--nother > 0) *other++ = c;
2427 *stdio = *other = '\0';
2429 *end = (*m ? m : NULL);
2435 #if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ == 0
2436 /* XXX retrofit glibc-2.1.x typedef on glibc-2.0.x systems */
2437 typedef _IO_cookie_io_functions_t cookie_io_functions_t;
2441 FD_t Fdopen(FD_t ofd, const char *fmode)
2443 char stdio[20], other[20], zstdio[20];
2444 const char *end = NULL;
2449 fprintf(stderr, "*** Fdopen(%p,%s) %s\n", fd, fmode, fdbg(fd));
2455 cvtfmode(fmode, stdio, sizeof(stdio), other, sizeof(other), &end, NULL);
2456 if (stdio[0] == '\0')
2459 strncat(zstdio, stdio, sizeof(zstdio) - strlen(zstdio));
2460 strncat(zstdio, other, sizeof(zstdio) - strlen(zstdio));
2462 if (end == NULL && other[0] == '\0')
2466 if (!strcmp(end, "fdio")) {
2468 } else if (!strcmp(end, "gzdio")) {
2470 fd = gzdFdopen(fd, zstdio);
2472 } else if (!strcmp(end, "bzdio")) {
2474 fd = bzdFdopen(fd, zstdio);
2476 } else if (!strcmp(end, "ufdio")) {
2478 } else if (!strcmp(end, "fadio")) {
2480 } else if (!strcmp(end, "fpio")) {
2483 int fdno = Fileno(fd);
2484 FILE * fp = fdopen(fdno, stdio);
2486 fprintf(stderr, "*** Fdopen fpio fp %p\n", fp);
2489 /* XXX gzdio/bzdio use fp for private data */
2490 if (fdGetFp(fd) == NULL)
2492 fdPush(fd, fpio, fp, fdno); /* Push fpio onto stack */
2495 } else if (other[0]) {
2496 for (end = other; *end && strchr("0123456789fh", *end); end++)
2500 fd = gzdFdopen(fd, zstdio);
2510 { cookie_io_functions_t ciof;
2511 ciof.read = iof->read;
2512 ciof.write = iof->write;
2513 ciof.seek = iof->seek;
2514 ciof.close = iof->close;
2515 fp = fopencookie(fd, stdio, ciof);
2516 DBGIO(fd, (stderr, "==> fopencookie(%p,\"%s\",*%p) returns fp %p\n", fd, stdio, iof, fp));
2521 /* XXX gzdio/bzdio use fp for private data */
2522 if (fdGetFp(fd) == NULL)
2524 fdPush(fd, fpio, fp, fileno(fp)); /* Push fpio onto stack */
2525 fd = fdLink(fd, "fopencookie");
2529 DBGIO(fd, (stderr, "==> Fdopen(%p,\"%s\") returns fd %p %s\n", ofd, fmode, fd, fdbg(fd)));
2533 FD_t Fopen(const char *path, const char *fmode)
2535 char stdio[20], other[20];
2536 const char *end = NULL;
2537 mode_t perms = 0666;
2541 if (path == NULL || fmode == NULL)
2544 cvtfmode(fmode, stdio, sizeof(stdio), other, sizeof(other), &end, &flags);
2545 if (stdio[0] == '\0')
2548 if (end == NULL || !strcmp(end, "fdio")) {
2550 fprintf(stderr, "*** Fopen fdio path %s fmode %s\n", path, fmode);
2551 fd = fdOpen(path, flags, perms);
2552 if (fdFileno(fd) < 0) {
2556 } else if (!strcmp(end, "fadio")) {
2558 fprintf(stderr, "*** Fopen fadio path %s fmode %s\n", path, fmode);
2559 fd = fadio->_open(path, flags, perms);
2560 if (fdFileno(fd) < 0) {
2569 /* XXX gzdio and bzdio here too */
2571 switch (urlIsURL(path)) {
2578 case URL_IS_UNKNOWN:
2580 fprintf(stderr, "*** Fopen ufdio path %s fmode %s\n", path, fmode);
2581 fd = ufdOpen(path, flags, perms);
2582 if (fd == NULL || fdFileno(fd) < 0)
2587 fprintf(stderr, "*** Fopen WTFO path %s fmode %s\n", path, fmode);
2589 /*@notreached@*/ break;
2592 /* XXX persistent HTTP/1.1 returns the previously opened fp */
2593 if (isHTTP && ((fp = fdGetFp(fd)) != NULL) && ((fdno = fdGetFdno(fd)) >= 0)) {
2594 fdPush(fd, fpio, fp, fileno(fp)); /* Push fpio onto stack */
2599 fd = Fdopen(fd, fmode);
2605 if (fd == NULL) return -1;
2606 if (fdGetIo(fd) == fpio)
2607 return fflush(fdGetFILE(fd));
2608 if (fdGetIo(fd) == gzdio)
2609 return gzdFlush(fdGetFp(fd));
2611 if (fdGetIo(fd) == bzdio)
2612 return bzdFlush(fdGetFp(fd));
2617 int Ferror(FD_t fd) {
2620 if (fd == NULL) return -1;
2621 for (i = fd->nfps; rc == 0 && i >= 0; i--) {
2622 FDSTACK_t * fps = &fd->fps[i];
2625 if (fps->io == fpio) {
2626 ec = ferror(fdGetFILE(fd));
2627 } else if (fps->io == gzdio) {
2628 ec = (fd->syserrno || fd->errcookie != NULL) ? -1 : 0;
2630 } else if (fps->io == bzdio) {
2631 ec = (fd->syserrno || fd->errcookie != NULL) ? -1 : 0;
2634 /* XXX need to check ufdio/gzdio/bzdio/fdio errors correctly. */
2635 ec = (fdFileno(fd) < 0 ? -1 : 0);
2641 DBGIO(fd, (stderr, "==> Ferror(%p) rc %d %s\n", fd, rc, fdbg(fd)));
2645 int Fileno(FD_t fd) {
2648 for (i = fd->nfps ; rc == -1 && i >= 0; i--) {
2649 rc = fd->fps[i].fdno;
2651 DBGIO(fd, (stderr, "==> Fileno(%p) rc %d %s\n", fd, rc, fdbg(fd)));
2655 /* XXX this is naive */
2656 int Fcntl(FD_t fd, int op, void *lip) {
2657 return fcntl(Fileno(fd), op, lip);
2660 /* =============================================================== */
2661 /* Helper routines that may be generally useful.
2664 /* XXX falloc.c: analogues to pread(3)/pwrite(3). */
2665 #ifdef USE_COOKIE_SEEK_POINTER
2666 ssize_t Pread(FD_t fd, void * buf, size_t count, _IO_off64_t offset) {
2668 ssize_t Pread(FD_t fd, void * buf, size_t count, off_t offset) {
2670 if (Fseek(fd, offset, SEEK_SET) < 0)
2672 return Fread(buf, sizeof(char), count, fd);
2675 #ifdef USE_COOKIE_SEEK_POINTER
2676 ssize_t Pwrite(FD_t fd, const void * buf, size_t count, _IO_off64_t offset) {
2678 ssize_t Pwrite(FD_t fd, const void * buf, size_t count, off_t offset) {
2680 if (Fseek(fd, offset, SEEK_SET) < 0)
2682 return Fwrite(buf, sizeof(char), count, fd);
2685 /* XXX rebuilddb.c: analogues to mkdir(2)/rmdir(2). */
2686 int Mkdir (const char *path, mode_t mode) {
2688 int ut = urlPath(path, &lpath);
2692 return ftpMkdir(path, mode);
2693 /*@notreached@*/ break;
2694 case URL_IS_HTTP: /* XXX WRONG WRONG WRONG */
2698 case URL_IS_UNKNOWN:
2703 /*@notreached@*/ break;
2705 return mkdir(path, mode);
2708 int Chdir (const char *path) {
2710 int ut = urlPath(path, &lpath);
2714 return ftpChdir(path);
2715 /*@notreached@*/ break;
2716 case URL_IS_HTTP: /* XXX WRONG WRONG WRONG */
2720 case URL_IS_UNKNOWN:
2725 /*@notreached@*/ break;
2730 int Rmdir (const char *path) {
2732 int ut = urlPath(path, &lpath);
2736 return ftpRmdir(path);
2737 /*@notreached@*/ break;
2738 case URL_IS_HTTP: /* XXX WRONG WRONG WRONG */
2742 case URL_IS_UNKNOWN:
2747 /*@notreached@*/ break;
2752 /* XXX rpmdb.c: analogue to rename(2). */
2754 int Rename (const char *oldpath, const char * newpath) {
2755 const char *oe = NULL;
2756 const char *ne = NULL;
2759 /* XXX lib/install.c used to rely on this behavior. */
2760 if (!strcmp(oldpath, newpath)) return 0;
2762 oldut = urlPath(oldpath, &oe);
2764 case URL_IS_FTP: /* XXX WRONG WRONG WRONG */
2765 case URL_IS_HTTP: /* XXX WRONG WRONG WRONG */
2767 case URL_IS_UNKNOWN:
2772 /*@notreached@*/ break;
2775 newut = urlPath(newpath, &ne);
2779 fprintf(stderr, "*** rename old %*s new %*s\n", (int)(oe - oldpath), oldpath, (int)(ne - newpath), newpath);
2780 if (!(oldut == newut && oe && ne && (oe - oldpath) == (ne - newpath) &&
2781 !strncasecmp(oldpath, newpath, (oe - oldpath))))
2783 return ftpRename(oldpath, newpath);
2784 /*@notreached@*/ break;
2785 case URL_IS_HTTP: /* XXX WRONG WRONG WRONG */
2790 case URL_IS_UNKNOWN:
2795 /*@notreached@*/ break;
2797 return rename(oldpath, newpath);
2800 int Link (const char *oldpath, const char * newpath) {
2801 const char *oe = NULL;
2802 const char *ne = NULL;
2805 oldut = urlPath(oldpath, &oe);
2807 case URL_IS_FTP: /* XXX WRONG WRONG WRONG */
2808 case URL_IS_HTTP: /* XXX WRONG WRONG WRONG */
2810 case URL_IS_UNKNOWN:
2815 /*@notreached@*/ break;
2818 newut = urlPath(newpath, &ne);
2820 case URL_IS_HTTP: /* XXX WRONG WRONG WRONG */
2821 case URL_IS_FTP: /* XXX WRONG WRONG WRONG */
2824 fprintf(stderr, "*** link old %*s new %*s\n", (int)(oe - oldpath), oldpath, (int)(ne - newpath), newpath);
2825 if (!(oldut == newut && oe && ne && (oe - oldpath) == (ne - newpath) &&
2826 !strncasecmp(oldpath, newpath, (oe - oldpath))))
2831 case URL_IS_UNKNOWN:
2836 /*@notreached@*/ break;
2838 return link(oldpath, newpath);
2841 /* XXX build/build.c: analogue to unlink(2). */
2843 int Unlink(const char * path) {
2845 int ut = urlPath(path, &lpath);
2849 return ftpUnlink(path);
2850 /*@notreached@*/ break;
2851 case URL_IS_HTTP: /* XXX WRONG WRONG WRONG */
2855 case URL_IS_UNKNOWN:
2860 /*@notreached@*/ break;
2862 return unlink(path);
2865 /* XXX swiped from mc-4.5.39-pre9 vfs/ftpfs.c */
2867 #define g_strdup xstrdup
2868 #define g_free xfree
2871 * FIXME: this is broken. It depends on mc not crossing border on month!
2873 static int current_mday;
2874 static int current_mon;
2875 static int current_year;
2877 /* Following stuff (parse_ls_lga) is used by ftpfs and extfs */
2880 static char *columns [MAXCOLS]; /* Points to the string in column n */
2881 static int column_ptr [MAXCOLS]; /* Index from 0 to the starting positions of the columns */
2884 vfs_split_text (char *p)
2890 for (numcols = 0; *p && numcols < MAXCOLS; numcols++){
2891 while (*p == ' ' || *p == '\r' || *p == '\n'){
2895 columns [numcols] = p;
2896 column_ptr [numcols] = p - original;
2897 while (*p && *p != ' ' && *p != '\r' && *p != '\n')
2906 if (!columns [idx] || columns [idx][0] < '0' || columns [idx][0] > '9')
2912 is_dos_date(char *str)
2914 if (strlen(str) == 8 && str[2] == str[5] && strchr("\\-/", (int)str[2]) != NULL)
2921 is_week (char *str, struct tm *tim)
2923 static char *week = "SunMonTueWedThuFriSat";
2926 if((pos=strstr(week, str)) != NULL){
2928 tim->tm_wday = (pos - week)/3;
2935 is_month (char *str, struct tm *tim)
2937 static char *month = "JanFebMarAprMayJunJulAugSepOctNovDec";
2940 if((pos=strstr(month, str)) != NULL){
2942 tim->tm_mon = (pos - month)/3;
2949 is_time (char *str, struct tm *tim)
2953 if ((p=strchr(str, ':')) && (p2=strrchr(str, ':'))) {
2955 if (sscanf (str, "%2d:%2d:%2d", &tim->tm_hour, &tim->tm_min, &tim->tm_sec) != 3)
2959 if (sscanf (str, "%2d:%2d", &tim->tm_hour, &tim->tm_min) != 2)
2969 static int is_year(char *str, struct tm *tim)
2973 if (strchr(str,':'))
2979 if (sscanf(str, "%ld", &year) != 1)
2982 if (year < 1900 || year > 3000)
2985 tim->tm_year = (int) (year - 1900);
2991 * FIXME: this is broken. Consider following entry:
2992 * -rwx------ 1 root root 1 Aug 31 10:04 2904 1234
2993 * where "2904 1234" is filename. Well, this code decodes it as year :-(.
2997 vfs_parse_filetype (char c)
3000 case 'd': return S_IFDIR;
3001 case 'b': return S_IFBLK;
3002 case 'c': return S_IFCHR;
3003 case 'l': return S_IFLNK;
3005 #ifdef IS_IFSOCK /* And if not, we fall through to IFIFO, which is pretty close */
3008 case 'p': return S_IFIFO;
3009 case 'm': case 'n': /* Don't know what these are :-) */
3010 case '-': case '?': return S_IFREG;
3015 static int vfs_parse_filemode (char *p)
3016 { /* converts rw-rw-rw- into 0666 */
3019 case 'r': res |= 0400; break;
3024 case 'w': res |= 0200; break;
3029 case 'x': res |= 0100; break;
3030 case 's': res |= 0100 | S_ISUID; break;
3031 case 'S': res |= S_ISUID; break;
3036 case 'r': res |= 0040; break;
3041 case 'w': res |= 0020; break;
3046 case 'x': res |= 0010; break;
3047 case 's': res |= 0010 | S_ISGID; break;
3048 case 'l': /* Solaris produces these */
3049 case 'S': res |= S_ISGID; break;
3054 case 'r': res |= 0004; break;
3059 case 'w': res |= 0002; break;
3064 case 'x': res |= 0001; break;
3065 case 't': res |= 0001 | S_ISVTX; break;
3066 case 'T': res |= S_ISVTX; break;
3073 static int vfs_parse_filedate(int idx, time_t *t)
3074 { /* This thing parses from idx in columns[] array */
3081 /* Let's setup default time values */
3082 tim.tm_year = current_year;
3083 tim.tm_mon = current_mon;
3084 tim.tm_mday = current_mday;
3088 tim.tm_isdst = -1; /* Let mktime() try to guess correct dst offset */
3090 p = columns [idx++];
3092 /* We eat weekday name in case of extfs */
3093 if(is_week(p, &tim))
3094 p = columns [idx++];
3097 if(is_month(p, &tim)){
3098 /* And we expect, it followed by day number */
3100 tim.tm_mday = (int)atol (columns [idx++]);
3102 return 0; /* No day */
3105 /* We usually expect:
3108 But in case of extfs we allow these date formats:
3111 Wek Mon DD hh:mm:ss YYYY
3113 where Mon is Jan-Dec, DD, MM, YY two digit day, month, year,
3114 YYYY four digit year, hh, mm, ss two digit hour, minute or second. */
3116 /* Here just this special case with MM-DD-YY */
3117 if (is_dos_date(p)){
3120 if(sscanf(p, "%2d-%2d-%2d", &d[0], &d[1], &d[2]) == 3){
3121 /* We expect to get:
3127 /* Hmm... maybe, next time :)*/
3129 /* At last, MM-DD-YY */
3130 d[0]--; /* Months are zerobased */
3140 return 0; /* sscanf failed */
3142 return 0; /* unsupported format */
3145 /* Here we expect to find time and/or year */
3148 if(is_time(columns[idx], &tim) || (got_year = is_year(columns[idx], &tim))) {
3151 /* This is a special case for ctime() or Mon DD YYYY hh:mm */
3153 ((got_year = is_year(columns[idx], &tim)) || is_time(columns[idx], &tim)))
3154 idx++; /* time & year or reverse */
3155 } /* only time or date */
3158 return 0; /* Nor time or date */
3161 * If the date is less than 6 months in the past, it is shown without year
3162 * other dates in the past or future are shown with year but without time
3163 * This does not check for years before 1900 ... I don't know, how
3164 * to represent them at all
3167 current_mon < 6 && current_mon < tim.tm_mon &&
3168 tim.tm_mon - current_mon >= 6)
3172 if ((*t = mktime(&tim)) < 0)
3178 vfs_parse_ls_lga (char *p, struct stat *s, char **filename, char **linkname)
3180 int idx, idx2, num_cols;
3184 if (strncmp (p, "total", 5) == 0)
3187 p_copy = g_strdup(p);
3188 /* XXX FIXME: parse out inode number from "NLST -lai ." */
3189 /* XXX FIXME: parse out sizein blocks from "NLST -lais ." */
3191 if ((i = vfs_parse_filetype(*(p++))) == -1)
3195 if (*p == ' ') /* Notwell 4 */
3198 if (strlen (p) <= 8 || p [8] != ']')
3200 /* Should parse here the Notwell permissions :) */
3201 if (S_ISDIR (s->st_mode))
3202 s->st_mode |= (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IXUSR | S_IXGRP | S_IXOTH);
3204 s->st_mode |= (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR);
3207 if ((i = vfs_parse_filemode(p)) == -1)
3212 /* This is for an extra ACL attribute (HP-UX) */
3218 p_copy = g_strdup(p);
3219 num_cols = vfs_split_text (p);
3221 s->st_nlink = atol (columns [0]);
3222 if (s->st_nlink < 0)
3227 s->st_uid = finduid (columns [1]);
3229 unameToUid (columns [1], &s->st_uid);
3232 s->st_uid = (uid_t) atol (columns [1]);
3234 /* Mhm, the ls -lg did not produce a group field */
3235 for (idx = 3; idx <= 5; idx++)
3236 if (is_month(columns [idx], NULL) || is_week(columns [idx], NULL) || is_dos_date(columns[idx]))
3239 if (idx == 6 || (idx == 5 && !S_ISCHR (s->st_mode) && !S_ISBLK (s->st_mode)))
3242 /* We don't have gid */
3243 if (idx == 3 || (idx == 4 && (S_ISCHR(s->st_mode) || S_ISBLK (s->st_mode))))
3246 /* We have gid field */
3248 s->st_gid = (gid_t) atol (columns [2]);
3251 s->st_gid = findgid (columns [2]);
3253 gnameToGid (columns [1], &s->st_gid);
3258 /* This is device */
3259 if (S_ISCHR (s->st_mode) || S_ISBLK (s->st_mode)){
3262 if (!is_num (idx2) || sscanf(columns [idx2], " %d,", &maj) != 1)
3265 if (!is_num (++idx2) || sscanf(columns [idx2], " %d", &min) != 1)
3269 s->st_rdev = ((maj & 0xff) << 8) | (min & 0xffff00ff);
3274 /* Common file size */
3278 s->st_size = (size_t) atol (columns [idx2]);
3284 idx = vfs_parse_filedate(idx, &s->st_mtime);
3287 /* Use resulting time value */
3288 s->st_atime = s->st_ctime = s->st_mtime;
3291 #ifdef HAVE_ST_BLKSIZE
3292 s->st_blksize = 512;
3294 #ifdef HAVE_ST_BLOCKS
3295 s->st_blocks = (s->st_size + 511) / 512;
3298 for (i = idx + 1, idx2 = 0; i < num_cols; i++ )
3299 if (strcmp (columns [i], "->") == 0){
3304 if (((S_ISLNK (s->st_mode) ||
3305 (num_cols == idx + 3 && s->st_nlink > 1))) /* Maybe a hardlink? (in extfs) */
3312 s = g_strndup (p_copy + column_ptr [idx], column_ptr [idx2] - column_ptr [idx] - 1);
3314 int nb = column_ptr [idx2] - column_ptr [idx] - 1;
3316 strncpy(s, p_copy + column_ptr [idx], nb);
3321 s = g_strdup (p_copy + column_ptr [idx2+1]);
3323 if (s [p-1] == '\r' || s [p-1] == '\n')
3325 if (s [p-2] == '\r' || s [p-2] == '\n')
3331 /* Extract the filename from the string copy, not from the columns
3332 * this way we have a chance of entering hidden directories like ". ."
3336 *filename = g_strdup (columns [idx++]);
3341 s = g_strdup (p_copy + column_ptr [idx++]);
3344 if (s [p-1] == '\r' || s [p-1] == '\n')
3346 if (s [p-2] == '\r' || s [p-2] == '\n')
3360 static int errorcount = 0;
3362 if (++errorcount < 5) {
3363 message_1s (1, "Could not parse:", p_copy);
3364 } else if (errorcount == 5)
3365 message_1s (1, "More parsing errors will be ignored.", "(sorry)" );
3369 if (p_copy != p) /* Carefull! */
3377 DO_FTP_READLINK = 3,
3381 static size_t ftpBufAlloced = 0;
3382 static char * ftpBuf = NULL;
3384 #define alloca_strdup(_s) strcpy(alloca(strlen(_s)+1), (_s))
3386 static int ftpNLST(const char * url, ftpSysCall_t ftpSysCall,
3387 struct stat * st, char * rlbuf, size_t rlbufsiz)
3391 int bufLength, moretodo;
3392 const char *n, *ne, *o, *oe;
3401 n = ne = o = oe = NULL;
3402 (void) urlPath(url, &path);
3406 switch (ftpSysCall) {
3408 fd = ftpOpen(url, 0, 0, &u);
3409 if (fd == NULL || u == NULL)
3412 u->openError = ftpReq(fd, "NLST", path);
3415 urldn = alloca_strdup(url);
3416 if ((bn = strrchr(urldn, '/')) == NULL)
3418 else if (bn == path)
3424 rc = ftpChdir(urldn); /* XXX don't care about CWD */
3428 fd = ftpOpen(url, 0, 0, &u);
3429 if (fd == NULL || u == NULL)
3432 /* XXX possibly should do "NLST -lais" to get st_ino/st_blocks also */
3433 u->openError = ftpReq(fd, "NLST", "-la");
3437 if (u->openError < 0) {
3438 fd = fdLink(fd, "error data (ftpStat)");
3443 if (ftpBufAlloced == 0 || ftpBuf == NULL) {
3444 ftpBufAlloced = url_iobuf_size;
3445 ftpBuf = xcalloc(ftpBufAlloced, sizeof(ftpBuf[0]));
3454 /* XXX FIXME: realloc ftpBuf is < ~128 chars remain */
3455 if ((ftpBufAlloced - bufLength) < (1024+80)) {
3456 ftpBufAlloced <<= 2;
3457 ftpBuf = xrealloc(ftpBuf, ftpBufAlloced);
3459 s = se = ftpBuf + bufLength;
3462 rc = fdFgets(fd, se, (ftpBufAlloced - bufLength));
3467 if (ftpSysCall == DO_FTP_GLOB) { /* XXX HACK */
3468 bufLength += strlen(se);
3472 for (s = se; *s != '\0'; s = se) {
3475 while (*se && *se != '\n') se++;
3476 if (se > s && se[-1] == '\r') se[-1] = '\0';
3477 if (*se == '\0') break;
3480 if (!strncmp(s, "total ", sizeof("total ")-1)) continue;
3483 for (bingo = 0, n = se; n >= s; n--) {
3489 if (o || !(n[-3] == ' ' && n[-2] == '-' && n[-1] == '>')) {
3490 while (*(++n) == ' ');
3494 for (o = n + 1; *o == ' '; o++);
3504 if (nbn != (ne - n)) continue; /* Same name length? */
3505 if (strncmp(n, bn, nbn)) continue; /* Same name? */
3511 if (moretodo && se > s) {
3512 bufLength = se - s - 1;
3514 memmove(ftpBuf, s, bufLength);
3520 switch (ftpSysCall) {
3523 /* XXX FIXME: symlink, replace urldn/bn from [o,oe) and restart */
3527 if (st == NULL || !(n && ne)) {
3530 rc = ((vfs_parse_ls_lga(s, st, NULL, NULL) > 0) ? 0 : -1);
3533 case DO_FTP_READLINK:
3534 if (rlbuf == NULL || !(o && oe)) {
3540 memcpy(rlbuf, o, rc);
3546 rc = 0; /* XXX WRONG WRONG WRONG */
3549 rc = 0; /* XXX WRONG WRONG WRONG */
3558 static int ftpStat(const char * path, struct stat *st)
3560 return ftpNLST(path, DO_FTP_STAT, st, NULL, 0);
3563 static int ftpLstat(const char * path, struct stat *st) {
3565 rc = ftpNLST(path, DO_FTP_LSTAT, st, NULL, 0);
3567 fprintf(stderr, "*** ftpLstat(%s) rc %d\n", path, rc);
3571 static int ftpReadlink(const char * path, char * buf, size_t bufsiz) {
3572 return ftpNLST(path, DO_FTP_READLINK, NULL, buf, bufsiz);
3575 static int ftpGlob(const char * path, int flags,
3576 int errfunc(const char * epath, int eerno), glob_t * pglob)
3582 rc = ftpNLST(path, DO_FTP_GLOB, NULL, NULL, 0);
3584 fprintf(stderr, "*** ftpGlob(%s,0x%x,%p,%p) ftpNLST rc %d\n", path, flags, errfunc, pglob, rc);
3587 rc = poptParseArgvString(ftpBuf, &pglob->gl_pathc, (const char ***)&pglob->gl_pathv);
3588 pglob->gl_offs = -1; /* XXX HACK HACK HACK */
3592 static void ftpGlobfree(glob_t * pglob) {
3594 fprintf(stderr, "*** ftpGlobfree(%p)\n", pglob);
3595 if (pglob->gl_offs == -1) /* XXX HACK HACK HACK */
3596 xfree(pglob->gl_pathv);
3599 int Stat(const char * path, struct stat * st) {
3601 int ut = urlPath(path, &lpath);
3604 fprintf(stderr, "*** Stat(%s,%p)\n", path, st);
3607 return ftpStat(path, st);
3608 /*@notreached@*/ break;
3609 case URL_IS_HTTP: /* XXX WRONG WRONG WRONG */
3613 case URL_IS_UNKNOWN:
3618 /*@notreached@*/ break;
3620 return stat(path, st);
3623 int Lstat(const char * path, struct stat * st) {
3625 int ut = urlPath(path, &lpath);
3628 fprintf(stderr, "*** Lstat(%s,%p)\n", path, st);
3631 return ftpLstat(path, st);
3632 /*@notreached@*/ break;
3633 case URL_IS_HTTP: /* XXX WRONG WRONG WRONG */
3637 case URL_IS_UNKNOWN:
3642 /*@notreached@*/ break;
3644 return lstat(path, st);
3647 int Readlink(const char * path, char * buf, size_t bufsiz) {
3649 int ut = urlPath(path, &lpath);
3653 return ftpReadlink(path, buf, bufsiz);
3654 /*@notreached@*/ break;
3655 case URL_IS_HTTP: /* XXX WRONG WRONG WRONG */
3659 case URL_IS_UNKNOWN:
3664 /*@notreached@*/ break;
3666 return readlink(path, buf, bufsiz);
3669 int Access(const char * path, int amode) {
3671 int ut = urlPath(path, &lpath);
3674 fprintf(stderr, "*** Access(%s,%d)\n", path, amode);
3676 case URL_IS_FTP: /* XXX WRONG WRONG WRONG */
3677 case URL_IS_HTTP: /* XXX WRONG WRONG WRONG */
3681 case URL_IS_UNKNOWN:
3686 /*@notreached@*/ break;
3688 return access(path, amode);
3691 int Glob(const char *path, int flags,
3692 int errfunc(const char * epath, int eerrno), glob_t *pglob)
3695 int ut = urlPath(path, &lpath);
3698 fprintf(stderr, "*** Glob(%s,0x%x,%p,%p)\n", path, flags, errfunc, pglob);
3700 case URL_IS_FTP: /* XXX WRONG WRONG WRONG */
3701 return ftpGlob(path, flags, errfunc, pglob);
3702 /*@notreached@*/ break;
3703 case URL_IS_HTTP: /* XXX WRONG WRONG WRONG */
3707 case URL_IS_UNKNOWN:
3712 /*@notreached@*/ break;
3714 return glob(path, flags, errfunc, pglob);
3717 void Globfree(glob_t *pglob)
3720 fprintf(stderr, "*** Globfree(%p)\n", pglob);
3721 if (pglob->gl_offs == -1) /* XXX HACK HACK HACK */
3727 DIR * Opendir(const char * path)
3730 int ut = urlPath(path, &lpath);
3733 fprintf(stderr, "*** Opendir(%s)\n", path);
3735 case URL_IS_FTP: /* XXX WRONG WRONG WRONG */
3736 case URL_IS_HTTP: /* XXX WRONG WRONG WRONG */
3740 case URL_IS_UNKNOWN:
3745 /*@notreached@*/ break;
3747 return opendir(path);
3750 struct dirent * Readdir(DIR * dir)
3753 fprintf(stderr, "*** Readdir(%p)\n", dir);
3754 return readdir(dir);
3757 int Closedir(DIR * dir)
3760 fprintf(stderr, "*** Closedir(%p)\n", dir);
3761 return closedir(dir);
3764 static struct FDIO_s fpio_s = {
3765 ufdRead, ufdWrite, fdSeek, ufdClose, XfdLink, XfdFree, XfdNew, fdFileno,
3766 ufdOpen, NULL, fdGetFp, NULL, Mkdir, Chdir, Rmdir, Rename, Unlink
3768 FDIO_t fpio = /*@-compmempass@*/ &fpio_s /*@=compmempass@*/ ;