diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2003-03-13 13:35:33 +0000 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2003-03-13 13:35:33 +0000 |
commit | 11bc148b1877b7c692f28d90847bc74737068416 (patch) | |
tree | f7ab6afb152098c504a5a30623efdf38d9837d59 | |
parent | 887e7b3b191eccce681c68947204abad634f8553 (diff) | |
download | mailutils-11bc148b1877b7c692f28d90847bc74737068416.tar.gz mailutils-11bc148b1877b7c692f28d90847bc74737068416.tar.bz2 |
'Moved from ../'
-rw-r--r-- | mailbox/imap/folder.c | 2283 | ||||
-rw-r--r-- | mailbox/imap/mbox.c | 2256 | ||||
-rw-r--r-- | mailbox/imap/url.c | 87 | ||||
-rw-r--r-- | mailbox/maildir/mbox.c | 224 | ||||
-rw-r--r-- | mailbox/mbox/folder.c | 427 | ||||
-rw-r--r-- | mailbox/mbox/mbox.c | 1753 | ||||
-rw-r--r-- | mailbox/mbox/mboxscan.c | 856 | ||||
-rw-r--r-- | mailbox/mbox/url.c | 258 | ||||
-rw-r--r-- | mailbox/mh/folder.c | 56 | ||||
-rw-r--r-- | mailbox/mh/url.c | 77 | ||||
-rw-r--r-- | mailbox/pop/folder.c | 138 | ||||
-rw-r--r-- | mailbox/pop/mbox.c | 2115 | ||||
-rw-r--r-- | mailbox/pop/url.c | 76 |
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)) |