NeoMutt  2023-03-22-27-g3cb248
Teaching an old dog new tricks
DOXYGEN
imap.c File Reference

IMAP network mailbox. More...

#include "config.h"
#include <limits.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "private.h"
#include "mutt/lib.h"
#include "config/lib.h"
#include "email/lib.h"
#include "core/lib.h"
#include "conn/lib.h"
#include "mutt.h"
#include "lib.h"
#include "enter/lib.h"
#include "parse/lib.h"
#include "progress/lib.h"
#include "question/lib.h"
#include "adata.h"
#include "auth.h"
#include "commands.h"
#include "edata.h"
#include "external.h"
#include "hook.h"
#include "mdata.h"
#include "msn.h"
#include "mutt_logging.h"
#include "mutt_socket.h"
#include "muttlib.h"
#include "mx.h"
#include "sort.h"
#include <libintl.h>
+ Include dependency graph for imap.c:

Go to the source code of this file.

Functions

void imap_init (void)
 Setup feature commands. More...
 
static int check_capabilities (struct ImapAccountData *adata)
 Make sure we can log in to this server. More...
 
static char * get_flags (struct ListHead *hflags, char *s)
 Make a simple list out of a FLAGS response. More...
 
static void set_flag (struct Mailbox *m, AclFlags aclflag, bool flag, const char *str, char *flags, size_t flsize)
 Append str to flags if we currently have permission according to aclflag. More...
 
static int make_msg_set (struct Mailbox *m, struct Buffer *buf, enum MessageType flag, bool changed, bool invert, int *pos)
 Make a message set. More...
 
static bool compare_flags_for_copy (struct Email *e)
 Compare local flags against the server. More...
 
static int sync_helper (struct Mailbox *m, AclFlags right, enum MessageType flag, const char *name)
 Sync flag changes to the server. More...
 
static size_t longest_common_prefix (char *dest, const char *src, size_t start, size_t dlen)
 Find longest prefix common to two strings. More...
 
static int complete_hosts (char *buf, size_t buflen)
 Look for completion matches for mailboxes. More...
 
int imap_create_mailbox (struct ImapAccountData *adata, const char *mailbox)
 Create a new mailbox. More...
 
int imap_access (const char *path)
 Check permissions on an IMAP mailbox with a new connection. More...
 
int imap_rename_mailbox (struct ImapAccountData *adata, char *oldname, const char *newname)
 Rename a mailbox. More...
 
int imap_delete_mailbox (struct Mailbox *m, char *path)
 Delete a mailbox. More...
 
static void imap_logout (struct ImapAccountData *adata)
 Gracefully log out of server. More...
 
void imap_logout_all (void)
 Close all open connections. More...
 
int imap_read_literal (FILE *fp, struct ImapAccountData *adata, unsigned long bytes, struct Progress *progress)
 Read bytes bytes from server into file. More...
 
void imap_notify_delete_email (struct Mailbox *m, struct Email *e)
 Inform IMAP that an Email has been deleted. More...
 
void imap_expunge_mailbox (struct Mailbox *m, bool resort)
 Purge messages from the server. More...
 
int imap_open_connection (struct ImapAccountData *adata)
 Open an IMAP connection. More...
 
void imap_close_connection (struct ImapAccountData *adata)
 Close an IMAP connection. More...
 
bool imap_has_flag (struct ListHead *flag_list, const char *flag)
 Does the flag exist in the list. More...
 
static int compare_uid (const void *a, const void *b)
 Compare two Emails by UID - Implements sort_t -. More...
 
int imap_exec_msgset (struct Mailbox *m, const char *pre, const char *post, enum MessageType flag, bool changed, bool invert)
 Prepare commands for all messages matching conditions. More...
 
int imap_sync_message_for_copy (struct Mailbox *m, struct Email *e, struct Buffer *cmd, enum QuadOption *err_continue)
 Update server to reflect the flags of a single message. More...
 
enum MxStatus imap_check_mailbox (struct Mailbox *m, bool force)
 Use the NOOP or IDLE command to poll for new mail. More...
 
static int imap_status (struct ImapAccountData *adata, struct ImapMboxData *mdata, bool queue)
 Refresh the number of total and new messages. More...
 
static enum MxStatus imap_mbox_check_stats (struct Mailbox *m, uint8_t flags)
 Check the Mailbox statistics - Implements MxOps::mbox_check_stats() -. More...
 
int imap_path_status (const char *path, bool queue)
 Refresh the number of total and new messages. More...
 
int imap_mailbox_status (struct Mailbox *m, bool queue)
 Refresh the number of total and new messages. More...
 
int imap_subscribe (char *path, bool subscribe)
 Subscribe to a mailbox. More...
 
int imap_complete (char *buf, size_t buflen, const char *path)
 Try to complete an IMAP folder path. More...
 
int imap_fast_trash (struct Mailbox *m, const char *dest)
 Use server COPY command to copy deleted messages to trash. More...
 
enum MxStatus imap_sync_mailbox (struct Mailbox *m, bool expunge, bool close)
 Sync all the changes to the server. More...
 
static bool imap_ac_owns_path (struct Account *a, const char *path)
 Check whether an Account owns a Mailbox path - Implements MxOps::ac_owns_path() -. More...
 
static bool imap_ac_add (struct Account *a, struct Mailbox *m)
 Add a Mailbox to an Account - Implements MxOps::ac_add() -. More...
 
static void imap_mbox_select (struct Mailbox *m)
 Select a Mailbox. More...
 
int imap_login (struct ImapAccountData *adata)
 Open an IMAP connection. More...
 
static enum MxOpenReturns imap_mbox_open (struct Mailbox *m)
 Open a mailbox - Implements MxOps::mbox_open() -. More...
 
static bool imap_mbox_open_append (struct Mailbox *m, OpenMailboxFlags flags)
 Open a Mailbox for appending - Implements MxOps::mbox_open_append() -. More...
 
static enum MxStatus imap_mbox_check (struct Mailbox *m)
 Check for new mail - Implements MxOps::mbox_check() -. More...
 
static enum MxStatus imap_mbox_close (struct Mailbox *m)
 Close a Mailbox - Implements MxOps::mbox_close() -. More...
 
static bool imap_msg_open_new (struct Mailbox *m, struct Message *msg, const struct Email *e)
 Open a new message in a Mailbox - Implements MxOps::msg_open_new() -. More...
 
static int imap_tags_edit (struct Mailbox *m, const char *tags, struct Buffer *buf)
 Prompt and validate new messages tags - Implements MxOps::tags_edit() -. More...
 
static int imap_tags_commit (struct Mailbox *m, struct Email *e, const char *buf)
 Save the tags to a message - Implements MxOps::tags_commit() -. More...
 
enum MailboxType imap_path_probe (const char *path, const struct stat *st)
 Is this an IMAP Mailbox? - Implements MxOps::path_probe() -. More...
 
int imap_path_canon (char *buf, size_t buflen)
 Canonicalise a Mailbox path - Implements MxOps::path_canon() -. More...
 
int imap_expand_path (struct Buffer *buf)
 Buffer wrapper around imap_path_canon() More...
 
static int imap_path_pretty (char *buf, size_t buflen, const char *folder)
 Abbreviate a Mailbox path - Implements MxOps::path_pretty() -. More...
 
static int imap_path_parent (char *buf, size_t buflen)
 Find the parent of a Mailbox path - Implements MxOps::path_parent() -. More...
 
static int imap_path_is_empty (const char *path)
 Is the mailbox empty - Implements MxOps::path_is_empty() -. More...
 

Variables

static const struct Command ImapCommands []
 Imap Commands. More...
 
struct MxOps MxImapOps
 IMAP Mailbox - Implements MxOps -. More...
 

Detailed Description

IMAP network mailbox.

Authors
  • Michael R. Elkins
  • Brandon Long
  • Brendan Cully
  • Richard Russon

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, see http://www.gnu.org/licenses/.

Definition in file imap.c.

Function Documentation

◆ imap_init()

void imap_init ( void  )

Setup feature commands.

Definition at line 86 of file imap.c.

87{
89}
void commands_register(const struct Command *cmds, const size_t num_cmds)
Add commands to Commands array.
Definition: command.c:53
static const struct Command ImapCommands[]
Imap Commands.
Definition: imap.c:76
#define mutt_array_size(x)
Definition: memory.h:36
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ check_capabilities()

static int check_capabilities ( struct ImapAccountData adata)
static

Make sure we can log in to this server.

Parameters
adataImap Account data
Return values
0Success
-1Failure

Definition at line 97 of file imap.c.

