diff options
-rw-r--r-- | OPS | 5 | ||||
-rw-r--r-- | browser.c | 153 | ||||
-rw-r--r-- | browser.h | 48 | ||||
-rw-r--r-- | complete.c | 15 | ||||
-rw-r--r-- | doc/manual.sgml.in | 52 | ||||
-rw-r--r-- | functions.h | 5 | ||||
-rw-r--r-- | imap.c | 988 | ||||
-rw-r--r-- | imap.h | 22 | ||||
-rw-r--r-- | init.h | 4 | ||||
-rw-r--r-- | menu.c | 7 | ||||
-rw-r--r-- | mutt.h | 4 | ||||
-rw-r--r-- | mutt_socket.h | 2 | ||||
-rw-r--r-- | mx.c | 4 | ||||
-rw-r--r-- | po/cs.po | 33 | ||||
-rw-r--r-- | socket.c | 32 |
15 files changed, 1104 insertions, 270 deletions
@@ -6,6 +6,9 @@ OP_BOUNCE_MESSAGE "remail a message to another user" OP_BROWSER_NEW_FILE "select a new file in this directory" OP_BROWSER_VIEW_FILE "view file" OP_BROWSER_TELL "display the currently selected file's name" +OP_BROWSER_SUBSCRIBE "subscribe to current mailbox (IMAP Only)" +OP_BROWSER_UNSUBSCRIBE "unsubscribe to current mailbox (IMAP Only)" +OP_BROWSER_TOGGLE_LSUB "toggle view all/subscribed mailboxes (IMAP Only)" OP_CHANGE_DIRECTORY "change directories" OP_CHECK_NEW "check mailboxes for new mail" OP_COMPOSE_ATTACH_FILE "attach a file(s) to this message" @@ -143,7 +146,7 @@ OP_TAG_SUBTHREAD "tag the current subthread" OP_TAG_THREAD "tag the current thread" OP_TOGGLE_NEW "toggle a message's 'new' flag" OP_TOGGLE_WRITE "toggle whether the mailbox will be rewritten" -OP_TOGGLE_MAILBOXES "toggle wether to browse mailboxes or all files" +OP_TOGGLE_MAILBOXES "toggle whether to browse mailboxes or all files" OP_TOP_PAGE "move to the top of the page" OP_UNDELETE "undelete the current entry" OP_UNDELETE_THREAD "undelete all messages in thread" @@ -23,6 +23,10 @@ #include "mapping.h" #include "sort.h" #include "mailbox.h" +#include "browser.h" +#ifdef USE_IMAP +#include "imap.h" +#endif #include <stdlib.h> #include <dirent.h> @@ -33,23 +37,6 @@ #include <pwd.h> #include <grp.h> -struct folder_file -{ - mode_t mode; - time_t mtime; - off_t size; - char *name; - char *desc; - short tagged; -}; - -struct browser_state -{ - struct folder_file *entry; - short entrylen; /* number of real entries */ - short entrymax; /* max entry */ -}; - static struct mapping_t FolderHelp[] = { { N_("Exit"), OP_EXIT }, { N_("Chdir"), OP_CHANGE_DIRECTORY }, @@ -456,6 +443,12 @@ static void init_menu (struct browser_state *state, MUTTMENU *menu, char *title, { strfcpy (path, LastDir, sizeof (path)); mutt_pretty_mailbox (path); +#ifdef USE_IMAP + if (state->imap_browse && option (OPTIMAPLSUB)) + snprintf (title, titlelen, _("Subscribed [%s], File mask: %s"), + path, Mask.pattern); + else +#endif snprintf (title, titlelen, _("Directory [%s], File mask: %s"), path, Mask.pattern); } @@ -491,6 +484,17 @@ void _mutt_select_file (char *f, size_t flen, int buffy, if (*f) { mutt_expand_path (f, flen); +#ifdef USE_IMAP + if (mx_is_imap (f)) + { + init_state (&state, NULL); + state.imap_browse = 1; + imap_init_browse (f, &state); + strfcpy (LastDir, f, sizeof (LastDir)); + } + else + { +#endif for (i = mutt_strlen (f) - 1; i > 0 && f[i] != '/' ; i--); if (i > 0) { @@ -520,9 +524,23 @@ void _mutt_select_file (char *f, size_t flen, int buffy, else strfcpy (prefix, f + i + 1, sizeof (prefix)); killPrefix = 1; +#ifdef USE_IMAP + } +#endif + } + else + { + if (!LastDir[0]) + strfcpy (LastDir, NONULL(Maildir), sizeof (LastDir)); +#ifdef USE_IMAP + if (!buffy && mx_is_imap (LastDir)) + { + init_state (&state, NULL); + state.imap_browse = 1; + imap_init_browse (LastDir, &state); + } +#endif } - else if (!LastDir[0]) - strfcpy (LastDir, NONULL(Maildir), sizeof (LastDir)); *f = 0; @@ -531,7 +549,11 @@ void _mutt_select_file (char *f, size_t flen, int buffy, if (examine_mailboxes (NULL, &state) == -1) return; } - else if (examine_directory (NULL, &state, LastDir, prefix) == -1) + else +#ifdef USE_IMAP + if (!state.imap_browse) +#endif + if (examine_directory (NULL, &state, LastDir, prefix) == -1) return; menu = mutt_new_menu (); @@ -561,7 +583,11 @@ void _mutt_select_file (char *f, size_t flen, int buffy, if (S_ISDIR (state.entry[menu->current].mode) || (S_ISLNK (state.entry[menu->current].mode) && - link_is_dir (state.entry[menu->current].name))) + link_is_dir (state.entry[menu->current].name)) +#ifdef USE_IMAP + || state.entry[menu->current].notfolder +#endif + ) { /* make sure this isn't a MH or maildir mailbox */ if (buffy) @@ -569,11 +595,21 @@ void _mutt_select_file (char *f, size_t flen, int buffy, strfcpy (buf, state.entry[menu->current].name, sizeof (buf)); mutt_expand_path (buf, sizeof (buf)); } +#ifdef USE_IMAP + else if (state.imap_browse) + { + strfcpy (buf, state.entry[menu->current].name, sizeof (buf)); + } +#endif else snprintf (buf, sizeof (buf), "%s/%s", LastDir, state.entry[menu->current].name); - if (mx_get_magic (buf) <= 0) + if ((mx_get_magic (buf) <= 0) +#ifdef USE_IMAP + || state.entry[menu->current].notfolder +#endif + ) { char OldLastDir[_POSIX_PATH_MAX]; @@ -604,6 +640,13 @@ void _mutt_select_file (char *f, size_t flen, int buffy, sprintf (LastDir, "%s", state.entry[menu->current].name); mutt_expand_path (LastDir, sizeof (LastDir)); } +#ifdef USE_IMAP + else if (state.imap_browse) + { + strfcpy (LastDir, state.entry[menu->current].name, + sizeof (LastDir)); + } +#endif else sprintf (LastDir + mutt_strlen (LastDir), "/%s", state.entry[menu->current].name); @@ -615,6 +658,16 @@ void _mutt_select_file (char *f, size_t flen, int buffy, killPrefix = 0; } buffy = 0; +#ifdef USE_IMAP + if (state.imap_browse) + { + init_state (&state, NULL); + state.imap_browse = 1; + imap_init_browse (LastDir, &state); + menu->data = state.entry; + } + else +#endif if (examine_directory (menu, &state, LastDir, prefix) == -1) { /* try to restore the old values */ @@ -637,6 +690,12 @@ void _mutt_select_file (char *f, size_t flen, int buffy, strfcpy (f, state.entry[menu->current].name, flen); mutt_expand_path (f, flen); } +#ifdef USE_IMAP + else if (state.imap_browse) + { + strfcpy (f, state.entry[menu->current].name, flen); + } +#endif else snprintf (f, flen, "%s/%s", LastDir, state.entry[menu->current].name); @@ -684,10 +743,35 @@ void _mutt_select_file (char *f, size_t flen, int buffy, if(state.entrylen) mutt_message(state.entry[menu->current].name); break; + +#ifdef USE_IMAP + case OP_BROWSER_SUBSCRIBE: + imap_subscribe (state.entry[menu->current].name, 1); + break; + + case OP_BROWSER_UNSUBSCRIBE: + imap_subscribe (state.entry[menu->current].name, 0); + break; + + case OP_BROWSER_TOGGLE_LSUB: + if (option (OPTIMAPLSUB)) + { + unset_option (OPTIMAPLSUB); + } + else + { + set_option (OPTIMAPLSUB); + } + mutt_ungetch (0, OP_CHECK_NEW); + break; +#endif case OP_CHANGE_DIRECTORY: strfcpy (buf, LastDir, sizeof (buf)); +#ifdef USE_IMAP + if (!state.imap_browse) +#endif {/* add '/' at the end of the directory name */ int len=mutt_strlen(LastDir); if (sizeof (buf) > len) @@ -699,6 +783,21 @@ void _mutt_select_file (char *f, size_t flen, int buffy, { buffy = 0; mutt_expand_path (buf, sizeof (buf)); +#ifdef USE_IMAP + if (mx_is_imap (buf)) + { + strfcpy (LastDir, buf, sizeof (LastDir)); + destroy_state (&state); + init_state (&state, NULL); + state.imap_browse = 1; + imap_init_browse (LastDir, &state); + menu->data = state.entry; + menu->current = 0; + menu->top = 0; + init_menu (&state, menu, title, sizeof (title), buffy); + } + else +#endif if (stat (buf, &st) == 0) { if (S_ISDIR (st.st_mode)) @@ -834,11 +933,21 @@ void _mutt_select_file (char *f, size_t flen, int buffy, destroy_state (&state); prefix[0] = 0; killPrefix = 0; + if (buffy) { if (examine_mailboxes (menu, &state) == -1) return; } +#ifdef USE_IMAP + else if (mx_is_imap (LastDir)) + { + init_state (&state, NULL); + state.imap_browse = 1; + imap_init_browse (LastDir, &state); + menu->data = state.entry; + } +#endif else if (examine_directory (menu, &state, LastDir, prefix) == -1) return; init_menu (&state, menu, title, sizeof (title), buffy); diff --git a/browser.h b/browser.h new file mode 100644 index 00000000..b19fb5b4 --- /dev/null +++ b/browser.h @@ -0,0 +1,48 @@ +/* $Id$ */ +/* + * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu> + * + * 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. + */ + +#ifndef _BROWSER_H +#define _BROWSER_H 1 + +struct folder_file +{ + mode_t mode; + time_t mtime; + off_t size; + char *name; + char *desc; +#ifdef USE_IMAP + short notfolder; +#endif +}; + +struct browser_state +{ + struct folder_file *entry; + short entrylen; /* number of real entries */ + short entrymax; /* max entry */ +#ifdef USE_IMAP + short imap_browse; + int noselect : 1; + int marked : 1; + int unmarked : 1; +#endif +}; + +#endif /* _BROWSER_H */ @@ -32,7 +32,7 @@ int mutt_complete (char *s) { char *p; - DIR *dirp; + DIR *dirp = NULL; struct dirent *de; int i ,init=0; size_t len; @@ -85,12 +85,23 @@ int mutt_complete (char *s) /* no directory name, so assume current directory. */ dirpart[0] = 0; strfcpy (filepart, s, sizeof (filepart)); - dirp = opendir ("."); +#ifdef USE_IMAP + if (s[0] != '{') +#endif + dirp = opendir ("."); } } if (dirp == NULL) { +#ifdef USE_IMAP + /* If we are trying to complete an IMAP folder, it will start with { + * in which case, we just return 0 at this point. Eventually, we + * might complete the actually folder name from the server + */ + if ((s[0] == '{') || (exp_dirpart[0] == '{')) + return 0; +#endif dprint (1, (debugfile, "mutt_complete(): %s: %s (errno %d).\n", exp_dirpart, strerror (errno), errno)); return (-1); } diff --git a/doc/manual.sgml.in b/doc/manual.sgml.in index d49d1337..7ddcdb3f 100644 --- a/doc/manual.sgml.in +++ b/doc/manual.sgml.in @@ -1811,7 +1811,7 @@ url="http://www.ccil.org/~esr/fetchmail" name="fetchmail"> If Mutt was compiled with IMAP support (by running the <em/configure/ script with the <em/--enable-imap/ flag), it has the ability to work -with folders located on a remote imap server. +with folders located on a remote IMAP server. You can access the remote inbox by selecting the folder <tt/{imapserver}inbox/, where <tt/imapserver/ is the name of the IMAP @@ -1821,8 +1821,30 @@ server, you should use <tt>{imapserver}path/to/folder</tt> where <tt>path/to/folder</tt> is the path of the folder you want to access relative to your home directory. -<bf/Note:/ The IMAP support is in a very early state and quite -unstable at the moment. If you need a more stable way to access your +You can select an alternative port by specifying it with the server, ie: +<tt/{imapserver:port}inbox/. + +Note that not all servers use / as the hierarchy separator. Mutt should +correctly notice which separator is being used by the server and convert +paths accordingly. + +When browsing folders on an IMAP server, you can toggle whether to look +at only the folders you are subscribed to, or all folders with the +<em/toggle-subscribed/ command. See also the +<ref id="imap_list_subscribed" name="$imap_list_subscribed"> variable. + +Mutt was designed to work with IMAP4rev1 servers, and has been tested +with both the UWash IMAP server v11.241 and the Cyrus IMAP server v1.5.14. +Note that if you are using mbox as the mail store on the UWash server, +the server will disconnect a client if another client selects the same +folder. + +Mutt currently does not support completion of IMAP folder names. Hit +the <em/complete/ key again to go to the IMAP browser. + +<bf/Note:/ The IMAP support has had very limited testing due to a lack +of developers using it. It should work with the reference servers +mentioned above, but if you need a more stable way to access your IMAP folder, consider using a specialized program, such as <htmlurl url="http://www.ccil.org/~esr/fetchmail" name="fetchmail">. @@ -2953,6 +2975,15 @@ Default: 0 This variable configures how often (in seconds) IMAP should look for new mail. +<sect2>imap_list_subscribed<label id="imap_list_subscribed"> +<p> +Type: boolean<newline> +Default: unset + +This variable configures whether IMAP folder browsing will look for only +subscribed folders or all folders. This can be toggled in the IMAP +browser with the <em/toggle-subscribed/ command. + <sect2>imap_pass<label id="imap_pass"> <p> Type: string<newline> @@ -2964,6 +2995,16 @@ you should only use this option when you are on a fairly secure machine, because the superuser can read your muttrc even if you are the only one who can read the file. +<sect2>imap_passive<label id="imap_passive"> +<p> +Type: boolean<newline> +Default: set + +When set, mutt will not open new IMAP connections to check for new mail. +Mutt will only check for new mail over existing IMAP connections. This +is useful if you don't want to be prompted to user/password pairs on +mutt invocation, or if opening the connection is slow. + <sect2>imap_user<label id="imap_user"> <p> Type: string<newline> @@ -4604,6 +4645,11 @@ search-reverse ESC / search backwards for a regular expression select-new N select a new file in this directory sort o sort messages sort-reverse O sort messages in reverse order +toggle-mailboxes TAB toggle whether to browse mailboxes or all files +view-file SPACE view file +subscribe s subscribe to current mailbox (IMAP Only) +unsubscribe u unsubscribe to current mailbox (IMAP Only) +toggle-subscribed T toggle view all/subscribed mailboxes (IMAP Only) </verb> <sect2>pgp <p> diff --git a/functions.h b/functions.h index 125dedcf..4b5ec6c3 100644 --- a/functions.h +++ b/functions.h @@ -323,6 +323,11 @@ struct binding_t OpBrowser[] = { { "check-new", OP_CHECK_NEW, NULL }, { "toggle-mailboxes", OP_TOGGLE_MAILBOXES, "\t" }, { "view-file", OP_BROWSER_VIEW_FILE, " " }, +#ifdef USE_IMAP + { "subscribe", OP_BROWSER_SUBSCRIBE, "s" }, + { "unsubscribe", OP_BROWSER_UNSUBSCRIBE, "u" }, + { "toggle-subscribed", OP_BROWSER_TOGGLE_LSUB, "T" }, +#endif { NULL, 0, NULL } }; @@ -1,5 +1,6 @@ /* * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu> + * Copyright (C) 1996-9 Brandon Long <blong@fiction.net> * * 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 @@ -23,6 +24,7 @@ #include "globals.h" #include "mutt_socket.h" #include "sort.h" +#include "browser.h" #include <unistd.h> #include <netinet/in.h> @@ -65,6 +67,15 @@ enum IMAP_OK_FAIL }; +enum +{ + /* States */ + IMAP_DISCONNECTED = 0, + IMAP_CONNECTED, + IMAP_AUTHENTICATED, + IMAP_SELECTED +}; + typedef struct { unsigned int index; @@ -73,15 +84,21 @@ typedef struct typedef struct { + /* This data is specific to a CONNECTION to an IMAP server */ short status; - unsigned short newMailCount; - char *mailbox; - short xxx; - IMAP_CACHE cache[IMAP_CACHE_LEN]; + short state; + char delim; CONNECTION *conn; + + /* The following data is all specific to the currently SELECTED mbox */ + CONTEXT *selected_ctx; + char *selected_mailbox; + unsigned int newMailCount; + IMAP_CACHE cache[IMAP_CACHE_LEN]; } IMAP_DATA; #define CTX_DATA ((IMAP_DATA *) ctx->data) +#define CONN_DATA ((IMAP_DATA *) conn->data) #define SELCTX_DATA ((IMAP_DATA *) selctx->data) /* Linked list to hold header information while downloading message @@ -102,14 +119,15 @@ typedef struct imap_header_info struct imap_header_info *next; } IMAP_HEADER_INFO; - static void imap_make_sequence (char *buf, size_t buflen) { static int sequence = 0; snprintf (buf, buflen, "a%04d", sequence++); -} + if (sequence > 9999) + sequence = 0; +} static void imap_error (const char *where, const char *msg) { @@ -307,6 +325,36 @@ static void imap_quote_string (char *dest, size_t slen, const char *src) *pt = 0; } +static void imap_unquote_string (char *s) +{ + char *d = s; + + if (*s == '\"') + s++; + else + return; + + while (*s) + { + if (*s == '\"') + { + *d = '\0'; + return; + } + if (*s == '\\') + { + s++; + } + if (*s) + { + *d = *s; + d++; + s++; + } + } + *d = '\0'; +} + static int imap_read_bytes (FILE *fp, CONNECTION *conn, long bytes) { long pos; @@ -342,15 +390,14 @@ static char *imap_next_word (char *s) return s; } -static int imap_handle_untagged (CONTEXT *ctx, char *s) +static int imap_handle_untagged (IMAP_DATA *idata, char *s) { char *pn; int count; - CONTEXT *selctx = (CONTEXT *)(CTX_DATA->conn->data); s = imap_next_word (s); - if (isdigit (*s)) + if ((idata->state == IMAP_SELECTED) && isdigit (*s)) { pn = s; s = imap_next_word (s); @@ -363,30 +410,27 @@ static int imap_handle_untagged (CONTEXT *ctx, char *s) /* new mail arrived */ count = atoi (pn); - if ( (SELCTX_DATA->status != IMAP_EXPUNGE) && - count < selctx->msgcount) + if ( (idata->status != IMAP_EXPUNGE) && + count < idata->selected_ctx->msgcount) { /* something is wrong because the server reported fewer messages * than we previously saw */ mutt_error _("Fatal error. Message count is out of sync!"); - SELCTX_DATA->status = IMAP_FATAL; - CTX_DATA->status = IMAP_FATAL; - mx_fastclose_mailbox (ctx); - if (ctx != selctx) - mx_fastclose_mailbox (selctx); + idata->status = IMAP_FATAL; + mx_fastclose_mailbox (idata->selected_ctx); return (-1); } else { - if (SELCTX_DATA->status != IMAP_EXPUNGE) - SELCTX_DATA->status = IMAP_NEW_MAIL; - SELCTX_DATA->newMailCount = count; + if (idata->status != IMAP_EXPUNGE) + idata->status = IMAP_NEW_MAIL; + idata->newMailCount = count; } } else if (mutt_strncasecmp ("EXPUNGE", s, 7) == 0) { - SELCTX_DATA->status = IMAP_EXPUNGE; + idata->status = IMAP_EXPUNGE; } } else if (mutt_strncasecmp ("BYE", s, 3) == 0) @@ -395,11 +439,9 @@ static int imap_handle_untagged (CONTEXT *ctx, char *s) s += 3; SKIPWS (s); mutt_error (s); - SELCTX_DATA->status = IMAP_BYE; - CTX_DATA->status = IMAP_BYE; - mx_fastclose_mailbox (ctx); - if (ctx != selctx) - mx_fastclose_mailbox (selctx); + idata->status = IMAP_BYE; + if (idata->state == IMAP_SELECTED) + mx_fastclose_mailbox (idata->selected_ctx); return (-1); } else @@ -515,7 +557,7 @@ static int imap_read_headers (CONTEXT *ctx, int msgbegin, int msgend) pc = buf; } } - else if (imap_handle_untagged (ctx, buf) != 0) + else if (imap_handle_untagged (CTX_DATA, buf) != 0) return (-1); } } @@ -643,8 +685,8 @@ static int imap_reopen_mailbox (CONTEXT *ctx, int *index_hint) ctx->id_hash = hash_create (1031); ctx->subj_hash = hash_create (1031); - mutt_message (_("Reopening mailbox..."), CTX_DATA->mailbox); - imap_quote_string (buf, sizeof(buf), CTX_DATA->mailbox); + mutt_message (_("Reopening mailbox... %s"), CTX_DATA->selected_mailbox); + imap_quote_string (buf, sizeof(buf), CTX_DATA->selected_mailbox); imap_make_sequence (seq, sizeof (seq)); snprintf (bufout, sizeof (bufout), "%s SELECT %s\r\n", seq, buf); mutt_socket_write (CTX_DATA->conn, bufout); @@ -670,12 +712,23 @@ static int imap_reopen_mailbox (CONTEXT *ctx, int *index_hint) if (mutt_strncasecmp ("EXISTS", pc, 6) == 0) count = n; } - else if (imap_handle_untagged (ctx, buf) != 0) + else if (imap_handle_untagged (CTX_DATA, buf) != 0) return (-1); } } while (mutt_strncmp (seq, buf, mutt_strlen (seq)) != 0); + if (!imap_code (buf)) + { + char *s; + s = imap_next_word (buf); /* skip seq */ + s = imap_next_word (s); /* Skip response */ + CTX_DATA->state = IMAP_AUTHENTICATED; + mutt_error (s); + sleep (1); + return (-1); + } + ctx->hdrmax = count; ctx->hdrs = safe_malloc (count * sizeof (HEADER *)); ctx->v2r = safe_malloc (count * sizeof (int)); @@ -768,47 +821,48 @@ static int imap_reopen_mailbox (CONTEXT *ctx, int *index_hint) * failing, this is used for checking for a mailbox on append and login * Return 0 on success, -1 on Failure, -2 on OK Failure */ -static int imap_exec (char *buf, size_t buflen, - CONTEXT *ctx, const char *seq, const char *cmd, int flags) +static int imap_exec (char *buf, size_t buflen, IMAP_DATA *idata, + const char *seq, const char *cmd, int flags) { int count; - CONTEXT *selctx = (CONTEXT *)(CTX_DATA->conn->data); - mutt_socket_write (CTX_DATA->conn, cmd); + mutt_socket_write (idata->conn, cmd); do { - if (mutt_socket_read_line_d (buf, buflen, CTX_DATA->conn) < 0) + if (mutt_socket_read_line_d (buf, buflen, idata->conn) < 0) return (-1); - if (buf[0] == '*' && imap_handle_untagged (ctx, buf) != 0) + if (buf[0] == '*' && imap_handle_untagged (idata, buf) != 0) return (-1); } while (mutt_strncmp (buf, seq, SEQLEN) != 0); - if (!selctx->closing && - (SELCTX_DATA->status == IMAP_NEW_MAIL || - SELCTX_DATA->status == IMAP_EXPUNGE)) + if ((idata->state == IMAP_SELECTED) && + !idata->selected_ctx->closing && + (idata->status == IMAP_NEW_MAIL || + idata->status == IMAP_EXPUNGE)) { - count = SELCTX_DATA->newMailCount; + count = idata->newMailCount; - if (SELCTX_DATA->status == IMAP_NEW_MAIL && count > selctx->msgcount) + if (idata->status == IMAP_NEW_MAIL && count > idata->selected_ctx->msgcount) { /* read new mail messages */ dprint (1, (debugfile, "imap_exec(): new mail detected\n")); - while (count > selctx->hdrmax) - mx_alloc_memory (selctx); + while (count > idata->selected_ctx->hdrmax) + mx_alloc_memory (idata->selected_ctx); - count = imap_read_headers (selctx, selctx->msgcount, count - 1) + 1; + count = imap_read_headers (idata->selected_ctx, + idata->selected_ctx->msgcount, count - 1) + 1; } else { - imap_reopen_mailbox (selctx, NULL); + imap_reopen_mailbox (idata->selected_ctx, NULL); } - SELCTX_DATA->status = 0; + idata->status = 0; mutt_clear_error (); } @@ -831,153 +885,240 @@ static int imap_exec (char *buf, size_t buflen, return 0; } -static int imap_parse_path (char *path, char *host, size_t hlen, char **mbox) +static int imap_get_delim (IMAP_DATA *idata, CONNECTION *conn) +{ + char buf[LONG_STRING]; + char seq[8]; + char *s; + + /* assume that the delim is /. If this fails, we're in bigger trouble + * than getting the delim wrong */ + idata->delim = '/'; + + imap_make_sequence (seq, sizeof (seq)); + snprintf (buf, sizeof (buf), "%s LIST \"\" \"\"\r\n", seq); + + mutt_socket_write (conn, buf); + + do + { + if (mutt_socket_read_line_d (buf, sizeof (buf), conn) < 0) + { + return (-1); + } + + if (buf[0] == '*') + { + s = imap_next_word (buf); + if (mutt_strncasecmp ("LIST", s, 4) == 0) + { + s = imap_next_word (s); + s = imap_next_word (s); + if (s && s[0] == '\"' && s[1] && s[2] == '\"') + idata->delim = s[1]; + else if (s && s[0] == '\"' && s[1] && s[1] == '\\' && s[2] && s[3] == '\"') + idata->delim = s[2]; + } + else + { + if (conn->data && + imap_handle_untagged (idata, buf) != 0) + return (-1); + } + } + } + while ((mutt_strncmp (buf, seq, SEQLEN) != 0)); + return 0; +} + +static int imap_parse_path (char *path, char *host, size_t hlen, int *port, + char **mbox) { int n; char *pc; + char *pt; + /* set default port */ + *port = IMAP_PORT; pc = path; if (*pc != '{') return (-1); pc++; n = 0; - while (*pc && *pc != '}' && (n < hlen-1)) + while (*pc && *pc != '}' && *pc != ':' && (n < hlen-1)) host[n++] = *pc++; host[n] = 0; if (!*pc) return (-1); + if (*pc == ':') + { + pc++; + pt = pc; + while (*pc && *pc != '}') pc++; + if (!*pc) + return (-1); + *pc = '\0'; + *port = atoi (pt); + *pc = '}'; + } pc++; *mbox = pc; return 0; } -static int imap_open_connection (CONTEXT *ctx, CONNECTION *conn) +/* + * Fix up the imap path. This is necessary because the rest of mutt + * assumes a hierarchy delimiter of '/', which is not necessarily true + * in IMAP. Additionally, the filesystem converts multiple hierarchy + * delimiters into a single one, ie "///" is equal to "/". IMAP servers + * are not required to do this. + */ +static char *imap_fix_path (IMAP_DATA *idata, char *mailbox, char *path, + size_t plen) +{ + int x = 0; + + if (!mailbox || !*mailbox) + { + strfcpy (path, "INBOX", plen); + return path; + } + + while (mailbox && *mailbox && (x < (plen - 1))) + { + if ((*mailbox == '/') || (*mailbox == idata->delim)) + { + while ((*mailbox == '/') || (*mailbox == idata->delim)) mailbox++; + path[x] = idata->delim; + } + else + { + path[x] = *mailbox; + mailbox++; + } + x++; + } + path[x] = '\0'; + return path; +} + +static int imap_authenticate (IMAP_DATA *idata, CONNECTION *conn) { - char *portnum, *hostname; - struct sockaddr_in sin; - struct hostent *he; char buf[LONG_STRING]; char user[SHORT_STRING], q_user[SHORT_STRING]; char pass[SHORT_STRING], q_pass[SHORT_STRING]; char seq[16]; - memset (&sin, 0, sizeof (sin)); - hostname = safe_strdup (conn->server); - portnum = strrchr (hostname, ':'); - if (portnum) - { - *portnum=0; - portnum++; - } - sin.sin_port = htons (portnum ? atoi(portnum) : IMAP_PORT); - sin.sin_family = AF_INET; - if ((he = gethostbyname (hostname)) == NULL) - { - safe_free ((void*)&hostname); - mutt_perror (conn->server); - return (-1); - } - safe_free ((void*)&hostname); - memcpy (&sin.sin_addr, he->h_addr_list[0], he->h_length); + int r = 1; - if ((conn->fd = socket (AF_INET, SOCK_STREAM, IPPROTO_IP)) < 0) + while (r != 0) { - mutt_perror ("socket"); - return (-1); + 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)); + + 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; +} - mutt_message (_("Connecting to %s..."), conn->server); +static int imap_open_connection (IMAP_DATA *idata, CONNECTION *conn) +{ + char buf[LONG_STRING]; - if (connect (conn->fd, (struct sockaddr *) &sin, sizeof (sin)) < 0) + if (mutt_socket_open_connection (conn) < 0) { - mutt_perror ("connect"); - close (conn->fd); + return (-1); } + idata->state = IMAP_CONNECTED; + if (mutt_socket_read_line_d (buf, sizeof (buf), conn) < 0) { close (conn->fd); + idata->state = IMAP_DISCONNECTED; return (-1); } if (mutt_strncmp ("* OK", buf, 4) == 0) { - int r = 1; - - while (r != 0) + if (imap_authenticate (idata, conn) != 0) { - if (!ImapUser) - { - strfcpy (user, NONULL(Username), sizeof (user)); - if (mutt_get_field (_("IMAP Use |