Example #1
0
static int
nntp_folder_open (mu_folder_t folder, int flags)
{
  f_nntp_t f_nntp = folder->data;
  mu_stream_t carrier = NULL;
  const char *host;
  unsigned port = MU_NNTP_DEFAULT_PORT; /* default nntp port.  */
  int status = 0;

  /* If we are already open for business, noop.  */
  mu_monitor_wrlock (folder->monitor);
  if (f_nntp->isopen)
    {
      mu_monitor_unlock (folder->monitor);
      return 0;
    }
  mu_monitor_unlock (folder->monitor);

  /* Fetch the server name and the port in the mu_url_t.  */
  status = mu_url_sget_host (folder->url, &host);
  if (status != 0)
    return status;
  mu_url_get_port (folder->url, &port);

  folder->flags = flags;

  /* Create the networking stack.  */
  status = mu_tcp_stream_create (&carrier, host, port, folder->flags);
  if (status != 0)
    return status;
  /* Ask for the stream internal buffering mechanism scheme.  */
  mu_stream_setbufsiz (carrier, BUFSIZ);
  mu_debug (MU_DEBCAT_FOLDER, MU_DEBUG_PROT, ("folder_nntp_open (%s:%ld)", 
             host, port));

  status = mu_nntp_create (&f_nntp->nntp);
  if (status == 0)
    {
      status = mu_nntp_set_carrier (f_nntp->nntp, carrier);
      if (status == 0)
	{
	  status = mu_nntp_connect (f_nntp->nntp);
	  if (status == 0)
	    {
	      mu_monitor_wrlock (folder->monitor);
	      f_nntp->isopen++;
	      mu_monitor_unlock (folder->monitor);
	    }
	}
    }

  return status;
}
Example #2
0
/* The folder is destroy if it is the last reference.  */
void
mu_folder_destroy (mu_folder_t *pfolder)
{
  if (pfolder && *pfolder)
    {
      mu_folder_t folder = *pfolder;
      int destroy_lock = 0;
      mu_monitor_t monitor = folder->monitor;

      mu_monitor_wrlock (monitor);

      /* Check if this the last reference for this folder.  If yes removed
         it from the list.  */
      mu_monitor_wrlock (&folder_lock);
      folder->ref--;
      /* Remove the folder from the list of known folder.  */
      if (folder->ref <= 0)
	mu_list_remove (known_folder_list, folder);
      /* If the list is empty we can safely remove it.  */
      if (mu_list_is_empty (known_folder_list))
	mu_list_destroy (&known_folder_list);

      mu_monitor_unlock (&folder_lock);

      if (folder->ref <= 0)
	{
	  mu_monitor_unlock (monitor);
	  destroy_lock = 1;
	  /* Notify the observers.  */
	  if (folder->observable)
	    {
	      mu_observable_notify (folder->observable, MU_EVT_FOLDER_DESTROY,
				    folder);
	      mu_observable_destroy (&folder->observable, folder);
	    }
	  if (folder->_destroy)
	    folder->_destroy (folder);
	  mu_monitor_wrlock (monitor);
	  if (folder->authority)
	    mu_authority_destroy (&folder->authority, folder);
	  if (folder->url)
	    mu_url_destroy (&folder->url);
	  if (folder->property)
	    mu_property_destroy (&folder->property);
	  free (folder);
	}
      mu_monitor_unlock (monitor);
      if (destroy_lock)
	mu_monitor_destroy (&monitor, folder);
      *pfolder = NULL;
    }
}
Example #3
0
static int
pop_close (mu_mailbox_t mbox)
{
  struct _pop3_mailbox *mpd = mbox->data;
  int status;
  
  status = mu_pop3_quit (mpd->pop3);
  if (status)
    mu_error ("mu_pop3_quit failed: %s", mu_strerror (status));
  status = mu_pop3_disconnect (mpd->pop3);
  if (status)
    mu_error ("mu_pop3_disconnect failed: %s", mu_strerror (status));
  if (mpd->msg)
    {
      size_t i;
      
      mu_monitor_wrlock (mbox->monitor);
      /* Destroy the pop messages and resources associated to them.  */
      for (i = 0; i < mpd->msg_count; i++)
	{
	  if (mpd->msg[i])
	    {
	      mu_message_destroy (&mpd->msg[i]->message, mpd->msg[i]);
	      if (mpd->msg[i]->uidl)
		free (mpd->msg[i]->uidl);
	      free (mpd->msg[i]);
	    }
	}
      mu_monitor_unlock (mbox->monitor);
    }
  mu_stream_destroy (&mpd->cache);
  return 0;
}
Example #4
0
void
mbox_cleanup (void *arg)
{
  mu_mailbox_t mailbox = arg;
  mu_monitor_unlock (mailbox->monitor);
  mu_locker_unlock (mailbox->locker);
}
Example #5
0
/* Free all ressources associated with Unix concrete mailbox.  */
static void
mbox_destroy (mu_mailbox_t mailbox)
{
  if (mailbox->data)
    {
      size_t i;
      mbox_data_t mud = mailbox->data;
      MU_DEBUG1 (mailbox->debug, MU_DEBUG_TRACE1,
		      "mbox_destroy (%s)\n", mud->name);
      mu_monitor_wrlock (mailbox->monitor);
      for (i = 0; i < mud->umessages_count; i++)
	{
	  mbox_message_t mum = mud->umessages[i];
	  if (mum)
	    {
	      mu_message_destroy (&(mum->message), mum);
	      free (mum);
	    }
	}
      if (mud->umessages)
	free (mud->umessages);
      if (mud->name)
	free (mud->name);
      free (mud);
      mailbox->data = NULL;
      mu_monitor_unlock (mailbox->monitor);
    }
}
Example #6
0
static int
nntp_folder_close (mu_folder_t folder)
{
  f_nntp_t f_nntp = folder->data;
  int status = 0;

  mu_monitor_wrlock (folder->monitor);
  f_nntp->isopen--;
  if (f_nntp->isopen)
    {
      mu_monitor_unlock (folder->monitor);
      return 0;
    }
  mu_monitor_unlock (folder->monitor);
  status = mu_nntp_quit (f_nntp->nntp);
  f_nntp->selected = NULL;
  return status;

}
Example #7
0
static int
_registrar_get_list (mu_list_t *plist)
{
  int status = 0;

  if (plist == NULL)
    return MU_ERR_OUT_PTR_NULL;
  mu_monitor_wrlock (&registrar_monitor);
  if (registrar_list == NULL)
    status = mu_list_create (&registrar_list);
  *plist = registrar_list;
  mu_monitor_unlock (&registrar_monitor);
  return status;
}
Example #8
0
static int
mbox_close (mu_mailbox_t mailbox)
{
  mbox_data_t mud = mailbox->data;
  size_t i; 

  if (mud == NULL)
    return EINVAL;

  MU_DEBUG1 (mailbox->debug, MU_DEBUG_TRACE1,  "mbox_close (%s)\n", mud->name);

  /* Make sure that we do not hold any file locking.  */
  mu_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 mu_mailbox_close/mu_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 mu_mailbox_open();mu_mailbox_close() will catch all
     the changes that might have been done to the mailbox */
  
  mu_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)
	{
	  mu_message_destroy (&(mum->message), mum);
	  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;
  mu_monitor_unlock (mailbox->monitor);

  return mu_stream_close (mailbox->stream);
}
Example #9
0
int
mu_registrar_get_iterator (mu_iterator_t *pitr)
{
  int status = 0;
  if (pitr == NULL)
    return MU_ERR_OUT_PTR_NULL;
  mu_monitor_wrlock (&registrar_monitor);
  if (registrar_list == NULL)
    {
      status = mu_list_create (&registrar_list);
      if (status)
	return status;
    }
  status = mu_list_get_iterator (registrar_list, pitr);
  mu_monitor_unlock (&registrar_monitor);
  return status;
}
Example #10
0
static int
mbox_readstream (mbox_message_t mum, char *buffer, size_t buflen,
		 mu_off_t off, size_t *pnread, int isreadline,
		 mu_off_t start, mu_off_t end)
{
  size_t nread = 0;
  int status = 0;

  if (buffer == NULL || buflen == 0)
    {
      if (pnread)
	*pnread = nread;
      return 0;
    }

  mu_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
  {
    mu_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 = mu_stream_readline (mum->mud->mailbox->stream,
				       buffer, buflen,
				       start + off, &nread);
	else
	  status = mu_stream_read (mum->mud->mailbox->stream, buffer, nread,
				   start + off, &nread);
      }
  }
  mu_monitor_unlock (mum->mud->mailbox->monitor);
