summaryrefslogtreecommitdiffstats
path: root/imap
diff options
context:
space:
mode:
authorThomas Roessler <roessler@does-not-exist.org>1999-08-20 08:24:01 +0000
committerThomas Roessler <roessler@does-not-exist.org>1999-08-20 08:24:01 +0000
commit9febf2c9a98ebcebded37065bc6058adfb0847a9 (patch)
treec6f06f934b0b32f38486a743e0596da140ee08d6 /imap
parentd79ed35520d7d5a3658e309af0e3e00d30ddf45d (diff)
Brendan Cully's latest IMAP clean-up.
Diffstat (limited to 'imap')
-rw-r--r--imap/Makefile.am8
-rw-r--r--imap/auth.c291
-rw-r--r--imap/browse.c486
-rw-r--r--imap/imap.c2274
-rw-r--r--imap/imap.h41
-rw-r--r--imap/imap_private.h184
-rw-r--r--imap/imap_socket.h44
-rw-r--r--imap/md5.h49
-rw-r--r--imap/md5c.c330
-rw-r--r--imap/socket.c139
10 files changed, 3846 insertions, 0 deletions
diff --git a/imap/Makefile.am b/imap/Makefile.am
new file mode 100644
index 00000000..70ed6542
--- /dev/null
+++ b/imap/Makefile.am
@@ -0,0 +1,8 @@
+## Process this file with automake to produce Makefile.in
+
+AUTOMAKE_OPTIONS = foreign
+
+noinst_LIBRARIES = libimap.a
+noinst_HEADERS = imap_private.h imap_socket.h md5.h
+
+libimap_a_SOURCES = imap.c imap.h auth.c browse.c md5c.c socket.c
diff --git a/imap/auth.c b/imap/auth.c
new file mode 100644
index 00000000..e04c6212
--- /dev/null
+++ b/imap/auth.c
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ * Copyright (C) 1996-9 Brandon Long <blong@fiction.net>
+ * Copyright (C) 1999 Brendan Cully <brendan@kublai.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* IMAP login/authentication code */
+
+#include "mutt.h"
+#include "imap_private.h"
+#include "md5.h"
+
+#define MD5_BLOCK_LEN 64
+#define MD5_DIGEST_LEN 16
+
+/* forward declarations */
+static void hmac_md5 (const char* password, char* challenge,
+ unsigned char* response);
+static int imap_auth_cram_md5 (IMAP_DATA* idata, const char* user,
+ const char* pass);
+
+/* hmac_md5: produce CRAM-MD5 challenge response. */
+static void hmac_md5 (const char* password, char* challenge,
+ unsigned char* response)
+{
+ MD5_CTX ctx;
+ unsigned char ipad[MD5_BLOCK_LEN], opad[MD5_BLOCK_LEN];
+ unsigned char secret[MD5_BLOCK_LEN+1];
+ unsigned char hash_passwd[MD5_DIGEST_LEN];
+ unsigned int secret_len, chal_len;
+ int i;
+
+ secret_len = strlen (password);
+ chal_len = strlen (challenge);
+
+ /* passwords longer than MD5_BLOCK_LEN bytes are substituted with their MD5
+ * digests */
+ if (secret_len > MD5_BLOCK_LEN)
+ {
+ MD5Init (&ctx);
+ MD5Update (&ctx, (unsigned char*) password, secret_len);
+ MD5Final (hash_passwd, &ctx);
+ strfcpy ((char*) secret, (char*) hash_passwd, MD5_DIGEST_LEN);
+ secret_len = MD5_DIGEST_LEN;
+ }
+ else
+ strfcpy (secret, password, sizeof (secret));
+
+ memset (ipad, 0, sizeof (ipad));
+ memset (opad, 0, sizeof (opad));
+ memcpy (ipad, secret, secret_len);
+ memcpy (opad, secret, secret_len);
+
+ for (i = 0; i < MD5_BLOCK_LEN; i++)
+ {
+ ipad[i] ^= 0x36;
+ opad[i] ^= 0x5c;
+ }
+
+ /* inner hash: challenge and ipadded secret */
+ MD5Init (&ctx);
+ MD5Update (&ctx, ipad, MD5_BLOCK_LEN);
+ MD5Update (&ctx, (unsigned char*) challenge, chal_len);
+ MD5Final (response, &ctx);
+
+ /* outer hash: inner hash and opadded secret */
+ MD5Init (&ctx);
+ MD5Update (&ctx, opad, MD5_BLOCK_LEN);
+ MD5Update (&ctx, response, MD5_DIGEST_LEN);
+ MD5Final (response, &ctx);
+}
+
+/* imap_auth_cram_md5: AUTH=CRAM-MD5 support. Used unconditionally if the
+ * server supports it */
+static int imap_auth_cram_md5 (IMAP_DATA* idata, const char* user,
+ const char* pass)
+{
+ char ibuf[LONG_STRING], obuf[LONG_STRING];
+ unsigned char hmac_response[MD5_DIGEST_LEN];
+ int len;
+ char seq[16];
+
+ dprint (2, (debugfile, "Attempting CRAM-MD5 login...\n"));
+ mutt_message _("Logging in (CRAM-MD5)...");
+ imap_make_sequence (seq, sizeof (seq));
+ snprintf (obuf, LONG_STRING, "%s AUTHENTICATE CRAM-MD5\r\n", seq);
+ mutt_socket_write (idata->conn, obuf);
+
+ /* From RFC 2195:
+ * The data encoded in the first ready response contains a presumptively
+ * arbitrary string of random digits, a timestamp, and the fully-qualified
+ * primary host name of the server. The syntax of the unencoded form must
+ * correspond to that of an RFC 822 'msg-id' [RFC822] as described in [POP3].
+ */
+ if (mutt_socket_read_line_d (ibuf, LONG_STRING, idata->conn) < 0)
+ {
+ dprint (1, (debugfile, "Error receiving server response.\n"));
+
+ return -1;
+ }
+
+ if (ibuf[0] != '+')
+ {
+ dprint (1, (debugfile, "Invalid response from server: %s\n", ibuf));
+
+ return -1;
+ }
+
+ if ((len = mutt_from_base64 (obuf, ibuf + 2)) == -1)
+ {
+ dprint (1, (debugfile, "Error decoding base64 response.\n"));
+
+ return -1;
+ }
+
+ obuf[len] = '\0';
+ dprint (2, (debugfile, "CRAM challenge: %s\n", obuf));
+
+ /* The client makes note of the data and then responds with a string
+ * consisting of the user name, a space, and a 'digest'. The latter is
+ * computed by applying the keyed MD5 algorithm from [KEYED-MD5] where the
+ * key is a shared secret and the digested text is the timestamp (including
+ * angle-brackets).
+ *
+ * (Note: it's unspecified whether the user name needs IMAP quoting.)
+ */
+ hmac_md5 (pass, obuf, hmac_response);
+ dprint (2, (debugfile, "CRAM response: %s,[%s]->", obuf, pass));
+ /* dubious optimisation I saw elsewhere: make the whole string in one call */
+ snprintf (obuf, sizeof (obuf),
+ "%s %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
+ user,
+ hmac_response[0], hmac_response[1], hmac_response[2], hmac_response[3],
+ hmac_response[4], hmac_response[5], hmac_response[6], hmac_response[7],
+ hmac_response[8], hmac_response[9], hmac_response[10], hmac_response[11],
+ hmac_response[12], hmac_response[13], hmac_response[14], hmac_response[15]);
+ dprint(2, (debugfile, "%s\n", obuf));
+
+ mutt_to_base64 ((unsigned char*) ibuf, (unsigned char*) obuf, strlen (obuf));
+ strcpy (ibuf + strlen (ibuf), "\r\n");
+ mutt_socket_write (idata->conn, ibuf);
+
+ if (mutt_socket_read_line_d (ibuf, LONG_STRING, idata->conn) < 0)
+ {
+ dprint (1, (debugfile, "Error receiving server response.\n"));
+
+ return -1;
+ }
+
+ if (imap_code (ibuf))
+ {
+ dprint (2, (debugfile, "CRAM login complete.\n"));
+
+ return 0;
+ }
+
+ dprint (2, (debugfile, "CRAM login failed.\n"));
+ return -1;
+}
+
+/* imap_authenticate: loop until success or user abort. At each loop, all
+ * supported authentication methods are tried, from strongest to weakest.
+ * Currently available:
+ * CRAM-MD5: like APOP or CHAP. Safe against replay and sniffing, but
+ * requires that your key be stored on the server, readable by the
+ * server account. UW-IMAP supports this method since at least 4.5, if
+ * the key file exists on the server.
+ * LOGIN: ugh. Don't use this if you can help it. Uses cleartext password
+ * exchange, furthermore uses unix login techniques so this same password
+ * can be used to log in to the server or others that share the
+ * credentials database.
+ * Pending:
+ * GSSAPI: strongest available form, requires Kerberos V infrastructure,
+ * or possibly alternatively Heimdal.
+ * Unavailable:
+ * KERBEROS_V4. Superceded by GSSAPI.
+ */
+int imap_authenticate (IMAP_DATA *idata, CONNECTION *conn)
+{
+ char buf[LONG_STRING];
+ char user[SHORT_STRING], q_user[SHORT_STRING];
+ char ckey[SHORT_STRING];
+ char pass[SHORT_STRING], q_pass[SHORT_STRING];
+ char seq[16];
+
+ int r = 1;
+
+ while (r != 0)
+ {
+ if (!ImapUser)
+ {
+ strfcpy (user, NONULL(Username), sizeof (user));
+ if (mutt_get_field (_("IMAP Username: "), user, sizeof (user), 0) != 0 ||
+ !user[0])
+ {
+ user[0] = 0;
+ return (-1);
+ }
+ }
+ else
+ strfcpy (user, ImapUser, sizeof (user));
+
+ /* attempt CRAM-MD5 if available */
+ if (mutt_bit_isset (idata->capabilities, ACRAM_MD5))
+ {
+ if (!ImapCRAMKey)
+ {
+ ckey[0] = '\0';
+ snprintf (buf, sizeof (buf), _("CRAM key for %s@%s: "), user,
+ conn->server);
+ if (mutt_get_field (buf, ckey, sizeof (ckey), M_PASS) != 0 ||
+ !ckey[0])
+ return -1;
+ else
+ /* strip CR */
+ ckey[strlen (ckey)] = '\0';
+ }
+ else
+ strfcpy (ckey, ImapCRAMKey, sizeof (ckey));
+
+ if ((r = imap_auth_cram_md5 (idata, user, ckey)))
+ {
+ mutt_error _("CRAM-MD5 login failed.");
+ sleep (1);
+ }
+ else
+ return 0;
+ }
+ else
+ dprint (2, (debugfile, "CRAM-MD5 authentication is not supported\n"));
+
+ if (!ImapPass)
+ {
+ pass[0]=0;
+ snprintf (buf, sizeof (buf), _("Password for %s@%s: "), user, conn->server);
+ if (mutt_get_field (buf, pass, sizeof (pass), M_PASS) != 0 ||
+ !pass[0])
+ {
+ return (-1);
+ }
+ }
+ else
+ strfcpy (pass, ImapPass, sizeof (pass));
+
+ imap_quote_string (q_user, sizeof (q_user), user);
+ imap_quote_string (q_pass, sizeof (q_pass), pass);
+
+ mutt_message _("Logging in...");
+ imap_make_sequence (seq, sizeof (seq));
+ snprintf (buf, sizeof (buf), "%s LOGIN %s %s\r\n", seq, q_user, q_pass);
+ r = imap_exec (buf, sizeof (buf), idata, seq, buf, IMAP_OK_FAIL);
+ if (r == -1)
+ {
+ /* connection or protocol problem */
+ imap_error ("imap_open_connection()", buf);
+ return (-1);
+ }
+ else if (r == -2)
+ {
+ /* Login failed, try again */
+ mutt_error _("Login failed.");
+ sleep (1);
+ FREE (&ImapUser);
+ FREE (&ImapPass);
+ }
+ else
+ {
+ /* If they have a successful login, we may as well cache the
+ * user/password. */
+ if (!ImapUser)
+ ImapUser = safe_strdup (user);
+ if (!ImapPass)
+ ImapPass = safe_strdup (pass);
+ }
+ }
+ return 0;
+}
diff --git a/imap/browse.c b/imap/browse.c
new file mode 100644
index 00000000..b6213f17
--- /dev/null
+++ b/imap/browse.c
@@ -0,0 +1,486 @@
+/*
+ * Copyright (C) 1996-9 Brandon Long <blong@fiction.net>
+ * Copyright (C) 1999 Brendan Cully <brendan@kublai.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* Mutt browser support routines */
+
+#include <stdlib.h>
+#include <ctype.h>
+
+#include "mutt.h"
+#include "imap.h"
+#include "imap_private.h"
+#include "imap_socket.h"
+
+/* -- forward declarations -- */
+static int add_list_result (CONNECTION *conn, const char *seq, const char *cmd,
+ struct browser_state *state, short isparent);
+static void imap_add_folder (char delim, char *folder, int noselect,
+ int noinferiors, struct browser_state *state, short isparent);
+static int compare_names(struct folder_file *a, struct folder_file *b);
+static int get_namespace (IMAP_DATA *idata, char *nsbuf, int nsblen,
+ IMAP_NAMESPACE_INFO *nsi, int nsilen, int *nns);
+static int verify_namespace (CONNECTION *conn, IMAP_NAMESPACE_INFO *nsi,
+ int nns);
+
+int imap_init_browse (char *path, struct browser_state *state)
+{
+ CONNECTION *conn;
+ IMAP_DATA *idata;
+ char buf[LONG_STRING];
+ char nsbuf[LONG_STRING];
+ char mbox[LONG_STRING];
+ char host[SHORT_STRING];
+ int port;
+ char list_cmd[5];
+ char seq[16];
+ char *ipath = NULL;
+ IMAP_NAMESPACE_INFO nsi[16];
+ int home_namespace = 0;
+ int n;
+ int i;
+ int nsup;
+ char ctmp;
+ int nns;
+ char *cur_folder;
+ short showparents = 0;
+ int noselect;
+ int noinferiors;
+
+ if (imap_parse_path (path, host, sizeof (host), &port, &ipath))
+ return (-1);
+
+ strfcpy (list_cmd, option (OPTIMAPLSUB) ? "LSUB" : "LIST", sizeof (list_cmd));
+
+ conn = mutt_socket_select_connection (host, port, 0);
+ idata = CONN_DATA;
+
+ if (!idata || (idata->state == IMAP_DISCONNECTED))
+ {
+ if (!idata)
+ {
+ /* The current connection is a new connection */
+ idata = safe_calloc (1, sizeof (IMAP_DATA));
+ conn->data = idata;
+ idata->conn = conn;
+ }
+ if (imap_open_connection (idata, conn))
+ return (-1);
+ }
+
+ if (ipath[0] == '\0')
+ {
+ home_namespace = 1;
+ mbox[0] = 0; /* Do not replace "" with "INBOX" here */
+ ipath = ImapHomeNamespace;
+ nns = 0;
+ if (mutt_bit_isset(idata->capabilities,NAMESPACE))
+ {
+ if (get_namespace (idata, nsbuf, sizeof (nsbuf),
+ nsi, sizeof (nsi), &nns) != 0)
+ return (-1);
+ if (verify_namespace (conn, nsi, nns) != 0)
+ return (-1);
+ }
+ if (!ipath) /* Any explicitly set imap_home_namespace wins */
+ {
+ for (i = 0; i < nns; i++)
+ if (nsi[i].listable &&
+ (nsi[i].type == IMAP_NS_PERSONAL || nsi[i].type == IMAP_NS_SHARED))
+ {
+ ipath = nsi->prefix;
+ nsi->home_namespace = 1;
+ break;
+ }
+ }
+ }
+
+ mutt_message _("Contacted server, getting folder list...");
+
+ if (ipath && ipath[0] != '\0')
+ {
+ imap_fix_path (idata, ipath, mbox, sizeof (mbox));
+ n = mutt_strlen (mbox);
+
+ dprint (3, (debugfile, "imap_init_browse: mbox: %s\n", mbox));
+
+ /* if our target exists, has inferiors and isn't selectable, enter it if we
+ * aren't already going to */
+ if (mbox[n-1] != idata->delim)
+ {
+ imap_make_sequence (seq, sizeof (seq));
+ snprintf (buf, sizeof (buf), "%s %s \"\" \"%s\"\r\n", seq, list_cmd,
+ mbox);
+ mutt_socket_write (conn, buf);
+ do
+ {
+ if (imap_parse_list_response(conn, buf, sizeof(buf), &cur_folder,
+ &noselect, &noinferiors, &(idata->delim)) != 0)
+ return -1;
+
+ if (cur_folder)
+ {
+ imap_unquote_string (cur_folder);
+
+ if (noselect && !noinferiors && cur_folder[0] &&
+ (n = strlen (mbox)) < LONG_STRING-1)
+ {
+ mbox[n++] = idata->delim;
+ mbox[n] = '\0';
+ }
+ }
+ }
+ while ((mutt_strncmp (buf, seq, SEQLEN) != 0));
+ }
+
+ /* if we're descending a folder, mark it as current in browser_state */
+ if (mbox[n-1] == idata->delim)
+ {
+ showparents = 1;
+ imap_qualify_path (buf, sizeof (buf), host, port, mbox, NULL);
+ state->folder = safe_strdup (buf);
+ n--;
+ }
+
+ /* Find superiors to list */
+ for (n--; n >= 0 && mbox[n] != idata->delim ; n--);
+ if (n > 0) /* "aaaa/bbbb/" -> "aaaa/" */
+ {
+ n++;
+ ctmp = mbox[n];
+ mbox[n] = '\0';
+ /* List it to see if it can be selected */
+ dprint (2, (debugfile, "imap_init_browse: listing %s\n", mbox));
+ imap_make_sequence (seq, sizeof (seq));
+ snprintf (buf, sizeof (buf), "%s %s \"\" \"%s\"\r\n", seq,
+ list_cmd, mbox);
+ /* add this entry as a superior, if we aren't tab-completing */
+ if (showparents && add_list_result (conn, seq, buf, state, 1))
+ return -1;
+ /* if our target isn't a folder, we are in our superior */
+ if (!state->folder)
+ {
+ imap_qualify_path (buf, sizeof (buf), host, port, mbox, NULL);
+ state->folder = safe_strdup (buf);
+ }
+ mbox[n] = ctmp;
+ }
+ /* "/bbbb/" -> add "/", "aaaa/" -> add "" */
+ else
+ {
+ char relpath[2];
+ /* folder may be "/" */
+ snprintf (relpath, sizeof (relpath), "%c" , n < 0 ? '\0' : idata->delim);
+ if (showparents)
+ imap_add_folder (idata->delim, relpath, 1, 0, state, 1);
+ if (!state->folder)
+ {
+ imap_qualify_path (buf, sizeof (buf), host, port, relpath, NULL);
+ state->folder = safe_strdup (buf);
+ }
+ }
+ }
+
+ /* no namespace, no folder: set folder to host only */
+ if (!state->folder)
+ {
+ imap_qualify_path (buf, sizeof (buf), host, port, NULL, NULL);
+ state->folder = safe_strdup (buf);
+ }
+
+ if (home_namespace && mbox[0] != '\0')
+ {
+ /* Listing the home namespace, so INBOX should be included. Home
+ * namespace is not "", so we have to list it explicitly. We ask the
+ * server to see if it has descendants. */
+ imap_make_sequence (seq, sizeof (seq));
+ snprintf (buf, sizeof (buf), "%s LIST \"\" \"INBOX\"\r\n", seq);
+ if (add_list_result (conn, seq, buf, state, 0))
+ return -1;
+ }
+
+ nsup = state->entrylen;
+
+ imap_make_sequence (seq, sizeof (seq));
+ snprintf (buf, sizeof (buf), "%s %s \"\" \"%s%%\"\r\n", seq,
+ list_cmd, mbox);
+ if (add_list_result (conn, seq, buf, state, 0))
+ return -1;
+
+ qsort(&(state->entry[nsup]),state->entrylen-nsup,sizeof(state->entry[0]),
+ (int (*)(const void*,const void*)) compare_names);
+ if (home_namespace)
+ { /* List additional namespaces */
+ for (i = 0; i < nns; i++)
+ if (nsi[i].listable && !nsi[i].home_namespace)
+ imap_add_folder(nsi[i].delim, nsi[i].prefix, nsi[i].noselect,
+ nsi[i].noinferiors, state, 0);
+ }
+
+ mutt_clear_error ();
+ return 0;
+}
+
+static int add_list_result (CONNECTION *conn, const char *seq, const char *cmd,
+ struct browser_state *state, short isparent)
+{
+ IMAP_DATA *idata = CONN_DATA;
+ char buf[LONG_STRING];
+ char host[SHORT_STRING];
+ int port;
+ char *curfolder;
+ char *name;
+ int noselect;
+ int noinferiors;
+
+ if (imap_parse_path (state->folder, host, sizeof (host), &port, &curfolder))
+ {
+ dprint (2, (debugfile,
+ "add_list_result: current folder %s makes no sense\n", state->folder));
+ return -1;
+ }
+
+ mutt_socket_write (conn, cmd);
+
+ do
+ {
+ if (imap_parse_list_response(conn, buf, sizeof(buf), &name,
+ &noselect, &noinferiors, &(idata->delim)) != 0)
+ return -1;
+
+ if (name)
+ {
+ imap_unquote_string (name);
+ /* prune current folder from output */
+ if (isparent || strncmp (name, curfolder, strlen (name)))
+ imap_add_folder (idata->delim, name, noselect, noinferiors, state,
+ isparent);
+ }
+ }
+ while ((mutt_strncmp (buf, seq, SEQLEN) != 0));
+ return (0);
+}
+
+/* imap_add_folder: add a folder name to the browser list, formatting it as
+ * necessary. NOTE: check for duplicate folders removed, believed to be
+ * useless. Tell me if otherwise (brendan@kublai.com) */
+static void imap_add_folder (char delim, char *folder, int noselect,
+ int noinferiors, struct browser_state *state, short isparent)
+{
+ char tmp[LONG_STRING];
+ char relpath[LONG_STRING];
+ char host[SHORT_STRING];
+ int port;
+ char *curfolder;
+ int flen = strlen (folder);
+
+ if (imap_parse_path (state->folder, host, sizeof (host), &port, &curfolder))
+ return;
+
+ imap_unquote_string (folder);
+
+ /* plus 2: folder may be selectable AND have inferiors */
+ if (state->entrylen + 2 == state->entrymax)
+ {
+ safe_realloc ((void **) &state->entry,
+ sizeof (struct folder_file) * (state->entrymax += 256));
+ }
+
+ /* render superiors as unix-standard ".." */
+ if (isparent)
+ strfcpy (relpath, "../", sizeof (relpath));
+ /* strip current folder from target, to render a relative path */
+ else if (!strncmp (curfolder, folder, strlen (curfolder)))
+ strfcpy (relpath, folder + strlen (curfolder), sizeof (relpath));
+ else
+ strfcpy (relpath, folder, sizeof (relpath));
+
+ /* apply filemask filter. This should really be done at menu setup rather
+ * than at scan, since it's so expensive to scan. But that's big changes
+ * to browser.c */
+ if (!((regexec (Mask.rx, relpath, 0, NULL, 0) == 0) ^ Mask.not))
+ return;
+
+ if (!noselect)
+ {
+ imap_qualify_path (tmp, sizeof (tmp), host, port, folder, NULL);
+ (state->entry)[state->entrylen].name = safe_strdup (tmp);
+
+ snprintf (tmp, sizeof (tmp), "IMAP %-25s %-25s", host, relpath);
+ (state->entry)[state->entrylen].desc = safe_strdup (tmp);
+
+ (state->entry)[state->entrylen].notfolder = 0;
+ (state->entrylen)++;
+ }
+ if (!noinferiors)
+ {
+ char trailing_delim[2];
+
+ /* create trailing delimiter if necessary */
+ trailing_delim[1] = '\0';
+ trailing_delim[0] = (flen && folder[flen - 1] != delim) ? delim : '\0';
+
+ imap_qualify_path (tmp, sizeof (tmp), host, port, folder, trailing_delim);
+ (state->entry)[state->entrylen].name = safe_strdup (tmp);
+
+ if (strlen (relpath) < sizeof (relpath) - 2)
+ strcat (relpath, trailing_delim);
+ snprintf (tmp, sizeof (tmp), "IMAP %-25s %-25s", host, relpath);
+ (state->entry)[state->entrylen].desc = safe_strdup (tmp);
+
+ (state->entry)[state->entrylen].notfolder = 1;
+ (state->entrylen)++;
+ }
+}
+
+static int compare_names(struct folder_file *a, struct folder_file *b)
+{
+ return mutt_strcmp(a->name, b->name);
+}
+
+static int get_namespace (IMAP_DATA *idata, char *nsbuf, int nsblen,
+ IMAP_NAMESPACE_INFO *nsi, int nsilen, int *nns)
+{
+ char buf[LONG_STRING];
+ char seq[16];
+ char *s;
+ int n;
+ char ns[LONG_STRING];
+ char delim = '/';
+ int type;
+ int nsbused = 0;
+
+ *nns = 0;
+ nsbuf[nsblen-1] = '\0';
+
+ imap_make_sequence (seq, sizeof (seq));
+ snprintf (buf, sizeof (buf), "%s NAMESPACE\r\n", seq);
+
+ mutt_socket_write (idata->conn, buf);
+ do
+ {
+ if (mutt_socket_read_line_d (buf, sizeof (buf), idata->conn) < 0)
+ {
+ return (-1);
+ }
+
+ if (buf[0] == '*')
+ {
+ s = imap_next_word (buf);
+ if (mutt_strncasecmp ("NAMESPACE", s, 9) == 0)
+ {
+ /* There are three sections to the response, User, Other, Shared,
+ * and maybe more by extension */
+ for (type = IMAP_NS_PERSONAL; *s; type++)
+ {
+ s = imap_next_word (s);
+ if (*s && strncmp (s, "NIL", 3))
+ {
+ s++;
+ while (*s && *s != ')')
+ {
+ s++; /* skip ( */
+ /* copy namespace */
+ n = 0;
+ if (*s == '\"')
+ {
+ s++;
+ while (*s && *s != '\"')
+ {
+ if (*s == '\\')
+ s++;
+ ns[n++] = *s;
+ s++;
+ }
+ }
+ else
+ while (*s && !ISSPACE (*s))
+ {
+ ns[n++] = *s;
+ s++;
+ }
+ ns[n] = '\0';
+ /* delim? */
+ s = imap_next_word (s);
+ if (*s && *s == '\"')
+ {
+ if (s[1] && s[2] == '\"')
+ delim = s[1];
+ else if (s[1] && s[1] == '\\' && s[2] && s[3] == '\"')
+ delim = s[2];
+ }
+ /* Save result (if space) */
+ if ((nsbused < nsblen) && (*nns < nsilen))
+ {
+ nsi->type = type;
+ strncpy(nsbuf+nsbused,ns,nsblen-nsbused-1);
+ nsi->prefix = nsbuf+nsbused;
+ nsbused += n+1;
+ nsi->delim = delim;
+ nsi++;
+ (*nns)++;
+ }
+ while (*s && *s != ')') s++;
+ s++;
+ }
+ }
+ }
+ }
+ else
+ {
+ if (imap_handle_untagged (idata, buf) != 0)
+ return (-1);
+ }
+ }
+ }
+ while ((mutt_strncmp (buf, seq, SEQLEN) != 0));
+ return 0;
+}
+
+/* Check which namespaces actually exist */
+static int verify_namespace (CONNECTION *conn, IMAP_NAMESPACE_INFO *nsi,
+ int nns)
+{
+ char buf[LONG_STRING];
+ char seq[16];
+ int i = 0;
+ char *name;
+ char delim;
+
+ for (i = 0; i < nns; i++, nsi++)
+ {
+ imap_make_sequence (seq, sizeof (seq));
+ snprintf (buf, sizeof (buf), "%s %s \"\" \"%s\"\r\n", seq,
+ option (OPTIMAPLSUB) ? "LSUB" : "LIST", nsi->prefix);
+ mutt_socket_write (conn, buf);
+
+ nsi->listable = 0;
+ nsi->home_namespace = 0;
+ do
+ {
+ if (imap_parse_list_response(conn, buf, sizeof(buf), &name,
+ &(nsi->noselect), &(nsi->noinferiors),
+ &delim) != 0)
+ return (-1);
+ nsi->listable |= (name != NULL);
+ }
+ while ((mutt_strncmp (buf, seq, SEQLEN) != 0));
+ }
+ return (0);
+}
+
diff --git a/imap/imap.c b/imap/imap.c
new file mode 100644
index 00000000..4d0fde52
--- /dev/null
+++ b/imap/imap.c
@@ -0,0 +1,2274 @@
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ * Copyright (C) 1996-9 Brandon Long <blong@fiction.net>
+ * Copyright (C) 1999 Brendan Cully <brendan@kublai.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* Support for IMAP4rev1, with the occasional nod to IMAP 4. */
+
+#include "mutt.h"
+#include "mutt_curses.h"
+#include "mx.h"
+#include "mailbox.h"
+#include "globals.h"
+#include "sort.h"
+#include "browser.h"
+#include "imap.h"
+#include "imap_private.h"
+#include "imap_socket.h"
+
+#include <unistd.h>
+#include <ctype.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+
+
+#ifdef _PGPPATH
+#include "pgp.h"
+#endif
+
+
+static char *Capabilities[] = {"IMAP4", "IMAP4rev1", "STATUS", "ACL",
+ "NAMESPACE", "AUTH=CRAM-MD5", "AUTH=KERBEROS_V4", "AUTH=GSSAPI",
+ "AUTH=LOGIN", "AUTH-LOGIN", "AUTH=PLAIN", "AUTH=SKEY", "IDLE",
+ "LOGIN-REFERRALS", "MAILBOX-REFERRALS", "QUOTA", "SCAN", "SORT",
+ "THREAD=ORDEREDSUBJECT", "UIDPLUS", NULL};
+
+/* imap forward declarations */
+static time_t imap_parse_date (char *s);
+static int imap_parse_fetch (IMAP_HEADER_INFO *h, char *s);
+static int imap_read_bytes (FILE *fp, CONNECTION *conn, long bytes);
+static int imap_wordcasecmp(const char *a, const char *b);
+static void imap_parse_capabilities (IMAP_DATA *idata, char *s);
+static int get_literal_count(const char *buf, long *bytes);
+static int imap_read_headers (CONTEXT *ctx, int msgbegin, int msgend);
+static int imap_reopen_mailbox (CONTEXT *ctx, int *index_hint);
+static int imap_get_delim (IMAP_DATA *idata, CONNECTION *conn);
+static int imap_check_acl (IMAP_DATA *idata);
+static int imap_check_capabilities (IMAP_DATA *idata);
+static int imap_create_mailbox (IMAP_DATA *idata, char *mailbox);
+
+/* everything above should be moved into a separate imap header file */
+
+void imap_make_sequence (char *buf, size_t buflen)
+{
+ static int sequence = 0;
+
+ snprintf (buf, buflen, "a%04d", sequence++);
+
+ if (sequence > 9999)
+ sequence = 0;
+}
+
+void imap_error (const char *where, const char *msg)
+{
+ mutt_error (_("imap_error(): unexpected response in %s: %s\n"), where, msg);
+}
+
+/* date is of the form: DD-MMM-YYYY HH:MM:SS +ZZzz */
+static time_t imap_parse_date (char *s)
+{
+ struct tm t;
+ time_t tz;
+
+ t.tm_mday = (s[0] == ' '? s[1] - '0' : (s[0] - '0') * 10 + (s[1] - '0'));
+ s += 2;
+ if (*s != '-')
+ return 0;
+ s++;
+ t.tm_mon = mutt_check_month (s);
+ s += 3;
+ if (*s != '-')
+ return 0;
+ s++;
+ t.tm_year = (s[0] - '0') * 1000 + (s[1] - '0') * 100 + (s[2] - '0') * 10 + (s[3] - '0') - 1900;
+ s += 4;
+ if (*s != ' ')
+ return 0;
+ s++;
+
+ /* time */
+ t.tm_hour = (s[0] - '0') * 10 + (s[1] - '0');
+ s += 2;
+ if (*s != ':')
+ return 0;
+ s++;
+ t.tm_min = (s[0] - '0') * 10 + (s[1] - '0');
+ s += 2;
+ if (*s != ':')
+ return 0;
+ s++;
+ t.tm_sec = (s[0] - '0') * 10 + (s[1] - '0');
+ s += 2;
+ if (*s != ' ')
+ return 0;
+ s++;
+
+ /* timezone */
+ tz = ((s[1] - '0') * 10 + (s[2] - '0')) * 3600 +
+ ((s[3] - '0') * 10 + (s[4] - '0')) * 60;
+ if (s[0] == '+')
+ tz = -tz;
+
+ return (mutt_mktime (&t, 0) + tz);
+}
+
+static int imap_parse_fetch (IMAP_HEADER_INFO *h, char *s)
+{
+ char tmp[SHORT_STRING];
+ char *ptmp;
+ int state = 0;
+ int recent = 0;
+
+ if (!s)
+ return (-1);
+
+ h->old = 0;
+
+ while (*s)
+ {
+ SKIPWS (s);
+
+ switch (state)
+ {
+ case 0:
+ if (mutt_strncasecmp ("FLAGS", s, 5) == 0)
+ {
+ s += 5;
+ SKIPWS (s);
+ if (*s != '(')
+ {
+ dprint (1, (debugfile, "imap_parse_fetch(): bogus FLAGS entry: %s\n", s));
+ return (-1); /* parse error */
+ }
+ /* we're about to get a new set of headers, so clear the old ones. */
+ h->deleted = 0;
+ h->flagged = 0;
+ h->replied = 0;
+ h->read = 0;
+ h->old = 0;
+ h->changed = 0;
+ recent = 0;
+ s++;
+ state = 1;
+ }
+ else if (mutt_strncasecmp ("INTERNALDATE", s, 12) == 0)
+ {
+ s += 12;
+ SKIPWS (s);
+ if (*s != '\"')
+ {
+ dprint (1, (debugfile, "imap_parse_fetch(): bogus INTERNALDATE entry: %s\n", s));
+ return (-1);
+ }
+ s++;
+ ptmp = tmp;
+ while (*s && *s != '\"')
+ *ptmp++ = *s++;
+ if (*s != '\"')
+ return (-1);
+ s++; /* skip past the trailing " */
+ *ptmp = 0;
+ h->received = imap_parse_date (tmp);
+ }
+ else if (mutt_strncasecmp ("RFC822.SIZE", s, 11) == 0)
+ {
+ s += 11;
+ SKIPWS (s);
+ ptmp = tmp;
+ while (isdigit (*s))
+ *ptmp++ = *s++;
+ *ptmp = 0;
+ h->content_length += atoi (tmp);
+ }
+ else if (*s == ')')
+ s++; /* end of request */
+ else if (*s)
+ {
+ /* got something i don't understand */
+ imap_error ("imap_parse_fetch()", s);
+ return (-1);
+ }
+ break;
+ case 1: /* flags */
+ if (*s == ')')
+ {
+ s++;
+ /* if a message is neither seen nor recent, it is OLD. */
+ if (option (OPTMARKOLD) && !recent && !(h->read))
+ h->old = 1;
+ state = 0;
+ }
+ else if (mutt_strncasecmp ("\\deleted", s, 8) == 0)
+ {
+ s += 8;
+ h->deleted = 1;
+ }
+ else if (mutt_strncasecmp ("\\flagged", s, 8) == 0)
+ {
+ s += 8;
+ h->flagged = 1;
+ }
+ else if (mutt_strncasecmp ("\\answered", s, 9) == 0)
+ {
+ s += 9;
+ h->replied = 1;
+ }
+ else if (mutt_strncasecmp ("\\seen", s, 5) == 0)
+ {
+ s += 5;
+ h->read = 1;
+ }
+ else if (mutt_strncasecmp ("\\recent", s, 5) == 0)
+ {
+ s += 7;
+ recent = 1;
+ }
+ else
+ {
+ while (*s && !ISSPACE (*s) && *s != ')')
+ s++;
+ }
+ break;
+ }
+ }
+ return 0;
+}
+
+void imap_quote_string (char *dest, size_t slen, const char *src)
+{
+ char quote[] = "\"\\", *pt;
+ const char *s;
+ size_t len = slen;
+
+ pt = dest;
+ s = src;
+
+ *pt++ = '"';
+ /* save room for trailing quote-char */
+ len -= 2;
+
+ for (; *s && len; s++)
+ {
+ if (strchr (quote, *s))
+ {
+ len -= 2;
+ if (!len)