diff options
Diffstat (limited to 'libmailutils/xscript-stream.c')
-rw-r--r-- | libmailutils/xscript-stream.c | 437 |
1 files changed, 437 insertions, 0 deletions
diff --git a/libmailutils/xscript-stream.c b/libmailutils/xscript-stream.c new file mode 100644 index 000000000..02fafa19e --- /dev/null +++ b/libmailutils/xscript-stream.c @@ -0,0 +1,437 @@ +/* GNU Mailutils -- a suite of utilities for electronic mail + Copyright (C) 1999, 2000, 2001, 2002, 2004, + 2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include <mailutils/types.h> +#include <mailutils/alloc.h> +#include <mailutils/errno.h> + +#include <mailutils/nls.h> +#include <mailutils/stream.h> +#include <mailutils/sys/stream.h> +#include <mailutils/sys/xscript-stream.h> +#include <mailutils/cctype.h> +#include <mailutils/cstr.h> + +/* A "transcript stream" transparently writes data to and reads data from + an underlying transport stream, writing each lineful of data to a "log + stream". Writes to log stream are prefixed with a string indicating + direction of the data (read/write). Default prefixes are those used in + RFCs -- "S: ", for data written ("Server"), and "C: ", for data read + ("Client"). */ + +#define TRANS_READ 0x1 +#define TRANS_WRITE 0x2 +#define TRANS_DISABLED 0x4 +#define FLAG_TO_PFX(c) ((c) - 1) + +static int +word_match (const char *buf, size_t len, int n, const char *word, + size_t *pos) +{ + size_t i = 0; + size_t wl = strlen (word); + + for (;; n--) + { + /* Skip whitespace separator */ + for (; i < len && mu_isspace (buf[i]); i++) + ; + + if (n == 0) + break; + + /* Skip the argument */ + if (buf[i] == '"') + { + for (i++; i < len && buf[i] != '"'; i++) + if (buf[i] == '\'') + i++; + } + else + { + for (; i < len && !mu_isspace (buf[i]); i++) + ; + } + } + + if (i + wl <= len && + mu_c_strncasecmp (buf + i, word, wl) == 0 && + mu_isblank (buf[i + wl])) + { + *pos = i + wl; + return 1; + } + + return 0; +} + +static void +print_transcript (struct _mu_xscript_stream *str, int flag, + const char *buf, size_t size) +{ + while (size) + { + const char *p; + size_t len; + + if (str->flags & flag) + { + mu_stream_write (str->logstr, + str->prefix[FLAG_TO_PFX(flag)], + strlen (str->prefix[FLAG_TO_PFX (flag)]), + NULL); + str->flags &= ~(flag | TRANS_DISABLED); + } + + if (str->flags & TRANS_DISABLED) + return; + + if (str->level == MU_XSCRIPT_PAYLOAD) + { + mu_stream_printf (str->logstr, "(data...)\n"); + str->flags |= TRANS_DISABLED; + return; + } + + p = memchr (buf, '\n', size); + if (p) + { + len = p - buf; + if (p > buf && p[-1] == '\r') + len--; + + if (str->level == MU_XSCRIPT_SECURE) + { + size_t i; + + if (word_match (buf, len, 0, "PASS", &i)) + mu_stream_printf (str->logstr, "PASS ***"); + else if (word_match (buf, len, 1, "LOGIN", &i)) + { + /* Skip the whitespace separator */ + for (; i < len && mu_isspace (buf[i]); i++) + ; + /* Skip the first argument (presumably the user name) */ + if (buf[i] == '"') + { + for (i++; i < len && buf[i] != '"'; i++) + if (buf[i] == '\'') + i++; + } + else + { + for (; i < len && !mu_isspace (buf[i]); i++) + ; + } + mu_stream_write (str->logstr, buf, i, NULL); + mu_stream_write (str->logstr, " \"***\"", 6, NULL); + } + else + mu_stream_write (str->logstr, buf, len, NULL); + } + else + mu_stream_write (str->logstr, buf, len, NULL); + mu_stream_write (str->logstr, "\n", 1, NULL); + str->flags |= flag; + + len = p - buf + 1; + buf = p + 1; + size -= len; + } + else + { + mu_stream_write (str->logstr, buf, size, NULL); + break; + } + } +} + +static void +_xscript_event_cb (mu_stream_t str, int ev, unsigned long size, void *ptr) +{ + struct _mu_xscript_stream *sp = (struct _mu_xscript_stream *)str; + + switch (ev) + { + case _MU_STR_EVENT_FILLBUF: + print_transcript (sp, TRANS_READ, ptr, size); + break; + + case _MU_STR_EVENT_FLUSHBUF: + print_transcript (sp, TRANS_WRITE, ptr, size); + } +} + +static int +_xscript_read (struct _mu_stream *str, char *buf, size_t bufsize, + size_t *pnread) +{ + struct _mu_xscript_stream *sp = (struct _mu_xscript_stream *)str; + return mu_stream_read (sp->transport, buf, bufsize, pnread); +} + +static int +_xscript_readdelim (struct _mu_stream *str, char *buf, size_t bufsize, + int delim, size_t *pnread) +{ + struct _mu_xscript_stream *sp = (struct _mu_xscript_stream *)str; + return mu_stream_readdelim (sp->transport, buf, bufsize, delim, pnread); +} + +static int +_xscript_write (struct _mu_stream *str, const char *buf, size_t bufsize, + size_t *pnwrite) +{ + struct _mu_xscript_stream *sp = (struct _mu_xscript_stream *)str; + return mu_stream_write (sp->transport, buf, bufsize, pnwrite); +} + +static int +_xscript_flush (struct _mu_stream *str) +{ + struct _mu_xscript_stream *sp = (struct _mu_xscript_stream *)str; + return mu_stream_flush (sp->transport); +} + +static int +_xscript_open (struct _mu_stream *str) +{ + struct _mu_xscript_stream *sp = (struct _mu_xscript_stream *)str; + return mu_stream_open (sp->transport); +} + +static int +_xscript_close (struct _mu_stream *str) +{ + struct _mu_xscript_stream *sp = (struct _mu_xscript_stream *)str; + return mu_stream_close (sp->transport); +} + +static void +_xscript_done (struct _mu_stream *str) +{ + struct _mu_xscript_stream *sp = (struct _mu_xscript_stream *)str; + free (sp->prefix[0]); + free (sp->prefix[1]); + mu_stream_unref (sp->transport); + mu_stream_unref (sp->logstr); +} + +static int +_xscript_seek (struct _mu_stream *str, mu_off_t off, mu_off_t *ppos) +{ + struct _mu_xscript_stream *sp = (struct _mu_xscript_stream *)str; + return mu_stream_seek (sp->transport, off, MU_SEEK_SET, ppos); +} + +static int +_xscript_size (struct _mu_stream *str, mu_off_t *psize) +{ + struct _mu_xscript_stream *sp = (struct _mu_xscript_stream *)str; + return mu_stream_size (sp->transport, psize); +} + +static int +_xscript_ctl (struct _mu_stream *str, int op, void *arg) +{ + struct _mu_xscript_stream *sp = (struct _mu_xscript_stream *)str; + mu_transport_t *ptrans; + int status = 0; + + switch (op) + { + case MU_IOCTL_GET_TRANSPORT: + if (!arg) + return EINVAL; + ptrans = arg; + ptrans[0] = (mu_transport_t) sp->transport; + ptrans[1] = (mu_transport_t) sp->logstr; + break; + + case MU_IOCTL_SET_TRANSPORT: + if (!arg) + return EINVAL; + ptrans = arg; + if (ptrans[0]) + sp->transport = (mu_stream_t) ptrans[0]; + if (ptrans[1]) + sp->logstr = (mu_stream_t) ptrans[1]; + break; + + case MU_IOCTL_SWAP_STREAM: + if (!arg) + return EINVAL; + if (!sp->transport) + status = ENOSYS; + else + status = mu_stream_ioctl (sp->transport, op, arg); + if (status == EINVAL || status == ENOSYS) + { + mu_stream_t *pstr = arg; + mu_stream_t tmp; + + if (pstr[0] != pstr[1]) + return EINVAL; /* FIXME */ + tmp = pstr[0]; + pstr[0] = sp->transport; + pstr[1] = sp->transport; + sp->transport = tmp; + if (!(str->flags & MU_STREAM_AUTOCLOSE)) + { + if (pstr[0]) + mu_stream_unref (pstr[0]); + if (tmp) + mu_stream_ref (tmp); + } + if (tmp) + mu_stream_ref (tmp); + status = 0; + } + break; + + case MU_IOCTL_GET_TRANSPORT_BUFFER: + case MU_IOCTL_SET_TRANSPORT_BUFFER: + { + if (!sp->transport) + return EINVAL; + return mu_stream_ioctl (sp->transport, op, arg); + } + + case MU_IOCTL_LEVEL: + if (!arg) + return EINVAL; + else + { + int oldlev = sp->level; + sp->level = *(int*)arg; + sp->flags = TRANS_READ | TRANS_WRITE; + *(int*)arg = oldlev; + } + break; + + default: + return mu_stream_ioctl (sp->transport, op, arg); + } + return status; +} + +static int +_xscript_wait (struct _mu_stream *str, int *pflags, struct timeval *tvp) +{ + struct _mu_xscript_stream *sp = (struct _mu_xscript_stream *)str; + return mu_stream_wait (sp->transport, pflags, tvp); +} + +static int +_xscript_truncate (struct _mu_stream *str, mu_off_t size) +{ + struct _mu_xscript_stream *sp = (struct _mu_xscript_stream *)str; + return mu_stream_truncate (sp->transport, size); +} + +static int +_xscript_shutdown (struct _mu_stream *str, int how) +{ + struct _mu_xscript_stream *sp = (struct _mu_xscript_stream *)str; + return mu_stream_shutdown (sp->transport, how); +} + +static const char * +_xscript_error_string (struct _mu_stream *str, int rc) +{ + struct _mu_xscript_stream *sp = (struct _mu_xscript_stream *)str; + const char *p = mu_stream_strerror (sp->transport, rc); + if (!p) + p = mu_strerror (rc); + return p; +} + +const char *default_prefix[2] = { + "C: ", "S: " +}; + +int +mu_xscript_stream_create(mu_stream_t *pref, mu_stream_t transport, + mu_stream_t logstr, + const char *prefix[]) +{ + int flags; + struct _mu_xscript_stream *sp; + + mu_stream_get_flags (transport, &flags); + sp = (struct _mu_xscript_stream *) _mu_stream_create (sizeof (*sp), flags); + if (!sp) + return ENOMEM; + + sp->stream.read = _xscript_read; + if (transport->readdelim) + sp->stream.readdelim = _xscript_readdelim; + sp->stream.write = _xscript_write; + sp->stream.flush = _xscript_flush; + sp->stream.open = _xscript_open; + sp->stream.close = _xscript_close; + sp->stream.done = _xscript_done; + sp->stream.seek = _xscript_seek; + sp->stream.size = _xscript_size; + sp->stream.ctl = _xscript_ctl; + sp->stream.wait = _xscript_wait; + sp->stream.truncate = _xscript_truncate; + sp->stream.shutdown = _xscript_shutdown; + sp->stream.error_string = _xscript_error_string; + sp->stream.event_cb = _xscript_event_cb; + sp->stream.event_mask = _MU_STR_EVMASK(_MU_STR_EVENT_FILLBUF) | + _MU_STR_EVMASK(_MU_STR_EVENT_FLUSHBUF); + if (!(flags & MU_STREAM_AUTOCLOSE)) + { + mu_stream_ref (transport); + mu_stream_ref (logstr); + } + sp->transport = transport; + sp->logstr = logstr; + + sp->flags = TRANS_READ | TRANS_WRITE; + if (prefix) + { + sp->prefix[0] = strdup(prefix[0] ? prefix[0] : default_prefix[0]); + sp->prefix[1] = strdup(prefix[1] ? prefix[1] : default_prefix[1]); + } + else + { + sp->prefix[0] = strdup(default_prefix[0]); + sp->prefix[1] = strdup(default_prefix[1]); + } + + if (sp->prefix[0] == NULL || sp->prefix[1] == 0) + { + free (sp->prefix[0]); + free (sp->prefix[1]); + free (sp); + return ENOMEM; + } + mu_stream_set_buffer ((mu_stream_t) sp, mu_buffer_line, 1024); + *pref = (mu_stream_t) sp; + return 0; +} + + |