#ifdef WITH_PTHREAD
  pthread_cleanup_pop (0);
#endif

  if (pnread)
    *pnread = nread;
  return status;
}
Example #11
0
int
mu_list_append (mu_list_t list, void *item)
{
  struct list_data *ldata;
  struct list_data *last;

  if (list == NULL)
    return EINVAL;
  last = list->head.prev;
  ldata = calloc (sizeof (*ldata), 1);
  if (ldata == NULL)
    return ENOMEM;
  ldata->item = item;
  mu_monitor_wrlock (list->monitor);
  ldata->next = &list->head;
  ldata->prev = list->head.prev;
  last->next = ldata;
  list->head.prev = ldata;
  list->count++;
  mu_monitor_unlock (list->monitor);
  return 0;
}
Example #12
0
/*  Cleaning up all the ressources associate with a newsgroup/mailbox.  */
static void
nntp_mailbox_destroy (mu_mailbox_t mbox)
{
  if (mbox->data)
    {
      m_nntp_t m_nntp = mbox->data;
      f_nntp_t f_nntp = m_nntp->f_nntp;
      size_t i;

      /* Deselect.  */
      if (m_nntp == f_nntp->selected)
	f_nntp->selected = NULL;

      mu_monitor_wrlock (mbox->monitor);

      if (m_nntp->name)
	free (m_nntp->name);

      /* Destroy the nntp messages and ressources associated to them.  */
      for (i = 0; i < m_nntp->messages_count; i++)
	{
	  if (m_nntp->messages[i])
	    {
	      mu_message_destroy (&(m_nntp->messages[i]->message), m_nntp->messages[i]);
	      if (m_nntp->messages[i]->mid)
		free (m_nntp->messages[i]->mid);
	      free (m_nntp->messages[i]);
	      m_nntp->messages[i] = NULL;
	    }
	}
      if (m_nntp->messages)
	free (m_nntp->messages);
      free (m_nntp);
      mbox->data = NULL;
      mu_monitor_unlock (mbox->monitor);
    }
}
Example #13
0
/* We can not close the folder in term of shuting down the connection but if
   we were the selected mailbox/newsgroup we deselect ourself.  */
