Logo Search packages:      
Sourcecode: mailutils version File versions

mbox.c

/* GNU Mailutils -- a suite of utilities for electronic mail
   Copyright (C) 1999, 2000, 2001, 2003,
   2004, 2005 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_transport    __P ((stream_t, mu_transport_t *, mu_transport_t *));
static int mbox_get_transport2         __P ((mbox_message_t, mu_transport_t *, mu_transport_t *));
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);

  /* Alain: 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.

     Sergey: No, it actually breaks reopening the mailbox. We should make
     sure that the sequence mailbox_open();mailbox_close() will catch all
     the changes that might have been done to the mailbox */
  
  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);

  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", decrement 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);
      }
    }
  *pcount = mud->messages_count;
  return 0;
}

/* Alain: 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(). 

   Sergey: Nope, we shouldn't abort. Handling it with MU_EVT_MAILBOX_CORRUPT
   is sensible enough. The caller must decide what's the best approach
   in this case. The simplest one is reopening the mailbox. Imap4d currently
   does that. */

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_transport (stream_t is, mu_transport_t *pin, mu_transport_t *pout)
{
  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_transport2 (mum, pin, pout);
}

static int
mbox_get_transport2 (mbox_message_t mum, mu_transport_t *pin, mu_transport_t *pout)
{
  if (mum == NULL)
    return EINVAL;
  return stream_get_transport2 (mum->mud->mailbox->stream, pin, pout);
}

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 = MU_ERR_NOENT;
  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 = MU_ERR_NOENT;
        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)
      {
        len--; /* leave space for the null */
        strncpy (buf, buffer + 5, len)[len] = '\0';
        len = strlen (buf);
      }
      else
      len = strlen (buffer + 5);
    }
  else
    len = 0;

  if (pnwrite)
    *pnwrite = len;
  return 0;
}

static int
mbox_get_message (mailbox_t mailbox, size_t msgno, message_t *pmsg)
{
  int status;
  mbox_data_t mud = mailbox->data;
  mbox_message_t mum;
  message_t msg = NULL;

  /* Sanity checks.  */
  if (pmsg == NULL)
    return MU_ERR_OUT_PTR_NULL;
  if (mud == NULL)
    return EINVAL;

  /* If we did not start a scanning yet do it now.  */
  if (mud->messages_count == 0)
    {
      status = mbox_scan0 (mailbox, 1, NULL, 0);
      if (status != 0)
      return status;
    }

  /* Second sanity: check the message number.  */
  if (!(mud->messages_count > 0
      && msgno > 0
      && msgno <= mud->messages_count))
    return EINVAL;

  mum = mud->umessages[msgno - 1];

  /* Check if we already have it.  */
  if (mum->message)
    {
      if (pmsg)
      *pmsg = mum->message;
      return 0;
    }

  MAILBOX_DEBUG2 (mailbox, MU_DEBUG_TRACE, "mbox_get_message (%s, %d)\n",
              mud->name, msgno);

  /* Get an empty message struct.  */
  status = message_create (&msg, mum);
  if (status != 0)
    return status;

  /* Set the header.  */
  {
    header_t header = NULL;
    status = header_create (&header, NULL, 0, msg);
    if (status != 0)
      {
      message_destroy (&msg, mum);
      return status;
      }
    header_set_fill (header, mbox_header_fill, msg);
    header_set_get_fvalue (header, mbox_header_get_fvalue, msg);
    header_set_size (header, mbox_header_size, msg);
    header_set_lines (header, mbox_header_lines, msg);
    message_set_header (msg, header, mum);
  }

  /* Set the attribute.  */
  {
    attribute_t attribute;
    status = attribute_create (&attribute, msg);
    if (status != 0)
      {
      message_destroy (&msg, mum);
      return status;
      }
    attribute_set_get_flags (attribute, mbox_get_attr_flags, msg);
    attribute_set_set_flags (attribute, mbox_set_attr_flags, msg);
    attribute_set_unset_flags (attribute, mbox_unset_attr_flags, msg);
    message_set_attribute (msg, attribute, mum);
  }

  /* Prepare the body.  */
  {
    body_t body = NULL;
    stream_t stream = NULL;
    if ((status = body_create (&body, msg)) != 0
      || (status = stream_create (&stream,
                            mailbox->flags | MU_STREAM_SEEKABLE,
                            body)) != 0)
      {
      body_destroy (&body, msg);
      stream_destroy (&stream, body);
      message_destroy (&msg, mum);
      return status;
      }
    stream_set_read (stream, mbox_body_read, body);
    stream_set_readline (stream, mbox_body_readline, body);
    stream_set_get_transport2 (stream, mbox_get_body_transport, body);
    stream_set_size (stream, mbox_stream_size, body);
    body_set_stream (body, stream, msg);
    body_set_size (body, mbox_body_size, msg);
    body_set_lines (body, mbox_body_lines, msg);
    message_set_body (msg, body, mum);
  }

  /* Set the envelope.  */
  {
    envelope_t envelope= NULL;
    status = envelope_create (&envelope, msg);
    if (status != 0)
      {
      message_destroy (&msg, mum);
      return status;
      }
    envelope_set_sender (envelope, mbox_envelope_sender, msg);
    envelope_set_date (envelope, mbox_envelope_date, msg);
    message_set_envelope (msg, envelope, mum);
  }

  /* Set the UID.  */
  message_set_uid (msg, mbox_message_uid, mum);

  /* Attach the message to the mailbox mbox data.  */
  mum->message = msg;
  message_set_mailbox (msg, mailbox, mum);

  *pmsg = msg;
  return 0;
}

