ac7704f1cee112ddab76cd2a0694c3995e8cf993
[platform/upstream/rpm.git] / rpmio / rpmio.c
1 /** \ingroup rpmio
2  * \file rpmio/rpmio.c
3  */
4
5 #include "system.h"
6 #include <stdarg.h>
7
8 #if HAVE_MACHINE_TYPES_H
9 # include <machine/types.h>
10 #endif
11
12 #if HAVE_SYS_SOCKET_H
13 # include <sys/socket.h>
14 #endif
15
16 #include <netinet/in.h>
17 #include <arpa/inet.h>          /* XXX for inet_aton and HP-UX */
18
19 #if HAVE_NETINET_IN_SYSTM_H
20 # include <sys/types.h>
21 # include <netinet/in_systm.h>
22 #endif
23
24 #if HAVE_LIBIO_H && defined(_G_IO_IO_FILE_VERSION)
25 #define _USE_LIBIO      1
26 #endif
27
28 /* XXX HP-UX w/o -D_XOPEN_SOURCE needs */
29 #if !defined(HAVE_HERRNO) && (defined(__hpux))
30 extern int h_errno;
31 #endif
32
33 #ifndef IPPORT_FTP
34 #define IPPORT_FTP      21
35 #endif
36 #ifndef IPPORT_HTTP
37 #define IPPORT_HTTP     80
38 #endif
39
40 #if !defined(HAVE_INET_ATON)
41 static int inet_aton(const char *cp, struct in_addr *inp)
42 {
43     long addr;
44
45     addr = inet_addr(cp);
46     if (addr == ((long) -1)) return 0;
47
48     memcpy(inp, &addr, sizeof(addr));
49     return 1;
50 }
51 #endif
52
53 #if defined(USE_ALT_DNS) && USE_ALT_DNS
54 #include "dns.h"
55 #endif
56
57 #include <rpmio_internal.h>
58 #undef  fdFileno
59 #undef  fdOpen
60 #define fdOpen  __fdOpen
61 #undef  fdRead
62 #define fdRead  __fdRead
63 #undef  fdWrite
64 #define fdWrite __fdWrite
65 #undef  fdClose
66 #define fdClose __fdClose
67
68 #include <rpmdav.h>
69 #include "ugid.h"
70 #include "rpmmessages.h"
71
72 #include "debug.h"
73
74 #define FDNREFS(fd)     (fd ? ((FD_t)fd)->nrefs : -9)
75 #define FDTO(fd)        (fd ? ((FD_t)fd)->rd_timeoutsecs : -99)
76 #define FDCPIOPOS(fd)   (fd ? ((FD_t)fd)->fd_cpioPos : -99)
77
78 #define FDONLY(fd)      assert(fdGetIo(fd) == fdio)
79 #define GZDONLY(fd)     assert(fdGetIo(fd) == gzdio)
80 #define BZDONLY(fd)     assert(fdGetIo(fd) == bzdio)
81
82 #define UFDONLY(fd)     /* assert(fdGetIo(fd) == ufdio) */
83
84 #define fdGetFILE(_fd)  ((FILE *)fdGetFp(_fd))
85
86 /**
87  */
88 #if _USE_LIBIO
89 int noLibio = 0;
90 #else
91 int noLibio = 1;
92 #endif
93
94 #define TIMEOUT_SECS 60
95
96 /**
97  */
98 static int ftpTimeoutSecs = TIMEOUT_SECS;
99
100 /**
101  */
102 static int httpTimeoutSecs = TIMEOUT_SECS;
103
104 /**
105  */
106 int _rpmio_debug = 0;
107
108 /**
109  */
110 int _av_debug = 0;
111
112 /**
113  */
114 int _ftp_debug = 0;
115
116 /**
117  */
118 int _dav_debug = 0;
119
120 /**
121  * Wrapper to free(3), hides const compilation noise, permit NULL, return NULL.
122  * @param p             memory to free
123  * @retval              NULL always
124  */
125 static inline void *
126 _free(const void * p)
127 {
128     if (p != NULL)      free((void *)p);
129     return NULL;
130 }
131
132 /* =============================================================== */
133
134 static const char * fdbg(FD_t fd)
135 {
136     static char buf[BUFSIZ];
137     char *be = buf;
138     int i;
139
140     buf[0] = '\0';
141     if (fd == NULL)
142         return buf;
143
144 #ifdef DYING
145     sprintf(be, "fd %p", fd);   be += strlen(be);
146     if (fd->rd_timeoutsecs >= 0) {
147         sprintf(be, " secs %d", fd->rd_timeoutsecs);
148         be += strlen(be);
149     }
150 #endif
151     if (fd->bytesRemain != -1) {
152         sprintf(be, " clen %d", (int)fd->bytesRemain);
153         be += strlen(be);
154      }
155     if (fd->wr_chunked) {
156         strcpy(be, " chunked");
157         be += strlen(be);
158      }
159     *be++ = '\t';
160     for (i = fd->nfps; i >= 0; i--) {
161         FDSTACK_t * fps = &fd->fps[i];
162         if (i != fd->nfps)
163             *be++ = ' ';
164         *be++ = '|';
165         *be++ = ' ';
166         if (fps->io == fdio) {
167             sprintf(be, "FD %d fp %p", fps->fdno, fps->fp);
168         } else if (fps->io == ufdio) {
169             sprintf(be, "UFD %d fp %p", fps->fdno, fps->fp);
170         } else if (fps->io == gzdio) {
171             sprintf(be, "GZD %p fdno %d", fps->fp, fps->fdno);
172 #if HAVE_BZLIB_H
173         } else if (fps->io == bzdio) {
174             sprintf(be, "BZD %p fdno %d", fps->fp, fps->fdno);
175 #endif
176         } else if (fps->io == fpio) {
177             sprintf(be, "%s %p(%d) fdno %d",
178                 (fps->fdno < 0 ? "LIBIO" : "FP"),
179                 fps->fp, fileno(((FILE *)fps->fp)), fps->fdno);
180         } else {
181             sprintf(be, "??? io %p fp %p fdno %d ???",
182                 fps->io, fps->fp, fps->fdno);
183         }
184         be += strlen(be);
185         *be = '\0';
186     }
187     return buf;
188 }
189
190 /* =============================================================== */
191 off_t fdSize(FD_t fd)
192 {
193     struct stat sb;
194     off_t rc = -1; 
195
196 #ifdef  NOISY
197 DBGIO(0, (stderr, "==>\tfdSize(%p) rc %ld\n", fd, (long)rc));
198 #endif
199     FDSANE(fd);
200     if (fd->contentLength >= 0)
201         rc = fd->contentLength;
202     else switch (fd->urlType) {
203     case URL_IS_PATH:
204     case URL_IS_UNKNOWN:
205         if (fstat(Fileno(fd), &sb) == 0)
206             rc = sb.st_size;
207     case URL_IS_HTTPS:
208     case URL_IS_HTTP:
209     case URL_IS_HKP:
210     case URL_IS_FTP:
211     case URL_IS_DASH:
212         break;
213     }
214     return rc;
215 }
216
217 FD_t fdDup(int fdno)
218 {
219     FD_t fd;
220     int nfdno;
221
222     if ((nfdno = dup(fdno)) < 0)
223         return NULL;
224     fd = fdNew("open (fdDup)");
225     fdSetFdno(fd, nfdno);
226 DBGIO(fd, (stderr, "==> fdDup(%d) fd %p %s\n", fdno, (fd ? fd : NULL), fdbg(fd)));
227     return fd;
228 }
229
230 static inline int fdSeekNot(void * cookie,
231                 _libio_pos_t pos,  int whence)
232 {
233     FD_t fd = c2f(cookie);
234     FDSANE(fd);         /* XXX keep gcc quiet */
235     return -2;
236 }
237
238 #ifdef UNUSED
239 FILE *fdFdopen(void * cookie, const char *fmode)
240 {
241     FD_t fd = c2f(cookie);
242     int fdno;
243     FILE * fp;
244
245     if (fmode == NULL) return NULL;
246     fdno = fdFileno(fd);
247     if (fdno < 0) return NULL;
248     fp = fdopen(fdno, fmode);
249 DBGIO(fd, (stderr, "==> fdFdopen(%p,\"%s\") fdno %d -> fp %p fdno %d\n", cookie, fmode, fdno, fp, fileno(fp)));
250     fd = fdFree(fd, "open (fdFdopen)");
251     return fp;
252 }
253 #endif
254
255 /* =============================================================== */
256 /* FIX: cookie is modified */
257 static inline FD_t XfdLink(void * cookie, const char * msg,
258                 const char * file, unsigned line)
259 {
260     FD_t fd;
261 if (cookie == NULL)
262 DBGREFS(0, (stderr, "--> fd  %p ++ %d %s at %s:%u\n", cookie, FDNREFS(cookie)+1, msg, file, line));
263     fd = c2f(cookie);
264     if (fd) {
265         fd->nrefs++;
266 DBGREFS(fd, (stderr, "--> fd  %p ++ %d %s at %s:%u %s\n", fd, fd->nrefs, msg, file, line, fdbg(fd)));
267     }
268     return fd;
269 }
270
271 static inline
272 FD_t XfdFree( FD_t fd, const char *msg,
273                 const char *file, unsigned line)
274 {
275         int i;
276
277 if (fd == NULL)
278 DBGREFS(0, (stderr, "--> fd  %p -- %d %s at %s:%u\n", fd, FDNREFS(fd), msg, file, line));
279     FDSANE(fd);
280     if (fd) {
281 DBGREFS(fd, (stderr, "--> fd  %p -- %d %s at %s:%u %s\n", fd, fd->nrefs, msg, file, line, fdbg(fd)));
282         if (--fd->nrefs > 0)
283             return fd;
284         fd->stats = _free(fd->stats);
285         for (i = fd->ndigests - 1; i >= 0; i--) {
286             FDDIGEST_t fddig = fd->digests + i;
287             if (fddig->hashctx == NULL)
288                 continue;
289             (void) rpmDigestFinal(fddig->hashctx, NULL, NULL, 0);
290             fddig->hashctx = NULL;
291         }
292         fd->ndigests = 0;
293         free(fd);
294     }
295     return NULL;
296 }
297
298 static inline
299 FD_t XfdNew(const char * msg, const char * file, unsigned line)
300 {
301     FD_t fd = xcalloc(1, sizeof(*fd));
302     if (fd == NULL) /* XXX xmalloc never returns NULL */
303         return NULL;
304     fd->nrefs = 0;
305     fd->flags = 0;
306     fd->magic = FDMAGIC;
307     fd->urlType = URL_IS_UNKNOWN;
308
309     fd->nfps = 0;
310     memset(fd->fps, 0, sizeof(fd->fps));
311
312     fd->fps[0].io = fdio;
313     fd->fps[0].fp = NULL;
314     fd->fps[0].fdno = -1;
315
316     fd->url = NULL;
317     fd->rd_timeoutsecs = 1;     /* XXX default value used to be -1 */
318     fd->contentLength = fd->bytesRemain = -1;
319     fd->wr_chunked = 0;
320     fd->syserrno = 0;
321     fd->errcookie = NULL;
322     fd->stats = xcalloc(1, sizeof(*fd->stats));
323
324     fd->ndigests = 0;
325     memset(fd->digests, 0, sizeof(fd->digests));
326
327     fd->ftpFileDoneNeeded = 0;
328     fd->firstFree = 0;
329     fd->fileSize = 0;
330     fd->fd_cpioPos = 0;
331
332     return XfdLink(fd, msg, file, line);
333 }
334
335 static ssize_t fdRead(void * cookie, char * buf, size_t count)
336 {
337     FD_t fd = c2f(cookie);
338     ssize_t rc;
339
340     if (fd->bytesRemain == 0) return 0; /* XXX simulate EOF */
341
342     fdstat_enter(fd, FDSTAT_READ);
343     /* HACK: flimsy wiring for davRead */
344     if (fd->req != NULL) {
345 #ifdef WITH_NEON
346         rc = davRead(fd, buf, (count > fd->bytesRemain ? fd->bytesRemain : count));
347 #else
348         rc = -1;
349 #endif
350         /* XXX Chunked davRead EOF. */
351         if (rc == 0)
352             fd->bytesRemain = 0;
353     } else
354         rc = read(fdFileno(fd), buf, (count > fd->bytesRemain ? fd->bytesRemain : count));
355     fdstat_exit(fd, FDSTAT_READ, rc);
356
357     if (fd->ndigests && rc > 0) fdUpdateDigests(fd, (void *)buf, rc);
358
359 DBGIO(fd, (stderr, "==>\tfdRead(%p,%p,%ld) rc %ld %s\n", cookie, buf, (long)count, (long)rc, fdbg(fd)));
360
361     return rc;
362 }
363
364 static ssize_t fdWrite(void * cookie, const char * buf, size_t count)
365 {
366     FD_t fd = c2f(cookie);
367     int fdno = fdFileno(fd);
368     ssize_t rc;
369
370     if (fd->bytesRemain == 0) return 0; /* XXX simulate EOF */
371
372     if (fd->ndigests && count > 0) fdUpdateDigests(fd, (void *)buf, count);
373
374     if (count == 0) return 0;
375
376     fdstat_enter(fd, FDSTAT_WRITE);
377     /* HACK: flimsy wiring for davWrite */
378     if (fd->req != NULL) {
379 #ifdef WITH_NEON
380         rc = davWrite(fd, buf, (count > fd->bytesRemain ? fd->bytesRemain : count));
381 #else
382         return -1;
383 #endif
384     } else
385         rc = write(fdno, buf, (count > fd->bytesRemain ? fd->bytesRemain : count));
386     fdstat_exit(fd, FDSTAT_WRITE, rc);
387
388 DBGIO(fd, (stderr, "==>\tfdWrite(%p,%p,%ld) rc %ld %s\n", cookie, buf, (long)count, (long)rc, fdbg(fd)));
389
390     return rc;
391 }
392
393 static inline int fdSeek(void * cookie, _libio_pos_t pos, int whence)
394 {
395 #ifdef USE_COOKIE_SEEK_POINTER
396     _IO_off64_t p = *pos;
397 #else
398     off_t p = pos;
399 #endif
400     FD_t fd = c2f(cookie);
401     off_t rc;
402
403     assert(fd->bytesRemain == -1);      /* XXX FIXME fadio only for now */
404     fdstat_enter(fd, FDSTAT_SEEK);
405     rc = lseek(fdFileno(fd), p, whence);
406     fdstat_exit(fd, FDSTAT_SEEK, rc);
407
408 DBGIO(fd, (stderr, "==>\tfdSeek(%p,%ld,%d) rc %lx %s\n", cookie, (long)p, whence, (unsigned long)rc, fdbg(fd)));
409
410     return rc;
411 }
412
413 static int fdClose( void * cookie)
414 {
415     FD_t fd;
416     int fdno;
417     int rc;
418
419     if (cookie == NULL) return -2;
420     fd = c2f(cookie);
421     fdno = fdFileno(fd);
422
423     fdSetFdno(fd, -1);
424
425     fdstat_enter(fd, FDSTAT_CLOSE);
426     /* HACK: flimsy wiring for davClose */
427     if (fd->req != NULL) {
428 #ifdef WITH_NEON
429         rc = davClose(fd);
430 #else
431         return -1;
432 #endif
433     } else
434         rc = ((fdno >= 0) ? close(fdno) : -2);
435     fdstat_exit(fd, FDSTAT_CLOSE, rc);
436
437 DBGIO(fd, (stderr, "==>\tfdClose(%p) rc %lx %s\n", (fd ? fd : NULL), (unsigned long)rc, fdbg(fd)));
438
439     fd = fdFree(fd, "open (fdClose)");
440     return rc;
441 }
442
443 static FD_t fdOpen(const char *path, int flags, mode_t mode)
444 {
445     FD_t fd;
446     int fdno;
447
448     fdno = open(path, flags, mode);
449     if (fdno < 0) return NULL;
450     if (fcntl(fdno, F_SETFD, FD_CLOEXEC)) {
451         (void) close(fdno);
452         return NULL;
453     }
454     fd = fdNew("open (fdOpen)");
455     fdSetFdno(fd, fdno);
456     fd->flags = flags;
457 DBGIO(fd, (stderr, "==>\tfdOpen(\"%s\",%x,0%o) %s\n", path, (unsigned)flags, (unsigned)mode, fdbg(fd)));
458     return fd;
459 }
460
461 /* LCL: function typedefs */
462 static struct FDIO_s fdio_s = {
463   fdRead, fdWrite, fdSeek, fdClose, XfdLink, XfdFree, XfdNew, fdFileno,
464   fdOpen, NULL, fdGetFp, NULL,  mkdir, chdir, rmdir, rename, unlink
465 };
466 FDIO_t fdio = &fdio_s ;
467
468 int fdWritable(FD_t fd, int secs)
469 {
470     int fdno;
471     int rc;
472 #if HAVE_POLL_H
473     int msecs = (secs >= 0 ? (1000 * secs) : -1);
474     struct pollfd wrfds;
475 #else
476     struct timeval timeout, *tvp = (secs >= 0 ? &timeout : NULL);
477     fd_set wrfds;
478     FD_ZERO(&wrfds);
479 #endif
480         
481     /* HACK: flimsy wiring for davWrite */
482     if (fd->req != NULL)
483         return 1;
484
485     if ((fdno = fdFileno(fd)) < 0)
486         return -1;      /* XXX W2DO? */
487         
488     do {
489 #if HAVE_POLL_H
490         wrfds.fd = fdno;
491         wrfds.events = POLLOUT;
492         wrfds.revents = 0;
493         rc = poll(&wrfds, 1, msecs);
494 #else
495         if (tvp) {
496             tvp->tv_sec = secs;
497             tvp->tv_usec = 0;
498         }
499         FD_SET(fdno, &wrfds);
500         rc = select(fdno + 1, NULL, &wrfds, NULL, tvp);
501 #endif
502
503         /* HACK: EBADF on PUT chunked termination from ufdClose. */
504 if (_rpmio_debug && !(rc == 1 && errno == 0))
505 fprintf(stderr, "*** fdWritable fdno %d rc %d %s\n", fdno, rc, strerror(errno));
506         if (rc < 0) {
507             switch (errno) {
508             case EINTR:
509                 continue;
510                 break;
511             default:
512                 return rc;
513                 break;
514             }
515         }
516         return rc;
517     } while (1);
518 }
519
520 int fdReadable(FD_t fd, int secs)
521 {
522     int fdno;
523     int rc;
524 #if HAVE_POLL_H
525     int msecs = (secs >= 0 ? (1000 * secs) : -1);
526     struct pollfd rdfds;
527 #else
528     struct timeval timeout, *tvp = (secs >= 0 ? &timeout : NULL);
529     fd_set rdfds;
530     FD_ZERO(&rdfds);
531 #endif
532
533     /* HACK: flimsy wiring for davRead */
534     if (fd->req != NULL)
535         return 1;
536
537     if ((fdno = fdFileno(fd)) < 0)
538         return -1;      /* XXX W2DO? */
539         
540     do {
541 #if HAVE_POLL_H
542         rdfds.fd = fdno;
543         rdfds.events = POLLIN;
544         rdfds.revents = 0;
545         rc = poll(&rdfds, 1, msecs);
546 #else
547         if (tvp) {
548             tvp->tv_sec = secs;
549             tvp->tv_usec = 0;
550         }
551         FD_SET(fdno, &rdfds);
552         rc = select(fdno + 1, &rdfds, NULL, NULL, tvp);
553 #endif
554
555         if (rc < 0) {
556             switch (errno) {
557             case EINTR:
558                 continue;
559                 break;
560             default:
561                 return rc;
562                 break;
563             }
564         }
565         return rc;
566     } while (1);
567 }
568
569 int fdFgets(FD_t fd, char * buf, size_t len)
570 {
571     int fdno;
572     int secs = fd->rd_timeoutsecs;
573     size_t nb = 0;
574     int ec = 0;
575     char lastchar = '\0';
576
577     if ((fdno = fdFileno(fd)) < 0)
578         return 0;       /* XXX W2DO? */
579         
580     do {
581         int rc;
582
583         /* Is there data to read? */
584         rc = fdReadable(fd, secs);
585
586         switch (rc) {
587         case -1:        /* error */
588             ec = -1;
589             continue;
590             break;
591         case  0:        /* timeout */
592             ec = -1;
593             continue;
594             break;
595         default:        /* data to read */
596             break;
597         }
598
599         errno = 0;
600 #ifdef  NOISY
601         rc = fdRead(fd, buf + nb, 1);
602 #else
603         rc = read(fdFileno(fd), buf + nb, 1);
604 #endif
605         if (rc < 0) {
606             fd->syserrno = errno;
607             switch (errno) {
608             case EWOULDBLOCK:
609                 continue;
610                 break;
611             default:
612                 break;
613             }
614 if (_rpmio_debug)
615 fprintf(stderr, "*** read: fd %p rc %d errno %d %s \"%s\"\n", fd, rc, errno, strerror(errno), buf);
616             ec = -1;
617             break;
618         } else if (rc == 0) {
619 if (_rpmio_debug)
620 fprintf(stderr, "*** read: fd %p rc %d EOF errno %d %s \"%s\"\n", fd, rc, errno, strerror(errno), buf);
621             break;
622         } else {
623             nb += rc;
624             buf[nb] = '\0';
625             lastchar = buf[nb - 1];
626         }
627     } while (ec == 0 && nb < len && lastchar != '\n');
628
629     return (ec >= 0 ? nb : ec);
630 }
631
632 /* =============================================================== */
633 /* Support for FTP/HTTP I/O.
634  */
635 const char * ftpStrerror(int errorNumber)
636 {
637     switch (errorNumber) {
638     case 0:
639         return _("Success");
640
641     /* HACK error impediance match, coalesce and rename. */
642     case FTPERR_NE_ERROR:
643         return ("NE_ERROR: Generic error.");
644     case FTPERR_NE_LOOKUP:
645         return ("NE_LOOKUP: Hostname lookup failed.");
646     case FTPERR_NE_AUTH:
647         return ("NE_AUTH: Server authentication failed.");
648     case FTPERR_NE_PROXYAUTH:
649         return ("NE_PROXYAUTH: Proxy authentication failed.");
650     case FTPERR_NE_CONNECT:
651         return ("NE_CONNECT: Could not connect to server.");
652     case FTPERR_NE_TIMEOUT:
653         return ("NE_TIMEOUT: Connection timed out.");
654     case FTPERR_NE_FAILED:
655         return ("NE_FAILED: The precondition failed.");
656     case FTPERR_NE_RETRY:
657         return ("NE_RETRY: Retry request.");
658     case FTPERR_NE_REDIRECT:
659         return ("NE_REDIRECT: Redirect received.");
660
661     case FTPERR_BAD_SERVER_RESPONSE:
662         return _("Bad server response");
663     case FTPERR_SERVER_IO_ERROR:
664         return _("Server I/O error");
665     case FTPERR_SERVER_TIMEOUT:
666         return _("Server timeout");
667     case FTPERR_BAD_HOST_ADDR:
668         return _("Unable to lookup server host address");
669     case FTPERR_BAD_HOSTNAME:
670         return _("Unable to lookup server host name");
671     case FTPERR_FAILED_CONNECT:
672         return _("Failed to connect to server");
673     case FTPERR_FAILED_DATA_CONNECT:
674         return _("Failed to establish data connection to server");
675     case FTPERR_FILE_IO_ERROR:
676         return _("I/O error to local file");
677     case FTPERR_PASSIVE_ERROR:
678         return _("Error setting remote server to passive mode");
679     case FTPERR_FILE_NOT_FOUND:
680         return _("File not found on server");
681     case FTPERR_NIC_ABORT_IN_PROGRESS:
682         return _("Abort in progress");
683
684     case FTPERR_UNKNOWN:
685     default:
686         return _("Unknown or unexpected error");
687     }
688 }
689
690 const char *urlStrerror(const char *url)
691 {
692     const char *retstr;
693     switch (urlIsURL(url)) {
694     case URL_IS_HTTPS:
695     case URL_IS_HTTP:
696     case URL_IS_HKP:
697     case URL_IS_FTP:
698     {   urlinfo u;
699 /* XXX This only works for httpReq/ftpLogin/ftpReq failures */
700         if (urlSplit(url, &u) == 0) {
701             retstr = ftpStrerror(u->openError);
702         } else
703             retstr = "Malformed URL";
704     }   break;
705     default:
706         retstr = strerror(errno);
707         break;
708     }
709     return retstr;
710 }
711
712 #if !defined(HAVE_GETADDRINFO)
713 #if !defined(USE_ALT_DNS) || !USE_ALT_DNS 
714 static int mygethostbyname(const char * host,
715                 struct in_addr * address)
716 {
717     struct hostent * hostinfo;
718
719     hostinfo = gethostbyname(host);
720     if (!hostinfo) return 1;
721
722     memcpy(address, hostinfo->h_addr_list[0], sizeof(*address));
723     return 0;
724 }
725 #endif
726
727 static int getHostAddress(const char * host, struct in_addr * address)
728 {
729 #if 0   /* XXX workaround nss_foo module hand-off using valgrind. */
730     if (!strcmp(host, "localhost")) {
731         if (!inet_aton("127.0.0.1", address))
732             return FTPERR_BAD_HOST_ADDR;
733     } else
734 #endif
735     if (xisdigit(host[0])) {
736         if (!inet_aton(host, address))
737             return FTPERR_BAD_HOST_ADDR;
738     } else {
739         if (mygethostbyname(host, address)) {
740             errno = h_errno;
741             return FTPERR_BAD_HOSTNAME;
742         }
743     }
744     
745     return 0;
746 }
747 #endif
748
749 static int tcpConnect(FD_t ctrl, const char * host, int port)
750 {
751     int fdno = -1;
752     int rc;
753 #ifdef  HAVE_GETADDRINFO
754     struct addrinfo hints, *res, *res0;
755     char pbuf[NI_MAXSERV];
756
757     memset(&hints, 0, sizeof(hints));
758     hints.ai_family = AF_UNSPEC;
759     hints.ai_socktype = SOCK_STREAM;
760     sprintf(pbuf, "%d", port);
761     pbuf[sizeof(pbuf)-1] = '\0';
762     rc = FTPERR_FAILED_CONNECT;
763     if (getaddrinfo(host, pbuf, &hints, &res0) == 0) {
764         for (res = res0; res != NULL; res= res->ai_next) {
765             if ((fdno = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) < 0)
766                 continue;
767             if (connect(fdno, res->ai_addr, res->ai_addrlen) < 0) {
768                 close(fdno);
769                 continue;
770             }
771             /* success */
772             rc = 0;
773             if (_ftp_debug) {
774                 char hbuf[NI_MAXHOST];
775                 getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, sizeof(hbuf),
776                                 NULL, 0, NI_NUMERICHOST);
777                 fprintf(stderr,"++ connect [%s]:%d on fdno %d\n",
778                                 hbuf, port, fdno);
779             }
780             break;
781         }
782         freeaddrinfo(res0);
783     }
784     if (rc < 0)
785         goto errxit;
786
787 #else   /* HAVE_GETADDRINFO */                      
788     struct sockaddr_in sin;
789
790     memset(&sin, 0, sizeof(sin));
791     sin.sin_family = AF_INET;
792     sin.sin_port = htons(port);
793     sin.sin_addr.s_addr = INADDR_ANY;
794     
795   do {
796     if ((rc = getHostAddress(host, &sin.sin_addr)) < 0)
797         break;
798
799     if ((fdno = socket(sin.sin_family, SOCK_STREAM, IPPROTO_IP)) < 0) {
800         rc = FTPERR_FAILED_CONNECT;
801         break;
802     }
803
804     if (connect(fdno, (struct sockaddr *) &sin, sizeof(sin))) {
805         rc = FTPERR_FAILED_CONNECT;
806         break;
807     }
808   } while (0);
809
810     if (rc < 0)
811         goto errxit;
812
813 if (_ftp_debug)
814 fprintf(stderr,"++ connect %s:%d on fdno %d\n",
815 inet_ntoa(sin.sin_addr)
816 ,
817 (int)ntohs(sin.sin_port), fdno);
818 #endif  /* HAVE_GETADDRINFO */
819
820     fdSetFdno(ctrl, (fdno >= 0 ? fdno : -1));
821     return 0;
822
823 errxit:
824     fdSetSyserrno(ctrl, errno, ftpStrerror(rc));
825     if (fdno >= 0)
826         (void) close(fdno);
827     return rc;
828 }
829
830 static int checkResponse(void * uu, FD_t ctrl,
831                 int *ecp, char ** str)
832 {
833     urlinfo u = uu;
834     char *buf;
835     size_t bufAlloced;
836     int bufLength = 0; 
837     const char *s;
838     char *se;
839     int ec = 0;
840     int moretodo = 1;
841     char errorCode[4];
842  
843     URLSANE(u);
844     if (u->bufAlloced == 0 || u->buf == NULL) {
845         u->bufAlloced = _url_iobuf_size;
846         u->buf = xcalloc(u->bufAlloced, sizeof(u->buf[0]));
847     }
848     buf = u->buf;
849     bufAlloced = u->bufAlloced;
850     *buf = '\0';
851
852     errorCode[0] = '\0';
853     
854     do {
855         int rc;
856
857         /*
858          * Read next line from server.
859          */
860         se = buf + bufLength;
861         *se = '\0';
862         rc = fdFgets(ctrl, se, (bufAlloced - bufLength));
863         if (rc < 0) {
864             ec = FTPERR_BAD_SERVER_RESPONSE;
865             continue;
866         } else if (rc == 0 || fdWritable(ctrl, 0) < 1)
867             moretodo = 0;
868
869         /*
870          * Process next line from server.
871          */
872         for (s = se; *s != '\0'; s = se) {
873                 const char *e;
874
875                 while (*se && *se != '\n') se++;
876
877                 if (se > s && se[-1] == '\r')
878                    se[-1] = '\0';
879                 if (*se == '\0')
880                     break;
881
882 if (_ftp_debug)
883 fprintf(stderr, "<- %s\n", s);
884
885                 /* HTTP: header termination on empty line */
886                 if (*s == '\0') {
887                     moretodo = 0;
888                     break;
889                 }
890                 *se++ = '\0';
891
892                 /* HTTP: look for "HTTP/1.1 123 ..." */
893                 if (!strncmp(s, "HTTP", sizeof("HTTP")-1)) {
894                     ctrl->contentLength = -1;
895                     if ((e = strchr(s, '.')) != NULL) {
896                         e++;
897                         u->httpVersion = *e - '0';
898                         if (u->httpVersion < 1 || u->httpVersion > 2)
899                             ctrl->persist = u->httpVersion = 0;
900                         else
901                             ctrl->persist = 1;
902                     }
903                     if ((e = strchr(s, ' ')) != NULL) {
904                         e++;
905                         if (strchr("0123456789", *e))
906                             strncpy(errorCode, e, 3);
907                         errorCode[3] = '\0';
908                     }
909                     continue;
910                 }
911
912                 /* HTTP: look for "token: ..." */
913                 for (e = s; *e && !(*e == ' ' || *e == ':'); e++)
914                     {};
915                 if (e > s && *e++ == ':') {
916                     size_t ne = (e - s);
917                     while (*e && *e == ' ') e++;
918 #if 0
919                     if (!strncmp(s, "Date:", ne)) {
920                     } else
921                     if (!strncmp(s, "Server:", ne)) {
922                     } else
923                     if (!strncmp(s, "Last-Modified:", ne)) {
924                     } else
925                     if (!strncmp(s, "ETag:", ne)) {
926                     } else
927 #endif
928                     if (!strncmp(s, "Accept-Ranges:", ne)) {
929                         if (!strcmp(e, "bytes"))
930                             u->httpHasRange = 1;
931                         if (!strcmp(e, "none"))
932                             u->httpHasRange = 0;
933                     } else
934                     if (!strncmp(s, "Content-Length:", ne)) {
935                         if (strchr("0123456789", *e))
936                             ctrl->contentLength = atoi(e);
937                     } else
938                     if (!strncmp(s, "Connection:", ne)) {
939                         if (!strcmp(e, "close"))
940                             ctrl->persist = 0;
941                     }
942 #if 0
943                     else
944                     if (!strncmp(s, "Content-Type:", ne)) {
945                     } else
946                     if (!strncmp(s, "Transfer-Encoding:", ne)) {
947                         if (!strcmp(e, "chunked"))
948                             ctrl->wr_chunked = 1;
949                         else
950                             ctrl->wr_chunked = 0;
951                     } else
952                     if (!strncmp(s, "Allow:", ne)) {
953                     }
954 #endif
955                     continue;
956                 }
957
958                 /* HTTP: look for "<TITLE>501 ... </TITLE>" */
959                 if (!strncmp(s, "<TITLE>", sizeof("<TITLE>")-1))
960                     s += sizeof("<TITLE>") - 1;
961
962                 /* FTP: look for "123-" and/or "123 " */
963                 if (strchr("0123456789", *s)) {
964                     if (errorCode[0] != '\0') {
965                         if (!strncmp(s, errorCode, sizeof("123")-1) && s[3] == ' ')
966                             moretodo = 0;
967                     } else {
968                         strncpy(errorCode, s, sizeof("123")-1);
969                         errorCode[3] = '\0';
970                         if (s[3] != '-')
971                             moretodo = 0;
972                     }
973                 }
974         }
975
976         if (moretodo && se > s) {
977             bufLength = se - s - 1;
978             if (s != buf)
979                 memmove(buf, s, bufLength);
980         } else {
981             bufLength = 0;
982         }
983     } while (moretodo && ec == 0);
984
985     if (str)    *str = buf;
986     if (ecp)    *ecp = atoi(errorCode);
987
988     return ec;
989 }
990
991 static int ftpCheckResponse(urlinfo u, char ** str)
992 {
993     int ec = 0;
994     int rc;
995
996     URLSANE(u);
997     rc = checkResponse(u, u->ctrl, &ec, str);
998
999     switch (ec) {
1000     case 550:
1001         return FTPERR_FILE_NOT_FOUND;
1002         break;
1003     case 552:
1004         return FTPERR_NIC_ABORT_IN_PROGRESS;
1005         break;
1006     default:
1007         if (ec >= 400 && ec <= 599) {
1008             return FTPERR_BAD_SERVER_RESPONSE;
1009         }
1010         break;
1011     }
1012     return rc;
1013 }
1014
1015 static int ftpCommand(urlinfo u, char ** str, ...)
1016 {
1017     va_list ap;
1018     int len = 0;
1019     const char * s, * t;
1020     char * te;
1021     int rc;
1022
1023     URLSANE(u);
1024     va_start(ap, str);
1025     while ((s = va_arg(ap, const char *)) != NULL) {
1026         if (len) len++;
1027         len += strlen(s);
1028     }
1029     len += sizeof("\r\n")-1;
1030     va_end(ap);
1031
1032     t = te = alloca(len + 1);
1033
1034     va_start(ap, str);
1035     while ((s = va_arg(ap, const char *)) != NULL) {
1036         if (te > t) *te++ = ' ';
1037         te = stpcpy(te, s);
1038     }
1039     te = stpcpy(te, "\r\n");
1040     va_end(ap);
1041
1042 if (_ftp_debug)
1043 fprintf(stderr, "-> %s", t);
1044     if (fdWrite(u->ctrl, t, (te-t)) != (te-t))
1045         return FTPERR_SERVER_IO_ERROR;
1046
1047     rc = ftpCheckResponse(u, str);
1048     return rc;
1049 }
1050
1051 static int ftpLogin(urlinfo u)
1052 {
1053     const char * host;
1054     const char * user;
1055     const char * password;
1056     int port;
1057     int rc;
1058
1059     URLSANE(u);
1060     u->ctrl = fdLink(u->ctrl, "open ctrl");
1061
1062     if (((host = (u->proxyh ? u->proxyh : u->host)) == NULL)) {
1063         rc = FTPERR_BAD_HOSTNAME;
1064         goto errxit;
1065     }
1066
1067     if ((port = (u->proxyp > 0 ? u->proxyp : u->port)) < 0) port = IPPORT_FTP;
1068
1069     if ((user = (u->proxyu ? u->proxyu : u->user)) == NULL)
1070         user = "anonymous";
1071
1072     if ((password = u->password) == NULL) {
1073         uid_t uid = getuid();
1074         struct passwd * pw;
1075         if (uid && (pw = getpwuid(uid)) != NULL) {
1076             char *myp = alloca(strlen(pw->pw_name) + sizeof("@"));
1077             strcpy(myp, pw->pw_name);
1078             strcat(myp, "@");
1079             password = myp;
1080         } else {
1081             password = "root@";
1082         }
1083     }
1084
1085     if (fdFileno(u->ctrl) >= 0 && fdWritable(u->ctrl, 0) < 1)
1086         (void) fdClose(u->ctrl);
1087
1088     if (fdFileno(u->ctrl) < 0) {
1089         rc = tcpConnect(u->ctrl, host, port);
1090         if (rc < 0)
1091             goto errxit2;
1092     }
1093
1094     if ((rc = ftpCheckResponse(u, NULL)))
1095         goto errxit;
1096
1097     if ((rc = ftpCommand(u, NULL, "USER", user, NULL)))
1098         goto errxit;
1099
1100     if ((rc = ftpCommand(u, NULL, "PASS", password, NULL)))
1101         goto errxit;
1102
1103     if ((rc = ftpCommand(u, NULL, "TYPE", "I", NULL)))
1104         goto errxit;
1105
1106     return 0;
1107
1108 errxit:
1109     fdSetSyserrno(u->ctrl, errno, ftpStrerror(rc));
1110 errxit2:
1111     if (fdFileno(u->ctrl) >= 0)
1112         (void) fdClose(u->ctrl);
1113     return rc;
1114 }
1115
1116 int ftpReq(FD_t data, const char * ftpCmd, const char * ftpArg)
1117 {
1118     urlinfo u = data->url;
1119 #if !defined(HAVE_GETADDRINFO)
1120     struct sockaddr_in dataAddress;
1121 #endif  /* HAVE_GETADDRINFO */
1122     char remoteIP[NI_MAXHOST];
1123     char * cmd;
1124     int cmdlen;
1125     char * passReply;
1126     char * chptr;
1127     int rc;
1128     int epsv;
1129     int port;
1130
1131     URLSANE(u);
1132     if (ftpCmd == NULL)
1133         return FTPERR_UNKNOWN;  /* XXX W2DO? */
1134
1135     cmdlen = strlen(ftpCmd) + (ftpArg ? 1+strlen(ftpArg) : 0) + sizeof("\r\n");
1136     chptr = cmd = alloca(cmdlen);
1137     chptr = stpcpy(chptr, ftpCmd);
1138     if (ftpArg) {
1139         *chptr++ = ' ';
1140         chptr = stpcpy(chptr, ftpArg);
1141     }
1142     chptr = stpcpy(chptr, "\r\n");
1143     cmdlen = chptr - cmd;
1144
1145 /*
1146  * Get the ftp version of the Content-Length.
1147  */
1148     if (!strncmp(cmd, "RETR", 4)) {
1149         unsigned cl;
1150
1151         passReply = NULL;
1152         rc = ftpCommand(u, &passReply, "SIZE", ftpArg, NULL);
1153         if (rc)
1154             goto errxit;
1155         if (sscanf(passReply, "%d %u", &rc, &cl) != 2) {
1156             rc = FTPERR_BAD_SERVER_RESPONSE;
1157             goto errxit;
1158         }
1159         rc = 0;
1160         data->contentLength = cl;
1161     }
1162
1163     epsv = 0;
1164     passReply = NULL;
1165 #ifdef HAVE_GETNAMEINFO
1166     rc = ftpCommand(u, &passReply, "EPSV", NULL);
1167     if (rc==0) {
1168 #ifdef HAVE_GETADDRINFO
1169         struct sockaddr_storage ss;
1170 #else /* HAVE_GETADDRINFO */
1171         struct sockaddr_in ss;
1172 #endif /* HAVE_GETADDRINFO */
1173         socklen_t size;
1174         /* we need to know IP of remote host */
1175         size=sizeof(ss);
1176         if ((getpeername(fdFileno(c2f(u->ctrl)), (struct sockaddr *)&ss, &size) == 0) &&
1177                         (getnameinfo((struct sockaddr *)&ss, size, remoteIP, sizeof(remoteIP),
1178                                 NULL, 0, NI_NUMERICHOST) == 0))
1179                 epsv++;
1180         else {
1181                 /* abort EPSV and fall back to PASV */
1182                 rc = ftpCommand(u, &passReply, "ABOR", NULL);
1183                 if (rc) {
1184                     rc = FTPERR_PASSIVE_ERROR;
1185                     goto errxit;
1186                 }
1187         }
1188     }
1189     if (epsv==0)
1190 #endif /* HAVE_GETNAMEINFO */
1191         rc = ftpCommand(u, &passReply, "PASV", NULL);
1192     if (rc) {
1193         rc = FTPERR_PASSIVE_ERROR;
1194         goto errxit;
1195     }
1196
1197     chptr = passReply;
1198     while (*chptr && *chptr != '(') chptr++;
1199     if (*chptr != '(') return FTPERR_PASSIVE_ERROR; 
1200     chptr++;
1201     passReply = chptr;
1202     while (*chptr && *chptr != ')') chptr++;
1203     if (*chptr != ')') return FTPERR_PASSIVE_ERROR;
1204     *chptr-- = '\0';
1205
1206     if (epsv) {
1207         int i;
1208         if(sscanf(passReply,"%*c%*c%*c%d%*c",&i) != 1) {
1209            rc = FTPERR_PASSIVE_ERROR;
1210            goto errxit;
1211         }
1212         port = i;
1213     } else {
1214  
1215     while (*chptr && *chptr != ',') chptr--;
1216     if (*chptr != ',') return FTPERR_PASSIVE_ERROR;
1217     chptr--;
1218     while (*chptr && *chptr != ',') chptr--;
1219     if (*chptr != ',') return FTPERR_PASSIVE_ERROR;
1220     *chptr++ = '\0';
1221     
1222     /* now passReply points to the IP portion, and chptr points to the
1223        port number portion */
1224
1225     {   int i, j;
1226         if (sscanf(chptr, "%d,%d", &i, &j) != 2) {
1227             rc = FTPERR_PASSIVE_ERROR;
1228             goto errxit;
1229         }
1230         port = (((unsigned)i) << 8) + j;
1231     }
1232
1233     chptr = passReply;
1234     while (*chptr++ != '\0') {
1235         if (*chptr == ',') *chptr = '.';
1236     }
1237     sprintf(remoteIP, "%s", passReply);
1238     } /* if (epsv) */
1239
1240 #ifdef HAVE_GETADDRINFO
1241     {
1242         struct addrinfo hints, *res, *res0;
1243         char pbuf[NI_MAXSERV];
1244
1245         memset(&hints, 0, sizeof(hints));
1246         hints.ai_family = AF_UNSPEC;
1247         hints.ai_socktype = SOCK_STREAM;
1248         hints.ai_flags = AI_NUMERICHOST;
1249         sprintf(pbuf, "%d", port);
1250         pbuf[sizeof(pbuf)-1] = '\0';
1251         if (getaddrinfo(remoteIP, pbuf, &hints, &res0)) {
1252             rc = FTPERR_PASSIVE_ERROR;
1253             goto errxit;
1254         }
1255
1256         for (res = res0; res != NULL; res = res->ai_next) {
1257             rc = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
1258             fdSetFdno(data, (rc >= 0 ? rc : -1));
1259             if (rc < 0) {
1260                 if (res->ai_next)
1261                     continue;
1262                 else {
1263                     rc = FTPERR_FAILED_CONNECT;
1264                     freeaddrinfo(res0);
1265                     goto errxit;
1266                 }
1267             }
1268             data = fdLink(data, "open data (ftpReq)");
1269
1270             /* XXX setsockopt SO_LINGER */
1271             /* XXX setsockopt SO_KEEPALIVE */
1272             /* XXX setsockopt SO_TOS IPTOS_THROUGHPUT */
1273             
1274             {
1275                 int criterr = 0;
1276                 while (connect(fdFileno(data), res->ai_addr, res->ai_addrlen) < 0) {
1277                     if (errno == EINTR)
1278                         continue;
1279                     criterr++;
1280                 }
1281                 if (criterr) {
1282                     if (res->ai_addr) {
1283                         fdClose(data);
1284                         continue;
1285                     } else {
1286                         rc = FTPERR_PASSIVE_ERROR;
1287                         freeaddrinfo(res0);
1288                         goto errxit;
1289                     }
1290                 }
1291             }
1292             /* success */
1293             rc = 0;
1294             break;
1295         }
1296         freeaddrinfo(res0);
1297     }
1298             
1299 #else /* HAVE_GETADDRINFO */
1300     memset(&dataAddress, 0, sizeof(dataAddress));
1301     dataAddress.sin_family = AF_INET;
1302     dataAddress.sin_port = htons(port);
1303
1304     if (!inet_aton(remoteIP, &dataAddress.sin_addr)) {
1305         rc = FTPERR_PASSIVE_ERROR;
1306         goto errxit;
1307     }
1308
1309     rc = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
1310     fdSetFdno(data, (rc >= 0 ? rc : -1));
1311     if (rc < 0) {
1312         rc = FTPERR_FAILED_CONNECT;
1313         goto errxit;
1314     }
1315     data = fdLink(data, "open data (ftpReq)");
1316
1317     /* XXX setsockopt SO_LINGER */
1318     /* XXX setsockopt SO_KEEPALIVE */
1319     /* XXX setsockopt SO_TOS IPTOS_THROUGHPUT */
1320
1321     while (connect(fdFileno(data), (struct sockaddr *) &dataAddress, 
1322                 sizeof(dataAddress)) < 0)
1323     {
1324         if (errno == EINTR)
1325             continue;
1326         rc = FTPERR_FAILED_DATA_CONNECT;
1327         goto errxit;
1328     }
1329 #endif /* HAVE_GETADDRINFO */
1330
1331 if (_ftp_debug)
1332 fprintf(stderr, "-> %s", cmd);
1333     if (fdWrite(u->ctrl, cmd, cmdlen) != cmdlen) {
1334         rc = FTPERR_SERVER_IO_ERROR;
1335         goto errxit;
1336     }
1337
1338     if ((rc = ftpCheckResponse(u, NULL))) {
1339         goto errxit;
1340     }
1341
1342     data->ftpFileDoneNeeded = 1;
1343     u->ctrl = fdLink(u->ctrl, "grab data (ftpReq)");
1344     u->ctrl = fdLink(u->ctrl, "open data (ftpReq)");
1345     return 0;
1346
1347 errxit:
1348     fdSetSyserrno(u->ctrl, errno, ftpStrerror(rc));
1349     if (fdFileno(data) >= 0)
1350         (void) fdClose(data);
1351     return rc;
1352 }
1353
1354 static rpmCallbackFunction      urlNotify = NULL;
1355
1356 static void *                   urlNotifyData = NULL;
1357
1358 static int                      urlNotifyCount = -1;
1359
1360 void urlSetCallback(rpmCallbackFunction notify, void *notifyData, int notifyCount) {
1361     urlNotify = notify;
1362     urlNotifyData = notifyData;
1363     urlNotifyCount = (notifyCount >= 0) ? notifyCount : 4096;
1364 }
1365
1366 int ufdCopy(FD_t sfd, FD_t tfd)
1367 {
1368     char buf[BUFSIZ];
1369     int itemsRead;
1370     int itemsCopied = 0;
1371     int rc = 0;
1372     int notifier = -1;
1373
1374     if (urlNotify) {
1375         /* FIX: check rc */
1376         (void)(*urlNotify) (NULL, RPMCALLBACK_INST_OPEN_FILE,
1377                 0, 0, NULL, urlNotifyData);
1378     }
1379     
1380     while (1) {
1381         rc = Fread(buf, sizeof(buf[0]), sizeof(buf), sfd);
1382         if (rc < 0)
1383             break;
1384         else if (rc == 0) {
1385             rc = itemsCopied;
1386             break;
1387         }
1388         itemsRead = rc;
1389         rc = Fwrite(buf, sizeof(buf[0]), itemsRead, tfd);
1390         if (rc < 0)
1391             break;
1392         if (rc != itemsRead) {
1393             rc = FTPERR_FILE_IO_ERROR;
1394             break;
1395         }
1396
1397         itemsCopied += itemsRead;
1398         if (urlNotify && urlNotifyCount > 0) {
1399             int n = itemsCopied/urlNotifyCount;
1400             if (n != notifier) {
1401                 /* FIX: check rc */
1402                 (void)(*urlNotify) (NULL, RPMCALLBACK_INST_PROGRESS,
1403                         itemsCopied, 0, NULL, urlNotifyData);
1404                 notifier = n;
1405             }
1406         }
1407     }
1408
1409     DBGIO(sfd, (stderr, "++ copied %d bytes: %s\n", itemsCopied,
1410         ftpStrerror(rc)));
1411
1412     if (urlNotify) {
1413         /* FIX: check rc */
1414         (void)(*urlNotify) (NULL, RPMCALLBACK_INST_OPEN_FILE,
1415                 itemsCopied, itemsCopied, NULL, urlNotifyData);
1416     }
1417     
1418     return rc;
1419 }
1420
1421 static int urlConnect(const char * url, urlinfo * uret)
1422 {
1423     urlinfo u;
1424     int rc = 0;
1425
1426     if (urlSplit(url, &u) < 0)
1427         return -1;
1428
1429     if (u->urltype == URL_IS_FTP) {
1430         FD_t fd;
1431
1432         if ((fd = u->ctrl) == NULL) {
1433             fd = u->ctrl = fdNew("persist ctrl (urlConnect FTP)");
1434             fdSetIo(u->ctrl, ufdio);
1435         }
1436         
1437         fd->rd_timeoutsecs = ftpTimeoutSecs;
1438         fd->contentLength = fd->bytesRemain = -1;
1439         fd->url = NULL;         /* XXX FTP ctrl has not */
1440         fd->ftpFileDoneNeeded = 0;
1441         fd = fdLink(fd, "grab ctrl (urlConnect FTP)");
1442
1443         if (fdFileno(u->ctrl) < 0) {
1444             rpmMessage(RPMMESS_DEBUG, _("logging into %s as %s, pw %s\n"),
1445                         u->host ? u->host : "???",
1446                         u->user ? u->user : "ftp",
1447                         u->password ? u->password : "(username)");
1448
1449             if ((rc = ftpLogin(u)) < 0) {       /* XXX save ftpLogin error */
1450                 u->ctrl = fdFree(fd, "grab ctrl (urlConnect FTP)");
1451                 u->openError = rc;
1452             }
1453         }
1454     }
1455
1456     if (uret != NULL)
1457         *uret = urlLink(u, "urlConnect");
1458     u = urlFree(u, "urlSplit (urlConnect)");    
1459
1460     return rc;
1461 }
1462
1463 int ufdGetFile(FD_t sfd, FD_t tfd)
1464 {
1465     int rc;
1466
1467     FDSANE(sfd);
1468     FDSANE(tfd);
1469     rc = ufdCopy(sfd, tfd);
1470     (void) Fclose(sfd);
1471     if (rc > 0)         /* XXX ufdCopy now returns no. bytes copied */
1472         rc = 0;
1473     return rc;
1474 }
1475
1476 int ftpCmd(const char * cmd, const char * url, const char * arg2)
1477 {
1478     urlinfo u;
1479     int rc;
1480     const char * path;
1481
1482     if (urlConnect(url, &u) < 0)
1483         return -1;
1484
1485     (void) urlPath(url, &path);
1486
1487     rc = ftpCommand(u, NULL, cmd, path, arg2, NULL);
1488     u->ctrl = fdFree(u->ctrl, "grab ctrl (ftpCmd)");
1489     return rc;
1490 }
1491
1492 /* XXX these aren't worth the pain of including correctly */
1493 #if !defined(IAC)
1494 #define IAC     255             /* interpret as command: */
1495 #endif
1496 #if !defined(IP)
1497 #define IP      244             /* interrupt process--permanently */
1498 #endif
1499 #if !defined(DM)
1500 #define DM      242             /* data mark--for connect. cleaning */
1501 #endif
1502 #if !defined(SHUT_RDWR)
1503 #define SHUT_RDWR       1+1
1504 #endif
1505
1506 static int ftpAbort(urlinfo u, FD_t data)
1507 {
1508     static unsigned char ipbuf[3] = { IAC, IP, IAC };
1509     FD_t ctrl;
1510     int rc;
1511     int tosecs;
1512
1513     URLSANE(u);
1514
1515     if (data != NULL) {
1516         data->ftpFileDoneNeeded = 0;
1517         if (fdFileno(data) >= 0)
1518             u->ctrl = fdFree(u->ctrl, "open data (ftpAbort)");
1519         u->ctrl = fdFree(u->ctrl, "grab data (ftpAbort)");
1520     }
1521     ctrl = u->ctrl;
1522
1523     DBGIO(0, (stderr, "-> ABOR\n"));
1524
1525     if (send(fdFileno(ctrl), ipbuf, sizeof(ipbuf), MSG_OOB) != sizeof(ipbuf)) {
1526         (void) fdClose(ctrl);
1527         return FTPERR_SERVER_IO_ERROR;
1528     }
1529
1530     sprintf(u->buf, "%cABOR\r\n",(char) DM);
1531     if (fdWrite(ctrl, u->buf, 7) != 7) {
1532         (void) fdClose(ctrl);
1533         return FTPERR_SERVER_IO_ERROR;
1534     }
1535
1536     if (data && fdFileno(data) >= 0) {
1537         /* XXX shorten data drain time wait */
1538         tosecs = data->rd_timeoutsecs;
1539         data->rd_timeoutsecs = 10;
1540         if (fdReadable(data, data->rd_timeoutsecs) > 0) {
1541             while (timedRead(data, u->buf, u->bufAlloced) > 0)
1542                 u->buf[0] = '\0';
1543         }
1544         data->rd_timeoutsecs = tosecs;
1545         /* XXX ftp abort needs to close the data channel to receive status */
1546         (void) shutdown(fdFileno(data), SHUT_RDWR);
1547         (void) close(fdFileno(data));
1548         data->fps[0].fdno = -1; /* XXX WRONG but expedient */
1549     }
1550
1551     /* XXX shorten ctrl drain time wait */
1552     tosecs = u->ctrl->rd_timeoutsecs;
1553     u->ctrl->rd_timeoutsecs = 10;
1554     if ((rc = ftpCheckResponse(u, NULL)) == FTPERR_NIC_ABORT_IN_PROGRESS) {
1555         rc = ftpCheckResponse(u, NULL);
1556     }
1557     rc = ftpCheckResponse(u, NULL);
1558     u->ctrl->rd_timeoutsecs = tosecs;
1559
1560     return rc;
1561 }
1562
1563 static int ftpFileDone(urlinfo u, FD_t data)
1564 {
1565     int rc = 0;
1566
1567     URLSANE(u);
1568     assert(data->ftpFileDoneNeeded);
1569
1570     if (data->ftpFileDoneNeeded) {
1571         data->ftpFileDoneNeeded = 0;
1572         u->ctrl = fdFree(u->ctrl, "open data (ftpFileDone)");
1573         u->ctrl = fdFree(u->ctrl, "grab data (ftpFileDone)");
1574         rc = ftpCheckResponse(u, NULL);
1575     }
1576     return rc;
1577 }
1578
1579 static int httpResp(urlinfo u, FD_t ctrl, char ** str)
1580 {
1581     int ec = 0;
1582     int rc;
1583
1584     URLSANE(u);
1585     rc = checkResponse(u, ctrl, &ec, str);
1586
1587 if (_ftp_debug && !(rc == 0 && (ec == 200 || ec == 201)))
1588 fprintf(stderr, "*** httpResp: rc %d ec %d\n", rc, ec);
1589
1590     switch (ec) {
1591     case 200:
1592     case 201:                   /* 201 Created. */
1593         break;
1594     case 204:                   /* HACK: if overwriting, 204 No Content. */
1595     case 403:                   /* 403 Forbidden. */
1596         ctrl->syserrno = EACCES;        /* HACK */
1597         rc = FTPERR_UNKNOWN;
1598         break;
1599     default:
1600         rc = FTPERR_FILE_NOT_FOUND;
1601         break;
1602     }
1603     return rc;
1604 }
1605
1606 static int httpReq(FD_t ctrl, const char * httpCmd, const char * httpArg)
1607 {
1608     urlinfo u;
1609     const char * host;
1610     const char * path;
1611     char hthost[NI_MAXHOST];
1612     int port;
1613     int rc;
1614     char * req;
1615     size_t len;
1616     int retrying = 0;
1617
1618 assert(ctrl != NULL);
1619     u = ctrl->url;
1620     URLSANE(u);
1621
1622     if (((host = (u->proxyh ? u->proxyh : u->host)) == NULL))
1623         return FTPERR_BAD_HOSTNAME;
1624     if (strchr(host, ':'))
1625         sprintf(hthost, "[%s]", host);
1626     else
1627         strcpy(hthost, host);
1628
1629     if ((port = (u->proxyp > 0 ? u->proxyp : u->port)) < 0) port = 80;
1630     path = (u->proxyh || u->proxyp > 0) ? u->url : httpArg;
1631     if (path == NULL) path = "";
1632
1633 reopen:
1634     if (fdFileno(ctrl) >= 0 && (rc = fdWritable(ctrl, 0)) < 1) {
1635         (void) fdClose(ctrl);
1636     }
1637
1638     if (fdFileno(ctrl) < 0) {
1639         rc = tcpConnect(ctrl, host, port);
1640         if (rc < 0)
1641             goto errxit2;
1642         ctrl = fdLink(ctrl, "open ctrl (httpReq)");
1643     }
1644
1645     len = sizeof("\
1646 req x HTTP/1.0\r\n\
1647 User-Agent: rpm/3.0.4\r\n\
1648 Host: y:z\r\n\
1649 Accept: text/plain\r\n\
1650 Transfer-Encoding: chunked\r\n\
1651 \r\n\
1652 ") + strlen(httpCmd) + strlen(path) + sizeof(VERSION) + strlen(hthost) + 20;
1653
1654     req = alloca(len);
1655     *req = '\0';
1656
1657   if (!strcmp(httpCmd, "PUT")) {
1658     sprintf(req, "\
1659 %s %s HTTP/1.%d\r\n\
1660 User-Agent: rpm/%s\r\n\
1661 Host: %s:%d\r\n\
1662 Accept: text/plain\r\n\
1663 Transfer-Encoding: chunked\r\n\
1664 \r\n\
1665 ",      httpCmd, path, (u->httpVersion ? 1 : 0), VERSION, hthost, port);
1666 } else {
1667     sprintf(req, "\
1668 %s %s HTTP/1.%d\r\n\
1669 User-Agent: rpm/%s\r\n\
1670 Host: %s:%d\r\n\
1671 Accept: text/plain\r\n\
1672 \r\n\
1673 ",      httpCmd, path, (u->httpVersion ? 1 : 0), VERSION, hthost, port);
1674 }
1675
1676 if (_ftp_debug)
1677 fprintf(stderr, "-> %s", req);
1678
1679     len = strlen(req);
1680     if (fdWrite(ctrl, req, len) != len) {
1681         rc = FTPERR_SERVER_IO_ERROR;
1682         goto errxit;
1683     }
1684
1685     if (!strcmp(httpCmd, "PUT")) {
1686         ctrl->wr_chunked = 1;
1687     } else {
1688
1689         rc = httpResp(u, ctrl, NULL);
1690
1691         if (rc) {
1692             if (!retrying) {    /* not HTTP_OK */
1693                 retrying = 1;
1694                 (void) fdClose(ctrl);
1695                 goto reopen;
1696             }
1697             goto errxit;
1698         }
1699     }
1700
1701     ctrl = fdLink(ctrl, "open data (httpReq)");
1702     return 0;
1703
1704 errxit:
1705     fdSetSyserrno(ctrl, errno, ftpStrerror(rc));
1706 errxit2:
1707     if (fdFileno(ctrl) >= 0)
1708         (void) fdClose(ctrl);
1709     return rc;
1710 }
1711
1712 /* XXX DYING: unused */
1713 void * ufdGetUrlinfo(FD_t fd)
1714 {
1715     FDSANE(fd);
1716     if (fd->url == NULL)
1717         return NULL;
1718     return urlLink(fd->url, "ufdGetUrlinfo");
1719 }
1720
1721 /* =============================================================== */
1722 static ssize_t ufdRead(void * cookie, char * buf, size_t count)
1723 {
1724     FD_t fd = c2f(cookie);
1725     int bytesRead;
1726     int total;
1727
1728     /* XXX preserve timedRead() behavior */
1729     if (fdGetIo(fd) == fdio) {
1730         struct stat sb;
1731         int fdno = fdFileno(fd);
1732         (void) fstat(fdno, &sb);
1733         if (S_ISREG(sb.st_mode))
1734             return fdRead(fd, buf, count);
1735     }
1736
1737     UFDONLY(fd);
1738     assert(fd->rd_timeoutsecs >= 0);
1739
1740     for (total = 0; total < count; total += bytesRead) {
1741
1742         int rc;
1743
1744         bytesRead = 0;
1745
1746         /* Is there data to read? */
1747         if (fd->bytesRemain == 0) return total; /* XXX simulate EOF */
1748         rc = fdReadable(fd, fd->rd_timeoutsecs);
1749
1750         switch (rc) {
1751         case -1:        /* error */
1752         case  0:        /* timeout */
1753             return total;
1754             break;
1755         default:        /* data to read */
1756             break;
1757         }
1758
1759         rc = fdRead(fd, buf + total, count - total);
1760
1761         if (rc < 0) {
1762             switch (errno) {
1763             case EWOULDBLOCK:
1764                 continue;
1765                 break;
1766             default:
1767                 break;
1768             }
1769 if (_rpmio_debug)
1770 fprintf(stderr, "*** read: rc %d errno %d %s \"%s\"\n", rc, errno, strerror(errno), buf);
1771             return rc;
1772             break;
1773         } else if (rc == 0) {
1774             return total;
1775             break;
1776         }
1777         bytesRead = rc;
1778     }
1779
1780     return count;
1781 }
1782
1783 static ssize_t ufdWrite(void * cookie, const char * buf, size_t count)
1784 {
1785     FD_t fd = c2f(cookie);
1786     int bytesWritten;
1787     int total = 0;
1788
1789 #ifdef  NOTYET
1790     if (fdGetIo(fd) == fdio) {
1791         struct stat sb;
1792         (void) fstat(fdGetFdno(fd), &sb);
1793         if (S_ISREG(sb.st_mode))
1794             return fdWrite(fd, buf, count);
1795     }
1796 #endif
1797
1798     UFDONLY(fd);
1799
1800     for (total = 0; total < count; total += bytesWritten) {
1801
1802         int rc;
1803
1804         bytesWritten = 0;
1805
1806         /* Is there room to write data? */
1807         if (fd->bytesRemain == 0) {
1808 fprintf(stderr, "*** ufdWrite fd %p WRITE PAST END OF CONTENT\n", fd);
1809             return total;       /* XXX simulate EOF */
1810         }
1811         rc = fdWritable(fd, 2);         /* XXX configurable? */
1812
1813         switch (rc) {
1814         case -1:        /* error */
1815         case  0:        /* timeout */
1816             return total;
1817             break;
1818         default:        /* data to write */
1819             break;
1820         }
1821
1822         rc = fdWrite(fd, buf + total, count - total);
1823
1824         if (rc < 0) {
1825             switch (errno) {
1826             case EWOULDBLOCK:
1827                 continue;
1828                 break;
1829             default:
1830                 break;
1831             }
1832 if (_rpmio_debug)
1833 fprintf(stderr, "*** write: rc %d errno %d %s \"%s\"\n", rc, errno, strerror(errno), buf);
1834             return rc;
1835             break;
1836         } else if (rc == 0) {
1837             return total;
1838             break;
1839         }
1840         bytesWritten = rc;
1841     }
1842
1843     return count;
1844 }
1845
1846 static inline int ufdSeek(void * cookie, _libio_pos_t pos, int whence)
1847 {
1848     FD_t fd = c2f(cookie);
1849
1850     switch (fd->urlType) {
1851     case URL_IS_UNKNOWN:
1852     case URL_IS_PATH:
1853         break;
1854     case URL_IS_HTTPS:
1855     case URL_IS_HTTP:
1856     case URL_IS_HKP:
1857     case URL_IS_FTP:
1858     case URL_IS_DASH:
1859     default:
1860         return -2;
1861         break;
1862     }
1863     return fdSeek(cookie, pos, whence);
1864 }
1865
1866 int ufdClose( void * cookie)
1867 {
1868     FD_t fd = c2f(cookie);
1869
1870     UFDONLY(fd);
1871
1872     if (fd->url) {
1873         urlinfo u = fd->url;
1874
1875         if (fd == u->data)
1876                 fd = u->data = fdFree(fd, "grab data (ufdClose persist)");
1877         else
1878                 fd = fdFree(fd, "grab data (ufdClose)");
1879         (void) urlFree(fd->url, "url (ufdClose)");
1880         fd->url = NULL;
1881         u->ctrl = fdFree(u->ctrl, "grab ctrl (ufdClose)");
1882
1883         if (u->urltype == URL_IS_FTP) {
1884
1885             /* XXX if not using libio, lose the fp from fpio */
1886             {   FILE * fp;
1887                 fp = fdGetFILE(fd);
1888                 if (noLibio && fp)
1889                     fdSetFp(fd, NULL);
1890             }
1891
1892             /*
1893              * Non-error FTP has 4 refs on the data fd:
1894              *  "persist data (ufdOpen FTP)"            rpmio.c:888
1895              *  "grab data (ufdOpen FTP)"               rpmio.c:892
1896              *  "open data (ftpReq)"                    ftp.c:633
1897              *  "fopencookie"                           rpmio.c:1507
1898              *
1899              * Non-error FTP has 5 refs on the ctrl fd:
1900              *  "persist ctrl"                          url.c:176
1901              *  "grab ctrl (urlConnect FTP)"            rpmio.c:404
1902              *  "open ctrl"                             ftp.c:504
1903              *  "grab data (ftpReq)"                    ftp.c:661
1904              *  "open data (ftpReq)"                    ftp.c:662
1905              */
1906             if (fd->bytesRemain > 0) {
1907                 if (fd->ftpFileDoneNeeded) {
1908                     if (fdReadable(u->ctrl, 0) > 0)
1909                         (void) ftpFileDone(u, fd);
1910                     else
1911                         (void) ftpAbort(u, fd);
1912                 }
1913             } else {
1914                 int rc;
1915                 /* XXX STOR et al require close before ftpFileDone */
1916                 rc = fdClose(fd);
1917 #if 0   /* XXX error exit from ufdOpen does not have this set */
1918                 assert(fd->ftpFileDoneNeeded != 0);
1919 #endif
1920                 /* FIX: u->data undefined */
1921                 if (fd->ftpFileDoneNeeded)
1922                     (void) ftpFileDone(u, fd);
1923                 return rc;
1924             }
1925         }
1926
1927         /* XXX Why not (u->urltype == URL_IS_HTTP) ??? */
1928         /* XXX Why not (u->urltype == URL_IS_HTTPS) ??? */
1929         /* XXX Why not (u->urltype == URL_IS_HKP) ??? */
1930         if (u->scheme != NULL
1931          && (!strncmp(u->scheme, "http", sizeof("http")-1) || !strncmp(u->scheme, "hkp", sizeof("hkp")-1)))
1932         {
1933             /*
1934              * HTTP has 4 (or 5 if persistent malloc) refs on the fd:
1935              *  "persist ctrl"                          url.c:177
1936              *  "grab ctrl (ufdOpen HTTP)"              rpmio.c:924
1937              *  "grab data (ufdOpen HTTP)"              rpmio.c:928
1938              *  "open ctrl (httpReq)"                   ftp.c:382
1939              *  "open data (httpReq)"                   ftp.c:435
1940              */
1941
1942             if (fd == u->ctrl)
1943                 fd = u->ctrl = fdFree(fd, "open data (ufdClose HTTP persist ctrl)");
1944             else if (fd == u->data)
1945                 fd = u->data = fdFree(fd, "open data (ufdClose HTTP persist data)");
1946             else
1947                 fd = fdFree(fd, "open data (ufdClose HTTP)");
1948
1949             /* XXX if not using libio, lose the fp from fpio */
1950             {   FILE * fp;
1951                 fp = fdGetFILE(fd);
1952                 if (noLibio && fp)
1953                     fdSetFp(fd, NULL);
1954             }
1955
1956             /* If content remains, then don't persist. */
1957             if (fd->bytesRemain > 0)
1958                 fd->persist = 0;
1959             fd->contentLength = fd->bytesRemain = -1;
1960
1961             /* If persisting, then Fclose will juggle refcounts. */
1962             if (fd->persist && (fd == u->ctrl || fd == u->data))
1963                 return 0;
1964         }
1965     }
1966     return fdClose(fd);
1967 }
1968
1969 FD_t ftpOpen(const char *url, int flags,
1970                 mode_t mode, urlinfo *uret)
1971 {
1972     urlinfo u = NULL;
1973     FD_t fd = NULL;
1974
1975 #if 0   /* XXX makeTempFile() heartburn */
1976     assert(!(flags & O_RDWR));
1977 #endif
1978     if (urlConnect(url, &u) < 0)
1979         goto exit;
1980
1981     if (u->data == NULL)
1982         u->data = fdNew("persist data (ftpOpen)");
1983
1984     if (u->data->url == NULL)
1985         fd = fdLink(u->data, "grab data (ftpOpen persist data)");
1986     else
1987         fd = fdNew("grab data (ftpOpen)");
1988
1989     if (fd) {
1990         fdSetIo(fd, ufdio);
1991         fd->ftpFileDoneNeeded = 0;
1992         fd->rd_timeoutsecs = ftpTimeoutSecs;
1993         fd->contentLength = fd->bytesRemain = -1;
1994         fd->url = urlLink(u, "url (ufdOpen FTP)");
1995         fd->urlType = URL_IS_FTP;
1996     }
1997
1998 exit:
1999     if (uret)
2000         *uret = u;
2001     return fd;
2002 }
2003
2004 #ifndef WITH_NEON
2005        /* FIX: u->{ctrl,data}->url undef after XurlLink. */
2006 static FD_t httpOpen(const char * url, int flags,
2007                 mode_t mode, urlinfo * uret)
2008 {
2009     urlinfo u = NULL;
2010     FD_t fd = NULL;
2011
2012 #if 0   /* XXX makeTempFile() heartburn */
2013     assert(!(flags & O_RDWR));
2014 #endif
2015     if (urlSplit(url, &u))
2016         goto exit;
2017
2018     if (u->ctrl == NULL)
2019         u->ctrl = fdNew("persist ctrl (httpOpen)");
2020     if (u->ctrl->nrefs > 2 && u->data == NULL)
2021         u->data = fdNew("persist data (httpOpen)");
2022
2023     if (u->ctrl->url == NULL)
2024         fd = fdLink(u->ctrl, "grab ctrl (httpOpen persist ctrl)");
2025     else if (u->data->url == NULL)
2026         fd = fdLink(u->data, "grab ctrl (httpOpen persist data)");
2027     else
2028         fd = fdNew("grab ctrl (httpOpen)");
2029
2030     if (fd) {
2031         fdSetIo(fd, ufdio);
2032         fd->ftpFileDoneNeeded = 0;
2033         fd->rd_timeoutsecs = httpTimeoutSecs;
2034         fd->contentLength = fd->bytesRemain = -1;
2035         fd->url = urlLink(u, "url (httpOpen)");
2036         fd = fdLink(fd, "grab data (httpOpen)");
2037         fd->urlType = URL_IS_HTTP;
2038     }
2039
2040 exit:
2041     if (uret)
2042         *uret = u;
2043     return fd;
2044 }
2045 #endif
2046
2047 static FD_t ufdOpen(const char * url, int flags, mode_t mode)
2048 {
2049     FD_t fd = NULL;
2050     const char * cmd;
2051     urlinfo u;
2052     const char * path;
2053     urltype urlType = urlPath(url, &path);
2054
2055 if (_rpmio_debug)
2056 fprintf(stderr, "*** ufdOpen(%s,0x%x,0%o)\n", url, (unsigned)flags, (unsigned)mode);
2057
2058     switch (urlType) {
2059     case URL_IS_FTP:
2060         fd = ftpOpen(url, flags, mode, &u);
2061         if (fd == NULL || u == NULL)
2062             break;
2063
2064         /* XXX W2DO? use STOU rather than STOR to prevent clobbering */
2065         cmd = ((flags & O_WRONLY) 
2066                 ?  ((flags & O_APPEND) ? "APPE" :
2067                    ((flags & O_CREAT) ? "STOR" : "STOR"))
2068                 :  ((flags & O_CREAT) ? "STOR" : "RETR"));
2069         u->openError = ftpReq(fd, cmd, path);
2070         if (u->openError < 0) {
2071             /* XXX make sure that we can exit through ufdClose */
2072             fd = fdLink(fd, "error data (ufdOpen FTP)");
2073         } else {
2074             fd->bytesRemain = ((!strcmp(cmd, "RETR"))
2075                 ?  fd->contentLength : -1);
2076             fd->wr_chunked = 0;
2077         }
2078         break;
2079     case URL_IS_HTTPS:
2080     case URL_IS_HTTP:
2081     case URL_IS_HKP:
2082 #ifdef WITH_NEON
2083         fd = davOpen(url, flags, mode, &u);
2084 #else
2085         fd = httpOpen(url, flags, mode, &u);
2086 #endif
2087         if (fd == NULL || u == NULL)
2088             break;
2089
2090         cmd = ((flags & O_WRONLY)
2091                 ?  ((flags & O_APPEND) ? "PUT" :
2092                    ((flags & O_CREAT) ? "PUT" : "PUT"))
2093                 : "GET");
2094 #ifdef WITH_NEON
2095         u->openError = davReq(fd, cmd, path);
2096 #else
2097         u->openError = httpReq(fd, cmd, path);
2098 #endif
2099         if (u->openError < 0) {
2100             /* XXX make sure that we can exit through ufdClose */
2101             fd = fdLink(fd, "error ctrl (ufdOpen HTTP)");
2102             fd = fdLink(fd, "error data (ufdOpen HTTP)");
2103         } else {
2104             fd->bytesRemain = ((!strcmp(cmd, "GET"))
2105                 ?  fd->contentLength : -1);
2106             fd->wr_chunked = ((!strcmp(cmd, "PUT"))
2107                 ?  fd->wr_chunked : 0);
2108         }
2109         break;
2110     case URL_IS_DASH:
2111         assert(!(flags & O_RDWR));
2112         fd = fdDup( ((flags & O_WRONLY) ? STDOUT_FILENO : STDIN_FILENO) );
2113         if (fd) {
2114             fdSetIo(fd, ufdio);
2115             fd->rd_timeoutsecs = 600;   /* XXX W2DO? 10 mins? */
2116             fd->contentLength = fd->bytesRemain = -1;
2117         }
2118         break;
2119     case URL_IS_PATH:
2120     case URL_IS_UNKNOWN:
2121     default:
2122         fd = fdOpen(path, flags, mode);
2123         if (fd) {
2124             fdSetIo(fd, ufdio);
2125             fd->rd_timeoutsecs = 1;
2126             fd->contentLength = fd->bytesRemain = -1;
2127         }
2128         break;
2129     }
2130
2131     if (fd == NULL) return NULL;
2132     fd->urlType = urlType;
2133     if (Fileno(fd) < 0) {
2134         (void) ufdClose(fd);
2135         return NULL;
2136     }
2137 DBGIO(fd, (stderr, "==>\tufdOpen(\"%s\",%x,0%o) %s\n", url, (unsigned)flags, (unsigned)mode, fdbg(fd)));
2138     return fd;
2139 }
2140
2141 /* LCL: function typedefs */
2142 static struct FDIO_s ufdio_s = {
2143   ufdRead, ufdWrite, ufdSeek, ufdClose, XfdLink, XfdFree, XfdNew, fdFileno,
2144   ufdOpen, NULL, fdGetFp, NULL, Mkdir, Chdir, Rmdir, Rename, Unlink
2145 };
2146 FDIO_t ufdio = &ufdio_s ;
2147
2148 /* =============================================================== */
2149 /* Support for GZIP library.
2150  */
2151 #ifdef  HAVE_ZLIB_H
2152
2153 #include <zlib.h>
2154
2155 static inline void * gzdFileno(FD_t fd)
2156 {
2157     void * rc = NULL;
2158     int i;
2159
2160     FDSANE(fd);
2161     for (i = fd->nfps; i >= 0; i--) {
2162         FDSTACK_t * fps = &fd->fps[i];
2163         if (fps->io != gzdio)
2164             continue;
2165         rc = fps->fp;
2166         break;
2167     }
2168     
2169     return rc;
2170 }
2171
2172 static
2173 FD_t gzdOpen(const char * path, const char * fmode)
2174 {
2175     FD_t fd;
2176     gzFile gzfile;
2177     if ((gzfile = gzopen(path, fmode)) == NULL)
2178         return NULL;
2179     fd = fdNew("open (gzdOpen)");
2180     fdPop(fd); fdPush(fd, gzdio, gzfile, -1);
2181     
2182 DBGIO(fd, (stderr, "==>\tgzdOpen(\"%s\", \"%s\") fd %p %s\n", path, fmode, (fd ? fd : NULL), fdbg(fd)));
2183     return fdLink(fd, "gzdOpen");
2184 }
2185
2186 static FD_t gzdFdopen(void * cookie, const char *fmode)
2187 {
2188     FD_t fd = c2f(cookie);
2189     int fdno;
2190     gzFile gzfile;
2191
2192     if (fmode == NULL) return NULL;
2193     fdno = fdFileno(fd);
2194     fdSetFdno(fd, -1);          /* XXX skip the fdio close */
2195     if (fdno < 0) return NULL;
2196     gzfile = gzdopen(fdno, fmode);
2197     if (gzfile == NULL) return NULL;
2198
2199     fdPush(fd, gzdio, gzfile, fdno);            /* Push gzdio onto stack */
2200
2201     return fdLink(fd, "gzdFdopen");
2202 }
2203
2204 static int gzdFlush(FD_t fd)
2205 {
2206     gzFile gzfile;
2207     gzfile = gzdFileno(fd);
2208     if (gzfile == NULL) return -2;
2209     return gzflush(gzfile, Z_SYNC_FLUSH);       /* XXX W2DO? */
2210 }
2211
2212 /* =============================================================== */
2213 static ssize_t gzdRead(void * cookie, char * buf, size_t count)
2214 {
2215     FD_t fd = c2f(cookie);
2216     gzFile gzfile;
2217     ssize_t rc;
2218
2219     if (fd == NULL || fd->bytesRemain == 0) return 0;   /* XXX simulate EOF */
2220
2221     gzfile = gzdFileno(fd);
2222     if (gzfile == NULL) return -2;      /* XXX can't happen */
2223
2224     fdstat_enter(fd, FDSTAT_READ);
2225     rc = gzread(gzfile, buf, count);
2226 DBGIO(fd, (stderr, "==>\tgzdRead(%p,%p,%u) rc %lx %s\n", cookie, buf, (unsigned)count, (unsigned long)rc, fdbg(fd)));
2227     if (rc < 0) {
2228         int zerror = 0;
2229         fd->errcookie = gzerror(gzfile, &zerror);
2230         if (zerror == Z_ERRNO) {
2231             fd->syserrno = errno;
2232             fd->errcookie = strerror(fd->syserrno);
2233         }
2234     } else if (rc >= 0) {
2235         fdstat_exit(fd, FDSTAT_READ, rc);
2236         if (fd->ndigests && rc > 0) fdUpdateDigests(fd, (void *)buf, rc);
2237     }
2238     return rc;
2239 }
2240
2241 static ssize_t gzdWrite(void * cookie, const char * buf, size_t count)
2242 {
2243     FD_t fd = c2f(cookie);
2244     gzFile gzfile;
2245     ssize_t rc;
2246
2247     if (fd == NULL || fd->bytesRemain == 0) return 0;   /* XXX simulate EOF */
2248
2249     if (fd->ndigests && count > 0) fdUpdateDigests(fd, (void *)buf, count);
2250
2251     gzfile = gzdFileno(fd);
2252     if (gzfile == NULL) return -2;      /* XXX can't happen */
2253
2254     fdstat_enter(fd, FDSTAT_WRITE);
2255     rc = gzwrite(gzfile, (void *)buf, count);
2256 DBGIO(fd, (stderr, "==>\tgzdWrite(%p,%p,%u) rc %lx %s\n", cookie, buf, (unsigned)count, (unsigned long)rc, fdbg(fd)));
2257     if (rc < 0) {
2258         int zerror = 0;
2259         fd->errcookie = gzerror(gzfile, &zerror);
2260         if (zerror == Z_ERRNO) {
2261             fd->syserrno = errno;
2262             fd->errcookie = strerror(fd->syserrno);
2263         }
2264     } else if (rc > 0) {
2265         fdstat_exit(fd, FDSTAT_WRITE, rc);
2266     }
2267     return rc;
2268 }
2269
2270 /* XXX zlib-1.0.4 has not */
2271 static inline int gzdSeek(void * cookie, _libio_pos_t pos, int whence)
2272 {
2273 #ifdef USE_COOKIE_SEEK_POINTER
2274     _IO_off64_t p = *pos;
2275 #else
2276     off_t p = pos;
2277 #endif
2278     int rc;
2279 #if HAVE_GZSEEK
2280     FD_t fd = c2f(cookie);
2281     gzFile gzfile;
2282
2283     if (fd == NULL) return -2;
2284     assert(fd->bytesRemain == -1);      /* XXX FIXME */
2285
2286     gzfile = gzdFileno(fd);
2287     if (gzfile == NULL) return -2;      /* XXX can't happen */
2288
2289     fdstat_enter(fd, FDSTAT_SEEK);
2290     rc = gzseek(gzfile, p, whence);
2291 DBGIO(fd, (stderr, "==>\tgzdSeek(%p,%ld,%d) rc %lx %s\n", cookie, (long)p, whence, (unsigned long)rc, fdbg(fd)));
2292     if (rc < 0) {
2293         int zerror = 0;
2294         fd->errcookie = gzerror(gzfile, &zerror);
2295         if (zerror == Z_ERRNO) {
2296             fd->syserrno = errno;
2297             fd->errcookie = strerror(fd->syserrno);
2298         }
2299     } else if (rc >= 0) {
2300         fdstat_exit(fd, FDSTAT_SEEK, rc);
2301     }
2302 #else
2303     rc = -2;
2304 #endif
2305     return rc;
2306 }
2307
2308 static int gzdClose( void * cookie)
2309 {
2310     FD_t fd = c2f(cookie);
2311     gzFile gzfile;
2312     int rc;
2313
2314     gzfile = gzdFileno(fd);
2315     if (gzfile == NULL) return -2;      /* XXX can't happen */
2316
2317     fdstat_enter(fd, FDSTAT_CLOSE);
2318     rc = gzclose(gzfile);
2319
2320     /* XXX TODO: preserve fd if errors */
2321
2322     if (fd) {
2323 DBGIO(fd, (stderr, "==>\tgzdClose(%p) zerror %d %s\n", cookie, rc, fdbg(fd)));
2324         if (rc < 0) {
2325             fd->errcookie = "gzclose error";
2326             if (rc == Z_ERRNO) {
2327                 fd->syserrno = errno;
2328                 fd->errcookie = strerror(fd->syserrno);
2329             }
2330         } else if (rc >= 0) {
2331             fdstat_exit(fd, FDSTAT_CLOSE, rc);
2332         }
2333     }
2334
2335 DBGIO(fd, (stderr, "==>\tgzdClose(%p) rc %lx %s\n", cookie, (unsigned long)rc, fdbg(fd)));
2336
2337     if (_rpmio_debug || rpmIsDebug()) fdstat_print(fd, "GZDIO", stderr);
2338     if (rc == 0)
2339         fd = fdFree(fd, "open (gzdClose)");
2340     return rc;
2341 }
2342
2343 /* LCL: function typedefs */
2344 static struct FDIO_s gzdio_s = {
2345   gzdRead, gzdWrite, gzdSeek, gzdClose, XfdLink, XfdFree, XfdNew, fdFileno,
2346   NULL, gzdOpen, gzdFileno, gzdFlush,   NULL, NULL, NULL, NULL, NULL
2347 };
2348 FDIO_t gzdio = &gzdio_s ;
2349
2350 #endif  /* HAVE_ZLIB_H */
2351
2352 /* =============================================================== */
2353 /* Support for BZIP2 library.
2354  */
2355 #if HAVE_BZLIB_H
2356
2357 #include <bzlib.h>
2358
2359 #ifdef HAVE_BZ2_1_0
2360 # define bzopen  BZ2_bzopen
2361 # define bzclose BZ2_bzclose
2362 # define bzdopen BZ2_bzdopen
2363 # define bzerror BZ2_bzerror
2364 # define bzflush BZ2_bzflush
2365 # define bzread  BZ2_bzread
2366 # define bzwrite BZ2_bzwrite
2367 #endif /* HAVE_BZ2_1_0 */
2368
2369 static inline void * bzdFileno(FD_t fd)
2370 {
2371     void * rc = NULL;
2372     int i;
2373
2374     FDSANE(fd);
2375     for (i = fd->nfps; i >= 0; i--) {
2376         FDSTACK_t * fps = &fd->fps[i];
2377         if (fps->io != bzdio)
2378             continue;
2379         rc = fps->fp;
2380         break;
2381     }
2382     
2383     return rc;
2384 }
2385
2386 static FD_t bzdOpen(const char * path, const char * mode)
2387 {
2388     FD_t fd;
2389     BZFILE *bzfile;;
2390     if ((bzfile = bzopen(path, mode)) == NULL)
2391         return NULL;
2392     fd = fdNew("open (bzdOpen)");
2393     fdPop(fd); fdPush(fd, bzdio, bzfile, -1);
2394     return fdLink(fd, "bzdOpen");
2395 }
2396
2397 static FD_t bzdFdopen(void * cookie, const char * fmode)
2398 {
2399     FD_t fd = c2f(cookie);
2400     int fdno;
2401     BZFILE *bzfile;
2402
2403     if (fmode == NULL) return NULL;
2404     fdno = fdFileno(fd);
2405     fdSetFdno(fd, -1);          /* XXX skip the fdio close */
2406     if (fdno < 0) return NULL;
2407     bzfile = bzdopen(fdno, fmode);
2408     if (bzfile == NULL) return NULL;
2409
2410     fdPush(fd, bzdio, bzfile, fdno);            /* Push bzdio onto stack */
2411
2412     return fdLink(fd, "bzdFdopen");
2413 }
2414
2415 static int bzdFlush(FD_t fd)
2416 {
2417     return bzflush(bzdFileno(fd));
2418 }
2419
2420 /* =============================================================== */
2421 static ssize_t bzdRead(void * cookie, char * buf, size_t count)
2422 {
2423     FD_t fd = c2f(cookie);
2424     BZFILE *bzfile;
2425     ssize_t rc = 0;
2426
2427     if (fd->bytesRemain == 0) return 0; /* XXX simulate EOF */
2428     bzfile = bzdFileno(fd);
2429     fdstat_enter(fd, FDSTAT_READ);
2430     if (bzfile)
2431         rc = bzread(bzfile, buf, count);
2432     if (rc == -1) {
2433         int zerror = 0;
2434         if (bzfile)
2435             fd->errcookie = bzerror(bzfile, &zerror);
2436     } else if (rc >= 0) {
2437         fdstat_exit(fd, FDSTAT_READ, rc);
2438         if (fd->ndigests && rc > 0) fdUpdateDigests(fd, (void *)buf, rc);
2439     }
2440     return rc;
2441 }
2442
2443 static ssize_t bzdWrite(void * cookie, const char * buf, size_t count)
2444 {
2445     FD_t fd = c2f(cookie);
2446     BZFILE *bzfile;
2447     ssize_t rc;
2448
2449     if (fd->bytesRemain == 0) return 0; /* XXX simulate EOF */
2450
2451     if (fd->ndigests && count > 0) fdUpdateDigests(fd, (void *)buf, count);
2452
2453     bzfile = bzdFileno(fd);
2454     fdstat_enter(fd, FDSTAT_WRITE);
2455     rc = bzwrite(bzfile, (void *)buf, count);
2456     if (rc == -1) {
2457         int zerror = 0;
2458         fd->errcookie = bzerror(bzfile, &zerror);
2459     } else if (rc > 0) {
2460         fdstat_exit(fd, FDSTAT_WRITE, rc);
2461     }
2462     return rc;
2463 }
2464
2465 static inline int bzdSeek(void * cookie, _libio_pos_t pos,
2466                         int whence)
2467 {
2468     FD_t fd = c2f(cookie);
2469
2470     BZDONLY(fd);
2471     return -2;
2472 }
2473
2474 static int bzdClose( void * cookie)
2475 {
2476     FD_t fd = c2f(cookie);
2477     BZFILE *bzfile;
2478     int rc;
2479
2480     bzfile = bzdFileno(fd);
2481
2482     if (bzfile == NULL) return -2;
2483     fdstat_enter(fd, FDSTAT_CLOSE);
2484     /* FIX: check rc */
2485     bzclose(bzfile);
2486     rc = 0;     /* XXX FIXME */
2487
2488     /* XXX TODO: preserve fd if errors */
2489
2490     if (fd) {
2491         if (rc == -1) {
2492             int zerror = 0;
2493             fd->errcookie = bzerror(bzfile, &zerror);
2494         } else if (rc >= 0) {
2495             fdstat_exit(fd, FDSTAT_CLOSE, rc);
2496         }
2497     }
2498
2499 DBGIO(fd, (stderr, "==>\tbzdClose(%p) rc %lx %s\n", cookie, (unsigned long)rc, fdbg(fd)));
2500
2501     if (_rpmio_debug || rpmIsDebug()) fdstat_print(fd, "BZDIO", stderr);
2502     if (rc == 0)
2503         fd = fdFree(fd, "open (bzdClose)");
2504     return rc;
2505 }
2506
2507 /* LCL: function typedefs */
2508 static struct FDIO_s bzdio_s = {
2509   bzdRead, bzdWrite, bzdSeek, bzdClose, XfdLink, XfdFree, XfdNew, fdFileno,
2510   NULL, bzdOpen, bzdFileno, bzdFlush,   NULL, NULL, NULL, NULL, NULL
2511 };
2512 FDIO_t bzdio = &bzdio_s ;
2513
2514 #endif  /* HAVE_BZLIB_H */
2515
2516 /* =============================================================== */
2517 static const char * getFdErrstr (FD_t fd)
2518 {
2519     const char *errstr = NULL;
2520
2521 #ifdef  HAVE_ZLIB_H
2522     if (fdGetIo(fd) == gzdio) {
2523         errstr = fd->errcookie;
2524     } else
2525 #endif  /* HAVE_ZLIB_H */
2526
2527 #ifdef  HAVE_BZLIB_H
2528     if (fdGetIo(fd) == bzdio) {
2529         errstr = fd->errcookie;
2530     } else
2531 #endif  /* HAVE_BZLIB_H */
2532
2533     {
2534         errstr = (fd->syserrno ? strerror(fd->syserrno) : "");
2535     }
2536
2537     return errstr;
2538 }
2539
2540 /* =============================================================== */
2541
2542 const char *Fstrerror(FD_t fd)
2543 {
2544     if (fd == NULL)
2545         return (errno ? strerror(errno) : "");
2546     FDSANE(fd);
2547     return getFdErrstr(fd);
2548 }
2549
2550 #define FDIOVEC(_fd, _vec)      \
2551   ((fdGetIo(_fd) && fdGetIo(_fd)->_vec) ? fdGetIo(_fd)->_vec : NULL)
2552
2553 ssize_t Fread(void *buf, size_t size, size_t nmemb, FD_t fd) {
2554     fdio_read_function_t _read;
2555     int rc;
2556
2557     FDSANE(fd);
2558 DBGIO(fd, (stderr, "==> Fread(%p,%u,%u,%p) %s\n", buf, (unsigned)size, (unsigned)nmemb, (fd ? fd : NULL), fdbg(fd)));
2559
2560     if (fdGetIo(fd) == fpio) {
2561         rc = fread(buf, size, nmemb, fdGetFILE(fd));
2562         return rc;
2563     }
2564
2565     _read = FDIOVEC(fd, read);
2566
2567     rc = (_read ? (*_read) (fd, buf, size * nmemb) : -2);
2568     return rc;
2569 }
2570
2571 ssize_t Fwrite(const void *buf, size_t size, size_t nmemb, FD_t fd)
2572 {
2573     fdio_write_function_t _write;
2574     int rc;
2575
2576     FDSANE(fd);
2577 DBGIO(fd, (stderr, "==> Fwrite(%p,%u,%u,%p) %s\n", buf, (unsigned)size, (unsigned)nmemb, (fd ? fd : NULL), fdbg(fd)));
2578
2579     if (fdGetIo(fd) == fpio) {
2580         rc = fwrite(buf, size, nmemb, fdGetFILE(fd));
2581         return rc;
2582     }
2583
2584     _write = FDIOVEC(fd, write);
2585
2586     rc = (_write ? _write(fd, buf, size * nmemb) : -2);
2587     return rc;
2588 }
2589
2590 int Fseek(FD_t fd, _libio_off_t offset, int whence) {
2591     fdio_seek_function_t _seek;
2592 #ifdef USE_COOKIE_SEEK_POINTER
2593     _IO_off64_t o64 = offset;
2594     _libio_pos_t pos = &o64;
2595 #else
2596     _libio_pos_t pos = offset;
2597 #endif
2598
2599     long int rc;
2600
2601     FDSANE(fd);
2602 DBGIO(fd, (stderr, "==> Fseek(%p,%ld,%d) %s\n", fd, (long)offset, whence, fdbg(fd)));
2603
2604     if (fdGetIo(fd) == fpio) {
2605         FILE *fp;
2606
2607         fp = fdGetFILE(fd);
2608         rc = fseek(fp, offset, whence);
2609         return rc;
2610     }
2611
2612     _seek = FDIOVEC(fd, seek);
2613
2614     rc = (_seek ? _seek(fd, pos, whence) : -2);
2615     return rc;
2616 }
2617
2618 int Fclose(FD_t fd)
2619 {
2620     int rc = 0, ec = 0;
2621
2622     FDSANE(fd);
2623 DBGIO(fd, (stderr, "==> Fclose(%p) %s\n", (fd ? fd : NULL), fdbg(fd)));
2624
2625     fd = fdLink(fd, "Fclose");
2626     while (fd->nfps >= 0) {
2627         FDSTACK_t * fps = &fd->fps[fd->nfps];
2628         
2629         if (fps->io == fpio) {
2630             FILE *fp;
2631             int fpno;
2632
2633             fp = fdGetFILE(fd);
2634             fpno = fileno(fp);
2635         /* XXX persistent HTTP/1.1 returns the previously opened fp */
2636             if (fd->nfps > 0 && fpno == -1 &&
2637                 fd->fps[fd->nfps-1].io == ufdio &&
2638                 fd->fps[fd->nfps-1].fp == fp &&
2639                 (fd->fps[fd->nfps-1].fdno >= 0 || fd->req != NULL))
2640             {
2641                 int hadreqpersist = (fd->req != NULL);
2642
2643                 if (fp)
2644                     rc = fflush(fp);
2645                 fd->nfps--;
2646                 rc = ufdClose(fd);
2647                 if (fdGetFdno(fd) >= 0)
2648                     break;
2649                 if (!fd->persist)
2650                     hadreqpersist = 0;
2651                 fdSetFp(fd, NULL);
2652                 fd->nfps++;
2653                 if (fp) {
2654                     /* HACK: flimsy Keepalive wiring. */
2655                     if (hadreqpersist) {
2656                         fd->nfps--;
2657                         fdSetFp(fd, fp);
2658                         (void) fdClose(fd);
2659                         fdSetFp(fd, NULL);
2660                         fd->nfps++;
2661                         (void) fdClose(fd);
2662                     } else
2663                         rc = fclose(fp);
2664                 }
2665                 fdPop(fd);
2666                 if (noLibio)
2667                     fdSetFp(fd, NULL);
2668             } else {
2669                 if (fp)
2670                     rc = fclose(fp);
2671                 if (fpno == -1) {
2672                     fd = fdFree(fd, "fopencookie (Fclose)");
2673                     fdPop(fd);
2674                 }
2675             }
2676         } else {
2677             fdio_close_function_t _close = FDIOVEC(fd, close);
2678             rc = _close(fd);
2679         }
2680         if (fd->nfps == 0)
2681             break;
2682         if (ec == 0 && rc)
2683             ec = rc;
2684         fdPop(fd);
2685     }
2686     fd = fdFree(fd, "Fclose");
2687     return ec;
2688 }
2689
2690 /**
2691  * Convert stdio fmode to open(2) mode, filtering out zlib/bzlib flags.
2692  *      returns stdio[0] = NUL on error.
2693  *
2694  * - gzopen:    [0-9] is compession level
2695  * - gzopen:    'f' is filtered (Z_FILTERED)
2696  * - gzopen:    'h' is Huffman encoding (Z_HUFFMAN_ONLY)
2697  * - bzopen:    [1-9] is block size (modulo 100K)
2698  * - bzopen:    's' is smallmode
2699  * - HACK:      '.' terminates, rest is type of I/O
2700  */
2701 static inline void cvtfmode (const char *m,
2702                                 char *stdio, size_t nstdio,
2703                                 char *other, size_t nother,
2704                                 const char **end, int * f)
2705 {
2706     int flags = 0;
2707     char c;
2708
2709     switch (*m) {
2710     case 'a':
2711         flags |= O_WRONLY | O_CREAT | O_APPEND;
2712         if (--nstdio > 0) *stdio++ = *m;
2713         break;
2714     case 'w':
2715         flags |= O_WRONLY | O_CREAT | O_TRUNC;
2716         if (--nstdio > 0) *stdio++ = *m;
2717         break;
2718     case 'r':
2719         flags |= O_RDONLY;
2720         if (--nstdio > 0) *stdio++ = *m;
2721         break;
2722     default:
2723         *stdio = '\0';
2724         return;
2725         break;
2726     }
2727     m++;
2728
2729     while ((c = *m++) != '\0') {
2730         switch (c) {
2731         case '.':
2732             break;
2733         case '+':
2734             flags &= ~(O_RDONLY|O_WRONLY);
2735             flags |= O_RDWR;
2736             if (--nstdio > 0) *stdio++ = c;
2737             continue;
2738             break;
2739         case 'b':
2740             if (--nstdio > 0) *stdio++ = c;
2741             continue;
2742             break;
2743         case 'x':
2744             flags |= O_EXCL;
2745             if (--nstdio > 0) *stdio++ = c;
2746             continue;
2747             break;
2748         default:
2749             if (--nother > 0) *other++ = c;
2750             continue;
2751             break;
2752         }
2753         break;
2754     }
2755
2756     *stdio = *other = '\0';
2757     if (end != NULL)
2758         *end = (*m != '\0' ? m : NULL);
2759     if (f != NULL)
2760         *f = flags;
2761 }
2762
2763 #if _USE_LIBIO
2764 #if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ == 0
2765 /* XXX retrofit glibc-2.1.x typedef on glibc-2.0.x systems */
2766 typedef _IO_cookie_io_functions_t cookie_io_functions_t;
2767 #endif
2768 #endif
2769
2770 FD_t Fdopen(FD_t ofd, const char *fmode)
2771 {
2772     char stdio[20], other[20], zstdio[20];
2773     const char *end = NULL;
2774     FDIO_t iof = NULL;
2775     FD_t fd = ofd;
2776
2777 if (_rpmio_debug)
2778 fprintf(stderr, "*** Fdopen(%p,%s) %s\n", fd, fmode, fdbg(fd));
2779     FDSANE(fd);
2780
2781     if (fmode == NULL)
2782         return NULL;
2783
2784     cvtfmode(fmode, stdio, sizeof(stdio), other, sizeof(other), &end, NULL);
2785     if (stdio[0] == '\0')
2786         return NULL;
2787     zstdio[0] = '\0';
2788     strncat(zstdio, stdio, sizeof(zstdio) - strlen(zstdio));
2789     strncat(zstdio, other, sizeof(zstdio) - strlen(zstdio));
2790
2791     if (end == NULL && other[0] == '\0')
2792         return fd;
2793
2794     if (end && *end) {
2795         if (!strcmp(end, "fdio")) {
2796             iof = fdio;
2797         } else if (!strcmp(end, "gzdio")) {
2798             iof = gzdio;
2799             fd = gzdFdopen(fd, zstdio);
2800 #if HAVE_BZLIB_H
2801         } else if (!strcmp(end, "bzdio")) {
2802             iof = bzdio;
2803             fd = bzdFdopen(fd, zstdio);
2804 #endif
2805         } else if (!strcmp(end, "ufdio")) {
2806             iof = ufdio;
2807         } else if (!strcmp(end, "fpio")) {
2808             iof = fpio;
2809             if (noLibio) {
2810                 int fdno = Fileno(fd);
2811                 FILE * fp = fdopen(fdno, stdio);
2812 if (_rpmio_debug)
2813 fprintf(stderr, "*** Fdopen fpio fp %p\n", (void *)fp);
2814                 if (fp == NULL)
2815                     return NULL;
2816                 /* XXX gzdio/bzdio use fp for private data */
2817                 if (fdGetFp(fd) == NULL)
2818                     fdSetFp(fd, fp);
2819                 fdPush(fd, fpio, fp, fdno);     /* Push fpio onto stack */
2820             }
2821         }
2822     } else if (other[0] != '\0') {
2823         for (end = other; *end && strchr("0123456789fh", *end); end++)
2824             {};
2825         if (*end == '\0') {
2826             iof = gzdio;
2827             fd = gzdFdopen(fd, zstdio);
2828         }
2829     }
2830     if (iof == NULL)
2831         return fd;
2832
2833     if (!noLibio) {
2834         FILE * fp = NULL;
2835
2836 #if _USE_LIBIO
2837         {   cookie_io_functions_t ciof;
2838             ciof.read = iof->read;
2839             ciof.write = iof->write;
2840             ciof.seek = iof->seek;
2841             ciof.close = iof->close;
2842             fp = fopencookie(fd, stdio, ciof);
2843 DBGIO(fd, (stderr, "==> fopencookie(%p,\"%s\",*%p) returns fp %p\n", fd, stdio, iof, fp));
2844         }
2845 #endif
2846
2847         if (fp) {
2848             /* XXX gzdio/bzdio use fp for private data */
2849             if (fdGetFp(fd) == NULL)
2850                 fdSetFp(fd, fp);
2851             fdPush(fd, fpio, fp, fileno(fp));   /* Push fpio onto stack */
2852             fd = fdLink(fd, "fopencookie");
2853         }
2854     }
2855
2856 DBGIO(fd, (stderr, "==> Fdopen(%p,\"%s\") returns fd %p %s\n", ofd, fmode, (fd ? fd : NULL), fdbg(fd)));
2857     return fd;
2858 }
2859
2860 FD_t Fopen(const char *path, const char *fmode)
2861 {
2862     char stdio[20], other[20];
2863     const char *end = NULL;
2864     mode_t perms = 0666;
2865     int flags = 0;
2866     FD_t fd;
2867
2868     if (path == NULL || fmode == NULL)
2869         return NULL;
2870
2871     stdio[0] = '\0';
2872     cvtfmode(fmode, stdio, sizeof(stdio), other, sizeof(other), &end, &flags);
2873     if (stdio[0] == '\0')
2874         return NULL;
2875
2876     if (end == NULL || !strcmp(end, "fdio")) {
2877 if (_rpmio_debug)
2878 fprintf(stderr, "*** Fopen fdio path %s fmode %s\n", path, fmode);
2879         fd = fdOpen(path, flags, perms);
2880         if (fdFileno(fd) < 0) {
2881             if (fd) (void) fdClose(fd);
2882             return NULL;
2883         }
2884     } else {
2885         FILE *fp;
2886         int fdno;
2887         int isHTTP = 0;
2888
2889         /* XXX gzdio and bzdio here too */
2890
2891         switch (urlIsURL(path)) {
2892         case URL_IS_HTTPS:
2893         case URL_IS_HTTP:
2894         case URL_IS_HKP:
2895             isHTTP = 1;
2896         case URL_IS_PATH:
2897         case URL_IS_DASH:
2898         case URL_IS_FTP:
2899         case URL_IS_UNKNOWN:
2900 if (_rpmio_debug)
2901 fprintf(stderr, "*** Fopen ufdio path %s fmode %s\n", path, fmode);
2902             fd = ufdOpen(path, flags, perms);
2903             if (fd == NULL || !(fdFileno(fd) >= 0 || fd->req != NULL))
2904                 return fd;
2905             break;
2906         default:
2907 if (_rpmio_debug)
2908 fprintf(stderr, "*** Fopen WTFO path %s fmode %s\n", path, fmode);
2909             return NULL;
2910             break;
2911         }
2912
2913         /* XXX persistent HTTP/1.1 returns the previously opened fp */
2914         if (isHTTP && ((fp = fdGetFp(fd)) != NULL) && ((fdno = fdGetFdno(fd)) >= 0 || fd->req != NULL))
2915         {
2916             fdPush(fd, fpio, fp, fileno(fp));   /* Push fpio onto stack */
2917             return fd;
2918         }
2919     }
2920
2921     if (fd)
2922         fd = Fdopen(fd, fmode);
2923     return fd;
2924 }
2925
2926 int Fflush(FD_t fd)
2927 {
2928     void * vh;
2929     if (fd == NULL) return -1;
2930     if (fdGetIo(fd) == fpio)
2931         return fflush(fdGetFILE(fd));
2932
2933     vh = fdGetFp(fd);
2934     if (vh && fdGetIo(fd) == gzdio)
2935         return gzdFlush(vh);
2936 #if HAVE_BZLIB_H
2937     if (vh && fdGetIo(fd) == bzdio)
2938         return bzdFlush(vh);
2939 #endif
2940
2941     return 0;
2942 }
2943
2944 int Ferror(FD_t fd)
2945 {
2946     int i, rc = 0;
2947
2948     if (fd == NULL) return -1;
2949     if (fd->req != NULL) {
2950         /* HACK: flimsy wiring for neon errors. */
2951         rc = (fd->syserrno  || fd->errcookie != NULL) ? -1 : 0;
2952     } else
2953     for (i = fd->nfps; rc == 0 && i >= 0; i--) {
2954         FDSTACK_t * fps = &fd->fps[i];
2955         int ec;
2956         
2957         if (fps->io == fpio) {
2958             ec = ferror(fdGetFILE(fd));
2959         } else if (fps->io == gzdio) {
2960             ec = (fd->syserrno || fd->errcookie != NULL) ? -1 : 0;
2961             i--;        /* XXX fdio under gzdio always has fdno == -1 */
2962 #if HAVE_BZLIB_H
2963         } else if (fps->io == bzdio) {
2964             ec = (fd->syserrno  || fd->errcookie != NULL) ? -1 : 0;
2965             i--;        /* XXX fdio under bzdio always has fdno == -1 */
2966 #endif
2967         } else {
2968         /* XXX need to check ufdio/gzdio/bzdio/fdio errors correctly. */
2969             ec = (fdFileno(fd) < 0 ? -1 : 0);
2970         }
2971
2972         if (rc == 0 && ec)
2973             rc = ec;
2974     }
2975 DBGIO(fd, (stderr, "==> Ferror(%p) rc %d %s\n", fd, rc, fdbg(fd)));
2976     return rc;
2977 }
2978
2979 int Fileno(FD_t fd)
2980 {
2981     int i, rc = -1;
2982
2983     if (fd == NULL) return -1;
2984     if (fd->req != NULL)
2985         rc = 123456789; /* HACK: https has no steenkin fileno. */
2986     else
2987     for (i = fd->nfps ; rc == -1 && i >= 0; i--) {
2988         rc = fd->fps[i].fdno;
2989     }
2990     
2991 DBGIO(fd, (stderr, "==> Fileno(%p) rc %d %s\n", (fd ? fd : NULL), rc, fdbg(fd)));
2992     return rc;
2993 }
2994
2995 /* XXX this is naive */
2996 int Fcntl(FD_t fd, int op, void *lip)
2997 {
2998     return fcntl(Fileno(fd), op, lip);
2999 }
3000
3001 /* =============================================================== */
3002 /* Helper routines that may be generally useful.
3003  */
3004 int rpmioMkpath(const char * path, mode_t mode, uid_t uid, gid_t gid)
3005 {
3006     char * d, * de;
3007     int created = 0;
3008     int rc;
3009
3010     if (path == NULL)
3011         return -1;
3012     d = alloca(strlen(path)+2);
3013     de = stpcpy(d, path);
3014     de[1] = '\0';
3015     for (de = d; *de != '\0'; de++) {
3016         struct stat st;
3017         char savec;
3018
3019         while (*de && *de != '/') de++;
3020         savec = de[1];
3021         de[1] = '\0';
3022
3023         rc = Stat(d, &st);
3024         if (rc) {
3025             switch(errno) {
3026             default:
3027                 return errno;
3028                 break;
3029             case ENOENT:
3030                 break;
3031             }
3032             rc = Mkdir(d, mode);
3033             if (rc)
3034                 return errno;
3035             created = 1;
3036             if (!(uid == (uid_t) -1 && gid == (gid_t) -1)) {
3037                 rc = chown(d, uid, gid);
3038                 if (rc)
3039                     return errno;
3040             }
3041         } else if (!S_ISDIR(st.st_mode)) {
3042             return ENOTDIR;
3043         }
3044         de[1] = savec;
3045     }
3046     rc = 0;
3047     if (created)
3048         rpmMessage(RPMMESS_DEBUG, "created directory(s) %s mode 0%o\n",
3049                         path, mode);
3050     return rc;
3051 }
3052
3053 int rpmioSlurp(const char * fn, byte ** bp, ssize_t * blenp)
3054 {
3055     static ssize_t blenmax = (32 * BUFSIZ);
3056     ssize_t blen = 0;
3057     byte * b = NULL;
3058     ssize_t size;
3059     FD_t fd;
3060     int rc = 0;
3061
3062     fd = Fopen(fn, "r.ufdio");
3063     if (fd == NULL || Ferror(fd)) {
3064         rc = 2;
3065         goto exit;
3066     }
3067
3068     size = fdSize(fd);
3069     blen = (size >= 0 ? size : blenmax);
3070     if (blen) {
3071         int nb;
3072         b = xmalloc(blen+1);
3073         b[0] = '\0';
3074         nb = Fread(b, sizeof(*b), blen, fd);
3075         if (Ferror(fd) || (size > 0 && nb != blen)) {
3076             rc = 1;
3077             goto exit;
3078         }
3079         if (blen == blenmax && nb < blen) {
3080             blen = nb;
3081             b = xrealloc(b, blen+1);
3082         }
3083         b[blen] = '\0';
3084     }
3085
3086 exit:
3087     if (fd) (void) Fclose(fd);
3088         
3089     if (rc) {
3090         if (b) free(b);
3091         b = NULL;
3092         blen = 0;
3093     }
3094
3095     if (bp) *bp = b;
3096     else if (b) free(b);
3097
3098     if (blenp) *blenp = blen;
3099
3100     return rc;
3101 }
3102
3103 /* LCL: function typedefs */
3104 static struct FDIO_s fpio_s = {
3105   ufdRead, ufdWrite, fdSeek, ufdClose, XfdLink, XfdFree, XfdNew, fdFileno,
3106   ufdOpen, NULL, fdGetFp, NULL, Mkdir, Chdir, Rmdir, Rename, Unlink
3107 };
3108 FDIO_t fpio = &fpio_s ;