/* GNU Mailutils -- a suite of utilities for electronic mail Copyright (C) 1999, 2000, 2001, 2002, 2004, 2005, 2006, 2007, 2009 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 <ctype.h> #include <pwd.h> #include <message0.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> #define MESSAGE_MODIFIED 0x10000; static int message_read (mu_stream_t is, char *buf, size_t buflen, mu_off_t off, size_t *pnread ); static int message_write (mu_stream_t os, const char *buf, size_t buflen, mu_off_t off, size_t *pnwrite); static int message_get_transport2 (mu_stream_t stream, mu_transport_t *pin, mu_transport_t *pout); static int message_sender (mu_envelope_t envelope, char *buf, size_t len, size_t *pnwrite); static int message_date (mu_envelope_t envelope, char *buf, size_t len, size_t *pnwrite); static int message_stream_size (mu_stream_t stream, mu_off_t *psize); static int message_header_fill (mu_header_t header, char *buffer, size_t buflen, mu_off_t off, size_t * pnread); static int message_body_read (mu_stream_t stream, char *buffer, size_t n, mu_off_t off, size_t *pn); /* 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); 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), msg); /* 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), msg); /* Mime. */ if (msg->mime) mu_mime_destroy (&(msg->mime)); /* Loose the owner. */ msg->owner = NULL; /* Mailbox maybe created floating i.e they were created implicitely by the message when doing something like: mu_message_create (&msg, "pop://localhost/msgno=2", NULL); mu_message_create (&msg, "imap://localhost/alain;uid=xxxxx", NULL); althought the semantics about this is still flaky we our making some provisions here for it. if (msg->floating_mailbox && msg->mailbox) mu_mailbox_destroy (&(msg->mailbox)); */ if (msg->ref == 0) 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 tostr = NULL; mu_off_t off = 0; size_t n = 0; char buf[512]; if (!to) return MU_ERR_OUT_PTR_NULL; if (!from) return EINVAL; if((status = mu_message_create (to, NULL))) return status; mu_message_get_stream (from, &fromstr); mu_message_get_stream (*to, &tostr); while ( (status = mu_stream_readline (fromstr, buf, sizeof(buf), off, &n)) == 0 && n > 0 ) { mu_stream_write (tostr, buf, n, off, NULL); off += n; } if(status) mu_message_destroy(to, NULL); 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; /* Is it a floating mesg */ if (msg->header == NULL) { mu_header_t header; int status = mu_header_create (&header, NULL, 0, msg); if (status != 0) return status; if (msg->stream) { /* Was it created by us? */ mu_message_t mesg = mu_stream_get_owner (msg->stream); if (mesg != msg) mu_header_set_fill (header, message_header_fill, msg); } msg->header = header; } *phdr = msg->header; return 0; } 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; /* Make sure we destroy the old if it was own by the mesg */ /* FIXME: I do not know if somebody has already a ref on this ? */ if (msg->header) mu_header_destroy (&(msg->header), msg); 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. */ if (msg->stream) { /* Was it created by us? */ mu_message_t mesg = mu_stream_get_owner (msg->stream); if (mesg != msg) { mu_stream_t stream; int flags = 0; mu_stream_get_flags (msg->stream, &flags); if ((status = mu_stream_create (&stream, flags, body)) != 0) { mu_body_destroy (&body, msg); return status; } mu_stream_set_read (stream, message_body_read, body); mu_stream_setbufsiz (stream, 128); 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 destoy the old if it was own 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; /* Make sure we destoy the old if it was own by the mesg. */ /* FIXME: I do not know if somebody has already a ref on this ? */ if (msg->stream) mu_stream_destroy (&(msg->stream), msg); msg->stream = stream; msg->flags |= MESSAGE_MODIFIED; return 0; } int mu_message_get_stream (mu_message_t msg, mu_stream_t *pstream) { if (msg == NULL) return EINVAL; if (pstream == NULL) return MU_ERR_OUT_PTR_NULL; if (msg->stream == NULL) { mu_stream_t stream; int status; status = mu_stream_create (&stream, MU_STREAM_RDWR, msg); if (status != 0) return status; mu_stream_set_read (stream, message_read, msg); mu_stream_set_write (stream, message_write, msg); mu_stream_set_get_transport2 (stream, message_get_transport2, msg); mu_stream_set_size (stream, message_stream_size, msg); mu_stream_set_flags (stream, MU_STREAM_RDWR); msg->stream = stream; } *pstream = msg->stream; 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_sender, msg); mu_envelope_set_date (envelope, message_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 (header, "X-UIDL", buffer, buflen, &n); if (status == 0 && n > 0) { /* FIXME: Is this necessary ? I did not see so far a x-uidl message that broken i.e. X-UIDL: <abaceekeke\n jakdkjaja> */ /* We need to collapse the header if it was mutiline. e points to the last char meaning in a C string that's '\0', s to the start. We also remove the spesky '<' '>' if they are around. */ char *s, *e; for (s = buffer, e = buffer + n; s <= e; s++) { if (isspace ((unsigned char)*s) || *s == '<' || *s == '>') { memmove (s, s + 1, e - (s + 1)); e -= 1; *e = '\0'; } } } else { size_t uid = 0; struct mu_md5_ctx md5context; mu_stream_t stream = NULL; char buf[1024]; mu_off_t offset = 0; unsigned char md5digest[16]; char *tmp; n = 0; mu_message_get_uid (msg, &uid); mu_message_get_stream (msg, &stream); mu_md5_init_ctx (&md5context); while (mu_stream_read (stream, buf, sizeof (buf), offset, &n) == 0 && n > 0) { mu_md5_process_bytes (buf, n, &md5context); offset += n; } 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'; } 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; } /* Implements the mu_stream_read () on the message stream. */ static int message_read (mu_stream_t is, char *buf, size_t buflen, mu_off_t off, size_t *pnread ) { mu_message_t msg = mu_stream_get_owner (is); mu_stream_t his, bis; size_t hread, hsize, bread, bsize; if (msg == NULL) return EINVAL; bsize = hsize = bread = hread = 0; his = bis = NULL; mu_header_size (msg->header, &hsize); mu_body_size (msg->body, &bsize); /* On some remote sever (POP) the size of the header and body is not known until you start reading them. So by checking hsize == bsize == 0, this kludge is a way of detecting the anomalie and start by the header. */ if ((size_t)off < hsize || (hsize == 0 && bsize == 0)) { mu_header_get_stream (msg->header, &his); mu_stream_read (his, buf, buflen, off, &hread); } else { mu_body_get_stream (msg->body, &bis); mu_stream_read (bis, buf, buflen, off - hsize, &bread); } if (pnread) *pnread = hread + bread; return 0; } /* Implements the mu_stream_write () on the message stream. */ static int message_write (mu_stream_t os, const char *buf, size_t buflen, mu_off_t off, size_t *pnwrite) { mu_message_t msg = mu_stream_get_owner (os); int status = 0; size_t bufsize = buflen; if (msg == NULL) return EINVAL; /* Skip the obvious. */ if (buf == NULL || buflen == 0) { if (pnwrite) *pnwrite = 0; return 0; } if (!msg->hdr_done) { size_t len; char *nl; mu_header_t header = NULL; mu_stream_t hstream = NULL; mu_message_get_header (msg, &header); mu_header_get_stream (header, &hstream); while (!msg->hdr_done && (nl = memchr (buf, '\n', buflen)) != NULL) { len = nl - buf + 1; status = mu_stream_write (hstream, buf, len, msg->hdr_buflen, NULL); if (status != 0) return status; msg->hdr_buflen += len; /* We detect an empty line .i.e "^\n$" this signal the end of the header. */ if (buf == nl) msg->hdr_done = 1; buf = nl + 1; buflen -= len; } } /* Message header is not complete but was not a full line. */ if (!msg->hdr_done && buflen > 0) { mu_header_t header = NULL; mu_stream_t hstream = NULL; mu_message_get_header (msg, &header); mu_header_get_stream (header, &hstream); status = mu_stream_write (hstream, buf, buflen, msg->hdr_buflen, NULL); if (status != 0) return status; msg->hdr_buflen += buflen; buflen = 0; } else if (buflen > 0) /* In the body. */ { mu_stream_t bs; mu_body_t body; size_t written = 0; if ((status = mu_message_get_body (msg, &body)) != 0 || (status = mu_body_get_stream (msg->body, &bs)) != 0) { msg->hdr_buflen = msg->hdr_done = 0; return status; } if (off < (mu_off_t)msg->hdr_buflen) off = 0; else off -= msg->hdr_buflen; status = mu_stream_write (bs, buf, buflen, off, &written); buflen -= written; } if (pnwrite) *pnwrite = bufsize - buflen; return status; } static int message_get_transport2 (mu_stream_t stream, mu_transport_t *pin, mu_transport_t *pout) { mu_message_t msg = mu_stream_get_owner (stream); mu_body_t body; mu_stream_t is; if (msg == NULL) return EINVAL; if (pout) *pout = NULL; /* Probably being lazy, then create a body for the stream. */ if (msg->body == NULL) { int status = mu_body_create (&body, msg); if (status != 0 ) return status; msg->body = body; } else body = msg->body; mu_body_get_stream (body, &is); return mu_stream_get_transport2 (is, pin, pout); } /* Implements the stream_stream_size () on the message stream. */ static int message_stream_size (mu_stream_t stream, mu_off_t *psize) { mu_message_t msg = mu_stream_get_owner (stream); size_t size; int rc = mu_message_size (msg, &size); /* FIXME: should it get mu_off_t as its 2nd argument */ if (rc == 0) *psize = size; return rc; } static int message_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_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; } static int message_header_fill (mu_header_t header, char *buffer, size_t buflen, mu_off_t off, size_t * pnread) { int status = 0; mu_message_t msg = mu_header_get_owner (header); mu_stream_t stream = NULL; size_t nread = 0; /* Noop. */ if (buffer == NULL || buflen == 0) { if (pnread) *pnread = nread; return 0; } if (!msg->hdr_done) { status = mu_message_get_stream (msg, &stream); if (status == 0) { /* Position the file pointer and the buffer. */ status = mu_stream_readline (stream, buffer, buflen, off, &nread); /* Detect the end of the headers. */ if (nread && buffer[0] == '\n' && buffer[1] == '\0') { msg->hdr_done = 1; } msg->hdr_buflen += nread; } } if (pnread) *pnread = nread; return status; } static int message_body_read (mu_stream_t stream, char *buffer, size_t n, mu_off_t off, size_t *pn) { mu_body_t body = mu_stream_get_owner (stream); mu_message_t msg = mu_body_get_owner (body); size_t nread = 0; mu_header_t header = NULL; mu_stream_t bstream = NULL; size_t size = 0; int status; mu_message_get_header (msg, &header); status = mu_header_size (msg->header, &size); if (status == 0) { mu_message_get_stream (msg, &bstream); status = mu_stream_read (bstream, buffer, n, size + off, &nread); } if (pn) *pn = nread; return status; } 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); mu_mailbox_destroy (&to); return rc; }