static int
mbox_append_message (mailbox_t mailbox, message_t msg)
{
  int status = 0;
  mbox_data_t mud = mailbox->data;

  if (msg == NULL || mud == NULL)
    return EINVAL;

  MAILBOX_DEBUG1 (mailbox, MU_DEBUG_TRACE, "mbox_append_message (%s)\n",
              mud->name);

  switch (mud->state)
    {
    case MBOX_NO_STATE:
      if ((status = locker_lock (mailbox->locker)) != 0)
      {
        MAILBOX_DEBUG1 (mailbox, MU_DEBUG_TRACE,
                    "mbox_append_message: %s\n",
                    mu_strerror(status));
        return status;
      }

    default:
      {
      off_t size;
      /* Move to the end of the file, not necesary if _APPEND mode.  */
      if ((status = stream_size (mailbox->stream, &size)) != 0
          || (status = mbox_append_message0 (mailbox, msg, &size, 0, 0)) != 0)
        {
          if (status != EAGAIN)
            locker_unlock (mailbox->locker);
          return status;
        }
      }
    }
  locker_unlock (mailbox->locker);
  return 0;
}

/* FIXME: We need to escape body line that begins with "From ", this
   will required to read the body by line instead of by chuncks hurting
   perfomance big time when expunging.  But should not this be the
   responsability of the client ?  */
