Intial commit
[profile/ivi/w3m.git] / istream.c
1 /* $Id: istream.c,v 1.26 2007/05/23 15:06:05 inu Exp $ */
2 #include "fm.h"
3 #include "myctype.h"
4 #include "istream.h"
5 #include <signal.h>
6 #ifdef USE_SSL
7 #include <openssl/x509v3.h>
8 #endif
9 #ifdef __MINGW32_VERSION
10 #include <winsock.h>
11 #endif
12
13 #define uchar           unsigned char
14
15 #define STREAM_BUF_SIZE 8192
16 #define SSL_BUF_SIZE    1536
17
18 #define MUST_BE_UPDATED(bs) ((bs)->stream.cur==(bs)->stream.next)
19
20 #define POP_CHAR(bs) ((bs)->iseos?'\0':(bs)->stream.buf[(bs)->stream.cur++])
21
22 static void basic_close(int *handle);
23 static int basic_read(int *handle, char *buf, int len);
24
25 static void file_close(struct file_handle *handle);
26 static int file_read(struct file_handle *handle, char *buf, int len);
27
28 static int str_read(Str handle, char *buf, int len);
29
30 #ifdef USE_SSL
31 static void ssl_close(struct ssl_handle *handle);
32 static int ssl_read(struct ssl_handle *handle, char *buf, int len);
33 #endif
34
35 static int ens_read(struct ens_handle *handle, char *buf, int len);
36 static void ens_close(struct ens_handle *handle);
37
38 static void
39 do_update(BaseStream base)
40 {
41     int len;
42     base->stream.cur = base->stream.next = 0;
43     len = base->read(base->handle, base->stream.buf, base->stream.size);
44     if (len <= 0)
45         base->iseos = TRUE;
46     else
47         base->stream.next += len;
48 }
49
50 static int
51 buffer_read(StreamBuffer sb, char *obuf, int count)
52 {
53     int len = sb->next - sb->cur;
54     if (len > 0) {
55         if (len > count)
56             len = count;
57         bcopy((const void *)&sb->buf[sb->cur], obuf, len);
58         sb->cur += len;
59     }
60     return len;
61 }
62
63 static void
64 init_buffer(BaseStream base, char *buf, int bufsize)
65 {
66     StreamBuffer sb = &base->stream;
67     sb->size = bufsize;
68     sb->cur = 0;
69     if (buf) {
70         sb->buf = (uchar *) buf;
71         sb->next = bufsize;
72     }
73     else {
74         sb->buf = NewAtom_N(uchar, bufsize);
75         sb->next = 0;
76     }
77     base->iseos = FALSE;
78 }
79
80 static void
81 init_base_stream(BaseStream base, int bufsize)
82 {
83     init_buffer(base, NULL, bufsize);
84 }
85
86 static void
87 init_str_stream(BaseStream base, Str s)
88 {
89     init_buffer(base, s->ptr, s->length);
90 }
91
92 InputStream
93 newInputStream(int des)
94 {
95     InputStream stream;
96     if (des < 0)
97         return NULL;
98     stream = New(union input_stream);
99     init_base_stream(&stream->base, STREAM_BUF_SIZE);
100     stream->base.type = IST_BASIC;
101     stream->base.handle = New(int);
102     *(int *)stream->base.handle = des;
103     stream->base.read = (int (*)())basic_read;
104     stream->base.close = (void (*)())basic_close;
105     return stream;
106 }
107
108 InputStream
109 newFileStream(FILE * f, void (*closep) ())
110 {
111     InputStream stream;
112     if (f == NULL)
113         return NULL;
114     stream = New(union input_stream);
115     init_base_stream(&stream->base, STREAM_BUF_SIZE);
116     stream->file.type = IST_FILE;
117     stream->file.handle = New(struct file_handle);
118     stream->file.handle->f = f;
119     if (closep)
120         stream->file.handle->close = closep;
121     else
122         stream->file.handle->close = (void (*)())fclose;
123     stream->file.read = (int (*)())file_read;
124     stream->file.close = (void (*)())file_close;
125     return stream;
126 }
127
128 InputStream
129 newStrStream(Str s)
130 {
131     InputStream stream;
132     if (s == NULL)
133         return NULL;
134     stream = New(union input_stream);
135     init_str_stream(&stream->base, s);
136     stream->str.type = IST_STR;
137     stream->str.handle = s;
138     stream->str.read = (int (*)())str_read;
139     stream->str.close = NULL;
140     return stream;
141 }
142
143 #ifdef USE_SSL
144 InputStream
145 newSSLStream(SSL * ssl, int sock)
146 {
147     InputStream stream;
148     if (sock < 0)
149         return NULL;
150     stream = New(union input_stream);
151     init_base_stream(&stream->base, SSL_BUF_SIZE);
152     stream->ssl.type = IST_SSL;
153     stream->ssl.handle = New(struct ssl_handle);
154     stream->ssl.handle->ssl = ssl;
155     stream->ssl.handle->sock = sock;
156     stream->ssl.read = (int (*)())ssl_read;
157     stream->ssl.close = (void (*)())ssl_close;
158     return stream;
159 }
160 #endif
161
162 InputStream
163 newEncodedStream(InputStream is, char encoding)
164 {
165     InputStream stream;
166     if (is == NULL || (encoding != ENC_QUOTE && encoding != ENC_BASE64 &&
167                        encoding != ENC_UUENCODE))
168         return is;
169     stream = New(union input_stream);
170     init_base_stream(&stream->base, STREAM_BUF_SIZE);
171     stream->ens.type = IST_ENCODED;
172     stream->ens.handle = New(struct ens_handle);
173     stream->ens.handle->is = is;
174     stream->ens.handle->pos = 0;
175     stream->ens.handle->encoding = encoding;
176     stream->ens.handle->s = NULL;
177     stream->ens.read = (int (*)())ens_read;
178     stream->ens.close = (void (*)())ens_close;
179     return stream;
180 }
181
182 int
183 ISclose(InputStream stream)
184 {
185     MySignalHandler(*prevtrap) ();
186     if (stream == NULL || stream->base.close == NULL ||
187         stream->base.type & IST_UNCLOSE)
188         return -1;
189     prevtrap = mySignal(SIGINT, SIG_IGN);
190     stream->base.close(stream->base.handle);
191     mySignal(SIGINT, prevtrap);
192     return 0;
193 }
194
195 int
196 ISgetc(InputStream stream)
197 {
198     BaseStream base;
199     if (stream == NULL)
200         return '\0';
201     base = &stream->base;
202     if (!base->iseos && MUST_BE_UPDATED(base))
203         do_update(base);
204     return POP_CHAR(base);
205 }
206
207 int
208 ISundogetc(InputStream stream)
209 {
210     StreamBuffer sb;
211     if (stream == NULL)
212         return -1;
213     sb = &stream->base.stream;
214     if (sb->cur > 0) {
215         sb->cur--;
216         return 0;
217     }
218     return -1;
219 }
220
221 #define MARGIN_STR_SIZE 10
222 Str
223 StrISgets(InputStream stream)
224 {
225     BaseStream base;
226     StreamBuffer sb;
227     Str s = NULL;
228     uchar *p;
229     int len;
230
231     if (stream == NULL)
232         return '\0';
233     base = &stream->base;
234     sb = &base->stream;
235
236     while (!base->iseos) {
237         if (MUST_BE_UPDATED(base)) {
238             do_update(base);
239         }
240         else {
241             if ((p = memchr(&sb->buf[sb->cur], '\n', sb->next - sb->cur))) {
242                 len = p - &sb->buf[sb->cur] + 1;
243                 if (s == NULL)
244                     s = Strnew_size(len);
245                 Strcat_charp_n(s, (char *)&sb->buf[sb->cur], len);
246                 sb->cur += len;
247                 return s;
248             }
249             else {
250                 if (s == NULL)
251                     s = Strnew_size(sb->next - sb->cur + MARGIN_STR_SIZE);
252                 Strcat_charp_n(s, (char *)&sb->buf[sb->cur],
253                                sb->next - sb->cur);
254                 sb->cur = sb->next;
255             }
256         }
257     }
258
259     if (s == NULL)
260         return Strnew();
261     return s;
262 }
263
264 Str
265 StrmyISgets(InputStream stream)
266 {
267     BaseStream base;
268     StreamBuffer sb;
269     Str s = NULL;
270     int i, len;
271
272     if (stream == NULL)
273         return '\0';
274     base = &stream->base;
275     sb = &base->stream;
276
277     while (!base->iseos) {
278         if (MUST_BE_UPDATED(base)) {
279             do_update(base);
280         }
281         else {
282             if (s && Strlastchar(s) == '\r') {
283                 if (sb->buf[sb->cur] == '\n')
284                     Strcat_char(s, (char)sb->buf[sb->cur++]);
285                 return s;
286             }
287             for (i = sb->cur;
288                  i < sb->next && sb->buf[i] != '\n' && sb->buf[i] != '\r';
289                  i++) ;
290             if (i < sb->next) {
291                 len = i - sb->cur + 1;
292                 if (s == NULL)
293                     s = Strnew_size(len + MARGIN_STR_SIZE);
294                 Strcat_charp_n(s, (char *)&sb->buf[sb->cur], len);
295                 sb->cur = i + 1;
296                 if (sb->buf[i] == '\n')
297                     return s;
298             }
299             else {
300                 if (s == NULL)
301                     s = Strnew_size(sb->next - sb->cur + MARGIN_STR_SIZE);
302                 Strcat_charp_n(s, (char *)&sb->buf[sb->cur],
303                                sb->next - sb->cur);
304                 sb->cur = sb->next;
305             }
306         }
307     }
308
309     if (s == NULL)
310         return Strnew();
311     return s;
312 }
313
314 int
315 ISread(InputStream stream, Str buf, int count)
316 {
317     int rest, len;
318     BaseStream base;
319
320     if (stream == NULL || (base = &stream->base)->iseos)
321         return 0;
322
323     len = buffer_read(&base->stream, buf->ptr, count);
324     rest = count - len;
325     if (MUST_BE_UPDATED(base)) {
326         len = base->read(base->handle, &buf->ptr[len], rest);
327         if (len <= 0) {
328             base->iseos = TRUE;
329             len = 0;
330         }
331         rest -= len;
332     }
333     Strtruncate(buf, count - rest);
334     if (buf->length > 0)
335         return 1;
336     return 0;
337 }
338
339 int
340 ISfileno(InputStream stream)
341 {
342     if (stream == NULL)
343         return -1;
344     switch (IStype(stream) & ~IST_UNCLOSE) {
345     case IST_BASIC:
346         return *(int *)stream->base.handle;
347     case IST_FILE:
348         return fileno(stream->file.handle->f);
349 #ifdef USE_SSL
350     case IST_SSL:
351         return stream->ssl.handle->sock;
352 #endif
353     case IST_ENCODED:
354         return ISfileno(stream->ens.handle->is);
355     default:
356         return -1;
357     }
358 }
359
360 int
361 ISeos(InputStream stream)
362 {
363     BaseStream base = &stream->base;
364     if (!base->iseos && MUST_BE_UPDATED(base))
365         do_update(base);
366     return base->iseos;
367 }
368
369 #ifdef USE_SSL
370 static Str accept_this_site;
371
372 void
373 ssl_accept_this_site(char *hostname)
374 {
375     if (hostname)
376         accept_this_site = Strnew_charp(hostname);
377     else
378         accept_this_site = NULL;
379 }
380
381 static int
382 ssl_match_cert_ident(char *ident, int ilen, char *hostname)
383 {
384     /* RFC2818 3.1.  Server Identity
385      * Names may contain the wildcard
386      * character * which is considered to match any single domain name
387      * component or component fragment. E.g., *.a.com matches foo.a.com but
388      * not bar.foo.a.com. f*.com matches foo.com but not bar.com.
389      */
390     int hlen = strlen(hostname);
391     int i, c;
392
393     /* Is this an exact match? */
394     if ((ilen == hlen) && strncasecmp(ident, hostname, hlen) == 0)
395         return TRUE;
396
397     for (i = 0; i < ilen; i++) {
398         if (ident[i] == '*' && ident[i + 1] == '.') {
399             while ((c = *hostname++) != '\0')
400                 if (c == '.')
401                     break;
402             i++;
403         }
404         else {
405             if (ident[i] != *hostname++)
406                 return FALSE;
407         }
408     }
409     return *hostname == '\0';
410 }
411
412 static Str
413 ssl_check_cert_ident(X509 * x, char *hostname)
414 {
415     int i;
416     Str ret = NULL;
417     int match_ident = FALSE;
418     /*
419      * All we need to do here is check that the CN matches.
420      *
421      * From RFC2818 3.1 Server Identity:
422      * If a subjectAltName extension of type dNSName is present, that MUST
423      * be used as the identity. Otherwise, the (most specific) Common Name
424      * field in the Subject field of the certificate MUST be used. Although
425      * the use of the Common Name is existing practice, it is deprecated and
426      * Certification Authorities are encouraged to use the dNSName instead.
427      */
428     i = X509_get_ext_by_NID(x, NID_subject_alt_name, -1);
429     if (i >= 0) {
430         X509_EXTENSION *ex;
431         STACK_OF(GENERAL_NAME) * alt;
432
433         ex = X509_get_ext(x, i);
434         alt = X509V3_EXT_d2i(ex);
435         if (alt) {
436             int n;
437             GENERAL_NAME *gn;
438             X509V3_EXT_METHOD *method;
439             Str seen_dnsname = NULL;
440
441             n = sk_GENERAL_NAME_num(alt);
442             for (i = 0; i < n; i++) {
443                 gn = sk_GENERAL_NAME_value(alt, i);
444                 if (gn->type == GEN_DNS) {
445                     char *sn = ASN1_STRING_data(gn->d.ia5);
446                     int sl = ASN1_STRING_length(gn->d.ia5);
447
448                     if (!seen_dnsname)
449                         seen_dnsname = Strnew();
450                     Strcat_m_charp(seen_dnsname, sn, " ", NULL);
451                     if (ssl_match_cert_ident(sn, sl, hostname))
452                         break;
453                 }
454             }
455             method = X509V3_EXT_get(ex);
456             sk_GENERAL_NAME_free(alt);
457             if (i < n)          /* Found a match */
458                 match_ident = TRUE;
459             else if (seen_dnsname)
460                 /* FIXME: gettextize? */
461                 ret = Sprintf("Bad cert ident from %s: dNSName=%s", hostname,
462                               seen_dnsname->ptr);
463         }
464     }
465
466     if (match_ident == FALSE && ret == NULL) {
467         X509_NAME *xn;
468         char buf[2048];
469
470         xn = X509_get_subject_name(x);
471
472         if (X509_NAME_get_text_by_NID(xn, NID_commonName,
473                                       buf, sizeof(buf)) == -1)
474             /* FIXME: gettextize? */
475             ret = Strnew_charp("Unable to get common name from peer cert");
476         else if (!ssl_match_cert_ident(buf, strlen(buf), hostname))
477             /* FIXME: gettextize? */
478             ret = Sprintf("Bad cert ident %s from %s", buf, hostname);
479         else
480             match_ident = TRUE;
481     }
482     return ret;
483 }
484
485 Str
486 ssl_get_certificate(SSL * ssl, char *hostname)
487 {
488     BIO *bp;
489     X509 *x;
490     X509_NAME *xn;
491     char *p;
492     int len;
493     Str s;
494     char buf[2048];
495     Str amsg = NULL;
496     Str emsg;
497     char *ans;
498
499     if (ssl == NULL)
500         return NULL;
501     x = SSL_get_peer_certificate(ssl);
502     if (x == NULL) {
503         if (accept_this_site
504             && strcasecmp(accept_this_site->ptr, hostname) == 0)
505             ans = "y";
506         else {
507             /* FIXME: gettextize? */
508             emsg = Strnew_charp("No SSL peer certificate: accept? (y/n)");
509             ans = inputAnswer(emsg->ptr);
510         }
511         if (ans && TOLOWER(*ans) == 'y')
512             /* FIXME: gettextize? */
513             amsg = Strnew_charp
514                 ("Accept SSL session without any peer certificate");
515         else {
516             /* FIXME: gettextize? */
517             char *e = "This SSL session was rejected "
518                 "to prevent security violation: no peer certificate";
519             disp_err_message(e, FALSE);
520             free_ssl_ctx();
521             return NULL;
522         }
523         if (amsg)
524             disp_err_message(amsg->ptr, FALSE);
525         ssl_accept_this_site(hostname);
526         /* FIXME: gettextize? */
527         s = amsg ? amsg : Strnew_charp("valid certificate");
528         return s;
529     }
530 #ifdef USE_SSL_VERIFY
531     /* check the cert chain.
532      * The chain length is automatically checked by OpenSSL when we
533      * set the verify depth in the ctx.
534      */
535     if (ssl_verify_server) {
536         long verr;
537         if ((verr = SSL_get_verify_result(ssl))
538             != X509_V_OK) {
539             const char *em = X509_verify_cert_error_string(verr);
540             if (accept_this_site
541                 && strcasecmp(accept_this_site->ptr, hostname) == 0)
542                 ans = "y";
543             else {
544                 /* FIXME: gettextize? */
545                 emsg = Sprintf("%s: accept? (y/n)", em);
546                 ans = inputAnswer(emsg->ptr);
547             }
548             if (ans && TOLOWER(*ans) == 'y') {
549                 /* FIXME: gettextize? */
550                 amsg = Sprintf("Accept unsecure SSL session: "
551                                "unverified: %s", em);
552             }
553             else {
554                 /* FIXME: gettextize? */
555                 char *e =
556                     Sprintf("This SSL session was rejected: %s", em)->ptr;
557                 disp_err_message(e, FALSE);
558                 free_ssl_ctx();
559                 return NULL;
560             }
561         }
562     }
563 #endif
564     emsg = ssl_check_cert_ident(x, hostname);
565     if (emsg != NULL) {
566         if (accept_this_site
567             && strcasecmp(accept_this_site->ptr, hostname) == 0)
568             ans = "y";
569         else {
570             Str ep = Strdup(emsg);
571             if (ep->length > COLS - 16)
572                 Strshrink(ep, ep->length - (COLS - 16));
573             Strcat_charp(ep, ": accept? (y/n)");
574             ans = inputAnswer(ep->ptr);
575         }
576         if (ans && TOLOWER(*ans) == 'y') {
577             /* FIXME: gettextize? */
578             amsg = Strnew_charp("Accept unsecure SSL session:");
579             Strcat(amsg, emsg);
580         }
581         else {
582             /* FIXME: gettextize? */
583             char *e = "This SSL session was rejected "
584                 "to prevent security violation";
585             disp_err_message(e, FALSE);
586             free_ssl_ctx();
587             return NULL;
588         }
589     }
590     if (amsg)
591         disp_err_message(amsg->ptr, FALSE);
592     ssl_accept_this_site(hostname);
593     /* FIXME: gettextize? */
594     s = amsg ? amsg : Strnew_charp("valid certificate");
595     Strcat_charp(s, "\n");
596     xn = X509_get_subject_name(x);
597     if (X509_NAME_get_text_by_NID(xn, NID_commonName, buf, sizeof(buf)) == -1)
598         Strcat_charp(s, " subject=<unknown>");
599     else
600         Strcat_m_charp(s, " subject=", buf, NULL);
601     xn = X509_get_issuer_name(x);
602     if (X509_NAME_get_text_by_NID(xn, NID_commonName, buf, sizeof(buf)) == -1)
603         Strcat_charp(s, ": issuer=<unknown>");
604     else
605         Strcat_m_charp(s, ": issuer=", buf, NULL);
606     Strcat_charp(s, "\n\n");
607
608     bp = BIO_new(BIO_s_mem());
609     X509_print(bp, x);
610     len = (int)BIO_ctrl(bp, BIO_CTRL_INFO, 0, (char *)&p);
611     Strcat_charp_n(s, p, len);
612     BIO_free_all(bp);
613     X509_free(x);
614     return s;
615 }
616 #endif
617
618 /* Raw level input stream functions */
619
620 static void
621 basic_close(int *handle)
622 {
623 #ifdef __MINGW32_VERSION
624     closesocket(*(int *)handle);
625 #else
626     close(*(int *)handle);
627 #endif
628 }
629
630 static int
631 basic_read(int *handle, char *buf, int len)
632 {
633 #ifdef __MINGW32_VERSION
634     return recv(*(int *)handle, buf, len, 0);
635 #else
636     return read(*(int *)handle, buf, len);
637 #endif
638 }
639
640 static void
641 file_close(struct file_handle *handle)
642 {
643     handle->close(handle->f);
644 }
645
646 static int
647 file_read(struct file_handle *handle, char *buf, int len)
648 {
649     return fread(buf, 1, len, handle->f);
650 }
651
652 static int
653 str_read(Str handle, char *buf, int len)
654 {
655     return 0;
656 }
657
658 #ifdef USE_SSL
659 static void
660 ssl_close(struct ssl_handle *handle)
661 {
662     close(handle->sock);
663     if (handle->ssl)
664         SSL_free(handle->ssl);
665 }
666
667 static int
668 ssl_read(struct ssl_handle *handle, char *buf, int len)
669 {
670     int status;
671     if (handle->ssl) {
672 #ifdef USE_SSL_VERIFY
673         for (;;) {
674             status = SSL_read(handle->ssl, buf, len);
675             if (status > 0)
676                 break;
677             switch (SSL_get_error(handle->ssl, status)) {
678             case SSL_ERROR_WANT_READ:
679             case SSL_ERROR_WANT_WRITE:  /* reads can trigger write errors; see SSL_get_error(3) */
680                 continue;
681             default:
682                 break;
683             }
684             break;
685         }
686 #else                           /* if !defined(USE_SSL_VERIFY) */
687         status = SSL_read(handle->ssl, buf, len);
688 #endif                          /* !defined(USE_SSL_VERIFY) */
689     }
690     else
691         status = read(handle->sock, buf, len);
692     return status;
693 }
694 #endif                          /* USE_SSL */
695
696 static void
697 ens_close(struct ens_handle *handle)
698 {
699     ISclose(handle->is);
700 }
701
702 static int
703 ens_read(struct ens_handle *handle, char *buf, int len)
704 {
705     if (handle->s == NULL || handle->pos == handle->s->length) {
706         char *p;
707         handle->s = StrmyISgets(handle->is);
708         if (handle->s->length == 0)
709             return 0;
710         cleanup_line(handle->s, PAGER_MODE);
711         if (handle->encoding == ENC_BASE64)
712             Strchop(handle->s);
713         else if (handle->encoding == ENC_UUENCODE) {
714             if (!strncmp(handle->s->ptr, "begin", 5))
715                 handle->s = StrmyISgets(handle->is);
716             Strchop(handle->s);
717         }
718         p = handle->s->ptr;
719         if (handle->encoding == ENC_QUOTE)
720             handle->s = decodeQP(&p);
721         else if (handle->encoding == ENC_BASE64)
722             handle->s = decodeB(&p);
723         else if (handle->encoding == ENC_UUENCODE)
724             handle->s = decodeU(&p);
725         handle->pos = 0;
726     }
727
728     if (len > handle->s->length - handle->pos)
729         len = handle->s->length - handle->pos;
730
731     bcopy(&handle->s->ptr[handle->pos], buf, len);
732     handle->pos += len;
733     return len;
734 }