diff options
Diffstat (limited to 'libmailutils/mailbox/message.c')
-rw-r--r-- | libmailutils/mailbox/message.c | 1294 |
1 files changed, 1294 insertions, 0 deletions
diff --git a/libmailutils/mailbox/message.c b/libmailutils/mailbox/message.c new file mode 100644 index 000000000..caeba8f9c --- /dev/null +++ b/libmailutils/mailbox/message.c @@ -0,0 +1,1294 @@ +/* GNU Mailutils -- a suite of utilities for electronic mail + Copyright (C) 1999, 2000, 2001, 2002, 2004, 2005, 2006, 2007, 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 of the License, 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 this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <time.h> +#include <unistd.h> +#include <string.h> +#include <pwd.h> + +#include <mailutils/cctype.h> +#include <mailutils/address.h> +#include <mailutils/attribute.h> +#include <mailutils/auth.h> +#include <mailutils/body.h> +#include <mailutils/debug.h> +#include <mailutils/envelope.h> +#include <mailutils/errno.h> +#include <mailutils/folder.h> +#include <mailutils/header.h> +#include <mailutils/mailbox.h> +#include <mailutils/mutil.h> +#include <mailutils/observer.h> +#include <mailutils/stream.h> +#include <mailutils/mu_auth.h> +#include <mailutils/nls.h> +#include <mailutils/md5.h> +#include <mailutils/io.h> + +#include <mailutils/sys/message.h> +#include <mailutils/sys/stream.h> + +#define MESSAGE_MODIFIED 0x10000 +#define MESSAGE_INTERNAL_STREAM 0x20000 + + +/* Message stream */ + +enum _message_stream_state + { + _mss_init, + _mss_header, + _mss_body, + _mss_eof + }; + +struct _mu_message_stream +{ + struct _mu_stream stream; + mu_message_t msg; + enum _message_stream_state state; + mu_stream_t transport; + mu_off_t limit; +}; + +static int +_check_stream_state (struct _mu_message_stream *str) +{ + int rc = 0; + + if (str->transport && mu_stream_eof (str->transport)) + mu_stream_destroy (&str->transport); + + switch (str->state) + { + case _mss_init: + if (!str->transport) + { + rc = mu_header_get_streamref (str->msg->header, &str->transport); + if (rc == 0) + { + str->state = _mss_header; + rc = mu_stream_seek (str->transport, 0, MU_SEEK_SET, NULL); + } + } + break; + + case _mss_header: + if (!str->transport) + { + rc = mu_body_get_streamref (str->msg->body, &str->transport); + if (rc == 0) + { + str->state = _mss_body; + rc = mu_stream_seek (str->transport, 0, MU_SEEK_SET, NULL); + } + } + break; + + case _mss_body: + if (!str->transport) + str->state = _mss_eof; + case _mss_eof: + break; + } + return rc; +} + +static void +_message_stream_done (struct _mu_stream *str) +{ + struct _mu_message_stream *sp = (struct _mu_message_stream *)str; + mu_stream_destroy (&sp->transport); +} + +static int +_message_stream_flush (struct _mu_stream *str) +{ + struct _mu_message_stream *sp = (struct _mu_message_stream *)str; + int rc = _check_stream_state (sp); + if (rc) + return rc; + return mu_stream_flush (sp->transport); +} + +static int +_message_stream_size (struct _mu_stream *str, mu_off_t *psize) +{ + struct _mu_message_stream *sp = (struct _mu_message_stream *)str; + size_t hsize, bsize; + mu_header_size (sp->msg->header, &hsize); + mu_body_size (sp->msg->body, &bsize); + if (psize) + *psize = hsize + bsize; + return 0; +} + +static int +_message_stream_seek (struct _mu_stream *str, mu_off_t off, mu_off_t *ppos) +{ + struct _mu_message_stream *sp = (struct _mu_message_stream *)str; + size_t hsize, size; + int rc; + + rc = _check_stream_state (sp); + if (rc) + return rc; + mu_header_size (sp->msg->header, &hsize); + mu_body_size (sp->msg->body, &size); + size += hsize; + + if (off < 0 || off >= size) + return ESPIPE; + + switch (sp->state) + { + case _mss_eof: + sp->state = _mss_init; + rc = _check_stream_state (sp); + if (rc) + return rc; + /* fall through */ + case _mss_header: + if (off < hsize) + break; + mu_stream_destroy (&sp->transport); + rc = _check_stream_state (sp); + if (rc) + return rc; + /* fall through */ + case _mss_body: + if (off > hsize) + off -= hsize; + else + { + mu_stream_destroy (&sp->transport); + sp->state = _mss_init; + rc = _check_stream_state (sp); + if (rc) + return rc; + } + + break; + + default: + break; + } + rc = mu_stream_seek (sp->transport, off, MU_SEEK_SET, &off); + if (rc == 0) + { + if (sp->state == _mss_body) + off += hsize; + *ppos = off; + } + return rc; +} + +static int +_message_stream_read (struct _mu_stream *str, char *buf, size_t bufsize, + size_t *pnread) +{ + struct _mu_message_stream *sp = (struct _mu_message_stream *)str; + size_t nread = 0; + int rc; + + while (bufsize) + { + size_t n; + rc = _check_stream_state (sp); + if (rc) + break; + if (sp->state == _mss_eof) + break; + rc = mu_stream_read (sp->transport, buf, bufsize, &n); + nread += n; + buf += n; + bufsize -= n; + } + *pnread = nread; + return rc; +} + +static int +_message_stream_readdelim (struct _mu_stream *str, char *buf, size_t bufsize, + int delim, size_t *pnread) +{ + struct _mu_message_stream *sp = (struct _mu_message_stream *)str; + size_t nread = 0; + int rc; + + while (bufsize) + { + size_t n; + rc = _check_stream_state (sp); + if (rc) + break; + if (sp->state == _mss_eof) + break; + rc = mu_stream_readdelim (sp->transport, buf, bufsize, delim, &n); + if (rc || n == 0) + break; + nread += n; + buf += n; + bufsize -= n; + } + *pnread = nread; + return rc; +} + +#if 0 +static int +_message_stream_write (struct _mu_stream *str, + const char *buf, size_t bufsize, + size_t *pnwritten) +{ + struct _mu_message_stream *sp = (struct _mu_message_stream *)str; + + /* FIXME */ +} +#endif + +static int +_message_stream_create (mu_stream_t *pmsg, mu_message_t msg, int flags) +{ + struct _mu_message_stream *sp; + + sp = (struct _mu_message_stream *) _mu_stream_create (sizeof (*sp), + flags | + MU_STREAM_SEEK | + _MU_STR_OPEN); + if (!sp) + return ENOMEM; + + sp->stream.read = _message_stream_read; + sp->stream.readdelim = _message_stream_readdelim; + /* FIXME: Write is not defined */ + /* sp->stream.write = _message_stream_write;*/ + sp->stream.done = _message_stream_done; + sp->stream.flush = _message_stream_flush; + sp->stream.seek = _message_stream_seek; + sp->stream.size = _message_stream_size; + sp->state = _mss_init; + sp->msg = msg; + *pmsg = (mu_stream_t) sp; + return 0; +} + + +enum eoh_state + { + eoh_no, + eoh_maybe, + eoh_yes + }; + +/* Message header stuff */ +static enum eoh_state +string_find_eoh (enum eoh_state eoh, const char *str, size_t len, + size_t *ppos) +{ + size_t pos; + + if (eoh == eoh_maybe && *str == '\n') + { + *ppos = 0; + return eoh_yes; + } + + for (pos = 0; pos < len - 1; pos++) + if (str[pos] == '\n' && str[pos + 1] == '\n') + { + *ppos = pos + 1; + return eoh_yes; + } + + *ppos = pos + 1; + return str[pos] == '\n' ? eoh_maybe : eoh_no; +} + +#define MIN_HEADER_BUF_SIZE 2048 + +static int +_header_fill (mu_stream_t stream, char **pbuf, size_t *plen) +{ + int status = 0; + char *buffer = NULL; + size_t bufsize = 0; + char inbuf[MIN_HEADER_BUF_SIZE]; + size_t nread; + enum eoh_state eoh = eoh_no; + + status = mu_stream_seek (stream, 0, MU_SEEK_SET, NULL); + if (status) + return status; + + while (eoh != eoh_yes + && (status = mu_stream_read (stream, inbuf, sizeof (inbuf), &nread)) + == 0 + && nread) + { + char *nbuf; + size_t len; + + eoh = string_find_eoh (eoh, inbuf, nread, &len); + + nbuf = realloc (buffer, bufsize + len); + if (!nbuf) + { + status = ENOMEM; + break; + } + memcpy (nbuf + bufsize, inbuf, len); + buffer = nbuf; + bufsize += len; + } + + if (status) + free (buffer); + else + { + *pbuf = buffer; + *plen = bufsize; + } + return status; +} + +static int +message_header_fill (void *data, char **pbuf, size_t *plen) +{ + int status = 0; + mu_message_t msg = data; + mu_stream_t stream; + + status = mu_message_get_streamref (msg, &stream); + if (status == 0) + { + status = _header_fill (stream, pbuf, plen); + mu_stream_destroy (&stream); + } + return status; +} + + +/* Message envelope */ +static int +message_envelope_date (mu_envelope_t envelope, char *buf, size_t len, + size_t *pnwrite) +{ + mu_message_t msg = mu_envelope_get_owner (envelope); + time_t t; + size_t n; + + if (msg == NULL) + return EINVAL; + + /* FIXME: extract the time from "Date:". */ + + if (buf == NULL || len == 0) + { + n = MU_ENVELOPE_DATE_LENGTH; + } + else + { + char tmpbuf[MU_ENVELOPE_DATE_LENGTH+1]; + t = time (NULL); + n = mu_strftime (tmpbuf, sizeof tmpbuf, + MU_ENVELOPE_DATE_FORMAT, localtime (&t)); + n = mu_cpystr (buf, tmpbuf, len); + } + if (pnwrite) + *pnwrite = n; + return 0; +} + +static int +message_envelope_sender (mu_envelope_t envelope, char *buf, size_t len, + size_t *pnwrite) +{ + mu_message_t msg = mu_envelope_get_owner (envelope); + mu_header_t header = NULL; + size_t n = 0; + int status; + + if (msg == NULL) + return EINVAL; + + /* Can it be extracted from the From: */ + mu_message_get_header (msg, &header); + status = mu_header_get_value (header, MU_HEADER_FROM, NULL, 0, &n); + if (status == 0 && n != 0) + { + char *sender; + mu_address_t address = NULL; + sender = calloc (1, n + 1); + if (sender == NULL) + return ENOMEM; + mu_header_get_value (header, MU_HEADER_FROM, sender, n + 1, NULL); + if (mu_address_create (&address, sender) == 0) + mu_address_get_email (address, 1, buf, n + 1, pnwrite); + free (sender); + mu_address_destroy (&address); + return 0; + } + else if (status == EAGAIN) + return status; + + /* oops! We are still here */ + { + struct mu_auth_data *auth = mu_get_auth_by_uid (getuid ()); + const char *sender = auth ? auth->name : "unknown"; + n = strlen (sender); + if (buf && len > 0) + { + len--; /* One for the null. */ + n = (n < len) ? n : len; + memcpy (buf, auth->name, n); + buf[n] = '\0'; + } + if (auth) + mu_auth_data_free (auth); + } + + if (pnwrite) + *pnwrite = n; + return 0; +} + + + +/* Allocate ressources for the mu_message_t. */ +int +mu_message_create (mu_message_t *pmsg, void *owner) +{ + mu_message_t msg; + int status; + + if (pmsg == NULL) + return MU_ERR_OUT_PTR_NULL; + msg = calloc (1, sizeof (*msg)); + if (msg == NULL) + return ENOMEM; + status = mu_monitor_create (&msg->monitor, 0, msg); + if (status != 0) + { + free (msg); + return status; + } + msg->owner = owner; + msg->ref = 1; + *pmsg = msg; + return 0; +} + +void +mu_message_destroy (mu_message_t *pmsg, void *owner) +{ + if (pmsg && *pmsg) + { + mu_message_t msg = *pmsg; + mu_monitor_t monitor = msg->monitor; + int destroy_lock = 0; + + mu_monitor_wrlock (monitor); + /* Note: msg->ref may be incremented by mu_message_ref without + additional checking for its owner, therefore decrementing + it must also occur independently of the owner checking. Due + to this inconsistency ref may reach negative values, which + is very unfortunate. + + The `owner' stuff is a leftover from older mailutils versions. + There is an ongoing attempt to remove it in the stream-cleanup + branch. When it is ready, it will be merged to the HEAD and this + will finally resolve this issue. */ + if (msg->ref > 0) + msg->ref--; + if ((msg->owner && msg->owner == owner) + || (msg->owner == NULL && msg->ref <= 0)) + { + destroy_lock = 1; + /* Notify the listeners. */ + /* FIXME: to be removed since we do not support this event. */ + if (msg->observable) + { + mu_observable_notify (msg->observable, MU_EVT_MESSAGE_DESTROY, + msg); + mu_observable_destroy (&msg->observable, msg); + } + + /* Envelope. */ + if (msg->envelope) + mu_envelope_destroy (&msg->envelope, msg); + + /* Header. */ + if (msg->header) + mu_header_destroy (&msg->header); + + /* Body. */ + if (msg->body) + mu_body_destroy (&msg->body, msg); + + /* Attribute. */ + if (msg->attribute) + mu_attribute_destroy (&msg->attribute, msg); + + /* Stream. */ + if (msg->stream) + mu_stream_destroy (&msg->stream); + + /* Mime. */ + if (msg->mime) + mu_mime_destroy (&msg->mime); + + /* Loose the owner. */ + msg->owner = NULL; + + free (msg); + } + mu_monitor_unlock (monitor); + if (destroy_lock) + mu_monitor_destroy (&monitor, msg); + /* Loose the link */ + *pmsg = NULL; + } +} + +int +mu_message_create_copy (mu_message_t *to, mu_message_t from) +{ + int status = 0; + mu_stream_t fromstr = NULL; + mu_stream_t tmp = NULL; + + if (!to) + return MU_ERR_OUT_PTR_NULL; + if (!from) + return EINVAL; + + status = mu_memory_stream_create (&tmp, MU_STREAM_RDWR|MU_STREAM_SEEK); + if (status) + return status; + + status = mu_message_get_streamref (from, &fromstr); + if (status) + { + mu_stream_destroy (&tmp); + return status; + } + + status = mu_stream_copy (tmp, fromstr, 0, NULL); + if (status == 0) + { + status = mu_message_create (to, NULL); + if (status == 0) + mu_message_set_stream (*to, tmp, NULL); + } + + if (status) + mu_stream_destroy (&tmp); + mu_stream_destroy (&fromstr); + + return status; +} + +int +mu_message_ref (mu_message_t msg) +{ + if (msg) + { + mu_monitor_wrlock (msg->monitor); + msg->ref++; + mu_monitor_unlock (msg->monitor); + } + return 0; +} + +void * +mu_message_get_owner (mu_message_t msg) +{ + return (msg == NULL) ? NULL : msg->owner; +} + +int +mu_message_is_modified (mu_message_t msg) +{ + int mod = 0; + if (msg) + { + mod |= mu_header_is_modified (msg->header); + mod |= mu_attribute_is_modified (msg->attribute); + mod |= mu_body_is_modified (msg->body); + mod |= msg->flags; + } + return mod; +} + +int +mu_message_clear_modified (mu_message_t msg) +{ + if (msg) + { + if (msg->header) + mu_header_clear_modified (msg->header); + if (msg->attribute) + mu_attribute_clear_modified (msg->attribute); + if (msg->body) + mu_body_clear_modified (msg->body); + msg->flags &= ~MESSAGE_MODIFIED; + } + return 0; +} + +int +mu_message_get_mailbox (mu_message_t msg, mu_mailbox_t *pmailbox) +{ + if (msg == NULL) + return EINVAL; + if (pmailbox == NULL) + return MU_ERR_OUT_PTR_NULL; + *pmailbox = msg->mailbox; + return 0; +} + +int +mu_message_set_mailbox (mu_message_t msg, mu_mailbox_t mailbox, void *owner) +{ + if (msg == NULL) + return EINVAL; + if (msg->owner != owner) + return EACCES; + msg->mailbox = mailbox; + return 0; +} + +int +mu_message_get_header (mu_message_t msg, mu_header_t *phdr) +{ + if (msg == NULL) + return EINVAL; + if (phdr == NULL) + return MU_ERR_OUT_PTR_NULL; + + if (msg->header == NULL) + { + mu_header_t header; + int status = mu_header_create (&header, NULL, 0); + if (status != 0) + return status; + if (msg->stream) + mu_header_set_fill (header, message_header_fill, msg); + status = mu_header_size (header, &msg->orig_header_size); + if (status) + return status; + msg->header = header; + } + *phdr = msg->header; + return 0; +} + +/* Note: mu_message_set_header steals the reference to hdr */ +int +mu_message_set_header (mu_message_t msg, mu_header_t hdr, void *owner) +{ + if (msg == NULL ) + return EINVAL; + if (msg->owner != owner) + return EACCES; + if (msg->header) + mu_header_destroy (&msg->header); + msg->header = hdr; + msg->flags |= MESSAGE_MODIFIED; + return 0; +} + +int +mu_message_get_body (mu_message_t msg, mu_body_t *pbody) +{ + if (msg == NULL) + return EINVAL; + if (pbody == NULL) + return MU_ERR_OUT_PTR_NULL; + + /* Is it a floating mesg. */ + if (msg->body == NULL) + { + mu_body_t body; + int status = mu_body_create (&body, msg); + if (status != 0) + return status; + /* If a stream is already set, use it to create the body stream. */ + /* FIXME: I'm not sure if the second condition is really needed */ + if (msg->stream/* && (msg->flags & MESSAGE_INTERNAL_STREAM)*/) + { + mu_stream_t stream; + int flags = 0; + + /* FIXME: The actual mu_header_size cannot be used as offset, + because the headers might have been modified in between. */ + + mu_stream_get_flags (msg->stream, &flags); + status = mu_streamref_create_abridged (&stream, msg->stream, + msg->orig_header_size, 0); + if (status) + { + mu_body_destroy (&body, msg); + return status; + } + mu_body_set_stream (body, stream, msg); + } + msg->body = body; + } + *pbody = msg->body; + return 0; +} + +int +mu_message_set_body (mu_message_t msg, mu_body_t body, void *owner) +{ + if (msg == NULL ) + return EINVAL; + if (msg->owner != owner) + return EACCES; + /* Make sure we destroy the old if it was owned by the mesg. */ + /* FIXME: I do not know if somebody has already a ref on this ? */ + if (msg->body) + mu_body_destroy (&msg->body, msg); + msg->body = body; + msg->flags |= MESSAGE_MODIFIED; + return 0; +} + +int +mu_message_set_stream (mu_message_t msg, mu_stream_t stream, void *owner) +{ + if (msg == NULL) + return EINVAL; + if (msg->owner != owner) + return EACCES; + if (msg->stream) + mu_stream_destroy (&msg->stream); + msg->stream = stream; + msg->flags |= MESSAGE_MODIFIED; + msg->flags &= ~MESSAGE_INTERNAL_STREAM; + return 0; +} + +static int +_message_get_stream (mu_message_t msg, mu_stream_t *pstream, int ref) +{ + int status; + + if (msg == NULL) + return EINVAL; + if (pstream == NULL) + return MU_ERR_OUT_PTR_NULL; + + if (msg->stream == NULL) + { + if (msg->_get_stream) + { + status = msg->_get_stream (msg, &msg->stream); + if (status) + return status; + } + else + { + mu_header_t hdr; + mu_body_t body; + + /* FIXME: Kind of a kludge: make sure the message has header + and body initialized. */ + status = mu_message_get_header (msg, &hdr); + if (status) + return status; + status = mu_message_get_body (msg, &body); + if (status) + return status; + + status = _message_stream_create (&msg->stream, msg, MU_STREAM_RDWR); + if (status) + return status; + msg->flags |= MESSAGE_INTERNAL_STREAM; + } + } + + if (!ref) + { + *pstream = msg->stream; + return 0; + } + return mu_streamref_create (pstream, msg->stream); +} + +int +mu_message_get_stream (mu_message_t msg, mu_stream_t *pstream) +{ + /* FIXME: Deprecation warning */ + return _message_get_stream (msg, pstream, 0); +} + +int +mu_message_get_streamref (mu_message_t msg, mu_stream_t *pstream) +{ + return _message_get_stream (msg, pstream, 1); +} + +int +mu_message_set_get_stream (mu_message_t msg, + int (*_getstr) (mu_message_t, mu_stream_t *), + void *owner) +{ + if (msg == NULL) + return EINVAL; + if (msg->owner != owner) + return EACCES; + msg->_get_stream = _getstr; + return 0; +} + +int +mu_message_set_lines (mu_message_t msg, int (*_lines) + (mu_message_t, size_t *), void *owner) +{ + if (msg == NULL) + return EINVAL; + if (msg->owner != owner) + return EACCES; + msg->_lines = _lines; + return 0; +} + +int +mu_message_lines (mu_message_t msg, size_t *plines) +{ + size_t hlines, blines; + int ret = 0; + + if (msg == NULL) + return EINVAL; + /* Overload. */ + if (msg->_lines) + return msg->_lines (msg, plines); + if (plines) + { + hlines = blines = 0; + if ( ( ret = mu_header_lines (msg->header, &hlines) ) == 0 ) + ret = mu_body_lines (msg->body, &blines); + *plines = hlines + blines; + } + return ret; +} + +int +mu_message_set_size (mu_message_t msg, int (*_size) + (mu_message_t, size_t *), void *owner) +{ + if (msg == NULL) + return EINVAL; + if (msg->owner != owner) + return EACCES; + msg->_size = _size; + return 0; +} + +int +mu_message_size (mu_message_t msg, size_t *psize) +{ + size_t hsize, bsize; + int ret = 0; + + if (msg == NULL) + return EINVAL; + /* Overload ? */ + if (msg->_size) + return msg->_size (msg, psize); + if (psize) + { + mu_header_t hdr = NULL; + mu_body_t body = NULL; + + hsize = bsize = 0; + mu_message_get_header (msg, &hdr); + mu_message_get_body (msg, &body); + if ( ( ret = mu_header_size (hdr, &hsize) ) == 0 ) + ret = mu_body_size (body, &bsize); + *psize = hsize + bsize; + } + return ret; +} + +int +mu_message_get_envelope (mu_message_t msg, mu_envelope_t *penvelope) +{ + if (msg == NULL) + return EINVAL; + if (penvelope == NULL) + return MU_ERR_OUT_PTR_NULL; + + if (msg->envelope == NULL) + { + mu_envelope_t envelope; + int status = mu_envelope_create (&envelope, msg); + if (status != 0) + return status; + mu_envelope_set_sender (envelope, message_envelope_sender, msg); + mu_envelope_set_date (envelope, message_envelope_date, msg); + msg->envelope = envelope; + } + *penvelope = msg->envelope; + return 0; +} + +int +mu_message_set_envelope (mu_message_t msg, mu_envelope_t envelope, void *owner) +{ + if (msg == NULL) + return EINVAL; + if (msg->owner != owner) + return EACCES; + if (msg->envelope) + mu_envelope_destroy (&msg->envelope, msg); + msg->envelope = envelope; + msg->flags |= MESSAGE_MODIFIED; + return 0; +} + +int +mu_message_get_attribute (mu_message_t msg, mu_attribute_t *pattribute) +{ + if (msg == NULL) + return EINVAL; + if (pattribute == NULL) + return MU_ERR_OUT_PTR_NULL; + if (msg->attribute == NULL) + { + mu_attribute_t attribute; + int status = mu_attribute_create (&attribute, msg); + if (status != 0) + return status; + msg->attribute = attribute; + } + *pattribute = msg->attribute; + return 0; +} + +int +mu_message_set_attribute (mu_message_t msg, mu_attribute_t attribute, void *owner) +{ + if (msg == NULL) + return EINVAL; + if (msg->owner != owner) + return EACCES; + if (msg->attribute) + mu_attribute_destroy (&msg->attribute, owner); + msg->attribute = attribute; + msg->flags |= MESSAGE_MODIFIED; + return 0; +} + +int +mu_message_get_uid (mu_message_t msg, size_t *puid) +{ + if (msg == NULL) + return EINVAL; + if (msg->_get_uid) + return msg->_get_uid (msg, puid); + *puid = 0; + return 0; +} + +int +mu_message_get_uidl (mu_message_t msg, char *buffer, size_t buflen, + size_t *pwriten) +{ + mu_header_t header = NULL; + size_t n = 0; + int status; + + if (msg == NULL || buffer == NULL || buflen == 0) + return EINVAL; + + buffer[0] = '\0'; + /* Try the function overload if error fallback. */ + if (msg->_get_uidl) + { + status = msg->_get_uidl (msg, buffer, buflen, pwriten); + if (status == 0) + return status; + } + + /* Be compatible with Qpopper ? qppoper saves the UIDL in "X-UIDL". + We generate a chksum and save it in the header. */ + mu_message_get_header (msg, &header); + status = mu_header_get_value_unfold (header, "X-UIDL", buffer, buflen, &n); + if (status != 0 || n == 0) + { + size_t uid = 0; + struct mu_md5_ctx md5context; + mu_stream_t stream = NULL; + char buf[1024]; + unsigned char md5digest[16]; + char *tmp; + n = 0; + mu_message_get_uid (msg, &uid); + mu_message_get_streamref (msg, &stream); + mu_md5_init_ctx (&md5context); + status = mu_stream_seek (stream, 0, MU_SEEK_SET, NULL); + if (status == 0) + { + while (mu_stream_read (stream, buf, sizeof (buf), &n) == 0 + && n > 0) + mu_md5_process_bytes (buf, n, &md5context); + mu_md5_finish_ctx (&md5context, md5digest); + tmp = buf; + for (n = 0; n < 16; n++, tmp += 2) + sprintf (tmp, "%02x", md5digest[n]); + *tmp = '\0'; + /* POP3 rfc says that an UID should not be longer than 70. */ + snprintf (buf + 32, 70, ".%lu.%lu", (unsigned long)time (NULL), + (unsigned long) uid); + + mu_header_set_value (header, "X-UIDL", buf, 1); + buflen--; /* leave space for the NULL. */ + strncpy (buffer, buf, buflen)[buflen] = '\0'; + } + mu_stream_destroy (&stream); + } + return status; +} + +int +mu_message_get_qid (mu_message_t msg, mu_message_qid_t *pqid) +{ + if (msg == NULL) + return EINVAL; + if (!msg->_get_qid) + return ENOSYS; + return msg->_get_qid (msg, pqid); +} + +int +mu_message_set_qid (mu_message_t msg, + int (*_get_qid) (mu_message_t, mu_message_qid_t *), + void *owner) +{ + if (msg == NULL) + return EINVAL; + if (msg->owner != owner) + return EACCES; + msg->_get_qid = _get_qid; + return 0; +} + +int +mu_message_set_uid (mu_message_t msg, int (*_get_uid) (mu_message_t, size_t *), + void *owner) +{ + if (msg == NULL) + return EINVAL; + if (msg->owner != owner) + return EACCES; + msg->_get_uid = _get_uid; + return 0; +} + +int +mu_message_set_uidl (mu_message_t msg, + int (* _get_uidl) (mu_message_t, char *, size_t, size_t *), + void *owner) +{ + if (msg == NULL) + return EINVAL; + if (msg->owner != owner) + return EACCES; + msg->_get_uidl = _get_uidl; + return 0; +} + +int +mu_message_set_is_multipart (mu_message_t msg, + int (*_is_multipart) (mu_message_t, int *), + void *owner) +{ + if (msg == NULL) + return EINVAL; + if (msg->owner != owner) + return EACCES; + msg->_is_multipart = _is_multipart; + return 0; +} + +int +mu_message_is_multipart (mu_message_t msg, int *pmulti) +{ + if (msg && pmulti) + { + if (msg->_is_multipart) + return msg->_is_multipart (msg, pmulti); + if (msg->mime == NULL) + { + int status = mu_mime_create (&msg->mime, msg, 0); + if (status != 0) + return 0; + } + *pmulti = mu_mime_is_multipart(msg->mime); + } + return 0; +} + +int +mu_message_get_num_parts (mu_message_t msg, size_t *pparts) +{ + if (msg == NULL || pparts == NULL) + return EINVAL; + + if (msg->_get_num_parts) + return msg->_get_num_parts (msg, pparts); + + if (msg->mime == NULL) + { + int status = mu_mime_create (&msg->mime, msg, 0); + if (status != 0) + return status; + } + return mu_mime_get_num_parts (msg->mime, pparts); +} + +int +mu_message_set_get_num_parts (mu_message_t msg, + int (*_get_num_parts) (mu_message_t, size_t *), + void *owner) +{ + if (msg == NULL) + return EINVAL; + if (msg->owner != owner) + return EACCES; + msg->_get_num_parts = _get_num_parts; + return 0; +} + +int +mu_message_get_part (mu_message_t msg, size_t part, mu_message_t *pmsg) +{ + if (msg == NULL || pmsg == NULL) + return EINVAL; + + /* Overload. */ + if (msg->_get_part) + return msg->_get_part (msg, part, pmsg); + + if (msg->mime == NULL) + { + int status = mu_mime_create (&msg->mime, msg, 0); + if (status != 0) + return status; + } + return mu_mime_get_part (msg->mime, part, pmsg); +} + +int +mu_message_set_get_part (mu_message_t msg, int (*_get_part) + (mu_message_t, size_t, mu_message_t *), + void *owner) +{ + if (msg == NULL) + return EINVAL; + if (msg->owner != owner) + return EACCES; + msg->_get_part = _get_part; + return 0; +} + +int +mu_message_get_observable (mu_message_t msg, mu_observable_t *pobservable) +{ + if (msg == NULL || pobservable == NULL) + return EINVAL; + + if (msg->observable == NULL) + { + int status = mu_observable_create (&msg->observable, msg); + if (status != 0) + return status; + } + *pobservable = msg->observable; + return 0; +} + +int +mu_message_save_to_mailbox (mu_message_t msg, + mu_debug_t debug, + const char *toname, int perms) +{ + int rc = 0; + mu_mailbox_t to = 0; + + if ((rc = mu_mailbox_create_default (&to, toname))) + { + MU_DEBUG2 (debug, MU_DEBUG_ERROR, + "mu_mailbox_create_default (%s) failed: %s\n", toname, + mu_strerror (rc)); + goto end; + } + + if (debug && (rc = mu_mailbox_set_debug (to, debug))) + goto end; + + if ((rc = mu_mailbox_open (to, + MU_STREAM_WRITE | MU_STREAM_CREAT + | (perms & MU_STREAM_IMASK)))) + { + MU_DEBUG2 (debug, MU_DEBUG_ERROR, + "mu_mailbox_open (%s) failed: %s\n", toname, + mu_strerror (rc)); + goto end; + } + + if ((rc = mu_mailbox_append_message (to, msg))) + { + MU_DEBUG2 (debug, MU_DEBUG_ERROR, + "mu_mailbox_append_message (%s) failed: %s\n", toname, + mu_strerror (rc)); + goto end; + } + +end: + + if (!rc) + { + if ((rc = mu_mailbox_close (to))) + MU_DEBUG2 (debug, MU_DEBUG_ERROR, + "mu_mailbox_close (%s) failed: %s\n", toname, + mu_strerror (rc)); + } + else + mu_mailbox_close (to); + |