static int
mbox_append_message0 (mailbox_t mailbox, message_t msg, off_t *psize,
                  int is_expunging, int first)
{
  mbox_data_t mud = mailbox->data;
  int status = 0;
  size_t n = 0;
  char nl = '\n';
  size_t orig_size = *psize;
  
  switch (mud->state)
    {
    case MBOX_NO_STATE:
      /* Allocate memory for the sender/date buffers.  */
      mud->sender = calloc (128, sizeof (char));
      if (mud->sender == NULL)
      return ENOMEM;
      mud->date = calloc (128, sizeof (char));
      if (mud->date == NULL)
      {
        free (mud->sender);
        mud->sender = NULL;
        return ENOMEM;
      }
      mud->off = 0;
      mud->state = MBOX_STATE_APPEND_SENDER;

    case MBOX_STATE_APPEND_SENDER:
      /* Generate the sender for the "From " separator.  */
      {
      char *s;
      size_t len = 0;
      envelope_t envelope = NULL;
      message_get_envelope (msg, &envelope);
      status = envelope_sender (envelope, mud->sender, 128, &len);
      if (status != 0)
        {
          if (status != EAGAIN)
            {
            free (mud->sender);
            free (mud->date);
            mud->date = mud->sender = NULL;
            mud->state = MBOX_NO_STATE;
            }
          return status;
        }

      /* Nuke trailing newline.  */
      s = memchr (mud->sender, nl, len);
      if (s)
        *s = '\0';
      mud->state = MBOX_STATE_APPEND_DATE;
      }

    case MBOX_STATE_APPEND_DATE:
      /* Generate a date for the "From "  separator.  */
      {
      char *s;
      size_t len = 0;
      envelope_t envelope = NULL;
      char buffer[1024];
      message_get_envelope (msg, &envelope);
      status = envelope_date (envelope, mud->date, 128, &len);
      if (status != 0)
        {
          if (status != EAGAIN)
            {
            free (mud->sender);
            free (mud->date);
            mud->date = mud->sender = NULL;
            mud->state = MBOX_NO_STATE;
            }
          return status;
        }

      /* Nuke trailing newline.  */
      s = memchr (mud->date, nl, len);
      if (s)
        *s = '\0';

      /* Write the separator to the mailbox.  */
      n = snprintf (buffer, sizeof (buffer), "From %s %s",
                  mud->sender, mud->date);
      status = stream_write (mailbox->stream, buffer, n, *psize, &n);
      if (status)
        break;
      *psize += n;

      /* Add the newline, the above may be truncated.  */
      status = stream_write (mailbox->stream, &nl , 1, *psize, &n);
      if (status)
        break;
      *psize += n;

      free (mud->sender);
      free (mud->date);
      mud->sender = mud->date = NULL;
      /* If we are not expunging get the message in one block via the stream
         message instead of the header/body.  This is good for POP where
         there is no separation between header and body(RETR).  */
      if (! is_expunging)
        {
          mud->state = MBOX_STATE_APPEND_MESSAGE;
          break;
        }
      mud->state = MBOX_STATE_APPEND_HEADER;
      }

    case MBOX_STATE_APPEND_HEADER:
      /* Append the Header.  */
      {
      char buffer[1024];
      size_t nread = 0;
      stream_t is = NULL;
      header_t header = NULL;
      message_get_header (msg, &header);
      header_get_stream (header, &is);
      do
        {
          status = stream_readline (is, buffer, sizeof (buffer), mud->off,
                              &nread);
          if (status != 0)
            {
            if (status != EAGAIN)
              {
                mud->state = MBOX_NO_STATE;
                mud->off = 0;
              }
            stream_truncate (mailbox->stream, orig_size);
            return status;
            }
          mud->off += nread;
          if (*buffer == '\n')
            break;

          /* We do not copy the Status since it is rewritten by the
             attribute code below. Ditto for X-UID and X-IMAPBase.
             FIXME:
             - We have a problem here the header may not fit the buffer.
             - Should  we skip the IMAP "X-Status"? */
          if ((strncasecmp (buffer, "Status", 6) == 0)
            || (strncasecmp (buffer, "X-IMAPbase", 10) == 0)
            /* FIXME: isn't the length of "X-UID" 5, not 4? And
             this will match X-UID and X-UIDL, is this intended? */
            || (strncasecmp (buffer, "X-UID", 4) == 0
                && (buffer[5] == ':' || buffer[5] == ' '
                  || buffer[5] == '\t')))
            continue;

          status = stream_write (mailbox->stream, buffer, nread,
                            *psize, &n);
          if (status)
            break;
          *psize += n;
        }
      while (nread > 0);
      mud->off = 0;

      /* Rewrite the X-IMAPbase marker. */
      if (first && is_expunging)
        {
          n = sprintf (buffer, "X-IMAPbase: %lu %u\n",
                   (unsigned long) mud->uidvalidity,
                   (unsigned) mud->uidnext);
          status = stream_write (mailbox->stream, buffer, n, *psize, &n);
          if (status)
            break;
          *psize += n;
        }
      mud->state = MBOX_STATE_APPEND_ATTRIBUTE;
      }

      case MBOX_STATE_APPEND_ATTRIBUTE:
      /* Put the new attributes.  */
      {
      char abuf[64];
      size_t na = 0;
      attribute_t attr = NULL;
      abuf[0] = '\0';
      message_get_attribute (msg, &attr);
      attribute_to_string (attr, abuf, sizeof(abuf), &na);

      status = stream_write (mailbox->stream, abuf, na, *psize, &n);
      if (status != 0)
        break;
      *psize += n;

      mud->state = MBOX_STATE_APPEND_UID;
      }

    case MBOX_STATE_APPEND_UID:
      /* The new X-UID. */
      {
      char suid[64];
      size_t uid = 0;
      if (is_expunging)
        {
          status = message_get_uid (msg, &uid);
          if (status == EAGAIN)
            return status;
        }
      else
        uid = mud->uidnext++;

      if (status == 0 || uid != 0)
        {
          n = sprintf (suid, "X-UID: %u\n", (unsigned) uid);
          /* Put the UID.  */
          status = stream_write (mailbox->stream, suid, n, *psize, &n);
          if (status)
            break;
          *psize += n;
        }

      /* New line separator of the Header.  */
      status = stream_write (mailbox->stream, &nl , 1, *psize, &n);
      if (status)
        break;
      *psize += n;
      mud->state = MBOX_STATE_APPEND_BODY;
      }

    case MBOX_STATE_APPEND_BODY:
      /* Append the Body.  */
      {
      char buffer[1024];
      size_t nread = 0;
      stream_t is = NULL;
      body_t body = NULL;
      message_get_body (msg, &body);
      body_get_stream (body, &is);
      do
        {
          status = stream_read (is, buffer, sizeof (buffer), mud->off,
                          &nread);
          if (status != 0)
            {
            if (status != EAGAIN)
              {
                mud->state = MBOX_NO_STATE;
                mud->off = 0;
              }
            return status;
            }
          mud->off += nread;
          status = stream_write (mailbox->stream, buffer, nread, *psize, &n);
          if (status)
            break;
          *psize += n;
        }
      while (nread > 0);
      mud->off = 0;
      n = 0;
      status = stream_write (mailbox->stream, &nl, 1, *psize, &n);
      if (status)
        break;
      *psize += n;
      }

    default:
      break;
    }

  /* If not expunging we are taking the stream message.  */
  if (!is_expunging)
    {
      switch (mud->state)
        {
      case MBOX_STATE_APPEND_MESSAGE:
        {
          /* Append the Message.  */
          char buffer[1024];
          size_t nread = 0;
          stream_t is = NULL;
          message_get_stream (msg, &is);
          do
            {
            status = stream_read (is, buffer, sizeof (buffer), mud->off,
                              &nread);
            if (status != 0)
              {
                if (status != EAGAIN)
                  {
                  mud->state = MBOX_NO_STATE;
                  mud->off = 0;
                  }
                stream_truncate (mailbox->stream, orig_size);
                return status;
              }
            status = stream_write (mailbox->stream, buffer, nread,
                               *psize, &n);
            if (status)
              break;
            mud->off += nread;
            *psize += n;
            }
          while (nread > 0);
          n = 0;
                status = stream_write (mailbox->stream, &nl, 1, *psize, &n);
          if (status)
            break;
          *psize += n;
        }

      default:
        break;
      }
    } /* is_expunging */
  mud->state = MBOX_NO_STATE;
  if (status)
    stream_truncate (mailbox->stream, orig_size);
  else
    stream_flush (mailbox->stream);
  return status;
}