98{
99 if (imap_exec(adata, "CAPABILITY", IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS)
100 {
101 imap_error("check_capabilities", adata->buf);
102 return -1;
103 }
104
105 if (!((adata->capabilities & IMAP_CAP_IMAP4) || (adata->capabilities & IMAP_CAP_IMAP4REV1)))
106 {
107 mutt_error(_("This IMAP server is ancient. NeoMutt does not work with it."));
108 return -1;
109 }
110
111 return 0;
112}
#define mutt_error(...)
Definition: logging.h:87
int imap_exec(struct ImapAccountData *adata, const char *cmdstr, ImapCmdFlags flags)
Execute a command and wait for the response from the server.
Definition: command.c:1260
#define IMAP_CMD_NO_FLAGS
No flags are set.
Definition: private.h:73
#define IMAP_CAP_IMAP4
Server supports IMAP4.
Definition: private.h:123
#define IMAP_CAP_IMAP4REV1
Server supports IMAP4rev1.
Definition: private.h:124
@ IMAP_EXEC_SUCCESS
Imap command executed or queued successfully.
Definition: private.h:84
void imap_error(const char *where, const char *msg)
Show an error and abort.
Definition: util.c:657
#define _(a)
Definition: message.h:28
ImapCapFlags capabilities
Capability flags.
Definition: adata.h:55
char * buf
Definition: adata.h:59
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ get_flags()

static char * get_flags ( struct ListHead *  hflags,
char *  s 
)
static

Make a simple list out of a FLAGS response.

Parameters
hflagsList to store flags
sString containing flags
Return values
ptrEnd of the flags
NULLFailure

return stream following FLAGS response

Definition at line 123 of file imap.c.

124{
125 /* sanity-check string */
126 const size_t plen = mutt_istr_startswith(s, "FLAGS");
127 if (plen == 0)
128 {
129 mutt_debug(LL_DEBUG1, "not a FLAGS response: %s\n", s);
130 return NULL;
131 }
132 s += plen;
133 SKIPWS(s);
134 if (*s != '(')
135 {
136 mutt_debug(LL_DEBUG1, "bogus FLAGS response: %s\n", s);
137 return NULL;
138 }
139
140 /* update caller's flags handle */
141 while (*s && (*s != ')'))
142 {
143 s++;
144 SKIPWS(s);
145 const char *flag_word = s;
146 while (*s && (*s != ')') && !IS_SPACE(*s))
147 s++;
148 const char ctmp = *s;
149 *s = '\0';
150 if (*flag_word)
151 mutt_list_insert_tail(hflags, mutt_str_dup(flag_word));
152 *s = ctmp;
153 }
154
155 /* note bad flags response */
156 if (*s != ')')
157 {
158 mutt_debug(LL_DEBUG1, "Unterminated FLAGS response: %s\n", s);
159 mutt_list_free(hflags);
160
161 return NULL;
162 }
163
164 s++;
165
166 return s;
167}
#define mutt_debug(LEVEL,...)
Definition: logging.h:84
struct ListNode * mutt_list_insert_tail(struct ListHead *h, char *s)
Append a string to the end of a List.
Definition: list.c:64
void mutt_list_free(struct ListHead *h)
Free a List AND its strings.
Definition: list.c:122
@ LL_DEBUG1
Log at debug level 1.
Definition: logging.h:40
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:250
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition: string.c:239
static size_t plen
Length of cached packet.
Definition: pgppacket.c:39
#define IS_SPACE(ch)
Definition: string2.h:38
#define SKIPWS(ch)
Definition: string2.h:46
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ set_flag()

static void set_flag ( struct Mailbox m,
AclFlags  aclflag,
bool  flag,
const char *  str,
char *  flags,
size_t  flsize 
)
static

Append str to flags if we currently have permission according to aclflag.

Parameters
[in]mSelected Imap Mailbox
[in]aclflagPermissions, see AclFlags
[in]flagDoes the email have the flag set?
[in]strServer flag name
[out]flagsBuffer for server command
[in]flsizeLength of buffer

Definition at line 178 of file imap.c.

180{
181 if (m->rights & aclflag)
182 if (flag && imap_has_flag(&imap_mdata_get(m)->flags, str))
183 mutt_str_cat(flags, flsize, str);
184}
struct ImapMboxData * imap_mdata_get(struct Mailbox *m)
Get the Mailbox data for this mailbox.
Definition: mdata.c:60
bool imap_has_flag(struct ListHead *flag_list, const char *flag)
Does the flag exist in the list.
Definition: imap.c:888
char * mutt_str_cat(char *buf, size_t buflen, const char *s)
Concatenate two strings.
Definition: string.c:265
AclFlags rights
ACL bits, see AclFlags.
Definition: mailbox.h:117
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ make_msg_set()

static int make_msg_set ( struct Mailbox m,
struct Buffer buf,
enum MessageType  flag,
bool  changed,
bool  invert,
int *  pos 
)
static

Make a message set.

Parameters
[in]mSelected Imap Mailbox
[in]bufBuffer to store message set
[in]flagFlags to match, e.g. MUTT_DELETED
[in]changedMatched messages that have been altered
[in]invertFlag matches should be inverted
[out]posCursor used for multiple calls to this function
Return values
numMessages in the set
Note
Headers must be in SORT_ORDER. See imap_exec_msgset() for args. Pos is an opaque pointer a la strtok(). It should be 0 at first call.

Definition at line 199 of file imap.c.

201{
202 int count = 0; /* number of messages in message set */
203 unsigned int setstart = 0; /* start of current message range */
204 int n;
205 bool started = false;
206
208 if (!adata || (adata->mailbox != m))
209 return -1;
210
211 for (n = *pos; (n < m->msg_count) && (mutt_buffer_len(buf) < IMAP_MAX_CMDLEN); n++)
212 {
213 struct Email *e = m->emails[n];
214 if (!e)
215 break;
216 bool match = false; /* whether current message matches flag condition */
217 /* don't include pending expunged messages.
218 *
219 * TODO: can we unset active in cmd_parse_expunge() and
220 * cmd_parse_vanished() instead of checking for index != INT_MAX. */
221 if (e->active && (e->index != INT_MAX))
222 {
223 switch (flag)
224 {
225 case MUTT_DELETED:
226 if (e->deleted != imap_edata_get(e)->deleted)
227 match = invert ^ e->deleted;
228 break;
229 case MUTT_FLAG:
230 if (e->flagged != imap_edata_get(e)->flagged)
231 match = invert ^ e->flagged;
232 break;
233 case MUTT_OLD:
234 if (e->old != imap_edata_get(e)->old)
235 match = invert ^ e->old;
236 break;
237 case MUTT_READ:
238 if (e->read != imap_edata_get(e)->read)
239 match = invert ^ e->read;
240 break;
241 case MUTT_REPLIED:
242 if (e->replied != imap_edata_get(e)->replied)
243 match = invert ^ e->replied;
244 break;
245 case MUTT_TAG:
246 if (e->tagged)
247 match = true;
248 break;
249 case MUTT_TRASH:
250 if (e->deleted && !e->purge)
251 match = true;
252 break;
253 default:
254 break;
255 }
256 }
257
258 if (match && (!changed || e->changed))
259 {
260 count++;
261 if (setstart == 0)
262 {
263 setstart = imap_edata_get(e)->uid;
264 if (started)
265 {
266 mutt_buffer_add_printf(buf, ",%u", imap_edata_get(e)->uid);
267 }
268 else
269 {
270 mutt_buffer_add_printf(buf, "%u", imap_edata_get(e)->uid);
271 started = true;
272 }
273 }
274 /* tie up if the last message also matches */
275 else if (n == (m->msg_count - 1))
276 mutt_buffer_add_printf(buf, ":%u", imap_edata_get(e)->uid);
277 }
278 /* End current set if message doesn't match. */
279 else if (setstart)
280 {
281 if (imap_edata_get(m->emails[n - 1])->uid > setstart)
282 mutt_buffer_add_printf(buf, ":%u", imap_edata_get(m->emails[n - 1])->uid);
283 setstart = 0;
284 }
285 }
286
287 *pos = n;
288
289 return count;
290}
size_t mutt_buffer_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition: buffer.c:409
int mutt_buffer_add_printf(struct Buffer *buf, const char *fmt,...)
Format a string appending a Buffer.
Definition: buffer.c:211
struct ImapAccountData * imap_adata_get(struct Mailbox *m)
Get the Account data for this mailbox.
Definition: adata.c:89
struct ImapEmailData * imap_edata_get(struct Email *e)
Get the private data for this Email.
Definition: edata.c:65
#define IMAP_MAX_CMDLEN
Maximum length of command lines before they must be split (for lazy servers)
Definition: private.h:62
@ MUTT_TRASH
Trashed messages.
Definition: mutt.h:93
@ MUTT_READ
Messages that have been read.
Definition: mutt.h:81
@ MUTT_OLD
Old messages.
Definition: mutt.h:79
@ MUTT_TAG
Tagged messages.
Definition: mutt.h:88
@ MUTT_FLAG
Flagged messages.
Definition: mutt.h:87
@ MUTT_DELETED
Deleted messages.
Definition: mutt.h:86
@ MUTT_REPLIED
Messages that have been replied to.
Definition: mutt.h:80
void * adata
Private data (for Mailbox backends)
Definition: account.h:43
The envelope/body of an email.
Definition: email.h:37
bool read
Email is read.
Definition: email.h:48
bool purge
Skip trash folder when deleting.
Definition: email.h:77
bool active
Message is not to be removed.
Definition: email.h:74
bool old
Email is seen, but unread.
Definition: email.h:47
bool changed
Email has been edited.
Definition: email.h:75
bool flagged
Marked important?
Definition: email.h:45
bool replied
Email has been replied to.
Definition: email.h:49
bool deleted
Email is deleted.
Definition: email.h:76
int index
The absolute (unsorted) message number.
Definition: email.h:109
bool tagged
Email is tagged.
Definition: email.h:106
IMAP-specific Account data -.
Definition: adata.h:40
unsigned int uid
32-bit Message UID
Definition: edata.h:44
bool deleted
Email has been deleted.
Definition: edata.h:38
bool old
Email has been seen.
Definition: edata.h:37
bool read
Email has been read.
Definition: edata.h:36
bool flagged
Email has been flagged.
Definition: edata.h:39
bool replied
Email has been replied to.
Definition: edata.h:40
int msg_count
Total number of messages.
Definition: mailbox.h:88
struct Email ** emails
Array of Emails.
Definition: mailbox.h:96
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ compare_flags_for_copy()

static bool compare_flags_for_copy ( struct Email e)
static

Compare local flags against the server.

Parameters
eEmail
Return values
trueFlags have changed
falseFlags match cached server flags

The comparison of flags EXCLUDES the deleted flag.

Definition at line 300 of file imap.c.

301{
302 struct ImapEmailData *edata = e->edata;
303
304 if (e->read != edata->read)
305 return true;
306 if (e->old != edata->old)
307 return true;
308 if (e->flagged != edata->flagged)
309 return true;
310 if (e->replied != edata->replied)
311 return true;
312
313 return false;
314}
void * edata
Driver-specific data.
Definition: email.h:72
IMAP-specific Email data -.
Definition: edata.h:34
+ Here is the caller graph for this function:

◆ sync_helper()

static int sync_helper ( struct Mailbox m,
AclFlags  right,
enum MessageType  flag,
const char *  name 
)
static

Sync flag changes to the server.

Parameters
mSelected Imap Mailbox
rightACL, see AclFlags
flagNeoMutt flag, e.g. MUTT_DELETED
nameName of server flag
Return values
>=0Success, number of messages
-1Failure

Definition at line 325 of file imap.c.

327{
328 int count = 0;
329 int rc;
330 char buf[1024] = { 0 };
331
332 if (!m)
333 return -1;
334
335 if ((m->rights & right) == 0)
336 return 0;
337
338 if ((right == MUTT_ACL_WRITE) && !imap_has_flag(&imap_mdata_get(m)->flags, name))
339 return 0;
340
341 snprintf(buf, sizeof(buf), "+FLAGS.SILENT (%s)", name);
342 rc = imap_exec_msgset(m, "UID STORE", buf, flag, true, false);
343 if (rc < 0)
344 return rc;
345 count += rc;
346
347 buf[0] = '-';
348 rc = imap_exec_msgset(m, "UID STORE", buf, flag, true, true);
349 if (rc < 0)
350 return rc;
351 count += rc;
352
353 return count;
354}
int imap_exec_msgset(struct Mailbox *m, const char *pre, const char *post, enum MessageType flag, bool changed, bool invert)
Prepare commands for all messages matching conditions.
Definition: imap.c:940
#define MUTT_ACL_WRITE
Write to a message (for flagging or linking threads)
Definition: mailbox.h:71
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ longest_common_prefix()

static size_t longest_common_prefix ( char *  dest,
const char *  src,
size_t  start,
size_t  dlen 
)
static

Find longest prefix common to two strings.

Parameters
destDestination buffer
srcSource buffer
startStarting offset into string
dlenDestination buffer length
Return values
numLength of the common string

Trim dest to the length of the longest prefix it shares with src.

Definition at line 366 of file imap.c.

367{
368 size_t pos = start;
369
370 while ((pos < dlen) && dest[pos] && (dest[pos] == src[pos]))
371 pos++;
372 dest[pos] = '\0';
373
374 return pos;
375}
+ Here is the caller graph for this function:

◆ complete_hosts()

static int complete_hosts ( char *  buf,
size_t  buflen 
)
static

Look for completion matches for mailboxes.

Parameters
bufPartial mailbox name to complete
buflenLength of buffer
Return values
0Success
-1Failure

look for IMAP URLs to complete from defined mailboxes. Could be extended to complete over open connections and account/folder hooks too.

Definition at line 387 of file imap.c.

388{
389 // struct Connection *conn = NULL;
390 int rc = -1;
391 size_t matchlen;
392
393 matchlen = mutt_str_len(buf);
394 struct MailboxList ml = STAILQ_HEAD_INITIALIZER(ml);
396 struct MailboxNode *np = NULL;
397 STAILQ_FOREACH(np, &ml, entries)
398 {
400 continue;
401
402 if (rc)
403 {
404 mutt_str_copy(buf, mailbox_path(np->mailbox), buflen);
405 rc = 0;
406 }
407 else
408 {
409 longest_common_prefix(buf, mailbox_path(np->mailbox), matchlen, buflen);
410 }
411 }
413
414#if 0
415 TAILQ_FOREACH(conn, mutt_socket_head(), entries)
416 {
417 struct Url url = { 0 };
418 char urlstr[1024] = { 0 };
419
420 if (conn->account.type != MUTT_ACCT_TYPE_IMAP)
421 continue;
422
423 mutt_account_tourl(&conn->account, &url);
424 /* FIXME: how to handle multiple users on the same host? */
425 url.user = NULL;
426 url.path = NULL;
427 url_tostring(&url, urlstr, sizeof(urlstr), U_NO_FLAGS);
428 if (mutt_strn_equal(buf, urlstr, matchlen))
429 {
430 if (rc)
431 {
432 mutt_str_copy(buf, urlstr, buflen);
433 rc = 0;
434 }
435 else
436 {
437 longest_common_prefix(buf, urlstr, matchlen, buflen);
438 }
439 }
440 }
441#endif
442
443 return rc;
444}
static size_t longest_common_prefix(char *dest, const char *src, size_t start, size_t dlen)
Find longest prefix common to two strings.
Definition: imap.c:366
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox's path string.
Definition: mailbox.h:209
@ MUTT_MAILBOX_ANY
Match any Mailbox type.
Definition: mailbox.h:42
bool mutt_strn_equal(const char *a, const char *b, size_t num)
Check for equality of two strings (to a maximum), safely.
Definition: string.c:496
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:227
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:567
size_t mutt_str_copy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:652
void mutt_account_tourl(struct ConnAccount *cac, struct Url *url)
Fill URL with info from account.
Definition: mutt_account.c:79
@ MUTT_ACCT_TYPE_IMAP
Imap Account.
Definition: mutt_account.h:37
void neomutt_mailboxlist_clear(struct MailboxList *ml)
Free a Mailbox List.
Definition: neomutt.c:141
size_t neomutt_mailboxlist_get_all(struct MailboxList *head, struct NeoMutt *n, enum MailboxType type)
Get a List of all Mailboxes.
Definition: neomutt.c:164
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:725
#define STAILQ_HEAD_INITIALIZER(head)
Definition: queue.h:324
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:352
List of Mailboxes.
Definition: mailbox.h:152
struct Mailbox * mailbox
Mailbox in the list.
Definition: mailbox.h:153
Container for Accounts, Notifications.
Definition: neomutt.h:37
A parsed URL proto://user:password@host:port/path?a=1&b=2
Definition: url.h:69
char * user
Username.
Definition: url.h:71
char * path
Path.
Definition: url.h:75
int url_tostring(struct Url *url, char *dest, size_t len, uint8_t flags)
Output the URL string for a given Url object.
Definition: url.c:420
#define U_NO_FLAGS
Definition: url.h:49
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_create_mailbox()

int imap_create_mailbox ( struct ImapAccountData adata,
const char *  mailbox 
)

Create a new mailbox.

Parameters
adataImap Account data
mailboxMailbox to create
Return values
0Success
-1Failure

Definition at line 453 of file imap.c.

454{
455 char buf[2048], mbox[1024];
456
457 imap_munge_mbox_name(adata->unicode, mbox, sizeof(mbox), mailbox);
458 snprintf(buf, sizeof(buf), "CREATE %s", mbox);
459
461 {
462 mutt_error(_("CREATE failed: %s"), imap_cmd_trailer(adata));
463 return -1;
464 }
465
466 return 0;
467}
const char * imap_cmd_trailer(struct ImapAccountData *adata)
Extra information after tagged command response if any.
Definition: command.c:1223
void imap_munge_mbox_name(bool unicode, char *dest, size_t dlen, const char *src)
Quote awkward characters in a mailbox name.
Definition: util.c:907
bool unicode
If true, we can send UTF-8, and the server will use UTF8 rather than mUTF7.
Definition: adata.h:62
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_access()

int imap_access ( const char *  path)

Check permissions on an IMAP mailbox with a new connection.

Parameters
pathMailbox path
Return values
0Success
<0Failure

TODO: ACL checks. Right now we assume if it exists we can mess with it. TODO: This method should take a Mailbox as parameter to be able to reuse the existing connection.

Definition at line 479 of file imap.c.

480{
481 if (imap_path_status(path, false) >= 0)
482 return 0;
483 return -1;
484}
int imap_path_status(const char *path, bool queue)
Refresh the number of total and new messages.
Definition: imap.c:1249
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_rename_mailbox()

int imap_rename_mailbox ( struct ImapAccountData adata,
char *  oldname,
const char *  newname 
)

Rename a mailbox.

Parameters
adataImap Account data
oldnameExisting mailbox
newnameNew name for mailbox
Return values
0Success
-1Failure

Definition at line 494 of file imap.c.

495{
496 char oldmbox[1024] = { 0 };
497 char newmbox[1024] = { 0 };
498 int rc = 0;
499
500 imap_munge_mbox_name(adata->unicode, oldmbox, sizeof(oldmbox), oldname);
501 imap_munge_mbox_name(adata->unicode, newmbox, sizeof(newmbox), newname);
502
503 struct Buffer *buf = mutt_buffer_pool_get();
504 mutt_buffer_printf(buf, "RENAME %s %s", oldmbox, newmbox);
505
507 rc = -1;
508
510
511 return rc;
512}
int mutt_buffer_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:168
static const char * mutt_buffer_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:78
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
String manipulation buffer.
Definition: buffer.h:34
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_delete_mailbox()

int imap_delete_mailbox ( struct Mailbox m,
char *  path 
)

Delete a mailbox.

Parameters
mMailbox
pathname of the mailbox to delete
Return values
0Success
-1Failure

Definition at line 521 of file imap.c.

522{
523 char buf[PATH_MAX + 7];
524 char mbox[PATH_MAX] = { 0 };
525 struct Url *url = url_parse(path);
526
528 imap_munge_mbox_name(adata->unicode, mbox, sizeof(mbox), url->path);
529 url_free(&url);
530 snprintf(buf, sizeof(buf), "DELETE %s", mbox);
532 return -1;
533
534 return 0;
535}
#define PATH_MAX
Definition: mutt.h:41
struct Account * account
Account that owns this Mailbox.
Definition: mailbox.h:127
struct Url * url_parse(const char *src)
Fill in Url.
Definition: url.c:236
void url_free(struct Url **ptr)
Free the contents of a URL.
Definition: url.c:123
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_logout()

static void imap_logout ( struct ImapAccountData adata)
static

Gracefully log out of server.

Parameters
adataImap Account data

Definition at line 541 of file imap.c.

542{
543 /* we set status here to let imap_handle_untagged know we _expect_ to
544 * receive a bye response (so it doesn't freak out and close the conn) */
545 if (adata->state == IMAP_DISCONNECTED)
546 {
547 return;
548 }
549
550 adata->status = IMAP_BYE;
551 imap_cmd_start(adata, "LOGOUT");
552 const short c_imap_poll_timeout = cs_subset_number(NeoMutt->sub, "imap_poll_timeout");
553 if ((c_imap_poll_timeout <= 0) ||
554 (mutt_socket_poll(adata->conn, c_imap_poll_timeout) != 0))
555 {
556 while (imap_cmd_step(adata) == IMAP_RES_CONTINUE)
557 ; // do nothing
558 }
559 mutt_socket_close(adata->conn);
560 adata->state = IMAP_DISCONNECTED;
561}
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition: helpers.c:169
int imap_cmd_start(struct ImapAccountData *adata, const char *cmdstr)
Given an IMAP command, send it to the server.
Definition: command.c:1071
int imap_cmd_step(struct ImapAccountData *adata)
Reads server responses from an IMAP command.
Definition: command.c:1085
@ IMAP_DISCONNECTED
Disconnected from server.
Definition: private.h:107
@ IMAP_BYE
Logged out from server.
Definition: private.h:98
#define IMAP_RES_CONTINUE
* ...
Definition: private.h:57
int mutt_socket_close(struct Connection *conn)
Close a socket.
Definition: socket.c:101
int mutt_socket_poll(struct Connection *conn, time_t wait_secs)
Checks whether reads would block.
Definition: socket.c:196
unsigned char state
ImapState, e.g. IMAP_AUTHENTICATED.
Definition: adata.h:44
unsigned char status
ImapFlags, e.g. IMAP_FATAL.
Definition: adata.h:45
struct Connection * conn
Connection to IMAP server.
Definition: adata.h:41
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_logout_all()

void imap_logout_all ( void  )

Close all open connections.

Quick and dirty until we can make sure we've got all the context we need.

Definition at line 568 of file imap.c.

569{
570 struct Account *np = NULL;
571 TAILQ_FOREACH(np, &NeoMutt->accounts, entries)
572 {
573 if (np->type != MUTT_IMAP)
574 continue;
575
576 struct ImapAccountData *adata = np->adata;
577 if (!adata)
578 continue;
579
580 struct Connection *conn = adata->conn;
581 if (!conn || (conn->fd < 0))
582 continue;
583
584 mutt_message(_("Closing connection to %s..."), conn->account.host);
585 imap_logout(np->adata);
587 }
588}
#define mutt_message(...)
Definition: logging.h:86
static void imap_logout(struct ImapAccountData *adata)
Gracefully log out of server.
Definition: imap.c:541
@ MUTT_IMAP
'IMAP' Mailbox type
Definition: mailbox.h:50
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
Definition: mutt_logging.c:73
A group of associated Mailboxes.
Definition: account.h:37
enum MailboxType type
Type of Mailboxes this Account contains.
Definition: account.h:38
char host[128]
Server to login to.
Definition: connaccount.h:54
struct ConnAccount account
Account details: username, password, etc.
Definition: connection.h:50
int fd
Socket file descriptor.
Definition: connection.h:54
struct AccountList accounts
List of all Accounts.
Definition: neomutt.h:40
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_read_literal()

int imap_read_literal ( FILE *  fp,
struct ImapAccountData adata,
unsigned long  bytes,
struct Progress *  progress 
)

Read bytes bytes from server into file.

Parameters
fpFile handle for email file
adataImap Account data
bytesNumber of bytes to read
progressProgress bar
Return values
0Success
-1Failure

Not explicitly buffered, relies on FILE buffering.

Note
Strips \r from \r\n. Apparently even literals use \r\n-terminated strings ?!

Definition at line 604 of file imap.c.

606{
607 char c;
608 bool r = false;
609 struct Buffer buf = { 0 }; // Do not allocate, maybe it won't be used
610
611 const short c_debug_level = cs_subset_number(NeoMutt->sub, "debug_level");
612 if (c_debug_level >= IMAP_LOG_LTRL)
613 mutt_buffer_alloc(&buf, bytes + 1);
614
615 mutt_debug(LL_DEBUG2, "reading %lu bytes\n", bytes);
616
617 for (unsigned long pos = 0; pos < bytes; pos++)
618 {
619 if (mutt_socket_readchar(adata->conn, &c) != 1)
620 {
621 mutt_debug(LL_DEBUG1, "error during read, %lu bytes read\n", pos);
622 adata->status = IMAP_FATAL;
623
625 return -1;
626 }
627
628 if (r && (c != '\n'))
629 fputc('\r', fp);
630
631 if (c == '\r')
632 {
633 r = true;
634 continue;
635 }
636 else
637 {
638 r = false;
639 }
640
641 fputc(c, fp);
642
643 if (progress && !(pos % 1024))
644 progress_update(progress, pos, -1);
645 if (c_debug_level >= IMAP_LOG_LTRL)
646 mutt_buffer_addch(&buf, c);
647 }
648
649 if (c_debug_level >= IMAP_LOG_LTRL)
650 {
651 mutt_debug(IMAP_LOG_LTRL, "\n%s", buf.data);
653 }
654 return 0;
655}
void mutt_buffer_alloc(struct Buffer *buf, size_t new_size)
Make sure a buffer can store at least new_size bytes.
Definition: buffer.c:313
void mutt_buffer_dealloc(struct Buffer *buf)
Release the memory allocated by a buffer.
Definition: buffer.c:347
size_t mutt_buffer_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:248
#define IMAP_LOG_LTRL
Definition: private.h:50
@ IMAP_FATAL
Unrecoverable error occurred.
Definition: private.h:97
@ LL_DEBUG2
Log at debug level 2.
Definition: logging.h:41
bool progress_update(struct Progress *progress, size_t pos, int percent)
Update the state of the progress bar.
Definition: progress.c:73
int mutt_socket_readchar(struct Connection *conn, char *c)
Simple read buffering to speed things up.
Definition: socket.c:214
char * data
Pointer to data.
Definition: buffer.h:35
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_notify_delete_email()

void imap_notify_delete_email ( struct Mailbox m,
struct Email e 
)

Inform IMAP that an Email has been deleted.

Parameters
mMailbox
eEmail

Definition at line 662 of file imap.c.

663{
664 struct ImapMboxData *mdata = imap_mdata_get(m);
666
667 if (!mdata || !edata)
668 return;
669
670 imap_msn_remove(&mdata->msn, edata->msn - 1);
671 edata->msn = 0;
672}
void imap_msn_remove(struct MSN *msn, size_t idx)
Remove an entry from the cache.
Definition: msn.c:113
IMAP-specific Mailbox data -.
Definition: mdata.h:39
void * mdata
Driver specific data.
Definition: mailbox.h:132
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_expunge_mailbox()

void imap_expunge_mailbox ( struct Mailbox m,
bool  resort 
)

Purge messages from the server.

Parameters
mMailbox
resortTrigger a resort?

Purge IMAP portion of expunged messages from the context. Must not be done while something has a handle on any headers (eg inside pager or editor). That is, check IMAP_REOPEN_ALLOW.

Definition at line 683 of file imap.c.

684{
686 struct ImapMboxData *mdata = imap_mdata_get(m);
687 if (!adata || !mdata)
688 return;
689
690 struct Email *e = NULL;
691
692#ifdef USE_HCACHE
693 imap_hcache_open(adata, mdata);
694#endif
695
696 for (int i = 0; i < m->msg_count; i++)
697 {
698 e = m->emails[i];
699 if (!e)
700 break;
701
702 if (e->index == INT_MAX)
703 {
704 mutt_debug(LL_DEBUG2, "Expunging message UID %u\n", imap_edata_get(e)->uid);
705
706 e->deleted = true;
707
708 imap_cache_del(m, e);
709#ifdef USE_HCACHE
710 imap_hcache_del(mdata, imap_edata_get(e)->uid);
711#endif
712
713 mutt_hash_int_delete(mdata->uid_hash, imap_edata_get(e)->uid, e);
714
715 imap_edata_free((void **) &e->edata);
716 }
717 else
718 {
719 /* NeoMutt has several places where it turns off e->active as a
720 * hack. For example to avoid FLAG updates, or to exclude from
721 * imap_exec_msgset.
722 *
723 * Unfortunately, when a reopen is allowed and the IMAP_EXPUNGE_PENDING
724 * flag becomes set (e.g. a flag update to a modified header),
725 * this function will be called by imap_cmd_finish().
726 *
727 * The ctx_update_tables() will free and remove these "inactive" headers,
728 * despite that an EXPUNGE was not received for them.
729 * This would result in memory leaks and segfaults due to dangling
730 * pointers in the msn_index and uid_hash.
731 *
732 * So this is another hack to work around the hacks. We don't want to
733 * remove the messages, so make sure active is on. */
734 e->active = true;
735 }
736 }
737
738#ifdef USE_HCACHE
739 imap_hcache_close(mdata);
740#endif
741
743 if (resort)
744 {
746 }
747}
void mutt_hash_int_delete(struct HashTable *table, unsigned int intkey, const void *data)
Remove an element from a Hash Table.
Definition: hash.c:444
void imap_edata_free(void **ptr)
Free the private Email data - Implements Email::edata_free()
Definition: edata.c:39
int imap_cache_del(struct Mailbox *m, struct Email *e)
Delete an email from the body cache.
Definition: message.c:1834
void imap_hcache_close(struct ImapMboxData *mdata)
Close the header cache.
Definition: util.c:338
int imap_hcache_del(struct ImapMboxData *mdata, unsigned int uid)
Delete an item from the header cache.
Definition: util.c:397
void imap_hcache_open(struct ImapAccountData *adata, struct ImapMboxData *mdata)
Open a header cache.
Definition: util.c:296
void mailbox_changed(struct Mailbox *m, enum NotifyMailbox action)
Notify observers of a change to a Mailbox.
Definition: mailbox.c:222
@ NT_MAILBOX_RESORT
Email list needs resorting.
Definition: mailbox.h:176
@ NT_MAILBOX_UPDATE
Update internal tables.
Definition: mailbox.h:177
struct HashTable * uid_hash
Definition: mdata.h:58
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_open_connection()

int imap_open_connection ( struct ImapAccountData adata)

Open an IMAP connection.

Parameters
adataImap Account data
Return values
0Success
-1Failure

Definition at line 755 of file imap.c.

756{
757 if (mutt_socket_open(adata->conn) < 0)
758 return -1;
759
760 adata->state = IMAP_CONNECTED;
761
762 if (imap_cmd_step(adata) != IMAP_RES_OK)
763 {
765 return -1;
766 }
767
768 if (mutt_istr_startswith(adata->buf, "* OK"))
769 {
770 if (!mutt_istr_startswith(adata->buf, "* OK [CAPABILITY") && check_capabilities(adata))
771 {
772 goto bail;
773 }
774#ifdef USE_SSL
775 /* Attempt STARTTLS if available and desired. */
776 const bool c_ssl_force_tls = cs_subset_bool(NeoMutt->sub, "ssl_force_tls");
777 if ((adata->conn->ssf == 0) &&
778 (c_ssl_force_tls || (adata->capabilities & IMAP_CAP_STARTTLS)))
779 {
780 enum QuadOption ans;
781
782 const enum QuadOption c_ssl_starttls = cs_subset_quad(NeoMutt->sub, "ssl_starttls");
783 if (c_ssl_force_tls)
784 {
785 ans = MUTT_YES;
786 }
787 else if ((ans = query_quadoption(c_ssl_starttls, _("Secure connection with TLS?"))) == MUTT_ABORT)
788 {
789 goto bail;
790 }
791 if (ans == MUTT_YES)
792 {
793 enum ImapExecResult rc = imap_exec(adata, "STARTTLS", IMAP_CMD_SINGLE);
794 // Clear any data after the STARTTLS acknowledgement
795 mutt_socket_empty(adata->conn);
796
797 if (rc == IMAP_EXEC_FATAL)
798 goto bail;
799 if (rc != IMAP_EXEC_ERROR)
800 {
801 if (mutt_ssl_starttls(adata->conn))
802 {
803 mutt_error(_("Could not negotiate TLS connection"));
804 goto bail;
805 }
806 else
807 {
808 /* RFC2595 demands we recheck CAPABILITY after TLS completes. */
809 if (imap_exec(adata, "CAPABILITY", IMAP_CMD_NO_FLAGS))
810 goto bail;
811 }
812 }
813 }
814 }
815
816 if (c_ssl_force_tls && (adata->conn->ssf == 0))
817 {
818 mutt_error(_("Encrypted connection unavailable"));
819 goto bail;
820 }
821#endif
822 }
823 else if (mutt_istr_startswith(adata->buf, "* PREAUTH"))
824 {
825#ifdef USE_SSL
826 /* Unless using a secure $tunnel, an unencrypted PREAUTH response may be a
827 * MITM attack. The only way to stop "STARTTLS" MITM attacks is via
828 * $ssl_force_tls: an attacker can easily spoof "* OK" and strip the
829 * STARTTLS capability. So consult $ssl_force_tls, not $ssl_starttls, to
830 * decide whether to abort. Note that if using $tunnel and
831 * $tunnel_is_secure, adata->conn->ssf will be set to 1. */
832 const bool c_ssl_force_tls = cs_subset_bool(NeoMutt->sub, "ssl_force_tls");
833 if ((adata->conn->ssf == 0) && c_ssl_force_tls)
834 {
835 mutt_error(_("Encrypted connection unavailable"));
836 goto bail;
837 }
838#endif
839
840 adata->state = IMAP_AUTHENTICATED;
841 if (check_capabilities(adata) != 0)
842 goto bail;
843 FREE(&adata->capstr);
844 }
845 else
846 {
847 imap_error("imap_open_connection()", adata->buf);
848 goto bail;
849 }
850
851 return 0;
852
853bail:
855 FREE(&adata->capstr);
856 return -1;
857}
enum QuadOption cs_subset_quad(const struct ConfigSubset *sub, const char *name)
Get a quad-value config item by name.
Definition: helpers.c:218
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
int mutt_ssl_starttls(struct Connection *conn)
Negotiate TLS over an already opened connection.
Definition: gnutls.c:1143
@ IMAP_AUTHENTICATED
Connection is authenticated.
Definition: private.h:109
@ IMAP_CONNECTED
Connected to server.
Definition: private.h:108
#define IMAP_RES_OK
<tag> OK ...
Definition: private.h:56
#define IMAP_CAP_STARTTLS
RFC2595: STARTTLS.
Definition: private.h:133
ImapExecResult
Imap_exec return code.
Definition: private.h:83
@ IMAP_EXEC_ERROR
Imap command failure.
Definition: private.h:85
@ IMAP_EXEC_FATAL
Imap connection failure.
Definition: private.h:86
#define IMAP_CMD_SINGLE
Run a single command.
Definition: private.h:77
void imap_close_connection(struct ImapAccountData *adata)
Close an IMAP connection.
Definition: imap.c:863
static int check_capabilities(struct ImapAccountData *adata)
Make sure we can log in to this server.
Definition: imap.c:97
#define FREE(x)
Definition: memory.h:43
QuadOption
Possible values for a quad-option.
Definition: quad.h:36
@ MUTT_ABORT
User aborted the question (with Ctrl-G)
Definition: quad.h:37
@ MUTT_YES
User answered 'Yes', or assume 'Yes'.
Definition: quad.h:39
enum QuadOption query_quadoption(enum QuadOption opt, const char *prompt)
Ask the user a quad-question.
Definition: question.c:386
void mutt_socket_empty(struct Connection *conn)
Clear out any queued data.
Definition: socket.c:319
int mutt_socket_open(struct Connection *conn)
Simple wrapper.
Definition: socket.c:77
unsigned int ssf
Security strength factor, in bits (see notes)
Definition: connection.h:51
char * capstr
Capability string from the server.
Definition: adata.h:54
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_close_connection()

void imap_close_connection ( struct ImapAccountData adata)

Close an IMAP connection.

Parameters
adataImap Account data

Definition at line 863 of file imap.c.

864{
865 if (adata->state != IMAP_DISCONNECTED)
866 {
867 mutt_socket_close(adata->conn);
868 adata->state = IMAP_DISCONNECTED;
869 }
870 adata->seqno = 0;
871 adata->nextcmd = 0;
872 adata->lastcmd = 0;
873 adata->status = 0;
874 memset(adata->cmds, 0, sizeof(struct ImapCommand) * adata->cmdslots);
875}
int lastcmd
Last command in the queue.
Definition: adata.h:72
int nextcmd
Next command to be sent.
Definition: adata.h:71
struct ImapCommand * cmds
Queue of commands for the server.
Definition: adata.h:69
int cmdslots
Size of the command queue.
Definition: adata.h:70
unsigned int seqno
tag sequence number, e.g. '{seqid}0001'
Definition: adata.h:57
IMAP command structure.
Definition: private.h:162
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_has_flag()

bool imap_has_flag ( struct ListHead *  flag_list,
const char *  flag 
)

Does the flag exist in the list.

Parameters
flag_listList of server flags
flagFlag to find
Return values
trueFlag exists

Do a caseless comparison of the flag against a flag list, return true if found or flag list has '*'. Note that "flag" might contain additional whitespace at the end, so we really need to compare up to the length of each element in "flag_list".

Definition at line 888 of file imap.c.

889{
890 if (STAILQ_EMPTY(flag_list))
891 return false;
892
893 const size_t flaglen = mutt_str_len(flag);
894 struct ListNode *np = NULL;
895 STAILQ_FOREACH(np, flag_list, entries)
896 {
897 const size_t nplen = strlen(np->data);
898 if ((flaglen >= nplen) && ((flag[nplen] == '\0') || (flag[nplen] == ' ')) &&
899 mutt_istrn_equal(np->data, flag, nplen))
900 {
901 return true;
902 }
903
904 if (mutt_str_equal(np->data, "\\*"))
905 return true;
906 }
907
908 return false;
909}
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:807
bool mutt_istrn_equal(const char *a, const char *b, size_t num)
Check for equality of two strings ignoring case (to a maximum), safely.
Definition: string.c:524
#define STAILQ_EMPTY(head)
Definition: queue.h:348
A List node for strings.
Definition: list.h:35
char * data
String.
Definition: list.h:36
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_exec_msgset()

int imap_exec_msgset ( struct Mailbox m,
const char *  pre,
const char *  post,
enum MessageType  flag,
bool  changed,
bool  invert 
)

Prepare commands for all messages matching conditions.

Parameters
mSelected Imap Mailbox
preprefix commands
postpostfix commands
flagflag type on which to filter, e.g. MUTT_REPLIED
changedinclude only changed messages in message set
invertinvert sense of flag, eg MUTT_READ matches unread messages
Return values
numMatched messages
-1Failure

pre/post: commands are of the form "%s %s %s %s", tag, pre, message set, post Prepares commands for all messages matching conditions (must be flushed with imap_exec)

Definition at line 940 of file imap.c.

942{
944 if (!adata || (adata->mailbox != m))
945 return -1;
946
947 struct Email **emails = NULL;
948 int pos;
949 int rc;
950 int count = 0;
951
952 struct Buffer cmd = mutt_buffer_make(0);
953
954 /* We make a copy of the headers just in case resorting doesn't give
955 exactly the original order (duplicate messages?), because other parts of
956 the mv are tied to the header order. This may be overkill. */
957 const short c_sort = cs_subset_sort(NeoMutt->sub, "sort");
958 if (c_sort != SORT_ORDER)
959 {
960 emails = m->emails;
961 // We overcommit here, just in case new mail arrives whilst we're sync-ing
962 m->emails = mutt_mem_malloc(m->email_max * sizeof(struct Email *));
963 memcpy(m->emails, emails, m->email_max * sizeof(struct Email *));
964
966 qsort(m->emails, m->msg_count, sizeof(struct Email *), compare_uid);
967 }
968
969 pos = 0;
970
971 do
972 {
973 mutt_buffer_reset(&cmd);
974 mutt_buffer_add_printf(&cmd, "%s ", pre);
975 rc = make_msg_set(m, &cmd, flag, changed, invert, &pos);
976 if (rc > 0)
977 {
978 mutt_buffer_add_printf(&cmd, " %s", post);
980 {
981 rc = -1;
982 goto out;
983 }
984 count += rc;
985 }
986 } while (rc > 0);
987
988 rc = count;
989
990out:
992 if (c_sort != SORT_ORDER)
993 {
994 cs_subset_str_native_set(NeoMutt->sub, "sort", c_sort, NULL);
995 FREE(&m->emails);
996 m->emails = emails;
997 }
998
999 return rc;
1000}
struct Buffer mutt_buffer_make(size_t size)
Make a new buffer on the stack.
Definition: buffer.c:67
void mutt_buffer_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:85
short cs_subset_sort(const struct ConfigSubset *sub, const char *name)
Get a sort config item by name.
Definition: helpers.c:292
static int compare_uid(const void *a, const void *b)
Compare two Emails by UID - Implements sort_t -.
Definition: imap.c:914
#define IMAP_CMD_QUEUE
Queue a command, do not execute.
Definition: private.h:75
static int make_msg_set(struct Mailbox *m, struct Buffer *buf, enum MessageType flag, bool changed, bool invert, int *pos)
Make a message set.
Definition: imap.c:199
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
@ SORT_ORDER
Sort by the order the messages appear in the mailbox.
Definition: sort2.h:44
int email_max
Number of pointers in emails.
Definition: mailbox.h:97
int cs_subset_str_native_set(const struct ConfigSubset *sub, const char *name, intptr_t value, struct Buffer *err)
Natively set the value of a string config item.
Definition: subset.c:305
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_sync_message_for_copy()

int imap_sync_message_for_copy ( struct Mailbox m,
struct Email e,
struct Buffer cmd,
enum QuadOption err_continue 
)

Update server to reflect the flags of a single message.

Parameters
[in]mMailbox
[in]eEmail
[in]cmdBuffer for the command string
[out]err_continueDid the user force a continue?
Return values
0Success
-1Failure

Update the IMAP server to reflect the flags for a single message before performing a "UID COPY".

Note
This does not sync the "deleted" flag state, because it is not desirable to propagate that flag into the copy.

Definition at line 1017 of file imap.c.

1019{
1021 if (!adata || (adata->mailbox != m))
1022 return -1;
1023
1024 char flags[1024] = { 0 };
1025 char *tags = NULL;
1026 char uid[11] = { 0 };
1027
1028 if (!compare_flags_for_copy(e))
1029 {
1030 if (e->deleted == imap_edata_get(e)->deleted)
1031 e->changed = false;
1032 return 0;
1033 }
1034
1035 snprintf(uid, sizeof(uid), "%u", imap_edata_get(e)->uid);
1036 mutt_buffer_reset(cmd);
1037 mutt_buffer_addstr(cmd, "UID STORE ");
1038 mutt_buffer_addstr(cmd, uid);
1039
1040 flags[0] = '\0';
1041
1042 set_flag(m, MUTT_ACL_SEEN, e->read, "\\Seen ", flags, sizeof(flags));
1043 set_flag(m, MUTT_ACL_WRITE, e->old, "Old ", flags, sizeof(flags));
1044 set_flag(m, MUTT_ACL_WRITE, e->flagged, "\\Flagged ", flags, sizeof(flags));
1045 set_flag(m, MUTT_ACL_WRITE, e->replied, "\\Answered ", flags, sizeof(flags));
1046 set_flag(m, MUTT_ACL_DELETE, imap_edata_get(e)->deleted, "\\Deleted ", flags,
1047 sizeof(flags));
1048
1049 if (m->rights & MUTT_ACL_WRITE)
1050 {
1051 /* restore system flags */
1052 if (imap_edata_get(e)->flags_system)
1053 mutt_str_cat(flags, sizeof(flags), imap_edata_get(e)->flags_system);
1054 /* set custom flags */
1056 if (tags)
1057 {
1058 mutt_str_cat(flags, sizeof(flags), tags);
1059 FREE(&tags);
1060 }
1061 }
1062
1064
1065 /* UW-IMAP is OK with null flags, Cyrus isn't. The only solution is to
1066 * explicitly revoke all system flags (if we have permission) */
1067 if (*flags == '\0')
1068 {
1069 set_flag(m, MUTT_ACL_SEEN, true, "\\Seen ", flags, sizeof(flags));
1070 set_flag(m, MUTT_ACL_WRITE, true, "Old ", flags, sizeof(flags));
1071 set_flag(m, MUTT_ACL_WRITE, true, "\\Flagged ", flags, sizeof(flags));
1072 set_flag(m, MUTT_ACL_WRITE, true, "\\Answered ", flags, sizeof(flags));
1073 set_flag(m, MUTT_ACL_DELETE, !imap_edata_get(e)->deleted, "\\Deleted ",
1074 flags, sizeof(flags));
1075
1076 /* erase custom flags */
1077 if ((m->rights & MUTT_ACL_WRITE) && imap_edata_get(e)->flags_remote)
1078 mutt_str_cat(flags, sizeof(flags), imap_edata_get(e)->flags_remote);
1079
1081
1082 mutt_buffer_addstr(cmd, " -FLAGS.SILENT (");
1083 }
1084 else
1085 {
1086 mutt_buffer_addstr(cmd, " FLAGS.SILENT (");
1087 }
1088
1089 mutt_buffer_addstr(cmd, flags);
1090 mutt_buffer_addstr(cmd, ")");
1091
1092 /* after all this it's still possible to have no flags, if you
1093 * have no ACL rights */
1094 if (*flags && (imap_exec(adata, cmd->data, IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS) &&
1095 err_continue && (*err_continue != MUTT_YES))
1096 {
1097 *err_continue = imap_continue("imap_sync_message: STORE failed", adata->buf);
1098 if (*err_continue != MUTT_YES)
1099 return -1;
1100 }
1101
1102 /* server have now the updated flags */
1103 FREE(&imap_edata_get(e)->flags_remote);
1105
1106 if (e->deleted == imap_edata_get(e)->deleted)
1107 e->changed = false;
1108
1109 return 0;
1110}
size_t mutt_buffer_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:233
enum QuadOption imap_continue(const char *msg, const char *resp)
Display a message and ask the user if they want to go on.
Definition: util.c:646
static void set_flag(struct Mailbox *m, AclFlags aclflag, bool flag, const char *str, char *flags, size_t flsize)
Append str to flags if we currently have permission according to aclflag.
Definition: imap.c:178
static bool compare_flags_for_copy(struct Email *e)
Compare local flags against the server.
Definition: imap.c:300
#define MUTT_ACL_DELETE
Delete a message.
Definition: mailbox.h:63
#define MUTT_ACL_SEEN
Change the 'seen' status of a message.
Definition: mailbox.h:70
void mutt_str_remove_trailing_ws(char *s)
Trim trailing whitespace from a string.
Definition: string.c:636
struct TagList tags
For drivers that support server tagging.
Definition: email.h:70
char * flags_remote
Definition: edata.h:48
char * driver_tags_get_with_hidden(struct TagList *list)
Get tags with hiddens.
Definition: tags.c:158
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_check_mailbox()

enum MxStatus imap_check_mailbox ( struct Mailbox m,
bool  force 
)

Use the NOOP or IDLE command to poll for new mail.

Parameters
mMailbox
forceDon't wait
Return values
numMxStatus

Definition at line 1118 of file imap.c.

1119{
1120 if (!m || !m->account)
1121 return MX_STATUS_ERROR;
1122
1124 struct ImapMboxData *mdata = imap_mdata_get(m);
1125
1126 /* overload keyboard timeout to avoid many mailbox checks in a row.
1127 * Most users don't like having to wait exactly when they press a key. */
1128 int rc = 0;
1129
1130 /* try IDLE first, unless force is set */
1131 const bool c_imap_idle = cs_subset_bool(NeoMutt->sub, "imap_idle");
1132 const short c_imap_keepalive = cs_subset_number(NeoMutt->sub, "imap_keepalive");
1133 if (!force && c_imap_idle && (adata->capabilities & IMAP_CAP_IDLE) &&
1134 ((adata->state != IMAP_IDLE) || (mutt_date_now() >= adata->lastread + c_imap_keepalive)))
1135 {
1136 if (imap_cmd_idle(adata) < 0)
1137 return MX_STATUS_ERROR;
1138 }
1139 if (adata->state == IMAP_IDLE)
1140 {
1141 while ((rc = mutt_socket_poll(adata->conn, 0)) > 0)
1142 {
1143 if (imap_cmd_step(adata) != IMAP_RES_CONTINUE)
1144 {
1145 mutt_debug(LL_DEBUG1, "Error reading IDLE response\n");
1146 return MX_STATUS_ERROR;
1147 }
1148 }
1149 if (rc < 0)
1150 {
1151 mutt_debug(LL_DEBUG1, "Poll failed, disabling IDLE\n");
1152 adata->capabilities &= ~IMAP_CAP_IDLE; // Clear the flag
1153 }
1154 }
1155
1156 const short c_timeout = cs_subset_number(NeoMutt->sub, "timeout");
1157 if ((force || ((adata->state != IMAP_IDLE) && (mutt_date_now() >= adata->lastread + c_timeout))) &&
1158 (imap_exec(adata, "NOOP", IMAP_CMD_POLL) != IMAP_EXEC_SUCCESS))
1159 {
1160 return MX_STATUS_ERROR;
1161 }
1162
1163 /* We call this even when we haven't run NOOP in case we have pending
1164 * changes to process, since we can reopen here. */
1165 imap_cmd_finish(adata);
1166
1167 enum MxStatus check = MX_STATUS_OK;
1168 if (mdata->check_status & IMAP_EXPUNGE_PENDING)
1169 check = MX_STATUS_REOPENED;
1170 else if (mdata->check_status & IMAP_NEWMAIL_PENDING)
1171 check = MX_STATUS_NEW_MAIL;
1172 else if (mdata->check_status & IMAP_FLAGS_PENDING)
1173 check = MX_STATUS_FLAGS;
1174 else if (rc < 0)
1175 check = MX_STATUS_ERROR;
1176
1177 mdata->check_status = IMAP_OPEN_NO_FLAGS;
1178
1179 return check;
1180}
time_t mutt_date_now(void)
Return the number of seconds since the Unix epoch.
Definition: date.c:432
int imap_cmd_idle(struct ImapAccountData *adata)
Enter the IDLE state.
Definition: command.c:1392
void imap_cmd_finish(struct ImapAccountData *adata)
Attempt to perform cleanup.
Definition: command.c:1325
#define IMAP_CAP_IDLE
RFC2177: IDLE.
Definition: private.h:135
@ IMAP_IDLE
Connection is idle.
Definition: private.h:113
#define IMAP_EXPUNGE_PENDING
Messages on the server have been expunged.
Definition: private.h:68
#define IMAP_OPEN_NO_FLAGS
No flags are set.
Definition: private.h:65
#define IMAP_CMD_POLL
Poll the tcp connection before running the imap command.
Definition: private.h:76
#define IMAP_NEWMAIL_PENDING
New mail is waiting on the server.
Definition: private.h:69
#define IMAP_FLAGS_PENDING
Flags have changed on the server.
Definition: private.h:70
MxStatus
Return values from mbox_check(), mbox_check_stats(), mbox_snc(), and mbox_close()
Definition: mxapi.h:84
@ MX_STATUS_ERROR
An error occurred.
Definition: mxapi.h:85
@ MX_STATUS_OK
No changes.
Definition: mxapi.h:86
@ MX_STATUS_FLAGS
Nondestructive flags change (IMAP)
Definition: mxapi.h:90
@ MX_STATUS_REOPENED
Mailbox was reopened.
Definition: mxapi.h:89
@ MX_STATUS_NEW_MAIL
New mail received in Mailbox.
Definition: mxapi.h:87
time_t lastread
last time we read a command for the server
Definition: adata.h:58
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_status()

static int imap_status ( struct ImapAccountData adata,
struct ImapMboxData mdata,
bool  queue 
)
static

Refresh the number of total and new messages.

Parameters
adataIMAP Account data
mdataIMAP Mailbox data
queueQueue the STATUS command
Return values
numTotal number of messages

Definition at line 1189 of file imap.c.

1190{
1191 char *uidvalidity_flag = NULL;
1192 char cmd[2048] = { 0 };
1193
1194 if (!adata || !mdata)
1195 return -1;
1196
1197 /* Don't issue STATUS on the selected mailbox, it will be NOOPed or
1198 * IDLEd elsewhere.
1199 * adata->mailbox may be NULL for connections other than the current
1200 * mailbox's. */
1201 if (adata->mailbox && (adata->mailbox->mdata == mdata))
1202 {
1203 adata->mailbox->has_new = false;
1204 return mdata->messages;
1205 }
1206
1207 if (adata->capabilities & IMAP_CAP_IMAP4REV1)
1208 uidvalidity_flag = "UIDVALIDITY";
1209 else if (adata->capabilities & IMAP_CAP_STATUS)
1210 uidvalidity_flag = "UID-VALIDITY";
1211 else
1212 {
1213 mutt_debug(LL_DEBUG2, "Server doesn't support STATUS\n");
1214 return -1;
1215 }
1216
1217 snprintf(cmd, sizeof(cmd), "STATUS %s (UIDNEXT %s UNSEEN RECENT MESSAGES)",
1218 mdata->munge_name, uidvalidity_flag);
1219
1220 int rc = imap_exec(adata, cmd, queue ? IMAP_CMD_QUEUE : IMAP_CMD_NO_FLAGS | IMAP_CMD_POLL);
1221 if (rc < 0)
1222 {
1223 mutt_debug(LL_DEBUG1, "Error queueing command\n");
1224 return rc;
1225 }
1226 return mdata->messages;
1227}
#define IMAP_CAP_STATUS
Server supports STATUS command.
Definition: private.h:125
struct Mailbox * mailbox
Current selected mailbox.
Definition: adata.h:76
unsigned int messages
Definition: mdata.h:53
char * munge_name
Munged version of the mailbox name.
Definition: mdata.h:41
bool has_new
Mailbox has new mail.
Definition: mailbox.h:85
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_path_status()

int imap_path_status ( const char *  path,
bool  queue 
)

Refresh the number of total and new messages.

Parameters
pathMailbox path
queueQueue the STATUS command
Return values
numTotal number of messages

Definition at line 1249 of file imap.c.

1250{
1251 struct Mailbox *m = mx_mbox_find2(path);
1252
1253 const bool is_temp = !m;
1254 if (is_temp)
1255 {
1256 m = mx_path_resolve(path);
1257 if (!mx_mbox_ac_link(m))
1258 {
1259 mailbox_free(&m);
1260 return 0;
1261 }
1262 }
1263
1264 int rc = imap_mailbox_status(m, queue);
1265
1266 if (is_temp)
1267 {
1268 mx_ac_remove(m, false);
1269 mailbox_free(&m);
1270 }
1271
1272 return rc;
1273}
int imap_mailbox_status(struct Mailbox *m, bool queue)
Refresh the number of total and new messages.
Definition: imap.c:1284
void mailbox_free(struct Mailbox **ptr)
Free a Mailbox.
Definition: mailbox.c:87
int mx_ac_remove(struct Mailbox *m, bool keep_account)
Remove a Mailbox from an Account and delete Account if empty.
Definition: mx.c:1788
struct Mailbox * mx_mbox_find2(const char *path)
Find a Mailbox on an Account.
Definition: mx.c:1656
bool mx_mbox_ac_link(struct Mailbox *m)
Link a Mailbox to an existing or new Account.
Definition: mx.c:268
struct Mailbox * mx_path_resolve(const char *path)
Get a Mailbox for a path.
Definition: mx.c:1684
A mailbox.
Definition: mailbox.h:79
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_mailbox_status()

int imap_mailbox_status ( struct Mailbox m,
bool  queue 
)

Refresh the number of total and new messages.

Parameters
mMailbox
queueQueue the STATUS command
Return values
numTotal number of messages
-1Error
Note
Prepare the mailbox if we are not connected

Definition at line 1284 of file imap.c.

1285{
1287 struct ImapMboxData *mdata = imap_mdata_get(m);
1288 if (!adata || !mdata)
1289 return -1;
1290 return imap_status(adata, mdata, queue);
1291}
static int imap_status(struct ImapAccountData *adata, struct ImapMboxData *mdata, bool queue)
Refresh the number of total and new messages.
Definition: imap.c:1189
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_subscribe()

int imap_subscribe ( char *  path,
bool  subscribe 
)

Subscribe to a mailbox.

Parameters
pathMailbox path
subscribeTrue: subscribe, false: unsubscribe
Return values
0Success
-1Failure

Definition at line 1300 of file imap.c.

1301{
1302 struct ImapAccountData *adata = NULL;
1303 struct ImapMboxData *mdata = NULL;
1304 char buf[2048] = { 0 };
1305 struct Buffer err;
1306
1307 if (imap_adata_find(path, &adata, &mdata) < 0)
1308 return -1;
1309
1310 if (subscribe)
1311 mutt_message(_("Subscribing to %s..."), mdata->name);
1312 else
1313 mutt_message(_("Unsubscribing from %s..."), mdata->name);
1314
1315 snprintf(buf, sizeof(buf), "%sSUBSCRIBE %s", subscribe ? "" : "UN", mdata->munge_name);
1316
1317 if (imap_exec(adata, buf, IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS)
1318 {
1319 imap_mdata_free((void *) &mdata);
1320 return -1;
1321 }
1322
1323 const bool c_imap_check_subscribed = cs_subset_bool(NeoMutt->sub, "imap_check_subscribed");
1324 if (c_imap_check_subscribed)
1325 {
1326 char mbox[1024] = { 0 };
1327 mutt_buffer_init(&err);
1328 err.dsize = 256;
1329 err.data = mutt_mem_malloc(err.dsize);
1330 size_t len = snprintf(mbox, sizeof(mbox), "%smailboxes ", subscribe ? "" : "un");
1331 imap_quote_string(mbox + len, sizeof(mbox) - len, path, true);
1332 if (parse_rc_line(mbox, &err))
1333 mutt_debug(LL_DEBUG1, "Error adding subscribed mailbox: %s\n", err.data);
1334 FREE(&err.data);
1335 }
1336
1337 if (subscribe)
1338 mutt_message(_("Subscribed to %s"), mdata->name);
1339 else
1340 mutt_message(_("Unsubscribed from %s"), mdata->name);
1341 imap_mdata_free((void *) &mdata);
1342 return 0;
1343}
struct Buffer * mutt_buffer_init(struct Buffer *buf)
Initialise a new Buffer.
Definition: buffer.c:52
void imap_mdata_free(void **ptr)
Free the private Mailbox data - Implements Mailbox::mdata_free()
Definition: mdata.c:40
void imap_quote_string(char *dest, size_t dlen, const char *src, bool quote_backtick)
Quote string according to IMAP rules.
Definition: util.c:833
int imap_adata_find(const char *path, struct ImapAccountData **adata, struct ImapMboxData **mdata)
Find the Account data for this path.
Definition: util.c:71
enum CommandResult parse_rc_line(const char *line, struct Buffer *err)
Parse a line of user config.
Definition: rc.c:109
char * name
Mailbox name.
Definition: mdata.h:40
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_complete()

int imap_complete ( char *  buf,
size_t  buflen,
const char *  path 
)

Try to complete an IMAP folder path.

Parameters
bufBuffer for result
buflenLength of buffer
pathPartial mailbox name to complete
Return values
0Success
-1Failure

Given a partial IMAP folder path, return a string which adds as much to the path as is unique

Definition at line 1356 of file imap.c.

1357{
1358 struct ImapAccountData *adata = NULL;
1359 struct ImapMboxData *mdata = NULL;
1360 char tmp[2048] = { 0 };
1361 struct ImapList listresp = { 0 };
1362 char completion[1024] = { 0 };
1363 size_t clen;
1364 size_t matchlen = 0;
1365 int completions = 0;
1366 int rc;
1367
1368 if (imap_adata_find(path, &adata, &mdata) < 0)
1369 {
1370 mutt_str_copy(buf, path, buflen);
1371 return complete_hosts(buf, buflen);
1372 }
1373
1374 /* fire off command */
1375 const bool c_imap_list_subscribed = cs_subset_bool(NeoMutt->sub, "imap_list_subscribed");
1376 snprintf(tmp, sizeof(tmp), "%s \"\" \"%s%%\"",
1377 c_imap_list_subscribed ? "LSUB" : "LIST", mdata->real_name);
1378
1379 imap_cmd_start(adata, tmp);
1380
1381 /* and see what the results are */
1382 mutt_str_copy(completion, mdata->name, sizeof(completion));
1383 imap_mdata_free((void *) &mdata);
1384
1385 adata->cmdresult = &listresp;
1386 do
1387 {
1388 listresp.name = NULL;
1389 rc = imap_cmd_step(adata);
1390
1391 if ((rc == IMAP_RES_CONTINUE) && listresp.name)
1392 {
1393 /* if the folder isn't selectable, append delimiter to force browse
1394 * to enter it on second tab. */
1395 if (listresp.noselect)
1396 {
1397 clen = strlen(listresp.name);
1398 listresp.name[clen++] = listresp.delim;
1399 listresp.name[clen] = '\0';
1400 }
1401 /* copy in first word */
1402 if (!completions)
1403 {
1404 mutt_str_copy(completion, listresp.name, sizeof(completion));
1405 matchlen = strlen(completion);
1406 completions++;
1407 continue;
1408 }
1409
1410 matchlen = longest_common_prefix(completion, listresp.name, 0, matchlen);
1411 completions++;
1412 }
1413 } while (rc == IMAP_RES_CONTINUE);
1414 adata->cmdresult = NULL;
1415
1416 if (completions)
1417 {
1418 /* reformat output */
1419 imap_qualify_path(buf, buflen, &adata->conn->account, completion);
1420 mutt_pretty_mailbox(buf, buflen);
1421 return 0;
1422 }
1423
1424 return -1;
1425}
void mutt_pretty_mailbox(char *buf, size_t buflen)
Shorten a mailbox path using '~' or '='.
Definition: muttlib.c:469
void imap_qualify_path(char *buf, size_t buflen, struct ConnAccount *conn_account, char *path)
Make an absolute IMAP folder target.
Definition: util.c:816
static int complete_hosts(char *buf, size_t buflen)
Look for completion matches for mailboxes.
Definition: imap.c:387
struct ImapList * cmdresult
Definition: adata.h:66
Items in an IMAP browser.
Definition: private.h:151
bool noselect
Definition: private.h:154
char * name
Definition: private.h:152
char delim
Definition: private.h:153
char * real_name
Original Mailbox name, e.g.: INBOX can be just \0.
Definition: mdata.h:42
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_fast_trash()

int imap_fast_trash ( struct Mailbox m,
const char *  dest 
)

Use server COPY command to copy deleted messages to trash.

Parameters
mMailbox
destMailbox to move to
Return values
-1Error
0Success
1Non-fatal error - try fetch/append

Definition at line 1435 of file imap.c.

1436{
1437 char prompt[1024] = { 0 };
1438 int rc = -1;
1439 bool triedcreate = false;
1440 enum QuadOption err_continue = MUTT_NO;
1441
1443 struct ImapAccountData *dest_adata = NULL;
1444 struct ImapMboxData *dest_mdata = NULL;
1445
1446 if (imap_adata_find(dest, &dest_adata, &dest_mdata) < 0)
1447 return -1;
1448
1449 struct Buffer sync_cmd = mutt_buffer_make(0);
1450
1451 /* check that the save-to folder is in the same account */
1452 if (!imap_account_match(&(adata->conn->account), &(dest_adata->conn->account)))
1453 {
1454 mutt_debug(LL_DEBUG3, "%s not same server as %s\n", dest, mailbox_path(m));
1455 goto out;
1456 }
1457
1458 for (int i = 0; i < m->msg_count; i++)
1459 {
1460 struct Email *e = m->emails[i];
1461 if (!e)
1462 break;
1463 if (e->active && e->changed && e->deleted && !e->purge)
1464 {
1465 rc = imap_sync_message_for_copy(m, e, &sync_cmd, &err_continue);
1466 if (rc < 0)
1467 {
1468 mutt_debug(LL_DEBUG1, "could not sync\n");
1469 goto out;
1470 }
1471 }
1472 }
1473
1474 /* loop in case of TRYCREATE */
1475 do
1476 {
1477 rc = imap_exec_msgset(m, "UID COPY", dest_mdata->munge_name, MUTT_TRASH, false, false);
1478 if (rc == 0)
1479 {
1480 mutt_debug(LL_DEBUG1, "No messages to trash\n");
1481 rc = -1;
1482 goto out;
1483 }
1484 else if (rc < 0)
1485 {
1486 mutt_debug(LL_DEBUG1, "could not queue copy\n");
1487 goto out;
1488 }
1489 else if (m->verbose)
1490 {
1491 mutt_message(ngettext("Copying %d message to %s...", "Copying %d messages to %s...", rc),
1492 rc, dest_mdata->name);
1493 }
1494
1495 /* let's get it on */
1496 rc = imap_exec(adata, NULL, IMAP_CMD_NO_FLAGS);
1497 if (rc == IMAP_EXEC_ERROR)
1498 {
1499 if (triedcreate)
1500 {
1501 mutt_debug(LL_DEBUG1, "Already tried to create mailbox %s\n", dest_mdata->name);
1502 break;
1503 }
1504 /* bail out if command failed for reasons other than nonexistent target */
1505 if (!mutt_istr_startswith(imap_get_qualifier(adata->buf), "[TRYCREATE]"))
1506 break;
1507 mutt_debug(LL_DEBUG3, "server suggests TRYCREATE\n");
1508 snprintf(prompt, sizeof(prompt), _("Create %s?"), dest_mdata->name);
1509 const bool c_confirm_create = cs_subset_bool(NeoMutt->sub, "confirm_create");
1510 if (c_confirm_create && (mutt_yesorno(prompt, MUTT_YES) != MUTT_YES))
1511 {
1513 goto out;
1514 }
1515 if (imap_create_mailbox(adata, dest_mdata->name) < 0)
1516 break;
1517 triedcreate = true;
1518 }
1519 } while (rc == IMAP_EXEC_ERROR);
1520
1521 if (rc != IMAP_EXEC_SUCCESS)
1522 {
1523 imap_error("imap_fast_trash", adata->buf);
1524 goto out;
1525 }
1526
1527 rc = IMAP_EXEC_SUCCESS;
1528
1529out:
1530 mutt_buffer_dealloc(&sync_cmd);
1531 imap_mdata_free((void *) &dest_mdata);
1532
1533 return ((rc == IMAP_EXEC_SUCCESS) ? 0 : -1);
1534}
bool imap_account_match(const struct ConnAccount *a1, const struct ConnAccount *a2)
Compare two Accounts.
Definition: util.c:1040
char * imap_get_qualifier(char *buf)
Get the qualifier from a tagged response.
Definition: util.c:768
int imap_create_mailbox(struct ImapAccountData *adata, const char *mailbox)
Create a new mailbox.
Definition: imap.c:453
int imap_sync_message_for_copy(struct Mailbox *m, struct Email *e, struct Buffer *cmd, enum QuadOption *err_continue)
Update server to reflect the flags of a single message.
Definition: imap.c:1017
@ LL_DEBUG3
Log at debug level 3.
Definition: logging.h:42
@ MUTT_NO
User answered 'No', or assume 'No'.
Definition: quad.h:38
enum QuadOption mutt_yesorno(const char *msg, enum QuadOption def)
Ask the user a Yes/No question.
Definition: question.c:194
bool verbose
Display status messages?
Definition: mailbox.h:114
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_sync_mailbox()

enum MxStatus imap_sync_mailbox ( struct Mailbox m,
bool  expunge,
bool  close 
)

Sync all the changes to the server.

Parameters
mMailbox
expungeif true do expunge
closeif true we move imap state to CLOSE
Return values
enumMxStatus
Note
The flag retvals come from a call to imap_check_mailbox()

Definition at line 1545 of file imap.c.

1546{
1547 if (!m)
1548 return -1;
1549
1550 struct Email **emails = NULL;
1551 int rc;
1552
1554 struct ImapMboxData *mdata = imap_mdata_get(m);
1555
1556 if (adata->state < IMAP_SELECTED)
1557 {
1558 mutt_debug(LL_DEBUG2, "no mailbox selected\n");
1559 return -1;
1560 }
1561
1562 /* This function is only called when the calling code expects the context
1563 * to be changed. */
1565
1566 enum MxStatus check = imap_check_mailbox(m, false);
1567 if (check == MX_STATUS_ERROR)
1568 return check;
1569
1570 /* if we are expunging anyway, we can do deleted messages very quickly... */
1571 if (expunge && (m->rights & MUTT_ACL_DELETE))
1572 {
1573 rc = imap_exec_msgset(m, "UID STORE", "+FLAGS.SILENT (\\Deleted)",
1574 MUTT_DELETED, true, false);
1575 if (rc < 0)
1576 {
1577 mutt_error(_("Expunge failed"));
1578 return rc;
1579 }
1580
1581 if (rc > 0)
1582 {
1583 /* mark these messages as unchanged so second pass ignores them. Done
1584 * here so BOGUS UW-IMAP 4.7 SILENT FLAGS updates are ignored. */
1585 for (int i = 0; i < m->msg_count; i++)
1586 {
1587 struct Email *e = m->emails[i];
1588 if (!e)
1589 break;
1590 if (e->deleted && e->changed)
1591 e->active = false;
1592 }
1593 if (m->verbose)
1594 {
1595 mutt_message(ngettext("Marking %d message deleted...",
1596 "Marking %d messages deleted...", rc),
1597 rc);
1598 }
1599 }
1600 }
1601
1602#ifdef USE_HCACHE
1603 imap_hcache_open(adata, mdata);
1604#endif
1605
1606 /* save messages with real (non-flag) changes */
1607 for (int i = 0; i < m->msg_count; i++)
1608 {
1609 struct Email *e = m->emails[i];
1610 if (!e)
1611 break;
1612
1613 if (e->deleted)
1614 {
1615 imap_cache_del(m, e);
1616#ifdef USE_HCACHE
1617 imap_hcache_del(mdata, imap_edata_get(e)->uid);
1618#endif
1619 }
1620
1621 if (e->active && e->changed)
1622 {
1623#ifdef USE_HCACHE
1624 imap_hcache_put(mdata, e);
1625#endif
1626 /* if the message has been rethreaded or attachments have been deleted
1627 * we delete the message and reupload it.
1628 * This works better if we're expunging, of course. */
1629 /* TODO: why the e->env check? */
1630 if ((e->env && e->env->changed) || e->attach_del)
1631 {
1632 /* L10N: The plural is chosen by the last %d, i.e. the total number */
1633 if (m->verbose)
1634 {
1635 mutt_message(ngettext("Saving changed message... [%d/%d]",
1636 "Saving changed messages... [%d/%d]", m->msg_count),
1637 i + 1, m->msg_count);
1638 }
1639 bool save_append = m->append;
1640 m->append = true;
1642 m->append = save_append;
1643 /* TODO: why the check for e->env? Is this possible? */
1644 if (e->env)
1645 e->env->changed = 0;
1646 }
1647 }
1648 }
1649
1650#ifdef USE_HCACHE
1651 imap_hcache_close(mdata);
1652#endif
1653
1654 /* presort here to avoid doing 10 resorts in imap_exec_msgset */
1655 const short c_sort = cs_subset_sort(NeoMutt->sub, "sort");
1656 if (c_sort != SORT_ORDER)
1657 {
1658 emails = m->emails;
1659 m->emails = mutt_mem_malloc(m->msg_count * sizeof(struct Email *));
1660 memcpy(m->emails, emails, m->msg_count * sizeof(struct Email *));
1661
1663 qsort(m->emails, m->msg_count, sizeof(struct Email *), compare_uid);
1664 }
1665
1666 rc = sync_helper(m, MUTT_ACL_DELETE, MUTT_DELETED, "\\Deleted");
1667 if (rc >= 0)
1668 rc |= sync_helper(m, MUTT_ACL_WRITE, MUTT_FLAG, "\\Flagged");
1669 if (rc >= 0)
1670 rc |= sync_helper(m, MUTT_ACL_WRITE, MUTT_OLD, "Old");
1671 if (rc >= 0)
1672 rc |= sync_helper(m, MUTT_ACL_SEEN, MUTT_READ, "\\Seen");
1673 if (rc >= 0)
1674 rc |= sync_helper(m, MUTT_ACL_WRITE, MUTT_REPLIED, "\\Answered");
1675
1676 if (c_sort != SORT_ORDER)
1677 {
1678 cs_subset_str_native_set(NeoMutt->sub, "sort", c_sort, NULL);
1679 FREE(&m->emails);
1680 m->emails = emails;
1681 }
1682
1683 /* Flush the queued flags if any were changed in sync_helper. */
1684 if (rc > 0)
1685 if (imap_exec(adata, NULL, IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS)
1686 rc = -1;
1687
1688 if (rc < 0)
1689 {
1690 if (close)
1691 {
1692 if (mutt_yesorno(_("Error saving flags. Close anyway?"), MUTT_NO) == MUTT_YES)
1693 {
1694 adata->state = IMAP_AUTHENTICATED;
1695 return 0;
1696 }
1697 }
1698 else
1699 {
1700 mutt_error(_("Error saving flags"));
1701 }
1702 return -1;
1703 }
1704
1705 /* Update local record of server state to reflect the synchronization just
1706 * completed. imap_read_headers always overwrites hcache-origin flags, so
1707 * there is no need to mutate the hcache after flag-only changes. */
1708 for (int i = 0; i < m->msg_count; i++)
1709 {
1710 struct Email *e = m->emails[i];
1711 if (!e)
1712 break;
1713 struct ImapEmailData *edata = imap_edata_get(e);
1714 edata->deleted = e->deleted;
1715 edata->flagged = e->flagged;
1716 edata->old = e->old;
1717 edata->read = e->read;
1718 edata->replied = e->replied;
1719 e->changed = false;
1720 }
1721 m->changed = false;
1722
1723 /* We must send an EXPUNGE command if we're not closing. */
1724 if (expunge && !close && (m->rights & MUTT_ACL_DELETE))
1725 {
1726 if (m->verbose)
1727 mutt_message(_("Expunging messages from server..."));
1728 /* Set expunge bit so we don't get spurious reopened messages */
1729 mdata->reopen |= IMAP_EXPUNGE_EXPECTED;
1730 if (imap_exec(adata, "EXPUNGE", IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS)
1731 {
1732 mdata->reopen &= ~IMAP_EXPUNGE_EXPECTED;
1733 imap_error(_("imap_sync_mailbox: EXPUNGE failed"), adata->buf);
1734 return -1;
1735 }
1736 mdata->reopen &= ~IMAP_EXPUNGE_EXPECTED;
1737 }
1738
1739 if (expunge && close)
1740 {
1741 adata->closing = true;
1742 imap_exec(adata, "CLOSE", IMAP_CMD_NO_FLAGS);
1743 adata->state = IMAP_AUTHENTICATED;
1744 }
1745
1746 const bool c_message_cache_clean = cs_subset_bool(NeoMutt->sub, "message_cache_clean");
1747 if (c_message_cache_clean)
1749
1750 return check;
1751}
int mutt_save_message_ctx(struct Mailbox *m_src, struct Email *e, enum MessageSaveOpt save_opt, enum MessageTransformOpt transform_opt, struct Mailbox *m_dst)
Save a message to a given mailbox.
Definition: external.c:759
@ TRANSFORM_NONE
No transformation.
Definition: external.h:40
@ SAVE_MOVE
Move message to another mailbox, removing the original.
Definition: external.h:51
int imap_cache_clean(struct Mailbox *m)
Delete all the entries in the message cache.
Definition: message.c:1853
void imap_allow_reopen(struct Mailbox *m)
Allow re-opening a folder upon expunge.
Definition: util.c:1012
@ IMAP_SELECTED
Mailbox is selected.
Definition: private.h:110
#define IMAP_EXPUNGE_EXPECTED
Messages will be expunged from the server.
Definition: private.h:67
int imap_hcache_put(struct ImapMboxData *mdata, struct Email *e)
Add an entry to the header cache.
Definition: util.c:379
static int sync_helper(struct Mailbox *m, AclFlags right, enum MessageType flag, const char *name)
Sync flag changes to the server.
Definition: imap.c:325
enum MxStatus imap_check_mailbox(struct Mailbox *m, bool force)
Use the NOOP or IDLE command to poll for new mail.
Definition: imap.c:1118
struct Envelope * env
Envelope information.
Definition: email.h:66
bool attach_del
Has an attachment marked for deletion.
Definition: email.h:98
unsigned char changed
Changed fields, e.g. MUTT_ENV_CHANGED_SUBJECT.
Definition: envelope.h:92
bool closing
If true, we are waiting for CLOSE completion.
Definition: adata.h:43
ImapOpenFlags reopen
Flags, e.g. IMAP_REOPEN_ALLOW.
Definition: mdata.h:44
bool changed
Mailbox has been modified.
Definition: mailbox.h:110
bool append
Mailbox is opened in append mode.
Definition: mailbox.h:109
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_mbox_select()

static void imap_mbox_select ( struct Mailbox m)
static

Select a Mailbox.

Parameters
mMailbox

Definition at line 1828 of file imap.c.

1829{
1831 struct ImapMboxData *mdata = imap_mdata_get(m);
1832 if (!adata || !mdata)
1833 return;
1834
1835 const char *condstore = NULL;
1836#ifdef USE_HCACHE
1837 const bool c_imap_condstore = cs_subset_bool(NeoMutt->sub, "imap_condstore");
1838 if ((adata->capabilities & IMAP_CAP_CONDSTORE) && c_imap_condstore)
1839 condstore = " (CONDSTORE)";
1840 else
1841#endif
1842 condstore = "";
1843
1844 char buf[PATH_MAX] = { 0 };
1845 snprintf(buf, sizeof(buf), "%s %s%s", m->readonly ? "EXAMINE" : "SELECT",
1846 mdata->munge_name, condstore);
1847
1848 adata->state = IMAP_SELECTED;
1849
1850 imap_cmd_start(adata, buf);
1851}
#define IMAP_CAP_CONDSTORE
RFC7162.
Definition: private.h:138
bool readonly
Don't allow changes to the mailbox.
Definition: mailbox.h:115
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_login()

int imap_login ( struct ImapAccountData adata)

Open an IMAP connection.

Parameters
adataImap Account data
Return values
0Success
-1Failure

Ensure ImapAccountData is connected and logged into the imap server.

Definition at line 1861 of file imap.c.

1862{
1863 if (!adata)
1864 return -1;
1865
1866 if (adata->state == IMAP_DISCONNECTED)
1867 {
1868 mutt_buffer_reset(&adata->cmdbuf); // purge outstanding queued commands
1869 imap_open_connection(adata);
1870 }
1871 if (adata->state == IMAP_CONNECTED)
1872 {
1874 {
1875 adata->state = IMAP_AUTHENTICATED;
1876 FREE(&adata->capstr);
1877 if (adata->conn->ssf != 0)
1878 {
1879 mutt_debug(LL_DEBUG2, "Communication encrypted at %d bits\n",
1880 adata->conn->ssf);
1881 }
1882 }
1883 else
1884 {
1886 }
1887 }
1888 if (adata->state == IMAP_AUTHENTICATED)
1889 {
1890 /* capabilities may have changed */
1891 imap_exec(adata, "CAPABILITY", IMAP_CMD_PASS);
1892
1893#ifdef USE_ZLIB
1894 /* RFC4978 */
1895 const bool c_imap_deflate = cs_subset_bool(NeoMutt->sub, "imap_deflate");
1896 if ((adata->capabilities & IMAP_CAP_COMPRESS) && c_imap_deflate &&
1897 (imap_exec(adata, "COMPRESS DEFLATE", IMAP_CMD_PASS) == IMAP_EXEC_SUCCESS))
1898 {
1899 mutt_debug(LL_DEBUG2, "IMAP compression is enabled on connection to %s\n",
1900 adata->conn->account.host);
1901 mutt_zstrm_wrap_conn(adata->conn);
1902 }
1903#endif
1904
1905 /* enable RFC2971, if the server supports that */
1906 const bool c_imap_send_id = cs_subset_bool(NeoMutt->sub, "imap_send_id");
1907 if (c_imap_send_id && (adata->capabilities & IMAP_CAP_ID))
1908 {
1909 imap_exec(adata, "ID (\"name\" \"NeoMutt\" \"version\" \"" PACKAGE_VERSION "\")",
1911 }
1912
1913 /* enable RFC6855, if the server supports that */
1914 const bool c_imap_rfc5161 = cs_subset_bool(NeoMutt->sub, "imap_rfc5161");
1915 if (c_imap_rfc5161 && (adata->capabilities & IMAP_CAP_ENABLE))
1916 imap_exec(adata, "ENABLE UTF8=ACCEPT", IMAP_CMD_QUEUE);
1917
1918 /* enable QRESYNC. Advertising QRESYNC also means CONDSTORE
1919 * is supported (even if not advertised), so flip that bit. */
1920 if (adata->capabilities & IMAP_CAP_QRESYNC)
1921 {
1923 const bool c_imap_qresync = cs_subset_bool(NeoMutt->sub, "imap_qresync");
1924 if (c_imap_rfc5161 && c_imap_qresync)
1925 imap_exec(adata, "ENABLE QRESYNC", IMAP_CMD_QUEUE);
1926 }
1927
1928 /* get root delimiter, '/' as default */
1929 adata->delim = '/';
1930 imap_exec(adata, "LIST \"\" \"\"", IMAP_CMD_QUEUE);
1931
1932 /* we may need the root delimiter before we open a mailbox */
1933 imap_exec(adata, NULL, IMAP_CMD_NO_FLAGS);
1934
1935 /* select the mailbox that used to be open before disconnect */
1936 if (adata->mailbox)
1937 {
1938 imap_mbox_select(adata->mailbox);
1939 }
1940 }
1941
1942 if (adata->state < IMAP_AUTHENTICATED)
1943 return -1;
1944
1945 return 0;
1946}
@ IMAP_AUTH_SUCCESS
Authentication successful.
Definition: auth.h:39
void mutt_account_unsetpass(struct ConnAccount *cac)
Unset ConnAccount's password.
Definition: connaccount.c:176
int imap_authenticate(struct ImapAccountData *adata)
Authenticate to an IMAP server.
Definition: auth.c:110
#define IMAP_CAP_ENABLE
RFC5161.
Definition: private.h:137
#define IMAP_CAP_ID
RFC2971: IMAP4 ID extension.
Definition: private.h:143
#define IMAP_CMD_PASS
Command contains a password. Suppress logging.
Definition: private.h:74
#define IMAP_CAP_QRESYNC
RFC7162.
Definition: private.h:139
#define IMAP_CAP_COMPRESS
RFC4978: COMPRESS=DEFLATE.
Definition: private.h:141
int imap_open_connection(struct ImapAccountData *adata)
Open an IMAP connection.
Definition: imap.c:755
static void imap_mbox_select(struct Mailbox *m)
Select a Mailbox.
Definition: imap.c:1828
char delim
Path delimiter.
Definition: adata.h:75
struct Buffer cmdbuf
Definition: adata.h:73
void mutt_zstrm_wrap_conn(struct Connection *conn)
Wrap a compression layer around a Connection.
Definition: zstrm.c:288
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_expand_path()

int imap_expand_path ( struct Buffer buf)

Buffer wrapper around imap_path_canon()

Parameters
bufPath to expand
Return values
0Success
-1Failure
Note
The path is expanded in place

Definition at line 2465 of file imap.c.

2466{
2468 return imap_path_canon(buf->data, PATH_MAX);
2469}
int imap_path_canon(char *buf, size_t buflen)
Canonicalise a Mailbox path - Implements MxOps::path_canon() -.
Definition: imap.c:2439
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

Variable Documentation

◆ ImapCommands

const struct Command ImapCommands[]
static
Initial value:
= {
{ "subscribe-to", parse_subscribe_to, 0 },
{ "unsubscribe-from", parse_unsubscribe_from, 0 },
}
enum CommandResult parse_unsubscribe_from(struct Buffer *buf, struct Buffer *s, intptr_t data, struct Buffer *err)
Parse the 'unsubscribe-from' command - Implements Command::parse() -.
Definition: commands.c:1383
enum CommandResult parse_subscribe_to(struct Buffer *buf, struct Buffer *s, intptr_t data, struct Buffer *err)
Parse the 'subscribe-to' command - Implements Command::parse() -.
Definition: commands.c:1045

Imap Commands.

Definition at line 76 of file imap.c.