diff options
Diffstat (limited to 'mailbox/mbox/mbox.c')
-rw-r--r-- | mailbox/mbox/mbox.c | 1753 |
1 files changed, 1753 insertions, 0 deletions
diff --git a/mailbox/mbox/mbox.c b/mailbox/mbox/mbox.c new file mode 100644 index 000000000..f35571982 --- /dev/null +++ b/mailbox/mbox/mbox.c @@ -0,0 +1,1753 @@ +/* GNU Mailutils -- a suite of utilities for electronic mail + Copyright (C) 1999, 2000, 2001, 2003 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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* First draft by Alain Magloire */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <mbox0.h> + +#define ATTRIBUTE_IS_DELETED(flag) (flag & MU_ATTRIBUTE_DELETED) +#define ATTRIBUTE_IS_EQUAL(flag1, flag2) (flag1 == flag2) + +static void mbox_destroy (mailbox_t); + +/* Below are the headers field-names that we are caching for speed, it is + more or less the list of headers in ENVELOPE command from IMAP. + + NOTE: These are indexed with H_.* macros defined in mbox0.h. Keep them + in sync if you add or remove something. */ + +const char *fhdr_table[HDRSIZE] = +{ + "Bcc", + "Cc", + "Content-Language", + "Content-Transfer-Encoding", + "Content-Type", + "Date", + "From", + "In-Reply-To", + "Message-ID", + "Reference", + "Reply-To", + "Sender", + "Subject", + "To", + "X-UIDL" +}; + +/* Mailbox concrete implementation. */ +static int mbox_open __P ((mailbox_t, int)); +static int mbox_close __P ((mailbox_t)); +static int mbox_get_message __P ((mailbox_t, size_t, message_t *)); +/* static int mbox_get_message_by_uid __P ((mailbox_t, size_t, message_t *)); */ +static int mbox_append_message __P ((mailbox_t, message_t)); +static int mbox_messages_count __P ((mailbox_t, size_t *)); +static int mbox_messages_recent __P ((mailbox_t, size_t *)); +static int mbox_message_unseen __P ((mailbox_t, size_t *)); +static int mbox_expunge0 __P ((mailbox_t, int)); +static int mbox_expunge __P ((mailbox_t)); +static int mbox_save_attributes __P ((mailbox_t)); +static int mbox_uidvalidity __P ((mailbox_t, unsigned long *)); +static int mbox_uidnext __P ((mailbox_t, size_t *)); +static int mbox_scan __P ((mailbox_t, size_t, size_t *)); +static int mbox_is_updated __P ((mailbox_t)); +static int mbox_get_size __P ((mailbox_t, off_t *)); + +/* private stuff */ +static int mbox_append_message0 __P ((mailbox_t, message_t, off_t *, + int, int)); +static int mbox_message_uid __P ((message_t, size_t *)); +static int mbox_header_fill __P ((header_t, char *, size_t, off_t, + size_t *)); +static int mbox_get_body_fd __P ((stream_t, int *)); +static int mbox_get_fd __P ((mbox_message_t, int *)); +static int mbox_get_attr_flags __P ((attribute_t, int *)); +static int mbox_set_attr_flags __P ((attribute_t, int)); +static int mbox_unset_attr_flags __P ((attribute_t, int)); +static int mbox_body_read __P ((stream_t, char *, size_t, off_t, + size_t *)); +static int mbox_body_readline __P ((stream_t, char *, size_t, off_t, + size_t *)); +static int mbox_readstream __P ((mbox_message_t, char *, size_t, + off_t, size_t *, int, off_t, + off_t)); +static int mbox_stream_size __P((stream_t stream, off_t *psize)); + +static int mbox_header_size __P ((header_t, size_t *)); +static int mbox_header_lines __P ((header_t, size_t *)); +static int mbox_body_size __P ((body_t, size_t *)); +static int mbox_body_lines __P ((body_t, size_t *)); +static int mbox_envelope_sender __P ((envelope_t, char *, size_t, + size_t *)); +static int mbox_envelope_date __P ((envelope_t, char *, size_t, + size_t *)); +static int mbox_tmpfile __P ((mailbox_t, char **pbox)); + +/* Allocate the mbox_data_t struct(concrete mailbox), but don't do any + parsing on the name or even test for existence. However we do strip any + leading "mbox:" part of the name, this is suppose to be the + protocol/scheme name. */ +int +_mailbox_mbox_init (mailbox_t mailbox) +{ + mbox_data_t mud; + size_t name_len; + + if (mailbox == NULL) + return EINVAL; + + /* Allocate specific mbox data. */ + mud = mailbox->data = calloc (1, sizeof (*mud)); + if (mailbox->data == NULL) + return ENOMEM; + + /* Back pointer. */ + mud->mailbox = mailbox; + + /* Copy the name: + We do not do any further interpretation after the scheme "mbox:" + Because for example on distributed system like QnX4 a file name is + //390/etc/passwd. So the best approach is to let the OS handle it + for example if we receive: "mbox:///var/mail/alain" the mailbox name + will be "///var/mail/alain", we let open() do the right thing. + So it will let things like this "mbox://390/var/mail/alain" where + the "//" _is_ part of the filename, pass correctely. */ + url_get_path (mailbox->url, NULL, 0, &name_len); + mud->name = calloc (name_len + 1, sizeof (char)); + if (mud->name == NULL) + { + free (mud); + mailbox->data = NULL; + return ENOMEM; + } + url_get_path (mailbox->url, mud->name, name_len + 1, NULL); + + mud->state = MBOX_NO_STATE; + + /* Overloading the defaults. */ + mailbox->_destroy = mbox_destroy; + + mailbox->_open = mbox_open; + mailbox->_close = mbox_close; + + /* Overloading of the entire mailbox object methods. */ + mailbox->_get_message = mbox_get_message; + mailbox->_append_message = mbox_append_message; + mailbox->_messages_count = mbox_messages_count; + mailbox->_messages_recent = mbox_messages_recent; + mailbox->_message_unseen = mbox_message_unseen; + mailbox->_expunge = mbox_expunge; + mailbox->_save_attributes = mbox_save_attributes; + mailbox->_uidvalidity = mbox_uidvalidity; + mailbox->_uidnext = mbox_uidnext; + + mailbox->_scan = mbox_scan; + mailbox->_is_updated = mbox_is_updated; + + mailbox->_get_size = mbox_get_size; + + /* Set our properties. */ + { + property_t property = NULL; + mailbox_get_property (mailbox, &property); + property_set_value (property, "TYPE", "MBOX", 1); + } + + MAILBOX_DEBUG1 (mailbox, MU_DEBUG_TRACE, "mbox_init(%s)\n", mud->name); + return 0; /* okdoke */ +} + +/* Free all ressources associated with Unix concrete mailbox. */ +static void +mbox_destroy (mailbox_t mailbox) +{ + if (mailbox->data) + { + size_t i; + mbox_data_t mud = mailbox->data; + MAILBOX_DEBUG1 (mailbox, MU_DEBUG_TRACE, + "mbox_destroy (%s)\n", mud->name); + monitor_wrlock (mailbox->monitor); + for (i = 0; i < mud->umessages_count; i++) + { + mbox_message_t mum = mud->umessages[i]; + if (mum) + { + size_t j; + message_destroy (&(mum->message), mum); + for (j = 0; j < HDRSIZE; j++) + if (mum->fhdr[j]) + free (mum->fhdr[j]); + free (mum); + } + } + if (mud->umessages) + free (mud->umessages); + if (mud->name) + free (mud->name); + free (mud); + mailbox->data = NULL; + monitor_unlock (mailbox->monitor); + } +} + +/* Open the file. For MU_STREAM_READ, the code tries mmap() first and fall + back to normal file. */ +static int +mbox_open (mailbox_t mailbox, int flags) +{ + mbox_data_t mud = mailbox->data; + int status = 0; + + if (mud == NULL) + return EINVAL; + + mailbox->flags = flags; + + /* Get a stream. */ + if (mailbox->stream == NULL) + { + /* We do not try to mmap for CREAT or APPEND, it is not supported. */ + status = (flags & MU_STREAM_CREAT) + || (mailbox->flags & MU_STREAM_APPEND); + + /* Try to mmap () the file first. */ + if (status == 0) + { + status = mapfile_stream_create (&mailbox->stream, mud->name, mailbox->flags); + if (status == 0) + { + status = stream_open (mailbox->stream); + } + } + + /* Fall back to normal file if mmap() failed. */ + if (status != 0) + { + status = file_stream_create (&mailbox->stream, mud->name, mailbox->flags); + if (status != 0) + return status; + status = stream_open (mailbox->stream); + } + /* All failed, bail out. */ + if (status != 0) + { + stream_destroy (&mailbox->stream, NULL); + return status; + } + /* Even on top, of normal FILE *, lets agressively cache. But this + may not be suitable for system tight on memory. */ + stream_setbufsiz (mailbox->stream, BUFSIZ); + } + else + { + status = stream_open (mailbox->stream); + if (status != 0) + return status; + } + + MAILBOX_DEBUG2 (mailbox, MU_DEBUG_TRACE, "mbox_open(%s, 0x%x)\n", + mud->name, mailbox->flags); + + if (mailbox->locker == NULL) + status = locker_create (&(mailbox->locker), mud->name, 0); + return status; +} + +static int +mbox_close (mailbox_t mailbox) +{ + mbox_data_t mud = mailbox->data; + /* size_t i; */ + + if (mud == NULL) + return EINVAL; + + MAILBOX_DEBUG1 (mailbox, MU_DEBUG_TRACE, "mbox_close(%s)\n", mud->name); + + /* Make sure that we do not hold any file locking. */ + locker_unlock (mailbox->locker); + +#if 0 + /* RFC: I'm not sure on the right approach especially if the client is + working in disconnected mode, where it can mailbox_close/mailbox_open + for each request, maybe we should keep them for a while. */ + monitor_wrlock (mailbox->monitor); + /* Before closing we need to remove all the messages + - to reclaim the memory + - to prepare for another scan. */ + for (i = 0; i < mud->umessages_count; i++) + { + mbox_message_t mum = mud->umessages[i]; + /* Destroy the attach messages. */ + if (mum) + { + size_t j; + message_destroy (&(mum->message), mum); + for (j = 0; j < HDRSIZE; j++) + if (mum->fhdr[j]) + free (mum->fhdr[j]); + free (mum); + } + } + if (mud->umessages) + free (mud->umessages); + mud->umessages = NULL; + mud->messages_count = mud->umessages_count = 0; + mud->size = 0; + mud->uidvalidity = 0; + mud->uidnext = 0; + monitor_unlock (mailbox->monitor); +#endif + return stream_close (mailbox->stream); +} + +/* Cover function that call the real thing, mbox_scan(), with + notification set. */ +static int +mbox_scan (mailbox_t mailbox, size_t msgno, size_t *pcount) +{ + size_t i; + mbox_data_t mud = mailbox->data; + MAILBOX_DEBUG1 (mailbox, MU_DEBUG_TRACE, "mbox_scan(%s)\n", mud->name); + if (! mbox_is_updated (mailbox)) + return mbox_scan0 (mailbox, msgno, pcount, 1); + /* Since the mailbox is already updated fake the scan. */ + if (msgno > 0) + msgno--; /* The fist message is number "1", decremente for the C array. */ + for (i = msgno; i < mud->messages_count; i++) + { + if (observable_notify (mailbox->observable, MU_EVT_MESSAGE_ADD) != 0) + break; + if (((i +1) % 50) == 0) + { + observable_notify (mailbox->observable, MU_EVT_MAILBOX_PROGRESS); + } + } + return 0; +} + +/* FIXME: How to handle a shrink ? meaning, the &^$^@%#@^& user start two + browsers and deleted emails in one session. My views is that we should + scream bloody murder and hunt them with a machette. But for now just play + dumb, but maybe the best approach is to pack our things and leave + .i.e exit()/abort(). */ +static int +mbox_is_updated (mailbox_t mailbox) +{ + off_t size = 0; + mbox_data_t mud = mailbox->data; + if (stream_size (mailbox->stream, &size) != 0) + return 0; + if (size < mud->size) + { + observable_notify (mailbox->observable, MU_EVT_MAILBOX_CORRUPT); + /* And be verbose. ? */ + mu_error ("* BAD : Mailbox corrupted, shrank size\n"); + /* FIXME: should I crash. */ + return 0; + } + return (mud->size == size); +} + +/* Try to create an uniq file, we no race conditions. */ +static int +mbox_tmpfile (mailbox_t mailbox, char **pbox) +{ + const char *tmpdir; + int fd; + const char *basename; + mbox_data_t mud = mailbox->data; + + /* P_tmpdir should be in <stdio.h>. */ +#ifndef P_tmpdir +# define P_tmpdir "/tmp" +#endif + + basename = strrchr (mud->name, '/'); + if (basename) + basename++; + else + basename = mud->name; + + tmpdir = getenv ("TMPDIR") ? getenv ("TMPDIR") : P_tmpdir; + /* (separator + null) == 2 + XXXXXX == 6 + ... */ + *pbox = calloc (strlen (tmpdir) + /* '/' */ 1 + /*strlen ("MU_")*/ 3 + + strlen (basename) + /*strlen ("_XXXXXX")*/ 7 + /*null*/1, + sizeof (**pbox)); + if (*pbox == NULL) + return -1; + sprintf (*pbox, "%s/MU_%s_XXXXXX", tmpdir, basename); +#ifdef HAVE_MKSTEMP + fd = mkstemp (*pbox); +#else + /* Create the file. It must not exist. If it does exist, fail. */ + if (mktemp (*pbox)) + { + fd = open (*pbox, O_RDWR|O_CREAT|O_EXCL, 0600); + } + else + { + free (*pbox); + fd = -1; + } +#endif + return fd; +} + +/* For the expunge bits we took a very cautionnary approach, meaning + we create a temporary mailbox in the tmpdir copy all the message not mark + deleted(Actually we copy all the message that may have been modified + i.e new header values set; UIDL or UID or etc .... + and skip the deleted ones, truncate the real mailbox to the desired size + and overwrite with the tmp mailbox. The approach to do everyting + in core is tempting but require + - to much memory, it is not rare nowadays to have 30 Megs mailbox, + - also there is danger for filesystems with quotas, + - if we run out of disk space everything is lost. + - or some program may not respect the advisory lock and decide to append + a new message while your expunging etc ... + The real downside to the approach is that when things go wrong + the temporary file may be left in /tmp, which is not all that bad + because at least, we have something to recuperate when failure. */ +static int +mbox_expunge0 (mailbox_t mailbox, int remove_deleted) +{ + mbox_data_t mud = mailbox->data; + mbox_message_t mum; + int status = 0; + sigset_t signalset; + int tempfile; + size_t i, j, dirty; /* dirty will indicate the first modified message. */ + off_t marker = 0; /* marker will be the position to truncate. */ + off_t total = 0; + char *tmpmboxname = NULL; + mailbox_t tmpmailbox = NULL; + size_t save_imapbase = 0; /* uidvalidity is save in the first message. */ +#ifdef WITH_PTHREAD + int state; +#endif + + if (mud == NULL) + return EINVAL; + + MAILBOX_DEBUG1 (mailbox, MU_DEBUG_TRACE, "mbox_expunge (%s)\n", mud->name); + + /* Noop. */ + if (mud->messages_count == 0) + return 0; + + /* Find the first dirty(modified) message. */ + for (dirty = 0; dirty < mud->messages_count; dirty++) + { + mum = mud->umessages[dirty]; + /* Message may have been tampered, break here. */ + if ((mum->attr_flags & MU_ATTRIBUTE_MODIFIED) || + (mum->attr_flags & MU_ATTRIBUTE_DELETED) || + (mum->message && message_is_modified (mum->message))) + break; + } + + /* Did something change ? */ + if (dirty == mud->messages_count) + return 0; /* Nothing change, bail out. */ + + /* Create a temporary file. */ + tempfile = mbox_tmpfile (mailbox, &tmpmboxname); + if (tempfile == -1) + { + if (tmpmboxname) + free (tmpmboxname); + mu_error ("Failed to create temporary file when expunging.\n"); + return errno; + } + + /* This is redundant, we go to the loop again. But it's more secure here + since we don't want to be disturb when expunging. Destroy all the + messages mark for deletion. */ + if (remove_deleted) + { + for (j = 0; j < mud->messages_count; j++) + { + mum = mud->umessages[j]; + if (mum && mum->message && ATTRIBUTE_IS_DELETED (mum->attr_flags)) + message_destroy (&(mum->message), mum); + } + } + + /* Create temporary mailbox_t. */ + { + mbox_data_t tmp_mud; + char *m = alloca (5 + strlen (tmpmboxname) + 1); + /* Try via the mbox: protocol. */ + sprintf (m, "mbox:%s", tmpmboxname); + status = mailbox_create (&tmpmailbox, m); + if (status != 0) + { + /* Do not give up just yet, maybe they register the path_record. */ + sprintf (m, "%s", tmpmboxname); + status = mailbox_create (&tmpmailbox, m); + if (status != 0) + { + /* Ok give up. */ + close (tempfile); + remove (tmpmboxname); + free (tmpmboxname); + return status; + } + } + + /* Must be flag CREATE if not the mailbox_open will try to mmap() + the file. */ + status = mailbox_open (tmpmailbox, MU_STREAM_CREAT | MU_STREAM_RDWR); + if (status != 0) + { + close (tempfile); + remove (tmpmboxname); + free (tmpmboxname); + return status; + } + close (tempfile); /* This one is useless the mailbox have its own. */ + tmp_mud = tmpmailbox->data; + /* May need when appending. */ + tmp_mud->uidvalidity = mud->uidvalidity; + tmp_mud->uidnext = mud->uidnext; + } + + /* Get the File lock. */ + if ((status = locker_lock (mailbox->locker)) != 0) + { + mailbox_close (tmpmailbox); + mailbox_destroy (&tmpmailbox); + remove (tmpmboxname); + free (tmpmboxname); + mu_error ("Failed to grab the lock: %s\n", mu_strerror(status)); + return status; + } + + /* Critical section, we can not allowed signal here. */ +#ifdef WITH_PTHREAD + pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, &state); +#endif + sigemptyset (&signalset); + sigaddset (&signalset, SIGTERM); + sigaddset (&signalset, SIGHUP); + sigaddset (&signalset, SIGTSTP); + sigaddset (&signalset, SIGINT); + sigaddset (&signalset, SIGWINCH); + sigprocmask (SIG_BLOCK, &signalset, 0); + + /* Set the marker position. */ + marker = mud->umessages[dirty]->header_from; + total = 0; + + /* Copy to the temporary mailbox emails not mark deleted. */ + for (i = dirty; i < mud->messages_count; i++) + { + mum = mud->umessages[i]; + + /* Skip it, if mark for deletion. */ + if (remove_deleted && ATTRIBUTE_IS_DELETED (mum->attr_flags)) + { + /* We save the uidvalidity in the first message, if it is being + deleted we need to move the uidvalidity to the first available + (non-deleted) message. */ + if (i == save_imapbase) + { + save_imapbase = i + 1; + if (save_imapbase < mud->messages_count) + (mud->umessages[save_imapbase])->attr_flags |= MU_ATTRIBUTE_MODIFIED; + } + continue; + } + + /* Do the expensive mbox_append_message0() only if mark dirty. */ + if ((mum->attr_flags & MU_ATTRIBUTE_MODIFIED) || + (mum->message && message_is_modified (mum->message))) + { + /* The message was not instanciated, probably the dirty flag was + set by mbox_scan(), create one here. */ + if (mum->message == 0) + { + message_t msg; + status = mbox_get_message (mailbox, i + 1, &msg); + if (status != 0) + { + mu_error ("Error expunge:%d: %s", __LINE__, + mu_strerror (status)); + goto bailout0; + } + } + status = mbox_append_message0 (tmpmailbox, mum->message, + &total, 1, (i == save_imapbase)); + if (status != 0) + { + mu_error ("Error expunge:%d: %s", __LINE__, + mu_strerror (status)); + goto bailout0; + } + /* Clear the dirty bits. */ + mum->attr_flags &= ~MU_ATTRIBUTE_MODIFIED; + message_clear_modified (mum->message); + } + else + { + /* Nothing changed copy the message straight. */ + char buffer [1024]; + size_t n; + off_t offset = mum->header_from; + size_t len = mum->body_end - mum->header_from; + while (len > 0) + { + n = (len < sizeof (buffer)) ? len : sizeof (buffer); + if ((status = stream_read (mailbox->stream, buffer, n, offset, + &n) != 0) + || (status = stream_write (tmpmailbox->stream, buffer, n, + total, &n) != 0)) + { + mu_error ("Error expunge:%d: %s", __LINE__, + mu_strerror (status)); + goto bailout0; + } + len -= n; + total += n; + offset += n; + } + /* Add the newline separator. */ + status = stream_write (tmpmailbox->stream, "\n", 1, total, &n); + if (status != 0) + { + mu_error ("Error expunge:%d: %s", __LINE__, + mu_strerror (status)); + goto bailout0; + } + total++; + } + } /* for (;;) */ + + /* Caution: before ftruncate()ing the file see + - if we've receive new mails. Some programs may not respect the lock, + - or the lock was held for too long. + - The mailbox may not have been properly updated before expunging. */ + { + off_t size = 0; + if (stream_size (mailbox->stream, &size) == 0) + { + off_t len = size - mud->size; + off_t offset = mud->size; + char buffer [1024]; + size_t n = 0; + if (len > 0 ) + { + while ((status = stream_read (mailbox->stream, buffer, + sizeof (buffer), offset, &n)) == 0 + && n > 0) + { + status = stream_write (tmpmailbox->stream, buffer, n, + total, &n); + if (status != 0) + { + mu_error ("Error expunge:%d: %s", __LINE__, + mu_strerror (status)); + goto bailout0; + } + total += n; + offset += n; + } + } + else if (len < 0) + { + /* Corrupted mailbox. */ + mu_error ("Error expunge:%d: %s", __LINE__, + mu_strerror (status)); + goto bailout0; + } + } + } /* End of precaution. */ + + /* Seek and rewrite it. */ + if (total > 0) + { + char buffer [1024]; + size_t n = 0; + off_t off = 0; + off_t offset = marker; + while ((status = stream_read (tmpmailbox->stream, buffer, + sizeof (buffer), off, &n)) == 0 + && n > 0) + { + status = stream_write (mailbox->stream, buffer, n, offset, &n); + if (status != 0) + { + mu_error ("Error expunge:%d: %s\n", __LINE__, + mu_strerror (status)); + goto bailout; + } + off += n; + offset += n; + } + } + + /* Flush/truncation. Need to flush before truncate. */ + stream_flush (mailbox->stream); + status = stream_truncate (mailbox->stream, total + marker); + if (status != 0) + { + mu_error ("Error expunging:%d: %s\n", __LINE__, + mu_strerror (status)); + goto bailout; + } + + /* Don't remove the tmp mbox in case of errors, when writing back. */ + bailout0: + remove (tmpmboxname); + + bailout: + + free (tmpmboxname); + /* Release the File lock. */ + locker_unlock (mailbox->locker); + mailbox_close (tmpmailbox); + mailbox_destroy (&tmpmailbox); + + /* Reenable signal. */ +#ifdef WITH_PTHREAD + pthread_setcancelstate (state, &state); +#endif + sigprocmask (SIG_UNBLOCK, &signalset, 0); + + /* We need to readjust the pointers. + It is a little hairy because we need to keep the message pointers alive + So we are going through the array and "defragmentize". For example + in (1 2 3 4) if 2 was deleted we need to move 3 4 up by one etc .. + */ + if (status == 0) + { + size_t dlast; + monitor_wrlock (mailbox->monitor); + for (j = dirty, dlast = mud->messages_count - 1; + j <= dlast; j++) + { + /* Clear all the references, any attach messages been already + destroy above. */ + mum = mud->umessages[j]; + if (remove_deleted && ATTRIBUTE_IS_DELETED (mum->attr_flags)) + { + if ((j + 1) <= dlast) + { + /* Move all the pointers up. So the message pointer + part of mum will be at the right position. */ + memmove (mud->umessages + j, mud->umessages + j + 1, + (dlast - j) * sizeof (mum)); +#if 0 + mum->header_from = mum->header_from_end = 0; + mum->body = mum->body_end = 0; + mum->header_lines = mum->body_lines = 0; +#endif + for (i = 0; i < HDRSIZE; i++) + if (mum->fhdr[i]) + { + free (mum->fhdr[i]); + mum->fhdr[i] = NULL; + } + memset (mum, 0, sizeof (*mum)); + /* We are not free()ing the useless mum, but instead + we put it back in the pool, to be reuse. */ + mud->umessages[dlast] = mum; + dlast--; + /* Set mum to the new value after the memmove so it + gets cleared to. */ + mum = mud->umessages[j]; + } + else + { + for (i = 0; i < HDRSIZE; i++) + if (mum->fhdr[i]) + { + free (mum->fhdr[i]); + mum->fhdr[i] = NULL; + } + memset (mum, 0, sizeof (*mum)); + } + } + mum->header_from = mum->header_from_end = 0; + mum->body = mum->body_end = 0; + mum->header_lines = mum->body_lines = 0; + for (i = 0; i < HDRSIZE; i++) + if (mum->fhdr[i]) + { + free (mum->fhdr[i]); + mum->fhdr[i] = NULL; + } + } + monitor_unlock (mailbox->monitor); + /* This is should reset the messages_count, the last argument 0 means + not to send event notification. */ + mbox_scan0 (mailbox, dirty, NULL, 0); + } + return status; +} + +static int +mbox_expunge (mailbox_t mailbox) +{ + return mbox_expunge0 (mailbox, 1); +} + +static int +mbox_save_attributes (mailbox_t mailbox) +{ + return mbox_expunge0 (mailbox, 0); +} + +static int +mbox_message_uid (message_t msg, size_t *puid) +{ + mbox_message_t mum = message_get_owner (msg); + if (puid) + *puid = mum->uid; + return 0; +} + +static int +mbox_get_body_fd (stream_t is, int *pfd) +{ + body_t body = stream_get_owner (is); + message_t msg = body_get_owner (body); + mbox_message_t mum = message_get_owner (msg); + return mbox_get_fd (mum, pfd); +} + +static int +mbox_get_fd (mbox_message_t mum, int *pfd) +{ + int status; + if (mum == NULL) + return EINVAL; + status = stream_get_fd (mum->mud->mailbox->stream, pfd); + return status; +} + +static int +mbox_get_attr_flags (attribute_t attr, int *pflags) +{ + message_t msg = attribute_get_owner (attr); + mbox_message_t mum = message_get_owner (msg); + + if (mum == NULL) + return EINVAL; + if (pflags) + *pflags = mum->attr_flags; + return 0; +} + +static int +mbox_set_attr_flags (attribute_t attr, int flags) +{ + message_t msg = attribute_get_owner (attr); + mbox_message_t mum = message_get_owner (msg); + + if (mum == NULL) + return EINVAL; + mum->attr_flags |= flags; + return 0; +} + +static int +mbox_unset_attr_flags (attribute_t attr, int flags) +{ + message_t msg = attribute_get_owner (attr); + mbox_message_t mum = message_get_owner (msg); + + if (mum == NULL) + return EINVAL; + mum->attr_flags &= ~flags; + return 0; +} + +static int +mbox_body_readline (stream_t is, char *buffer, size_t buflen, + off_t off, size_t *pnread) +{ + body_t body = stream_get_owner (is); + message_t msg = body_get_owner (body); + mbox_message_t mum = message_get_owner (msg); + + return mbox_readstream (mum, buffer, buflen, off, pnread, 1, + mum->body, mum->body_end); +} + +static int +mbox_body_read (stream_t is, char *buffer, size_t buflen, + off_t off, size_t *pnread) +{ + body_t body = stream_get_owner (is); + message_t msg = body_get_owner (body); + mbox_message_t mum = message_get_owner (msg); + return mbox_readstream (mum, buffer, buflen, off, pnread, 0, + mum->body, mum->body_end); +} + +static int +mbox_readstream (mbox_message_t mum, char *buffer, size_t buflen, + off_t off, size_t *pnread, int isreadline, + off_t start, off_t end) +{ + size_t nread = 0; + int status = 0; + + if (buffer == NULL || buflen == 0) + { + if (pnread) + *pnread = nread; + return 0; + } + + monitor_rdlock (mum->mud->mailbox->monitor); +#ifdef WITH_PTHREAD + /* read() is cancellation point since we're doing a potentially + long operation. Lets make sure we clean the state. */ + pthread_cleanup_push (mbox_cleanup, (void *)mum->mud->mailbox); +#endif + { + off_t ln = end - (start + off); + if (ln > 0) + { + /* Position the file pointer and the buffer. */ + nread = ((size_t)ln < buflen) ? (size_t)ln : buflen; + if (isreadline) + status = stream_readline (mum->mud->mailbox->stream, buffer, buflen, + start + off, &nread); + else + status = stream_read (mum->mud->mailbox->stream, buffer, nread, + start + off, &nread); + } + } + monitor_unlock (mum->mud->mailbox->monitor); +#ifdef WITH_PTHREAD + pthread_cleanup_pop (0); +#endif + + if (pnread) + *pnread = nread; + return status; +} + +static int +mbox_header_fill (header_t header, char *buffer, size_t len, + off_t off, size_t *pnread) +{ + message_t msg = header_get_owner (header); + mbox_message_t mum = message_get_owner (msg); + size_t j; + /* Since we are filling the header there is no need for the cache headers + discard them. */ + for (j = 0; j < HDRSIZE; j++) + { + if (mum->fhdr[j]) + { + free (mum->fhdr[j]); + mum->fhdr[j] = NULL; + } + } + return mbox_readstream (mum, buffer, len, off, pnread, 0, + mum->header_from_end, mum->body); +} + +static int +mbox_header_get_fvalue (header_t header, const char *name, char *buffer, + size_t buflen, size_t *pnread) +{ + size_t i, fv_len = 0; + message_t msg = header_get_owner (header); + mbox_message_t mum = message_get_owner (msg); + int err = ENOENT; + for (i = 0; i < HDRSIZE; i++) + { + if (*name == *(fhdr_table[i]) && strcasecmp (fhdr_table[i], name) == 0) + { + if (mum->fhdr[i]) + { + fv_len = strlen (mum->fhdr[i]); + if (buffer && buflen > 0) + { + /* For the null. */ + buflen--; + fv_len = (fv_len < buflen) ? fv_len : buflen; + memcpy (buffer, mum->fhdr[i], fv_len); + buffer[fv_len] = '\0'; + } + err = 0; + } + else + err = ENOENT; + break; + } + } + + if (pnread) + *pnread = fv_len; + return err; +} + +static int +mbox_header_size (header_t header, size_t *psize) +{ + message_t msg = header_get_owner (header); + mbox_message_t mum = message_get_owner (msg); + if (mum == NULL) + return EINVAL; + if (psize) + *psize = mum->body - mum->header_from_end; + return 0; +} + +static int +mbox_header_lines (header_t header, size_t *plines) +{ + message_t msg = header_get_owner (header); + mbox_message_t mum = message_get_owner (msg); + if (mum == NULL) + return EINVAL; + if (plines) + *plines = mum->header_lines; + return 0; +} + +static int +mbox_body_size (body_t body, size_t *psize) +{ + message_t msg = body_get_owner (body); + mbox_message_t mum = message_get_owner (msg); + if (mum == NULL) + return EINVAL; + if (psize) + *psize = mum->body_end - mum->body; + return 0; +} + +static int +mbox_stream_size (stream_t stream, off_t *psize) +{ + body_t body = stream_get_owner (stream); + return mbox_body_size (body, (size_t*) psize); +} + +static int +mbox_body_lines (body_t body, size_t *plines) +{ + message_t msg = body_get_owner (body); + mbox_message_t mum = message_get_owner (msg); + if (mum == NULL) + return EINVAL; + if (plines) + *plines = mum->body_lines; + return 0; +} + +static int +mbox_envelope_date (envelope_t envelope, char *buf, size_t len, + size_t *pnwrite) +{ + message_t msg = envelope_get_owner (envelope); + mbox_message_t mum = message_get_owner (msg); + size_t n = 0; + int status; + char buffer[512]; + char *s; + + if (mum == NULL) + return EINVAL; + + status = stream_readline (mum->mud->mailbox->stream, buffer, sizeof(buffer), + mum->header_from, &n); + if (status != 0) + { + if (pnwrite) + *pnwrite = 0; + return status; + } + + /* Format: "From [sender] [date]" */ + /* strlen ("From ") == 5 */ + if (n > 5 && (s = strchr (buffer + 5, ' ')) != NULL) + { + if (buf && len > 0) + { + len--; /* Leave space for the null. */ + strncpy (buf, s + 1, len)[len] = '\0'; + len = strlen (buf); + } + else + len = strlen (s + 1); + } + else + len = 0; + + if (pnwrite) + *pnwrite = len; + return 0; +} + +static int +mbox_envelope_sender (envelope_t envelope, char *buf, size_t len, + size_t *pnwrite) +{ + message_t msg = envelope_get_owner (envelope); + mbox_message_t mum = message_get_owner (msg); + size_t n = 0; + int status; + char buffer[512]; + char *s; + + if (mum == NULL) + return EINVAL; + + status = stream_readline (mum->mud->mailbox->stream, buffer, sizeof(buffer), + mum->header_from, &n); + if (status != 0) + { + if (pnwrite) + *pnwrite = 0; + return status; + } + + /* Format: "From [sender] [date]" */ + /* strlen ("From ") == 5 */ + if (n > 5 && (s = strchr (buffer + 5, ' ')) != NULL) + { + /* Put a NULL to isolate the sender string, make a C string. */ + *s = '\0'; + if (buf && len > 0) + { + l |