static int
mbox_get_size (mailbox_t mailbox, off_t *psize)
{
  off_t size;
  int status;

  /* Maybe was not open yet ??  */
  status  = stream_size (mailbox->stream, &size);
  if (status != 0)
    return status;
  if (psize)
    *psize = size;
  return 0;
}

static int
mbox_messages_count (mailbox_t mailbox, size_t *pcount)
{
  mbox_data_t mud = mailbox->data;

  if (mud == NULL)
    return EINVAL;

  if (! mbox_is_updated (mailbox))
    return mbox_scan0 (mailbox,  mud->messages_count, pcount, 0);

  if (pcount)
    *pcount = mud->messages_count;

  return 0;
}

/* A "recent" message is the one not marked with MU_ATTRIBUTE_SEEN
   ('O' in the Status header), i.e. a message that is first seen
   by the current session (see attributes.h) */
static int
mbox_messages_recent (mailbox_t mailbox, size_t *pcount)
{
  mbox_data_t mud = mailbox->data;
  mbox_message_t mum;
  size_t j, recent;

  /* If we did not start a scanning yet do it now.  */
  if (mud->messages_count == 0)
    {
      int status = mbox_scan0 (mailbox, 1, NULL, 0);
      if (status != 0)
      return status;
    }
  for (recent = j = 0; j < mud->messages_count; j++)
    {
      mum = mud->umessages[j];
      if (mum && MU_ATTRIBUTE_IS_UNSEEN(mum->attr_flags))
      recent++;
    }
  *pcount = recent;
  return 0;
}