static int
nntp_mailbox_close (mu_mailbox_t mailbox)
{
  m_nntp_t m_nntp = mailbox->data;
  f_nntp_t f_nntp = m_nntp->f_nntp;
  int i;

  mu_monitor_wrlock (mailbox->monitor);

  /* Destroy the nntp posts and ressources associated to them.  */
  for (i = 0; i < m_nntp->messages_count; i++)
    {
      if (m_nntp->messages[i])
	{
	  msg_nntp_t msg_nntp = m_nntp->messages[i];
	  if (msg_nntp->message)
	    mu_message_destroy (&(msg_nntp->message), msg_nntp);
	}
      free (m_nntp->messages[i]);
    }
  if (m_nntp->messages)
    free (m_nntp->messages);
  m_nntp->messages = NULL;
  m_nntp->messages_count = 0;
  m_nntp->number = 0;
  m_nntp->low = 0;
  m_nntp->high = 0;
  mu_monitor_unlock (mailbox->monitor);

  /* Deselect.  */
  if (m_nntp != f_nntp->selected)
    f_nntp->selected = NULL;

  /* Decrement the ref count. */
  return mu_folder_close (mailbox->folder);
}
Example #14
0
/* A folder could be remote (IMAP), or local(a spool directory) like $HOME/Mail
   etc ..  We maintain a list of known folders to avoid creating multiple
   folders for the same URL.  So, when mu_folder_create is called we check if
   we already have a folder for that URL and return it, otherwise we create a
   new one.  Downsides: the scheme to detect the same URL is very weak, and
   there could be cases where you'll want a different folder for the same URL,
   there is not easy way to do this.  */
