summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2003-03-13 13:35:33 +0000
committerSergey Poznyakoff <gray@gnu.org.ua>2003-03-13 13:35:33 +0000
commit11bc148b1877b7c692f28d90847bc74737068416 (patch)
treef7ab6afb152098c504a5a30623efdf38d9837d59
parent887e7b3b191eccce681c68947204abad634f8553 (diff)
downloadmailutils-11bc148b1877b7c692f28d90847bc74737068416.tar.gz
mailutils-11bc148b1877b7c692f28d90847bc74737068416.tar.bz2
'Moved from ../'
-rw-r--r--mailbox/imap/folder.c2283
-rw-r--r--mailbox/imap/mbox.c2256
-rw-r--r--mailbox/imap/url.c87
-rw-r--r--mailbox/maildir/mbox.c224
-rw-r--r--mailbox/mbox/folder.c427
-rw-r--r--mailbox/mbox/mbox.c1753
-rw-r--r--mailbox/mbox/mboxscan.c856
-rw-r--r--mailbox/mbox/url.c258
-rw-r--r--mailbox/mh/folder.c56
-rw-r--r--mailbox/mh/url.c77
-rw-r--r--mailbox/pop/folder.c138
-rw-r--r--mailbox/pop/mbox.c2115
-rw-r--r--mailbox/pop/url.c76
13 files changed, 10606 insertions, 0 deletions
diff --git a/mailbox/imap/folder.c b/mailbox/imap/folder.c
new file mode 100644
index 000000000..b0d971b31
--- /dev/null
+++ b/mailbox/imap/folder.c
@@ -0,0 +1,2283 @@
+/* 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 */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifdef ENABLE_IMAP
+
+#include <stdlib.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <string.h>
+#include <assert.h>
+#include <fnmatch.h>
+
+#ifdef HAVE_ALLOCA_H
+# include <alloca.h>
+#endif
+
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+
+#include <imap0.h>
+#include <url0.h>
+
+#include <mailutils/auth.h>
+#include <mailutils/attribute.h>
+#include <mailutils/debug.h>
+#include <mailutils/error.h>
+#include <mailutils/header.h>
+#include <mailutils/observer.h>
+#include <mailutils/stream.h>
+
+/* For dbg purposes set to one to see different level of traffic. */
+/* Print to stderr the command sent to the IMAP server. */
+#define DEBUG_SHOW_COMMAND 0
+/* Print to stderr the responses received from the IMAP server. */
+#define DEBUG_SHOW_RESPONSE 0
+/* Print to stderr the literal/quoted string received from the IMAP server. */
+#define DEBUG_SHOW_DATA 0
+
+/* Variable use for the registrar. */
+static struct _record _imap_record =
+{
+ MU_IMAP_SCHEME,
+ _url_imap_init, /* url entry. */
+ _mailbox_imap_init, /* Mailbox entry. */
+ NULL, /* Mailer entry. */
+ _folder_imap_init, /* Folder entry. */
+ NULL, /* No need for a back pointer. */
+ NULL, /* _is_scheme method. */
+ NULL, /* _get_url method. */
+ NULL, /* _get_mailbox method. */
+ NULL, /* _get_mailer method. */
+ NULL /* _get_folder method. */
+};
+
+/* We export this variable: url parsing and the initialisation of the mailbox,
+ via the register entry/record. */
+record_t imap_record = &_imap_record;
+
+#ifndef HAVE_STRTOK_R
+char *strtok_r __P ((char *, const char *, char **));
+#endif
+
+/* Concrete folder_t IMAP implementation. */
+static int folder_imap_open __P ((folder_t, int));
+static int folder_imap_close __P ((folder_t));
+static void folder_imap_destroy __P ((folder_t));
+static int folder_imap_delete __P ((folder_t, const char *));
+static int folder_imap_list __P ((folder_t, const char *, const char *,
+ struct folder_list *));
+static int folder_imap_lsub __P ((folder_t, const char *, const char *,
+ struct folder_list *));
+static int folder_imap_rename __P ((folder_t, const char *,
+ const char *));
+static int folder_imap_subscribe __P ((folder_t, const char *));
+static int folder_imap_unsubscribe __P ((folder_t, const char *));
+static int folder_imap_get_authority __P ((folder_t, authority_t *));
+
+static int authenticate_imap_login __P ((authority_t));
+static int authenticate_imap_sasl_anon __P ((authority_t));
+
+/* FETCH */
+static int imap_fetch __P ((f_imap_t));
+static int imap_rfc822 __P ((f_imap_t, char **));
+static int imap_rfc822_size __P ((f_imap_t, char **));
+static int imap_rfc822_header __P ((f_imap_t, char **));
+static int imap_rfc822_text __P ((f_imap_t, char **));
+static int imap_fetch_flags __P ((f_imap_t, char **));
+static int imap_permanentflags __P ((f_imap_t, char **));
+static int imap_flags __P ((char **, int *));
+static int imap_bodystructure __P ((f_imap_t, char **));
+static int imap_body __P ((f_imap_t, char **));
+static int imap_internaldate __P ((f_imap_t, char **));
+
+static int imap_uid __P ((f_imap_t, char **));
+static int imap_status __P ((f_imap_t));
+static int imap_expunge __P ((f_imap_t, unsigned int));
+static int imap_search __P ((f_imap_t));
+
+/* String. */
+static int imap_literal_string __P ((f_imap_t, char **));
+static int imap_string __P ((f_imap_t, char **));
+static int imap_quoted_string __P ((f_imap_t, char **));
+static int imap_mailbox_name_match __P ((const char* pattern, const char* mailbox));
+
+static int imap_token __P ((char *, size_t, char **));
+
+/* Initialize the concrete IMAP mailbox: overload the folder functions */
+int
+_folder_imap_init (folder_t folder)
+{
+ int status;
+ f_imap_t f_imap;
+
+ /* Set the authority early:
+ (1) so we can check for errors.
+ (2) allow the client to get the authority for setting the ticket
+ before the open. */
+ status = folder_imap_get_authority (folder, NULL);
+ if (status != 0)
+ return status;
+
+ f_imap = folder->data = calloc (1, sizeof (*f_imap));
+ if (f_imap == NULL)
+ return ENOMEM;
+
+ f_imap->folder = folder;
+ f_imap->state = IMAP_NO_STATE;
+
+ folder->_destroy = folder_imap_destroy;
+
+ folder->_open = folder_imap_open;
+ folder->_close = folder_imap_close;
+
+ folder->_list = folder_imap_list;
+ folder->_lsub = folder_imap_lsub;
+ folder->_subscribe = folder_imap_subscribe;
+ folder->_unsubscribe = folder_imap_unsubscribe;
+ folder->_delete = folder_imap_delete;
+ folder->_rename = folder_imap_rename;
+
+ return 0;
+}
+
+/* Destroy the folder resources. */
+static void
+folder_imap_destroy (folder_t folder)
+{
+ if (folder->data)
+ {
+ f_imap_t f_imap = folder->data;
+ if (f_imap->buffer)
+ free (f_imap->buffer);
+ if (f_imap->capa)
+ free (f_imap->capa);
+ free (f_imap);
+ folder->data = NULL;
+ }
+}
+
+static int
+folder_imap_get_authority (folder_t folder, authority_t *pauth)
+{
+ int status = 0;
+ if (folder->authority == NULL)
+ {
+ /* assert (folder->url); */
+ if (folder->url == NULL)
+ return EINVAL;
+
+ if (folder->url->auth == NULL
+ || strcasecmp (folder->url->auth, "*") == 0)
+ {
+ status = authority_create (&folder->authority, NULL, folder);
+ authority_set_authenticate (folder->authority,
+ authenticate_imap_login, folder);
+ }
+ else if (strcasecmp (folder->url->auth, "anon") == 0)
+ {
+ status = authority_create (&folder->authority, NULL, folder);
+ authority_set_authenticate (folder->authority,
+ authenticate_imap_sasl_anon, folder);
+ }
+ else
+ {
+ /* Not a supported authentication mechanism. */
+ status = ENOSYS;
+ }
+ }
+ if (pauth)
+ *pauth = folder->authority;
+ return status;
+}
+
+/* Simple User/pass authentication for imap. */
+static int
+authenticate_imap_login (authority_t auth)
+{
+ folder_t folder = authority_get_owner (auth);
+ f_imap_t f_imap = folder->data;
+ ticket_t ticket;
+ int status = 0;
+
+ switch (f_imap->state)
+ {
+ case IMAP_AUTH:
+ {
+ /* Grab the User and Passwd information. */
+ size_t n = 0;
+ authority_get_ticket (auth, &ticket);
+ if (f_imap->user)
+ free (f_imap->user);
+ if (f_imap->passwd)
+ free (f_imap->passwd);
+ /* Was it in the URL? */
+ status = url_get_user (folder->url, NULL, 0, &n);
+ if (status != 0 || n == 0)
+ ticket_pop (ticket, folder->url, "Imap User: ", &f_imap->user);
+ else
+ {
+ f_imap->user = calloc (1, n + 1);
+ url_get_user (folder->url, f_imap->user, n + 1, NULL);
+ }
+ /* Was it in the URL? */
+ status = url_get_passwd (folder->url, NULL, 0, &n);
+ if (status != 0 || n == 0)
+ ticket_pop (ticket, folder->url, "Imap Passwd: ", &f_imap->passwd);
+ else
+ {
+ f_imap->passwd = calloc (1, n + 1);
+ url_get_passwd (folder->url, f_imap->passwd, n + 1, NULL);
+ }
+
+ if (f_imap->user == NULL || f_imap->passwd == NULL)
+ {
+ CHECK_ERROR_CLOSE (folder, f_imap, EINVAL);
+ }
+ status = imap_writeline (f_imap, "g%u LOGIN %s %s\r\n",
+ f_imap->seq, f_imap->user, f_imap->passwd);
+ CHECK_ERROR_CLOSE(folder, f_imap, status);
+ FOLDER_DEBUG2 (folder, MU_DEBUG_PROT, "g%u LOGIN %s *\n",
+ f_imap->seq, f_imap->user);
+ f_imap->seq++;
+ free (f_imap->user);
+ f_imap->user = NULL;
+ /* We have to nuke the passwd. */
+ memset (f_imap->passwd, '\0', strlen (f_imap->passwd));
+ free (f_imap->passwd);
+ f_imap->passwd = NULL;
+ f_imap->state = IMAP_LOGIN;
+ }
+
+ case IMAP_LOGIN:
+ /* Send it across. */
+ status = imap_send (f_imap);
+ CHECK_EAGAIN (f_imap, status);
+ /* Clear the buffer it contains the passwd. */
+ memset (f_imap->buffer, '\0', f_imap->buflen);
+ f_imap->state = IMAP_LOGIN_ACK;
+
+ case IMAP_LOGIN_ACK:
+ /* Get the login ack. */
+ status = imap_parse (f_imap);
+ CHECK_EAGAIN (f_imap, status);
+ FOLDER_DEBUG0 (folder, MU_DEBUG_PROT, f_imap->buffer);
+ f_imap->state = IMAP_AUTH_DONE;
+
+ default:
+ break; /* We're outta here. */
+ }
+ CLEAR_STATE (f_imap);
+ return 0;
+}
+
+/*
+ The anonymous SASL mechanism is defined in rfc2245.txt as a single
+ message from client to server:
+
+ message = [email / token]
+
+ So the message is optional.
+
+ The command is:
+
+ C: <tag> authenticate anonymous
+
+ The server responds with a request for continuation data (the "message"
+ in the SASL syntax). We respond with no data, which is legal.
+
+ S: +
+ C:
+
+ The server should then respond with OK on success, or else a failure
+ code (NO or BAD).
+
+ If OK, then we are authenticated!
+
+ So, states are:
+
+ AUTH_ANON_REQ
+
+ > g%u AUTHENTICATE ANONYMOUS
+
+ AUTH_ANON_WAIT_CONT
+
+ < +
+
+ AUTH_ANON_MSG
+
+ >
+
+ AUTH_ANON_WAIT_RESP
+
+ < NO/BAD/OK
+
+*/
+
+static int
+authenticate_imap_sasl_anon (authority_t auth)
+{
+ folder_t folder = authority_get_owner (auth);
+ f_imap_t f_imap = folder->data;
+ int status = 0;
+
+ assert (f_imap->state == IMAP_AUTH);
+
+ switch (f_imap->auth_state)
+ {
+ case IMAP_AUTH_ANON_REQ_WRITE:
+ {
+ FOLDER_DEBUG1 (folder, MU_DEBUG_PROT, "g%u AUTHENTICATE ANONYMOUS\n",
+ f_imap->seq);
+ status = imap_writeline (f_imap, "g%u AUTHENTICATE ANONYMOUS\r\n",
+ f_imap->seq);
+ f_imap->seq++;
+ CHECK_ERROR_CLOSE (folder, f_imap, status);
+ f_imap->state = IMAP_AUTH_ANON_REQ_SEND;
+ }
+
+ case IMAP_AUTH_ANON_REQ_SEND:
+ status = imap_send (f_imap);
+ CHECK_EAGAIN (f_imap, status);
+ f_imap->state = IMAP_AUTH_ANON_WAIT_CONT;
+
+ case IMAP_AUTH_ANON_WAIT_CONT:
+ status = imap_parse (f_imap);
+ CHECK_EAGAIN (f_imap, status);
+ FOLDER_DEBUG0 (folder, MU_DEBUG_PROT, f_imap->buffer);
+ if (strncmp ("+", f_imap->buffer, 2) == 0)
+ {
+ f_imap->state = IMAP_AUTH_ANON_MSG;
+ }
+ else
+ {
+ /* Something is wrong! */
+ }
+ f_imap->state = IMAP_AUTH_ANON_MSG;
+
+ case IMAP_AUTH_ANON_MSG:
+ FOLDER_DEBUG0 (folder, MU_DEBUG_PROT, "\n");
+ status = imap_writeline (f_imap, "\r\n");
+ CHECK_ERROR_CLOSE (folder, f_imap, status);
+ f_imap->state = IMAP_AUTH_ANON_MSG_SEND;
+
+ case IMAP_AUTH_ANON_MSG_SEND:
+ status = imap_send (f_imap);
+ CHECK_EAGAIN (f_imap, status);
+
+ f_imap->state = IMAP_AUTH_ANON_WAIT_RESP;
+
+ case IMAP_AUTH_ANON_WAIT_RESP:
+ status = imap_parse (f_imap);
+ CHECK_EAGAIN (f_imap, status);
+ FOLDER_DEBUG0 (folder, MU_DEBUG_PROT, f_imap->buffer);
+
+ default:
+ break; /* We're outta here. */
+ }
+ CLEAR_STATE (f_imap);
+ return 0;
+}
+
+/* Create/Open the stream for IMAP. */
+static int
+folder_imap_open (folder_t folder, int flags)
+{
+ f_imap_t f_imap = folder->data;
+ char *host;
+ long port = 143; /* default imap port. */
+ int status = 0;
+ size_t len = 0;
+
+ /* If we are already open for business, noop. */
+ monitor_wrlock (folder->monitor);
+ if (f_imap->isopen)
+ {
+ monitor_unlock (folder->monitor);
+ return 0;
+ }
+ monitor_unlock (folder->monitor);
+
+ /* Fetch the server name and the port in the url_t. */
+ status = url_get_host (folder->url, NULL, 0, &len);
+ if (status != 0)
+ return status;
+ host = alloca (len + 1);
+ url_get_host (folder->url, host, len + 1, NULL);
+ url_get_port (folder->url, &port);
+
+ folder->flags = flags;
+
+ switch (f_imap->state)
+ {
+ case IMAP_NO_STATE:
+ /* allocate working io buffer. */
+ if (f_imap->buffer == NULL)
+ {
+ /* There is no particular limit on the length of a command/response
+ in IMAP. We start with 255, which is quite reasonnable and grow
+ as we go along. */
+ f_imap->buflen = 255;
+ f_imap->buffer = calloc (f_imap->buflen + 1, 1);
+ if (f_imap->buffer == NULL)
+ {
+ CHECK_ERROR (f_imap, ENOMEM);
+ }
+ status = memory_stream_create (&f_imap->string.stream, NULL, MU_STREAM_RDWR);
+ CHECK_ERROR (f_imap, status);
+ stream_open (f_imap->string.stream);
+ }
+ else
+ {
+ /* Clear from any residue. */
+ memset (f_imap->buffer, '\0', f_imap->buflen);
+ stream_truncate (f_imap->string.stream, 0);
+ f_imap->string.offset = 0;
+ f_imap->string.nleft = 0;
+ }
+ f_imap->ptr = f_imap->buffer;
+
+ /* Create the networking stack. */
+ if (folder->stream == NULL)
+ {
+ status = tcp_stream_create (&folder->stream, host, port, folder->flags);
+ CHECK_ERROR (f_imap, status);
+ /* Ask for the stream internal buffering mechanism scheme. */
+ stream_setbufsiz (folder->stream, BUFSIZ);
+ }
+ else
+ stream_close (folder->stream);
+ FOLDER_DEBUG2 (folder, MU_DEBUG_PROT, "imap_open (%s:%d)\n", host, port);
+ f_imap->state = IMAP_OPEN_CONNECTION;
+
+ case IMAP_OPEN_CONNECTION:
+ /* Establish the connection. */
+ status = stream_open (folder->stream);
+ CHECK_EAGAIN (f_imap, status);
+ /* Can't recover bailout. */
+ CHECK_ERROR_CLOSE (folder, f_imap, status);
+ f_imap->state = IMAP_GREETINGS;
+
+ case IMAP_GREETINGS:
+ {
+ /* Swallow the greetings. */
+ status = imap_readline (f_imap);
+ CHECK_EAGAIN (f_imap, status);
+ f_imap->ptr = f_imap->buffer;
+ FOLDER_DEBUG0 (folder, MU_DEBUG_PROT, f_imap->buffer);
+ /* Are they open for business ? The server send an untag response
+ for greeting. Thenically it can be OK/PREAUTH/BYE. The BYE is
+ the one that we do not want, server being unfriendly. */
+ if (strncasecmp (f_imap->buffer, "* PREAUTH", 9) == 0)
+ {
+ f_imap->state = IMAP_AUTH_DONE;
+ }
+ else
+ {
+ if (strncasecmp (f_imap->buffer, "* OK", 4) != 0)
+ CHECK_ERROR_CLOSE (folder, f_imap, EACCES);
+ f_imap->state = IMAP_AUTH;
+ }
+ }
+
+ case IMAP_AUTH:
+ case IMAP_LOGIN:
+ case IMAP_LOGIN_ACK:
+ assert (folder->authority);
+ {
+ status = authority_authenticate (folder->authority);
+ CHECK_EAGAIN (f_imap, status);
+ }
+
+ case IMAP_AUTH_DONE:
+ default:
+ break;
+ }
+ f_imap->state = IMAP_NO_STATE;
+ monitor_wrlock (folder->monitor);
+ f_imap->isopen++;
+ monitor_unlock (folder->monitor);
+ return 0;
+}
+
+
+/* Shutdown the connection. */
+static int
+folder_imap_close (folder_t folder)
+{
+ f_imap_t f_imap = folder->data;
+ int status = 0;
+
+ monitor_wrlock (folder->monitor);
+ f_imap->isopen--;
+ if (f_imap->isopen)
+ {
+ monitor_unlock (folder->monitor);
+ return 0;
+ }
+ monitor_unlock (folder->monitor);
+
+ switch (f_imap->state)
+ {
+ case IMAP_NO_STATE:
+ status = imap_writeline (f_imap, "g%u LOGOUT\r\n", f_imap->seq++);
+ CHECK_ERROR (f_imap, status);
+ FOLDER_DEBUG0 (folder, MU_DEBUG_PROT, f_imap->buffer);
+ f_imap->state = IMAP_LOGOUT;
+
+ case IMAP_LOGOUT:
+ status = imap_send (f_imap);
+ CHECK_EAGAIN (f_imap, status);
+ f_imap->state = IMAP_LOGOUT_ACK;
+
+ case IMAP_LOGOUT_ACK:
+ /* Check for "* Bye" from the imap server. */
+ status = imap_parse (f_imap);
+ CHECK_EAGAIN (f_imap, status);
+ FOLDER_DEBUG0 (folder, MU_DEBUG_PROT, f_imap->buffer);
+ /* This is done when we received the BYE in the parser code. */
+ /* stream_close (folder->stream); */
+ /* f_imap->isopen = 0 ; */
+
+ default:
+ break;
+ }
+ f_imap->state = IMAP_NO_STATE;
+ f_imap->selected = NULL;
+ return 0;
+}
+
+/* Remove a mailbox. */
+static int
+folder_imap_delete (folder_t folder, const char *name)
+{
+ f_imap_t f_imap = folder->data;
+ int status = 0;
+
+ if (name == NULL)
+ return EINVAL;
+
+ status = folder_open (folder, folder->flags);
+ if (status != 0)
+ return status;
+
+ switch (f_imap->state)
+ {
+ case IMAP_NO_STATE:
+ status = imap_writeline (f_imap, "g%u DELETE %s\r\n", f_imap->seq++,
+ name);
+ CHECK_ERROR (f_imap, status);
+ FOLDER_DEBUG0 (folder, MU_DEBUG_PROT, f_imap->buffer);
+ f_imap->state = IMAP_DELETE;
+
+ case IMAP_DELETE:
+ status = imap_send (f_imap);
+ CHECK_EAGAIN (f_imap, status);
+ f_imap->state = IMAP_DELETE_ACK;
+
+ case IMAP_DELETE_ACK:
+ status = imap_parse (f_imap);
+ CHECK_EAGAIN (f_imap, status);
+ FOLDER_DEBUG0 (folder, MU_DEBUG_PROT, f_imap->buffer);
+
+ default:
+ break;
+ }
+ f_imap->state = IMAP_NO_STATE;
+ return status;
+}
+
+/* Since mailutils API does not offer recursive listing. There is no need
+ to follow IMAP "bizarre" recursive rules. The use of '%' is sufficient. So
+ the approach is everywhere there is a regex in the path we change that
+ branch for '%' and do the matching ourself with fnmatch(). */
+static int
+folder_imap_list (folder_t folder, const char *ref, const char *name,
+ struct folder_list *pflist)
+{
+ f_imap_t f_imap = folder->data;
+ int status = 0;
+ char *path = NULL;
+
+ /* NOOP. */
+ if (pflist == NULL)
+ return EINVAL;
+
+ status = folder_open (folder, folder->flags);
+ if (status != 0)
+ return status;
+
+ if (ref == NULL)
+ ref = "";
+ if (name == NULL)
+ name = "";
+
+ path = strdup ("");
+ if (path == NULL)
+ return ENOMEM;
+
+ /* We break the string to pieces and change the occurences of "*?[" for
+ the imap magic "%" for expansion. Then reassemble the string:
+ "/home/?/Mail/a*lain*" --> "/usr/%/Mail/%". */
+ {
+ int done = 0;
+ size_t i;
+ char **node = NULL;
+ size_t nodelen = 0;
+ const char *p = name;
+ /* Disassemble. */
+ while (!done && *p)
+ {
+ char **n;
+ n = realloc (node, (nodelen + 1) * sizeof (*node));
+ if (n == NULL)
+ break;
+ node = n;
+ if (*p == '/')
+ {
+ node[nodelen] = strdup ("/");
+ p++;
+ }
+ else
+ {
+ const char *s = strchr (p, '/');
+ if (s)
+ {
+ node[nodelen] = calloc (s - p + 1, 1);
+ if (node[nodelen])
+ memcpy (node[nodelen], p, s - p);
+ p = s;
+ }
+ else
+ {
+ node[nodelen] = strdup (p);
+ done = 1;
+ }
+ if (node[nodelen] && strpbrk (node[nodelen], "*?["))
+ {
+ free (node[nodelen]);
+ node[nodelen] = strdup ("%");
+ }
+ }
+ nodelen++;
+ if (done)
+ break;
+ }
+ /* Reassemble. */
+ for (i = 0; i < nodelen; i++)
+ {
+ if (node[i])
+ {
+ char *pth;
+ pth = realloc (path, strlen (path) + strlen (node[i]) + 1);
+ if (pth)
+ {
+ path = pth;
+ strcat (path, node[i]);
+ }
+ free (node[i]);
+ }
+ }
+ if (node)
+ free (node);
+ }
+
+ switch (f_imap->state)
+ {
+ case IMAP_NO_STATE:
+ status = imap_writeline (f_imap, "g%u LIST \"%s\" \"%s\"\r\n",
+ f_imap->seq++, ref, path);
+ free (path);
+ CHECK_ERROR (f_imap, status);
+ FOLDER_DEBUG0 (folder, MU_DEBUG_PROT, f_imap->buffer);
+ f_imap->state = IMAP_LIST;
+
+ case IMAP_LIST:
+ status = imap_send (f_imap);
+ CHECK_EAGAIN (f_imap, status);
+ f_imap->state = IMAP_LIST_ACK;
+
+ case IMAP_LIST_ACK:
+ status = imap_parse (f_imap);
+ CHECK_EAGAIN (f_imap, status);
+ FOLDER_DEBUG0 (folder, MU_DEBUG_PROT, f_imap->buffer);
+
+ default:
+ break;
+ }
+
+ /* Build the folder list. */
+ if (f_imap->flist.num > 0)
+ {
+ struct list_response **plist = NULL;
+ size_t num = f_imap->flist.num;
+ size_t j = 0;
+ plist = calloc (num, sizeof (*plist));
+ if (plist)
+ {
+ size_t i;
+ for (i = 0; i < num; i++)
+ {
+ struct list_response *lr = f_imap->flist.element[i];
+ if (imap_mailbox_name_match (name, lr->name) == 0)
+ {
+ /*
+ FOLDER_DEBUG2(folder, MU_DEBUG_TRACE,
+ "fnmatch against %s: %s - match!\n", name, lr->name);
+ */
+ plist[i] = calloc (1, sizeof (**plist));
+ if (plist[i] == NULL
+ || (plist[i]->name = strdup (lr->name)) == NULL)
+ {
+ break;
+ }
+ plist[i]->type = lr->type;
+ plist[i]->separator = lr->separator;
+ j++;
+ }
+ /*
+ else
+ FOLDER_DEBUG2(folder, MU_DEBUG_TRACE,
+ "fnmatch against %s: %s - no match!\n", name, lr->name);
+ */
+ }
+ }
+ pflist->element = plist;
+ pflist->num = j;
+ }
+ folder_list_destroy (&(f_imap->flist));
+ f_imap->state = IMAP_NO_STATE;
+ return status;
+}
+
+static int
+folder_imap_lsub (folder_t folder, const char *ref, const char *name,
+ struct folder_list *pflist)
+{
+ f_imap_t f_imap = folder->data;
+ int status = 0;
+
+ /* NOOP. */
+ if (pflist == NULL)
+ return EINVAL;
+
+ status = folder_open (folder, folder->flags);
+ if (status != 0)
+ return status;
+
+ if (ref == NULL) ref = "";
+ if (name == NULL) name = "";
+
+ switch (f_imap->state)
+ {
+ case IMAP_NO_STATE:
+ status = imap_writeline (f_imap, "g%u LSUB \"%s\" \"%s\"\r\n",
+ f_imap->seq++, ref, name);
+ CHECK_ERROR (f_imap, status);
+ FOLDER_DEBUG0 (folder, MU_DEBUG_PROT, f_imap->buffer);
+ f_imap->state = IMAP_LSUB;
+
+ case IMAP_LSUB:
+ status = imap_send (f_imap);
+ CHECK_EAGAIN (f_imap, status);
+ f_imap->state = IMAP_LSUB_ACK;
+
+ case IMAP_LSUB_ACK:
+ status = imap_parse (f_imap);
+ CHECK_EAGAIN (f_imap, status);
+ FOLDER_DEBUG0 (folder, MU_DEBUG_PROT, f_imap->buffer);
+
+ default:
+ break;
+ }
+
+ /* Build the folder list. */
+ if (f_imap->flist.num > 0)
+ {
+ struct list_response **plist = NULL;
+ size_t num = f_imap->flist.num;
+ size_t j = 0;
+ plist = calloc (num, sizeof (*plist));
+ if (plist)
+ {
+ size_t i;
+ for (i = 0; i < num; i++)
+ {
+ struct list_response *lr = f_imap->flist.element[i];
+ /* printf ("%s --> %s\n", lr->name, name); */
+ plist[i] = calloc (1, sizeof (**plist));
+ if (plist[i] == NULL
+ || (plist[i]->name = strdup (lr->name)) == NULL)
+ {
+ break;
+ }
+ plist[i]->type = lr->type;
+ plist[i]->separator = lr->separator;
+ j++;
+ }
+ }
+ pflist->element = plist;
+ pflist->num = j;
+ folder_list_destroy (&(f_imap->flist));
+ }
+ f_imap->state = IMAP_NO_STATE;
+ f_imap->state = IMAP_NO_STATE;
+ return 0;
+}
+
+static int
+folder_imap_rename (folder_t folder, const char *oldpath, const char *newpath)
+{
+ f_imap_t f_imap = folder->data;
+ int status = 0;
+ if (oldpath == NULL || newpath == NULL)
+ return EINVAL;
+
+ status = folder_open (folder, folder->flags);
+ if (status != 0)
+ return status;
+
+ switch (f_imap->state)
+ {
+ case IMAP_NO_STATE:
+ status = imap_writeline (f_imap, "g%u RENAME %s %s\r\n",
+ f_imap->seq++, oldpath, newpath);
+ CHECK_ERROR (f_imap, status);
+ FOLDER_DEBUG0 (folder, MU_DEBUG_PROT, f_imap->buffer);
+ f_imap->state = IMAP_RENAME;
+
+ case IMAP_RENAME:
+ status = imap_send (f_imap);
+ CHECK_EAGAIN (f_imap, status);
+ f_imap->state = IMAP_RENAME_ACK;
+
+ case IMAP_RENAME_ACK:
+ status = imap_parse (f_imap);
+ CHECK_EAGAIN (f_imap, status);
+ FOLDER_DEBUG0 (folder, MU_DEBUG_PROT, f_imap->buffer);
+
+ default:
+ break;
+ }
+ f_imap->state = IMAP_NO_STATE;
+ return status;
+}
+
+static int
+folder_imap_subscribe (folder_t folder, const char *name)
+{
+ f_imap_t f_imap = folder->data;
+ int status = 0;
+
+ status = folder_open (folder, folder->flags);
+ if (status != 0)
+ return status;
+
+ if (name == NULL)
+ return EINVAL;
+ switch (f_imap->state)
+ {
+ case IMAP_NO_STATE:
+ status = imap_writeline (f_imap, "g%u SUBSCRIBE %s\r\n",
+ f_imap->seq++, name);
+ CHECK_ERROR (f_imap, status);
+ FOLDER_DEBUG0 (folder, MU_DEBUG_PROT, f_imap->buffer);
+ f_imap->state = IMAP_SUBSCRIBE;
+
+ case IMAP_SUBSCRIBE:
+ status = imap_send (f_imap);
+ CHECK_EAGAIN (f_imap, status);
+ f_imap->state = IMAP_SUBSCRIBE_ACK;
+
+ case IMAP_SUBSCRIBE_ACK:
+ status = imap_parse (f_imap);
+ CHECK_EAGAIN (f_imap, status);
+ FOLDER_DEBUG0 (folder, MU_DEBUG_PROT, f_imap->buffer);
+
+ default:
+ break;
+ }
+ f_imap->state = IMAP_NO_STATE;
+ return status;
+}
+
+static int
+folder_imap_unsubscribe (folder_t folder, const char *name)
+{
+ f_imap_t f_imap = folder->data;
+ int status = 0;
+
+ status = folder_open (folder, folder->flags);
+ if (status != 0)
+ return status;
+
+ if (name == NULL)
+ return EINVAL;
+ switch (f_imap->state)
+ {
+ case IMAP_NO_STATE:
+ status = imap_writeline (f_imap, "g%u UNSUBSCRIBE %s\r\n",
+ f_imap->seq++, name);
+ CHECK_ERROR (f_imap, status);
+ FOLDER_DEBUG0 (folder, MU_DEBUG_PROT, f_imap->buffer);
+ f_imap->state = IMAP_UNSUBSCRIBE;
+
+ case IMAP_UNSUBSCRIBE:
+ status = imap_send (f_imap);
+ CHECK_EAGAIN (f_imap, status);
+ f_imap->state = IMAP_UNSUBSCRIBE_ACK;
+
+ case IMAP_UNSUBSCRIBE_ACK:
+ status = imap_parse (f_imap);
+ CHECK_EAGAIN (f_imap, status);
+ FOLDER_DEBUG0 (folder, MU_DEBUG_PROT, f_imap->buffer);
+
+ default:
+ break;
+ }
+ f_imap->state = IMAP_NO_STATE;
+ return status;
+}
+
+/* A literal is a sequence of zero or more octets (including CR and LF),
+ prefix-quoted with an octet count in the form of an open brace ("{"),
+ the number of octets, close brace ("}"), and CRLF. The sequence is read
+ and put in the string buffer. */
+static int
+imap_literal_string (f_imap_t f_imap, char **ptr)
+{
+ size_t len, len0, total;
+ int status = 0;
+ int nl;
+ /* The (len + 1) in the for is to count the strip '\r' by imap_readline. */
+ for (len0 = len = total = 0; total < f_imap->string.nleft; total += (len + 1))
+ {
+ status = imap_readline (f_imap);
+ if (DEBUG_SHOW_DATA)
+ fprintf (stderr, "%s", f_imap->buffer);
+ if (status != 0)
+ {
+ /* Return what we got so far. */
+ break;
+ }
+ f_imap->ptr = f_imap->buffer;
+
+ /* How much ? */
+ len0 = len = f_imap->nl - f_imap->buffer;
+ /* Check if the last read did not finish on a line, if yes do not copy in
+ string buffer the terminating sequence ")\r\n". We are doing this
+ by checking if the amount(total) we got so far + the len of the line
+ +1 (taking to account the strip '\r') goes behond the request. */
+ if ((total + len + 1) > f_imap->string.nleft)
+ {
+ len0 = len = f_imap->string.nleft - total;
+ /* ALERT: if we ask for a substring, for example we have :
+ "123456\n", and ask for body[]<0.7> the server will send
+ body[] {7} --> "123456\r". There was not enough space
+ to fit the nl .. annoying. Take care of this here. */
+ if (f_imap->buffer[len - 1] == '\r')
+ len0--;
+ }
+
+ stream_write (f_imap->string.stream, f_imap->buffer,
+ len0, f_imap->string.offset, NULL);
+ f_imap->string.offset += len0;
+
+ /* Depending on the type of request we incremente the xxxx_lines
+ and xxxx_sizes. */
+ nl = (memchr (f_imap->buffer, '\n', len0)) ? 1 : 0;
+ if (f_imap->string.msg_imap)
+ {
+ switch (f_imap->string.type)
+ {
+ case IMAP_HEADER:
+ f_imap->string.msg_imap->header_lines += nl;
+ f_imap->string.msg_imap->header_size += len0;
+ break;
+
+ case IMAP_BODY:
+ f_imap->string.msg_imap->body_lines += nl;
+ f_imap->string.msg_imap->body_size += len0;
+ break;
+
+ case IMAP_MESSAGE:
+ f_imap->string.msg_imap->message_lines += nl;
+ /* The message size is known by sending RFC822.SIZE. */
+
+ default:
+ break;
+ }
+ }
+ }
+ f_imap->string.nleft -= total;
+ /* We may have trailing junk like the closing ")\r\n" from a literal string
+ glob it by moving the command buffer, or doing a full readline. */
+ if (len == (size_t)(f_imap->nl - f_imap->buffer))
+ {
+ len = 0;
+ status = imap_readline (f_imap);
+ }
+ *ptr = f_imap->buffer + len;
+ return status;
+}
+
+/* A quoted string is a sequence of zero or more 7-bit characters,
+ excluding CR and LF, with double quote (<">) characters at each end.
+ Same thing as the literal, diferent format the result is put in the
+ string buffer for the mailbox/callee. */
+static int
+imap_quoted_string (f_imap_t f_imap, char **ptr)
+{
+ char *bquote;
+ int escaped = 0;
+ int len;
+
+ (*ptr)++;
+ bquote = *ptr;
+ while (**ptr && (**ptr != '"' || escaped))
+ {
+ escaped = (**ptr == '\\') ? 1 : 0;
+ (*ptr)++;
+ }
+
+ len = *ptr - bquote;
+ stream_write (f_imap->string.stream, bquote, len,
+ f_imap->string.offset, NULL);
+ f_imap->string.offset += len;
+ if (**ptr == '"')
+ (*ptr)++;
+ if (DEBUG_SHOW_DATA)
+ fprintf (stderr, "%.*s", len, bquote);
+ return 0;
+}
+
+/* Find which type of string the response is: literal or quoted and let the
+ function fill the string buffer. */
+static int
+imap_string (f_imap_t f_imap, char **ptr)
+{
+ int status = 0;
+
+ /* Skip whites. */
+ while (**ptr == ' ')
+ (*ptr)++;
+ switch (**ptr)
+ {
+ case '{':
+ f_imap->string.nleft = strtol ((*ptr) + 1, ptr, 10);
+ if (**ptr == '}')
+ {
+ (*ptr)++;
+ /* Reset the buffer to the beginning. */
+ f_imap->ptr = f_imap->buffer;
+ status = imap_literal_string (f_imap, ptr);
+ }
+ break;
+ case '"':
+ status = imap_quoted_string (f_imap, ptr);
+ break;
+ /* NIL */
+ case 'N':
+ case 'n':
+ (*ptr)++; /* N|n */
+ (*ptr)++; /* I|i */
+ (*ptr)++; /* L|l */
+ break;
+ default:
+ /* Problem. */
+ status = 1;
+ break;
+ }
+ return status;
+}
+
+/* FIXME: does not work for nonblocking. */
+static int
+imap_list (f_imap_t f_imap)
+{
+ char *tok;
+ char *sp = NULL;
+ size_t len = f_imap->nl - f_imap->buffer - 1;
+ char *buffer;
+ struct list_response **plr;
+ struct list_response *lr;
+ int status = 0;
+
+ buffer = alloca (len);
+ memcpy (buffer, f_imap->buffer, len);
+ buffer[len] = '\0';
+ plr = realloc (f_imap->flist.element,
+ (f_imap->flist.num + 1) * sizeof (*plr));
+ if (plr == NULL)
+ return ENOMEM;
+ f_imap->flist.element = plr;
+ lr = plr[f_imap->flist.num] = calloc (1, sizeof (*lr));
+ if (lr == NULL)
+ return ENOMEM;
+ (f_imap->flist.num)++;
+
+ /* Glob untag. */
+ tok = strtok_r (buffer, " ", &sp);
+ /* Glob LIST. */
+ tok = strtok_r (NULL, " ", &sp);
+ /* Get the attibutes. */
+ tok = strtok_r (NULL, ")", &sp);
+ if (tok)
+ {
+ char *s = NULL;
+ char *p = tok;
+ while ((tok = strtok_r (p, " ()", &s)) != NULL)
+ {
+ if (strcasecmp (tok, "\\Noselect") == 0)
+ {
+ lr->type |= MU_FOLDER_ATTRIBUTE_DIRECTORY;
+ }
+ else if (strcasecmp (tok, "\\Marked") == 0)
+ {
+ }
+ else if (strcasecmp (tok, "\\Unmarked") == 0)
+ {
+ }
+ else if (strcasecmp (tok, "\\Noinferiors") == 0)
+ {
+ lr->type |= MU_FOLDER_ATTRIBUTE_FILE;
+ }
+ else
+ {
+ lr->type |= MU_FOLDER_ATTRIBUTE_DIRECTORY;
+ }
+ p = NULL;
+ }
+ }
+ /* Hiearchy delimeter. */
+ tok = strtok_r (NULL, " ", &sp);
+ if (tok && strlen (tok) > 2 && strcasecmp (tok, "NIL"))
+ lr->separator = tok[1];
+ /* The path. */
+ tok = strtok_r (NULL, " ", &sp);
+ if (tok)
+ {
+ char *s = strchr (tok, '{');
+ if (s)
+ {
+ size_t n = strtoul (s + 1, NULL, 10);
+ lr->name = calloc (n + 1, 1);
+ if (!lr->name)
+ status = ENOMEM;
+ else
+ {
+ f_imap->ptr = f_imap->buffer;
+ imap_readline (f_imap);
+ memcpy (lr->name, f_imap->buffer, n);
+ }
+ }
+ else if ((status = imap_string (f_imap, &tok)) == 0)
+ {
+ off_t sz = 0;
+
+ stream_size (f_imap->string.stream, &sz);
+ lr->name = calloc (sz + 1, 1);
+ if (!lr->name)
+ status = ENOMEM;
+ else
+ stream_read (f_imap->string.stream, lr->name, sz, 0, NULL);
+ stream_truncate (f_imap->string.stream, 0);
+ f_imap->string.offset = 0;
+ f_imap->string.nleft = 0;
+ }
+ else
+ {
+ lr->name = strdup (tok);
+ if (!lr->name)
+ status = ENOMEM;
+ }
+ }
+ return status;
+}
+/* Helping function to figure out the section name of the message: for example
+ a 2 part message with the first part being sub in two will be:
+ {1}, {1,1} {1,2} The first subpart of the message and its sub parts
+ {2} The second subpar of the message. */
+char *
+section_name (msg_imap_t msg_imap)
+{
+ size_t sectionlen = 0;
+ char *section = strdup ("");
+
+ /* Build the section name, but it is in reverse. */
+ for (; msg_imap; msg_imap = msg_imap->parent)
+ {
+ if (msg_imap->part != 0)
+ {
+ char *tmp;
+ char part[64];
+ size_t partlen;
+ snprintf (part, sizeof part, "%lu", (unsigned long) msg_imap->part);
+ partlen = strlen (part);
+ tmp = realloc (section, sectionlen + partlen + 2);
+ if (tmp == NULL)
+ break;
+ section = tmp;
+ memset (section + sectionlen, '\0', partlen + 2);
+ if (sectionlen != 0)
+ strcat (section, ".");
+ strcat (section, part);
+ sectionlen = strlen (section);
+ }
+ }
+
+ /* Reverse the string. */
+ if (section)
+ {
+ char *begin, *last;
+ char c;
+ for (begin = section, last = section + sectionlen - 1; begin < last;
+ begin++, last--)
+ {
+ c = *begin;
+ *begin = *last;
+ *last = c;
+ }
+ }
+ return section;
+}
+
+/* We do not pay particular attention to the content of the bodystructure
+ but rather to the paremetized list layout to discover how many messages
+ the originial message is composed of. The information is later retrieve
+ when needed via the body[header.fields] command. Note that this function
+ is recursive. */
+static int
+imap_bodystructure0 (msg_imap_t msg_imap, char **ptr)
+{
+ int paren = 0;
+ int no_arg = 0;
+ int status = 0;
+ int have_size = 0;
+
+ /* Skip space. */
+ while (**ptr == ' ')
+ (*ptr)++;
+ /* Pass lparen. */
+ if (**ptr == '(')
+ {
+ ++(*ptr);
+ paren++;
+ no_arg++;
+ }
+ /* NOTE : this loop has side effects in strtol() and imap_string(), the
+ order of the if's are important. */
+ while (**ptr)
+ {
+ /* Skip the string argument. */
+ if (**ptr != '(' && **ptr != ')')
+ {
+ char *start = *ptr;
+ /* FIXME: set the command callback if EAGAIN to resume. */
+ status = imap_string (msg_imap->m_imap->f_imap, ptr);
+ if (status != 0)
+ return status;
+ if (start != *ptr)
+ no_arg = 0;
+ }
+
+ if (isdigit ((unsigned)**ptr))
+ {
+ char *start = *ptr;
+ size_t size = strtoul (*ptr, ptr, 10);
+ if (start != *ptr)
+ {
+ if (!have_size && msg_imap && msg_imap->parent)
+ msg_imap->message_size = size;
+ have_size = 1;
+ no_arg = 0;
+ }
+ }
+
+ if (**ptr == '(')
+ {
+ if (no_arg)
+ {
+ msg_imap_t new_part;
+ msg_imap_t *tmp;
+ tmp = realloc (msg_imap->parts,
+ ((msg_imap->num_parts + 1) * sizeof (*tmp)));
+ if (tmp)
+ {
+ new_part = calloc (1, sizeof (*new_part));
+ if (new_part)
+ {
+ msg_imap->parts = tmp;
+ msg_imap->parts[msg_imap->num_parts] = new_part;
+ new_part->part = ++(msg_imap->num_parts);
+ new_part->parent = msg_imap;
+ new_part->num = msg_imap->num;
+ new_part->m_imap = msg_imap->m_imap;
+ new_part->flags = msg_imap->flags;
+ status = imap_bodystructure0 (new_part, ptr);
+ /* Jump up, the rparen been swallen already. */
+ continue;
+ }
+ else
+ {
+ status = ENOMEM;
+ free (tmp);
+ break;
+ }
+ }
+ else
+ {
+ status = ENOMEM;
+ break;
+ }
+ }
+ paren++;
+ }
+
+ if (**ptr == ')')
+ {
+ no_arg = 1;
+ paren--;
+ /* Did we reach the same number of close paren ? */
+ if (paren <= 0)
+ {
+ /* Swallow the rparen. */
+ (*ptr)++;
+ break;
+ }
+ }
+
+ if (**ptr == '\0')
+ break;
+
+ (*ptr)++;
+ }
+ return status;
+}
+
+static int
+imap_bodystructure (f_imap_t f_imap, char **ptr)
+{
+ return imap_bodystructure0 (f_imap->string.msg_imap, ptr);
+}
+
+/* The Format for a FLAG response is :
+ mailbox_data ::= "FLAGS" SPACE flag_list
+ flag_list ::= "(" #flag ")"
+ flag ::= "\Answered" / "\Flagged" / "\Deleted" /
+ "\Seen" / "\Draft" / flag_keyword / flag_extension
+ flag_extension ::= "\" atom
+ ;; Future expansion. Client implementations
+ ;; MUST accept flag_extension flags. Server
+ ;; implementations MUST NOT generate
+ ;; flag_extension flags except as defined by
+ ;; future standard or standards-track
+ ;; revisions of this specification.
+ flag_keyword ::= atom
+
+ S: * 14 FETCH (FLAGS (\Seen \Deleted))
+ S: * FLAGS (\Answered \Flagged \Deleted \Seen \Draft)
+
+ We assume that the '*' or the FLAGS keyword are strip.
+
+ FIXME: User flags are not take to account. */
+static int
+imap_fetch_flags (f_imap_t f_imap, char **ptr)
+{
+ msg_imap_t msg_imap = f_imap->string.msg_imap;
+ if (msg_imap)
+ imap_flags (ptr, &msg_imap->flags);
+ return 0;
+}
+
+static int
+imap_permanentflags (f_imap_t f_imap, char **ptr)
+{
+ imap_flags (ptr, &f_imap->flags);
+ return 0;
+}
+
+static int
+imap_flags (char **ptr, int *pflags)
+{
+ char *start;
+ char *end;
+ int flags = 0;
+
+ /* Skip space. */
+ while (**ptr == ' ')
+ (*ptr)++;
+
+ /* Skip LPAREN. */
+ if (**ptr == '(')
+ (*ptr)++;
+
+ /* Go through the list and break on ')' */
+ do
+ {
+ /* Skip space before next word. */
+ while (**ptr == ' ')
+ (*ptr)++;
+
+ /* Save the beginning of the word. */
+ start = *ptr;
+ /* Get the next word boundary. */
+ while (**ptr && **ptr != ' ' && **ptr != ')')
+ ++(*ptr);
+
+ /* Save the end for the strcasecmp. */
+ end = *ptr;
+
+ /* Bail out. */
+ if (*start == '\0')
+ break;
+
+ /* Guess the flag. */
+ if (end == start)
+ flags |= MU_ATTRIBUTE_SEEN;
+ else
+ {
+ if (strncasecmp (start, "\\Seen", end - start) == 0)
+ {
+ flags |= MU_ATTRIBUTE_READ;
+ }
+ else if (strncasecmp (start, "\\Answered", end - start) == 0)
+ {
+ flags |= MU_ATTRIBUTE_ANSWERED;
+ }
+ else if (strncasecmp (start, "\\Flagged", end - start) == 0)
+ {
+ flags |= MU_ATTRIBUTE_FLAGGED;
+ }
+ else if (strncasecmp (start, "\\Deleted", end - start) == 0)
+ {
+ flags |= MU_ATTRIBUTE_DELETED;
+ }
+ else if (strncasecmp (start, "\\Draft", end - start) == 0)
+ {
+ flags |= MU_ATTRIBUTE_DRAFT;
+ }
+ else if (strncasecmp (start, "\\Recent", end - start))
+ flags |= MU_ATTRIBUTE_SEEN;
+ }
+ }
+ while (**ptr && **ptr != ')'); /* do {} */
+
+ /* Skip the last rparen. */
+ if (**ptr == ')')
+ (*ptr)++;
+
+ if (pflags)
+ *pflags = flags;
+ return 0;
+}
+
+static int
+imap_body (f_imap_t f_imap, char **ptr)
+{
+ int status;
+
+ /* Skip leading spaces. */
+ while (**ptr && **ptr == ' ')
+ (*ptr)++;
+
+ if (**ptr == '[')
+ {
+ char *sep = strchr (*ptr, ']');
+ (*ptr)++; /* Move pass the '[' */
+ if (sep)
+ {
+ size_t len = sep - *ptr;
+ char *section = alloca (len + 1);
+ char *p = section;
+ strncpy (section, *ptr, len);
+ section[len] = '\0';
+ /* strupper. */
+ for (; *p; p++) if (isupper((unsigned)*p)) *p = toupper ((unsigned)*p);
+ /* Set the string type to update the correct line count. */
+ /*if (!strstr (section, "FIELD"))*/
+ {
+ if (strstr (section, "MIME") || (strstr (section, "HEADER")))
+ {
+ f_imap->string.type = IMAP_HEADER;
+ }
+ else if (strstr (section, "TEXT") || len > 0)
+ {
+ f_imap->string.type = IMAP_BODY;
+ }
+ else if (len == 0) /* body[] */
+ {
+ f_imap->string.type = IMAP_MESSAGE;
+ }
+ }
+ sep++; /* Move pass the ']' */
+ *ptr = sep;
+ }
+ }
+ while (**ptr && **ptr == ' ')
+ (*ptr)++;
+ if (**ptr == '<')
+ {
+ char *sep = strchr (*ptr, '>');
+ if (sep)
+ {
+ sep++;
+ *ptr = sep;
+ }
+ }
+ status = imap_string (f_imap, ptr);
+
+ /* If the state scan. Catch it here. */
+ if (f_imap->state == IMAP_SCAN_ACK)
+ {
+ char *buffer;
+ off_t total = 0;
+ if (f_imap->string.msg_imap && f_imap->string.msg_imap->fheader)
+ header_destroy (&f_imap->string.msg_imap->fheader, NULL);
+ stream_size (f_imap->string.stream, &total);
+ buffer = malloc (total + 1);
+ stream_read (f_imap->string.stream, buffer, total, 0, NULL);
+ status = header_create (&f_imap->string.msg_imap->fheader,
+ buffer, total, NULL);
+ free (buffer);
+ stream_truncate (f_imap->string.stream, 0);
+ f_imap->string.offset = 0;
+ f_imap->string.nleft = 0;
+ }
+ return status;
+}
+
+static int
+imap_internaldate (f_imap_t f_imap, char **ptr)
+{
+ return imap_string (f_imap, ptr);
+}
+
+static int
+imap_uid (f_imap_t f_imap, char **ptr)
+{
+ char token[128];
+ imap_token (token, sizeof token, ptr);
+ if (f_imap->string.msg_imap)
+ f_imap->string.msg_imap->uid = strtoul (token, NULL, 10);
+ return 0;
+}
+
+static int
+imap_rfc822 (f_imap_t f_imap, char **ptr)
+{
+ int status;
+ f_imap->string.type = IMAP_MESSAGE;
+ status = imap_body (f_imap, ptr);
+ f_imap->string.type = 0;
+ return status;
+}
+
+static int
+imap_rfc822_size (f_imap_t f_imap, char **ptr)
+{
+ char token[128];
+ imap_token (token, sizeof token, ptr);
+ if (f_imap->string.msg_imap)
+ f_imap->string.msg_imap->message_size = strtoul (token, NULL, 10);
+ return 0;
+}
+
+static int
+imap_rfc822_header (f_imap_t f_imap, char **ptr)
+{
+ int status;
+ f_imap->string.type = IMAP_HEADER;
+ status = imap_string (f_imap, ptr);
+ f_imap->string.type = 0;
+ return status;
+}
+
+static int
+imap_rfc822_text (f_imap_t f_imap, char **ptr)
+{
+ int status;
+ f_imap->string.type = IMAP_HEADER;
+ status = imap_string (f_imap, ptr);
+ f_imap->string.type = 0;
+ return status;
+}
+
+/* Parse imap unfortunately FETCH is use as response for many commands.
+ We just use a small set an ignore the other ones :
+ not use : ALL
+ use : BODY
+ not use : BODY[<section>]<<partial>>
+ use : BODY.PEEK[<section>]<<partial>>
+ not use : BODYSTRUCTURE
+ not use : ENVELOPE
+ not use : FAST
+ use : FLAGS
+ not use : FULL
+ use : INTERNALDATE
+ not use : RFC822
+ not use : RFC822.HEADER
+ use : RFC822.SIZE
+ not use : RFC822.TEXT
+ not use : UID
+ */
+static int
+imap_fetch (f_imap_t f_imap)
+{
+ char token[128];
+ size_t msgno = 0;
+ m_imap_t m_imap = f_imap->selected;
+ int status = 0;
+ char *sp = NULL;
+
+ /* We should have a mailbox selected. */
+ assert (m_imap != NULL);
+
+ /* Parse the FETCH respones to extract the pieces. */
+ sp = f_imap->buffer;
+
+ /* Skip untag '*'. */
+ imap_token (token, sizeof token, &sp);
+ /* Get msgno. */
+ imap_token (token, sizeof token, &sp);
+ msgno = strtol (token, NULL, 10);
+ /* Skip FETCH . */
+ imap_token (token, sizeof token, &sp);
+
+ /* It is actually possible, but higly unlikely that we do not have the
+ message yet, for example a "FETCH (FLAGS (\Recent))" notification
+ for a newly messsage. */
+ if (f_imap->string.msg_imap == NULL
+ || f_imap->string.msg_imap->num != msgno)
+ {
+ /* Find the imap mesg struct. */
+ size_t i;
+ message_t msg = NULL;
+ mailbox_get_message (m_imap->mailbox, msgno, &msg);
+ for (i = 0; i < m_imap->imessages_count; i++)
+ {
+ if (m_imap->imessages[i] && m_imap->imessages[i]->num == msgno)
+ {
+ f_imap->string.msg_imap = m_imap->imessages[i];
+ break;
+ }
+ }
+ /* message_destroy (&msg); */
+ }
+
+ while (*sp && *sp != ')')
+ {
+ /* Get the token. */
+ imap_token (token, sizeof token, &sp);
+
+ if (strncmp (token, "FLAGS", 5) == 0)
+ {
+ status = imap_fetch_flags (f_imap, &sp);
+ }
+ else if (strcasecmp (token, "BODY") == 0)
+ {
+ if (*sp == '[')
+ status = imap_body (f_imap, &sp);
+ else
+ status = imap_bodystructure (f_imap, &sp);
+ }
+ else if (strcasecmp (token, "BODYSTRUCTURE") == 0)
+ {
+ status = imap_bodystructure (f_imap, &sp);
+ }
+ else if (strncmp (token, "INTERNALDATE", 12) == 0)
+ {
+ status = imap_internaldate (f_imap, &sp);
+ }
+ else if (strncmp (token, "RFC822", 10) == 0)
+ {
+ if (*sp == '.')
+ {
+ sp++;
+ imap_token (token, sizeof token, &sp);
+ if (strcasecmp (token, "SIZE") == 0)
+ {
+ status = imap_rfc822_size (f_imap, &sp);
+ }
+ else if (strcasecmp (token, "TEXT") == 0)
+ {
+ status = imap_rfc822_text (f_imap, &sp);
+ }
+ else if (strcasecmp (token, "HEADER") == 0)
+ {
+ status = imap_rfc822_header (f_imap, &sp);
+ }
+ /* else mu_error ("not supported RFC822 option\n"); */
+ }
+ else
+ {
+ status = imap_rfc822 (f_imap, &sp);
+ }
+ }
+ else if (strncmp (token, "UID", 3) == 0)
+ {
+ status = imap_uid (f_imap, &sp);
+ }
+ /* else mu_error ("not supported FETCH command\n"); */
+ }
+ return status;
+}
+
+static int
+imap_search (f_imap_t f_imap)
+{
+ (void)f_imap;
+ /* Not implemented. No provision for this in the API, yet. */
+ return 0;
+}
+
+static int
+imap_status (f_imap_t f_imap)
+{
+ (void)f_imap;
+ /* Not implemented. No provision for this in the API, yet. */
+ return 0;
+}
+
+static int
+imap_expunge (f_imap_t f_imap, unsigned msgno)
+{
+ (void)f_imap; (void)msgno;
+ /* We should not have this, since do not send the expunge, but rather
+ use SELECT/CLOSE. */
+ return 0;
+}
+
+
+/* This function will advance ptr to the next character that IMAP
+ recognise as special: " .()[]<>" and put the result in buf which
+ is of size len. */
+static int
+imap_token (char *buf, size_t len, char **ptr)
+{
+ char *start = *ptr;
+ size_t i;
+ /* Skip leading space. */
+ while (**ptr && **ptr == ' ')
+ (*ptr)++;
+ /* Break the string by token, when we recognise Atoms we stop. */
+ for (i = 1; **ptr && i < len; (*ptr)++, buf++, i++)
+ {
+ if (**ptr == ' ' || **ptr == '.'
+ || **ptr == '(' || **ptr == ')'
+ || **ptr == '[' || **ptr == ']'
+ || **ptr == '<' || **ptr == '>')
+ {
+ /* Advance. */
+ if (start == (*ptr))
+ (*ptr)++;
+ break;
+ }
+ *buf = **ptr;
+ }
+ *buf = '\0';
+ /* Skip trailing space. */
+ while (**ptr && **ptr == ' ')
+ (*ptr)++;
+ return *ptr - start;;
+}
+
+/* Checks to see if a mailbox name matches a pattern, treating
+ INBOX case insensitively, as required (INBOX is a special
+ name no matter what the case is).
+ */
+static int
+imap_mailbox_name_match(const char* pattern, const char* mailbox)
+{
+ if(strcasecmp(pattern, "inbox") == 0)
+ {
+ return strcasecmp(pattern, mailbox);
+ }
+ return fnmatch(pattern, mailbox, 0);
+}
+
+/* C99 says that a conforming implementations of snprintf () should return the
+ number of char that would have been call but many GNU/Linux && BSD
+ implementations return -1 on error. Worse QnX/Neutrino actually does not
+ put the terminal null char. So let's try to cope. */
+int
+imap_writeline (f_imap_t f_imap, const char *format, ...)
+{
+ int len;
+ va_list ap;
+ int done = 1;
+
+ va_start(ap, format);
+ do
+ {
+ len = vsnprintf (f_imap->buffer, f_imap->buflen - 1, format, ap);
+ if (len < 0 || len >= (int)f_imap->buflen
+ || !memchr (f_imap->buffer, '\0', len + 1))
+ {
+ f_imap->buflen *= 2;
+ f_imap->buffer = realloc (f_imap->buffer, f_imap->buflen);
+ if (f_imap->buffer == NULL)
+ return ENOMEM;
+ done = 0;
+ }
+ else
+ done = 1;
+ }
+ while (!done);
+ va_end(ap);
+ f_imap->ptr = f_imap->buffer + len;
+
+ if (DEBUG_SHOW_COMMAND)
+ fprintf (stderr, "%s", f_imap->buffer);
+ return 0;
+}
+
+/* Cover to send requests. */
+int
+imap_send (f_imap_t f_imap)
+{
+ int status = 0;
+ if (f_imap->ptr > f_imap->buffer)
+ {
+ size_t len;
+ size_t n = 0;
+ len = f_imap->ptr - f_imap->buffer;
+ status = stream_write (f_imap->folder->stream, f_imap->buffer, len,
+ 0, &n);
+ if (status == 0)
+ {
+ memmove (f_imap->buffer, f_imap->buffer + n, len - n);
+ f_imap->ptr -= n;
+ }
+ }
+ else
+ f_imap->ptr = f_imap->buffer;
+ return status;
+}
+
+/* Read a complete line form the imap server. Transform CRLF to LF, put a null
+ in the buffer when done. Note f_imap->offset is not really of any use
+ but rather to keep the stream internal buffer scheme happy, so we have to
+ be in sync with the stream. */
+int
+imap_readline (f_imap_t f_imap)
+{
+ size_t n = 0;
+ size_t total = f_imap->ptr - f_imap->buffer;
+ int status;
+
+ /* Must get a full line before bailing out. */
+ do
+ {
+ status = stream_readline (f_imap->folder->stream, f_imap->buffer + total,
+ f_imap->buflen - total, f_imap->offset, &n);
+ if (status != 0)
+ return status;
+
+ /* The server went away: It maybe a timeout and some imap server
+ does not send the BYE. Consider like an error. */
+ if (n == 0)
+ return EIO;
+
+ total += n;
+ f_imap->offset += n;
+ f_imap->nl = memchr (f_imap->buffer, '\n', total);
+ if (f_imap->nl == NULL) /* Do we have a full line. */
+ {
+ /* Allocate a bigger buffer ? */
+ if (total >= f_imap->buflen -1)
+ {
+ f_imap->buflen *= 2;
+ f_imap->buffer = realloc (f_imap->buffer, f_imap->buflen + 1);
+ if (f_imap->buffer == NULL)
+ return ENOMEM;
+ }
+ }
+ f_imap->ptr = f_imap->buffer + total;
+ }
+ while (f_imap->nl == NULL);
+
+ /* Conversion \r\n --> \n\0 */
+ if (f_imap->nl > f_imap->buffer)
+ {
+ *(f_imap->nl - 1) = '\n';
+ *(f_imap->nl) = '\0';
+ f_imap->ptr = f_imap->nl;
+ }
+ return 0;
+}
+
+/*
+ The parsing was inspired by this article form the BeNews channel: "BE
+ ENGINEERING INSIGHTS: IMAP for the Masses." By Adam Haberlach adam@be.com
+
+ The server responses are in three forms: status responses, server data,
+ and command continuation request, ...
+ An untagged response is indicated by the token "*" instead of a tag.
+ Untagged status responses indicate server greeting, or server status
+ that does not indicate the completion of a command (for example, an
+ impending system shutdown alert).
+ ....
+ The client MUST be prepared to accept any response at all times.
+
+ Status responses are OK, NO, BAD, PREAUTH and BYE. OK, NO, and BAD
+ may be tagged or untagged. PREAUTH and BYE are always untagged.
+
+ Server Responses - Status Responses
+ BAD *|tag
+ BYE *
+ NO *|tag
+ OK *|tag
+ PREAUTH *
+
+ The code for status responses are
+ ALERT
+ BADCHARSET(IMAPV)
+ CAPABILITY(IMAPV)
+ NEWNAME
+ PARSE
+ PERMANENTFLAGS
+ READ-ONLY
+ READ-WRITE
+ TRYCREATE
+ UIDNEXT
+ UIDVALIDITY
+ UNSEEN
+
+ Server Responses - Server and Mailbox Status.
+ These responses are always untagged.
+ CAPABILITY *
+ EXISTS *
+ EXPUNGE *
+ FLAGS *
+ FETCH *
+ LIST *
+ LSUB *
+ RECENT *
+ SEARCH *
+ STATUS *
+
+ Server Responses - Command Continuation Request
+ +
+
+*/
+int
+imap_parse (f_imap_t f_imap)
+{
+ int done = 0;
+ int status = 0;
+ char empty[2];
+ char *buffer = NULL;
+ folder_t folder = f_imap->folder;
+
+ /* We use that moronic hack to not check null for the tockenize strings. */
+ empty[0] = '\0';
+ empty[1] = '\0';
+ while (! done)
+ {
+ char *tag, *response, *remainder;
+
+ status = imap_readline (f_imap);
+ if (status != 0)
+ {
+ break;
+ }
+ /* Comment out to see all reading traffic. */
+ if (DEBUG_SHOW_RESPONSE)
+ mu_error ("\t\t%s", f_imap->buffer);
+
+ /* We do not want to step over f_imap->buffer since it can be use
+ further down the chain. */
+ if (buffer)
+ {
+ free (buffer);
+ buffer = NULL;
+ }
+ buffer = calloc ((f_imap->ptr - f_imap->buffer) + 1, 1);
+ memcpy (buffer, f_imap->buffer, (f_imap->ptr - f_imap->buffer));
+
+ /* Tokenize the string. */
+ {
+ char *sp = NULL;
+ tag = strtok_r (buffer, " ", &sp);
+ response = strtok_r (NULL, " ", &sp);
+ if (!response)
+ response = empty;
+ remainder = strtok_r (NULL, "\n", &sp);
+ if (!remainder)
+ remainder = empty;
+ }
+
+ /* Is the response untagged ? */
+ if (tag && tag[0] == '*')
+ {
+ FOLDER_DEBUG2(folder, MU_DEBUG_PROT, "* %s %s\n",
+ response, remainder);
+ /* Is it a Status Response. */
+ if (strcasecmp (response, "OK") == 0)
+ {
+ /* Check for status response [code]. */
+ if (*remainder == '[')
+ {
+ char *cruft, *subtag;
+ char *sp = NULL;
+ remainder++;
+ cruft = strtok_r (remainder, "]", &sp);
+ if (!cruft) cruft = empty;
+ subtag = strtok_r (cruft, " ", &sp);
+ if (!subtag) subtag = empty;
+
+ if (strcasecmp (subtag, "ALERT") == 0)
+ {
+ /* The human-readable text contains a special alert that
+ MUST be presented to the user in a fashion that calls
+ the user's attention to the message. */
+ mu_error ("ALERT: %s\n", (sp) ? sp : "");
+ }
+ else if (strcasecmp (subtag, "BADCHARSET") == 0)
+ {
+ /* Optionally followed by a parenthesized list of
+ charsets. A SEARCH failed because the given charset
+ is not supported by this implementation. If the
+ optional list of charsets is given, this lists the
+ charsets that are supported by this implementation. */
+ mu_error ("BADCHARSET: %s\n", (sp) ? sp : "");
+ }
+ else if (strcasecmp (subtag, "CAPABILITY") == 0)
+ {
+ /* Followed by a list of capabilities. This can appear
+ in the initial OK or PREAUTH response to transmit an
+ initial capabilities list. This makes it unnecessary
+ for a client to send a separate CAPABILITY command if
+ it recognizes this response. */
+ if (f_imap->capa)
+ free (f_imap->capa);
+ f_imap->capa = strdup (cruft);
+ }
+ else if (strcasecmp (subtag, "NEWNAME") == 0)
+ {
+ /* Followed by a mailbox name and a new mailbox name. A
+ SELECT or EXAMINE failed because the target mailbox
+ name (which once existed) was renamed to the new
+ mailbox name. This is a hint to the client that the
+ operation can succeed if the SELECT or EXAMINE is
+ reissued with the new mailbox name. */
+ mu_error ("NEWNAME: %s\n", (sp) ? sp : "");
+ }
+ else if (strcasecmp (subtag, "PARSE") == 0)
+ {
+ /* The human-readable text represents an error in
+ parsing the [RFC-822] header or [MIME-IMB] headers
+ of a message in the mailbox. */
+ mu_error ("PARSE: %s\n", (sp) ? sp : "");
+ }
+ else if (strcasecmp (subtag, "PERMANENTFLAGS") == 0)
+ {
+ /* Followed by a parenthesized list of flags, indicates
+ which of the known flags that the client can change
+ permanently. Any flags that are in the FLAGS
+ untagged response, but not the PERMANENTFLAGS list,
+ can not be set permanently. If the client attempts
+ to STORE a flag that is not in the PERMANENTFLAGS
+ list, the server will either ignore the change or
+ store the state change for the remainder of the
+ current session only. The PERMANENTFLAGS list can
+ also include the special flag \*, which indicates
+ that it is possible to create new keywords by
+ attempting to store those flags in the mailbox. */
+ }
+ else if (strcasecmp (subtag, "READ-ONLY") == 0)
+ {
+ /* The mailbox is selected read-only, or its access
+ while selected has changed from read-write to
+ read-only. */
+ }
+ else if (strcasecmp (subtag, "READ-WRITE") == 0)
+ {
+ /* The mailbox is selected read-write, or its access
+ while selected has changed from read-only to
+ read-write. */
+ }
+ else if (strcasecmp (subtag, "TRYCREATE") == 0)
+ {
+ /* An APPEND or COPY attempt is failing because the
+ target mailbox does not exist (as opposed to some
+ other reason). This is a hint to the client that
+ the operation can succeed if the mailbox is first
+ created by the CREATE command. */
+ mu_error ("TRYCREATE: %s\n", (sp) ? sp : "");
+ }
+ else if (strcasecmp (subtag, "UIDNEXT") == 0)
+ {
+ /* Followed by a decimal number, indicates the next
+ unique identifier value. Refer to section 2.3.1.1
+ for more information. */
+ char *value = strtok_r (NULL, " ", &sp);
+ f_imap->selected->uidnext = strtol (value, NULL, 10);
+ }
+ else if (strcasecmp (subtag, "UIDVALIDITY") == 0)
+ {
+ /* Followed by a decimal number, indicates the unique
+ identifier validity value. Refer to section 2.3.1.1
+ for more information. */
+ char *value = strtok_r (NULL, " ", &sp);
+ f_imap->selected->uidvalidity = strtol (value, NULL, 10);
+ }
+ else if (strcasecmp (subtag, "UNSEEN") == 0)
+ {
+ /* Followed by a decimal number, indicates the number of
+ the first message without the \Seen flag set. */
+ char *value = strtok_r (NULL, " ", &sp);
+ f_imap->selected->unseen = strtol (value, NULL, 10);
+ }
+ else
+ {
+ /* Additional response codes defined by particular
+ client or server implementations SHOULD be prefixed
+ with an "X" until they are added to a revision of
+ this protocol. Client implementations SHOULD ignore
+ response codes that they do not recognize. */
+ }
+ } /* End of code. */
+ else
+ {
+ /* Not sure why we would get an untagged ok...but we do... */
+ /* Still should we be verbose about is ? */
+ mu_error ("Untagged OK: %s\n", remainder);
+ }
+ }
+ else if (strcasecmp (response, "NO") == 0)
+ {
+ /* This does not mean failure but rather a strong warning. */
+ mu_error ("Untagged NO: %s\n", remainder);
+ }
+ else if (strcasecmp (response, "BAD") == 0)
+ {
+ /* We're dead, protocol/syntax error. */
+ mu_error ("Untagged BAD: %s\n", remainder);
+ }
+ else if (strcasecmp (response, "PREAUTH") == 0)
+ {
+ /* Should we be dealing with this? */
+ }
+ else if (strcasecmp (response, "BYE") == 0)
+ {
+ /* We should close the stream. This is not recoverable. */
+ done = 1;
+ monitor_wrlock (f_imap->folder->monitor);
+ f_imap->isopen = 0;
+ f_imap->selected = NULL;
+ monitor_unlock (f_imap->folder->monitor);
+ stream_close (f_imap->folder->stream);
+ }
+ else if (strcasecmp (response, "CAPABILITY") == 0)
+ {
+ if (f_imap->capa)
+ free (f_imap->capa);
+ f_imap->capa = strdup (remainder);
+ }
+ else if (strcasecmp (remainder, "EXISTS") == 0)
+ {
+ f_imap->selected->messages_count = strtol (response, NULL, 10);
+ }
+ else if (strcasecmp (remainder, "EXPUNGE") == 0)
+ {
+ unsigned int msgno = strtol (response, NULL, 10);
+ status = imap_expunge (f_imap, msgno);
+ }
+ else if (strncasecmp (remainder, "FETCH", 5) == 0)
+ {
+ status = imap_fetch (f_imap);
+ if (status != 0)
+ break;
+ }
+ else if (strcasecmp (response, "FLAGS") == 0)
+ {
+ /* Flags define on the mailbox not a message flags. */
+ status = imap_permanentflags (f_imap, &remainder);
+ }
+ else if (strcasecmp (response, "LIST") == 0)
+ {
+ status = imap_list (f_imap);
+ }
+ else if (strcasecmp (response, "LSUB") == 0)
+ {
+ status = imap_list (f_imap);
+ }
+ else if (strcasecmp (remainder, "RECENT") == 0)
+ {
+ f_imap->selected->recent = strtol (response, NULL, 10);
+ }
+ else if (strcasecmp (response, "SEARCH") == 0)
+ {
+ status = imap_search (f_imap);
+ }
+ else if (strcasecmp (response, "STATUS") == 0)
+ {
+ status = imap_status (f_imap);
+ }
+ else
+ {
+ /* Once again, check for something strange. */
+ mu_error ("unknown untagged response: \"%s\" %s\n",
+ response, remainder);
+ }
+ }
+ /* Continuation token ???. */
+ else if (tag && tag[0] == '+')
+ {
+ done = 1;
+ }
+ else
+ {
+ /* Every transaction ends with a tagged response. */
+ done = 1;
+ if (strcasecmp (response, "OK") == 0)
+ {
+ /* Cool we are doing ok. */
+ }
+ else /* NO and BAD */
+ {
+ if (strncasecmp (remainder, "LOGIN", 5) == 0)
+ {
+ observable_t observable = NULL;
+ folder_get_observable (f_imap->folder, &observable);
+ observable_notify (observable, MU_EVT_AUTHORITY_FAILED);
+ }
+ mu_error ("NO/Bad Tagged: %s %s\n", response, remainder);
+ status = EINVAL;
+ }
+ }
+ f_imap->ptr = f_imap->buffer;
+ }
+
+ if (buffer)
+ free (buffer);
+ return status;
+}
+
+#else
+#include <stdio.h>
+#include <registrar0.h>
+record_t imap_record = NULL;
+#endif
diff --git a/mailbox/imap/mbox.c b/mailbox/imap/mbox.c
new file mode 100644
index 000000000..34132c01f
--- /dev/null
+++ b/mailbox/imap/mbox.c
@@ -0,0 +1,2256 @@
+/* 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 */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifdef ENABLE_IMAP
+
+#include <errno.h>
+#include <string.h>
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+#include <stdlib.h>
+#include <assert.h>
+#include <time.h>
+
+#include <mailutils/address.h>
+#include <mailutils/attribute.h>
+#include <mailutils/body.h>
+#include <mailutils/debug.h>
+#include <mailutils/envelope.h>
+#include <mailutils/error.h>
+#include <mailutils/header.h>
+#include <mailutils/message.h>
+#include <mailutils/mutil.h>
+#include <mailutils/observer.h>
+#include <mailutils/property.h>
+#include <mailutils/stream.h>
+
+#include <imap0.h>
+#include <mailbox0.h>
+#include <registrar0.h>
+#include <url0.h>
+
+#undef min
+#define min(a,b) ((a) < (b) ? (a) : (b))
+
+#define MU_IMAP_CACHE_HEADERS "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_t API. */
+static void mailbox_imap_destroy __P ((mailbox_t));
+static int mailbox_imap_open __P ((mailbox_t, int));
+static int mailbox_imap_close __P ((mailbox_t));
+static int imap_uidvalidity __P ((mailbox_t, unsigned long *));
+static int imap_uidnext __P ((mailbox_t, size_t *));
+static int imap_expunge __P ((mailbox_t));
+static int imap_get_message __P ((mailbox_t, size_t, message_t *));
+static int imap_messages_count __P ((mailbox_t, size_t *));
+static int imap_messages_recent __P ((mailbox_t, size_t *));
+static int imap_message_unseen __P ((mailbox_t, size_t *));
+static int imap_scan __P ((mailbox_t, size_t, size_t *));
+static int imap_scan0 __P ((mailbox_t, size_t, size_t *, int));
+static int imap_is_updated __P ((mailbox_t));
+static int imap_append_message __P ((mailbox_t, message_t));
+static int imap_append_message0 __P ((mailbox_t, message_t));
+static int imap_copy_message __P ((mailbox_t, message_t));
+
+/* message_t API. */
+static int imap_submessage_size __P ((msg_imap_t, size_t *));
+static int imap_message_size __P ((message_t, size_t *));
+static int imap_message_lines __P ((message_t, size_t *));
+static int imap_message_fd __P ((stream_t, int *));
+static int imap_message_read __P ((stream_t , char *, size_t, off_t, size_t *));
+static int imap_message_uid __P ((message_t, size_t *));
+
+/* mime_t API. */
+static int imap_is_multipart __P ((message_t, int *));
+static int imap_get_num_parts __P ((message_t, size_t *));
+static int imap_get_part __P ((message_t, size_t, message_t *));
+
+/* envelope_t API */
+static int imap_envelope_sender __P ((envelope_t, char *, size_t, size_t *));
+static int imap_envelope_date __P ((envelope_t, char *, size_t, size_t *));
+
+/* attribute_t API */
+static int imap_attr_get_flags __P ((attribute_t, int *));
+static int imap_attr_set_flags __P ((attribute_t, int));
+static int imap_attr_unset_flags __P ((attribute_t, int));
+
+/* header_t API. */
+static int imap_header_read __P ((header_t, char*, size_t, off_t, size_t *));
+static int imap_header_get_value __P ((header_t, const char*, char *, size_t, size_t *));
+static int imap_header_get_fvalue __P ((header_t, const char*, char *, size_t, size_t *));
+
+/* body_t API. */
+static int imap_body_read __P ((stream_t, char *, size_t, off_t,
+ size_t *));
+static int imap_body_size __P ((body_t, size_t *));
+static int imap_body_lines __P ((body_t, size_t *));
+static int imap_body_fd __P ((stream_t, int *));
+
+/* Helpers. */
+static int imap_get_fd __P ((msg_imap_t, int *));
+static int imap_get_message0 __P ((msg_imap_t, message_t *));
+static int fetch_operation __P ((f_imap_t, msg_imap_t, char *, size_t, size_t *));
+static void free_subparts __P ((msg_imap_t));
+static int flags_to_string __P ((char **, int));
+static int delete_to_string __P ((m_imap_t, char **));
+static int is_same_folder __P ((mailbox_t, message_t));
+
+/* Initialize the concrete object mailbox_t by overloading the function of the
+ structure. */
+int
+_mailbox_imap_init (mailbox_t mailbox)
+{
+ m_imap_t m_imap;
+ size_t name_len = 0;
+ folder_t folder = NULL;
+
+ assert(mailbox);
+
+ folder = mailbox->folder;
+
+ m_imap = mailbox->data = calloc (1, sizeof (*m_imap));
+ if (m_imap == NULL)
+ return ENOMEM;
+
+ /* Retrieve the name of the mailbox from the URL. */
+ url_get_path (mailbox->url, NULL, 0, &name_len);
+ if (name_len == 0)
+ {
+ /* name "INBOX" is the default. */
+ m_imap->name = calloc (6, sizeof (char));
+ strcpy (m_imap->name, "INBOX");
+ }
+ else
+ {
+ m_imap->name = calloc (name_len + 1, sizeof (char));
+ url_get_path (mailbox->url, m_imap->name, name_len + 1, NULL);
+ }
+
+ /* Overload the functions. */
+ mailbox->_destroy = mailbox_imap_destroy;
+
+ mailbox->_open = mailbox_imap_open;
+ mailbox->_close = mailbox_imap_close;
+
+ /* Messages. */
+ mailbox->_get_message = imap_get_message;
+ mailbox->_append_message = imap_append_message;
+ mailbox->_messages_count = imap_messages_count;
+ mailbox->_messages_recent = imap_messages_recent;
+ mailbox->_message_unseen = imap_message_unseen;
+ mailbox->_expunge = imap_expunge;
+ mailbox->_uidvalidity = imap_uidvalidity;
+ mailbox->_uidnext = imap_uidnext;
+
+ mailbox->_scan = imap_scan;
+ mailbox->_is_updated = imap_is_updated;
+
+ /* Get the back pointer of the concrete folder. */
+ if (mailbox->folder)
+ m_imap->f_imap = mailbox->folder->data;
+ /* maibox back pointer. */
+ m_imap->mailbox = mailbox;
+
+ /* Set our properties. */
+ {
+ property_t property = NULL;
+ mailbox_get_property (mailbox, &property);
+ property_set_value (property, "TYPE", "IMAP4", 1);
+ }
+
+ return 0;
+}
+
+/* Recursive call to free all the subparts of a message. */
+static void
+free_subparts (msg_imap_t msg_imap)
+{
+ size_t i;
+ for (i = 0; i < msg_imap->num_parts; i++)
+ {
+ if (msg_imap->parts[i])
+ free_subparts (msg_imap->parts[i]);
+ }
+
+ if (msg_imap->message)
+ message_destroy (&(msg_imap->message), msg_imap);
+ if (msg_imap->parts)
+ free (msg_imap->parts);
+ if (msg_imap->fheader)
+ header_destroy (&msg_imap->fheader, NULL);
+ if (msg_imap->internal_date)
+ free (msg_imap->internal_date);
+ free(msg_imap);
+}
+
+/* Give back all the resources. But it does not mean to shutdown the channel
+ this is done on the folder. */
+static void
+mailbox_imap_destroy (mailbox_t mailbox)
+{
+ if (mailbox->data)
+ {
+ m_imap_t m_imap = mailbox->data;
+ f_imap_t f_imap = m_imap->f_imap;
+ size_t i;
+
+ /* Deselect. */
+ if (m_imap != f_imap->selected)
+ f_imap->selected = NULL;
+
+ monitor_wrlock (mailbox->monitor);
+ /* Destroy the imap messages and ressources associated to them. */
+ for (i = 0; i < m_imap->imessages_count; i++)
+ {
+ if (m_imap->imessages[i])
+ free_subparts (m_imap->imessages[i]);
+ }
+ if (m_imap->imessages)
+ free (m_imap->imessages);
+ if (m_imap->name)
+ free (m_imap->name);
+ free (m_imap);
+ mailbox->data = NULL;
+ monitor_unlock (mailbox->monitor);
+ }
+}
+
+/* If the connection was not up it is open by the folder since the stream
+ socket is actually created by the folder. It is not necessary
+ to set select the mailbox right away, there are maybe on going operations.
+ But on any operation by a particular mailbox, it will be selected first. */
+static int
+mailbox_imap_open (mailbox_t mailbox, int flags)
+{
+ int status = 0;
+ m_imap_t m_imap = mailbox->data;
+ f_imap_t f_imap = m_imap->f_imap;
+ folder_t folder = f_imap->folder;
+ struct folder_list folders = { 0, 0 };
+
+ /* m_imap must have been created during mailbox initialization. */
+ assert (mailbox->data);
+ assert (m_imap->name);
+
+ mailbox->flags = flags;
+
+ if ((status = folder_open (mailbox->folder, flags)))
+ return status;
+
+ /* We might not have to SELECT the mailbox, but we need to know it
+ exists, and CREATE it if it doesn't, and CREATE is specified in
+ the flags.
+ */
+
+ switch (m_imap->state)
+ {
+ case IMAP_NO_STATE:
+ m_imap->state = IMAP_LIST;
+
+ case IMAP_LIST:
+ status = folder_list (folder, NULL, m_imap->name, &folders);
+ if (status != 0)
+ {
+ if (status != EAGAIN && status != EINPROGRESS && status != EINTR)
+ m_imap->state = IMAP_NO_STATE;
+
+ return status;
+ }
+ m_imap->state = IMAP_NO_STATE;
+ if (folders.num)
+ return 0;
+
+ if ((flags & MU_STREAM_CREAT) == 0)
+ return ENOENT;
+
+ m_imap->state = IMAP_CREATE;
+
+ case IMAP_CREATE:
+ switch (f_imap->state)
+ {
+ case IMAP_NO_STATE:
+ {
+ char *path;
+ size_t len;
+ url_get_path (folder->url, NULL, 0, &len);
+ if (len == 0)
+ return 0;
+ path = calloc (len + 1, sizeof (*path));
+ if (path == NULL)
+ return ENOMEM;
+ url_get_path (folder->url, path, len + 1, NULL);
+ status = imap_writeline (f_imap, "g%u CREATE %s\r\n",
+ f_imap->seq, path);
+ MAILBOX_DEBUG2 (folder, MU_DEBUG_PROT, "g%u CREATE %s\n",
+ f_imap->seq, path);
+ f_imap->seq++;
+ free (path);
+ if (status != 0)
+ {
+ m_imap->state = f_imap->state = IMAP_NO_STATE;
+ return status;
+ }
+ f_imap->state = IMAP_CREATE;
+ }
+
+ case IMAP_CREATE:
+ status = imap_send (f_imap);
+ if (status != 0)
+ {
+ if (status != EAGAIN && status != EINPROGRESS
+ && status != EINTR)
+ m_imap->state = f_imap->state = IMAP_NO_STATE;
+
+ return status;
+ }
+ f_imap->state = IMAP_CREATE_ACK;
+
+ case IMAP_CREATE_ACK:
+ status = imap_parse (f_imap);
+ if (status != 0)
+ {
+ if (status == EINVAL)
+ status = EACCES;
+
+ if (status != EAGAIN && status != EINPROGRESS
+ && status != EINTR)
+ m_imap->state = f_imap->state = IMAP_NO_STATE;
+
+ return status;
+ }
+ f_imap->state = IMAP_NO_STATE;
+
+ default:
+ status = EINVAL;
+ break;
+ }
+ m_imap->state = IMAP_NO_STATE;
+ break;
+
+ default:
+ status = EINVAL;
+ break;
+ }
+
+ return status;
+}
+
+/* We can not close the folder in term of shuting down the connection but if
+ we were the selected mailbox we send the close and deselect ourself.
+ The CLOSE is also use to expunge instead of sending expunge. */
+static int
+mailbox_imap_close (mailbox_t mailbox)
+{
+ m_imap_t m_imap = mailbox->data;
+ f_imap_t f_imap = m_imap->f_imap;
+ int status = 0;
+
+ /* If we are not the selected mailbox, just close the stream. */
+ if (m_imap != f_imap->selected)
+ return folder_close (mailbox->folder);
+
+ /* Select first. */
+ status = imap_messages_count (mailbox, NULL);
+ if (status != 0)
+ return status;
+
+ switch (f_imap->state)
+ {
+ case IMAP_NO_STATE:
+ status = imap_writeline (f_imap, "g%d CLOSE\r\n", f_imap->seq++);
+ CHECK_ERROR (f_imap, status);
+ MAILBOX_DEBUG0 (mailbox, MU_DEBUG_PROT, f_imap->buffer);
+ f_imap->state = IMAP_CLOSE;
+
+ case IMAP_CLOSE:
+ status = imap_send (f_imap);
+ CHECK_EAGAIN (f_imap, status);
+ f_imap->state = IMAP_CLOSE_ACK;
+
+ case IMAP_CLOSE_ACK:
+ {
+ size_t i;
+ status = imap_parse (f_imap);
+ CHECK_EAGAIN (f_imap, status);
+ MAILBOX_DEBUG0 (mailbox, MU_DEBUG_PROT, f_imap->buffer);
+
+ monitor_wrlock (mailbox->monitor);
+ /* Destroy the imap messages and ressources associated to them. */
+ for (i = 0; i < m_imap->imessages_count; i++)
+ {
+ if (m_imap->imessages[i])
+ free_subparts (m_imap->imessages[i]);
+ }
+ if (m_imap->imessages)
+ free (m_imap->imessages);
+ m_imap->imessages = NULL;
+ m_imap->imessages_count = 0;
+ m_imap->messages_count = 0;
+ m_imap->recent = 0;
+ m_imap->unseen = 0;
+ /* Clear the callback string structure. */
+ stream_truncate (f_imap->string.stream, 0);
+ f_imap->string.offset = 0;
+ f_imap->string.nleft = 0;
+ f_imap->string.type = IMAP_NO_STATE;
+ f_imap->string.msg_imap = NULL;
+ monitor_unlock (mailbox->monitor);
+ }
+ break;
+
+ default:
+ /* mu_error ("imap_close unknown state: reconnect\n");*/
+ break;
+ }
+
+ /* Deselect. */
+ f_imap->selected = NULL;
+
+ f_imap->state = IMAP_NO_STATE;
+ return folder_close (mailbox->folder);
+}
+
+/* Construction of the message_t, nothing else is done then this setup. To
+ clarify this is different from say message_get_part(). This call is for the
+ mailbox and we are setting up the message_t structure. */
+static int
+imap_get_message (mailbox_t mailbox, size_t msgno, message_t *pmsg)
+{
+ m_imap_t m_imap = mailbox->data;
+ msg_imap_t msg_imap;
+ int status = 0;
+
+ if (pmsg == NULL || msgno == 0 || msgno > m_imap->messages_count)
+ return EINVAL;
+
+ /* Check to see if we have already this message. */
+ monitor_rdlock (mailbox->monitor);
+ {
+ size_t i;
+ for (i = 0; i < m_imap->imessages_count; i++)
+ {
+ if (m_imap->imessages[i])
+ {
+ if (m_imap->imessages[i]->num == msgno)
+ {
+ *pmsg = m_imap->imessages[i]->message;
+ monitor_unlock (mailbox->monitor);
+ return 0;
+ }
+ }
+ }
+ }
+ monitor_unlock (mailbox->monitor);
+
+ /* Allocate a concrete imap message. */
+ msg_imap = calloc (1, sizeof *msg_imap);
+ if (msg_imap == NULL)
+ return ENOMEM;
+ /* Back pointer. */
+ msg_imap->m_imap = m_imap;
+ msg_imap->num = msgno;
+ status = imap_get_message0 (msg_imap, pmsg);
+ if (status == 0)
+ {
+ /* Add it to the list. */
+ monitor_wrlock (mailbox->monitor);
+ {
+ msg_imap_t *m ;
+ m = realloc (m_imap->imessages,
+ (m_imap->imessages_count + 1) * sizeof *m);
+ if (m == NULL)
+ {
+ message_destroy (pmsg, msg_imap);
+ monitor_unlock (mailbox->monitor);
+ return ENOMEM;
+ }
+ m_imap->imessages = m;
+ m_imap->imessages[m_imap->imessages_count] = msg_imap;
+ m_imap->imessages_count++;
+ }
+ monitor_unlock (mailbox->monitor);
+
+ msg_imap->message = *pmsg;
+ }
+ else
+ free (msg_imap);
+ return status;
+}
+
+/* Set all the message_t functions and parts. */
+static int
+imap_get_message0 (msg_imap_t msg_imap, message_t *pmsg)
+{
+ int status = 0;
+ message_t msg = NULL;
+ mailbox_t mailbox = msg_imap->m_imap->mailbox;
+
+ /* Create the message and its stream. */
+ {
+ stream_t stream = NULL;
+ if ((status = message_create (&msg, msg_imap)) != 0
+ || (status = stream_create (&stream, mailbox->flags, msg)) != 0)
+ {
+ stream_destroy (&stream, msg);
+ message_destroy (&msg, msg_imap);
+ return status;
+ }
+ stream_setbufsiz (stream, 128);
+ stream_set_read (stream, imap_message_read, msg);
+ stream_set_fd (stream, imap_message_fd, msg);
+ message_set_stream (msg, stream, msg_imap);
+ message_set_size (msg, imap_message_size, msg_imap);
+ message_set_lines (msg, imap_message_lines, msg_imap);
+ }
+
+ /* Create the header. */
+ {
+ header_t header = NULL;
+ if ((status = header_create (&header, NULL, 0, msg)) != 0)
+ {
+ message_destroy (&msg, msg_imap);
+ return status;
+ }
+ header_set_fill (header, imap_header_read, msg);
+ header_set_get_value (header, imap_header_get_value, msg);
+ header_set_get_fvalue (header, imap_header_get_fvalue, msg);
+ message_set_header (msg, header, msg_imap);
+ }
+
+ /* Create the attribute. */
+ {
+ attribute_t attribute;
+ status = attribute_create (&attribute, msg);
+ if (status != 0)
+ {
+ message_destroy (&msg, msg_imap);
+ return status;
+ }
+ attribute_set_get_flags (attribute, imap_attr_get_flags, msg);
+ attribute_set_set_flags (attribute, imap_attr_set_flags, msg);
+ attribute_set_unset_flags (attribute, imap_attr_unset_flags, msg);
+ message_set_attribute (msg, attribute, msg_imap);
+ }
+
+ /* Create the body and its stream. */
+ {
+ body_t body = NULL;
+ stream_t stream = NULL;
+ if ((status = body_create (&body, msg)) != 0
+ || (status = stream_create (&stream, mailbox->flags, body)) != 0)
+ {
+ body_destroy (&body, msg);
+ stream_destroy (&stream, body);
+ message_destroy (&msg, msg_imap);
+ return status;
+ }
+ stream_setbufsiz (stream, 128);
+ stream_set_read (stream, imap_body_read, body);
+ stream_set_fd (stream, imap_body_fd, body);
+ body_set_size (body, imap_body_size, msg);
+ body_set_lines (body, imap_body_lines, msg);
+ body_set_stream (body, stream, msg);
+ message_set_body (msg, body, msg_imap);
+ }
+
+ /* Set the envelope. */
+ {
+ envelope_t envelope= NULL;
+ status = envelope_create (&envelope, msg);
+ if (status != 0)
+ {
+ message_destroy (&msg, msg_imap);
+ return status;
+ }
+ envelope_set_sender (envelope, imap_envelope_sender, msg);
+ envelope_set_date (envelope, imap_envelope_date, msg);
+ message_set_envelope (msg, envelope, msg_imap);
+ }
+
+ /* Set the mime handling. */
+ message_set_is_multipart (msg, imap_is_multipart, msg_imap);
+ message_set_get_num_parts (msg, imap_get_num_parts, msg_imap);
+ message_set_get_part (msg, imap_get_part, msg_imap);
+
+ /* Set the UID on the message. */
+ message_set_uid (msg, imap_message_uid, msg_imap);
+ message_set_mailbox (msg, mailbox, msg_imap);
+
+ /* We are done here. */
+ *pmsg = msg;
+ return 0;
+}
+
+static int
+imap_message_unseen (mailbox_t mailbox, size_t *punseen)
+{
+ m_imap_t m_imap = mailbox->data;
+ *punseen = m_imap->unseen;
+ return 0;
+}
+
+static int
+imap_messages_recent (mailbox_t mailbox, size_t *precent)
+{
+ m_imap_t m_imap = mailbox->data;
+ *precent = m_imap->recent;
+ return 0;
+}
+
+static int
+imap_uidvalidity (mailbox_t mailbox, unsigned long *puidvalidity)
+{
+ m_imap_t m_imap = mailbox->data;
+ *puidvalidity = m_imap->uidvalidity;
+ return 0;
+}
+
+static int
+imap_uidnext (mailbox_t mailbox, size_t *puidnext)
+{
+ m_imap_t m_imap = mailbox->data;
+ *puidnext = m_imap->uidnext;
+ return 0;
+}
+
+/* There is no explicit call to get the message count. The count is send on
+ a SELECT/EXAMINE command it is also sent async, meaning it will be piggy
+ back on other server response as an untag "EXIST" response. The
+ function is also use as a way to select mailbox by other functions. */
+static int
+imap_messages_count (mailbox_t mailbox, size_t *pnum)
+{
+ m_imap_t m_imap = mailbox->data;
+ f_imap_t f_imap = m_imap->f_imap;
+ int status = 0;
+
+ /* FIXME: It is debatable if we should reconnect when the connection
+ timeout or die. Probably for timeout client should ping i.e. send
+ a NOOP via imap_is_updated() function to keep the connection alive. */
+ status = folder_open (mailbox->folder, mailbox->flags);
+ if (status != 0)
+ return status;
+
+ /* Are we already selected ? */
+ if (m_imap == (f_imap->selected))
+ {
+ if (pnum)
+ *pnum = m_imap->messages_count;
+ return 0;
+ }
+
+ /* Put the mailbox as selected. */
+ f_imap->selected = m_imap;
+
+ switch (f_imap->state)
+ {
+ case IMAP_NO_STATE:
+ status = imap_writeline (f_imap, "g%d SELECT %s\r\n",
+ f_imap->seq++, m_imap->name);
+ CHECK_ERROR (f_imap, status);
+ MAILBOX_DEBUG0 (mailbox, MU_DEBUG_PROT, f_imap->buffer);
+ f_imap->state = IMAP_SELECT;
+
+ case IMAP_SELECT:
+ status = imap_send (f_imap);
+ CHECK_EAGAIN (f_imap, status);
+ f_imap->state = IMAP_SELECT_ACK;
+
+ case IMAP_SELECT_ACK:
+ status = imap_parse (f_imap);
+ CHECK_EAGAIN (f_imap, status);
+ MAILBOX_DEBUG0 (mailbox, MU_DEBUG_PROT, f_imap->buffer);
+ break;
+
+ default:
+ /*mu_error ("imap_message_count unknown state: reconnect\n");*/
+ break;
+ }
+
+ if (pnum)
+ *pnum = m_imap->messages_count;
+
+ f_imap->state = IMAP_NO_STATE;
+ return status;
+}
+
+static int
+imap_scan (mailbox_t mailbox, size_t msgno, size_t *pcount)
+{
+ return imap_scan0 (mailbox, msgno, pcount , 1);
+}
+
+/* Usually when this function is call it is because there is an oberver
+ attach an the client is try to build some sort of list/tree header
+ as the scanning progress. But doing this for each message can be
+ time consuming and inefficient. So we bundle all the request
+ into one and ask the server for everything "FETCH 1:*". The good
+ side is that everything will be faster and we do not do lot of small
+ transcation but rather a big one. The bad thing is that every thing
+ will be cache in the structure using a lot of memory. */
+static int
+imap_scan0 (mailbox_t mailbox, size_t msgno, size_t *pcount, int notif)
+{
+ int status;
+ size_t i;
+ size_t count = 0;
+ m_imap_t m_imap = mailbox->data;
+ f_imap_t f_imap = m_imap->f_imap;
+
+ /* Selected. */
+ status = imap_messages_count (mailbox, &count);
+ if (pcount)
+ *pcount = count;
+ if (status != 0)
+ return status;
+
+ /* No need to scan, there is no messages. */
+ if (count == 0)
+ return 0;
+
+ switch (f_imap->state)
+ {
+ case IMAP_NO_STATE:
+ status = imap_writeline (f_imap,
+ "g%d FETCH 1:* (FLAGS RFC822.SIZE BODY.PEEK[HEADER.FIELDS (%s)])\r\n",
+ f_imap->seq++, MU_IMAP_CACHE_HEADERS);
+ CHECK_ERROR (f_imap, status);
+ MAILBOX_DEBUG0 (mailbox, MU_DEBUG_PROT, f_imap->buffer);
+ f_imap->state = IMAP_SCAN;
+
+ case IMAP_SCAN:
+ status = imap_send (f_imap);
+ CHECK_EAGAIN (f_imap, status);
+ f_imap->state = IMAP_SCAN_ACK;
+ /* Clear the callback string structure. */
+ stream_truncate (f_imap->string.stream, 0);
+ f_imap->string.offset = 0;
+ f_imap->string.nleft = 0;
+ f_imap->string.type = IMAP_NO_STATE;
+ f_imap->string.msg_imap = NULL;
+
+ case IMAP_SCAN_ACK:
+ status = imap_parse (f_imap);
+ CHECK_EAGAIN (f_imap, status);
+ MAILBOX_DEBUG0 (mailbox, MU_DEBUG_PROT, f_imap->buffer);
+ /* Clear the callback string structure. */
+ stream_truncate (f_imap->string.stream, 0);
+ f_imap->string.offset = 0;
+ f_imap->string.nleft = 0;
+ f_imap->string.type = IMAP_NO_STATE;
+ f_imap->string.msg_imap = NULL;
+ break;
+
+ default:
+ /*mu_error ("imap_scan unknown state: reconnect\n");*/
+ return EINVAL;
+ }
+
+ f_imap->state = IMAP_NO_STATE;
+
+ /* Do not send notifications. */
+ if (!notif)
+ return 0;
+
+ /* If no callbacks bail out early. */
+ if (mailbox->observable == NULL)
+ return 0;
+
+ for (i = msgno; i <= count; i++)
+ {
+ if (observable_notify (mailbox->observable, MU_EVT_MESSAGE_ADD) != 0)
+ break;
+ if (((i + 1) % 100) == 0)
+ {
+ observable_notify (mailbox->observable, MU_EVT_MAILBOX_PROGRESS);
+ }
+ }
+ return 0;
+}
+
+/* Send a NOOP and see if the count has changed. */
+static int
+imap_is_updated (mailbox_t mailbox)
+{
+ m_imap_t m_imap = mailbox->data;
+ size_t oldcount = m_imap->messages_count;
+ f_imap_t f_imap = m_imap->f_imap;
+ int status = 0;
+
+ /* Selected. */
+ status = imap_messages_count (mailbox, &oldcount);
+ if (status != 0)
+ return status;
+
+ /* Send a noop, and let imap piggy pack the information. */
+ switch (f_imap->state)
+ {
+ case IMAP_NO_STATE:
+ status = imap_writeline (f_imap, "g%d NOOP\r\n", f_imap->seq++);
+ CHECK_ERROR (f_imap, status);
+ MAILBOX_DEBUG0 (mailbox, MU_DEBUG_PROT, f_imap->buffer);
+ f_imap->state = IMAP_NOOP;
+
+ case IMAP_NOOP:
+ status = imap_send (f_imap);
+ CHECK_EAGAIN (f_imap, status);
+ f_imap->state = IMAP_NOOP_ACK;
+
+ case IMAP_NOOP_ACK:
+ status = imap_parse (f_imap);
+ CHECK_EAGAIN (f_imap, status);
+ MAILBOX_DEBUG0 (mailbox, MU_DEBUG_PROT, f_imap->buffer);
+ break;
+
+ default:
+ /*mu_error ("imap_noop unknown state: reconnect\n"); */
+ break;
+ }
+ f_imap->state = IMAP_NO_STATE;
+ return (oldcount == m_imap->messages_count);
+}
+
+
+/* It is only here that the Deleted flags are sent. Expunge is not
+ call rather the mailbox is close explicitely, letting the server
+ do the expunge without sending the notifications. It's faster. */
+static int
+imap_expunge (mailbox_t mailbox)
+{
+ int status;
+ m_imap_t m_imap = mailbox->data;
+ f_imap_t f_imap = m_imap->f_imap;
+
+ /* Select first. */
+ status = imap_messages_count (mailbox, NULL);
+ if (status != 0)
+ return status;
+
+ switch (f_imap->state)
+ {
+ case IMAP_NO_STATE:
+ {
+ char *set = NULL;
+ status = delete_to_string (m_imap, &set);
+ CHECK_ERROR (f_imap, status);
+ if (set == NULL || *set == '\0')
+ {
+ if (set)
+ free (set);
+ return 0;
+ }
+ status = imap_writeline (f_imap,
+ "g%d STORE %s +FLAGS.SILENT (\\Deleted)\r\n",
+ f_imap->seq++, set);
+ free (set);
+ CHECK_ERROR (f_imap, status);
+ MAILBOX_DEBUG0 (m_imap->mailbox, MU_DEBUG_PROT, f_imap->buffer);
+ f_imap->state = IMAP_STORE;
+ }
+
+ /* Send DELETE. */
+ case IMAP_STORE:
+ status = imap_send (f_imap);
+ CHECK_EAGAIN (f_imap, status);
+ f_imap->state = IMAP_STORE_ACK;
+
+ case IMAP_STORE_ACK:
+ status = imap_parse (f_imap);
+ CHECK_EAGAIN (f_imap, status);
+ MAILBOX_DEBUG0 (m_imap->mailbox, MU_DEBUG_PROT, f_imap->buffer);
+ f_imap->state = IMAP_NO_STATE;
+
+ /* We are not sending EXPUNGE, rather we close the mailbox
+ which will purge. */
+ case IMAP_CLOSE:
+ case IMAP_CLOSE_ACK:
+ status = mailbox_imap_close (mailbox);
+ CHECK_EAGAIN (f_imap, status);
+
+ /* Rescan after expunging but do not trigger the observers. */
+ case IMAP_SCAN:
+ case IMAP_SCAN_ACK:
+ status = imap_scan0 (mailbox, 1, NULL, 0);
+ CHECK_EAGAIN (f_imap, status);
+
+ default:
+ /* mu_error ("imap_expunge: unknow state\n"); */
+ break;
+ }
+
+ return status;
+}
+
+/* FIXME: Not ___Nonblocking___ safe. */
+/* DANGER: The message_t object makes no guaranty about the size and the lines
+ that it returns, if its pointing to non-local file messages, so we
+ make a local copy. */
+static int
+imap_append_message (mailbox_t mailbox, message_t msg)
+{
+ int status = 0;
+ m_imap_t m_imap = mailbox->data;
+ f_imap_t f_imap = m_imap->f_imap;
+
+ /* FIXME: It is debatable if we should reconnect when the connection
+ timeout or die. For timeout client should ping i.e. send
+ a NOOP via imap_is_updated() function to keep the connection alive. */
+ status = folder_open (mailbox->folder, mailbox->flags);
+ if (status != 0)
+ return status;
+
+ /* FIXME: Can we append to self. */
+
+ /* Check to see if we are selected. If the message was not modified
+ and came from the same imap folder. use COPY.*/
+ if (f_imap->selected != m_imap && !message_is_modified (msg)
+ && is_same_folder (mailbox, msg))
+ return imap_copy_message (mailbox, msg);
+
+ /* copy the message to local disk by createing a floating message. */
+ {
+ message_t message = NULL;
+
+ status = message_create_copy(&message, msg);
+
+ if (status == 0)
+ status = imap_append_message0 (mailbox, message);
+ message_destroy (&message, NULL);
+ }
+ return status;
+}
+
+/* Ok this mean that the message is coming from somewhere else. IMAP
+ is very susceptible on the size, example:
+ A003 APPEND saved-messages (\Seen) {310}
+ if the server does not get the right size advertise in the string literal
+ it will misbehave. Sine we are assuming that the message will be
+ in native file system format meaning ending with NEWLINE, we will have
+ to do the calculation. But what is worse; the value return
+ by message_size () and message_lines () are no mean exact but rather
+ a gross approximation for certain type of mailbox. So the sane
+ thing to do is to save the message in temporary file, this we say
+ we guarantee the size of the message. */
+static int
+imap_append_message0 (mailbox_t mailbox, message_t msg)
+{
+ size_t total;
+ int status = 0;
+ m_imap_t m_imap = mailbox->data;
+ f_imap_t f_imap = m_imap->f_imap;
+
+ switch (f_imap->state)
+ {
+ case IMAP_NO_STATE:
+ {
+ size_t lines, size;
+ char *path;
+ char *abuf = malloc (1);
+ /* Get the desired flags attribute. */
+ if (abuf == NULL)
+ return ENOMEM;
+ *abuf = '\0';
+ {
+ attribute_t attribute = NULL;
+ int flags = 0;
+ message_get_attribute (msg, &attribute);
+ attribute_get_flags (attribute, &flags);
+ status = flags_to_string (&abuf, flags);
+ if (status != 0)
+ return status;
+ /* Put the surrounding parenthesis, wu-IMAP is sensible to this. */
+ {
+ char *tmp = calloc (strlen (abuf) + 3, 1);
+ if (tmp == NULL)
+ {
+ free (abuf);
+ return ENOMEM;
+ }
+ sprintf (tmp, "(%s)", abuf);
+ free (abuf);
+ abuf = tmp;
+ }
+ }
+
+ /* Get the mailbox filepath. */
+ {
+ size_t n = 0;
+ url_get_path (mailbox->url, NULL, 0, &n);
+ if (n == 0)
+ {
+ if (!(path = strdup ("INBOX")))
+ {
+ free (abuf);
+ return ENOMEM;
+ }
+ }
+ else
+ {
+ path = calloc (n + 1, sizeof (*path));
+ if (path == NULL)
+ {
+ free (abuf);
+ return ENOMEM;
+ }
+ url_get_path (mailbox->url, path, n + 1, NULL);
+ }
+ }
+
+ /* FIXME: we need to get the envelope_date and use it.
+ currently it is ignored. */
+
+ /* Get the total size, assuming that it is in UNIX format. */
+ lines = size = 0;
+ message_size (msg, &size);
+ message_lines (msg, &lines);
+ total = size + lines;
+ status = imap_writeline (f_imap, "g%d APPEND %s %s {%d}\r\n",
+ f_imap->seq++, path, abuf, size + lines);
+ free (abuf);
+ free (path);
+ CHECK_ERROR (f_imap, status);
+ MAILBOX_DEBUG0 (mailbox, MU_DEBUG_PROT, f_imap->buffer);
+ f_imap->state = IMAP_APPEND;
+ }
+
+ case IMAP_APPEND:
+ status = imap_send (f_imap);
+ CHECK_EAGAIN (f_imap, status);
+ f_imap->state = IMAP_APPEND_CONT;
+
+ case IMAP_APPEND_CONT:
+ status = imap_parse (f_imap);
+ CHECK_EAGAIN (f_imap, status);
+ MAILBOX_DEBUG0 (mailbox, MU_DEBUG_PROT, f_imap->buffer);
+ /* If we did not receive the continuation token, it is an error
+ bail out. */
+ if (f_imap->buffer[0] != '+')
+ {
+ status = EACCES;
+ break;
+ }
+ f_imap->state = IMAP_APPEND_SEND;
+
+ case IMAP_APPEND_SEND:
+ {
+ stream_t stream = NULL;
+ off_t off = 0;
+ size_t n = 0;
+ char buffer[255];
+ message_get_stream (msg, &stream);
+ while (stream_readline (stream, buffer, sizeof buffer, off, &n) == 0
+ && n > 0)
+ {
+ if (buffer[n - 1] == '\n')
+ {
+ buffer[n - 1] = '\0';
+ status = imap_writeline (f_imap, "%s\r\n", buffer);
+ }
+ else
+ imap_writeline (f_imap, "%s", buffer);
+ off += n;
+ status = imap_send (f_imap);
+ CHECK_EAGAIN (f_imap, status);
+ }
+ f_imap->state = IMAP_APPEND_ACK;
+ }
+ /* !@#%$ UW-IMAP server hack: insists on the last line. */
+ imap_writeline (f_imap, "\n");
+ status = imap_send (f_imap);
+ CHECK_EAGAIN (f_imap, status);
+
+ case IMAP_APPEND_ACK:
+ status = imap_parse (f_imap);
+ CHECK_EAGAIN (f_imap, status);
+ MAILBOX_DEBUG0 (m_imap->mailbox, MU_DEBUG_PROT, f_imap->buffer);
+
+ default:
+ /* mu_error ("imap_append: unknown state\n"); */
+ break;
+ }
+ f_imap->state = IMAP_NO_STATE;
+ return status;
+}
+
+/* If the message is on the same server. Use the COPY command much more
+ efficient. */
+static int
+imap_copy_message (mailbox_t mailbox, message_t msg)
+{
+ m_imap_t m_imap = mailbox->data;
+ f_imap_t f_imap = m_imap->f_imap;
+ msg_imap_t msg_imap = message_get_owner (msg);
+ int status = 0;
+
+ /* FIXME: It is debatable if we should reconnect when the connection
+ timeout or die. For timeout client should ping i.e. send
+ a NOOP via imap_is_updated() function to keep the connection alive. */
+ status = folder_open (mailbox->folder, mailbox->flags);
+ if (status != 0)
+ return status;
+
+ switch (f_imap->state)
+ {
+ case IMAP_NO_STATE:
+ {
+ char *path;
+ size_t n = 0;
+ /* Check for a valid mailbox name. */
+ url_get_path (mailbox->url, NULL, 0, &n);
+ if (n == 0)
+ return EINVAL;
+ path = calloc (n + 1, sizeof (*path));
+ if (path == NULL)
+ return ENOMEM;
+ url_get_path (mailbox->url, path, n + 1, NULL);
+ status = imap_writeline (f_imap, "g%d COPY %d %s\r\n", f_imap->seq++,
+ msg_imap->num, path);
+ free (path);
+ CHECK_ERROR (f_imap, status);
+ MAILBOX_DEBUG0 (mailbox, MU_DEBUG_PROT, f_imap->buffer);
+ f_imap->state = IMAP_COPY;
+ }
+
+ case IMAP_COPY:
+ status = imap_send (f_imap);
+ CHECK_EAGAIN (f_imap, status);
+ f_imap->state = IMAP_COPY_ACK;
+
+ case IMAP_COPY_ACK:
+ status = imap_parse (f_imap);
+ CHECK_EAGAIN (f_imap, status);
+ MAILBOX_DEBUG0 (mailbox, MU_DEBUG_PROT, f_imap->buffer);
+
+ default:
+ break;
+ }
+ f_imap->state = IMAP_NO_STATE;
+ return status;
+}
+
+/* Message read overload */
+static int
+imap_message_read (stream_t stream, char *buffer, size_t buflen,
+ off_t offset, size_t *plen)
+{
+ message_t msg = stream_get_owner (stream);
+ msg_imap_t msg_imap = message_get_owner (msg);
+ m_imap_t m_imap = msg_imap->m_imap;
+ f_imap_t f_imap = m_imap->f_imap;
+ char *oldbuf = NULL;
+ char newbuf[2];
+ int status;
+
+ /* This is so annoying, a buffer len of 1 is a killer. If you have for
+ example "\n" to retrieve from the server, IMAP will transform this to
+ "\r\n" and since you ask for only 1, the server will send '\r' only.
+ And ... '\r' will be stripped by (imap_readline()) the number of char
+ read will be 0 which means we're done .... sigh ... So we guard by at
+ least ask for 2 chars. */
+ if (buflen == 1)
+ {
+ oldbuf = buffer;
+ buffer = newbuf;
+ buflen = 2;
+ }
+
+ /* Start over. */
+ if (offset == 0)
+ msg_imap->message_lines = 0;
+
+ status = imap_messages_count (m_imap->mailbox, NULL);
+ if (status != 0)
+ return status;
+
+ /* Select first. */
+ if (f_imap->state == IMAP_NO_STATE)
+ {
+ char *section = NULL;
+
+ if (msg_imap->part)
+ section = section_name (msg_imap);
+
+ /* We have strip the \r, but the offset on the imap server is with that
+ octet(CFLF) so add it in the offset, it's the number of lines. */
+ status = imap_writeline (f_imap,
+ "g%d FETCH %d BODY.PEEK[%s]<%d.%d>\r\n",
+ f_imap->seq++, msg_imap->num,
+ (section) ? section : "",
+ offset + msg_imap->message_lines, buflen);
+ if (section)
+ free (section);
+ CHECK_ERROR (f_imap, status);
+ MAILBOX_DEBUG0 (m_imap->mailbox, MU_DEBUG_PROT, f_imap->buffer);
+ f_imap->state = IMAP_FETCH;
+ }
+ status = fetch_operation (f_imap, msg_imap, buffer, buflen, plen);
+
+ if (oldbuf)
+ oldbuf[0] = buffer[0];
+ return status;
+}
+
+static int
+imap_message_lines (message_t msg, size_t *plines)
+{
+ msg_imap_t msg_imap = message_get_owner (msg);
+ if (plines && msg_imap)
+ {
+ if (msg_imap->message_lines == 0)
+ *plines = msg_imap->body_lines + msg_imap->header_lines;
+ else
+ *plines = msg_imap->message_lines;
+ }
+ return 0;
+}
+
+/* Sometimes a message is just a place container for other sub parts.
+ In those cases imap bodystructure does not set the message_size aka
+ the body_size. But we can calculate it since the message_size
+ is the sum of its subparts. */
+static int
+imap_submessage_size (msg_imap_t msg_imap, size_t *psize)
+{
+ if (psize)
+ {
+ *psize = 0;
+ if (msg_imap->message_size == 0)
+ {
+ size_t i, size;
+ for (size = i = 0; i < msg_imap->num_parts; i++, size = 0)
+ {
+ if (msg_imap->parts[i])
+ imap_submessage_size (msg_imap->parts[i], &size);
+ *psize += size;
+ }
+ }
+ else
+ *psize = (msg_imap->message_size + msg_imap->header_size)
+ - msg_imap->message_lines;
+ }
+ return 0;
+}
+
+static int
+imap_message_size (message_t msg, size_t *psize)
+{
+ msg_imap_t msg_imap = message_get_owner (msg);
+ m_imap_t m_imap = msg_imap->m_imap;
+ f_imap_t f_imap = m_imap->f_imap;
+ int status = 0;;
+
+ status = imap_messages_count (m_imap->mailbox, NULL);
+ if (status != 0)
+ return status;
+
+ /* If there is a parent it means it is a sub message, IMAP does not give
+ the full size of mime messages, so the message_size retrieved from
+ doing a bodystructure represents rather the body_size. */
+ if (msg_imap->parent)
+ return imap_submessage_size (msg_imap, psize);
+
+ if (msg_imap->message_size == 0)
+ {
+ /* Select first. */
+ if (f_imap->state == IMAP_NO_STATE)
+ {
+ /* We strip the \r, but the offset/size on the imap server is with
+ that octet so add it in the offset, since it's the number of
+ lines. */
+ status = imap_writeline (f_imap,
+ "g%d FETCH %d RFC822.SIZE\r\n",
+ f_imap->seq++, msg_imap->num);
+ CHECK_ERROR (f_imap, status);
+ MAILBOX_DEBUG0 (m_imap->mailbox, MU_DEBUG_PROT, f_imap->buffer);
+ f_imap->state = IMAP_FETCH;
+ }
+ status = fetch_operation (f_imap, msg_imap, 0, 0, 0);
+ }
+
+ if (status == 0)
+ {
+ if (psize)
+ *psize = msg_imap->message_size - msg_imap->message_lines;
+ }
+ return status;
+}
+
+static int
+imap_message_uid (message_t msg, size_t *puid)
+{
+ msg_imap_t msg_imap = message_get_owner (msg);
+ m_imap_t m_imap = msg_imap->m_imap;
+ f_imap_t f_imap = m_imap->f_imap;
+ int status;
+
+ if (puid)
+ return 0;
+
+ /* Select first. */
+ status = imap_messages_count (m_imap->mailbox, NULL);
+ if (status != 0)
+ return status;
+
+ if (f_imap->state == IMAP_NO_STATE)
+ {
+ if (msg_imap->uid)
+ {
+ *puid = msg_imap->uid;
+ return 0;
+ }
+ status = imap_writeline (f_imap, "g%d FETCH %d UID\r\n",
+ f_imap->seq++, msg_imap->num);
+ CHECK_ERROR (f_imap, status);
+ MAILBOX_DEBUG0 (m_imap->mailbox, MU_DEBUG_PROT, f_imap->buffer);
+ f_imap->state = IMAP_FETCH;
+ }
+ status = fetch_operation (f_imap, msg_imap, 0, 0, 0);
+ if (status != 0)
+ return status;
+ *puid = msg_imap->uid;
+ return 0;
+}
+
+static int
+imap_message_fd (stream_t stream, int * pfd)
+{
+ message_t msg = stream_get_owner (stream);
+ msg_imap_t msg_imap = message_get_owner (msg);
+ return imap_get_fd (msg_imap, pfd);
+}
+
+/* Mime. */
+static int
+imap_is_multipart (message_t msg, int *ismulti)
+{
+ msg_imap_t msg_imap = message_get_owner (msg);
+ m_imap_t m_imap = msg_imap->m_imap;
+ f_imap_t f_imap = m_imap->f_imap;
+ int status;
+
+ /* Select first. */
+ status = imap_messages_count (m_imap->mailbox, NULL);
+ if (status != 0)
+ return status;
+
+ if (f_imap->state == IMAP_NO_STATE)
+ {
+ if (msg_imap->num_parts || msg_imap->part)
+ {
+ if (ismulti)
+ *ismulti = (msg_imap->num_parts > 1);
+ return 0;
+ }
+ status = imap_writeline (f_imap,
+ "g%d FETCH %d BODYSTRUCTURE\r\n",
+ f_imap->seq++, msg_imap->num);
+ CHECK_ERROR (f_imap, status);
+ MAILBOX_DEBUG0 (m_imap->mailbox, MU_DEBUG_PROT, f_imap->buffer);
+ f_imap->state = IMAP_FETCH;
+ }
+ status = fetch_operation (f_imap, msg_imap, 0, 0, 0);
+ if (status != 0)
+ return status;
+ if (ismulti)
+ *ismulti = (msg_imap->num_parts > 1);
+ return 0;
+}
+
+static int
+imap_get_num_parts (message_t msg, size_t *nparts)
+{
+ msg_imap_t msg_imap = message_get_owner (msg);
+ if (msg_imap)
+ {
+ if (msg_imap->num_parts == 0)
+ {
+ int status = imap_is_multipart (msg, NULL);
+ if (status != 0)
+ return status;
+ }
+ if (nparts)
+ *nparts = (msg_imap->num_parts == 0) ? 1 : msg_imap->num_parts;
+ }
+ return 0;
+}
+
+static int
+imap_get_part (message_t msg, size_t partno, message_t *pmsg)
+{
+ msg_imap_t msg_imap = message_get_owner (msg);
+ int status = 0;
+
+ if (msg_imap->num_parts == 0)
+ {
+ status = imap_get_num_parts (msg, NULL);
+ if (status != 0)
+ return status;
+ }
+
+ if (partno <= msg_imap->num_parts)
+ {
+ if (msg_imap->parts[partno - 1]->message)
+ {
+ if (pmsg)
+ *pmsg = msg_imap->parts[partno - 1]->message;
+ }
+ else
+ {
+ message_t message;
+ status = imap_get_message0 (msg_imap->parts[partno - 1], &message);
+ if (status == 0)
+ {
+ header_t header;
+ message_get_header (message, &header);
+ header_set_get_value (header, NULL, message);
+ message_set_stream (message, NULL, msg_imap->parts[partno - 1]);
+ /* message_set_size (message, NULL, msg_imap->parts[partno - 1]); */
+ msg_imap->parts[partno - 1]->message = message;
+ if (pmsg)
+ *pmsg = message;
+ }
+ }
+ }
+ else
+ {
+ if (pmsg)
+ *pmsg = msg_imap->message;
+ }
+ return status;
+}
+
+/* Envelope. */
+static int
+imap_envelope_sender (envelope_t envelope, char *buffer, size_t buflen,
+ size_t *plen)
+{
+ message_t msg = envelope_get_owner (envelope);
+ header_t header;
+ int status;
+
+ if (buflen == 0)
+ return 0;
+
+ message_get_header (msg, &header);
+ status = header_get_value (header, MU_HEADER_SENDER, buffer, buflen, plen);
+ if (status == EAGAIN)
+ return status;
+ else if (status != 0)
+ status = header_get_value (header, MU_HEADER_FROM, buffer, buflen, plen);
+ if (status == 0)
+ {
+ address_t address;
+ if (address_create (&address, buffer) == 0)
+ {
+ address_get_email (address, 1, buffer, buflen, plen);
+ address_destroy (&address);
+ }
+ }
+ else if (status != EAGAIN)
+ {
+ strncpy (buffer, "Unknown", buflen)[buflen - 1] = '0';
+ if (plen)
+ *plen = strlen (buffer);
+ }
+ return status;
+}
+
+static int
+imap_envelope_date (envelope_t envelope, char *buffer, size_t buflen,
+ size_t *plen)
+{
+ message_t msg = envelope_get_owner (envelope);
+ msg_imap_t msg_imap = message_get_owner (msg);
+ m_imap_t m_imap = msg_imap->m_imap;
+ f_imap_t f_imap = m_imap->f_imap;
+ struct tm tm;
+ mu_timezone tz;
+ time_t now;
+ char datebuf[] = "mm-dd-yyyy hh:mm:ss +0000";
+ const char* date = datebuf;
+ const char** datep = &date;
+ /* reserve as much space as we need for internal-date */
+ int status;
+
+ /* Select first. */
+ status = imap_messages_count (m_imap->mailbox, NULL);
+ if (status != 0)
+ return status;
+ if (msg_imap->internal_date == NULL)
+ {
+ if (f_imap->state == IMAP_NO_STATE)
+ {
+ status = imap_writeline (f_imap,
+ "g%d FETCH %d INTERNALDATE\r\n",
+ f_imap->seq++, msg_imap->num);
+ CHECK_ERROR (f_imap, status);
+ MAILBOX_DEBUG0 (m_imap->mailbox, MU_DEBUG_PROT, f_imap->buffer);
+ f_imap->state = IMAP_FETCH;
+ }
+ status = fetch_operation (f_imap, msg_imap, datebuf,
+ sizeof datebuf, NULL);
+ if (status != 0)
+ return status;
+ msg_imap->internal_date = strdup (datebuf);
+ }
+ else
+ {
+ date = msg_imap->internal_date;
+ datep = &date;
+ }
+
+ if (mu_parse_imap_date_time(datep, &tm, &tz) != 0)
+ now = (time_t)-1;
+ else
+ now = mu_tm2time (&tm, &tz);
+
+ /* if the time was unparseable, or mktime() didn't like what we
+ parsed, use the calendar time. */
+ if (now == (time_t)-1)
+ {
+ struct tm* gmt;
+
+ time(&now);
+ gmt = gmtime(&now);
+ tm = *gmt;
+ }
+
+ {
+ int len = strftime (buffer, buflen, " %a %b %d %H:%M:%S %Y", &tm);
+
+ /* FIXME: I don't know what strftime does if the buflen is too
+ short, or it fails. Assuming that it won't fail, this is my guess
+ as to the right thing.
+
+ I think if the buffer is too short, it will fill it as much
+ as it can, and nul terminate it. But I'll terminate it anyhow.
+ */
+ if(len == 0)
+ {
+ len = buflen - 1;
+ buffer[len] = 0;
+ }
+
+ if(plen)
+ *plen = len;
+ }
+ return 0;
+}
+
+/* Attributes. */
+static int
+imap_attr_get_flags (attribute_t attribute, int *pflags)
+{
+ message_t msg = attribute_get_owner (attribute);
+ msg_imap_t msg_imap = message_get_owner (msg);
+ m_imap_t m_imap = msg_imap->m_imap;
+ f_imap_t f_imap = m_imap->f_imap;
+ int status = 0;
+
+ /* Select first. */
+ status = imap_messages_count (m_imap->mailbox, NULL);
+ if (status != 0)
+ return status;
+
+ /* Did we retrieve it alread ? */
+ if (msg_imap->flags != 0)
+ {
+ if (pflags)
+ *pflags = msg_imap->flags;
+ return 0;
+ }
+
+ if (f_imap->state == IMAP_NO_STATE)
+ {
+ status = imap_writeline (f_imap, "g%d FETCH %d FLAGS\r\n",
+ f_imap->seq++, msg_imap->num);
+ CHECK_ERROR (f_imap, status);
+ MAILBOX_DEBUG0 (m_imap->mailbox, MU_DEBUG_PROT, f_imap->buffer);
+ f_imap->state = IMAP_FETCH;
+ }
+ status = fetch_operation (f_imap, msg_imap, NULL, 0, NULL);
+ if (status == 0)
+ {
+ if (pflags)
+ *pflags = msg_imap->flags;
+ }
+ return status;
+}
+
+static int
+imap_attr_set_flags (attribute_t attribute, int flag)
+{
+ message_t msg = attribute_get_owner (attribute);
+ msg_imap_t msg_imap = message_get_owner (msg);
+ m_imap_t m_imap = msg_imap->m_imap;
+ f_imap_t f_imap = m_imap->f_imap;
+ int status = 0;
+
+ /* Select first. */
+ status = imap_messages_count (m_imap->mailbox, NULL);
+ if (status != 0)
+ return status;
+
+ /* If already set don't bother. */
+ if (msg_imap->flags & flag)
+ return 0;
+
+ /* The delete FLAG is not pass yet but only on the expunge. */
+ if (flag & MU_ATTRIBUTE_DELETED)
+ {
+ msg_imap->flags |= MU_ATTRIBUTE_DELETED;
+ flag &= ~MU_ATTRIBUTE_DELETED;
+ }
+
+ if (f_imap->state == IMAP_NO_STATE)
+ {
+ char *abuf = malloc (1);
+ if (abuf == NULL)
+ return ENOMEM;
+ *abuf = '\0';
+ status = flags_to_string (&abuf, flag);
+ if (status != 0)
+ return status;
+ /* No flags to send?? */
+ if (*abuf == '\0')
+ {
+ free (abuf);
+ return 0;
+ }
+ status = imap_writeline (f_imap, "g%d STORE %d +FLAGS.SILENT (%s)\r\n",
+ f_imap->seq++, msg_imap->num, abuf);
+ free (abuf);
+ CHECK_ERROR (f_imap, status);
+ MAILBOX_DEBUG0 (m_imap->mailbox, MU_DEBUG_PROT, f_imap->buffer);
+ msg_imap->flags |= flag;
+ f_imap->state = IMAP_FETCH;
+ }
+ return fetch_operation (f_imap, msg_imap, NULL, 0, NULL);
+}
+
+static int
+imap_attr_unset_flags (attribute_t attribute, int flag)
+{
+ message_t msg = attribute_get_owner (attribute);
+ msg_imap_t msg_imap = message_get_owner (msg);
+ m_imap_t m_imap = msg_imap->m_imap;
+ f_imap_t f_imap = m_imap->f_imap;
+ int status = 0;
+
+ /* Select first. */
+ status = imap_messages_count (m_imap->mailbox, NULL);
+ if (status != 0)
+ return status;
+
+ /* The delete FLAG is not pass yet but only on the expunge. */
+ if (flag & MU_ATTRIBUTE_DELETED)
+ {
+ msg_imap->flags &= ~MU_ATTRIBUTE_DELETED;
+ flag &= ~MU_ATTRIBUTE_DELETED;
+ }
+
+ if (f_imap->state == IMAP_NO_STATE)
+ {
+ char *abuf = malloc (1);
+ if (abuf == NULL)
+ return ENOMEM;
+ *abuf = '\0';
+ status = flags_to_string (&abuf, flag);
+ if (status != 0)
+ return status;
+ /* No flags to send?? */
+ if (*abuf == '\0')
+ {
+ free (abuf);
+ return 0;
+ }
+ status = imap_writeline (f_imap, "g%d STORE %d -FLAGS.SILENT (%s)\r\n",
+ f_imap->seq++, msg_imap->num, abuf);
+ free (abuf);
+ CHECK_ERROR (f_imap, status);
+ MAILBOX_DEBUG0 (m_imap->mailbox, MU_DEBUG_PROT, f_imap->buffer);
+ msg_imap->flags &= ~flag;
+ f_imap->state = IMAP_FETCH;
+ }
+ return fetch_operation (f_imap, msg_imap, NULL, 0, NULL);
+}
+
+/* Header. */
+static int
+imap_header_get_value (header_t header, const char *field, char * buffer,
+ size_t buflen, size_t *plen)
+{
+ message_t msg = header_get_owner (header);
+ msg_imap_t msg_imap = message_get_owner (msg);
+ m_imap_t m_imap = msg_imap->m_imap;
+ f_imap_t f_imap = m_imap->f_imap;
+ int status;
+ size_t len = 0;
+ char *value;
+
+ /* Select first. */
+ status = imap_messages_count (m_imap->mailbox, NULL);
+ if (status != 0)
+ return status;
+
+ /* Hack, if buffer == NULL they want to know how big is the field value,
+ Unfortunately IMAP does not say, so we take a guess hoping that the
+ value will not be over 1024. */
+ if (buffer == NULL || buflen == 0)
+ len = 1024;
+ else
+ len = strlen (field) + buflen + 4;
+
+ if (f_imap->state == IMAP_NO_STATE)
+ {
+ /* Select first. */
+ status = imap_messages_count (m_imap->mailbox, NULL);
+ if (status != 0)
+ return status;
+ status = imap_writeline (f_imap,
+ "g%d FETCH %d BODY.PEEK[HEADER.FIELDS (%s)]<0.%d>\r\n",
+ f_imap->seq++, msg_imap->num, field, len);
+ CHECK_ERROR (f_imap, status);
+ MAILBOX_DEBUG0 (m_imap->mailbox, MU_DEBUG_PROT, f_imap->buffer);
+ f_imap->state = IMAP_FETCH;
+
+ }
+
+ value = calloc (len, sizeof (*value));
+ status = fetch_operation (f_imap, msg_imap, value, len, &len);
+ if (status == 0)
+ {
+ char *colon;
+ /* The field-matching is case-insensitive. In all cases, the
+ delimiting newline between the header and the body is always
+ included. Nuke it */
+ if (len)
+ value[len - 1] = '\0';
+
+ /* Move pass the field-name. */
+ colon = strchr (value, ':');
+ if (colon)
+ {
+ colon++;
+ if (*colon == ' ')
+ colon++;
+ }
+ else
+ colon = value;
+
+ while (*colon && colon[strlen (colon) - 1] == '\n')
+ colon[strlen (colon) - 1] = '\0';
+
+ if (buffer && buflen)
+ {
+ strncpy (buffer, colon, buflen);
+ buffer[buflen - 1] = '\0';
+ }
+ len = strlen (buffer);
+ if (plen)
+ *plen = len;
+ if (len == 0)
+ status = ENOENT;
+ }
+ free (value);
+ return status;
+}
+
+static int
+imap_header_get_fvalue (header_t header, const char *field, char * buffer,
+ size_t buflen, size_t *plen)
+{
+ message_t msg = header_get_owner (header);
+ msg_imap_t msg_imap = message_get_owner (msg);
+ m_imap_t m_imap = msg_imap->m_imap;
+ f_imap_t f_imap = m_imap->f_imap;
+ int status;
+ size_t len = 0;
+ char *value;
+
+ /* Select first. */
+ status = imap_messages_count (m_imap->mailbox, NULL);
+ if (status != 0)
+ return status;
+
+ /* Do we all ready have the headers. */
+ if (msg_imap->fheader)
+ return header_get_value (msg_imap->fheader, field, buffer, buflen, plen);
+
+ /* We are caching the must use headers. */
+ if (f_imap->state == IMAP_NO_STATE)
+ {
+ /* Select first. */
+ status = imap_messages_count (m_imap->mailbox, NULL);
+ if (status != 0)
+ return status;
+ status = imap_writeline (f_imap,
+ "g%d FETCH %d (FLAGS RFC822.SIZE BODY.PEEK[HEADER.FIELDS (%s)])\r\n",
+ f_imap->seq++, msg_imap->num,
+ MU_IMAP_CACHE_HEADERS);
+ CHECK_ERROR (f_imap, status);
+ MAILBOX_DEBUG0 (m_imap->mailbox, MU_DEBUG_PROT, f_imap->buffer);
+ f_imap->state = IMAP_FETCH;
+
+ }
+
+ /* Should be enough for our needs. */
+ len = 2048;
+ value = calloc (len, sizeof *value);
+ status = fetch_operation (f_imap, msg_imap, value, len, &len);
+ if (status == 0)
+ {
+ status = header_create (&msg_imap->fheader, value, len, NULL);
+ if (status == 0)
+ status = header_get_value (msg_imap->fheader, field, buffer,
+ buflen, plen);
+ }
+ free (value);
+ return status;
+}
+
+static int
+imap_header_read (header_t header, char *buffer, size_t buflen, off_t offset,
+ size_t *plen)
+{
+ message_t msg = header_get_owner (header);
+ msg_imap_t msg_imap = message_get_owner (msg);
+ m_imap_t m_imap = msg_imap->m_imap;
+ f_imap_t f_imap = m_imap->f_imap;
+ char *oldbuf = NULL;
+ char newbuf[2];
+ int status;
+
+ /* This is so annoying, a buffer len of 1 is a killer. If you have for
+ example "\n" to retrieve from the server, IMAP will transform this to
+ "\r\n" and since you ask for only 1, the server will send '\r' only.
+ And ... '\r' will be stripped by (imap_readline()) the number of char
+ read will be 0 which means we're done .... sigh ... So we guard by at
+ least ask for 2 chars. */
+ if (buflen == 1)
+ {
+ oldbuf = buffer;
+ buffer = newbuf;
+ buflen = 2;
+ }
+
+ /* Start over. */
+ if (offset == 0)
+ msg_imap->header_lines = 0;
+
+ /* Select first. */
+ status = imap_messages_count (m_imap->mailbox, NULL);
+ if (status != 0)
+ return status;
+
+ if (f_imap->state == IMAP_NO_STATE)
+ {
+ /* We strip the \r, but the offset/size on the imap server is with that
+ octet so add it in the offset, since it's the number of lines. */
+ if (msg_imap->part)
+ {
+ char *section = section_name (msg_imap);
+ status = imap_writeline (f_imap,
+ "g%d FETCH %d BODY.PEEK[%s.MIME]<%d.%d>\r\n",
+ f_imap->seq++, msg_imap->num,
+ (section) ? section : "",
+ offset + msg_imap->header_lines, buflen);
+ if (section)
+ free (section);
+ }
+ else
+ status = imap_writeline (f_imap,
+ "g%d FETCH %d BODY.PEEK[HEADER]<%d.%d>\r\n",
+ f_imap->seq++, msg_imap->num,
+ offset + msg_imap->header_lines, buflen);
+ CHECK_ERROR (f_imap, status);
+ MAILBOX_DEBUG0 (m_imap->mailbox, MU_DEBUG_PROT, f_imap->buffer);
+ f_imap->state = IMAP_FETCH;
+
+ }
+ status = fetch_operation (f_imap, msg_imap, buffer, buflen, plen);
+ if (oldbuf)
+ oldbuf[0] = buffer[0];
+ return status;
+}
+
+/* Body. */
+static int
+imap_body_size (body_t body, size_t *psize)
+{
+ message_t msg = body_get_owner (body);
+ msg_imap_t msg_imap = message_get_owner (msg);
+ if (psize && msg_imap)
+ {
+ /* If there is a parent it means it is a sub message, IMAP does not give
+ the full size of mime messages, so the message_size was retrieve from
+ doing a bodystructure and represents rather the body_size. */
+ if (msg_imap->parent)
+ {
+ *psize = msg_imap->message_size - msg_imap->message_lines;
+ }
+ else
+ {
+ if (msg_imap->body_size)
+ *psize = msg_imap->body_size;
+ else if (msg_imap->message_size)
+ *psize = msg_imap->message_size
+ - (msg_imap->header_size + msg_imap->header_lines);
+ else
+ *psize = 0;
+ }
+ }
+ return 0;
+}
+
+static int
+imap_body_lines (body_t body, size_t *plines)
+{
+ message_t msg = body_get_owner (body);
+ msg_imap_t msg_imap = message_get_owner (msg);
+ if (plines && msg_imap)
+ *plines = msg_imap->body_lines;
+ return 0;
+}
+
+/* FIXME: Send EISPIPE if trying to seek back. */
+static int
+imap_body_read (stream_t stream, char *buffer, size_t buflen, off_t offset,
+ size_t *plen)
+{
+ body_t body = stream_get_owner (stream);
+ message_t msg = body_get_owner (body);
+ msg_imap_t msg_imap = message_get_owner (msg);
+ m_imap_t m_imap = msg_imap->m_imap;
+ f_imap_t f_imap = m_imap->f_imap;
+ char *oldbuf = NULL;
+ char newbuf[2];
+ int status;
+
+ /* This is so annoying, a buffer len of 1 is a killer. If you have for
+ example "\n" to retrieve from the server, IMAP will transform this to
+ "\r\n" and since you ask for only 1, the server will send '\r' only.
+ And ... '\r' will be stripped by (imap_readline()) the number of char
+ read will be 0 which means we're done .... sigh ... So we guard by at
+ least ask for 2 chars. */
+ if (buflen == 1)
+ {
+ oldbuf = buffer;
+ buffer = newbuf;
+ buflen = 2;
+ }
+
+ /* Start over. */
+ if (offset == 0)
+ {
+ msg_imap->body_lines = 0;
+ msg_imap->body_size = 0;
+ }
+
+ /* Select first. */
+ status = imap_messages_count (m_imap->mailbox, NULL);
+ if (status != 0)
+ return status;
+
+ if (f_imap->state == IMAP_NO_STATE)
+ {
+ /* We strip the \r, but the offset/size on the imap server is with the
+ octet, so add it since it's the number of lines. */
+ if (msg_imap->part)
+ {
+ char *section = section_name (msg_imap);
+ status = imap_writeline (f_imap,
+ "g%d FETCH %d BODY.PEEK[%s]<%d.%d>\r\n",
+ f_imap->seq++, msg_imap->num,
+ (section) ? section: "",
+ offset + msg_imap->body_lines, buflen);
+ if (section)
+ free (section);
+ }
+ else
+ status = imap_writeline (f_imap,
+ "g%d FETCH %d BODY.PEEK[TEXT]<%d.%d>\r\n",
+ f_imap->seq++, msg_imap->num,
+ offset + msg_imap->body_lines, buflen);
+ CHECK_ERROR (f_imap, status);
+ MAILBOX_DEBUG0 (m_imap->mailbox, MU_DEBUG_PROT, f_imap->buffer);
+ f_imap->state = IMAP_FETCH;
+
+ }
+ status = fetch_operation (f_imap, msg_imap, buffer, buflen, plen);
+ if (oldbuf)
+ oldbuf[0] = buffer[0];
+ return status;
+}
+
+static int
+imap_body_fd (stream_t stream, int *pfd)
+{
+ body_t body = stream_get_owner (stream);
+ message_t msg = body_get_owner (body);
+ msg_imap_t msg_imap = message_get_owner (msg);
+ return imap_get_fd (msg_imap, pfd);
+}
+
+
+static int
+imap_get_fd (msg_imap_t msg_imap, int *pfd)
+{
+ if ( msg_imap
+ && msg_imap->m_imap
+ && msg_imap->m_imap->f_imap
+ && msg_imap->m_imap->f_imap->folder)
+ return stream_get_fd (msg_imap->m_imap->f_imap->folder->stream, pfd);
+ return EINVAL;
+}
+
+/* Since so many operations are fetch, we regoup this into one function. */
+static int
+fetch_operation (f_imap_t f_imap, msg_imap_t msg_imap, char *buffer,
+ size_t buflen, size_t *plen)
+{
+ int status = 0;
+
+ switch (f_imap->state)
+ {
+ case IMAP_FETCH:
+ status = imap_send (f_imap);
+ CHECK_EAGAIN (f_imap, status);
+ stream_truncate (f_imap->string.stream, 0);
+ f_imap->string.offset = 0;
+ f_imap->string.nleft = 0;
+ f_imap->string.type = IMAP_NO_STATE;
+ f_imap->string.msg_imap = msg_imap;
+ f_imap->state = IMAP_FETCH_ACK;
+
+ case IMAP_FETCH_ACK:
+ status = imap_parse (f_imap);
+ CHECK_EAGAIN (f_imap, status);
+ if (f_imap->selected)
+ MAILBOX_DEBUG0 (f_imap->selected->mailbox, MU_DEBUG_PROT,
+ f_imap->buffer);
+
+ default:
+ break;
+ }
+
+ f_imap->state = IMAP_NO_STATE;
+
+ /* The server may have timeout any case connection is gone away. */
+ if (status == 0 && f_imap->isopen == 0 && f_imap->string.offset == 0)
+ status = EBADF;
+
+ if (buffer)
+ stream_read (f_imap->string.stream, buffer, buflen, 0, plen);
+ else if (plen)
+ *plen = 0;
+ stream_truncate (f_imap->string.stream, 0);
+ f_imap->string.offset = 0;
+ f_imap->string.nleft = 0;
+ f_imap->string.type = IMAP_NO_STATE;
+ f_imap->string.msg_imap = NULL;
+ return status;
+}
+
+/* Decide whether the message came from the same folder as the mailbox. */
+static int
+is_same_folder (mailbox_t mailbox, message_t msg)
+{
+ mailbox_t mbox = NULL;
+ message_get_mailbox (msg, &mbox);
+ return (mbox != NULL && mbox->url != NULL
+ && url_is_same_scheme (mbox->url, mailbox->url)
+ && url_is_same_host (mbox->url, mailbox->url)
+ && url_is_same_port (mbox->url, mailbox->url));
+}
+
+/* Convert flag attribute to IMAP String attributes. */
+static int
+flags_to_string (char **pbuf, int flag)
+{
+ char *abuf = *pbuf;
+ if (flag & MU_ATTRIBUTE_DELETED)
+ {
+ char *tmp = realloc (abuf, strlen (abuf) + strlen ("\\Deleted") + 2);
+ if (tmp == NULL)
+ {
+ free (abuf);
+ return ENOMEM;
+ }
+ abuf = tmp;
+ if (*abuf)
+ strcat (abuf, " ");
+ strcat (abuf, "\\Deleted");
+ }
+ if (flag & MU_ATTRIBUTE_READ)
+ {
+ char *tmp = realloc (abuf, strlen (abuf) + strlen ("\\Seen") + 2);
+ if (tmp == NULL)
+ {
+ free (abuf);
+ return ENOMEM;
+ }
+ abuf = tmp;
+ if (*abuf)
+ strcat (abuf, " ");
+ strcat (abuf, "\\Seen");
+ }
+ if (flag & MU_ATTRIBUTE_ANSWERED)
+ {
+ char *tmp = realloc (abuf, strlen (abuf) + strlen ("\\Answered") + 2);
+ if (tmp == NULL)
+ {
+ free (abuf);
+ return ENOMEM;
+ }
+ abuf = tmp;
+ if (*abuf)
+ strcat (abuf, " ");
+ strcat (abuf, "\\Answered");
+ }
+ if (flag & MU_ATTRIBUTE_DRAFT)
+ {
+ char *tmp = realloc (abuf, strlen (abuf) + strlen ("\\Draft") + 2);
+ if (tmp == NULL)
+ {
+ free (abuf);
+ return ENOMEM;
+ }
+ abuf = tmp;
+ if (*abuf)
+ strcat (abuf, " ");
+ strcat (abuf, "\\Draft");
+ }
+ if (flag & MU_ATTRIBUTE_FLAGGED)
+ {
+ char *tmp = realloc (abuf, strlen (abuf) + strlen ("\\Flagged") + 2);
+ if (tmp == NULL)
+ {
+ free (abuf);
+ return ENOMEM;
+ }
+ abuf = tmp;
+ if (*abuf)
+ strcat (abuf, " ");
+ strcat (abuf, "\\Flagged");
+ }
+ *pbuf = abuf;
+ return 0;
+}
+
+/* Convert a suite of number to IMAP message number. */
+static int
+add_number (char **pset, size_t start, size_t end)
+{
+ char buf[128];
+ char *set;
+ char *tmp;
+ size_t set_len = 0;
+
+ if (pset == NULL)
+ return 0;
+
+ set = *pset;
+
+ if (set)
+ set_len = strlen (set);
+
+ /* We had a previous seqence. */
+ if (start == 0)
+ *buf = '\0';
+ else if (start != end)
+ snprintf (buf, sizeof buf, "%lu:%lu",
+ (unsigned long) start,
+ (unsigned long) end);
+ else
+ snprintf (buf, sizeof buf, "%lu", (unsigned long) start);
+
+ if (set_len)
+ tmp = realloc (set, set_len + strlen (buf) + 2 /* null and comma */);
+ else
+ tmp = calloc (strlen (buf) + 1, 1);
+
+ if (tmp == NULL)
+ {
+ free (set);
+ return ENOMEM;
+ }
+ set = tmp;
+
+ /* If we had something add a comma separator. */
+ if (set_len)
+ strcat (set, ",");
+ strcat (set, buf);
+
+ *pset = set;
+ return 0;
+}
+
+static int
+delete_to_string (m_imap_t m_imap, char **pset)
+{
+ int status;
+ size_t i, prev = 0, is_range = 0;
+ size_t start = 0, cur = 0;
+ char *set = NULL;
+
+ /* Reformat the number for IMAP. */
+ for (i = 0; i < m_imap->imessages_count; ++i)
+ {
+ if (m_imap->imessages[i]
+ && (m_imap->imessages[i]->flags & MU_ATTRIBUTE_DELETED))
+ {
+ cur = m_imap->imessages[i]->num;
+ /* The first number. */
+ if (start == 0)
+ {
+ start = prev = cur;
+ }
+ /* Is it a sequence? */
+ else if ((prev + 1) == cur)
+ {
+ prev = cur;
+ is_range = 1;
+ }
+ continue;
+ }
+
+ if (start)
+ {
+ status = add_number (&set, start, cur);
+ if (status != 0)
+ return status;
+ start = 0;
+ prev = 0;
+ cur = 0;
+ is_range = 0;
+ }
+ } /* for () */
+
+ status = add_number (&set, start, cur);
+ if (status != 0)
+ return status;
+ *pset = set;
+ return 0;
+}
+
+#endif
diff --git a/mailbox/imap/url.c b/mailbox/imap/url.c
new file mode 100644
index 000000000..65ceb9af9
--- /dev/null
+++ b/mailbox/imap/url.c
@@ -0,0 +1,87 @@
+/* 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 */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifdef ENABLE_IMAP
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+
+#include <registrar0.h>
+#include <url0.h>
+
+static void url_imap_destroy (url_t url);
+
+static void
+url_imap_destroy (url_t url)
+{
+ (void)url;
+}
+
+/*
+ IMAP URL:
+ imap://[<user>[;AUTH=<auth>]@]<host>[/<mailbox>]
+ else
+ imap://[<user>[:<pass>]@]<host>[/<mailbox>]
+*/
+
+int
+_url_imap_init (url_t url)
+{
+ int status = 0;
+
+ url->_destroy = url_imap_destroy;
+
+ status = url_parse (url);
+
+ if (status)
+ return status;
+
+ if(!url->host || url->query)
+ return EINVAL;
+
+ /* is it pop? */
+ if (strcmp ("imap", url->scheme) != 0)
+ return EINVAL;
+
+ /* fill in default port, if necesary */
+ if (url->port == 0)
+ url->port = MU_IMAP_PORT;
+
+ /* fill in default auth, if necessary */
+ if (!url->auth)
+ {
+ url->auth = malloc (1 + 1);
+ if (!url->auth)
+ return ENOMEM;
+
+ url->auth[0] = '*';
+ url->auth[1] = '\0';
+ }
+
+ return status;
+}
+
+#endif
diff --git a/mailbox/maildir/mbox.c b/mailbox/maildir/mbox.c
new file mode 100644
index 000000000..a3ca2bbde
--- /dev/null
+++ b/mailbox/maildir/mbox.c
@@ -0,0 +1,224 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 1999, 2000, 2001, 2002, 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 Jeff Bailey based on mbox by Alain Magloire */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <signal.h>
+#include <time.h>
+#include <string.h>
+#include <ctype.h>
+#include <limits.h>
+#include <errno.h>
+
+#ifdef WITH_PTHREAD
+# ifdef HAVE_PTHREAD_H
+# define _XOPEN_SOURCE 500
+# include <pthread.h>
+# endif
+#endif
+
+#ifdef HAVE_ALLOCA_H
+# include <alloca.h>
+#endif
+
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+
+#include <mailbox0.h>
+#include <registrar0.h>
+
+#include <mailutils/address.h>
+#include <mailutils/attribute.h>
+#include <mailutils/body.h>
+#include <mailutils/debug.h>
+#include <mailutils/envelope.h>
+#include <mailutils/errno.h>
+#include <mailutils/error.h>
+#include <mailutils/header.h>
+#include <mailutils/locker.h>
+#include <mailutils/message.h>
+#include <mailutils/mutil.h>
+#include <mailutils/observer.h>
+#include <mailutils/property.h>
+#include <mailutils/stream.h>
+#include <mailutils/url.h>
+
+/* Mailbox concrete implementation. */
+static int maildir_open __P ((mailbox_t, int));
+static int maildir_close __P ((mailbox_t));
+static void maildir_destroy __P ((mailbox_t));
+static int maildir_get_message __P ((mailbox_t, size_t, message_t *));
+/* static int maildir_get_message_by_uid __P ((mailbox_t, size_t, message_t *)); */
+static int maildir_append_message __P ((mailbox_t, message_t));
+static int maildir_messages_count __P ((mailbox_t, size_t *));
+static int maildir_messages_recent __P ((mailbox_t, size_t *));
+static int maildir_message_unseen __P ((mailbox_t, size_t *));
+static int maildir_expunge __P ((mailbox_t));
+static int maildir_save_attributes __P ((mailbox_t));
+static int maildir_uidvalidity __P ((mailbox_t, unsigned long *));
+static int maildir_uidnext __P ((mailbox_t, size_t *));
+static int maildir_scan __P ((mailbox_t, size_t, size_t *));
+static int maildir_is_updated __P ((mailbox_t));
+static int maildir_get_size __P ((mailbox_t, off_t *));
+
+int
+_mailbox_maildir_init (mailbox_t mailbox)
+{
+
+ if (mailbox == NULL)
+ return EINVAL;
+
+ /* Overloading the defaults. */
+ mailbox->_destroy = maildir_destroy;
+
+ mailbox->_open = maildir_open;
+ mailbox->_close = maildir_close;
+
+ /* Overloading of the entire mailbox object methods. */
+ mailbox->_get_message = maildir_get_message;
+ mailbox->_append_message = maildir_append_message;
+ mailbox->_messages_count = maildir_messages_count;
+ mailbox->_messages_recent = maildir_messages_recent;
+ mailbox->_message_unseen = maildir_message_unseen;
+ mailbox->_expunge = maildir_expunge;
+ mailbox->_save_attributes = maildir_save_attributes;
+ mailbox->_uidvalidity = maildir_uidvalidity;
+ mailbox->_uidnext = maildir_uidnext;
+
+ mailbox->_scan = maildir_scan;
+ mailbox->_is_updated = maildir_is_updated;
+
+ mailbox->_get_size = maildir_get_size;
+
+ return 0; /* okdoke */
+}
+
+/* Destruct maildir setup */
+static void
+maildir_destroy (mailbox_t mailbox)
+{
+ return;
+}
+
+/* Open the file. For MU_STREAM_READ, the code tries mmap() first and fall
+ back to normal file. */
+static int
+maildir_open (mailbox_t mailbox, int flags)
+{
+ return -1;
+}
+
+static int
+maildir_close (mailbox_t mailbox)
+{
+ return -1;
+}
+
+/* Cover function that call the real thing, maildir_scan(), with
+ notification set. */
+static int
+maildir_scan (mailbox_t mailbox, size_t msgno, size_t *pcount)
+{
+ 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
+maildir_is_updated (mailbox_t mailbox)
+{
+ return -1;
+}
+
+static int
+maildir_expunge (mailbox_t mailbox)
+{
+ return -1;
+}
+
+static int
+maildir_get_size (mailbox_t mailbox, off_t *psize)
+{
+ return -1;
+}
+
+static int
+maildir_save_attributes (mailbox_t mailbox)
+{
+ return -1;
+}
+
+static int
+maildir_get_message (mailbox_t mailbox, size_t msgno, message_t *pmsg)
+{
+ return -1;
+}
+
+static int
+maildir_append_message (mailbox_t mailbox, message_t msg)
+{
+ return -1;
+}
+
+static int
+maildir_messages_count (mailbox_t mailbox, size_t *pcount)
+{
+ return -1;
+}
+
+/* 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
+maildir_messages_recent (mailbox_t mailbox, size_t *pcount)
+{
+ return -1;
+}
+
+/* An "unseen" message is the one that has not been read yet */
+static int
+maildir_message_unseen (mailbox_t mailbox, size_t *pmsgno)
+{
+ return -1;
+}
+
+static int
+maildir_uidvalidity (mailbox_t mailbox, unsigned long *puidvalidity)
+{
+ return -1;
+}
+
+static int
+maildir_uidnext (mailbox_t mailbox, size_t *puidnext)
+{
+ return -1;
+}
+
diff --git a/mailbox/mbox/folder.c b/mailbox/mbox/folder.c
new file mode 100644
index 000000000..6568ab36a
--- /dev/null
+++ b/mailbox/mbox/folder.c
@@ -0,0 +1,427 @@
+/* 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 */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <errno.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <glob.h>
+#include <fnmatch.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <folder0.h>
+#include <registrar0.h>
+
+#include <mailutils/auth.h>
+#include <mailutils/url.h>
+#include <mailutils/stream.h>
+
+/* We export url parsing and the initialisation of
+ the mailbox, via the register entry/record. */
+
+static struct _record _mbox_record =
+{
+ MU_MBOX_SCHEME,
+ _url_mbox_init, /* Mailbox init. */
+ _mailbox_mbox_init, /* Mailbox init. */
+ NULL, /* Mailer init. */
+ _folder_mbox_init, /* Folder init. */
+ NULL, /* No need for an back pointer. */
+ NULL, /* _is_scheme method. */
+ NULL, /* _get_url method. */
+ NULL, /* _get_mailbox method. */
+ NULL, /* _get_mailer method. */
+ NULL /* _get_folder method. */
+};
+record_t mbox_record = &_mbox_record;
+
+static struct _record _file_record =
+{
+ MU_FILE_SCHEME,
+ _url_file_init, /* Mailbox init. */
+ _mailbox_file_init, /* Mailbox init. */
+ NULL, /* Mailer init. */
+ _folder_mbox_init, /* Folder init. */
+ NULL, /* No need for an owner. */
+ NULL, /* _is_scheme method. */
+ NULL, /* _get_url method. */
+ NULL, /* _get_mailbox method. */
+ NULL, /* _get_mailer method. */
+ NULL /* _get_folder method. */
+};
+record_t file_record = &_file_record;
+
+static struct _record _path_record =
+{
+ MU_PATH_SCHEME,
+ _url_path_init, /* Mailbox init. */
+ _mailbox_file_init, /* Mailbox init. */
+ NULL, /* Mailer init. */
+ _folder_mbox_init, /* Folder init. */
+ NULL, /* No need for an owner. */
+ NULL, /* is_scheme method. */
+ NULL, /* get_url method. */
+ NULL, /* get_mailbox method. */
+ NULL, /* get_mailer method. */
+ NULL /* get_folder method. */
+};
+record_t path_record = &_path_record;
+
+/* lsub/subscribe/unsubscribe are not needed. */
+static void folder_mbox_destroy __P ((folder_t));
+static int folder_mbox_open __P ((folder_t, int));
+static int folder_mbox_close __P ((folder_t));
+static int folder_mbox_delete __P ((folder_t, const char *));
+static int folder_mbox_rename __P ((folder_t , const char *,
+ const char *));
+static int folder_mbox_list __P ((folder_t, const char *, const char *,
+ struct folder_list *));
+static int folder_mbox_subscribe __P ((folder_t, const char *));
+static int folder_mbox_unsubscribe __P ((folder_t, const char *));
+static int folder_mbox_lsub __P ((folder_t, const char *, const char *,
+ struct folder_list *));
+
+
+static char *get_pathname __P ((const char *, const char *));
+
+static int folder_mbox_get_authority __P ((folder_t folder,
+ authority_t * pauth));
+
+struct _fmbox
+{
+ char *dirname;
+ char **subscribe;
+ size_t sublen;
+};
+typedef struct _fmbox *fmbox_t;
+
+
+int
+_folder_mbox_init (folder_t folder)
+{
+ fmbox_t dfolder;
+ size_t name_len = 0;
+ int status = 0;
+
+ /* We create an authority so the API is uniform across the mailbox
+ types. */
+ status = folder_mbox_get_authority (folder, NULL);
+ if (status != 0)
+ return status;
+
+ dfolder = folder->data = calloc (1, sizeof (*dfolder));
+ if (dfolder == NULL)
+ return ENOMEM;
+
+ url_get_path (folder->url, NULL, 0, &name_len);
+ dfolder->dirname = calloc (name_len + 1, sizeof (char));
+ if (dfolder->dirname == NULL)
+ {
+ free (dfolder);
+ folder->data = NULL;
+ return ENOMEM;
+ }
+ url_get_path (folder->url, dfolder->dirname, name_len + 1, NULL);
+
+ folder->_destroy = folder_mbox_destroy;
+
+ folder->_open = folder_mbox_open;
+ folder->_close = folder_mbox_close;
+
+ folder->_list = folder_mbox_list;
+ folder->_lsub = folder_mbox_lsub;
+ folder->_subscribe = folder_mbox_subscribe;
+ folder->_unsubscribe = folder_mbox_unsubscribe;
+ folder->_delete = folder_mbox_delete;
+ folder->_rename = folder_mbox_rename;
+ return 0;
+}
+
+void
+folder_mbox_destroy (folder_t folder)
+{
+ if (folder->data)
+ {
+ fmbox_t fmbox = folder->data;
+ if (fmbox->dirname)
+ free (fmbox->dirname);
+ if (fmbox->subscribe)
+ free (fmbox->subscribe);
+ free (folder->data);
+ folder->data = NULL;
+ }
+}
+
+/* Noop. */
+static int
+folder_mbox_open (folder_t folder, int flags)
+{
+ fmbox_t fmbox = folder->data;
+ if (flags & MU_STREAM_CREAT)
+ {
+ return (mkdir (fmbox->dirname, S_IRWXU) == 0) ? 0 : errno;
+ }
+ (void)(flags);
+ return 0;
+}
+
+/* Noop. */
+static int
+folder_mbox_close (folder_t folder)
+{
+ (void)(folder);
+ return 0;
+}
+
+static int
+folder_mbox_delete (folder_t folder, const char *filename)
+{
+ fmbox_t fmbox = folder->data;
+ if (filename)
+ {
+ int status = 0;
+ char *pathname = get_pathname (fmbox->dirname, filename);
+ if (pathname)
+ {
+ if (remove (pathname) != 0)
+ status = errno;
+ free (pathname);
+ }
+ else
+ status = ENOMEM;
+ return status;
+ }
+ return EINVAL;
+}
+
+static int
+folder_mbox_rename (folder_t folder, const char *oldpath, const char *newpath)
+{
+ fmbox_t fmbox = folder->data;
+ if (oldpath && newpath)
+ {
+ int status = 0;
+ char *pathold = get_pathname (fmbox->dirname, oldpath);
+ if (pathold)
+ {
+ char *pathnew = get_pathname (fmbox->dirname, newpath);
+ if (pathnew)
+ {
+ if (rename (pathold, pathnew) != 0)
+ status = errno;
+ free (pathnew);
+ }
+ else
+ status = ENOMEM;
+ free (pathold);
+ }
+ else
+ status = ENOMEM;
+ return status;
+ }
+ return EINVAL;
+}
+
+/* The listing is not recursif and we use glob() some expansion for us.
+ Unfortunately glob() does not expand the '~'. We also return
+ The full pathname so it can be use to create other folders. */
+static int
+folder_mbox_list (folder_t folder, const char *dirname, const char *pattern,
+ struct folder_list *pflist)
+{
+ fmbox_t fmbox = folder->data;
+ char *pathname = NULL;
+ int status;
+ size_t num = 0;
+ glob_t gl;
+
+ if (dirname == NULL || dirname[0] == '\0')
+ dirname = (const char *)fmbox->dirname;
+
+ pathname = get_pathname (dirname, pattern);
+ if (pathname)
+ {
+ memset(&gl, 0, sizeof(gl));
+ status = glob (pathname, 0, NULL, &gl);
+ free (pathname);
+ num = gl.gl_pathc;
+ }
+ else
+ status = ENOMEM;
+
+ /* Build the folder list from glob. */
+ if (status == 0)
+ {
+ if (pflist)
+ {
+ struct list_response **plist;
+ plist = calloc (num, sizeof (*plist));
+ if (plist)
+ {
+ size_t i;
+ struct stat stbuf;
+ for (i = 0; i < num; i++)
+ {
+ plist[i] = calloc (1, sizeof (**plist));
+ if (plist[i] == NULL
+ || (plist[i]->name = strdup (gl.gl_pathv[i])) == NULL)
+ {
+ num = i;
+ break;
+ }
+ if (stat (gl.gl_pathv[i], &stbuf) == 0)
+ {
+ if (S_ISDIR(stbuf.st_mode))
+ plist[i]->type = MU_FOLDER_ATTRIBUTE_DIRECTORY;
+ if (S_ISREG(stbuf.st_mode))
+ plist[i]->type = MU_FOLDER_ATTRIBUTE_FILE;
+ }
+ plist[i]->separator = '/';
+ }
+ }
+ pflist->element = plist;
+ pflist->num = num;
+ }
+ globfree (&gl);
+ }
+ else
+ {
+ status = (status == GLOB_NOSPACE) ? ENOMEM : EINVAL;
+ }
+ return status;
+}
+
+static int
+folder_mbox_lsub (folder_t folder, const char *ref, const char *name,
+ struct folder_list *pflist)
+{
+ fmbox_t fmbox = folder->data;
+ size_t j = 0;
+
+ if (pflist == NULL)
+ return EINVAL;
+
+ (void)ref;
+ if (name == NULL || *name == '\0')
+ name = "*";
+
+ if (fmbox->sublen > 0)
+ {
+ struct list_response **plist;
+ size_t i;
+ plist = calloc (fmbox->sublen, sizeof (*plist));
+ for (i = 0; i < fmbox->sublen; i++)
+ {
+ if (fmbox->subscribe[i]
+ && fnmatch (name, fmbox->subscribe[i], 0) == 0)
+ {
+ plist[i] = calloc (1, sizeof (**plist));
+ if (plist[i] == NULL
+ || (plist[i]->name = strdup (fmbox->subscribe[i])) == NULL)
+ break;
+ plist[i]->type = MU_FOLDER_ATTRIBUTE_FILE;
+ plist[i]->separator = '/';
+ j++;
+ }
+ }
+ pflist->element = plist;
+ }
+ pflist->num = j;
+ return 0;
+}
+
+static int
+folder_mbox_subscribe (folder_t folder, const char *name)
+{
+ fmbox_t fmbox = folder->data;
+ char **tmp;
+ size_t i;
+ for (i = 0; i < fmbox->sublen; i++)
+ {
+ if (fmbox->subscribe[i] && strcmp (fmbox->subscribe[i], name) == 0)
+ return 0;
+ }
+ tmp = realloc (fmbox->subscribe, (fmbox->sublen + 1) * sizeof (*tmp));
+ if (tmp == NULL)
+ return ENOMEM;
+ fmbox->subscribe = tmp;
+ fmbox->subscribe[fmbox->sublen] = strdup (name);
+ if (fmbox->subscribe[fmbox->sublen] == NULL)
+ return ENOMEM;
+ fmbox->sublen++;
+ return 0;
+}
+
+static int
+folder_mbox_unsubscribe (folder_t folder, const char *name)
+{
+ fmbox_t fmbox = folder->data;
+ size_t i;
+ for (i = 0; i < fmbox->sublen; i++)
+ {
+ if (fmbox->subscribe[i] && strcmp (fmbox->subscribe[i], name) == 0)
+ {
+ free (fmbox->subscribe[i]);
+ fmbox->subscribe[i] = NULL;
+ return 0;
+ }
+ }
+ return ENOENT;
+}
+
+static char *
+get_pathname (const char *dirname, const char *basename)
+{
+ char *pathname = NULL;
+ /* null basename gives dirname. */
+ if (basename == NULL)
+ pathname = (dirname) ? strdup (dirname) : strdup (".");
+ /* Absolute. */
+ else if (basename[0] == '/')
+ pathname = strdup (basename);
+ /* Relative. */
+ else
+ {
+ size_t len = strlen (basename);
+ pathname = calloc (strlen (dirname) + len + 2, sizeof (char));
+ if (pathname)
+ sprintf (pathname, "%s/%s", dirname, basename);
+ }
+ return pathname;
+}
+
+static int
+folder_mbox_get_authority (folder_t folder, authority_t *pauth)
+{
+ int status = 0;
+ if (folder->authority == NULL)
+ {
+ status = authority_create_null (&folder->authority, folder);
+ }
+ if (!status && pauth)
+ *pauth = folder->authority;
+ return status;
+}
+
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)
+ {
+ 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 || 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_fd (stream, mbox_get_body_fd, 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';
+
+ 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);
+ stream_write (mailbox->stream, buffer, n, *psize, &n);
+ *psize += n;
+
+ /* Add the newline, the above may be truncated. */
+ stream_write (mailbox->stream, &nl , 1, *psize, &n);
+ *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;
+ }
+ 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 != 0)
+ 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);
+ stream_write (mailbox->stream, buffer, n, *psize, &n);
+ *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 != 0)
+ break;
+ *psize += n;
+ }
+
+ /* New line separator of the Header. */
+ status = stream_write (mailbox->stream, &nl , 1, *psize, &n);
+ if (status != 0)
+ 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 != 0)
+ break;
+ *psize += n;
+ }
+ while (nread > 0);
+ mud->off = 0;
+ n = 0;
+ stream_write (mailbox->stream, &nl, 1, *psize, &n);
+ *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_flush (mailbox->stream);
+ return status;
+ }
+ stream_write (mailbox->stream, buffer, nread, *psize, &n);
+ mud->off += nread;
+ *psize += n;
+ }
+ while (nread > 0);
+ n = 0;
+ stream_write (mailbox->stream, &nl, 1, *psize, &n);
+ *psize += n;
+ }
+
+ default:
+ break;
+ }
+ } /* is_expunging */
+ mud->state = MBOX_NO_STATE;
+ 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
diff --git a/mailbox/mbox/mboxscan.c b/mailbox/mbox/mboxscan.c
new file mode 100644
index 000000000..0d136e156
--- /dev/null
+++ b/mailbox/mbox/mboxscan.c
@@ -0,0 +1,856 @@
+/* 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 */
+
+/* Mailbox Parsing. */
+
+/* Credits to the c-client and its Authors
+ * The notorius c-client VALID() macro, was written by Mark Crispin.
+ */
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifdef WITH_PTHREAD
+# ifdef HAVE_PTHREAD_H
+# define _XOPEN_SOURCE 500
+# include <pthread.h>
+# endif
+#endif
+
+#include <stdlib.h>
+#include <mbox0.h>
+
+/* Parsing.
+ The approach is to detect the "From " as start of a new message, give the
+ position of the header and scan until "\n" then set header_end, set body
+ position, scan until we it another "From " and set body_end.
+ ************************************
+ This is a classic case of premature optimisation being the root of all
+ Evil(Donald E. Knuth). But I'm under "pressure" ;-) to come with
+ something "faster". I think it's wastefull * to spend time to gain a few
+ seconds on 30Megs mailboxes ... but then again ... in computer time, 60
+ seconds, is eternity. If they use the event notification stuff to get
+ some headers/messages early ... it's like pissing in the wind(sorry don't
+ have the english equivalent). The worst is progress_bar it should be ...
+ &*($^ nuke. For the events, we have to remove the *.LCK file, release the
+ locks, flush the stream save the pointers etc ... hurry and wait...
+ I this point I'm pretty much ranting. */
+
+
+/* From the C-Client, part of pine */
+/* You are not expected to understand this macro, but read the next page if
+ * you are not faint of heart.
+ *
+ * Known formats to the VALID macro are:
+ * From user Wed Dec 2 05:53 1992
+ * BSD From user Wed Dec 2 05:53:22 1992
+ * SysV From user Wed Dec 2 05:53 PST 1992
+ * rn From user Wed Dec 2 05:53:22 PST 1992
+ * From user Wed Dec 2 05:53 -0700 1992
+ * From user Wed Dec 2 05:53:22 -0700 1992
+ * From user Wed Dec 2 05:53 1992 PST
+ * From user Wed Dec 2 05:53:22 1992 PST
+ * From user Wed Dec 2 05:53 1992 -0700
+ * Solaris From user Wed Dec 2 05:53:22 1992 -0700
+ *
+ * Plus all of the above with `` remote from xxx'' after it. Thank you very
+ * much, smail and Solaris, for making my life considerably more complicated.
+ */
+/*
+ * What? You want to understand the VALID macro anyway? Alright, since you
+ * insist. Actually, it isn't really all that difficult, provided that you
+ * take it step by step.
+ *
+ * Line 1 Initializes the return ti value to failure (0);
+ * Lines 2-3 Validates that the 1st-5th characters are ``From ''.
+ * Lines 4-6 Validates that there is an end of line and points x at it.
+ * Lines 7-14 First checks to see if the line is at least 41 characters long
+.
+ * If so, it scans backwards to find the rightmost space. From
+ * that point, it scans backwards to see if the string matches
+ * `` remote from''. If so, it sets x to point to the space at
+ * the start of the string.
+ * Line 15 Makes sure that there are at least 27 characters in the line.
+ * Lines 16-21 Checks if the date/time ends with the year (there is a space
+ * five characters back). If there is a colon three characters
+ * further back, there is no timezone field, so zn is set to 0
+ * and ti is set in front of the year. Otherwise, there must
+ * either to be a space four characters back for a three-letter
+ * timezone, or a space six characters back followed by a + or -
+ * for a numeric timezone; in either case, zn and ti become the
+ * offset of the space immediately before it.
+ * Lines 22-24 Are the failure case for line 14. If there is a space four
+ * characters back, it is a three-letter timezone; there must be
+a
+ * space for the year nine characters back. zn is the zone
+ * offset; ti is the offset of the space.
+ * Lines 25-28 Are the failure case for line 20. If there is a space six
+ * characters back, it is a numeric timezone; there must be a
+ * space eleven characters back and a + or - five characters back
+.
+ * zn is the zone offset; ti is the offset of the space.
+ * Line 29-32 If ti is valid, make sure that the string before ti is of the
+ * form www mmm dd hh:mm or www mmm dd hh:mm:ss, otherwise
+ * invalidate ti. There must be a colon three characters back
+ * and a space six or nine characters back (depending upon
+ * whether or not the character six characters back is a colon).
+ * There must be a space three characters further back (in front
+ * of the day), one seven characters back (in front of the month)
+,
+ * and one eleven characters back (in front of the day of week).
+ * ti is set to be the offset of the space before the time.
+ *
+ * Why a macro? It gets invoked a *lot* in a tight loop. On some of the
+ * newer pipelined machines it is faster being open-coded than it would be if
+ * subroutines are called.
+ *
+ * Why does it scan backwards from the end of the line, instead of doing the
+ * much easier forward scan? There is no deterministic way to parse the
+ * ``user'' field, because it may contain unquoted spaces! Yes, I tested it t
+o
+ * see if unquoted spaces were possible. They are, and I've encountered enoug
+h
+ * evil mail to be totally unwilling to trust that ``it will never happen''.
+ */
+#define VALID(s,x,ti,zn) { \
+ ti = 0; \
+ if ((*s == 'F') && (s[1] == 'r') && (s[2] == 'o') && (s[3] == 'm') && \
+ (s[4] == ' ')) { \
+ for (x = s + 5; *x && *x != '\n'; x++); \
+ if (x) { \
+ if (x - s >= 41) { \
+ for (zn = -1; x[zn] != ' '; zn--); \
+ if ((x[zn-1] == 'm') && (x[zn-2] == 'o') && (x[zn-3] == 'r') && \
+ (x[zn-4] == 'f') && (x[zn-5] == ' ') && (x[zn-6] == 'e') && \
+ (x[zn-7] == 't') && (x[zn-8] == 'o') && (x[zn-9] == 'm') && \
+ (x[zn-10] == 'e') && (x[zn-11] == 'r') && (x[zn-12] == ' '))\
+ x += zn - 12; \
+ } \
+ if (x - s >= 27) { \
+ if (x[-5] == ' ') { \
+ if (x[-8] == ':') zn = 0,ti = -5; \
+ else if (x[-9] == ' ') ti = zn = -9; \
+ else if ((x[-11] == ' ') && ((x[-10]=='+') || (x[-10]=='-'))) \
+ ti = zn = -11; \
+ } \
+ else if (x[-4] == ' ') { \
+ if (x[-9] == ' ') zn = -4,ti = -9; \
+ } \
+ else if (x[-6] == ' ') { \
+ if ((x[-11] == ' ') && ((x[-5] == '+') || (x[-5] == '-'))) \
+ zn = -6,ti = -11; \
+ } \
+ if (ti && !((x[ti - 3] == ':') && \
+ (x[ti -= ((x[ti - 6] == ':') ? 9 : 6)] == ' ') && \
+ (x[ti - 3] == ' ') && (x[ti - 7] == ' ') && \
+ (x[ti - 11] == ' '))) ti = 0; \
+ } \
+ } \
+ } \
+}
+
+#define ATTRIBUTE_SET(buf,mum,c0,c1,type) \
+do \
+{ \
+ char *s; \
+ for (s = (buf) + 7; *s; s++) \
+ { \
+ if (*s == c0 || *s == c1) \
+ { \
+ (mum)->attr_flags |= (type); \
+ break; \
+ } \
+ } \
+} while (0)
+
+#define ISBCC(buf) (\
+(buf[0] == 'B' || buf[0] == 'b') \
+ && (buf[1] == 'C' || buf[1] == 'c') \
+ && (buf[2] == 'C' || buf[2] == 'c') \
+ && (buf[3] == ':' || buf[3] == ' ' || buf[3] == '\t'))
+
+#define ISCC(buf) (\
+(buf[0] == 'C' || buf[0] == 'c') \
+ && (buf[1] == 'C' || buf[1] == 'c') \
+ && (buf[2] == ':' || buf[2] == ' ' || buf[2] == '\t'))
+
+#define ISCONTENT_LANGUAGE(buf) (\
+(buf[0] == 'C' || buf[0] == 'c') \
+ && (buf[1] == 'O' || buf[1] == 'o') \
+ && (buf[2] == 'N' || buf[2] == 'n') \
+ && (buf[3] == 'T' || buf[3] == 't') \
+ && (buf[4] == 'E' || buf[4] == 'e') \
+ && (buf[5] == 'N' || buf[5] == 'n') \
+ && (buf[6] == 'T' || buf[6] == 't') \
+ && (buf[7] == '-') \
+ && (buf[8] == 'L' || buf[8] == 'l') \
+ && (buf[9] == 'A' || buf[9] == 'a') \
+ && (buf[10] == 'N' || buf[10] == 'n') \
+ && (buf[11] == 'G' || buf[11] == 'g') \
+ && (buf[12] == 'U' || buf[12] == 'u') \
+ && (buf[13] == 'A' || buf[13] == 'a') \
+ && (buf[14] == 'G' || buf[14] == 'g') \
+ && (buf[15] == 'E' || buf[15] == 'e') \
+ && (buf[16] == ':' || buf[16] == ' ' || buf[16] == '\t'))
+
+#define ISCONTENT_TRANSFER_ENCODING(buf) (\
+(buf[0] == 'C' || buf[0] == 'c') \
+ && (buf[1] == 'O' || buf[1] == 'o') \
+ && (buf[2] == 'N' || buf[2] == 'n') \
+ && (buf[3] == 'T' || buf[3] == 't') \
+ && (buf[4] == 'E' || buf[4] == 'e') \
+ && (buf[5] == 'N' || buf[5] == 'n') \
+ && (buf[6] == 'T' || buf[6] == 't') \
+ && (buf[7] == '-') \
+ && (buf[8] == 'T' || buf[8] == 't') \
+ && (buf[9] == 'R' || buf[9] == 'r') \
+ && (buf[10] == 'A' || buf[10] == 'a') \
+ && (buf[11] == 'N' || buf[11] == 'n') \
+ && (buf[12] == 'S' || buf[12] == 's') \
+ && (buf[13] == 'F' || buf[13] == 'f') \
+ && (buf[14] == 'E' || buf[14] == 'e') \
+ && (buf[15] == 'R' || buf[15] == 'r') \
+ && (buf[16] == '-') \
+ && (buf[17] == 'E' || buf[17] == 'e') \
+ && (buf[18] == 'N' || buf[18] == 'n') \
+ && (buf[19] == 'C' || buf[19] == 'c') \
+ && (buf[20] == 'O' || buf[20] == 'o') \
+ && (buf[21] == 'D' || buf[21] == 'd') \
+ && (buf[22] == 'I' || buf[22] == 'i') \
+ && (buf[23] == 'N' || buf[23] == 'n') \
+ && (buf[24] == 'G' || buf[24] == 'g') \
+ && (buf[25] == ':' || buf[25] == ' ' || buf[25] == '\t'))
+
+#define ISCONTENT_TYPE(buf) (\
+(buf[0] == 'C' || buf[0] == 'c') \
+ && (buf[1] == 'O' || buf[1] == 'o') \
+ && (buf[2] == 'N' || buf[2] == 'n') \
+ && (buf[3] == 'T' || buf[3] == 't') \
+ && (buf[4] == 'E' || buf[4] == 'e') \
+ && (buf[5] == 'N' || buf[5] == 'n') \
+ && (buf[6] == 'T' || buf[6] == 't') \
+ && (buf[7] == '-') \
+ && (buf[8] == 'T' || buf[8] == 't') \
+ && (buf[9] == 'Y' || buf[9] == 'y') \
+ && (buf[10] == 'P' || buf[10] == 'p') \
+ && (buf[11] == 'E' || buf[11] == 'e') \
+ && (buf[12] == ':' || buf[12] == ' ' || buf[12] == '\t'))
+
+#define ISDATE(buf) (\
+(buf[0] == 'D' || buf[0] == 'd') \
+ && (buf[1] == 'A' || buf[1] == 'a') \
+ && (buf[2] == 'T' || buf[2] == 't') \
+ && (buf[3] == 'E' || buf[3] == 'e') \
+ && (buf[4] == ':' || buf[4] == ' ' || buf[4] == '\t'))
+
+#define ISFROM(buf) (\
+(buf[0] == 'F' || buf[0] == 'f') \
+ && (buf[1] == 'R' || buf[1] == 'r') \
+ && (buf[2] == 'O' || buf[2] == 'o') \
+ && (buf[3] == 'M' || buf[3] == 'm') \
+ && (buf[4] == ':' || buf[4] == ' ' || buf[4] == '\t'))
+
+#define ISIN_REPLY_TO(buf) (\
+(buf[0] == 'I' || buf[0] == 'i') \
+ && (buf[1] == 'N' || buf[1] == 'n') \
+ && (buf[2] == '-' || buf[2] == '-') \
+ && (buf[3] == 'R' || buf[3] == 'r') \
+ && (buf[4] == 'E' || buf[4] == 'e') \
+ && (buf[5] == 'P' || buf[5] == 'p') \
+ && (buf[6] == 'L' || buf[6] == 'l') \
+ && (buf[7] == 'Y' || buf[7] == 'y') \
+ && (buf[8] == '-') \
+ && (buf[9] == 'T' || buf[9] == 't') \
+ && (buf[10] == 'O' || buf[10] == 'o') \
+ && (buf[11] == ':' || buf[11] == ' ' || buf[11] == '\t'))
+
+#define ISMESSAGE_ID(buf) (\
+(buf[0] == 'M' || buf[0] == 'm') \
+ && (buf[1] == 'E' || buf[1] == 'e') \
+ && (buf[2] == 'S' || buf[2] == 's') \
+ && (buf[3] == 'S' || buf[3] == 's') \
+ && (buf[4] == 'A' || buf[4] == 'a') \
+ && (buf[5] == 'G' || buf[5] == 'g') \
+ && (buf[6] == 'E' || buf[6] == 'e') \
+ && (buf[7] == '-') \
+ && (buf[8] == 'I' || buf[8] == 'i') \
+ && (buf[9] == 'D' || buf[9] == 'd') \
+ && (buf[10] == ':' || buf[10] == ' ' || buf[10] == '\t'))
+
+#define ISREFERENCE(buf) (\
+(buf[0] == 'R' || buf[0] == 'r') \
+ && (buf[1] == 'E' || buf[1] == 'e') \
+ && (buf[2] == 'F' || buf[2] == 'f') \
+ && (buf[3] == 'E' || buf[3] == 'e') \
+ && (buf[4] == 'R' || buf[4] == 'r') \
+ && (buf[5] == 'E' || buf[5] == 'e') \
+ && (buf[6] == 'n' || buf[6] == 'n') \
+ && (buf[7] == 'C' || buf[7] == 'c') \
+ && (buf[8] == 'E' || buf[8] == 'e') \
+ && (buf[9] == ':' || buf[9] == ' ' || buf[9] == '\t'))
+
+#define ISREPLY_TO(buf) (\
+(buf[0] == 'R' || buf[0] == 'r') \
+ && (buf[1] == 'E' || buf[1] == 'e') \
+ && (buf[2] == 'P' || buf[2] == 'p') \
+ && (buf[3] == 'L' || buf[3] == 'l') \
+ && (buf[4] == 'Y' || buf[4] == 'y') \
+ && (buf[5] == '-') \
+ && (buf[6] == 'T' || buf[6] == 't') \
+ && (buf[7] == 'O' || buf[7] == 'o') \
+ && (buf[8] == ':' || buf[8] == ' ' || buf[8] == '\t'))
+
+#define ISSENDER(buf) (\
+(buf[0] == 'S' || buf[0] == 's') \
+ && (buf[1] == 'E' || buf[1] == 'e') \
+ && (buf[2] == 'N' || buf[2] == 'n') \
+ && (buf[3] == 'D' || buf[3] == 'd') \
+ && (buf[4] == 'E' || buf[4] == 'e') \
+ && (buf[5] == 'R' || buf[5] == 'r') \
+ && (buf[6] == ':' || buf[6] == ' ' || buf[6] == '\t'))
+
+#define ISSTATUS(buf) (\
+(buf[0] == 'S' || buf[0] == 's') \
+ && (buf[1] == 'T' || buf[1] == 't') \
+ && (buf[2] == 'A' || buf[2] == 'a') \
+ && (buf[3] == 'T' || buf[3] == 't') \
+ && (buf[4] == 'U' || buf[4] == 'u') \
+ && (buf[5] == 'S' || buf[5] == 's') \
+ && (buf[6] == ':' || buf[6] == ' ' || buf[6] == '\t'))
+
+#define ISSUBJECT(buf) (\
+(buf[0] == 'S' || buf[0] == 's') \
+ && (buf[1] == 'U' || buf[1] == 'u') \
+ && (buf[2] == 'B' || buf[2] == 'b') \
+ && (buf[3] == 'J' || buf[3] == 'j') \
+ && (buf[4] == 'E' || buf[4] == 'e') \
+ && (buf[5] == 'C' || buf[5] == 'c') \
+ && (buf[6] == 'T' || buf[6] == 't') \
+ && (buf[7] == ':' || buf[7] == ' ' || buf[7] == '\t'))
+
+#define ISTO(buf) (\
+(buf[0] == 'T' || buf[0] == 't') \
+ && (buf[1] == 'O' || buf[1] == 'o') \
+ && (buf[2] == ':' || buf[2] == ' ' || buf[2] == '\t'))
+
+#define ISX_IMAPBASE(buf) (\
+(buf[0] == 'X' || buf[0] == 'x') \
+ && (buf[1] == '-') \
+ && (buf[2] == 'I' || buf[2] == 'i') \
+ && (buf[3] == 'M' || buf[3] == 'm') \
+ && (buf[4] == 'A' || buf[4] == 'a') \
+ && (buf[5] == 'P' || buf[5] == 'p') \
+ && (buf[6] == 'B' || buf[6] == 'b') \
+ && (buf[7] == 'A' || buf[7] == 'a') \
+ && (buf[8] == 'S' || buf[8] == 's') \
+ && (buf[9] == 'E' || buf[9] == 'e') \
+ && (buf[10] == ':' || buf[10] == ' ' || buf[10] == '\t'))
+
+#define ISX_UIDL(buf) (\
+(buf[0] == 'X' || buf[0] == 'x') \
+ && (buf[1] == '-') \
+ && (buf[2] == 'U' || buf[2] == 'u') \
+ && (buf[3] == 'I' || buf[3] == 'i') \
+ && (buf[4] == 'D' || buf[4] == 'd') \
+ && (buf[5] == 'L' || buf[5] == 'l') \
+ && (buf[6] == ':' || buf[6] == ' ' || buf[6] == '\t'))
+
+#define ISX_UID(buf) (\
+(buf[0] == 'X' || buf[0] == 'x') \
+ && (buf[1] == '-') \
+ && (buf[2] == 'U' || buf[2] == 'u') \
+ && (buf[3] == 'I' || buf[3] == 'i') \
+ && (buf[4] == 'D' || buf[4] == 'd') \
+ && (buf[5] == ':' || buf[5] == ' ' || buf[5] == '\t'))
+
+/* Skip prepend spaces. */
+#define SKIPSPACE(p) while (*p == ' ') p++
+
+#define ATOI(a,i) \
+do {\
+ SKIPSPACE(a); \
+ for (i = 0; *a >= '0' && *a <= '9'; a++) \
+ i = 10 * i + (*a - '0'); \
+} while (0)
+
+/* Save/concatenate the field-value in the fast header(fhd) field. */
+#define FAST_HEADER(field,buf,n) \
+do { \
+ int i = 0; \
+ char *s = field; \
+ char *p = buf; \
+ if (s) \
+ while (*s++) i++; \
+ else \
+ p = memchr (buf, ':', n); \
+ if (p) \
+ { \
+ int l; \
+ char *tmp; \
+ buf[n - 1] = '\0'; \
+ p++; \
+ if (!field) \
+ SKIPSPACE(p); \
+ l = n - (p - buf); \
+ tmp = realloc (field, (l + i + 1) * sizeof (char)); \
+ if (tmp) \
+ { \
+ field = tmp; \
+ memcpy (field + i, p, l); \
+ } \
+ } \
+} while (0)
+
+#define FAST_H_BCC(mum,save_field,buf,n) \
+FAST_HEADER(mum->fhdr[H_BCC],buf,n); \
+save_field = &(mum->fhdr[H_BCC])
+
+#define FAST_H_CC(mum,save_field,buf,n) \
+FAST_HEADER(mum->fhdr[H_CC],buf,n); \
+save_field = &(mum->fhdr[H_CC])
+
+#define FAST_H_CONTENT_LANGUAGE(mum,save_field,buf,n) \
+FAST_HEADER(mum->fhdr[H_CONTENT_LANGUAGE],buf,n); \
+save_field = &(mum->fhdr[H_CONTENT_LANGUAGE])
+
+#define FAST_H_CONTENT_TRANSFER_ENCODING(mum,save_field,buf,n) \
+FAST_HEADER(mum->fhdr[H_CONTENT_TRANSFER_ENCODING],buf,n); \
+save_field = &(mum->fhdr[H_CONTENT_TRANSFER_ENCODING])
+
+#define FAST_H_CONTENT_TYPE(mum,save_field,buf,n) \
+FAST_HEADER(mum->fhdr[H_CONTENT_TYPE],buf,n); \
+save_field = &(mum->fhdr[H_CONTENT_TYPE])
+
+#define FAST_H_DATE(mum,save_field,buf,n) \
+FAST_HEADER(mum->fhdr[H_DATE],buf,n); \
+save_field = &(mum->fhdr[H_DATE])
+
+#define FAST_H_FROM(mum,save_field,buf,n) \
+FAST_HEADER(mum->fhdr[H_FROM],buf,n); \
+save_field = &(mum->fhdr[H_FROM])
+
+#define FAST_H_IN_REPLY_TO(mum,save_field,buf,n) \
+FAST_HEADER(mum->fhdr[H_IN_REPLY_TO],buf,n); \
+save_field = &(mum->fhdr[H_IN_REPLY_TO])
+
+#define FAST_H_MESSAGE_ID(mum,save_field,buf,n) \
+FAST_HEADER(mum->fhdr[H_MESSAGE_ID],buf,n); \
+save_field = &(mum->fhdr[H_MESSAGE_ID])
+
+#define FAST_H_REFERENCE(mum,save_field,buf,n) \
+FAST_HEADER(mum->fhdr[H_REFERENCE],buf,n); \
+save_field = &(mum->fhdr[H_REFERENCE])
+
+#define FAST_H_REPLY_TO(mum,save_field,buf,n) \
+FAST_HEADER(mum->fhdr[H_REPLY_TO],buf,n); \
+save_field = &(mum->fhdr[H_REPLY_TO])
+
+#define FAST_H_SENDER(mum,save_field,buf,n) \
+FAST_HEADER(mum->fhdr[H_SENDER],buf,n); \
+save_field = &(mum->fhdr[H_SENDER])
+
+#define FAST_H_SUBJECT(mum,save_field,buf,n) \
+FAST_HEADER(mum->fhdr[H_SUBJECT],buf,n); \
+save_field = &(mum->fhdr[H_SUBJECT])
+
+#define FAST_H_TO(mum,save_field,buf,n) \
+FAST_HEADER(mum->fhdr[H_TO],buf,n); \
+save_field = &(mum->fhdr[H_TO])
+
+#define FAST_H_X_UIDL(mum,save_field,buf,n) \
+FAST_HEADER(mum->fhdr[H_X_UIDL],buf,n); \
+save_field = &(mum->fhdr[H_X_UIDL])
+
+/* Notifications ADD_MESG. */
+#define DISPATCH_ADD_MSG(mbox,mud) \
+do \
+{ \
+ int bailing = 0; \
+ monitor_unlock (mbox->monitor); \
+ if (mbox->observable) \
+ bailing = observable_notify (mbox->observable, MU_EVT_MESSAGE_ADD); \
+ if (bailing != 0) \
+ { \
+ if (pcount) \
+ *pcount = (mud)->messages_count; \
+ locker_unlock (mbox->locker); \
+ return EINTR; \
+ } \
+ monitor_wrlock (mbox->monitor); \
+} while (0);
+
+/* Notification MBX_PROGRESS
+ We do not want to fire up the progress notification every line, it will be
+ too expensive, so we do it arbitrarely every 10 000 Lines.
+ FIXME: maybe this should be configurable. */
+/* This is more tricky we can not leave the mum struct incomplete. So we
+ only tell them about the complete messages. */
+#define DISPATCH_PROGRESS(mbox,mud) \
+do \
+{ \
+ { \
+ int bailing = 0; \
+ monitor_unlock (mbox->monitor); \
+ mud->messages_count--; \
+ if (mbox->observable) \
+ bailing = observable_notify (mbox->observable, MU_EVT_MAILBOX_PROGRESS); \
+ if (bailing != 0) \
+ { \
+ if (pcount) \
+ *pcount = (mud)->messages_count; \
+ locker_unlock (mbox->locker); \
+ return EINTR; \
+ } \
+ mud->messages_count++; \
+ monitor_wrlock (mbox->monitor); \
+ } \
+} while (0)
+
+/* Allocate slots for the new messages. */
+/* size_t num = 2 * ((mud)->messages_count) + 10; */
+#define ALLOCATE_MSGS(mbox,mud) \
+do \
+{ \
+ if ((mud)->messages_count >= (mud)->umessages_count) \
+ { \
+ mbox_message_t *m; \
+ size_t num = ((mud)->umessages_count) + 1; \
+ m = realloc ((mud)->umessages, num * sizeof (*m)); \
+ if (m == NULL) \
+ { \
+ locker_unlock (mbox->locker); \
+ monitor_unlock (mbox->monitor); \
+ return ENOMEM; \
+ } \
+ (mud)->umessages = m; \
+ (mud)->umessages[num - 1] = calloc (1, sizeof (*(mum))); \
+ if ((mud)->umessages[num - 1] == NULL) \
+ { \
+ locker_unlock (mbox->locker); \
+ monitor_unlock (mbox->monitor); \
+ return ENOMEM; \
+ } \
+ (mud)->umessages_count = num; \
+ } \
+} while (0)
+
+int
+mbox_scan0 (mailbox_t mailbox, size_t msgno, size_t *pcount, int do_notif)
+{
+#define MSGLINELEN 1024
+ char buf[MSGLINELEN];
+ int inheader;
+ int inbody;
+ off_t total = 0;
+ mbox_data_t mud = mailbox->data;
+ mbox_message_t mum = NULL;
+ int status = 0;
+ size_t lines;
+ int newline;
+ size_t n = 0;
+ stream_t stream;
+ char **sfield = NULL;
+ size_t min_uid = 0;
+ int zn, isfrom = 0;
+ char *temp;
+
+ /* Sanity. */
+ if (mud == NULL)
+ return EINVAL;
+
+ /* Grab the lock. */
+ monitor_wrlock (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 *)mailbox);
+#endif
+
+ /* Save the timestamp and size. */
+ status = stream_size (mailbox->stream, &(mud->size));
+ if (status != 0)
+ {
+ monitor_unlock (mailbox->monitor);
+ return status;
+ }
+
+ if((status = locker_lock (mailbox->locker)))
+ {
+ monitor_unlock (mailbox->monitor);
+ return status;
+ }
+
+ /* Seek to the starting point. */
+ if (mud->umessages && msgno > 0 && mud->messages_count > 0
+ && msgno <= mud->messages_count)
+ {
+ mum = mud->umessages[msgno - 1];
+ if (mum)
+ total = mum->header_from;
+ mud->messages_count = msgno - 1;
+ }
+ else
+ mud->messages_count = 0;
+
+#if 0
+ {
+ size_t j, k;
+ for (j = 0; j < mud->umessages_count; j++)
+ {
+ mum = mud->umessages[j];
+ for (k = 0; k < HDRSIZE; k++)
+ if (mum->fhdr[k])
+ free (mum->fhdr[k]);
+ }
+ }
+#endif
+ newline = 1;
+ errno = lines = inheader = inbody = 0;
+
+ stream = mailbox->stream;
+ while ((status = stream_readline (mailbox->stream, buf, sizeof (buf),
+ total, &n)) == 0 && n != 0)
+ {
+ int nl;
+ total += n;
+
+ nl = (*buf == '\n') ? 1 : 0;
+ VALID(buf, temp, isfrom, zn);
+ isfrom = (isfrom) ? 1 : 0;
+
+ /* Which part of the message are we in ? */
+ inheader = isfrom | ((!nl) & inheader);
+ inbody = (!isfrom) & (!inheader);
+
+ if (buf[n - 1] == '\n')
+ lines++;
+
+ if (inheader)
+ {
+ /* New message. */
+ if (isfrom)
+ {
+ size_t j;
+ /* Signal the end of the body. */
+ if (mum && !mum->body_end)
+ {
+ mum->body_end = total - n - newline;
+ mum->body_lines = --lines - newline;
+
+ if (mum->uid <= min_uid)
+ {
+ mum->uid = ++min_uid;
+ /* Note that modification for when expunging. */
+ mum->attr_flags |= MU_ATTRIBUTE_MODIFIED;
+ }
+ else
+ min_uid = mum->uid;
+
+ if (do_notif)
+ DISPATCH_ADD_MSG(mailbox, mud);
+
+ }
+ /* Allocate_msgs will initialize mum. */
+ ALLOCATE_MSGS(mailbox, mud);
+ mud->messages_count++;
+ mum = mud->umessages[mud->messages_count - 1];
+ mum->mud = mud;
+ mum->header_from = total - n;
+ mum->header_from_end = total;
+ mum->body_end = mum->body = 0;
+ mum->attr_flags = 0;
+ lines = 0;
+ sfield = NULL;
+ for (j = 0; j < HDRSIZE; j++)
+ if (mum->fhdr[j])
+ {
+ free (mum->fhdr[j]);
+ mum->fhdr[j] = NULL;
+ }
+ }
+ else if (ISSTATUS(buf))
+ {
+ ATTRIBUTE_SET(buf, mum, 'r', 'R', MU_ATTRIBUTE_READ);
+ ATTRIBUTE_SET(buf, mum, 'o', 'O', MU_ATTRIBUTE_SEEN);
+ ATTRIBUTE_SET(buf, mum, 'a', 'A', MU_ATTRIBUTE_ANSWERED);
+ ATTRIBUTE_SET(buf, mum, 'd', 'D', MU_ATTRIBUTE_DELETED);
+ sfield = NULL;
+ }
+ else if (ISBCC(buf))
+ {
+ FAST_H_BCC(mum, sfield, buf, n);
+ }
+ else if (ISCC(buf))
+ {
+ FAST_H_CC(mum, sfield, buf, n);
+ }
+ else if (ISCONTENT_LANGUAGE(buf))
+ {
+ FAST_H_CONTENT_LANGUAGE(mum, sfield, buf, n);
+ }
+ else if (ISCONTENT_TRANSFER_ENCODING(buf))
+ {
+ FAST_H_CONTENT_TRANSFER_ENCODING(mum, sfield, buf, n);
+ }
+ else if (ISCONTENT_TYPE(buf))
+ {
+ FAST_H_CONTENT_TYPE(mum, sfield, buf, n);
+ }
+ else if (ISDATE(buf))
+ {
+ FAST_H_DATE(mum, sfield, buf, n);
+ }
+ else if (ISFROM(buf))
+ {
+ FAST_H_FROM(mum, sfield, buf, n);
+ }
+ else if (ISIN_REPLY_TO(buf))
+ {
+ FAST_H_IN_REPLY_TO(mum, sfield, buf, n);
+ }
+ else if (ISMESSAGE_ID(buf))
+ {
+ FAST_H_MESSAGE_ID(mum, sfield, buf, n);
+ }
+ else if (ISREFERENCE(buf))
+ {
+ FAST_H_REFERENCE(mum, sfield, buf, n);
+ }
+ else if (ISREPLY_TO(buf))
+ {
+ FAST_H_REPLY_TO(mum, sfield, buf, n);
+ }
+ else if (ISSENDER(buf))
+ {
+ FAST_H_SENDER (mum, sfield, buf, n);
+ }
+ else if (ISSUBJECT(buf))
+ {
+ FAST_H_SUBJECT (mum, sfield, buf, n);
+ }
+ else if (ISTO(buf))
+ {
+ FAST_H_TO (mum, sfield, buf, n);
+ }
+ else if (ISX_UIDL(buf))
+ {
+ FAST_H_X_UIDL (mum, sfield, buf, n);
+ }
+ else if (ISX_IMAPBASE(buf))
+ {
+ char *s = memchr (buf, ':', n);
+ if (s)
+ {
+ s++;
+ ATOI(s, mud->uidvalidity);
+ ATOI(s, mud->uidnext);
+ }
+ }
+ else if (ISX_UID(buf))
+ {
+ char *s = memchr (buf, ':', n);
+ if (s)
+ {
+ s++;
+ ATOI(s, mum->uid);
+ }
+ }
+ else if (sfield && (buf[0] == ' ' || buf[0] == '\t'))
+ {
+ char *save = *sfield;
+ FAST_HEADER (save, buf, n);
+ *sfield = save;
+ }
+ else
+ {
+ sfield = NULL;
+ }
+ }
+
+ /* Body. */
+ if (inbody)
+ {
+ /* Set the body position. */
+ if (mum && !mum->body)
+ {
+ mum->body = total - n + nl;
+ mum->header_lines = lines;
+ lines = 0;
+ }
+ }
+
+ newline = nl;
+
+ /* Every 100 mesgs update the lock, it should be every minute. */
+ if ((mud->messages_count % 100) == 0)
+ locker_touchlock (mailbox->locker);
+
+ /* Ping them every 1000 lines. Should be tunable. */
+ if (do_notif)
+ if (((lines +1) % 1000) == 0)
+ DISPATCH_PROGRESS(mailbox, mud);
+
+ } /* while */
+
+ if (mum)
+ {
+ mum->body_end = total - newline;
+ mum->body_lines = lines - newline;
+
+ if (mum->uid <= min_uid)
+ {
+ mum->uid = ++min_uid;
+ /* Note that modification for when expunging. */
+ mum->attr_flags |= MU_ATTRIBUTE_MODIFIED;
+ }
+ else
+ min_uid = mum->uid;
+
+ if (do_notif)
+ DISPATCH_ADD_MSG(mailbox, mud);
+ }
+ if (pcount)
+ *pcount = mud->messages_count;
+ locker_unlock (mailbox->locker);
+ monitor_unlock (mailbox->monitor);
+
+ /* Reset the uidvalidity. */
+ if (mud->messages_count > 0)
+ {
+ mum = mud->umessages[0];
+ if (mud->uidvalidity == 0)
+ {
+ mud->uidvalidity = (unsigned long)time (NULL);
+ mud->uidnext = mud->messages_count + 1;
+ /* Tell that we have been modified for expunging. */
+ mum->attr_flags |= MU_ATTRIBUTE_MODIFIED;
+ }
+ }
+
+ if (mud->messages_count > 0 && min_uid >= mud->uidnext)
+ {
+ mum = mud->umessages[0];
+ mud->uidnext = min_uid + 1;
+ mum->attr_flags |= MU_ATTRIBUTE_MODIFIED;
+ }
+
+#ifdef WITH_PTHREAD
+ pthread_cleanup_pop (0);
+#endif
+ return status;
+}
diff --git a/mailbox/mbox/url.c b/mailbox/mbox/url.c
new file mode 100644
index 000000000..f9653740d
--- /dev/null
+++ b/mailbox/mbox/url.c
@@ -0,0 +1,258 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 1999, 2000, 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 */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+
+#include <registrar0.h>
+#include <url0.h>
+
+static void url_mbox_destroy (url_t purl);
+
+static void
+url_mbox_destroy (url_t url)
+{
+ (void) url;
+}
+
+/* Default mailbox path generator */
+static char *
+_url_path_default (const char *spooldir, const char *user, int unused)
+{
+ char *mbox = malloc (sizeof(spooldir) + strlen(user) + 2);
+ if (!mbox)
+ errno = ENOMEM;
+ else
+ sprintf (mbox, "%s/%s", spooldir, user);
+ return mbox;
+}
+
+/* Hashed indexing */
+static char *
+_url_path_hashed (const char *spooldir, const char *user, int param)
+{
+ int i;
+ int ulen = strlen (user);
+ char *mbox;
+ unsigned hash;
+
+ if (param > ulen)
+ param = ulen;
+ for (i = 0, hash = 0; i < param; i++)
+ hash += user[i];
+
+ mbox = malloc (ulen + strlen (spooldir) + 5);
+ sprintf (mbox, "%s/%02X/%s", spooldir, hash % 256, user);
+ return mbox;
+}
+
+static int transtab[] = {
+ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
+ 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
+ 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
+ 'y', 'z', 'a', 'b', 'c', 'd', 'e', 'f',
+ 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
+ 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
+ 'w', 'x', 'y', 'z', 'a', 'b', 'c', 'd',
+ 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
+ 'm', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
+ 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
+ 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
+ 'x', 'y', 'z', 'b', 'c', 'd', 'e', 'f',
+ 'g', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
+ 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
+ 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
+ 'x', 'y', 'z', 'b', 'c', 'd', 'e', 'f',
+ 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',
+ 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
+ 'r', 's', 't', 'u', 'v', 'w', 'x', 'y',
+ 'z', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
+ 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
+ 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
+ 'x', 'y', 'z', 'a', 'b', 'c', 'd', 'e',
+ 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
+ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
+ 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
+ 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
+ 'y', 'z', 'b', 'c', 'd', 'e', 'f', 'g',
+ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
+ 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
+ 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
+ 'y', 'z', 'b', 'c', 'd', 'e', 'f', 'g'
+};
+
+/* Forward Indexing */
+static char *
+_url_path_index (const char *spooldir, const char *iuser, int index_depth)
+{
+ const unsigned char* user = (const unsigned char*) iuser;
+ int i, ulen = strlen (user);
+ char *mbox, *p;
+
+ if (ulen == 0)
+ return NULL;
+
+ mbox = malloc (ulen + strlen (spooldir) + 2*index_depth + 2);
+ strcpy (mbox, spooldir);
+ p = mbox + strlen (mbox);
+ for (i = 0; i < index_depth && i < ulen; i++)
+ {
+ *p++ = '/';
+ *p++ = transtab[ user[i] ];
+ }
+ for (; i < index_depth; i++)
+ {
+ *p++ = '/';
+ *p++ = transtab[ user[ulen-1] ];
+ }
+ *p++ = '/';
+ strcpy (p, user);
+ return mbox;
+}
+
+/* Reverse Indexing */
+static char *
+_url_path_rev_index (const char *spooldir, const char *iuser, int index_depth)
+{
+ const unsigned char* user = (const unsigned char*) iuser;
+ int i, ulen = strlen (user);
+ char *mbox, *p;
+
+ if (ulen == 0)
+ return NULL;
+
+ mbox = malloc (ulen + strlen (spooldir) + 2*index_depth + 1);
+ strcpy (mbox, spooldir);
+ p = mbox + strlen (mbox);
+ for (i = 0; i < index_depth && i < ulen; i++)
+ {
+ *p++ = '/';
+ *p++ = transtab[ user[ulen - i - 1] ];
+ }
+ for (; i < index_depth; i++)
+ {
+ *p++ = '/';
+ *p++ = transtab[ user[0] ];
+ }
+ *p++ = '/';
+ strcpy (p, user);
+ return mbox;
+}
+
+/*
+ UNIX Mbox
+ mbox:path[;type=TYPE][;param=PARAM][;user=USERNAME]
+*/
+int
+_url_mbox_init (url_t url)
+{
+ const char *name = url_to_string (url);
+ size_t len = strlen (name);
+ char *p;
+
+ /* reject the obvious */
+ if (name == NULL || strncmp (MU_MBOX_SCHEME, name, MU_MBOX_SCHEME_LEN) != 0
+ || len < (MU_MBOX_SCHEME_LEN + 1) /* (scheme)+1(path)*/)
+ return EINVAL;
+
+ /* do I need to decode url encoding '% hex hex' ? */
+
+ /* TYPE */
+ url->_destroy = url_mbox_destroy;
+
+ /* SCHEME */
+ url->scheme = strdup (MU_MBOX_SCHEME);
+ if (url->scheme == NULL)
+ {
+ url_mbox_destroy (url);
+ return ENOMEM;
+ }
+
+ /* PATH */
+ name += MU_MBOX_SCHEME_LEN; /* pass the scheme */
+ url->path = strdup (name);
+ if (url->path == NULL)
+ {
+ url_mbox_destroy (url);
+ return ENOMEM;
+ }
+ p = strchr (url->path, ';');
+ if (p)
+ {
+ char *(*fun)() = _url_path_default;
+ char *user = NULL;
+ int param = 0;
+
+ *p++ = 0;
+ while (p)
+ {
+ char *q = strchr (p, ';');
+ if (q)
+ *q++ = 0;
+ if (strncasecmp (p, "type=", 5) == 0)
+ {
+ char *type = p + 5;
+
+ if (strcmp (type, "hash") == 0)
+ fun = _url_path_hashed;
+ else if (strcmp (type, "index") == 0)
+ fun = _url_path_index;
+ else if (strcmp (type, "rev-index") == 0)
+ fun = _url_path_rev_index;
+ else
+ {
+ url_mbox_destroy (url);
+ return ENOENT;
+ }
+ }
+ else if (strncasecmp (p, "user=", 5) == 0)
+ {
+ user = p + 5;
+ }
+ else if (strncasecmp (p, "param=", 6) == 0)
+ {
+ param = strtoul (p+6, NULL, 0);
+ }
+ p = q;
+ }
+
+ if (user)
+ {
+ p = fun (url->path, user, param);
+ free (url->path);
+ url->path = p;
+ }
+ else
+ {
+ url_mbox_destroy (url);
+ return ENOENT;
+ }
+ }
+
+ return 0;
+}
+
diff --git a/mailbox/mh/folder.c b/mailbox/mh/folder.c
new file mode 100644
index 000000000..199510faf
--- /dev/null
+++ b/mailbox/mh/folder.c
@@ -0,0 +1,56 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 1999, 2000, 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 */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifdef ENABLE_MH
+
+#include <errno.h>
+
+#include <folder0.h>
+#include <registrar0.h>
+
+static struct _record _mh_record =
+{
+ MU_MH_SCHEME,
+ _url_mh_init, /* Url init. */
+ _mailbox_mh_init, /* Mailbox init. */
+ NULL, /* Mailer init. */
+ _folder_mh_init, /* Folder init. */
+ NULL, /* back pointer. */
+ NULL, /* _is_scheme method. */
+ NULL, /* _get_url method. */
+ NULL, /* _get_mailbox method. */
+ NULL, /* _get_mailer method. */
+ NULL /* _get_folder method. */
+};
+record_t mh_record = &_mh_record;
+
+int
+_folder_mh_init (folder_t folder)
+{
+ (void)folder;
+ return 0;
+}
+
+#else
+#include <stdio.h>
+#include <registrar0.h>
+record_t mh_record = NULL;
+#endif
diff --git a/mailbox/mh/url.c b/mailbox/mh/url.c
new file mode 100644
index 000000000..b988ac1a5
--- /dev/null
+++ b/mailbox/mh/url.c
@@ -0,0 +1,77 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 1999, 2000, 2002, 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 */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifdef ENABLE_MH
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <url0.h>
+#include <registrar0.h>
+
+static void
+url_mh_destroy (url_t url)
+{
+ (void) url;
+}
+
+/*
+ MH url
+ mh:path
+*/
+int
+_url_mh_init (url_t url)
+{
+ const char *name = url_to_string (url);
+ size_t len = strlen (name);
+
+ /* reject the obvious */
+ if (name == NULL || strncmp (MU_MH_SCHEME, name, MU_MH_SCHEME_LEN) != 0
+ || len < (MU_MH_SCHEME_LEN + 1) /* (scheme)+1(path)*/)
+ return EINVAL;
+
+ /* do I need to decode url encoding '% hex hex' ? */
+
+ /* TYPE */
+ url->_destroy = url_mh_destroy;
+
+ /* SCHEME */
+ url->scheme = strdup (MU_MH_SCHEME);
+ if (url->scheme == NULL)
+ {
+ url_mh_destroy (url);
+ return ENOMEM;
+ }
+
+ /* PATH */
+ name += MU_MH_SCHEME_LEN; /* pass the scheme */
+ url->path = strdup (name);
+ if (url->path == NULL)
+ {
+ url_mh_destroy (url);
+ return ENOMEM;
+ }
+
+ return 0;
+}
+
+#endif
diff --git a/mailbox/pop/folder.c b/mailbox/pop/folder.c
new file mode 100644
index 000000000..36a61ad2b
--- /dev/null
+++ b/mailbox/pop/folder.c
@@ -0,0 +1,138 @@
+/* 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 */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifdef ENABLE_POP
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+
+#include <mailutils/auth.h>
+#include <mailutils/mailbox.h>
+
+#include <folder0.h>
+#include <registrar0.h>
+#include <url0.h>
+
+/* We export url parsing and the initialisation of
+ the mailbox, via the register entry/record. */
+
+static struct _record _pop_record =
+{
+ MU_POP_SCHEME,
+ _url_pop_init, /* Url init. */
+ _mailbox_pop_init, /* Mailbox init. */
+ NULL, /* Mailer init. */
+ _folder_pop_init, /* Folder init. */
+ NULL, /* No need for an back pointer. */
+ NULL, /* _is_scheme method. */
+ NULL, /* _get_url method. */
+ NULL, /* _get_mailbox method. */
+ NULL, /* _get_mailer method. */
+ NULL /* _get_folder method. */
+};
+record_t pop_record = &_pop_record;
+
+static int folder_pop_open __P ((folder_t, int));
+static int folder_pop_close __P ((folder_t));
+static int folder_pop_get_authority __P ((folder_t, authority_t *));
+extern int _pop_user __P ((authority_t));
+extern int _pop_apop __P ((authority_t));
+
+/* XXX: The way, the POP folder is handle is not clean at all.
+ the I/O functions should have been here on folder, not in mbx_pop.c */
+int
+_folder_pop_init (folder_t folder)
+{
+ int status;
+
+ /* Set the authority early:
+ (1) so we can check for errors.
+ (2) allow the client to get the authority for setting the ticket
+ before the open. */
+ status = folder_pop_get_authority (folder, NULL);
+ if (status != 0)
+ return status;
+
+ folder->_open = folder_pop_open;
+ folder->_close = folder_pop_close;
+ return 0;
+}
+
+static int
+folder_pop_open (folder_t folder, int flags)
+{
+ mailbox_t mbox = folder->data;
+ return mailbox_open (mbox, flags);
+}
+
+static int
+folder_pop_close (folder_t folder)
+{
+ mailbox_t mbox = folder->data;
+ return mailbox_close (mbox);
+}
+
+static int
+folder_pop_get_authority (folder_t folder, authority_t *pauth)
+{
+ int status = 0;
+ if (folder->authority == NULL)
+ {
+ /* assert (folder->url); */
+ if (folder->url == NULL)
+ return EINVAL;
+
+ if (folder->url->auth == NULL
+ || strcasecmp (folder->url->auth, "*") == 0)
+ {
+ status = authority_create (&folder->authority, NULL, folder);
+ authority_set_authenticate (folder->authority, _pop_user, folder);
+ }
+ /*
+ "+apop" could be supported.
+ Anything else starting with "+" is an extension mechanism.
+ Without a "+" it's a SASL mechanism.
+ */
+ else if (strcasecmp (folder->url->auth, "+APOP") == 0)
+ {
+ status = authority_create (&folder->authority, NULL, folder);
+ authority_set_authenticate (folder->authority, _pop_apop, folder);
+ }
+ else
+ {
+ status = ENOSYS;
+ }
+ }
+ if (pauth)
+ *pauth = folder->authority;
+ return status;
+}
+
+#else
+#include <stdio.h>
+#include <registrar0.h>
+record_t pop_record = NULL;
+#endif
diff --git a/mailbox/pop/mbox.c b/mailbox/pop/mbox.c
new file mode 100644
index 000000000..ab3e8183a
--- /dev/null
+++ b/mailbox/pop/mbox.c
@@ -0,0 +1,2115 @@
+/* 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 */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifdef ENABLE_POP
+
+#include <termios.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdarg.h>
+
+#ifdef HAVE_ALLOCA_H
+# include <alloca.h>
+#endif
+
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+
+#include <md5.h>
+
+#include <mailutils/attribute.h>
+#include <mailutils/auth.h>
+#include <mailutils/body.h>
+#include <mailutils/debug.h>
+#include <mailutils/errno.h>
+#include <mailutils/error.h>
+#include <mailutils/header.h>
+#include <mailutils/message.h>
+#include <mailutils/observer.h>
+#include <mailutils/property.h>
+#include <mailutils/stream.h>
+#include <mailutils/url.h>
+
+#include <folder0.h>
+#include <mailbox0.h>
+#include <registrar0.h>
+#include <url0.h>
+
+#define PROP_RFC822 1
+
+/* Advance declarations. */
+struct _pop_data;
+struct _pop_message;
+
+typedef struct _pop_data * pop_data_t;
+typedef struct _pop_message * pop_message_t;
+
+/* The different possible states of a Pop client, Note that POP3 is not
+ reentrant i.e. it is only one channel, so it is not possible to start
+ Another operation while one is running. The only resort is to close the
+ connection and reopen it again. This is what we do, the downside is that
+ the client as to get the authentication again user/pass. */
+enum pop_state
+{
+ POP_NO_STATE, POP_STATE_DONE,
+ POP_OPEN_CONNECTION,
+ POP_GREETINGS,
+ POP_APOP, POP_APOP_ACK,
+ POP_DELE, POP_DELE_ACK,
+ POP_LIST, POP_LIST_ACK, POP_LIST_RX,
+ POP_QUIT, POP_QUIT_ACK,
+ POP_NOOP, POP_NOOP_ACK,
+ POP_RETR, POP_RETR_ACK, POP_RETR_RX_HDR, POP_RETR_RX_BODY,
+ POP_RSET, POP_RSET_ACK,
+ POP_STAT, POP_STAT_ACK,
+ POP_TOP, POP_TOP_ACK, POP_TOP_RX,
+ POP_UIDL, POP_UIDL_ACK,
+ POP_AUTH, POP_AUTH_DONE,
+ POP_AUTH_USER, POP_AUTH_USER_ACK,
+ POP_AUTH_PASS, POP_AUTH_PASS_ACK
+};
+
+static void pop_destroy __P ((mailbox_t));
+
+/* Functions/Methods that implements the mailbox_t API. */
+static int pop_open __P ((mailbox_t, int));
+static int pop_close __P ((mailbox_t));
+static int pop_get_message __P ((mailbox_t, size_t, message_t *));
+static int pop_messages_count __P ((mailbox_t, size_t *));
+static int pop_messages_recent __P ((mailbox_t, size_t *));
+static int pop_message_unseen __P ((mailbox_t, size_t *));
+static int pop_expunge __P ((mailbox_t));
+static int pop_scan __P ((mailbox_t, size_t, size_t *));
+static int pop_is_updated __P ((mailbox_t));
+
+/* The implementation of message_t */
+int _pop_user __P ((authority_t));
+int _pop_apop __P ((authority_t));
+static int pop_get_size __P ((mailbox_t, off_t *));
+/* We use pop_top for retreiving headers. */
+/* static int pop_header_read (header_t, char *, size_t, off_t, size_t *); */
+static int pop_body_fd __P ((stream_t, int *));
+static int pop_body_size __P ((body_t, size_t *));
+static int pop_body_lines __P ((body_t, size_t *));
+static int pop_body_read __P ((stream_t, char *, size_t, off_t, size_t *));
+static int pop_message_read __P ((stream_t, char *, size_t, off_t, size_t *));
+static int pop_message_size __P ((message_t, size_t *));
+static int pop_message_fd __P ((stream_t, int *));
+static int pop_top __P ((header_t, char *, size_t, off_t, size_t *));
+static int pop_retr __P ((pop_message_t, char *, size_t, off_t, size_t *));
+static int pop_get_fd __P ((pop_message_t, int *));
+static int pop_get_attribute __P ((attribute_t, int *));
+static int pop_set_attribute __P ((attribute_t, int));
+static int pop_unset_attribute __P ((attribute_t, int));
+static int pop_uidl __P ((message_t, char *, size_t, size_t *));
+static int pop_uid __P ((message_t, size_t *));
+static int fill_buffer __P ((pop_data_t, char *, size_t));
+static int pop_sleep __P ((int));
+static int pop_readline __P ((pop_data_t));
+static int pop_read_ack __P ((pop_data_t));
+static int pop_writeline __P ((pop_data_t, const char *, ...));
+static int pop_write __P ((pop_data_t));
+static int pop_get_user __P ((authority_t));
+static int pop_get_passwd __P ((authority_t));
+static char *pop_get_timestamp __P ((pop_data_t));
+static int pop_get_md5 __P ((pop_data_t));
+
+/* This structure holds the info for a message. The pop_message_t
+ type, will serve as the owner of the message_t and contains the command to
+ send to "RETR"eive the specify message. The problem comes from the header.
+ If the POP server supports TOP, we can cleanly fetch the header.
+ But otherwise we use the clumsy approach. .i.e for the header we read 'til
+ ^\n then discard the rest, for the body we read after ^\n and discard the
+ beginning. This is a waste, Pop was not conceive for this obviously. */
+struct _pop_message
+{
+ int inbody;
+ int skip_header;
+ int skip_body;
+ size_t body_size;
+ size_t header_size;
+ size_t body_lines;
+ size_t header_lines;
+ size_t message_size;
+ size_t num;
+ char *uidl; /* Cache the uidl string. */
+ int attr_flags;
+ message_t message;
+ pop_data_t mpd; /* Back pointer. */
+};
+
+/* Structure to hold things general to the POP mailbox, like its state, how
+ many messages we have so far etc ... */
+struct _pop_data
+{
+ void *func; /* Indicate a command is in operation, busy. */
+ size_t id; /* A second level of distincion, we maybe in the same function
+ but working on a different message. */
+ enum pop_state state;
+ pop_message_t *pmessages;
+ size_t pmessages_count;
+ size_t messages_count;
+ size_t size;
+
+ /* Working I/O buffers. */
+ char *buffer;
+ size_t buflen; /* Len of buffer. */
+ char *ptr; /* Points to the end of the buffer i.e the non consume chars. */
+ char *nl; /* Points to the '\n' char in te string. */
+ off_t offset; /* Dummy, this is use because of the stream buffering.
+ The stream_t maintains and offset and the offset we use must
+ be in sync. */
+
+ int is_updated;
+ char *user; /* Temporary holders for user and passwd. */
+ char *passwd; /* Temporary holders for passwd memset (0) when finish. */
+ mailbox_t mbox; /* Back pointer. */
+} ;
+
+/* Usefull little Macros, since these are very repetitive. */
+
+/* Check if we're busy ? */
+/* POP is a one channel download protocol, so if someone
+ is trying to execute a command while another is running
+ something is seriously incorrect, So the best course
+ of action is to close down the connection and start a new one.
+ For example mime_t only reads part of the message. If a client
+ wants to read different part of the message via mime it should
+ download it first. POP does not have the features of IMAP for
+ multipart messages.
+ Let see a concrete example:
+ {
+ mailbox_t mbox; message_t msg; stream_t stream; char buffer[105];
+ mailbox_create (&mbox, "pop://qnx.com");
+ mailbox_get_message (mbox, 1, &msg);
+ message_get_stream (msg, &stream);
+ while (stream_readline (stream, buffer, sizeof(buffer), NULL) != 0) { ..}
+ }
+ if in the while of the readline, one try to get another email. The pop
+ server will get seriously confused, and the second message will still
+ be the first one, There is no way to tell POP servers yo! stop/abort.
+ The approach is to close the stream and reopen again. So every time
+ we go in to a function our state is preserve by the triplets
+ mpd->{func,state,id}. The macro CHECK_BUSY checks if we are not
+ in another operation if not you get access if yes the stream is close
+ and pop_open() is recall again for a new connection.
+ */
+#define CHECK_BUSY(mbox, mpd, function, identity) \
+do \
+ { \
+ int err = monitor_wrlock (mbox->monitor); \
+ if (err != 0) \
+ return err; \
+ if ((mpd->func && mpd->func != function) \
+ || (mpd->id && mpd->id != (size_t)identity)) \
+ { \
+ mpd->id = 0; \
+ mpd->func = (void *)pop_open; \
+ mpd->state = POP_NO_STATE; \
+ monitor_unlock (mbox->monitor); \
+ err = pop_open (mbox, mbox->flags); \
+ if (err != 0) \
+ { \
+ return err; \
+ } \
+ } \
+ else \
+ { \
+ mpd->id = (size_t)identity; \
+ mpd->func = func; \
+ monitor_unlock (mbox->monitor); \
+ } \
+ } \
+while (0)
+
+/* Clear the state. */
+#define CLEAR_STATE(mpd) \
+ mpd->id = 0, mpd->func = NULL, mpd->state = POP_NO_STATE
+
+/* Clear the state and close the stream. */
+#define CHECK_ERROR_CLOSE(mbox, mpd, status) \
+do \
+ { \
+ if (status != 0) \
+ { \
+ stream_close (mbox->stream); \
+ CLEAR_STATE (mpd); \
+ mpd->func = (void *)-1; \
+ MAILBOX_DEBUG1(mbox, MU_DEBUG_PROT, "CHECK_ERROR_CLOSE: %s\n", mu_strerror (status));\
+ return status; \
+ } \
+ } \
+while (0)
+
+/* If error, clear the state and return. */
+#define CHECK_ERROR(mpd, status) \
+do \
+ { \
+ if (status != 0) \
+ { \
+ CLEAR_STATE (mpd); \
+ mpd->func = (void*)-1; \
+ MAILBOX_DEBUG1(mpd->mbox, MU_DEBUG_PROT, "CHECK_ERROR: %s\n", mu_strerror (status));\
+ return status; \
+ } \
+ } \
+while (0)
+
+/* Clear the state for non recoverable error. */
+#define CHECK_EAGAIN(mpd, status) \
+do \
+ { \
+ if (status != 0) \
+ { \
+ if (status != EAGAIN && status != EINPROGRESS && status != EINTR) \
+ { \
+ CLEAR_STATE (mpd); \
+ mpd->func = (void *)-1; \
+ MAILBOX_DEBUG1(mpd->mbox, MU_DEBUG_PROT, "CHECK_EAGAIN: %s\n", mu_strerror (status));\
+ } \
+ return status; \
+ } \
+ } \
+while (0)
+
+
+/* Allocate mailbox_t, allocate pop internal structures. */
+int
+_mailbox_pop_init (mailbox_t mbox)
+{
+ pop_data_t mpd;
+ int status = 0;
+
+ /* Allocate specifics for pop data. */
+ mpd = mbox->data = calloc (1, sizeof (*mpd));
+ if (mbox->data == NULL)
+ return ENOMEM;
+
+ mpd->mbox = mbox; /* Back pointer. */
+
+ mpd->state = POP_NO_STATE; /* Init with no state. */
+
+ /* Initialize the structure. */
+ mbox->_destroy = pop_destroy;
+
+ mbox->_open = pop_open;
+ mbox->_close = pop_close;
+
+ /* Messages. */
+ mbox->_get_message = pop_get_message;
+ mbox->_messages_count = pop_messages_count;
+ mbox->_messages_recent = pop_messages_recent;
+ mbox->_message_unseen = pop_message_unseen;
+ mbox->_expunge = pop_expunge;
+
+ mbox->_scan = pop_scan;
+ mbox->_is_updated = pop_is_updated;
+
+ mbox->_get_size = pop_get_size;
+
+ /* Set our properties. */
+ {
+ property_t property = NULL;
+ mailbox_get_property (mbox, &property);
+ property_set_value (property, "TYPE", "POP3", 1);
+ }
+
+ /* Hack! POP does not really have a folder. */
+ mbox->folder->data = mbox;
+
+ return status;
+}
+
+/* Cleaning up all the ressources associate with a pop mailbox. */
+static void
+pop_destroy (mailbox_t mbox)
+{
+ if (mbox->data)
+ {
+ pop_data_t mpd = mbox->data;
+ size_t i;
+ monitor_wrlock (mbox->monitor);
+ /* Destroy the pop messages and ressources associated to them. */
+ for (i = 0; i < mpd->pmessages_count; i++)
+ {
+ if (mpd->pmessages[i])
+ {
+ message_destroy (&(mpd->pmessages[i]->message),
+ mpd->pmessages[i]);
+ if (mpd->pmessages[i]->uidl)
+ free (mpd->pmessages[i]->uidl);
+ free (mpd->pmessages[i]);
+ mpd->pmessages[i] = NULL;
+ }
+ }
+ if (mpd->buffer)
+ free (mpd->buffer);
+ if (mpd->pmessages)
+ free (mpd->pmessages);
+ free (mpd);
+ mbox->data = NULL;
+ monitor_unlock (mbox->monitor);
+ }
+}
+
+/* Simple User/pass authentication for pop. We ask for the info
+ from the standard input. */
+int
+_pop_user (authority_t auth)
+{
+ folder_t folder = authority_get_owner (auth);
+ mailbox_t mbox = folder->data;
+ pop_data_t mpd = mbox->data;
+ int status;
+
+ switch (mpd->state)
+ {
+ case POP_AUTH:
+ /* Fetch the user from them. */
+ status = pop_get_user (auth);
+ if (status != 0 || mpd->user == NULL || mpd->user[0] == '\0')
+ {
+ CHECK_ERROR_CLOSE (mbox, mpd, EINVAL);
+ }
+ status = pop_writeline (mpd, "USER %s\r\n", mpd->user);
+ CHECK_ERROR_CLOSE(mbox, mpd, status);
+ MAILBOX_DEBUG0 (mbox, MU_DEBUG_PROT, mpd->buffer);
+ free (mpd->user);
+ mpd->user = NULL;
+ mpd->state = POP_AUTH_USER;
+
+ case POP_AUTH_USER:
+ /* Send username. */
+ status = pop_write (mpd);
+ CHECK_EAGAIN (mpd, status);
+ mpd->state = POP_AUTH_USER_ACK;
+
+ case POP_AUTH_USER_ACK:
+ /* Get the user ack. */
+ status = pop_read_ack (mpd);
+ CHECK_EAGAIN (mpd, status);
+ MAILBOX_DEBUG0 (mbox, MU_DEBUG_PROT, mpd->buffer);
+ if (strncasecmp (mpd->buffer, "+OK", 3) != 0)
+ {
+ observable_t observable = NULL;
+ mailbox_get_observable (mbox, &observable);
+ CLEAR_STATE (mpd);
+ observable_notify (observable, MU_EVT_AUTHORITY_FAILED);
+ CHECK_ERROR_CLOSE (mbox, mpd, EACCES);
+ }
+ status = pop_get_passwd (auth);
+ if (status != 0 || mpd->passwd == NULL || mpd->passwd[0] == '\0')
+ {
+ CHECK_ERROR_CLOSE (mbox, mpd, EINVAL);
+ }
+ status = pop_writeline (mpd, "PASS %s\r\n", mpd->passwd);
+ MAILBOX_DEBUG0 (mbox, MU_DEBUG_PROT, mpd->buffer);
+ /* Leave not trail of the passwd. */
+ memset (mpd->passwd, '\0', strlen (mpd->passwd));
+ free (mpd->passwd);
+ mpd->passwd = NULL;
+ CHECK_ERROR_CLOSE (mbox, mpd, status);
+ mpd->state = POP_AUTH_PASS;
+
+ case POP_AUTH_PASS:
+ /* Send passwd. */
+ status = pop_write (mpd);
+ CHECK_EAGAIN (mpd, status);
+ /* Clear the buffer it contains the passwd. */
+ memset (mpd->buffer, '\0', mpd->buflen);
+ mpd->state = POP_AUTH_PASS_ACK;
+
+ case POP_AUTH_PASS_ACK:
+ /* Get the ack from passwd. */
+ status = pop_read_ack (mpd);
+ CHECK_EAGAIN (mpd, status);
+ MAILBOX_DEBUG0 (mbox, MU_DEBUG_PROT, mpd->buffer);
+ if (strncasecmp (mpd->buffer, "+OK", 3) != 0)
+ {
+ observable_t observable = NULL;
+ mailbox_get_observable (mbox, &observable);
+ CLEAR_STATE (mpd);
+ observable_notify (observable, MU_EVT_AUTHORITY_FAILED);
+ CHECK_ERROR_CLOSE (mbox, mpd, EACCES);
+ }
+ mpd->state = POP_AUTH_DONE;
+ break; /* We're outta here. */
+
+ default:
+ break;
+ }
+ CLEAR_STATE (mpd);
+ return 0;
+}
+
+int
+_pop_apop (authority_t auth)
+{
+ folder_t folder = authority_get_owner (auth);
+ mailbox_t mbox = folder->data;
+ pop_data_t mpd = mbox->data;
+ int status;
+
+ switch (mpd->state)
+ {
+ case POP_AUTH:
+ /* Fetch the user from them. */
+ status = pop_get_user (auth);
+ if (status != 0 || mpd->user == NULL || mpd->user[0] == '\0')
+ {
+ CHECK_ERROR_CLOSE (mbox, mpd, EINVAL);
+ }
+
+ /* Fetch the secret from them. */
+ status = pop_get_passwd (auth);
+ if (status != 0 || mpd->passwd == NULL || mpd->passwd[0] == '\0')
+ {
+ CHECK_ERROR_CLOSE (mbox, mpd, EINVAL);
+ }
+
+ /* Make the MD5 digest string. */
+ status = pop_get_md5 (mpd);
+ if (status != 0)
+ {
+ CHECK_ERROR_CLOSE (mbox, mpd, status);
+ }
+ status = pop_writeline (mpd, "APOP %s %s\r\n", mpd->user, mpd->passwd);
+ MAILBOX_DEBUG0 (mbox, MU_DEBUG_PROT, mpd->buffer);
+ /* We have to obscure the md5 string. */
+ memset (mpd->passwd, '\0', strlen (mpd->passwd));
+ free (mpd->user);
+ free (mpd->passwd);
+ mpd->user = NULL;
+ mpd->passwd = NULL;
+ CHECK_ERROR_CLOSE (mbox, mpd, status);
+ mpd->state = POP_APOP;
+
+ case POP_APOP:
+ /* Send apop. */
+ status = pop_write (mpd);
+ CHECK_EAGAIN (mpd, status);
+ /* Clear the buffer it contains the md5. */
+ memset (mpd->buffer, '\0', mpd->buflen);
+ mpd->state = POP_APOP_ACK;
+
+ case POP_APOP_ACK:
+ status = pop_read_ack (mpd);
+ CHECK_EAGAIN (mpd, status);
+ MAILBOX_DEBUG0 (mbox, MU_DEBUG_PROT, mpd->buffer);
+ if (strncasecmp (mpd->buffer, "+OK", 3) != 0)
+ {
+ observable_t observable = NULL;
+ mailbox_get_observable (mbox, &observable);
+ CLEAR_STATE (mpd);
+ observable_notify (observable, MU_EVT_AUTHORITY_FAILED);
+ CHECK_ERROR_CLOSE (mbox, mpd, EACCES);
+ }
+ mpd->state = POP_AUTH_DONE;
+ break; /* We're outta here. */
+
+ default:
+ break;
+ }
+ CLEAR_STATE (mpd);
+ return 0;
+}
+
+
+/* Open the connection to the sever, and send the authentication.
+ FIXME: Should also send the CAPA command to detect for example the suport
+ for TOP, APOP, ... and DTRT(Do The Right Thing). */
+static int
+pop_open (mailbox_t mbox, int flags)
+{
+ pop_data_t mpd = mbox->data;
+ int status;
+ char *host;
+ size_t hostlen = 0;
+ long port = 110;
+
+ /* Sanity checks. */
+ if (mpd == NULL)
+ return EINVAL;
+
+ /* Fetch the pop server name and the port in the url_t. */
+ status = url_get_host (mbox->url, NULL, 0, &hostlen);
+ if (status != 0)
+ return status;
+ host = alloca (hostlen + 1);
+ url_get_host (mbox->url, host, hostlen + 1, NULL);
+ url_get_port (mbox->url, &port);
+
+ mbox->flags = flags;
+
+ /* Do not check for reconnect here. */
+ /* CHECK_BUSY (mbox, mpd, func, 0); */
+
+ /* Enter the pop state machine, and boogy: AUTHORISATION State. */
+ switch (mpd->state)
+ {
+ case POP_NO_STATE:
+ /* Allocate a working io buffer. */
+ if (mpd->buffer == NULL)
+ {
+ /* 255 is the limit lenght of a POP3 command according to RFCs. */
+ mpd->buflen = 255;
+ mpd->buffer = calloc (mpd->buflen + 1, sizeof (char));
+ if (mpd->buffer == NULL)
+ {
+ CHECK_ERROR (mpd, ENOMEM);
+ }
+ }
+ else
+ {
+ /* Clear any residual from a previous connection. */
+ memset (mpd->buffer, '\0', mpd->buflen);
+ }
+ mpd->ptr = mpd->buffer;
+
+ /* Create the networking stack. */
+ if (mbox->stream == NULL)
+ {
+ status = tcp_stream_create (&mbox->stream, host, port, mbox->flags);
+ CHECK_ERROR(mpd, status);
+ /* Using the awkward stream_t buffering. */
+ stream_setbufsiz (mbox->stream, BUFSIZ);
+ }
+ else
+ {
+ /* This is sudden death: for many pop servers, it is important to
+ let them time to remove locks or move the .user.pop files. This
+ happen when we do BUSY_CHECK(). For example, the user does not
+ want to read the entire file, and wants start to read a new
+ message, closing the connection and immediately contact the
+ server again, and we'll end up having "-ERR Mail Lock busy" or
+ something similar. To prevent this race condition we sleep 2
+ seconds. */
+ stream_close (mbox->stream);
+ pop_sleep (2);
+ }
+ mpd->state = POP_OPEN_CONNECTION;
+
+ case POP_OPEN_CONNECTION:
+ /* Establish the connection. */
+ MAILBOX_DEBUG2 (mbox, MU_DEBUG_PROT, "open (%s:%d)\n", host, port);
+ status = stream_open (mbox->stream);
+ CHECK_EAGAIN (mpd, status);
+ /* Can't recover bailout. */
+ CHECK_ERROR_CLOSE (mbox, mpd, status);
+ mpd->state = POP_GREETINGS;
+
+ case POP_GREETINGS:
+ {
+ /* Swallow the greetings. */
+ status = pop_read_ack (mpd);
+ CHECK_EAGAIN (mpd, status);
+ MAILBOX_DEBUG0 (mbox, MU_DEBUG_PROT, mpd->buffer);
+ if (strncasecmp (mpd->buffer, "+OK", 3) != 0)
+ {
+ CHECK_ERROR_CLOSE (mbox, mpd, EACCES);
+ }
+ mpd->state = POP_AUTH;
+ }
+
+ case POP_AUTH:
+ case POP_AUTH_USER:
+ case POP_AUTH_USER_ACK:
+ case POP_AUTH_PASS:
+ case POP_AUTH_PASS_ACK:
+ case POP_APOP:
+ case POP_APOP_ACK:
+ /* Authenticate. */
+ status = authority_authenticate (mbox->folder->authority);
+ CHECK_EAGAIN (mpd, status);
+
+ case POP_AUTH_DONE:
+ break;
+
+ default:
+ /*
+ mu_error ("pop_open unknown state\n");
+ */
+ break;
+ }/* End AUTHORISATION state. */
+
+ /* Clear any state. */
+ CLEAR_STATE (mpd);
+ return 0;
+}
+
+/* Send the QUIT and close the socket. */
+static int
+pop_close (mailbox_t mbox)
+{
+ pop_data_t mpd = mbox->data;
+ void *func = (void *)pop_close;
+ int status;
+ size_t i;
+
+ if (mpd == NULL)
+ return EINVAL;
+
+ /* Should not check for Busy, we're shuting down anyway. */
+ /* CHECK_BUSY (mbox, mpd, func, 0); */
+ monitor_wrlock (mbox->monitor);
+ if (mpd->func && mpd->func != func)
+ mpd->state = POP_NO_STATE;
+ mpd->id = 0;
+ mpd->func = func;
+ monitor_unlock (mbox->monitor);
+
+ /* Ok boys, it's a wrap: UPDATE State. */
+ switch (mpd->state)
+ {
+ case POP_NO_STATE:
+ /* Initiate the close. */
+ status = pop_writeline (mpd, "QUIT\r\n");
+ CHECK_ERROR (mpd, status);
+ MAILBOX_DEBUG0 (mbox, MU_DEBUG_PROT, mpd->buffer);
+ mpd->state = POP_QUIT;
+
+ case POP_QUIT:
+ /* Send the quit. */
+ status = pop_write (mpd);
+ CHECK_EAGAIN (mpd, status);
+ mpd->state = POP_QUIT_ACK;
+
+ case POP_QUIT_ACK:
+ /* Glob the acknowledge. */
+ status = pop_read_ack (mpd);
+ CHECK_EAGAIN (mpd, status);
+ MAILBOX_DEBUG0 (mbox, MU_DEBUG_PROT, mpd->buffer);
+ /* Now what ! and how can we tell them about errors ? So far now
+ lets just be verbose about the error but close the connection
+ anyway. */
+ if (strncasecmp (mpd->buffer, "+OK", 3) != 0)
+ mu_error ("pop_close: %s\n", mpd->buffer);
+ stream_close (mbox->stream);
+ break;
+
+ default:
+ /*
+ mu_error ("pop_close unknow state");
+ */
+ break;
+ } /* UPDATE state. */
+
+ /* Free the messages. */
+ for (i = 0; i < mpd->pmessages_count; i++)
+ {
+ if (mpd->pmessages[i])
+ {
+ message_destroy (&(mpd->pmessages[i]->message),
+ mpd->pmessages[i]);
+ if (mpd->pmessages[i]->uidl)
+ free (mpd->pmessages[i]->uidl);
+ free (mpd->pmessages[i]);
+ mpd->pmessages[i] = NULL;
+ }
+ }
+ /* And clear any residue. */
+ if (mpd->pmessages)
+ free (mpd->pmessages);
+ mpd->pmessages = NULL;
+ mpd->pmessages_count = 0;
+ mpd->is_updated = 0;
+ if (mpd->buffer)
+ free (mpd->buffer);
+ mpd->buffer = NULL;
+
+ CLEAR_STATE (mpd);
+ return 0;
+}
+
+/* Only build/setup the message_t structure for a mesgno. pop_message_t,
+ will act as the owner of messages. */
+static int
+pop_get_message (mailbox_t mbox, size_t msgno, message_t *pmsg)
+{
+ pop_data_t mpd = mbox->data;
+ message_t msg = NULL;
+ pop_message_t mpm;
+ int status;
+ size_t i;
+
+ /* Sanity. */
+ if (pmsg == NULL || mpd == NULL || msgno > mpd->messages_count)
+ return EINVAL;
+
+ monitor_rdlock (mbox->monitor);
+ /* See if we have already this message. */
+ for (i = 0; i < mpd->pmessages_count; i++)
+ {
+ if (mpd->pmessages[i])
+ {
+ if (mpd->pmessages[i]->num == msgno)
+ {
+ *pmsg = mpd->pmessages[i]->message;
+ monitor_unlock (mbox->monitor);
+ return 0;
+ }
+ }
+ }
+ monitor_unlock (mbox->monitor);
+
+ mpm = calloc (1, sizeof (*mpm));
+ if (mpm == NULL)
+ return ENOMEM;
+
+ /* Back pointer. */
+ mpm->mpd = mpd;
+ mpm->num = msgno;
+
+ /* Create the message. */
+ {
+ stream_t stream = NULL;
+ if ((status = message_create (&msg, mpm)) != 0
+ || (status = stream_create (&stream, mbox->flags, msg)) != 0)
+ {
+ stream_destroy (&stream, msg);
+ message_destroy (&msg, mpm);
+ free (mpm);
+ return status;
+ }
+ /* Help for the readline()s */
+ stream_setbufsiz (stream, 128);
+ stream_set_read (stream, pop_message_read, msg);
+ stream_set_fd (stream, pop_message_fd, msg);
+ message_set_stream (msg, stream, mpm);
+ message_set_size (msg, pop_message_size, mpm);
+ }
+
+ /* Create the header. */
+ {
+ header_t header = NULL;
+ if ((status = header_create (&header, NULL, 0, msg)) != 0)
+ {
+ message_destroy (&msg, mpm);
+ free (mpm);
+ return status;
+ }
+ header_set_fill (header, pop_top, msg);
+ message_set_header (msg, header, mpm);
+ }
+
+ /* Create the attribute. */
+ {
+ attribute_t attribute;
+ status = attribute_create (&attribute, msg);
+ if (status != 0)
+ {
+ message_destroy (&msg, mpm);
+ free (mpm);
+ return status;
+ }
+ attribute_set_get_flags (attribute, pop_get_attribute, msg);
+ attribute_set_set_flags (attribute, pop_set_attribute, msg);
+ attribute_set_unset_flags (attribute, pop_unset_attribute, msg);
+ message_set_attribute (msg, attribute, mpm);
+ }
+
+ /* Create the body and its stream. */
+ {
+ body_t body = NULL;
+ stream_t stream = NULL;
+ if ((status = body_create (&body, msg)) != 0
+ || (status = stream_create (&stream, mbox->flags, body)) != 0)
+ {
+ body_destroy (&body, msg);
+ stream_destroy (&stream, body);
+ message_destroy (&msg, mpm);
+ free (mpm);
+ return status;
+ }
+ /* Helps for the readline()s */
+ stream_setbufsiz (stream, 128);
+ stream_set_read (stream, pop_body_read, body);
+ stream_set_fd (stream, pop_body_fd, body);
+ body_set_size (body, pop_body_size, msg);
+ body_set_lines (body, pop_body_lines, msg);
+ body_set_stream (body, stream, msg);
+ message_set_body (msg, body, mpm);
+ }
+
+ /* Set the UIDL call on the message. */
+ message_set_uidl (msg, pop_uidl, mpm);
+
+ /* Set the UID on the message. */
+ message_set_uid (msg, pop_uid, mpm);
+
+ /* Add it to the list. */
+ monitor_wrlock (mbox->monitor);
+ {
+ pop_message_t *m ;
+ m = realloc (mpd->pmessages, (mpd->pmessages_count + 1)*sizeof (*m));
+ if (m == NULL)
+ {
+ message_destroy (&msg, mpm);
+ free (mpm);
+ monitor_unlock (mbox->monitor);
+ return ENOMEM;
+ }
+ mpd->pmessages = m;
+ mpd->pmessages[mpd->pmessages_count] = mpm;
+ mpd->pmessages_count++;
+ }
+ monitor_unlock (mbox->monitor);
+
+ /* Save The message pointer. */
+ message_set_mailbox (msg, mbox, mpm);
+ *pmsg = mpm->message = msg;
+
+ return 0;
+}
+
+/* There is no such thing in pop all messages should be consider recent.
+ FIXME: We could cheat and peek at the status if it was not strip
+ by the server ... */
+static int
+pop_messages_recent (mailbox_t mbox, size_t *precent)
+{
+ return pop_messages_count (mbox, precent);
+}
+
+/* There is no such thing in pop all messages should be consider unseen.
+ FIXME: We could cheat and peek at the status if it was not strip
+ by the server ... */
+static int
+pop_message_unseen (mailbox_t mbox, size_t *punseen)
+{
+ size_t count = 0;
+ int status = pop_messages_count (mbox, &count);
+ if (status != 0)
+ return status;
+ if (punseen)
+ *punseen = (count > 0) ? 1 : 0;
+ return 0;
+}
+
+/* How many messages we have. Done with STAT. */
+static int
+pop_messages_count (mailbox_t mbox, size_t *pcount)
+{
+ pop_data_t mpd = mbox->data;
+ int status;
+ void *func = (void *)pop_messages_count;
+
+ if (mpd == NULL)
+ return EINVAL;
+
+ /* Do not send a STAT if we know the answer. */
+ if (pop_is_updated (mbox))
+ {
+ if (pcount)
+ *pcount = mpd->messages_count;
+ return 0;
+ }
+
+ /* Flag busy. */
+ CHECK_BUSY (mbox, mpd, func, 0);
+
+ /* TRANSACTION state. */
+ switch (mpd->state)
+ {
+ case POP_NO_STATE:
+ status = pop_writeline (mpd, "STAT\r\n");
+ CHECK_ERROR (mpd, status);
+ MAILBOX_DEBUG0 (mbox, MU_DEBUG_PROT, mpd->buffer);
+ mpd->state = POP_STAT;
+
+ case POP_STAT:
+ /* Send the STAT. */
+ status = pop_write (mpd);
+ CHECK_EAGAIN (mpd, status);
+ mpd->state = POP_STAT_ACK;
+
+ case POP_STAT_ACK:
+ /* Get the ACK. */
+ status = pop_read_ack (mpd);
+ CHECK_EAGAIN (mpd, status);
+ MAILBOX_DEBUG0 (mbox, MU_DEBUG_PROT, mpd->buffer);
+ break;
+
+ default:
+ /*
+ mu_error ("pop_messages_count: unknow state\n");
+ */
+ break;
+ }
+
+
+ /* Parse the answer. */
+ status = sscanf (mpd->buffer, "+OK %d %d", &(mpd->messages_count),
+ &(mpd->size));
+
+ /* Clear the state _after_ the scanf, since another thread could
+ start writing over mpd->buffer. */
+ CLEAR_STATE (mpd);
+
+ if (status == EOF || status != 2)
+ return EIO;
+
+ if (pcount)
+ *pcount = mpd->messages_count;
+ mpd->is_updated = 1;
+ return 0;
+}
+
+/* Update and scanning. */
+static int
+pop_is_updated (mailbox_t mbox)
+{
+ pop_data_t mpd = mbox->data;
+ if (mpd == NULL)
+ return 0;
+ return mpd->is_updated;
+}
+
+/* We just simulate by sending a notification for the total msgno. */
+/* FIXME is message is set deleted should we sent a notif ? */
+static int
+pop_scan (mailbox_t mbox, size_t msgno, size_t *pcount)
+{
+ int status;
+ size_t i;
+ size_t count = 0;
+
+ status = pop_messages_count (mbox, &count);
+ if (pcount)
+ *pcount = count;
+ if (status != 0)
+ return status;
+ if (mbox->observable == NULL)
+ return 0;
+ for (i = msgno; i <= count; i++)
+ {
+ if (observable_notify (mbox->observable, MU_EVT_MESSAGE_ADD) != 0)
+ break;
+ if (((i +1) % 10) == 0)
+ {
+ observable_notify (mbox->observable, MU_EVT_MAILBOX_PROGRESS);
+ }
+ }
+ return 0;
+}
+
+/* This is where we actually send the DELE command. Meaning that when
+ the attribute on the message is set deleted the comand DELE is not
+ sent right away and if we did there is no way to mark a message undeleted
+ beside closing down the connection without going to the update state via
+ QUIT. So DELE is send only when in expunge. */
+static int
+pop_expunge (mailbox_t mbox)
+{
+ pop_data_t mpd = mbox->data;
+ size_t i;
+ attribute_t attr;
+ int status;
+ void *func = (void *)pop_expunge;
+
+ if (mpd == NULL)
+ return EINVAL;
+
+ /* Busy ? */
+ CHECK_BUSY (mbox, mpd, func, 0);
+
+ for (i = (int)mpd->id; i < mpd->pmessages_count; mpd->id = ++i)
+ {
+ if (message_get_attribute (mpd->pmessages[i]->message, &attr) == 0)
+ {
+ if (attribute_is_deleted (attr))
+ {
+ switch (mpd->state)
+ {
+ case POP_NO_STATE:
+ status = pop_writeline (mpd, "DELE %d\r\n",
+ mpd->pmessages[i]->num);
+ CHECK_ERROR (mpd, status);
+ MAILBOX_DEBUG0 (mbox, MU_DEBUG_PROT, mpd->buffer);
+ mpd->state = POP_DELE;
+
+ case POP_DELE:
+ /* Send DELETE. */
+ status = pop_write (mpd);
+ CHECK_EAGAIN (mpd, status);
+ mpd->state = POP_DELE_ACK;
+
+ case POP_DELE_ACK:
+ /* Ack Delete. */
+ status = pop_read_ack (mpd);
+ CHECK_EAGAIN (mpd, status);
+ MAILBOX_DEBUG0 (mbox, MU_DEBUG_PROT, mpd->buffer);
+ if (strncasecmp (mpd->buffer, "+OK", 3) != 0)
+ {
+ CHECK_ERROR (mpd, ERANGE);
+ }
+ mpd->state = POP_NO_STATE;
+ break;
+
+ default:
+ /* mu_error ("pop_expunge: unknow state\n"); */
+ break;
+ } /* switch (state) */
+ } /* if attribute_is_deleted() */
+ } /* message_get_attribute() */
+ } /* for */
+ CLEAR_STATE (mpd);
+ /* Invalidate. But Really they should shutdown the channel POP protocol
+ is not meant for this like IMAP. */
+ mpd->is_updated = 0;
+ return 0;
+}
+
+/* Mailbox size ? It is part of the STAT command */
+static int
+pop_get_size (mailbox_t mbox, off_t *psize)
+{
+ pop_data_t mpd = mbox->data;
+ int status = 0;
+
+ if (mpd == NULL)
+ return EINVAL;
+
+ if (! pop_is_updated (mbox))
+ status = pop_messages_count (mbox, &mpd->size);
+ if (psize)
+ *psize = mpd->size;
+ return status;
+}
+
+/* Form the RFC:
+ "It is important to note that the octet count for a message on the
+ server host may differ from the octet count assigned to that message
+ due to local conventions for designating end-of-line. Usually,
+ during the AUTHORIZATION state of the POP3 session, the POP3 server
+ can calculate the size of each message in octets when it opens the
+ maildrop. For example, if the POP3 server host internally represents
+ end-of-line as a single character, then the POP3 server simply counts
+ each occurrence of this character in a message as two octets."
+
+ This is not perfect if we do not know the number of lines in the message
+ then the octets returned will not be correct so we do our best.
+ */
+static int
+pop_message_size (message_t msg, size_t *psize)
+{
+ pop_message_t mpm = message_get_owner (msg);
+ pop_data_t mpd;
+ int status = 0;
+ void *func = (void *)pop_message_size;
+ size_t num;
+
+ if (mpm == NULL)
+ return EINVAL;
+
+ /* Did we have it already ? */
+ if (mpm->message_size != 0)
+ {
+ *psize = mpm->message_size;
+ return 0;
+ }
+
+ mpd = mpm->mpd;
+ /* Busy ? */
+ CHECK_BUSY (mpd->mbox, mpd, func, msg);
+
+ /* Get the size. */
+ switch (mpd->state)
+ {
+ case POP_NO_STATE:
+ status = pop_writeline (mpd, "LIST %d\r\n", mpm->num);
+ CHECK_ERROR (mpd, status);
+ MAILBOX_DEBUG0 (mpd->mbox, MU_DEBUG_PROT, mpd->buffer);
+ mpd->state = POP_LIST;
+
+ case POP_LIST:
+ /* Send the LIST. */
+ status = pop_write (mpd);
+ CHECK_EAGAIN (mpd, status);
+ mpd->state = POP_LIST_ACK;
+
+ case POP_LIST_ACK:
+ /* Resp from LIST. */
+ status = pop_read_ack (mpd);
+ CHECK_EAGAIN (mpd, status);
+ MAILBOX_DEBUG0 (mpd->mbox, MU_DEBUG_PROT, mpd->buffer);
+ break;
+
+ default:
+ /*
+ mu_error ("pop_message_size state\n");
+ */
+ break;
+ }
+
+ status = sscanf (mpd->buffer, "+OK %d %d\n", &num, &mpm->message_size);
+ CLEAR_STATE (mpd);
+
+ if (status != 2)
+ status = EINVAL;
+
+ /* The size of the message is with the extra '\r' octet for everyline.
+ Substract to get, hopefully, a good count. */
+ if (psize)
+ *psize = mpm->message_size - (mpm->header_lines + mpm->body_lines);
+ return 0;
+}
+
+/* Another source of trouble, POP only gives the size of the message
+ not the size of subparts like headers, body etc .. Again we're doing
+ our best with what we know but the only way to get a precise number
+ is by dowloading the whole message. */
+static int
+pop_body_size (body_t body, size_t *psize)
+{
+ message_t msg = body_get_owner (body);
+ pop_message_t mpm = message_get_owner (msg);
+
+ if (mpm == NULL)
+ return EINVAL;
+
+ /* Did we have it already ? */
+ if (mpm->body_size != 0)
+ {
+ *psize = mpm->body_size;
+ }
+ else if (mpm->message_size != 0)
+ {
+ /* Take a guest. */
+ *psize = mpm->message_size - mpm->header_size - mpm->body_lines;
+ }
+ else
+ *psize = 0;
+
+ return 0;
+}
+
+/* Not know until the whole message get downloaded. */
+static int
+pop_body_lines (body_t body, size_t *plines)
+{
+ message_t msg = body_get_owner (body);
+ pop_message_t mpm = message_get_owner (msg);
+ if (mpm == NULL)
+ return EINVAL;
+ if (plines)
+ *plines = mpm->body_lines;
+ return 0;
+}
+
+/* Pop does not have any command for this, We fake by reading the "Status: "
+ header. But this is hackish some POP server(Qpopper) skip it. Also
+ because we call header_get_value the function may return EAGAIN... uncool.
+ To put it another way, many servers simply remove the "Status:" header
+ field, when you dowload a message, so a message will always look like
+ new even if you already read it. There is also no way to set an attribute
+ on remote mailbox via the POP server and many server once you do a RETR
+ and in some cases a TOP will mark the message as read; "Status: RO"
+ or maybe worst some ISP configure there servers to delete after
+ the RETR some go as much as deleting after the TOP, since technicaly
+ you can download a message via TOP without RET'reiving it. */
+static int
+pop_get_attribute (attribute_t attr, int *pflags)
+{
+ message_t msg = attribute_get_owner (attr);
+ pop_message_t mpm = message_get_owner (msg);
+ char hdr_status[64];
+ header_t header = NULL;
+
+ if (mpm == NULL || pflags == NULL)
+ return EINVAL;
+ if (mpm->attr_flags == 0)
+ {
+ hdr_status[0] = '\0';
+ message_get_header (mpm->message, &header);
+ header_get_value (header, "Status", hdr_status, sizeof hdr_status, NULL);
+ string_to_flags (hdr_status, &(mpm->attr_flags));
+ }
+ *pflags = mpm->attr_flags;
+ return 0;
+}
+
+static int
+pop_set_attribute (attribute_t attr, int flags)
+{
+ message_t msg = attribute_get_owner (attr);
+ pop_message_t mpm = message_get_owner (msg);
+
+ if (mpm == NULL)
+ return EINVAL;
+ mpm->attr_flags |= flags;
+ return 0;
+}
+
+static int
+pop_unset_attribute (attribute_t attr, int flags)
+{
+ message_t msg = attribute_get_owner (attr);
+ pop_message_t mpm = message_get_owner (msg);
+
+ if (mpm == NULL)
+ return EINVAL;
+ mpm->attr_flags &= ~flags;
+ return 0;
+}
+
+/* Stub to call the fd from body object. */
+static int
+pop_body_fd (stream_t stream, int *pfd)
+{
+ body_t body = stream_get_owner (stream);
+ message_t msg = body_get_owner (body);
+ pop_message_t mpm = message_get_owner (msg);
+ return pop_get_fd (mpm, pfd);
+}
+
+/* Stub to call the fd from message object. */
+static int
+pop_message_fd (stream_t stream, int *pfd)
+{
+ message_t msg = stream_get_owner (stream);
+ pop_message_t mpm = message_get_owner (msg);
+ return pop_get_fd (mpm, pfd);
+}
+
+/* Finally return the fd. */
+static int
+pop_get_fd (pop_message_t mpm, int *pfd)
+{
+ if (mpm && mpm->mpd && mpm->mpd->mbox)
+ return stream_get_fd (mpm->mpd->mbox->stream, pfd);
+ return EINVAL;
+}
+
+static int
+pop_uid (message_t msg, size_t *puid)
+{
+ pop_message_t mpm = message_get_owner (msg);
+ if (puid)
+ *puid = mpm->num;
+ return 0;
+}
+
+/* Get the UIDL. Client should be prepare since it may fail. UIDL is
+ optional on many POP servers.
+ FIXME: We should check this with CAPA and fall back to a md5 scheme ?
+ Or maybe check for "X-UIDL" a la Qpopper ? */
+static int
+pop_uidl (message_t msg, char *buffer, size_t buflen, size_t *pnwriten)
+{
+ pop_message_t mpm = message_get_owner (msg);
+ pop_data_t mpd;
+ int status = 0;
+ void *func = (void *)pop_uidl;
+ size_t num;
+ /* According to the RFC uidl's are no longer then 70 chars. Still playit
+ safe */
+ char uniq[128];
+
+ if (mpm == NULL)
+ return EINVAL;
+
+ /* Is it cache ? */
+ if (mpm->uidl)
+ {
+ size_t len = strlen (mpm->uidl);
+ if (buffer)
+ {
+ buflen--; /* Leave space for the null. */
+ buflen = (len > buflen) ? buflen : len;
+ memcpy (buffer, mpm->uidl, buflen);
+ buffer[buflen] = '\0';
+ }
+ else
+ buflen = len;
+ if (pnwriten)
+ *pnwriten = buflen;
+ return 0;
+ }
+
+ mpd = mpm->mpd;
+
+ /* Busy ? */
+ CHECK_BUSY (mpd->mbox, mpd, func, 0);
+
+ /* Get the UIDL. */
+ switch (mpd->state)
+ {
+ case POP_NO_STATE:
+ status = pop_writeline (mpd, "UIDL %d\r\n", mpm->num);
+ CHECK_ERROR (mpd, status);
+ MAILBOX_DEBUG0 (mpd->mbox, MU_DEBUG_PROT, mpd->buffer);
+ mpd->state = POP_UIDL;
+
+ case POP_UIDL:
+ /* Send the UIDL. */
+ status = pop_write (mpd);
+ CHECK_EAGAIN (mpd, status);
+ mpd->state = POP_UIDL_ACK;
+
+ case POP_UIDL_ACK:
+ /* Resp from UIDL. */
+ status = pop_read_ack (mpd);
+ CHECK_EAGAIN (mpd, status);
+ MAILBOX_DEBUG0 (mpd->mbox, MU_DEBUG_PROT, mpd->buffer);
+ break;
+
+ default:
+ /*
+ mu_error ("pop_uidl state\n");
+ */
+ break;
+ }
+
+ /* FIXME: I should cache the result. */
+ *uniq = '\0';
+ status = sscanf (mpd->buffer, "+OK %d %127s\n", &num, uniq);
+ if (status != 2)
+ {
+ status = EINVAL;
+ buflen = 0;
+ }
+ else
+ {
+ num = strlen (uniq);
+ uniq[num - 1] = '\0'; /* Nuke newline. */
+ if (buffer)
+ {
+ buflen--; /* Leave space for the null. */
+ buflen = (buflen < num) ? buflen : num;
+ memcpy (buffer, uniq, buflen);
+ buffer [buflen] = '\0';
+ }
+ else
+ buflen = num - 1; /* Do not count newline. */
+ mpm->uidl = strdup (uniq);
+ status = 0;
+ }
+
+ CLEAR_STATE (mpd);
+
+ if (pnwriten)
+ *pnwriten = buflen;
+ return status;
+}
+
+/* How we retrieve the headers. If it fails we jump to the pop_retr()
+ code .i.e send a RETR and skip the body, ugly.
+ NOTE: if the offset is different, flag an error, offset is meaningless
+ on a socket but we better warn them, some stuff like mime_t may try to
+ read ahead, for example for the headers. */
+static int
+pop_top (header_t header, char *buffer, size_t buflen,
+ off_t offset, size_t *pnread)
+{
+ message_t msg = header_get_owner (header);
+ pop_message_t mpm = message_get_owner (msg);
+ pop_data_t mpd;
+ size_t nread = 0;
+ int status = 0;
+ void *func = (void *)pop_top;
+
+ if (mpm == NULL)
+ return EINVAL;
+
+ mpd = mpm->mpd;
+
+ /* Busy ? */
+ CHECK_BUSY (mpd->mbox, mpd, func, msg);
+
+ /* We start fresh then reset the sizes. */
+ if (mpd->state == POP_NO_STATE)
+ mpm->header_size = 0;
+
+ /* Throw an error if trying to seek back. */
+ if ((size_t)offset < mpm->header_size)
+ return ESPIPE;
+
+ /* Get the header. */
+ switch (mpd->state)
+ {
+ case POP_NO_STATE:
+ /* TOP is an optionnal command, if we want to be compliant we can not
+ count on it to exists. So we should be prepare when it fails and
+ fall to a second scheme. */
+ status = pop_writeline (mpd, "TOP %d 0\r\n", mpm->num);
+ CHECK_ERROR (mpd, status);
+ MAILBOX_DEBUG0 (mpd->mbox, MU_DEBUG_PROT, mpd->buffer);
+ mpd->state = POP_TOP;
+
+ case POP_TOP:
+ /* Send the TOP. */
+ status = pop_write (mpd);
+ CHECK_EAGAIN (mpd, status);
+ mpd->state = POP_TOP_ACK;
+
+ case POP_TOP_ACK:
+ /* Ack from TOP. */
+ status = pop_read_ack (mpd);
+ CHECK_EAGAIN (mpd, status);
+ MAILBOX_DEBUG0 (mpd->mbox, MU_DEBUG_PROT, mpd->buffer);
+ if (strncasecmp (mpd->buffer, "+OK", 3) != 0)
+ {
+ /* mu_error ("TOP not implemented\n"); */
+ /* Fall back to RETR call. */
+ mpd->state = POP_NO_STATE;
+ mpm->skip_header = 0;
+ mpm->skip_body = 1;
+ return pop_retr (mpm, buffer, buflen, offset, pnread);
+ }
+ mpd->state = POP_TOP_RX;
+
+ case POP_TOP_RX:
+ /* Get the header. */
+ do
+ {
+ /* Seek in position. */
+ ssize_t pos = offset - mpm->header_size;
+ /* Do we need to fill up. */
+ if (mpd->nl == NULL || mpd->ptr == mpd->buffer)
+ {
+ status = pop_readline (mpd);
+ CHECK_EAGAIN (mpd, status);
+ mpm->header_lines++;
+ }
+ /* If we have to skip some data to get to the offset. */
+ if (pos > 0)
+ nread = fill_buffer (mpd, NULL, pos);
+ else
+ nread = fill_buffer (mpd, buffer, buflen);
+ mpm->header_size += nread;
+ }
+ while (nread > 0 && (size_t)offset > mpm->header_size);
+ break;
+
+ default:
+ /* Probaly TOP was not supported so we have fall back to RETR. */
+ mpm->skip_header = 0;
+ mpm->skip_body = 1;
+ return pop_retr (mpm, buffer, buflen, offset, pnread);
+ } /* switch (state) */
+
+ if (nread == 0)
+ {
+ CLEAR_STATE (mpd);
+ }
+ if (pnread)
+ *pnread = nread;
+ return 0;
+}
+
+/* This is no longer use, see pop_top to retreive headers, we still
+ keep it around for debugging purposes. */
+#if 0
+/* Stub to call pop_retr (). Call form the stream object of the header. */
+static int
+pop_header_read (header_t header, char *buffer, size_t buflen, off_t offset,
+ size_t *pnread)
+{
+ message_t msg = header_get_owner (header);
+ pop_message_t mpm = message_get_owner (msg);
+ pop_data_t mpd;
+ void *func = (void *)pop_header_read;
+
+ if (mpm == NULL)
+ return EINVAL;
+
+ mpd = mpm->mpd;
+
+ /* Busy ? */
+ CHECK_BUSY (mpd->mbox, mpd, func, msg);
+
+ /* We start fresh then reset the sizes. */
+ if (mpd->state == POP_NO_STATE)
+ mpm->header_size = mpm->inbody = 0;
+
+ /* Throw an error if trying to seek back. */
+ if ((size_t)offset < mpm->header_size)
+ return ESPIPE;
+
+ mpm->skip_header = 0;
+ mpm->skip_body = 1;
+ return pop_retr (mpm, buffer, buflen, offset, pnread);
+}
+#endif
+
+/* Stub to call pop_retr (). Call from the stream object of the body. */
+static int
+pop_body_read (stream_t is, char *buffer, size_t buflen, off_t offset,
+ size_t *pnread)
+{
+ body_t body = stream_get_owner (is);
+ message_t msg = body_get_owner (body);
+ pop_message_t mpm = message_get_owner (msg);
+ pop_data_t mpd;
+ void *func = (void *)pop_body_read;
+
+ if (mpm == NULL)
+ return EINVAL;
+
+ mpd = mpm->mpd;
+
+ /* Busy ? */
+ CHECK_BUSY (mpd->mbox, mpd, func, msg);
+
+ /* We start fresh then reset the sizes. */
+ if (mpd->state == POP_NO_STATE)
+ mpm->body_size = mpm->inbody = 0;
+
+ /* Can not seek back this a stream socket. */
+ if ((size_t)offset < mpm->body_size)
+ return ESPIPE;
+
+ mpm->skip_header = 1;
+ mpm->skip_body = 0;
+ return pop_retr (mpm, buffer, buflen, offset, pnread);
+}
+
+/* Stub to call pop_retr (), calling from the stream object of a message. */
+static int
+pop_message_read (stream_t is, char *buffer, size_t buflen, off_t offset,
+ size_t *pnread)
+{
+ message_t msg = stream_get_owner (is);
+ pop_message_t mpm = message_get_owner (msg);
+ pop_data_t mpd;
+ void *func = (void *)pop_message_read;
+
+ if (mpm == NULL)
+ return EINVAL;
+
+ mpd = mpm->mpd;
+
+ /* Busy ? */
+ CHECK_BUSY (mpd->mbox, mpd, func, msg);
+
+ /* We start fresh then reset the sizes. */
+ if (mpd->state == POP_NO_STATE)
+ mpm->header_size = mpm->body_size = mpm->inbody = 0;
+
+ /* Can not seek back this is a stream socket. */
+ if ((size_t)offset < (mpm->body_size + mpm->header_size))
+ return ESPIPE;
+
+ mpm->skip_header = mpm->skip_body = 0;
+ return pop_retr (mpm, buffer, buflen, offset, pnread);
+}
+
+/* Little helper to fill the buffer without overflow. */
+static int
+fill_buffer (pop_data_t mpd, char *buffer, size_t buflen)
+{
+ int nleft, n, nread = 0;
+
+ /* How much we can copy ? */
+ n = mpd->ptr - mpd->buffer;
+ nleft = buflen - n;
+
+ /* We got more then requested. */
+ if (nleft < 0)
+ {
+ size_t sentinel;
+ nread = buflen;
+ sentinel = mpd->ptr - (mpd->buffer + nread);
+ if (buffer)
+ memcpy (buffer, mpd->buffer, nread);
+ memmove (mpd->buffer, mpd->buffer + nread, sentinel);
+ mpd->ptr = mpd->buffer + sentinel;
+ }
+ else
+ {
+ /* Drain our buffer. */;
+ nread = n;
+ if (buffer)
+ memcpy (buffer, mpd->buffer, nread);
+ mpd->ptr = mpd->buffer;
+ }
+
+ return nread;
+}
+
+/* The heart of most funtions. Send the RETR and skip different parts. */
+static int
+pop_retr (pop_message_t mpm, char *buffer, size_t buflen, off_t offset,
+ size_t *pnread)
+{
+ pop_data_t mpd;
+ size_t nread = 0;
+ int status = 0;
+ size_t oldbuflen = buflen;
+
+ /* Meaningless. */
+ (void)offset;
+
+ mpd = mpm->mpd;
+
+ if (pnread)
+ *pnread = nread;
+
+ /* Take care of the obvious. */
+ if (buffer == NULL || buflen == 0)
+ {
+ CLEAR_STATE (mpd);
+ return 0;
+ }
+
+ /* pop_retr() is not call directly so we assume that the locks were set. */
+
+ switch (mpd->state)
+ {
+ case POP_NO_STATE:
+ mpm->body_lines = mpm->body_size = 0;
+ status = pop_writeline (mpd, "RETR %d\r\n", mpm->num);
+ MAILBOX_DEBUG0 (mpd->mbox, MU_DEBUG_PROT, mpd->buffer);
+ CHECK_ERROR (mpd, status);
+ mpd->state = POP_RETR;
+
+ case POP_RETR:
+ /* Send the RETR command. */
+ status = pop_write (mpd);
+ CHECK_EAGAIN (mpd, status);
+ mpd->state = POP_RETR_ACK;
+
+ case POP_RETR_ACK:
+ /* RETR ACK. */
+ status = pop_read_ack (mpd);
+ CHECK_EAGAIN (mpd, status);
+ MAILBOX_DEBUG0 (mpd->mbox, MU_DEBUG_PROT, mpd->buffer);
+
+ if (strncasecmp (mpd->buffer, "+OK", 3) != 0)
+ {
+ CHECK_ERROR (mpd, EACCES);
+ }
+ mpd->state = POP_RETR_RX_HDR;
+
+ case POP_RETR_RX_HDR:
+ /* Skip/Take the header. */
+ while (!mpm->inbody)
+ {
+ /* Do we need to fill up. */
+ if (mpd->nl == NULL || mpd->ptr == mpd->buffer)
+ {
+ status = pop_readline (mpd);
+ if (status != 0)
+ {
+ /* Do we have something in the buffer flush it first. */
+ if (buflen != oldbuflen)
+ return 0;
+ CHECK_EAGAIN (mpd, status);
+ }
+ mpm->header_lines++;
+ }
+ /* Oops !! Hello houston we have a major problem here. */
+ if (mpd->buffer[0] == '\0')
+ {
+ /* Still Do the right thing. */
+ if (buflen != oldbuflen)
+ {
+ CLEAR_STATE (mpd);
+ }
+ else
+ mpd->state = POP_STATE_DONE;
+ return 0;
+ }
+ /* The problem is that we are using RETR instead of TOP to retreive
+ headers, i.e the server contacted does not support it. So we
+ have to make sure that the next line really means end of the
+ headers. Still some cases we may loose. But 99.9% of POPD
+ encounter support TOP. In the 0.1% case get GNU pop3d, or the
+ hack below will suffice. */
+ if (mpd->buffer[0] == '\n' && mpd->buffer[1] == '\0')
+ mpm->inbody = 1; /* break out of the while. */
+ if (!mpm->skip_header)
+ {
+ ssize_t pos = offset - mpm->header_size;
+ if (pos > 0)
+ {
+ nread = fill_buffer (mpd, NULL, pos);
+ mpm->header_size += nread;
+ }
+ else
+ {
+ nread = fill_buffer (mpd, buffer, buflen);
+ mpm->header_size += nread;
+ if (pnread)
+ *pnread += nread;
+ buflen -= nread ;
+ if (buflen > 0)
+ buffer += nread;
+ else
+ return 0;
+ }
+ }
+ else
+ mpd->ptr = mpd->buffer;
+ }
+ mpd->state = POP_RETR_RX_BODY;
+
+ case POP_RETR_RX_BODY:
+ /* Start/Take the body. */
+ while (mpm->inbody)
+ {
+ /* Do we need to fill up. */
+ if (mpd->nl == NULL || mpd->ptr == mpd->buffer)
+ {
+ status = pop_readline (mpd);
+ if (status != 0)
+ {
+ /* Flush The Buffer ? */
+ if (buflen != oldbuflen)
+ return 0;
+ CHECK_EAGAIN (mpd, status);
+ }
+ mpm->body_lines++;
+ }
+
+ if (mpd->buffer[0] == '\0')
+ mpm->inbody = 0; /* Breakout of the while. */
+
+ if (!mpm->skip_body)
+ {
+ /* If we did not skip the header, it means that we are
+ downloading the entire message and the header_size should be
+ part of the offset count. */
+ ssize_t pos = offset - (mpm->body_size + ((mpm->skip_header) ?
+ 0 : mpm->header_size));
+ if (pos > 0)
+ {
+ nread = fill_buffer (mpd, NULL, pos);
+ mpm->body_size += nread;
+ }
+ else
+ {
+ nread = fill_buffer (mpd, buffer, buflen);
+ mpm->body_size += nread;
+ if (pnread)
+ *pnread += nread;
+ buflen -= nread ;
+ if (buflen > 0)
+ buffer += nread;
+ else
+ return 0;
+ }
+ }
+ else
+ {
+ mpm->body_size += (mpd->ptr - mpd->buffer);
+ mpd->ptr = mpd->buffer;
+ }
+ }
+ mpm->message_size = mpm->body_size + mpm->header_size;
+ mpd->state = POP_STATE_DONE;
+ /* Return here earlier, because we want to return nread = 0 to notify
+ the callee that we've finish, since there is already data
+ we have to return them first and _then_ tell them its finish. If we
+ don't we will start over again by sending another RETR. */
+ if (buflen != oldbuflen)
+ return 0;
+
+ case POP_STATE_DONE:
+ /* A convenient break, this is here we can return 0, we're done. */
+
+ default:
+ /* mu_error ("pop_retr unknow state\n"); */
+ break;
+ } /* Switch state. */
+
+ CLEAR_STATE (mpd);
+ mpm->skip_header = mpm->skip_body = 0;
+ return 0;
+}
+
+/* Extract the User from the URL or the ticket. */
+static int
+pop_get_user (authority_t auth)
+{
+ folder_t folder = authority_get_owner (auth);
+ mailbox_t mbox = folder->data;
+ pop_data_t mpd = mbox->data;
+ ticket_t ticket = NULL;
+ int status;
+ /* Fetch the user from them. */
+ size_t n = 0;
+
+ authority_get_ticket (auth, &ticket);
+ if (mpd->user)
+ {
+ free (mpd->user);
+ mpd->user = NULL;
+ }
+ /* Was it in the URL? */
+ status = url_get_user (mbox->url, NULL, 0, &n);
+ if (status != 0 || n == 0)
+ ticket_pop (ticket, mbox->url, "Pop User: ", &mpd->user);
+ else
+ {
+ mpd->user = calloc (1, n + 1);
+ url_get_user (mbox->url, mpd->user, n + 1, NULL);
+ }
+ return 0;
+}
+
+/* Extract the User from the URL or the ticket. */
+static int
+pop_get_passwd (authority_t auth)
+{
+ folder_t folder = authority_get_owner (auth);
+ mailbox_t mbox = folder->data;
+ pop_data_t mpd = mbox->data;
+ ticket_t ticket = NULL;
+ int status;
+ /* Fetch the user from them. */
+ size_t n = 0;
+
+ authority_get_ticket (auth, &ticket);
+ if (mpd->passwd)
+ {
+ free (mpd->passwd);
+ mpd->passwd = NULL;
+ }
+ /* Was it in the URL? */
+ status = url_get_passwd (mbox->url, NULL, 0, &n);
+ if (status != 0 || n == 0)
+ ticket_pop (ticket, mbox->url, "Pop Passwd: ", &mpd->passwd);
+ else
+ {
+ mpd->passwd = calloc (1, n + 1);
+ url_get_passwd (mbox->url, mpd->passwd, n + 1, NULL);
+ }
+ return 0;
+}
+
+
+static char *
+pop_get_timestamp (pop_data_t mpd)
+{
+ char *right, *left;
+ char *timestamp = NULL;
+ size_t len;
+
+ len = strlen (mpd->buffer);
+ right = memchr (mpd->buffer, '<', len);
+ if (right)
+ {
+ len = len - (right - mpd->buffer);
+ left = memchr (right, '>', len);
+ if (left)
+ {
+ len = left - right + 1;
+ timestamp = calloc (len + 1, 1);
+ if (timestamp != NULL)
+ {
+ memcpy (timestamp, right, len);
+ }
+ }
+ }
+ return timestamp;
+}
+
+/* Make the MD5 string. */
+static int
+pop_get_md5 (pop_data_t mpd)
+{
+ MD5_CTX md5context;
+ unsigned char md5digest[16];
+ char digest[64]; /* Really it just has to be 32 + 1(null). */
+ char *tmp;
+ size_t n;
+ char *timestamp;
+
+ timestamp = pop_get_timestamp (mpd);
+ if (timestamp == NULL)
+ return EINVAL;
+
+ MD5Init (&md5context);
+ MD5Update (&md5context, (unsigned char *)timestamp, strlen (timestamp));
+ MD5Update (&md5context, (unsigned char *)mpd->passwd, strlen (mpd->passwd));
+ MD5Final (md5digest, &md5context);
+ for (tmp = digest, n = 0; n < 16; n++, tmp += 2)
+ sprintf (tmp, "%02x", md5digest[n]);
+ *tmp = '\0';
+ free (timestamp);
+ free (mpd->passwd);
+ mpd->passwd = strdup (digest);
+ return 0;
+}
+
+/* GRRRRR!! We can not use sleep in the library since this we'll
+ muck up any alarm() done by the user. */
+static int
+pop_sleep (int seconds)
+{
+ struct timeval tval;
+ tval.tv_sec = seconds;
+ tval.tv_usec = 0;
+ return select (1, NULL, NULL, NULL, &tval);
+}
+
+/* C99 says that a conforming implementation of snprintf () should return the
+ number of char that would have been call but many old GNU/Linux && BSD
+ implementations return -1 on error. Worse QnX/Neutrino actually does not
+ put the terminal null char. So let's try to cope. */
+static int
+pop_writeline (pop_data_t mpd, const char *format, ...)
+{
+ int len;
+ va_list ap;
+ int done = 1;
+
+ if (mpd->buffer == NULL)
+ return EINVAL;
+ va_start(ap, format);
+ do
+ {
+ len = vsnprintf (mpd->buffer, mpd->buflen - 1, format, ap);
+ if (len < 0 || len >= (int)mpd->buflen
+ || !memchr (mpd->buffer, '\0', len + 1))
+ {
+ mpd->buflen *= 2;
+ mpd->buffer = realloc (mpd->buffer, mpd->buflen);
+ if (mpd->buffer == NULL)
+ return ENOMEM;
+ done = 0;
+ }
+ else
+ done = 1;
+ }
+ while (!done);
+ va_end(ap);
+ mpd->ptr = mpd->buffer + len;
+ return 0;
+}
+
+/* A socket may write less then expected and we have to cope with nonblocking.
+ if the write failed we keep track and restart where left. */
+static int
+pop_write (pop_data_t mpd)
+{
+ int status = 0;
+ if (mpd->ptr > mpd->buffer)
+ {
+ size_t len;
+ size_t n = 0;
+ len = mpd->ptr - mpd->buffer;
+ status = stream_write (mpd->mbox->stream, mpd->buffer, len, 0, &n);
+ if (status == 0)
+ {
+ memmove (mpd->buffer, mpd->buffer + n, len - n);
+ mpd->ptr -= n;
+ }
+ }
+ else
+ mpd->ptr = mpd->buffer;
+ return status;
+}
+
+/* Call readline and reset the mpd->ptr to the buffer, signalling that we have
+ done the read to completion. */
+static int
+pop_read_ack (pop_data_t mpd)
+{
+ int status = pop_readline (mpd);
+ if (status == 0)
+ mpd->ptr = mpd->buffer;
+ return status;
+}
+
+/* Read a complete line form the pop server. Transform CRLF to LF, remove
+ the stuff byte termination octet ".", put a null in the buffer
+ when done. */
+static int
+pop_readline (pop_data_t mpd)
+{
+ size_t n = 0;
+ size_t total = mpd->ptr - mpd->buffer;
+ int status;
+
+ /* Must get a full line before bailing out. */
+ do
+ {
+ status = stream_readline (mpd->mbox->stream, mpd->buffer + total,
+ mpd->buflen - total, mpd->offset, &n);
+ if (status != 0)
+ return status;
+
+ /* The server went away: It maybe a timeout and some pop server
+ does not send the -ERR. Consider this like an error. */
+ if (n == 0)
+ return EIO;
+
+ total += n;
+ mpd->offset += n;
+ mpd->nl = memchr (mpd->buffer, '\n', total);
+ if (mpd->nl == NULL) /* Do we have a full line. */
+ {
+ /* Allocate a bigger buffer ? */
+ if (total >= mpd->buflen -1)
+ {
+ mpd->buflen *= 2;
+ mpd->buffer = realloc (mpd->buffer, mpd->buflen + 1);
+ if (mpd->buffer == NULL)
+ return ENOMEM;
+ }
+ }
+ mpd->ptr = mpd->buffer + total;
+ }
+ while (mpd->nl == NULL);
+
+ /* When examining a multi-line response, the client checks to see if the
+ line begins with the termination octet "."(DOT). If yes and if octets
+ other than CRLF follow, the first octet of the line (the termination
+ octet) is stripped away. */
+ if (total >= 3 && mpd->buffer[0] == '.')
+ {
+ if (mpd->buffer[1] != '\r' && mpd->buffer[2] != '\n')
+ {
+ memmove (mpd->buffer, mpd->buffer + 1, total - 1);
+ mpd->ptr--;
+ mpd->nl--;
+ }
+ /* And if CRLF immediately follows the termination character, then the
+ response from the POP server is ended and the line containing
+ ".CRLF" is not considered part of the multi-line response. */
+ else if (mpd->buffer[1] == '\r' && mpd->buffer[2] == '\n')
+ {
+ mpd->buffer[0] = '\0';
+ mpd->ptr = mpd->buffer;
+ mpd->nl = NULL;
+ }
+ }
+ /* \r\n --> \n\0, conversion. */
+ if (mpd->nl > mpd->buffer)
+ {
+ *(mpd->nl - 1) = '\n';
+ *(mpd->nl) = '\0';
+ mpd->ptr = mpd->nl;
+ }
+ return 0;
+}
+
+#endif
diff --git a/mailbox/pop/url.c b/mailbox/pop/url.c
new file mode 100644
index 000000000..12de5ce40
--- /dev/null
+++ b/mailbox/pop/url.c
@@ -0,0 +1,76 @@
+/* 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 */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifdef ENABLE_POP
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+
+#include <url0.h>
+#include <registrar0.h>
+
+static void url_pop_destroy (url_t url);
+
+static void
+url_pop_destroy (url_t url)
+{
+ (void)url;
+}
+
+/*
+ POP URL:
+ pop://[<user>[;AUTH=<auth>]@]<host>[:<port>]
+ or:
+ pop://[<user>[:pass]@]<host>[:<port>]
+*/
+
+int
+_url_pop_init (url_t url)
+{
+ int status = 0;
+
+ url->_destroy = url_pop_destroy;
+
+ status = url_parse(url);
+
+ if(status)
+ return status;
+
+ /* is it pop? */
+ if (strcmp ("pop", url->scheme) != 0)
+ return EINVAL;
+
+ /* not valid in a pop url */
+ if (url->path || url->query || !url->host)
+ return EINVAL;
+
+ if (url->port == 0)
+ url->port = MU_POP_PORT;
+
+ return status;
+}
+
+#endif

Return to:

Send suggestions and report system problems to the System administrator.