/* An "unseen" message is the one that has not been read yet */
static int
mbox_message_unseen (mailbox_t mailbox, size_t *pmsgno)
{
  mbox_data_t mud = mailbox->data;
  mbox_message_t mum;
  size_t j, unseen;

  /* If we did not start a scanning yet do it now.  */
  if (mud->messages_count == 0)
    {
      int status = mbox_scan0 (mailbox, 1, NULL, 0);
      if (status != 0)
      return status;
    }
  for (unseen = j = 0; j < mud->messages_count; j++)
    {
      mum = mud->umessages[j];
      if (mum && MU_ATTRIBUTE_IS_UNREAD(mum->attr_flags))
      {
        unseen = j + 1;
        break;
      }
    }
  *pmsgno = unseen;
  return 0;
}

static int
mbox_uidvalidity (mailbox_t mailbox, unsigned long *puidvalidity)
{
  mbox_data_t mud = mailbox->data;
  int status = mbox_messages_count (mailbox, NULL);
  if (status != 0)
    return status;
  /* If we did not start a scanning yet do it now.  */
  if (mud->messages_count == 0)
    {
      status = mbox_scan0 (mailbox, 1, NULL, 0);
      if (status != 0)
      return status;
    }
  if (puidvalidity)
    *puidvalidity = mud->uidvalidity;
  return 0;
}

static int
mbox_uidnext (mailbox_t mailbox, size_t *puidnext)
{
  mbox_data_t mud = mailbox->data;
  int status = mbox_messages_count (mailbox, NULL);
  if (status != 0)
    return status;
  /* If we did not start a scanning yet do it now.  */
  if (mud->messages_count == 0)
    {
      status = mbox_scan0 (mailbox, 1, NULL, 0);
      if (status != 0)
      return status;
    }
  if (puidnext)
    *puidnext = mud->uidnext;
  return 0;
}

#ifdef WITH_PTHREAD
void
mbox_cleanup (void *arg)
{
  mailbox_t mailbox = arg;
  monitor_unlock (mailbox->monitor);
  locker_unlock (mailbox->locker);
}
#endif

Generated by  Doxygen 1.6.0   Back to index