int
mu_folder_create_from_record (mu_folder_t *pfolder, mu_url_t url,
			      mu_record_t record)
{
  if (!pfolder)
    return MU_ERR_OUT_PTR_NULL;

  if (record || 
      /* Look in the registrar list(iterator), for a possible concrete mailbox
	 implementation that could match the URL.  */
      mu_registrar_lookup_url (url, MU_FOLDER_ATTRIBUTE_DIRECTORY, &record,
			       NULL) == 0)
    {
      int (*f_init) (mu_folder_t) = NULL;
      
      mu_record_get_folder (record, &f_init);
      if (f_init)
        {
	  int status, mask;
	  mu_folder_t folder;
	  int (*u_init) (mu_url_t) = NULL;

	  status = mu_record_check_url (record, url, &mask);
	  if (status)
	    /* FIXME: mask would provide more info */
	    return status;
	  
	  mu_record_get_url (record, &u_init);
	  if (u_init)
	    {
	      status = u_init (url);
	      if (status)
		return status;
	    }
	  
	  mu_monitor_wrlock (&folder_lock);

	  /* Check if we already have the same URL folder.  */
	  if (is_known_folder (url, &folder))
	    {
	      folder->ref++;
	      *pfolder = folder;
	      mu_url_destroy (&url); /* FIXME: Hmm */
	      mu_monitor_unlock (&folder_lock);
	      return  0;
	    }
	  else
	    mu_monitor_unlock (&folder_lock);
	  
	  /* Create a new folder.  */

	  /* Allocate memory for the folder.  */
	  folder = calloc (1, sizeof (*folder));
	  if (folder != NULL)
	    {
	      folder->url = url;
	      /* Initialize the internal foilder lock, now so the
		 concrete folder could use it.  */
	      status = mu_monitor_create (&folder->monitor, 0, folder);
	      if (status == 0)
		{
		  /* Create the concrete folder type.  */
		  status = f_init (folder);
		  if (status == 0)
		    {
		      if (!folder->_match)
			folder->_match = mu_folder_imap_match;
		      *pfolder = folder;
		      folder->ref++;
		      /* Put on the internal list of known folders.  */
		      if (known_folder_list == NULL)
			mu_list_create (&known_folder_list);
		      mu_list_append (known_folder_list, folder);
		    }
		}
	      /* Something went wrong, destroy the object. */
	      if (status)
		{
		  if (folder->monitor)
		    mu_monitor_destroy (&folder->monitor, folder);
		  free (folder);
		}
	    }
	  return status;
	}
    }

  return MU_ERR_NOENT;
}
Example #15
0
static int
nntp_mailbox_get_message (mu_mailbox_t mbox, size_t msgno, mu_message_t *pmsg)
{
  m_nntp_t m_nntp = mbox->data;
  msg_nntp_t msg_nntp;
  mu_message_t msg = NULL;
  int status;
  size_t i;

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

 msgno--;
  mu_monitor_rdlock (mbox->monitor);
  /* See if we have already this message.  */
  for (i = 0; i < m_nntp->messages_count; i++)
    {
      if (m_nntp->messages[i])
	{
	  if (m_nntp->messages[i]->msgno == msgno + m_nntp->low)
	    {
	      *pmsg = m_nntp->messages[i]->message;
	      mu_monitor_unlock (mbox->monitor);
	      return 0;
	    }
	}
    }
  mu_monitor_unlock (mbox->monitor);

  msg_nntp = calloc (1, sizeof (*msg_nntp));
  if (msg_nntp == NULL)
    return ENOMEM;

  /* Back pointer.  */
  msg_nntp->m_nntp = m_nntp;
  msg_nntp->msgno = msgno + m_nntp->low;

  /* Create the message.  */
  {
    mu_stream_t stream = NULL;
    if ((status = mu_message_create (&msg, msg_nntp)) != 0
	|| (status = mu_stream_create (&stream, mbox->flags, msg)) != 0)
      {
	mu_stream_destroy (&stream, msg);
	mu_message_destroy (&msg, msg_nntp);
	free (msg_nntp);
	return status;
      }
    /* Help for the readline()s  */
    mu_stream_set_read (stream, nntp_message_read, msg);
    mu_stream_set_get_transport2 (stream, nntp_message_get_transport2, msg);
    mu_message_set_stream (msg, stream, msg_nntp);
    mu_message_set_size (msg, nntp_message_size, msg_nntp);
  }

  /* Create the header.  */
  {
    mu_header_t header = NULL;
    if ((status = mu_header_create (&header, NULL, 0,  msg)) != 0)
      {
	mu_message_destroy (&msg, msg_nntp);
	free (msg_nntp);
	return status;
      }
    mu_header_set_fill (header, nntp_header_fill, msg);
    mu_message_set_header (msg, header, msg_nntp);
  }

  /* Create the body and its stream.  */
  {
    mu_body_t body = NULL;
    mu_stream_t stream = NULL;
    if ((status = mu_body_create (&body, msg)) != 0
	|| (status = mu_stream_create (&stream, mbox->flags, body)) != 0)
      {
	mu_body_destroy (&body, msg);
	mu_stream_destroy (&stream, body);
	mu_message_destroy (&msg, msg_nntp);
	free (msg_nntp);
	return status;
      }
    /* Helps for the readline()s  */
    mu_stream_set_read (stream, nntp_body_read, body);
    mu_stream_set_get_transport2 (stream, nntp_body_get_transport2, body);
    mu_body_set_size (body, nntp_body_size, msg);
    mu_body_set_lines (body, nntp_body_lines, msg);
    mu_body_set_stream (body, stream, msg);
    mu_message_set_body (msg, body, msg_nntp);
  }

  /* Set the UID on the message. */
  mu_message_set_uid (msg, nntp_message_uid, msg_nntp);

  /* Add it to the list.  */
  mu_monitor_wrlock (mbox->monitor);
  {
    msg_nntp_t *m ;
    m = realloc (m_nntp->messages, (m_nntp->messages_count + 1)*sizeof (*m));
    if (m == NULL)
      {
	mu_message_destroy (&msg, msg_nntp);
	free (msg_nntp);
	mu_monitor_unlock (mbox->monitor);
	return ENOMEM;
      }
    m_nntp->messages = m;
    m_nntp->messages[m_nntp->messages_count] = msg_nntp;
    m_nntp->messages_count++;
  }
  mu_monitor_unlock (mbox->monitor);

  /* Save The message pointer.  */
  mu_message_set_mailbox (msg, mbox, msg_nntp);
  *pmsg = msg_nntp->message = msg;

  return 0;
}
Example #16
0
/* 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 (mu_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.  */
  mu_off_t marker = 0;    /* marker will be the position to truncate.  */
  mu_off_t total = 0;
  char *tmpmboxname = NULL;
  mu_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;

  MU_DEBUG1 (mailbox->debug, MU_DEBUG_TRACE1, "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 && mu_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"));
      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))
	    mu_message_destroy (&(mum->message), mum);
	}
    }

  /* Create temporary mu_mailbox_t.  */
  {
    mbox_data_t tmp_mud;
    char *m = malloc (5 + strlen (tmpmboxname) + 1);
    if (!m)
      return ENOMEM;
    /* Try via the mbox: protocol.  */
    sprintf (m, "mbox:%s", tmpmboxname);
    status = mu_mailbox_create (&tmpmailbox, m);
    if (status != 0)
      {
	/* Do not give up just yet, maybe they register the mu_path_record.  */
	sprintf (m, "%s", tmpmboxname);
	status = mu_mailbox_create (&tmpmailbox, m);
	if (status != 0)
	  {
	    /* Ok give up.  */
	    close (tempfile);
	    remove (tmpmboxname);
            free (m);
	    free (tmpmboxname);
	    return status;
	  }
      }
    free (m);
    /* Must be flag CREATE if not the mu_mailbox_open will try to mmap()
       the file.  */
    status = mu_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 = mu_locker_lock (mailbox->locker)) != 0)
    {
      mu_mailbox_close (tmpmailbox);
      mu_mailbox_destroy (&tmpmailbox);
      remove (tmpmboxname);
      free (tmpmboxname);
      mu_error (_("failed to grab the lock: %s"), 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 && mu_message_is_modified (mum->message)))
	{
	  /* The message was not instantiated, probably the dirty flag was
	     set by mbox_scan(), create one here.  */
	  if (mum->message == 0)
	    {
	      mu_message_t msg;
	      status = mbox_get_message (mailbox, i + 1, &msg);
	      if (status != 0)
		{
		  mu_error (_("error expunging:%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 expunging:%d: %s"), __LINE__,
		        mu_strerror (status));
	      goto bailout0;
	    }
	  /* Clear the dirty bits.  */
	  mum->attr_flags &= ~MU_ATTRIBUTE_MODIFIED;
	  mu_message_clear_modified (mum->message);
	}
      else
	{
	  /* Nothing changed copy the message straight.  */
	  char buffer [1024];
	  size_t n;
	  mu_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 = mu_stream_read (mailbox->stream, buffer, n, offset,
					 &n) != 0)
		  || (status = mu_stream_write (tmpmailbox->stream, buffer, n,
					     total, &n) != 0))
		{
		  mu_error (_("error expunging:%d: %s"), __LINE__,
			    mu_strerror (status));
		  goto bailout0;
		}
	      len -= n;
	      total += n;
	      offset += n;
	    }
	  /* Add the newline separator.  */
	  status = mu_stream_write (tmpmailbox->stream, "\n", 1, total, &n);
	  if (status != 0)
	    {
	      mu_error (_("error expunging:%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.  */
  {
    mu_off_t size = 0;
    if (mu_stream_size (mailbox->stream, &size) == 0)
      {
	mu_off_t len = size - mud->size;
	mu_off_t offset = mud->size;
	char buffer [1024];
	size_t n = 0;
	if (len > 0 )
	  {
	    while ((status = mu_stream_read (mailbox->stream, buffer,
					  sizeof (buffer), offset, &n)) == 0
		   && n > 0)
	      {
		status = mu_stream_write (tmpmailbox->stream, buffer, n,
				       total, &n);
		if (status != 0)
		  {
		    mu_error (_("error expunging:%d: %s"), __LINE__,
			      mu_strerror (status));
		    goto bailout0;
		  }
		total += n;
		offset += n;
	      }
	  }
	else if (len < 0)
	  {
	    /* Corrupted mailbox.  */
	    mu_error (_("error expunging:%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;
      mu_off_t off = 0;
      mu_off_t offset = marker;
      while ((status = mu_stream_read (tmpmailbox->stream, buffer,
				    sizeof (buffer), off, &n)) == 0
	     && n > 0)
	{
	  status = mu_stream_write (mailbox->stream, buffer, n, offset, &n);
	  if (status != 0)
	    {
	      mu_error (_("error expunging:%d: %s"), __LINE__,
		        mu_strerror (status));
	      goto bailout;
	    }
	  off += n;
	  offset += n;
	}
    }

  /* Flush/truncation. Need to flush before truncate.  */
  mu_stream_flush (mailbox->stream);
  status = mu_stream_truncate (mailbox->stream, total + marker);
  if (status != 0)
    {
      mu_error (_("error expunging:%d: %s"), __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.  */
  mu_locker_unlock (mailbox->locker);
  mu_mailbox_close (tmpmailbox);
  mu_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;
      mu_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
		  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
		{
		  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;
	}
      mu_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;
}