update Sun Jun 13 18:37:00 PDT 2010
[pkgsrc.git] / net / libfetch / files / common.c
1 /*      $NetBSD: common.c,v 1.27 2010/06/13 21:38:09 joerg Exp $        */
2 /*-
3  * Copyright (c) 1998-2004 Dag-Erling Coïdan Smørgrav
4  * Copyright (c) 2008, 2010 Joerg Sonnenberger <joerg@NetBSD.org>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer
12  *    in this position and unchanged.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. The name of the author may not be used to endorse or promote products
17  *    derived from this software without specific prior written permission
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  *
30  * $FreeBSD: common.c,v 1.53 2007/12/19 00:26:36 des Exp $
31  */
32
33 #if HAVE_CONFIG_H
34 #include "config.h"
35 #endif
36 #ifndef NETBSD
37 #include <nbcompat.h>
38 #endif
39
40 #include <sys/types.h>
41 #include <sys/socket.h>
42 #include <sys/time.h>
43 #include <sys/uio.h>
44
45 #include <netinet/in.h>
46 #include <arpa/inet.h>
47
48 #include <ctype.h>
49 #include <errno.h>
50 #if defined(HAVE_INTTYPES_H) || defined(NETBSD)
51 #include <inttypes.h>
52 #endif
53 #ifndef NETBSD
54 #include <nbcompat/netdb.h>
55 #else
56 #include <netdb.h>
57 #endif
58 #include <pwd.h>
59 #include <stdarg.h>
60 #include <stdlib.h>
61 #include <stdio.h>
62 #include <string.h>
63 #include <unistd.h>
64
65 #ifndef MSG_NOSIGNAL
66 #include <signal.h>
67 #endif
68
69 #include "fetch.h"
70 #include "common.h"
71
72 /*** Local data **************************************************************/
73
74 /*
75  * Error messages for resolver errors
76  */
77 static struct fetcherr netdb_errlist[] = {
78 #ifdef EAI_NODATA
79         { EAI_NODATA,   FETCH_RESOLV,   "Host not found" },
80 #endif
81         { EAI_AGAIN,    FETCH_TEMP,     "Transient resolver failure" },
82         { EAI_FAIL,     FETCH_RESOLV,   "Non-recoverable resolver failure" },
83         { EAI_NONAME,   FETCH_RESOLV,   "No address record" },
84         { -1,           FETCH_UNKNOWN,  "Unknown resolver error" }
85 };
86
87 /*** Error-reporting functions ***********************************************/
88
89 /*
90  * Map error code to string
91  */
92 static struct fetcherr *
93 fetch_finderr(struct fetcherr *p, int e)
94 {
95         while (p->num != -1 && p->num != e)
96                 p++;
97         return (p);
98 }
99
100 /*
101  * Set error code
102  */
103 void
104 fetch_seterr(struct fetcherr *p, int e)
105 {
106         p = fetch_finderr(p, e);
107         fetchLastErrCode = p->cat;
108         snprintf(fetchLastErrString, MAXERRSTRING, "%s", p->string);
109 }
110
111 /*
112  * Set error code according to errno
113  */
114 void
115 fetch_syserr(void)
116 {
117         switch (errno) {
118         case 0:
119                 fetchLastErrCode = FETCH_OK;
120                 break;
121         case EPERM:
122         case EACCES:
123         case EROFS:
124 #ifdef EAUTH
125         case EAUTH:
126 #endif
127 #ifdef ENEEDAUTH
128         case ENEEDAUTH:
129 #endif
130                 fetchLastErrCode = FETCH_AUTH;
131                 break;
132         case ENOENT:
133         case EISDIR: /* XXX */
134                 fetchLastErrCode = FETCH_UNAVAIL;
135                 break;
136         case ENOMEM:
137                 fetchLastErrCode = FETCH_MEMORY;
138                 break;
139         case EBUSY:
140         case EAGAIN:
141                 fetchLastErrCode = FETCH_TEMP;
142                 break;
143         case EEXIST:
144                 fetchLastErrCode = FETCH_EXISTS;
145                 break;
146         case ENOSPC:
147                 fetchLastErrCode = FETCH_FULL;
148                 break;
149         case EADDRINUSE:
150         case EADDRNOTAVAIL:
151         case ENETDOWN:
152         case ENETUNREACH:
153         case ENETRESET:
154         case EHOSTUNREACH:
155                 fetchLastErrCode = FETCH_NETWORK;
156                 break;
157         case ECONNABORTED:
158         case ECONNRESET:
159                 fetchLastErrCode = FETCH_ABORT;
160                 break;
161         case ETIMEDOUT:
162                 fetchLastErrCode = FETCH_TIMEOUT;
163                 break;
164         case ECONNREFUSED:
165         case EHOSTDOWN:
166                 fetchLastErrCode = FETCH_DOWN;
167                 break;
168 default:
169                 fetchLastErrCode = FETCH_UNKNOWN;
170         }
171         snprintf(fetchLastErrString, MAXERRSTRING, "%s", strerror(errno));
172 }
173
174
175 /*
176  * Emit status message
177  */
178 void
179 fetch_info(const char *fmt, ...)
180 {
181         va_list ap;
182
183         va_start(ap, fmt);
184         vfprintf(stderr, fmt, ap);
185         va_end(ap);
186         fputc('\n', stderr);
187 }
188
189
190 /*** Network-related utility functions ***************************************/
191
192 /*
193  * Return the default port for a scheme
194  */
195 int
196 fetch_default_port(const char *scheme)
197 {
198         struct servent *se;
199
200         if ((se = getservbyname(scheme, "tcp")) != NULL)
201                 return (ntohs(se->s_port));
202         if (strcasecmp(scheme, SCHEME_FTP) == 0)
203                 return (FTP_DEFAULT_PORT);
204         if (strcasecmp(scheme, SCHEME_HTTP) == 0)
205                 return (HTTP_DEFAULT_PORT);
206         return (0);
207 }
208
209 /*
210  * Return the default proxy port for a scheme
211  */
212 int
213 fetch_default_proxy_port(const char *scheme)
214 {
215         if (strcasecmp(scheme, SCHEME_FTP) == 0)
216                 return (FTP_DEFAULT_PROXY_PORT);
217         if (strcasecmp(scheme, SCHEME_HTTP) == 0)
218                 return (HTTP_DEFAULT_PROXY_PORT);
219         return (0);
220 }
221
222
223 /*
224  * Create a connection for an existing descriptor.
225  */
226 conn_t *
227 fetch_reopen(int sd)
228 {
229         conn_t *conn;
230
231         /* allocate and fill connection structure */
232         if ((conn = calloc(1, sizeof(*conn))) == NULL)
233                 return (NULL);
234         conn->ftp_home = NULL;
235         conn->cache_url = NULL;
236         conn->next_buf = NULL;
237         conn->next_len = 0;
238         conn->sd = sd;
239         return (conn);
240 }
241
242
243 /*
244  * Bind a socket to a specific local address
245  */
246 int
247 fetch_bind(int sd, int af, const char *addr)
248 {
249         struct addrinfo hints, *res, *res0;
250
251         memset(&hints, 0, sizeof(hints));
252         hints.ai_family = af;
253         hints.ai_socktype = SOCK_STREAM;
254         hints.ai_protocol = 0;
255         if (getaddrinfo(addr, NULL, &hints, &res0))
256                 return (-1);
257         for (res = res0; res; res = res->ai_next) {
258                 if (bind(sd, res->ai_addr, res->ai_addrlen) == 0)
259                         return (0);
260         }
261         return (-1);
262 }
263
264
265 /*
266  * Establish a TCP connection to the specified port on the specified host.
267  */
268 conn_t *
269 fetch_connect(struct url *url, int af, int verbose)
270 {
271         conn_t *conn;
272         char pbuf[10];
273         const char *bindaddr;
274         struct addrinfo hints, *res, *res0;
275         int sd, error;
276
277         if (verbose)
278                 fetch_info("looking up %s", url->host);
279
280         /* look up host name and set up socket address structure */
281         snprintf(pbuf, sizeof(pbuf), "%d", url->port);
282         memset(&hints, 0, sizeof(hints));
283         hints.ai_family = af;
284         hints.ai_socktype = SOCK_STREAM;
285         hints.ai_protocol = 0;
286         if ((error = getaddrinfo(url->host, pbuf, &hints, &res0)) != 0) {
287                 netdb_seterr(error);
288                 return (NULL);
289         }
290         bindaddr = getenv("FETCH_BIND_ADDRESS");
291
292         if (verbose)
293                 fetch_info("connecting to %s:%d", url->host, url->port);
294
295         /* try to connect */
296         for (sd = -1, res = res0; res; sd = -1, res = res->ai_next) {
297                 if ((sd = socket(res->ai_family, res->ai_socktype,
298                          res->ai_protocol)) == -1)
299                         continue;
300                 if (bindaddr != NULL && *bindaddr != '\0' &&
301                     fetch_bind(sd, res->ai_family, bindaddr) != 0) {
302                         fetch_info("failed to bind to '%s'", bindaddr);
303                         close(sd);
304                         continue;
305                 }
306                 if (connect(sd, res->ai_addr, res->ai_addrlen) == 0)
307                         break;
308                 close(sd);
309         }
310         freeaddrinfo(res0);
311         if (sd == -1) {
312                 fetch_syserr();
313                 return (NULL);
314         }
315
316         if ((conn = fetch_reopen(sd)) == NULL) {
317                 fetch_syserr();
318                 close(sd);
319                 return (NULL);
320         }
321         conn->cache_url = fetchCopyURL(url);
322         conn->cache_af = af;
323         return (conn);
324 }
325
326 static conn_t *connection_cache;
327 static int cache_global_limit = 0;
328 static int cache_per_host_limit = 0;
329
330 /*
331  * Initialise cache with the given limits.
332  */
333 void
334 fetchConnectionCacheInit(int global_limit, int per_host_limit)
335 {
336
337         if (global_limit < 0)
338                 cache_global_limit = INT_MAX;
339         else if (per_host_limit > global_limit)
340                 cache_global_limit = per_host_limit;
341         else
342                 cache_global_limit = global_limit;
343         if (per_host_limit < 0)
344                 cache_per_host_limit = INT_MAX;
345         else
346                 cache_per_host_limit = per_host_limit;
347 }
348
349 /*
350  * Flush cache and free all associated resources.
351  */
352 void
353 fetchConnectionCacheClose(void)
354 {
355         conn_t *conn;
356
357         while ((conn = connection_cache) != NULL) {
358                 connection_cache = conn->next_cached;
359                 (*conn->cache_close)(conn);
360         }
361 }
362
363 /*
364  * Check connection cache for an existing entry matching
365  * protocol/host/port/user/password/family.
366  */
367 conn_t *
368 fetch_cache_get(const struct url *url, int af)
369 {
370         conn_t *conn, *last_conn = NULL;
371
372         for (conn = connection_cache; conn; conn = conn->next_cached) {
373                 if (conn->cache_url->port == url->port &&
374                     strcmp(conn->cache_url->scheme, url->scheme) == 0 &&
375                     strcmp(conn->cache_url->host, url->host) == 0 &&
376                     strcmp(conn->cache_url->user, url->user) == 0 &&
377                     strcmp(conn->cache_url->pwd, url->pwd) == 0 &&
378                     (conn->cache_af == AF_UNSPEC || af == AF_UNSPEC ||
379                      conn->cache_af == af)) {
380                         if (last_conn != NULL)
381                                 last_conn->next_cached = conn->next_cached;
382                         else
383                                 connection_cache = conn->next_cached;
384                         return conn;
385                 }
386         }
387
388         return NULL;
389 }
390
391 /*
392  * Put the connection back into the cache for reuse.
393  * If the connection is freed due to LRU or if the cache
394  * is explicitly closed, the given callback is called.
395  */
396 void
397 fetch_cache_put(conn_t *conn, int (*closecb)(conn_t *))
398 {
399         conn_t *iter, *last;
400         int global_count, host_count;
401
402         if (conn->cache_url == NULL || cache_global_limit == 0) {
403                 (*closecb)(conn);
404                 return;
405         }
406
407         global_count = host_count = 0;
408         last = NULL;
409         for (iter = connection_cache; iter;
410             last = iter, iter = iter->next_cached) {
411                 ++global_count;
412                 if (strcmp(conn->cache_url->host, iter->cache_url->host) == 0)
413                         ++host_count;
414                 if (global_count < cache_global_limit &&
415                     host_count < cache_per_host_limit)
416                         continue;
417                 --global_count;
418                 if (last != NULL)
419                         last->next_cached = iter->next_cached;
420                 else
421                         connection_cache = iter->next_cached;
422                 (*iter->cache_close)(iter);
423         }
424
425         conn->cache_close = closecb;
426         conn->next_cached = connection_cache;
427         connection_cache = conn;
428 }
429
430 /*
431  * Enable SSL on a connection.
432  */
433 int
434 fetch_ssl(conn_t *conn, int verbose)
435 {
436
437 #ifdef WITH_SSL
438         /* Init the SSL library and context */
439         if (!SSL_library_init()){
440                 fprintf(stderr, "SSL library init failed\n");
441                 return (-1);
442         }
443
444         SSL_load_error_strings();
445
446         conn->ssl_meth = SSLv23_client_method();
447         conn->ssl_ctx = SSL_CTX_new(conn->ssl_meth);
448         SSL_CTX_set_mode(conn->ssl_ctx, SSL_MODE_AUTO_RETRY);
449
450         conn->ssl = SSL_new(conn->ssl_ctx);
451         if (conn->ssl == NULL){
452                 fprintf(stderr, "SSL context creation failed\n");
453                 return (-1);
454         }
455         SSL_set_fd(conn->ssl, conn->sd);
456         if (SSL_connect(conn->ssl) == -1){
457                 ERR_print_errors_fp(stderr);
458                 return (-1);
459         }
460
461         if (verbose) {
462                 X509_NAME *name;
463                 char *str;
464
465                 fprintf(stderr, "SSL connection established using %s\n",
466                     SSL_get_cipher(conn->ssl));
467                 conn->ssl_cert = SSL_get_peer_certificate(conn->ssl);
468                 name = X509_get_subject_name(conn->ssl_cert);
469                 str = X509_NAME_oneline(name, 0, 0);
470                 printf("Certificate subject: %s\n", str);
471                 free(str);
472                 name = X509_get_issuer_name(conn->ssl_cert);
473                 str = X509_NAME_oneline(name, 0, 0);
474                 printf("Certificate issuer: %s\n", str);
475                 free(str);
476         }
477
478         return (0);
479 #else
480         (void)conn;
481         (void)verbose;
482         fprintf(stderr, "SSL support disabled\n");
483         return (-1);
484 #endif
485 }
486
487
488 /*
489  * Read a character from a connection w/ timeout
490  */
491 ssize_t
492 fetch_read(conn_t *conn, char *buf, size_t len)
493 {
494         struct timeval now, timeout, waittv;
495         fd_set readfds;
496         ssize_t rlen;
497         int r;
498
499         if (len == 0)
500                 return 0;
501
502         if (conn->next_len != 0) {
503                 if (conn->next_len < len)
504                         len = conn->next_len;
505                 memmove(buf, conn->next_buf, len);
506                 conn->next_len -= len;
507                 conn->next_buf += len;
508                 return len;
509         }
510
511         if (fetchTimeout) {
512                 FD_ZERO(&readfds);
513                 gettimeofday(&timeout, NULL);
514                 timeout.tv_sec += fetchTimeout;
515         }
516
517         for (;;) {
518                 while (fetchTimeout && !FD_ISSET(conn->sd, &readfds)) {
519                         FD_SET(conn->sd, &readfds);
520                         gettimeofday(&now, NULL);
521                         waittv.tv_sec = timeout.tv_sec - now.tv_sec;
522                         waittv.tv_usec = timeout.tv_usec - now.tv_usec;
523                         if (waittv.tv_usec < 0) {
524                                 waittv.tv_usec += 1000000;
525                                 waittv.tv_sec--;
526                         }
527                         if (waittv.tv_sec < 0) {
528                                 errno = ETIMEDOUT;
529                                 fetch_syserr();
530                                 return (-1);
531                         }
532                         errno = 0;
533                         r = select(conn->sd + 1, &readfds, NULL, NULL, &waittv);
534                         if (r == -1) {
535                                 if (errno == EINTR && fetchRestartCalls)
536                                         continue;
537                                 fetch_syserr();
538                                 return (-1);
539                         }
540                 }
541 #ifdef WITH_SSL
542                 if (conn->ssl != NULL)
543                         rlen = SSL_read(conn->ssl, buf, len);
544                 else
545 #endif
546                         rlen = read(conn->sd, buf, len);
547                 if (rlen >= 0)
548                         break;
549         
550                 if (errno != EINTR || !fetchRestartCalls)
551                         return (-1);
552         }
553         return (rlen);
554 }
555
556
557 /*
558  * Read a line of text from a connection w/ timeout
559  */
560 #define MIN_BUF_SIZE 1024
561
562 int
563 fetch_getln(conn_t *conn)
564 {
565         char *tmp, *next;
566         size_t tmpsize;
567         ssize_t len;
568
569         if (conn->buf == NULL) {
570                 if ((conn->buf = malloc(MIN_BUF_SIZE)) == NULL) {
571                         errno = ENOMEM;
572                         return (-1);
573                 }
574                 conn->bufsize = MIN_BUF_SIZE;
575         }
576
577         conn->buflen = 0;
578         next = NULL;
579
580         do {
581                 /*
582                  * conn->bufsize != conn->buflen at this point,
583                  * so the buffer can be NUL-terminated below for
584                  * the case of len == 0.
585                  */
586                 len = fetch_read(conn, conn->buf + conn->buflen,
587                     conn->bufsize - conn->buflen);
588                 if (len == -1)
589                         return (-1);
590                 if (len == 0)
591                         break;
592                 next = memchr(conn->buf + conn->buflen, '\n', len);
593                 conn->buflen += len;
594                 if (conn->buflen == conn->bufsize && next == NULL) {
595                         tmp = conn->buf;
596                         tmpsize = conn->bufsize * 2;
597                         if (tmpsize < conn->bufsize) {
598                                 errno = ENOMEM;
599                                 return (-1);
600                         }
601                         if ((tmp = realloc(tmp, tmpsize)) == NULL) {
602                                 errno = ENOMEM;
603                                 return (-1);
604                         }
605                         conn->buf = tmp;
606                         conn->bufsize = tmpsize;
607                 }
608         } while (next == NULL);
609
610         if (next != NULL) {
611                 *next = '\0';
612                 conn->next_buf = next + 1;
613                 conn->next_len = conn->buflen - (conn->next_buf - conn->buf);
614                 conn->buflen = next - conn->buf;
615         } else {
616                 conn->buf[conn->buflen] = '\0';
617                 conn->next_len = 0;
618         }
619         return (0);
620 }
621
622 /*
623  * Write a vector to a connection w/ timeout
624  * Note: can modify the iovec.
625  */
626 ssize_t
627 fetch_write(conn_t *conn, const void *buf, size_t len)
628 {
629         struct timeval now, timeout, waittv;
630         fd_set writefds;
631         ssize_t wlen, total;
632         int r;
633 #ifndef MSG_NOSIGNAL
634         static int killed_sigpipe;
635 #endif
636
637 #ifndef MSG_NOSIGNAL
638         if (!killed_sigpipe) {
639                 signal(SIGPIPE, SIG_IGN);
640                 killed_sigpipe = 1;
641         }
642 #endif
643
644
645         if (fetchTimeout) {
646                 FD_ZERO(&writefds);
647                 gettimeofday(&timeout, NULL);
648                 timeout.tv_sec += fetchTimeout;
649         }
650
651         total = 0;
652         while (len) {
653                 while (fetchTimeout && !FD_ISSET(conn->sd, &writefds)) {
654                         FD_SET(conn->sd, &writefds);
655                         gettimeofday(&now, NULL);
656                         waittv.tv_sec = timeout.tv_sec - now.tv_sec;
657                         waittv.tv_usec = timeout.tv_usec - now.tv_usec;
658                         if (waittv.tv_usec < 0) {
659                                 waittv.tv_usec += 1000000;
660                                 waittv.tv_sec--;
661                         }
662                         if (waittv.tv_sec < 0) {
663                                 errno = ETIMEDOUT;
664                                 fetch_syserr();
665                                 return (-1);
666                         }
667                         errno = 0;
668                         r = select(conn->sd + 1, NULL, &writefds, NULL, &waittv);
669                         if (r == -1) {
670                                 if (errno == EINTR && fetchRestartCalls)
671                                         continue;
672                                 return (-1);
673                         }
674                 }
675                 errno = 0;
676 #ifdef WITH_SSL
677                 if (conn->ssl != NULL)
678                         wlen = SSL_write(conn->ssl, buf, len);
679                 else
680 #endif
681 #ifndef MSG_NOSIGNAL
682                         wlen = send(conn->sd, buf, len, 0);
683 #else
684                         wlen = send(conn->sd, buf, len, MSG_NOSIGNAL);
685 #endif
686                 if (wlen == 0) {
687                         /* we consider a short write a failure */
688                         errno = EPIPE;
689                         fetch_syserr();
690                         return (-1);
691                 }
692                 if (wlen < 0) {
693                         if (errno == EINTR && fetchRestartCalls)
694                                 continue;
695                         return (-1);
696                 }
697                 total += wlen;
698                 buf = (const char *)buf + wlen;
699                 len -= wlen;
700         }
701         return (total);
702 }
703
704
705 /*
706  * Close connection
707  */
708 int
709 fetch_close(conn_t *conn)
710 {
711         int ret;
712
713         ret = close(conn->sd);
714         if (conn->cache_url)
715                 fetchFreeURL(conn->cache_url);
716         free(conn->ftp_home);
717         free(conn->buf);
718         free(conn);
719         return (ret);
720 }
721
722
723 /*** Directory-related utility functions *************************************/
724
725 int
726 fetch_add_entry(struct url_list *ue, struct url *base, const char *name,
727     int pre_quoted)
728 {
729         struct url *tmp;
730         char *tmp_name;
731         size_t base_doc_len, name_len, i;
732         unsigned char c;
733
734         if (strchr(name, '/') != NULL ||
735             strcmp(name, "..") == 0 ||
736             strcmp(name, ".") == 0)
737                 return 0;
738
739         if (strcmp(base->doc, "/") == 0)
740                 base_doc_len = 0;
741         else
742                 base_doc_len = strlen(base->doc);
743
744         name_len = 1;
745         for (i = 0; name[i] != '\0'; ++i) {
746                 if ((!pre_quoted && name[i] == '%') ||
747                     !fetch_urlpath_safe(name[i]))
748                         name_len += 3;
749                 else
750                         ++name_len;
751         }
752
753         tmp_name = malloc( base_doc_len + name_len + 1);
754         if (tmp_name == NULL) {
755                 errno = ENOMEM;
756                 fetch_syserr();
757                 return (-1);
758         }
759
760         if (ue->length + 1 >= ue->alloc_size) {
761                 tmp = realloc(ue->urls, (ue->alloc_size * 2 + 1) * sizeof(*tmp));
762                 if (tmp == NULL) {
763                         free(tmp_name);
764                         errno = ENOMEM;
765                         fetch_syserr();
766                         return (-1);
767                 }
768                 ue->alloc_size = ue->alloc_size * 2 + 1;
769                 ue->urls = tmp;
770         }
771
772         tmp = ue->urls + ue->length;
773         strcpy(tmp->scheme, base->scheme);
774         strcpy(tmp->user, base->user);
775         strcpy(tmp->pwd, base->pwd);
776         strcpy(tmp->host, base->host);
777         tmp->port = base->port;
778         tmp->doc = tmp_name;
779         memcpy(tmp->doc, base->doc, base_doc_len);
780         tmp->doc[base_doc_len] = '/';
781
782         for (i = base_doc_len + 1; *name != '\0'; ++name) {
783                 if ((!pre_quoted && *name == '%') ||
784                     !fetch_urlpath_safe(*name)) {
785                         tmp->doc[i++] = '%';
786                         c = (unsigned char)*name / 16;
787                         if (c < 10)
788                                 tmp->doc[i++] = '0' + c;
789                         else
790                                 tmp->doc[i++] = 'a' - 10 + c;
791                         c = (unsigned char)*name % 16;
792                         if (c < 10)
793                                 tmp->doc[i++] = '0' + c;
794                         else
795                                 tmp->doc[i++] = 'a' - 10 + c;
796                 } else {
797                         tmp->doc[i++] = *name;
798                 }
799         }
800         tmp->doc[i] = '\0';
801
802         tmp->offset = 0;
803         tmp->length = 0;
804         tmp->last_modified = -1;
805
806         ++ue->length;
807
808         return (0);
809 }
810
811 void
812 fetchInitURLList(struct url_list *ue)
813 {
814         ue->length = ue->alloc_size = 0;
815         ue->urls = NULL;
816 }
817
818 int
819 fetchAppendURLList(struct url_list *dst, const struct url_list *src)
820 {
821         size_t i, j, len;
822
823         len = dst->length + src->length;
824         if (len > dst->alloc_size) {
825                 struct url *tmp;
826
827                 tmp = realloc(dst->urls, len * sizeof(*tmp));
828                 if (tmp == NULL) {
829                         errno = ENOMEM;
830                         fetch_syserr();
831                         return (-1);
832                 }
833                 dst->alloc_size = len;
834                 dst->urls = tmp;
835         }
836
837         for (i = 0, j = dst->length; i < src->length; ++i, ++j) {
838                 dst->urls[j] = src->urls[i];
839                 dst->urls[j].doc = strdup(src->urls[i].doc);
840                 if (dst->urls[j].doc == NULL) {
841                         while (i-- > 0)
842                                 free(dst->urls[j].doc);
843                         fetch_syserr();
844                         return -1;
845                 }
846         }
847         dst->length = len;
848
849         return 0;
850 }
851
852 void
853 fetchFreeURLList(struct url_list *ue)
854 {
855         size_t i;
856
857         for (i = 0; i < ue->length; ++i)
858                 free(ue->urls[i].doc);
859         free(ue->urls);
860         ue->length = ue->alloc_size = 0;
861 }
862
863
864 /*** Authentication-related utility functions ********************************/
865
866 static const char *
867 fetch_read_word(FILE *f)
868 {
869         static char word[1024];
870
871         if (fscanf(f, " %1023s ", word) != 1)
872                 return (NULL);
873         return (word);
874 }
875
876 /*
877  * Get authentication data for a URL from .netrc
878  */
879 int
880 fetch_netrc_auth(struct url *url)
881 {
882         char fn[PATH_MAX];
883         const char *word;
884         char *p;
885         FILE *f;
886
887         if ((p = getenv("NETRC")) != NULL) {
888                 if (snprintf(fn, sizeof(fn), "%s", p) >= (int)sizeof(fn)) {
889                         fetch_info("$NETRC specifies a file name "
890                             "longer than PATH_MAX");
891                         return (-1);
892                 }
893         } else {
894                 if ((p = getenv("HOME")) != NULL) {
895                         struct passwd *pwd;
896
897                         if ((pwd = getpwuid(getuid())) == NULL ||
898                             (p = pwd->pw_dir) == NULL)
899                                 return (-1);
900                 }
901                 if (snprintf(fn, sizeof(fn), "%s/.netrc", p) >= (int)sizeof(fn))
902                         return (-1);
903         }
904
905         if ((f = fopen(fn, "r")) == NULL)
906                 return (-1);
907         while ((word = fetch_read_word(f)) != NULL) {
908                 if (strcmp(word, "default") == 0)
909                         break;
910                 if (strcmp(word, "machine") == 0 &&
911                     (word = fetch_read_word(f)) != NULL &&
912                     strcasecmp(word, url->host) == 0) {
913                         break;
914                 }
915         }
916         if (word == NULL)
917                 goto ferr;
918         while ((word = fetch_read_word(f)) != NULL) {
919                 if (strcmp(word, "login") == 0) {
920                         if ((word = fetch_read_word(f)) == NULL)
921                                 goto ferr;
922                         if (snprintf(url->user, sizeof(url->user),
923                                 "%s", word) > (int)sizeof(url->user)) {
924                                 fetch_info("login name in .netrc is too long");
925                                 url->user[0] = '\0';
926                         }
927                 } else if (strcmp(word, "password") == 0) {
928                         if ((word = fetch_read_word(f)) == NULL)
929                                 goto ferr;
930                         if (snprintf(url->pwd, sizeof(url->pwd),
931                                 "%s", word) > (int)sizeof(url->pwd)) {
932                                 fetch_info("password in .netrc is too long");
933                                 url->pwd[0] = '\0';
934                         }
935                 } else if (strcmp(word, "account") == 0) {
936                         if ((word = fetch_read_word(f)) == NULL)
937                                 goto ferr;
938                         /* XXX not supported! */
939                 } else {
940                         break;
941                 }
942         }
943         fclose(f);
944         return (0);
945  ferr:
946         fclose(f);
947         return (-1);
948 }
949
950 /*
951  * The no_proxy environment variable specifies a set of domains for
952  * which the proxy should not be consulted; the contents is a comma-,
953  * or space-separated list of domain names.  A single asterisk will
954  * override all proxy variables and no transactions will be proxied
955  * (for compatability with lynx and curl, see the discussion at
956  * <http://curl.haxx.se/mail/archive_pre_oct_99/0009.html>).
957  */
958 int
959 fetch_no_proxy_match(const char *host)
960 {
961         const char *no_proxy, *p, *q;
962         size_t h_len, d_len;
963
964         if ((no_proxy = getenv("NO_PROXY")) == NULL &&
965             (no_proxy = getenv("no_proxy")) == NULL)
966                 return (0);
967
968         /* asterisk matches any hostname */
969         if (strcmp(no_proxy, "*") == 0)
970                 return (1);
971
972         h_len = strlen(host);
973         p = no_proxy;
974         do {
975                 /* position p at the beginning of a domain suffix */
976                 while (*p == ',' || isspace((unsigned char)*p))
977                         p++;
978
979                 /* position q at the first separator character */
980                 for (q = p; *q; ++q)
981                         if (*q == ',' || isspace((unsigned char)*q))
982                                 break;
983
984                 d_len = q - p;
985                 if (d_len > 0 && h_len > d_len &&
986                     strncasecmp(host + h_len - d_len,
987                         p, d_len) == 0) {
988                         /* domain name matches */
989                         return (1);
990                 }
991
992                 p = q + 1;
993         } while (*q);
994
995         return (0);
996 }
997
998 struct fetchIO {
999         void *io_cookie;
1000         ssize_t (*io_read)(void *, void *, size_t);
1001         ssize_t (*io_write)(void *, const void *, size_t);
1002         void (*io_close)(void *);
1003 };
1004
1005 void
1006 fetchIO_close(fetchIO *f)
1007 {
1008         if (f->io_close != NULL)
1009                 (*f->io_close)(f->io_cookie);
1010
1011         free(f);
1012 }
1013
1014 fetchIO *
1015 fetchIO_unopen(void *io_cookie, ssize_t (*io_read)(void *, void *, size_t),
1016     ssize_t (*io_write)(void *, const void *, size_t),
1017     void (*io_close)(void *))
1018 {
1019         fetchIO *f;
1020
1021         f = malloc(sizeof(*f));
1022         if (f == NULL)
1023                 return f;
1024
1025         f->io_cookie = io_cookie;
1026         f->io_read = io_read;
1027         f->io_write = io_write;
1028         f->io_close = io_close;
1029
1030         return f;
1031 }
1032
1033 ssize_t
1034 fetchIO_read(fetchIO *f, void *buf, size_t len)
1035 {
1036         if (f->io_read == NULL)
1037                 return EBADF;
1038         return (*f->io_read)(f->io_cookie, buf, len);
1039 }
1040
1041 ssize_t
1042 fetchIO_write(fetchIO *f, const void *buf, size_t len)
1043 {
1044         if (f->io_read == NULL)
1045                 return EBADF;
1046         return (*f->io_write)(f->io_cookie, buf, len);
1047 }