summaryrefslogtreecommitdiff
path: root/libmailutils/message_stream.c
diff options
context:
space:
mode:
Diffstat (limited to 'libmailutils/message_stream.c')
-rw-r--r--libmailutils/message_stream.c438
1 files changed, 438 insertions, 0 deletions
diff --git a/libmailutils/message_stream.c b/libmailutils/message_stream.c
new file mode 100644
index 000000000..cfcd2ddbe
--- /dev/null
+++ b/libmailutils/message_stream.c
@@ -0,0 +1,438 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 1999, 2000, 2001, 2002, 2005, 2006, 2007, 2009, 2010
+ Free Software Foundation, Inc.
+
+ GNU Mailutils is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GNU Mailutils 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Mailutils; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02110-1301 USA */
+
+/* This file implements an MH draftfile stream: a read-only stream used
+ to transparently pass MH draftfiles to mailers. The only difference
+ between the usual RFC822 and MH draft is that the latter allows to use
+ a string of dashes to separate the headers from the body. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+
+#include <mailutils/types.h>
+#include <mailutils/address.h>
+#include <mailutils/alloc.h>
+#include <mailutils/envelope.h>
+#include <mailutils/message.h>
+#include <mailutils/header.h>
+#include <mailutils/body.h>
+#include <mailutils/stream.h>
+#include <mailutils/mutil.h>
+#include <mailutils/errno.h>
+#include <mailutils/error.h>
+#include <mailutils/cctype.h>
+#include <mailutils/cstr.h>
+#include <mailutils/sys/message_stream.h>
+
+
+static int
+_env_msg_date (mu_envelope_t envelope, char *buf, size_t len, size_t *pnwrite)
+{
+ struct _mu_message_stream *str = mu_envelope_get_owner (envelope);
+
+ if (!str || !str->date)
+ return EINVAL;
+ if (buf)
+ {
+ strncpy (buf, str->date, len);
+ buf[len-1] = 0;
+ if (pnwrite)
+ *pnwrite = len;
+ }
+ else if (!pnwrite)
+ return EINVAL;
+ else
+ *pnwrite = strlen (str->date);
+ return 0;
+}
+
+static int
+_env_msg_sender (mu_envelope_t envelope, char *buf, size_t len,
+ size_t *pnwrite)
+{
+ struct _mu_message_stream *str = mu_envelope_get_owner (envelope);
+
+ if (!str || !str->from)
+ return EINVAL;
+ if (buf)
+ {
+ strncpy (buf, str->from, len);
+ buf[len-1] = 0;
+ if (pnwrite)
+ *pnwrite = len;
+ }
+ else if (!pnwrite)
+ return EINVAL;
+ else
+ *pnwrite = strlen (str->from);
+
+ return 0;
+}
+
+
+static int
+_message_read (mu_stream_t stream, char *optr, size_t osize, size_t *nbytes)
+{
+ int rc;
+ struct _mu_message_stream *s = (struct _mu_message_stream*) stream;
+ mu_off_t offset = s->offset + s->envelope_length;
+ size_t rsize;
+
+ if (offset < s->mark_offset)
+ {
+ if (offset + osize >= s->mark_offset)
+ osize = s->mark_offset - offset;
+ }
+ else
+ offset += s->mark_length;
+ /* FIXME: Seeking each time before read is awkward. The streamref
+ should be modified to take care of it */
+ rc = mu_stream_seek (s->transport, offset, MU_SEEK_SET, NULL);
+ if (rc == 0)
+ rc = mu_stream_read (s->transport, optr, osize, &rsize);
+ if (rc == 0)
+ {
+ s->offset += rsize;
+ if (nbytes)
+ *nbytes = rsize;
+ }
+ else
+ s->stream.last_err = rc;
+ return rc;
+}
+
+static int
+_message_size (mu_stream_t stream, mu_off_t *psize)
+{
+ struct _mu_message_stream *s = (struct _mu_message_stream*) stream;
+ int rc = mu_stream_size (s->transport, psize);
+
+ if (rc == 0)
+ *psize -= s->envelope_length + s->mark_length;
+ return rc;
+}
+
+static int
+scan_stream (mu_stream_t stream)
+{
+ char *from = NULL;
+ char *env_from = NULL;
+ char *env_date = NULL;
+ int rc;
+ char *buffer = NULL;
+ size_t bufsize = 0;
+ size_t len;
+ mu_off_t body_start, body_end;
+ struct _mu_message_stream *str = (struct _mu_message_stream *) stream;
+
+ if (str->envelope)
+ {
+ char *s = str->envelope + 5;
+ char *p = strchr (s, ' ');
+ size_t len;
+
+ if (p)
+ {
+ len = p - s;
+ env_from = mu_alloc (len + 1);
+ if (!env_from)
+ return ENOMEM;
+ memcpy (env_from, s, len);
+ env_from[len] = 0;
+ env_date = mu_strdup (p + 1);
+ if (!env_date)
+ {
+ free (env_from);
+ return ENOMEM;
+ }
+ }
+ }
+
+ rc = mu_stream_seek (stream, 0, MU_SEEK_SET, NULL);
+ if (rc)
+ return rc;
+ while ((rc = mu_stream_getline (stream, &buffer, &bufsize, &len)) == 0
+ && len > 0)
+ {
+ if (buffer[0] == '\n')
+ break;
+
+ if (!env_from || !env_date)
+ {
+ if (!from && mu_c_strncasecmp (buffer, MU_HEADER_FROM,
+ sizeof (MU_HEADER_FROM) - 1) == 0)
+
+ from = mu_strdup (mu_str_skip_class (buffer +
+ sizeof (MU_HEADER_FROM),
+ MU_CTYPE_SPACE));
+ else if (!env_from
+ && mu_c_strncasecmp (buffer, MU_HEADER_ENV_SENDER,
+ sizeof (MU_HEADER_ENV_SENDER) - 1) == 0)
+ env_from = mu_strdup (mu_str_skip_class (buffer +
+ sizeof (MU_HEADER_ENV_SENDER),
+ MU_CTYPE_SPACE));
+ else if (!env_date
+ && mu_c_strncasecmp (buffer, MU_HEADER_ENV_DATE,
+ sizeof (MU_HEADER_ENV_DATE) - 1) == 0)
+ env_date = mu_strdup (mu_str_skip_class (buffer +
+ sizeof (MU_HEADER_ENV_DATE),
+ MU_CTYPE_SPACE));
+ }
+ }
+
+ free (buffer);
+
+ rc = mu_stream_seek (stream, 0, MU_SEEK_CUR, &body_start);
+ if (rc)
+ return rc;
+ else
+ body_start++;
+ mu_stream_size (stream, &body_end);
+
+ if (!env_from)
+ {
+ if (from)
+ {
+ mu_address_t addr;
+
+ mu_address_create (&addr, from);
+ if (!addr
+ || mu_address_aget_email (addr, 1, &env_from))
+ env_from = mu_strdup ("GNU-Mailutils");
+ mu_address_destroy (&addr);
+ }
+ else
+ env_from = mu_strdup ("GNU-MH");
+ }
+ free (from);
+
+ if (!env_date)
+ {
+ struct tm *tm;
+ time_t t;
+ char date[80]; /* FIXME: This size is way too big */
+
+ time(&t);
+ tm = gmtime(&t);
+ mu_strftime (date, sizeof (date), "%a %b %e %H:%M:%S %Y", tm);
+ env_date = strdup (date);
+ }
+
+ str->from = env_from;
+ str->date = env_date;
+
+ str->body_start = body_start;
+ str->body_end = body_end;
+
+ return 0;
+}
+
+static int
+_message_open (mu_stream_t stream)
+{
+ struct _mu_message_stream *s = (struct _mu_message_stream*) stream;
+ size_t offset, len;
+ char *buffer = NULL;
+ size_t bufsize = 0;
+ int rc;
+
+ offset = 0;
+ mu_stream_seek (s->transport, 0, MU_SEEK_SET, NULL);
+ while ((rc = mu_stream_getline (s->transport, &buffer, &bufsize,
+ &len)) == 0
+ && len > 0)
+ {
+ if (offset == 0 && memcmp (buffer, "From ", 5) == 0)
+ {
+ s->envelope_length = len;
+ s->envelope = mu_strdup (buffer);
+ if (!s->envelope)
+ return ENOMEM;
+ s->envelope[len - 1] = 0;
+ }
+ else if (mu_mh_delim (buffer))
+ {
+ s->mark_offset = offset;
+ s->mark_length = len - 1; /* do not count the terminating newline */
+ break;
+ }
+
+ offset += len;
+ }
+ free (buffer);
+
+ return scan_stream (s->transport);
+}
+
+static int
+_message_close (mu_stream_t stream)
+{
+ struct _mu_message_stream *s = (struct _mu_message_stream*) stream;
+ return s->stream.last_err = mu_stream_close (s->transport);
+}
+
+static void
+_message_done (mu_stream_t stream)
+{
+ struct _mu_message_stream *s = (struct _mu_message_stream*) stream;
+
+ free (s->envelope);
+ free (s->date);
+ free (s->from);
+ mu_stream_destroy (&s->transport);
+}
+
+static int
+_message_seek (struct _mu_stream *stream, mu_off_t off, mu_off_t *presult)
+{
+ struct _mu_message_stream *s = (struct _mu_message_stream*) stream;
+ mu_off_t size;
+
+ mu_stream_size (stream, &size);
+ if (off < 0 || off >= size)
+ return ESPIPE;
+ s->offset = off;
+ *presult = off;
+ return 0;
+}
+
+const char *
+_message_error_string (struct _mu_stream *stream, int rc)
+{
+ struct _mu_message_stream *str = (struct _mu_message_stream*) stream;
+ return mu_stream_strerror (str->transport, rc);
+}
+
+int
+mu_message_stream_create (mu_stream_t *stream, mu_stream_t src, int flags)
+{
+ struct _mu_message_stream *s;
+ int sflag;
+ int rc;
+
+ mu_stream_get_flags (src, &sflag);
+ sflag &= MU_STREAM_SEEK;
+
+ if (!flags)
+ flags = MU_STREAM_READ;
+ if (flags & (MU_STREAM_WRITE|MU_STREAM_CREAT|MU_STREAM_APPEND))
+ return EINVAL;
+ s = (struct _mu_message_stream *) _mu_stream_create (sizeof (*s),
+ flags | sflag);
+ if (!s)
+ return ENOMEM;
+
+ rc = mu_streamref_create (&s->transport, src);
+ if (rc)
+ {
+ free (s);
+ return rc;
+ }
+ s->stream.open = _message_open;
+ s->stream.close = _message_close;
+ s->stream.done = _message_done;
+ s->stream.read = _message_read;
+ s->stream.size = _message_size;
+ s->stream.seek = _message_seek;
+ s->stream.error_string = _message_error_string;
+ return 0;
+}
+
+
+/* *************************** MH draft message **************************** */
+
+
+
+static int
+_body_obj_size (mu_body_t body, size_t *size)
+{
+ mu_message_t msg = mu_body_get_owner (body);
+ struct _mu_message_stream *str = mu_message_get_owner (msg);
+
+ if (size)
+ *size = str->body_end - str->body_start;
+ return 0;
+}
+
+
+
+int
+mu_stream_to_message (mu_stream_t instream, mu_message_t *pmsg)
+{
+ mu_envelope_t env;
+ mu_message_t msg;
+ mu_body_t body;
+ mu_stream_t bstream;
+ mu_stream_t draftstream;
+ int rc;
+ struct _mu_message_stream *sp;
+
+ /* FIXME: Perhaps MU_STREAM_NO_CLOSE is needed */
+ if ((rc = mu_message_stream_create (&draftstream, instream, 0)))
+ return rc;
+
+ if ((rc = mu_stream_open (draftstream)))
+ {
+ mu_stream_destroy (&draftstream);
+ return rc;
+ }
+
+ if ((rc = mu_message_create (&msg, draftstream)))
+ {
+ mu_stream_destroy (&draftstream);
+ return rc;
+ }
+
+ mu_message_set_stream (msg, draftstream, draftstream);
+
+ if ((rc = mu_envelope_create (&env, draftstream)))
+ {
+ mu_message_destroy (&msg, draftstream);
+ mu_stream_destroy (&draftstream);
+ return rc;
+ }
+
+ mu_envelope_set_date (env, _env_msg_date, msg);
+ mu_envelope_set_sender (env, _env_msg_sender, msg);
+ mu_message_set_envelope (msg, env, draftstream);
+
+ mu_body_create (&body, msg);
+ /* FIXME: It would be cleaner to use ioctl here */
+ sp = (struct _mu_message_stream *) draftstream;
+ rc = mu_streamref_create_abridged (&bstream, instream,
+ sp->body_start, sp->body_end);
+ if (rc)
+ {
+ mu_body_destroy (&body, msg);
+ mu_message_destroy (&msg, draftstream);
+ mu_stream_destroy (&draftstream);
+ return rc;
+ }
+
+ mu_body_set_stream (body, bstream, msg);
+ mu_body_set_size (body, _body_obj_size, msg);
+ mu_message_set_body (msg, body, draftstream);
+
+ *pmsg = msg;
+ return 0;
+}

Return to:

Send suggestions and report system problems to the System administrator.