490 lines
10 KiB
C
490 lines
10 KiB
C
/* $Id: istream.c,v 1.4 2001/11/16 22:02:00 ukai Exp $ */
|
|
#include "fm.h"
|
|
#include "istream.h"
|
|
#include <signal.h>
|
|
|
|
#define uchar unsigned char
|
|
|
|
#define STREAM_BUF_SIZE 8192
|
|
#define SSL_BUF_SIZE 1536
|
|
|
|
#define MUST_BE_UPDATED(bs) ((bs)->stream.cur==(bs)->stream.next)
|
|
|
|
#define POP_CHAR(bs) ((bs)->iseos?'\0':(bs)->stream.buf[(bs)->stream.cur++])
|
|
|
|
static void basic_close(int *handle);
|
|
static int basic_read(int *handle, char *buf, int len);
|
|
|
|
static void file_close(struct file_handle *handle);
|
|
static int file_read(struct file_handle *handle, char *buf, int len);
|
|
|
|
static int str_read(Str handle, char *buf, int len);
|
|
|
|
#ifdef USE_SSL
|
|
static void ssl_close(struct ssl_handle *handle);
|
|
static int ssl_read(struct ssl_handle *handle, char *buf, int len);
|
|
#endif
|
|
|
|
static int ens_read(struct ens_handle *handle, char *buf, int len);
|
|
static void ens_close(struct ens_handle *handle);
|
|
|
|
static void
|
|
do_update(BaseStream base)
|
|
{
|
|
int len;
|
|
base->stream.cur = base->stream.next = 0;
|
|
len = base->read(base->handle, base->stream.buf, base->stream.size);
|
|
if (len <= 0)
|
|
base->iseos = TRUE;
|
|
else
|
|
base->stream.next += len;
|
|
}
|
|
|
|
static int
|
|
buffer_read(StreamBuffer sb, char *obuf, int count)
|
|
{
|
|
int len = sb->next - sb->cur;
|
|
if (len > 0) {
|
|
if (len > count)
|
|
len = count;
|
|
bcopy((const void *)&sb->buf[sb->cur], obuf, len);
|
|
sb->cur += len;
|
|
}
|
|
return len;
|
|
}
|
|
|
|
static void
|
|
init_buffer(BaseStream base, char *buf, int bufsize)
|
|
{
|
|
StreamBuffer sb = &base->stream;
|
|
sb->size = bufsize;
|
|
sb->cur = 0;
|
|
if (buf) {
|
|
sb->buf = (uchar *)buf;
|
|
sb->next = bufsize;
|
|
}
|
|
else {
|
|
sb->buf = NewAtom_N(uchar, bufsize);
|
|
sb->next = 0;
|
|
}
|
|
base->iseos = FALSE;
|
|
}
|
|
|
|
static void
|
|
init_base_stream(BaseStream base, int bufsize)
|
|
{
|
|
init_buffer(base, NULL, bufsize);
|
|
}
|
|
|
|
static void
|
|
init_str_stream(BaseStream base, Str s)
|
|
{
|
|
init_buffer(base, s->ptr, s->length);
|
|
}
|
|
|
|
InputStream
|
|
newInputStream(int des)
|
|
{
|
|
InputStream stream;
|
|
if (des < 0)
|
|
return NULL;
|
|
stream = New(union input_stream);
|
|
init_base_stream(&stream->base, STREAM_BUF_SIZE);
|
|
stream->base.type = IST_BASIC;
|
|
stream->base.handle = New(int);
|
|
*(int *)stream->base.handle = des;
|
|
stream->base.read = (int (*)()) basic_read;
|
|
stream->base.close = (void (*)()) basic_close;
|
|
return stream;
|
|
}
|
|
|
|
InputStream
|
|
newFileStream(FILE *f, void (*closep)())
|
|
{
|
|
InputStream stream;
|
|
if (f == NULL)
|
|
return NULL;
|
|
stream = New(union input_stream);
|
|
init_base_stream(&stream->base, STREAM_BUF_SIZE);
|
|
stream->file.type = IST_FILE;
|
|
stream->file.handle = New(struct file_handle);
|
|
stream->file.handle->f = f;
|
|
if (closep)
|
|
stream->file.handle->close = closep;
|
|
else
|
|
stream->file.handle->close = (void (*)()) fclose;
|
|
stream->file.read = (int (*)()) file_read;
|
|
stream->file.close = (void (*)()) file_close;
|
|
return stream;
|
|
}
|
|
|
|
InputStream
|
|
newStrStream(Str s)
|
|
{
|
|
InputStream stream;
|
|
if (s == NULL)
|
|
return NULL;
|
|
stream = New(union input_stream);
|
|
init_str_stream(&stream->base, s);
|
|
stream->str.type = IST_STR;
|
|
stream->str.handle = s;
|
|
stream->str.read = (int (*)()) str_read;
|
|
stream->str.close = NULL;
|
|
return stream;
|
|
}
|
|
|
|
#ifdef USE_SSL
|
|
InputStream
|
|
newSSLStream(SSL *ssl, int sock)
|
|
{
|
|
InputStream stream;
|
|
if (sock < 0)
|
|
return NULL;
|
|
stream = New(union input_stream);
|
|
init_base_stream(&stream->base, SSL_BUF_SIZE);
|
|
stream->ssl.type = IST_SSL;
|
|
stream->ssl.handle = New(struct ssl_handle);
|
|
stream->ssl.handle->ssl = ssl;
|
|
stream->ssl.handle->sock = sock;
|
|
stream->ssl.read = (int (*)()) ssl_read;
|
|
stream->ssl.close = (void (*)()) ssl_close;
|
|
return stream;
|
|
}
|
|
#endif
|
|
|
|
InputStream
|
|
newEncodedStream(InputStream is, char encoding)
|
|
{
|
|
InputStream stream;
|
|
if (is == NULL || (encoding != ENC_QUOTE && encoding != ENC_BASE64 &&
|
|
encoding != ENC_UUENCODE))
|
|
return is;
|
|
stream = New(union input_stream);
|
|
init_base_stream(&stream->base, STREAM_BUF_SIZE);
|
|
stream->ens.type = IST_ENCODED;
|
|
stream->ens.handle = New(struct ens_handle);
|
|
stream->ens.handle->is = is;
|
|
stream->ens.handle->pos = 0;
|
|
stream->ens.handle->encoding = encoding;
|
|
stream->ens.handle->s = NULL;
|
|
stream->ens.read = (int (*)()) ens_read;
|
|
stream->ens.close = (void (*)()) ens_close;
|
|
return stream;
|
|
}
|
|
|
|
void
|
|
ISclose(InputStream stream)
|
|
{
|
|
MySignalHandler(*prevtrap) ();
|
|
if (stream == NULL || stream->base.close == NULL)
|
|
return;
|
|
prevtrap = signal(SIGINT, SIG_IGN);
|
|
stream->base.close(stream->base.handle);
|
|
signal(SIGINT, prevtrap);
|
|
}
|
|
|
|
int
|
|
ISgetc(InputStream stream)
|
|
{
|
|
BaseStream base;
|
|
if (stream == NULL)
|
|
return '\0';
|
|
base = &stream->base;
|
|
if (!base->iseos && MUST_BE_UPDATED(base))
|
|
do_update(base);
|
|
return POP_CHAR(base);
|
|
}
|
|
|
|
int
|
|
ISundogetc(InputStream stream)
|
|
{
|
|
StreamBuffer sb;
|
|
if (stream == NULL)
|
|
return -1;
|
|
sb = &stream->base.stream;
|
|
if (sb->cur > 0) {
|
|
sb->cur--;
|
|
return 0;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
#define MARGIN_STR_SIZE 10
|
|
Str
|
|
StrISgets(InputStream stream)
|
|
{
|
|
BaseStream base;
|
|
StreamBuffer sb;
|
|
Str s = NULL;
|
|
uchar *p;
|
|
int len;
|
|
|
|
if (stream == NULL)
|
|
return '\0';
|
|
base = &stream->base;
|
|
sb = &base->stream;
|
|
|
|
while (!base->iseos) {
|
|
if (MUST_BE_UPDATED(base)) {
|
|
do_update(base);
|
|
}
|
|
else {
|
|
if (p = memchr(&sb->buf[sb->cur], '\n', sb->next - sb->cur)) {
|
|
len = p - &sb->buf[sb->cur] + 1;
|
|
if (s == NULL)
|
|
s = Strnew_size(len);
|
|
Strcat_charp_n(s, (char *)&sb->buf[sb->cur], len);
|
|
sb->cur += len;
|
|
return s;
|
|
}
|
|
else {
|
|
if (s == NULL)
|
|
s = Strnew_size(sb->next - sb->cur + MARGIN_STR_SIZE);
|
|
Strcat_charp_n(s, (char *)&sb->buf[sb->cur], sb->next - sb->cur);
|
|
sb->cur = sb->next;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (s == NULL)
|
|
return Strnew();
|
|
return s;
|
|
}
|
|
|
|
Str
|
|
StrmyISgets(InputStream stream)
|
|
{
|
|
BaseStream base;
|
|
StreamBuffer sb;
|
|
Str s = NULL;
|
|
int i, len;
|
|
|
|
if (stream == NULL)
|
|
return '\0';
|
|
base = &stream->base;
|
|
sb = &base->stream;
|
|
|
|
while (!base->iseos) {
|
|
if (MUST_BE_UPDATED(base)) {
|
|
do_update(base);
|
|
}
|
|
else {
|
|
if (s && Strlastchar(s) == '\r') {
|
|
if (sb->buf[sb->cur] == '\n')
|
|
Strcat_char(s, (char)sb->buf[sb->cur++]);
|
|
return s;
|
|
}
|
|
for (i = sb->cur;
|
|
i < sb->next && sb->buf[i] != '\n' && sb->buf[i] != '\r';
|
|
i++);
|
|
if (i < sb->next) {
|
|
len = i - sb->cur + 1;
|
|
if (s == NULL)
|
|
s = Strnew_size(len + MARGIN_STR_SIZE);
|
|
Strcat_charp_n(s, (char *)&sb->buf[sb->cur], len);
|
|
sb->cur = i + 1;
|
|
if (sb->buf[i] == '\n')
|
|
return s;
|
|
}
|
|
else {
|
|
if (s == NULL)
|
|
s = Strnew_size(sb->next - sb->cur + MARGIN_STR_SIZE);
|
|
Strcat_charp_n(s, (char *)&sb->buf[sb->cur], sb->next - sb->cur);
|
|
sb->cur = sb->next;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (s == NULL)
|
|
return Strnew();
|
|
return s;
|
|
}
|
|
|
|
int
|
|
ISread(InputStream stream, Str buf, int count)
|
|
{
|
|
int rest, len;
|
|
BaseStream base;
|
|
|
|
if (stream == NULL || (base = &stream->base)->iseos)
|
|
return 0;
|
|
|
|
len = buffer_read(&base->stream, buf->ptr, count);
|
|
rest = count - len;
|
|
if (MUST_BE_UPDATED(base)) {
|
|
len = base->read(base->handle, &buf->ptr[len], rest);
|
|
if (len <= 0) {
|
|
base->iseos = TRUE;
|
|
len = 0;
|
|
}
|
|
rest -= len;
|
|
}
|
|
Strtruncate(buf, count - rest);
|
|
if (buf->length > 0)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
ISfileno(InputStream stream)
|
|
{
|
|
if (stream == NULL)
|
|
return -1;
|
|
switch (IStype(stream)) {
|
|
case IST_BASIC:
|
|
return *(int *)stream->base.handle;
|
|
case IST_FILE:
|
|
return fileno(stream->file.handle->f);
|
|
case IST_ENCODED:
|
|
return ISfileno(stream->ens.handle->is);
|
|
default:
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
int
|
|
ISeos(InputStream stream)
|
|
{
|
|
BaseStream base = &stream->base;
|
|
if (!base->iseos && MUST_BE_UPDATED(base))
|
|
do_update(base);
|
|
return base->iseos;
|
|
}
|
|
|
|
#ifdef USE_SSL
|
|
Str
|
|
ssl_get_certificate(InputStream stream)
|
|
{
|
|
BIO *bp;
|
|
X509 *x;
|
|
char *p;
|
|
int len;
|
|
Str s;
|
|
if (stream == NULL)
|
|
return NULL;
|
|
if (IStype(stream) != IST_SSL)
|
|
return NULL;
|
|
if (stream->ssl.handle == NULL)
|
|
return NULL;
|
|
x = SSL_get_peer_certificate(stream->ssl.handle->ssl);
|
|
if (x == NULL)
|
|
return NULL;
|
|
bp = BIO_new(BIO_s_mem());
|
|
X509_print(bp, x);
|
|
len = (int)BIO_ctrl(bp, BIO_CTRL_INFO,0,(char *)&p);
|
|
s = Strnew_charp_n(p, len);
|
|
BIO_free_all(bp);
|
|
return s;
|
|
}
|
|
#endif
|
|
|
|
/* Raw level input stream functions */
|
|
|
|
static void
|
|
basic_close(int *handle)
|
|
{
|
|
close(*(int *)handle);
|
|
}
|
|
|
|
static int
|
|
basic_read(int *handle, char *buf, int len)
|
|
{
|
|
return read(*(int *)handle, buf, len);
|
|
}
|
|
|
|
static void
|
|
file_close(struct file_handle *handle)
|
|
{
|
|
handle->close(handle->f);
|
|
}
|
|
|
|
static int
|
|
file_read(struct file_handle *handle, char *buf, int len)
|
|
{
|
|
return fread(buf, 1, len, handle->f);
|
|
}
|
|
|
|
static int
|
|
str_read(Str handle, char *buf, int len)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
#ifdef USE_SSL
|
|
static void
|
|
ssl_close(struct ssl_handle *handle)
|
|
{
|
|
close(handle->sock);
|
|
if (handle->ssl)
|
|
SSL_free(handle->ssl);
|
|
}
|
|
|
|
static int
|
|
ssl_read(struct ssl_handle *handle, char *buf, int len)
|
|
{
|
|
int status;
|
|
if (handle->ssl) {
|
|
#ifdef USE_SSL_VERIFY
|
|
for (;;) {
|
|
status = SSL_read(handle->ssl, buf, len);
|
|
if (status > 0)
|
|
break;
|
|
switch (SSL_get_error(handle->ssl, status)) {
|
|
case SSL_ERROR_WANT_READ:
|
|
case SSL_ERROR_WANT_WRITE: /* reads can trigger write errors; see SSL_get_error(3) */
|
|
continue;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
#else /* if !defined(USE_SSL_VERIFY) */
|
|
status = SSL_read(handle->ssl, buf, len);
|
|
#endif /* !defined(USE_SSL_VERIFY) */
|
|
}
|
|
else
|
|
status = read(handle->sock, buf, len);
|
|
return status;
|
|
}
|
|
#endif /* USE_SSL */
|
|
|
|
static void
|
|
ens_close(struct ens_handle *handle)
|
|
{
|
|
ISclose(handle->is);
|
|
}
|
|
|
|
static int
|
|
ens_read(struct ens_handle *handle, char *buf, int len)
|
|
{
|
|
if (handle->s == NULL || handle->pos == handle->s->length) {
|
|
char *p;
|
|
handle->s = StrmyISgets(handle->is);
|
|
if (handle->s->length == 0)
|
|
return 0;
|
|
cleanup_line(handle->s, PAGER_MODE);
|
|
if (handle->encoding == ENC_BASE64)
|
|
Strchop(handle->s);
|
|
else if (handle->encoding == ENC_UUENCODE) {
|
|
if (! strncmp(handle->s->ptr, "begin", 5))
|
|
handle->s = StrmyISgets(handle->is);
|
|
Strchop(handle->s);
|
|
}
|
|
p = handle->s->ptr;
|
|
if (handle->encoding == ENC_QUOTE)
|
|
handle->s = decodeQP(&p);
|
|
else if (handle->encoding == ENC_BASE64)
|
|
handle->s = decodeB(&p);
|
|
else if (handle->encoding == ENC_UUENCODE)
|
|
handle->s = decodeU(&p);
|
|
handle->pos = 0;
|
|
}
|
|
|
|
if (len > handle->s->length - handle->pos)
|
|
len = handle->s->length - handle->pos;
|
|
|
|
bcopy(&handle->s->ptr[handle->pos], buf, len);
|
|
handle->pos += len;
|
|
return len;
|
|
}
|