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))