w3m

Unnamed repository; edit this file to name it for gitweb.
git clone https://logand.com/git/w3m.git/
Log | Files | Refs | README

istream.c (17554B)


      1 /* $Id$ */
      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 		    /* replace \0 to make full string visible to user */
    451 		    if (sl != strlen(sn)) {
    452 			int i;
    453 			for (i = 0; i < sl; ++i) {
    454 			    if (!sn[i])
    455 				sn[i] = '!';
    456 			}
    457 		    }
    458 		    Strcat_m_charp(seen_dnsname, sn, " ", NULL);
    459 		    if (sl == strlen(sn) /* catch \0 in SAN */
    460 			&& ssl_match_cert_ident(sn, sl, hostname))
    461 			break;
    462 		}
    463 	    }
    464 	    method = X509V3_EXT_get(ex);
    465 	    sk_GENERAL_NAME_free(alt);
    466 	    if (i < n)		/* Found a match */
    467 		match_ident = TRUE;
    468 	    else if (seen_dnsname)
    469 		/* FIXME: gettextize? */
    470 		ret = Sprintf("Bad cert ident from %s: dNSName=%s", hostname,
    471 			      seen_dnsname->ptr);
    472 	}
    473     }
    474 
    475     if (match_ident == FALSE && ret == NULL) {
    476 	X509_NAME *xn;
    477 	char buf[2048];
    478 	int slen;
    479 
    480 	xn = X509_get_subject_name(x);
    481 
    482 	slen = X509_NAME_get_text_by_NID(xn, NID_commonName, buf, sizeof(buf));
    483 	if ( slen == -1)
    484 	    /* FIXME: gettextize? */
    485 	    ret = Strnew_charp("Unable to get common name from peer cert");
    486 	else if (slen != strlen(buf)
    487 		|| !ssl_match_cert_ident(buf, strlen(buf), hostname)) {
    488 	    /* replace \0 to make full string visible to user */
    489 	    if (slen != strlen(buf)) {
    490 		int i;
    491 		for (i = 0; i < slen; ++i) {
    492 		    if (!buf[i])
    493 			buf[i] = '!';
    494 		}
    495 	    }
    496 	    /* FIXME: gettextize? */
    497 	    ret = Sprintf("Bad cert ident %s from %s", buf, hostname);
    498 	}
    499 	else
    500 	    match_ident = TRUE;
    501     }
    502     return ret;
    503 }
    504 
    505 Str
    506 ssl_get_certificate(SSL * ssl, char *hostname)
    507 {
    508     BIO *bp;
    509     X509 *x;
    510     X509_NAME *xn;
    511     char *p;
    512     int len;
    513     Str s;
    514     char buf[2048];
    515     Str amsg = NULL;
    516     Str emsg;
    517     char *ans;
    518 
    519     if (ssl == NULL)
    520 	return NULL;
    521     x = SSL_get_peer_certificate(ssl);
    522     if (x == NULL) {
    523 	if (accept_this_site
    524 	    && strcasecmp(accept_this_site->ptr, hostname) == 0)
    525 	    ans = "y";
    526 	else {
    527 	    /* FIXME: gettextize? */
    528 	    emsg = Strnew_charp("No SSL peer certificate: accept? (y/n)");
    529 	    ans = inputAnswer(emsg->ptr);
    530 	}
    531 	if (ans && TOLOWER(*ans) == 'y')
    532 	    /* FIXME: gettextize? */
    533 	    amsg = Strnew_charp
    534 		("Accept SSL session without any peer certificate");
    535 	else {
    536 	    /* FIXME: gettextize? */
    537 	    char *e = "This SSL session was rejected "
    538 		"to prevent security violation: no peer certificate";
    539 	    disp_err_message(e, FALSE);
    540 	    free_ssl_ctx();
    541 	    return NULL;
    542 	}
    543 	if (amsg)
    544 	    disp_err_message(amsg->ptr, FALSE);
    545 	ssl_accept_this_site(hostname);
    546 	/* FIXME: gettextize? */
    547 	s = amsg ? amsg : Strnew_charp("valid certificate");
    548 	return s;
    549     }
    550 #ifdef USE_SSL_VERIFY
    551     /* check the cert chain.
    552      * The chain length is automatically checked by OpenSSL when we
    553      * set the verify depth in the ctx.
    554      */
    555     if (ssl_verify_server) {
    556 	long verr;
    557 	if ((verr = SSL_get_verify_result(ssl))
    558 	    != X509_V_OK) {
    559 	    const char *em = X509_verify_cert_error_string(verr);
    560 	    if (accept_this_site
    561 		&& strcasecmp(accept_this_site->ptr, hostname) == 0)
    562 		ans = "y";
    563 	    else {
    564 		/* FIXME: gettextize? */
    565 		emsg = Sprintf("%s: accept? (y/n)", em);
    566 		ans = inputAnswer(emsg->ptr);
    567 	    }
    568 	    if (ans && TOLOWER(*ans) == 'y') {
    569 		/* FIXME: gettextize? */
    570 		amsg = Sprintf("Accept unsecure SSL session: "
    571 			       "unverified: %s", em);
    572 	    }
    573 	    else {
    574 		/* FIXME: gettextize? */
    575 		char *e =
    576 		    Sprintf("This SSL session was rejected: %s", em)->ptr;
    577 		disp_err_message(e, FALSE);
    578 		free_ssl_ctx();
    579 		return NULL;
    580 	    }
    581 	}
    582     }
    583 #endif
    584     emsg = ssl_check_cert_ident(x, hostname);
    585     if (emsg != NULL) {
    586 	if (accept_this_site
    587 	    && strcasecmp(accept_this_site->ptr, hostname) == 0)
    588 	    ans = "y";
    589 	else {
    590 	    Str ep = Strdup(emsg);
    591 	    if (ep->length > COLS - 16)
    592 		Strshrink(ep, ep->length - (COLS - 16));
    593 	    Strcat_charp(ep, ": accept? (y/n)");
    594 	    ans = inputAnswer(ep->ptr);
    595 	}
    596 	if (ans && TOLOWER(*ans) == 'y') {
    597 	    /* FIXME: gettextize? */
    598 	    amsg = Strnew_charp("Accept unsecure SSL session:");
    599 	    Strcat(amsg, emsg);
    600 	}
    601 	else {
    602 	    /* FIXME: gettextize? */
    603 	    char *e = "This SSL session was rejected "
    604 		"to prevent security violation";
    605 	    disp_err_message(e, FALSE);
    606 	    free_ssl_ctx();
    607 	    return NULL;
    608 	}
    609     }
    610     if (amsg)
    611 	disp_err_message(amsg->ptr, FALSE);
    612     ssl_accept_this_site(hostname);
    613     /* FIXME: gettextize? */
    614     s = amsg ? amsg : Strnew_charp("valid certificate");
    615     Strcat_charp(s, "\n");
    616     xn = X509_get_subject_name(x);
    617     if (X509_NAME_get_text_by_NID(xn, NID_commonName, buf, sizeof(buf)) == -1)
    618 	Strcat_charp(s, " subject=<unknown>");
    619     else
    620 	Strcat_m_charp(s, " subject=", buf, NULL);
    621     xn = X509_get_issuer_name(x);
    622     if (X509_NAME_get_text_by_NID(xn, NID_commonName, buf, sizeof(buf)) == -1)
    623 	Strcat_charp(s, ": issuer=<unknown>");
    624     else
    625 	Strcat_m_charp(s, ": issuer=", buf, NULL);
    626     Strcat_charp(s, "\n\n");
    627 
    628     bp = BIO_new(BIO_s_mem());
    629     X509_print(bp, x);
    630     len = (int)BIO_ctrl(bp, BIO_CTRL_INFO, 0, (char *)&p);
    631     Strcat_charp_n(s, p, len);
    632     BIO_free_all(bp);
    633     X509_free(x);
    634     return s;
    635 }
    636 #endif
    637 
    638 /* Raw level input stream functions */
    639 
    640 static void
    641 basic_close(int *handle)
    642 {
    643 #ifdef __MINGW32_VERSION
    644     closesocket(*(int *)handle);
    645 #else
    646     close(*(int *)handle);
    647 #endif
    648 }
    649 
    650 static int
    651 basic_read(int *handle, char *buf, int len)
    652 {
    653 #ifdef __MINGW32_VERSION
    654     return recv(*(int *)handle, buf, len, 0);
    655 #else
    656     return read(*(int *)handle, buf, len);
    657 #endif
    658 }
    659 
    660 static void
    661 file_close(struct file_handle *handle)
    662 {
    663     handle->close(handle->f);
    664 }
    665 
    666 static int
    667 file_read(struct file_handle *handle, char *buf, int len)
    668 {
    669     return fread(buf, 1, len, handle->f);
    670 }
    671 
    672 static int
    673 str_read(Str handle, char *buf, int len)
    674 {
    675     return 0;
    676 }
    677 
    678 #ifdef USE_SSL
    679 static void
    680 ssl_close(struct ssl_handle *handle)
    681 {
    682     close(handle->sock);
    683     if (handle->ssl)
    684 	SSL_free(handle->ssl);
    685 }
    686 
    687 static int
    688 ssl_read(struct ssl_handle *handle, char *buf, int len)
    689 {
    690     int status;
    691     if (handle->ssl) {
    692 #ifdef USE_SSL_VERIFY
    693 	for (;;) {
    694 	    status = SSL_read(handle->ssl, buf, len);
    695 	    if (status > 0)
    696 		break;
    697 	    switch (SSL_get_error(handle->ssl, status)) {
    698 	    case SSL_ERROR_WANT_READ:
    699 	    case SSL_ERROR_WANT_WRITE:	/* reads can trigger write errors; see SSL_get_error(3) */
    700 		continue;
    701 	    default:
    702 		break;
    703 	    }
    704 	    break;
    705 	}
    706 #else				/* if !defined(USE_SSL_VERIFY) */
    707 	status = SSL_read(handle->ssl, buf, len);
    708 #endif				/* !defined(USE_SSL_VERIFY) */
    709     }
    710     else
    711 	status = read(handle->sock, buf, len);
    712     return status;
    713 }
    714 #endif				/* USE_SSL */
    715 
    716 static void
    717 ens_close(struct ens_handle *handle)
    718 {
    719     ISclose(handle->is);
    720 }
    721 
    722 static int
    723 ens_read(struct ens_handle *handle, char *buf, int len)
    724 {
    725     if (handle->s == NULL || handle->pos == handle->s->length) {
    726 	char *p;
    727 	handle->s = StrmyISgets(handle->is);
    728 	if (handle->s->length == 0)
    729 	    return 0;
    730 	cleanup_line(handle->s, PAGER_MODE);
    731 	if (handle->encoding == ENC_BASE64)
    732 	    Strchop(handle->s);
    733 	else if (handle->encoding == ENC_UUENCODE) {
    734 	    if (!strncmp(handle->s->ptr, "begin", 5))
    735 		handle->s = StrmyISgets(handle->is);
    736 	    Strchop(handle->s);
    737 	}
    738 	p = handle->s->ptr;
    739 	if (handle->encoding == ENC_QUOTE)
    740 	    handle->s = decodeQP(&p);
    741 	else if (handle->encoding == ENC_BASE64)
    742 	    handle->s = decodeB(&p);
    743 	else if (handle->encoding == ENC_UUENCODE)
    744 	    handle->s = decodeU(&p);
    745 	handle->pos = 0;
    746     }
    747 
    748     if (len > handle->s->length - handle->pos)
    749 	len = handle->s->length - handle->pos;
    750 
    751     bcopy(&handle->s->ptr[handle->pos], buf, len);
    752     handle->pos += len;
    753     return len;
    754 }