NeoMutt  2025-12-11-911-gd8d604
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
imap.c File Reference

IMAP network mailbox. More...

#include "config.h"
#include <errno.h>
#include <limits.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <time.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 "commands/lib.h"
#include "editor/lib.h"
#include "history/lib.h"
#include "hooks/lib.h"
#include "parse/lib.h"
#include "progress/lib.h"
#include "question/lib.h"
#include "adata.h"
#include "auth.h"
#include "edata.h"
#include "external.h"
#include "mdata.h"
#include "msg_set.h"
#include "msn.h"
#include "mutt_logging.h"
#include "mutt_socket.h"
#include "muttlib.h"
#include "mx.h"
#include <libintl.h>
+ Include dependency graph for imap.c:

Go to the source code of this file.

Functions

enum CommandResult parse_subscribe_to (const struct Command *cmd, struct Buffer *line, const struct ParseContext *pc, struct ParseError *pe)
 Parse the 'subscribe-to' command - Implements Command::parse() -.
 
enum CommandResult parse_unsubscribe_from (const struct Command *cmd, struct Buffer *line, const struct ParseContext *pc, struct ParseError *pe)
 Parse the 'unsubscribe-from' command - Implements Command::parse() -.
 
static int check_capabilities (struct ImapAccountData *adata)
 Make sure we can log in to this server.
 
static char * get_flags (struct ListHead *hflags, char *s)
 Make a simple list out of a FLAGS response.
 
static void set_flag (struct Mailbox *m, AclFlags aclflag, bool flag, const char *str, struct Buffer *flags)
 Append str to flags if we currently have permission according to aclflag.
 
static bool compare_flags_for_copy (struct Email *e)
 Compare local flags against the server.
 
static int select_email_uids (struct Email **emails, int num_emails, enum MessageType flag, bool changed, bool invert, struct UidArray *uida)
 Create a list of Email UIDs by type.
 
static int sync_helper (struct Mailbox *m, struct Email **emails, int num_emails, AclFlags right, enum MessageType flag, const char *name)
 Sync flag changes to the server.
 
static size_t longest_common_prefix (struct Buffer *buf, const char *src, size_t start)
 Find longest prefix common to two strings.
 
static int complete_hosts (struct Buffer *buf)
 Look for completion matches for mailboxes.
 
int imap_create_mailbox (struct ImapAccountData *adata, const char *mailbox)
 Create a new mailbox.
 
int imap_access (const char *path)
 Check permissions on an IMAP mailbox with a new connection.
 
int imap_rename_mailbox (struct ImapAccountData *adata, char *oldname, const char *newname)
 Rename a mailbox.
 
int imap_delete_mailbox (struct Mailbox *m, char *path)
 Delete a mailbox.
 
static void imap_logout (struct ImapAccountData *adata)
 Gracefully log out of server.
 
void imap_logout_all (void)
 Close all open connections.
 
int imap_read_literal (FILE *fp, struct ImapAccountData *adata, unsigned long bytes, struct Progress *progress)
 Read bytes bytes from server into file.
 
void imap_notify_delete_email (struct Mailbox *m, struct Email *e)
 Inform IMAP that an Email has been deleted.
 
void imap_expunge_mailbox (struct Mailbox *m, bool resort)
 Purge messages from the server.
 
int imap_open_connection (struct ImapAccountData *adata)
 Open an IMAP connection.
 
void imap_close_connection (struct ImapAccountData *adata)
 Close an IMAP connection.
 
bool imap_has_flag (struct ListHead *flag_list, const char *flag)
 Does the flag exist in the list.
 
static int imap_sort_email_uid (const void *a, const void *b, void *sdata)
 Compare two Emails by UID - Implements sort_t -.
 
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.
 
enum MxStatus imap_check_mailbox (struct Mailbox *m, bool force)
 Use the NOOP or IDLE command to poll for new mail.
 
static int imap_status (struct ImapAccountData *adata, struct ImapMboxData *mdata, bool queue)
 Refresh the number of total and new messages.
 
static enum MxStatus imap_mbox_check_stats (struct Mailbox *m, uint8_t flags)
 Check the Mailbox statistics - Implements MxOps::mbox_check_stats() -.
 
int imap_path_status (const char *path, bool queue)
 Refresh the number of total and new messages.
 
int imap_mailbox_status (struct Mailbox *m, bool queue)
 Refresh the number of total and new messages.
 
int imap_subscribe (const char *path, bool subscribe)
 Subscribe to a mailbox.
 
int imap_complete (struct Buffer *buf, const char *path)
 Try to complete an IMAP folder path.
 
int imap_fast_trash (struct Mailbox *m, const char *dest)
 Use server COPY command to copy deleted messages to trash.
 
enum MxStatus imap_sync_mailbox (struct Mailbox *m, bool expunge, bool close)
 Sync all the changes to the server.
 
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() -.
 
static bool imap_ac_add (struct Account *a, struct Mailbox *m)
 Add a Mailbox to an Account - Implements MxOps::ac_add() -.
 
static void imap_mbox_select (struct Mailbox *m)
 Select a Mailbox.
 
int imap_login (struct ImapAccountData *adata)
 Open an IMAP connection.
 
static int imap_select_and_poll (struct Mailbox *m, int *countp)
 Send SELECT and parse the untagged responses.
 
int imap_reopen_mailbox (struct ImapAccountData *adata)
 Re-SELECT the current mailbox after reconnecting.
 
static enum MxOpenReturns imap_mbox_open (struct Mailbox *m)
 Open a mailbox - Implements MxOps::mbox_open() -.
 
static bool imap_mbox_open_append (struct Mailbox *m, OpenMailboxFlags flags)
 Open a Mailbox for appending - Implements MxOps::mbox_open_append() -.
 
static enum MxStatus imap_mbox_check (struct Mailbox *m)
 Check for new mail - Implements MxOps::mbox_check() -.
 
static enum MxStatus imap_mbox_close (struct Mailbox *m)
 Close a Mailbox - Implements MxOps::mbox_close() -.
 
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() -.
 
static int imap_tags_edit (struct Mailbox *m, const char *tags, struct Buffer *buf)
 Prompt and validate new messages tags - Implements MxOps::tags_edit() -.
 
static int imap_tags_commit (struct Mailbox *m, struct Email *e, const char *buf)
 Save the tags to a message - Implements MxOps::tags_commit() -.
 
enum MailboxType imap_path_probe (const char *path, const struct stat *st)
 Is this an IMAP Mailbox?
 
int imap_path_canon (struct Buffer *path)
 Canonicalise a Mailbox path - Implements MxOps::path_canon() -.
 
static int imap_path_is_empty (struct Buffer *path)
 Is the mailbox empty - Implements MxOps::path_is_empty() -.
 

Variables

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

Detailed Description

IMAP network mailbox.

Authors
  • Michael R. Elkins
  • Brandon Long
  • Brendan Cully
  • Richard Russon
  • Mehdi Abaakouk
  • Pietro Cerutti
  • Federico Kircheis
  • Ian Zimmerman
  • Sergey Alirzaev
  • Reto Brunner
  • Anna Figueiredo Gomes
  • Dennis Schön
  • Thomas Klausner

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

◆ 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 207 of file imap.c.

208{
209 if (imap_exec(adata, "CAPABILITY", IMAP_CMD_NONE) != IMAP_EXEC_SUCCESS)
210 {
211 imap_error("check_capabilities", adata->buf);
212 return -1;
213 }
214
215 if (!((adata->capabilities & IMAP_CAP_IMAP4) || (adata->capabilities & IMAP_CAP_IMAP4REV1)))
216 {
217 mutt_error(_("This IMAP server is ancient. NeoMutt does not work with it."));
218 return -1;
219 }
220
221 return 0;
222}
#define mutt_error(...)
Definition logging2.h:94
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:1420
@ IMAP_EXEC_SUCCESS
Imap command executed or queued successfully.
Definition private.h:95
void imap_error(const char *where, const char *msg)
Show an error and abort.
Definition util.c:665
@ IMAP_CAP_IMAP4REV1
Server supports IMAP4rev1.
Definition private.h:136
@ IMAP_CAP_IMAP4
Server supports IMAP4.
Definition private.h:135
@ IMAP_CMD_NONE
No flags are set.
Definition private.h:82
#define _(a)
Definition message.h:28
ImapCapFlags capabilities
Capability flags.
Definition adata.h:56
char * buf
Command buffer.
Definition adata.h:61
+ 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 233 of file imap.c.

234{
235 /* sanity-check string */
236 const size_t plen = mutt_istr_startswith(s, "FLAGS");
237 if (plen == 0)
238 {
239 mutt_debug(LL_DEBUG1, "not a FLAGS response: %s\n", s);
240 return NULL;
241 }
242 s += plen;
243 SKIPWS(s);
244 if (*s != '(')
245 {
246 mutt_debug(LL_DEBUG1, "bogus FLAGS response: %s\n", s);
247 return NULL;
248 }
249
250 /* update caller's flags handle */
251 while (*s && (*s != ')'))
252 {
253 s++;
254 SKIPWS(s);
255 const char *flag_word = s;
256 while (*s && (*s != ')') && !mutt_isspace(*s))
257 s++;
258 const char ctmp = *s;
259 *s = '\0';
260 if (*flag_word)
261 mutt_list_insert_tail(hflags, mutt_str_dup(flag_word));
262 *s = ctmp;
263 }
264
265 /* note bad flags response */
266 if (*s != ')')
267 {
268 mutt_debug(LL_DEBUG1, "Unterminated FLAGS response: %s\n", s);
269 mutt_list_free(hflags);
270
271 return NULL;
272 }
273
274 s++;
275
276 return s;
277}
bool mutt_isspace(int arg)
Wrapper for isspace(3)
Definition ctype.c:96
#define mutt_debug(LEVEL,...)
Definition logging2.h:91
struct ListNode * mutt_list_insert_tail(struct ListHead *h, char *s)
Append a string to the end of a List.
Definition list.c:65
void mutt_list_free(struct ListHead *h)
Free a List AND its strings.
Definition list.c:123
@ LL_DEBUG1
Log at debug level 1.
Definition logging2.h:45
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition string.c:257
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition string.c:246
#define SKIPWS(ch)
Definition string2.h:52
+ 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,
struct Buffer * flags )
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

Definition at line 287 of file imap.c.

289{
290 struct ImapMboxData *mdata = imap_mdata_get(m);
291 if (!mdata)
292 return;
293
294 if (m->rights & aclflag)
295 if (flag && imap_has_flag(&mdata->flags, str))
296 buf_addstr(flags, str);
297}
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition buffer.c:226
struct ImapMboxData * imap_mdata_get(struct Mailbox *m)
Get the Mailbox data for this mailbox.
Definition mdata.c:61
bool imap_has_flag(struct ListHead *flag_list, const char *flag)
Does the flag exist in the list.
Definition imap.c:1054
IMAP-specific Mailbox data -.
Definition mdata.h:40
struct ListHead flags
List of permanent flags.
Definition mdata.h:50
AclFlags rights
ACL bits, see AclFlags.
Definition mailbox.h:121
void * mdata
Driver specific data.
Definition mailbox.h:134
+ 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 307 of file imap.c.

308{
309 struct ImapEmailData *edata = e->edata;
310
311 if (e->read != edata->read)
312 return true;
313 if (e->old != edata->old)
314 return true;
315 if (e->flagged != edata->flagged)
316 return true;
317 if (e->replied != edata->replied)
318 return true;
319
320 return false;
321}
bool read
Email is read.
Definition email.h:50
void * edata
Driver-specific data.
Definition email.h:74
bool old
Email is seen, but unread.
Definition email.h:49
bool flagged
Marked important?
Definition email.h:47
bool replied
Email has been replied to.
Definition email.h:51
IMAP-specific Email data -.
Definition edata.h:35
+ Here is the caller graph for this function:

◆ select_email_uids()

static int select_email_uids ( struct Email ** emails,
int num_emails,
enum MessageType flag,
bool changed,
bool invert,
struct UidArray * uida )
static

Create a list of Email UIDs by type.

Parameters
emailsArray of Emails
num_emailsNumber of Emails in the array
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
uidaArray to fill with UIDs
Return values
numNumber of UIDs added
-1Error

Definition at line 334 of file imap.c.

336{
337 if (!emails || !uida)
338 return -1;
339
340 for (int i = 0; i < num_emails; i++)
341 {
342 struct Email *e = emails[i];
343 if (!e || (changed && !e->changed))
344 continue;
345
346 /* don't include pending expunged messages.
347 *
348 * TODO: can we unset active in cmd_parse_expunge() and
349 * cmd_parse_vanished() instead of checking for index != INT_MAX. */
350 if (!e || !e->active || (e->index == INT_MAX))
351 continue;
352
354
355 bool match = false;
356 switch (flag)
357 {
358 case MUTT_DELETED:
359 if (e->deleted != edata->deleted)
360 match = invert ^ e->deleted;
361 break;
362 case MUTT_FLAG:
363 if (e->flagged != edata->flagged)
364 match = invert ^ e->flagged;
365 break;
366 case MUTT_OLD:
367 if (e->old != edata->old)
368 match = invert ^ e->old;
369 break;
370 case MUTT_READ:
371 if (e->read != edata->read)
372 match = invert ^ e->read;
373 break;
374 case MUTT_REPLIED:
375 if (e->replied != edata->replied)
376 match = invert ^ e->replied;
377 break;
378 case MUTT_TRASH:
379 if (e->deleted && !e->purge)
380 match = true;
381 break;
382 default:
383 break;
384 }
385
386 if (match)
387 ARRAY_ADD(uida, edata->uid);
388 }
389
390 return ARRAY_SIZE(uida);
391}
#define ARRAY_ADD(head, elem)
Add an element at the end of the array.
Definition array.h:157
#define ARRAY_SIZE(head)
The number of elements stored.
Definition array.h:87
struct ImapEmailData * imap_edata_get(struct Email *e)
Get the private data for this Email.
Definition edata.c:66
@ MUTT_TRASH
Trashed messages.
Definition mutt.h:104
@ MUTT_READ
Messages that have been read.
Definition mutt.h:92
@ MUTT_OLD
Old messages.
Definition mutt.h:90
@ MUTT_FLAG
Flagged messages.
Definition mutt.h:98
@ MUTT_DELETED
Deleted messages.
Definition mutt.h:97
@ MUTT_REPLIED
Messages that have been replied to.
Definition mutt.h:91
The envelope/body of an email.
Definition email.h:39
bool purge
Skip trash folder when deleting.
Definition email.h:79
bool active
Message is not to be removed.
Definition email.h:76
bool changed
Email has been edited.
Definition email.h:77
bool deleted
Email is deleted.
Definition email.h:78
int index
The absolute (unsorted) message number.
Definition email.h:110
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ sync_helper()

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

Sync flag changes to the server.

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

Definition at line 404 of file imap.c.

406{
408 struct ImapMboxData *mdata = imap_mdata_get(m);
409 if (!adata || !mdata)
410 return -1;
411
412 if ((m->rights & right) == 0)
413 return 0;
414
415 if ((right == MUTT_ACL_WRITE) && !imap_has_flag(&mdata->flags, name))
416 return 0;
417
418 int count = 0;
419 char buf[1024] = { 0 };
420
421 struct UidArray uida = ARRAY_HEAD_INITIALIZER;
422
423 // Set the flag (+FLAGS) on matching emails
424 select_email_uids(emails, num_emails, flag, true, false, &uida);
425 snprintf(buf, sizeof(buf), "+FLAGS.SILENT (%s)", name);
426 int rc = imap_exec_msg_set(adata, "UID STORE", buf, &uida);
427 if (rc < 0)
428 return rc;
429 count += rc;
430 ARRAY_FREE(&uida);
431
432 // Clear the flag (-FLAGS) on non-matching emails
433 select_email_uids(emails, num_emails, flag, true, true, &uida);
434 buf[0] = '-';
435 rc = imap_exec_msg_set(adata, "UID STORE", buf, &uida);
436 if (rc < 0)
437 return rc;
438 count += rc;
439 ARRAY_FREE(&uida);
440
441 return count;
442}
#define ARRAY_FREE(head)
Release all memory.
Definition array.h:209
#define ARRAY_HEAD_INITIALIZER
Static initializer for arrays.
Definition array.h:58
@ MUTT_ACL_WRITE
Write to a message (for flagging or linking threads)
Definition mailbox.h:71
struct ImapAccountData * imap_adata_get(struct Mailbox *m)
Get the Account data for this mailbox.
Definition adata.c:162
static int select_email_uids(struct Email **emails, int num_emails, enum MessageType flag, bool changed, bool invert, struct UidArray *uida)
Create a list of Email UIDs by type.
Definition imap.c:334
int imap_exec_msg_set(struct ImapAccountData *adata, const char *pre, const char *post, struct UidArray *uida)
Execute a command using a set of UIDs.
Definition msg_set.c:127
void * adata
Private data (for Mailbox backends)
Definition account.h:42
IMAP-specific Account data -.
Definition adata.h:40
char * name
Mailbox name.
Definition mdata.h:41
+ 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 ( struct Buffer * buf,
const char * src,
size_t start )
static

Find longest prefix common to two strings.

Parameters
bufDestination buffer
srcSource buffer
startStarting offset into string
Return values
numLength of the common string

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

Definition at line 453 of file imap.c.

454{
455 size_t pos = start;
456
457 size_t len = buf_len(buf);
458 while ((pos < len) && (buf_at(buf, pos) != '\0') && (buf_at(buf, pos) == src[pos]))
459 pos++;
460 buf->data[pos] = '\0';
461
462 buf_fix_dptr(buf);
463
464 return pos;
465}
size_t buf_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition buffer.c:491
void buf_fix_dptr(struct Buffer *buf)
Move the dptr to end of the Buffer.
Definition buffer.c:182
char buf_at(const struct Buffer *buf, size_t offset)
Return the character at the given offset.
Definition buffer.c:668
char * data
Pointer to data.
Definition buffer.h:37
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ complete_hosts()

static int complete_hosts ( struct Buffer * buf)
static

Look for completion matches for mailboxes.

Parameters
bufPartial mailbox name to complete
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 476 of file imap.c.

477{
478 int rc = -1;
479 size_t matchlen;
480
481 matchlen = buf_len(buf);
482 struct MailboxArray ma = neomutt_mailboxes_get(NeoMutt, MUTT_MAILBOX_ANY);
483 struct Mailbox **mp = NULL;
484 ARRAY_FOREACH(mp, &ma)
485 {
486 struct Mailbox *m = *mp;
487
489 continue;
490
491 if (rc)
492 {
493 buf_strcpy(buf, mailbox_path(m));
494 rc = 0;
495 }
496 else
497 {
498 longest_common_prefix(buf, mailbox_path(m), matchlen);
499 }
500 }
501 ARRAY_FREE(&ma); // Clean up the ARRAY, but not the Mailboxes
502
503#if 0
504 TAILQ_FOREACH(conn, mutt_socket_head(), entries)
505 {
506 struct Url url = { 0 };
507 char urlstr[1024] = { 0 };
508
509 if (conn->account.type != MUTT_ACCT_TYPE_IMAP)
510 continue;
511
512 account_to_url(&conn->account, &url);
513 /* FIXME: how to handle multiple users on the same host? */
514 url.user = NULL;
515 url.path = NULL;
516 url_tostring(&url, urlstr, sizeof(urlstr), U_NONE);
517 if (mutt_strn_equal(buf, urlstr, matchlen))
518 {
519 if (rc)
520 {
521 mutt_str_copy(buf, urlstr, buflen);
522 rc = 0;
523 }
524 else
525 {
526 longest_common_prefix(buf, urlstr, matchlen);
527 }
528 }
529 }
530#endif
531
532 return rc;
533}
#define ARRAY_FOREACH(elem, head)
Iterate over all elements of the array.
Definition array.h:223
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition buffer.c:395
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition buffer.h:96
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox's path string.
Definition mailbox.h:216
@ MUTT_MAILBOX_ANY
Match any Mailbox type.
Definition mailbox.h:41
static size_t longest_common_prefix(struct Buffer *buf, const char *src, size_t start)
Find longest prefix common to two strings.
Definition imap.c:453
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:429
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition string.c:234
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:586
void account_to_url(struct ConnAccount *cac, struct Url *url)
Fill URL with info from account.
@ MUTT_ACCT_TYPE_IMAP
Imap Account.
struct MailboxArray neomutt_mailboxes_get(struct NeoMutt *n, enum MailboxType type)
Get an Array of matching Mailboxes.
Definition neomutt.c:604
#define TAILQ_FOREACH(var, head, field)
Definition queue.h:782
A mailbox.
Definition mailbox.h:81
Container for Accounts, Notifications.
Definition neomutt.h:41
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(const struct Url *url, char *dest, size_t len, uint8_t flags)
Output the URL string for a given Url object.
Definition url.c:426
#define U_NONE
No flags are set for URL parsing.
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 542 of file imap.c.

543{
544 char buf[2048] = { 0 };
545 char mbox[1024] = { 0 };
546
547 imap_munge_mbox_name(adata->unicode, mbox, sizeof(mbox), mailbox);
548 snprintf(buf, sizeof(buf), "CREATE %s", mbox);
549
550 if (imap_exec(adata, buf, IMAP_CMD_NONE) != IMAP_EXEC_SUCCESS)
551 {
552 mutt_error(_("CREATE failed: %s"), imap_cmd_trailer(adata));
553 return -1;
554 }
555
556 return 0;
557}
const char * imap_cmd_trailer(struct ImapAccountData *adata)
Extra information after tagged command response if any.
Definition command.c:1383
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:971
bool unicode
If true, we can send UTF-8, and the server will use UTF8 rather than mUTF7.
Definition adata.h:64
+ 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 569 of file imap.c.

570{
571 if (imap_path_status(path, false) >= 0)
572 return 0;
573 return -1;
574}
int imap_path_status(const char *path, bool queue)
Refresh the number of total and new messages.
Definition imap.c:1362
+ 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 584 of file imap.c.

585{
586 char oldmbox[1024] = { 0 };
587 char newmbox[1024] = { 0 };
588 int rc = 0;
589
590 imap_munge_mbox_name(adata->unicode, oldmbox, sizeof(oldmbox), oldname);
591 imap_munge_mbox_name(adata->unicode, newmbox, sizeof(newmbox), newname);
592
593 struct Buffer *buf = buf_pool_get();
594 buf_printf(buf, "RENAME %s %s", oldmbox, newmbox);
595
597 rc = -1;
598
599 buf_pool_release(&buf);
600
601 return rc;
602}
int buf_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition buffer.c:161
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition pool.c:91
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition pool.c:111
String manipulation buffer.
Definition buffer.h:36
+ 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 611 of file imap.c.

612{
613 char buf[PATH_MAX + 7];
614 char mbox[PATH_MAX] = { 0 };
615
617 if (!adata)
618 return -1;
619
620 struct Url *url = url_parse(path);
621 if (!url)
622 return -1;
623
624 imap_munge_mbox_name(adata->unicode, mbox, sizeof(mbox), url->path);
625 url_free(&url);
626 snprintf(buf, sizeof(buf), "DELETE %s", mbox);
628 return -1;
629
630 return 0;
631}
#define PATH_MAX
Definition mutt.h:49
struct Account * account
Account that owns this Mailbox.
Definition mailbox.h:129
struct Url * url_parse(const char *src)
Fill in Url.
Definition url.c:242
void url_free(struct Url **ptr)
Free the contents of a URL.
Definition url.c:124
+ 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 637 of file imap.c.

638{
639 if (adata->status != IMAP_FATAL)
640 {
641 /* we set status here to let imap_handle_untagged know we _expect_ to
642 * receive a bye response (so it doesn't freak out and close the conn) */
643 if (adata->state == IMAP_DISCONNECTED)
644 {
645 return;
646 }
647
648 adata->status = IMAP_BYE;
649 imap_cmd_start(adata, "LOGOUT");
650 const short c_imap_poll_timeout = cs_subset_number(NeoMutt->sub, "imap_poll_timeout");
651 if ((c_imap_poll_timeout <= 0) ||
652 (mutt_socket_poll(adata->conn, c_imap_poll_timeout) != 0))
653 {
654 while (imap_cmd_step(adata) == IMAP_RES_CONTINUE)
655 ; // do nothing
656 }
657 }
658 mutt_socket_close(adata->conn);
659 adata->state = IMAP_DISCONNECTED;
660}
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition helpers.c:143
int imap_cmd_start(struct ImapAccountData *adata, const char *cmdstr)
Given an IMAP command, send it to the server.
Definition command.c:1216
int imap_cmd_step(struct ImapAccountData *adata)
Reads server responses from an IMAP command.
Definition command.c:1230
@ IMAP_DISCONNECTED
Disconnected from server.
Definition private.h:118
@ IMAP_BYE
Logged out from server.
Definition private.h:109
@ IMAP_FATAL
Unrecoverable error occurred.
Definition private.h:108
#define IMAP_RES_CONTINUE
* ...
Definition private.h:55
int mutt_socket_close(struct Connection *conn)
Close a socket.
Definition socket.c:100
int mutt_socket_poll(struct Connection *conn, time_t wait_secs)
Checks whether reads would block.
Definition socket.c:182
unsigned char state
ImapState, e.g. IMAP_AUTHENTICATED.
Definition adata.h:45
unsigned char status
ImapFlags, e.g. IMAP_FATAL.
Definition adata.h:46
struct Connection * conn
Connection to IMAP server.
Definition adata.h:41
struct ConfigSubset * sub
Inherited config items.
Definition neomutt.h:49
+ 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 667 of file imap.c.

668{
669 struct Account **ap = NULL;
671 {
672 struct Account *a = *ap;
673 if (a->type != MUTT_IMAP)
674 continue;
675
676 struct ImapAccountData *adata = a->adata;
677 if (!adata)
678 continue;
679
680 struct Connection *conn = adata->conn;
681 if (!conn || (conn->fd < 0))
682 continue;
683
684 mutt_message(_("Closing connection to %s..."), conn->account.host);
685 imap_logout(a->adata);
687 }
688}
@ MUTT_IMAP
'IMAP' Mailbox type
Definition mailbox.h:49
#define mutt_message(...)
Definition logging2.h:93
static void imap_logout(struct ImapAccountData *adata)
Gracefully log out of server.
Definition imap.c:637
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
A group of associated Mailboxes.
Definition account.h:36
enum MailboxType type
Type of Mailboxes this Account contains.
Definition account.h:37
char host[128]
Server to login to.
Definition connaccount.h:60
struct ConnAccount account
Account details: username, password, etc.
Definition connection.h:49
int fd
Socket file descriptor.
Definition connection.h:53
struct AccountArray accounts
All Accounts.
Definition neomutt.h:50
+ 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 704 of file imap.c.

706{
707 char c;
708 bool r = false;
709 struct Buffer buf = { 0 }; // Do not allocate, maybe it won't be used
710
711 const short c_debug_level = cs_subset_number(NeoMutt->sub, "debug_level");
712 if (c_debug_level >= IMAP_LOG_LTRL)
713 buf_alloc(&buf, bytes + 1);
714
715 mutt_debug(LL_DEBUG2, "reading %lu byte literal from server\n", bytes);
716
717 /* For large transfers, calculate checkpoint for progress logging */
718 unsigned long checkpoint = bytes / 10; // Log every 10%
719 if (checkpoint == 0)
720 checkpoint = bytes; // For small transfers, don't log progress
721
722 time_t start_time = mutt_date_now();
723 time_t last_progress = start_time;
724 time_t last_activity = start_time;
725
726 /* Get timeout value - use imap_poll_timeout or default to 60 seconds */
727 const short c_imap_poll_timeout = cs_subset_number(NeoMutt->sub, "imap_poll_timeout");
728 const int stall_timeout = (c_imap_poll_timeout > 0) ? c_imap_poll_timeout : 60;
729
730 for (unsigned long pos = 0; pos < bytes; pos++)
731 {
732 /* Check for user interrupt (Ctrl-C) periodically */
733 if ((pos % 4096) == 0)
734 {
735 if (SigInt)
736 {
737 mutt_debug(LL_DEBUG1, "Literal read interrupted by user at %lu/%lu bytes\n",
738 pos, bytes);
739 mutt_error(_("Download interrupted"));
740 SigInt = false;
741 adata->status = IMAP_FATAL;
742 buf_dealloc(&buf);
743 return -1;
744 }
745
746 /* Check for stalled transfer */
747 time_t now = mutt_date_now();
748 if ((now - last_activity) > stall_timeout)
749 {
750 mutt_debug(LL_DEBUG1, "Literal read stalled at %lu/%lu bytes (no data for %d seconds)\n",
751 pos, bytes, stall_timeout);
752 mutt_error(_("Download stalled - no data received for %d seconds"), stall_timeout);
753 adata->status = IMAP_FATAL;
754 buf_dealloc(&buf);
755 return -1;
756 }
757 }
758
759 if (mutt_socket_readchar(adata->conn, &c) != 1)
760 {
761 time_t duration = mutt_date_now() - start_time;
762 mutt_debug(LL_DEBUG1, "Error during literal read at byte %lu/%lu (%.1f%% complete)\n",
763 pos, bytes, (bytes > 0) ? ((double) pos / bytes * 100.0) : 0.0);
764 mutt_debug(LL_DEBUG1, "Read failed after %ld seconds (errno=%d: %s)\n",
765 (long) duration, errno, strerror(errno));
766 adata->status = IMAP_FATAL;
767
768 buf_dealloc(&buf);
769 return -1;
770 }
771
772 last_activity = mutt_date_now();
773
774 if (r && (c != '\n'))
775 fputc('\r', fp);
776
777 if (c == '\r')
778 {
779 r = true;
780 continue;
781 }
782 else
783 {
784 r = false;
785 }
786
787 fputc(c, fp);
788
789 if ((pos % 1024) == 0)
790 progress_update(progress, pos, -1);
791
792 /* Log progress every 10% for large transfers */
793 if ((checkpoint > 0) && ((pos % checkpoint) == 0) && (pos > 0))
794 {
795 time_t now = mutt_date_now();
796 if (now > last_progress)
797 {
798 mutt_debug(LL_DEBUG2, "Literal read progress: %lu/%lu bytes (%.1f%%)\n",
799 pos, bytes, ((double) pos / bytes * 100.0));
800 last_progress = now;
801 }
802 }
803
804 if (c_debug_level >= IMAP_LOG_LTRL)
805 buf_addch(&buf, c);
806 }
807
808 time_t duration = mutt_date_now() - start_time;
809 if (duration > 0)
810 {
811 mutt_debug(LL_DEBUG2, "Literal read complete: %lu bytes in %ld seconds\n",
812 bytes, (long) duration);
813 }
814
815 if (c_debug_level >= IMAP_LOG_LTRL)
816 {
817 mutt_debug(IMAP_LOG_LTRL, "\n%s", buf.data);
818 buf_dealloc(&buf);
819 }
820 return 0;
821}
void buf_dealloc(struct Buffer *buf)
Release the memory allocated by a buffer.
Definition buffer.c:377
size_t buf_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition buffer.c:241
void buf_alloc(struct Buffer *buf, size_t new_size)
Make sure a buffer can store at least new_size bytes.
Definition buffer.c:337
#define IMAP_LOG_LTRL
Log literal values.
Definition private.h:48
@ LL_DEBUG2
Log at debug level 2.
Definition logging2.h:46
time_t mutt_date_now(void)
Return the number of seconds since the Unix epoch.
Definition date.c:457
bool progress_update(struct Progress *progress, size_t pos, int percent)
Update the state of the progress bar.
Definition progress.c:80
volatile sig_atomic_t SigInt
true after SIGINT is received
Definition signal.c:68
int mutt_socket_readchar(struct Connection *conn, char *c)
Simple read buffering to speed things up.
Definition socket.c:200
+ 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 828 of file imap.c.

829{
830 struct ImapMboxData *mdata = imap_mdata_get(m);
832
833 if (!mdata || !edata)
834 return;
835
836 imap_msn_remove(&mdata->msn, edata->msn - 1);
837 edata->msn = 0;
838}
void imap_msn_remove(struct MSNArray *msn, int idx)
Remove an entry from the cache.
Definition msn.c:116
+ 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 849 of file imap.c.

850{
852 struct ImapMboxData *mdata = imap_mdata_get(m);
853 if (!adata || !mdata)
854 return;
855
856 struct Email *e = NULL;
857
858#ifdef USE_HCACHE
859 imap_hcache_open(adata, mdata, false);
860#endif
861
862 for (int i = 0; i < m->msg_count; i++)
863 {
864 e = m->emails[i];
865 if (!e)
866 break;
867
868 if (e->index == INT_MAX)
869 {
870 mutt_debug(LL_DEBUG2, "Expunging message UID %u\n", imap_edata_get(e)->uid);
871
872 e->deleted = true;
873
874 imap_cache_del(m, e);
875#ifdef USE_HCACHE
876 imap_hcache_del(mdata, imap_edata_get(e)->uid);
877#endif
878
879 mutt_hash_int_delete(mdata->uid_hash, imap_edata_get(e)->uid, e);
880
881 imap_edata_free((void **) &e->edata);
882 }
883 else
884 {
885 /* NeoMutt has several places where it turns off e->active as a
886 * hack. For example to avoid FLAG updates, or to exclude from
887 * imap_exec_msg_set.
888 *
889 * Unfortunately, when a reopen is allowed and the IMAP_EXPUNGE_PENDING
890 * flag becomes set (e.g. a flag update to a modified header),
891 * this function will be called by imap_cmd_finish().
892 *
893 * The ctx_update_tables() will free and remove these "inactive" headers,
894 * despite that an EXPUNGE was not received for them.
895 * This would result in memory leaks and segfaults due to dangling
896 * pointers in the msn_index and uid_hash.
897 *
898 * So this is another hack to work around the hacks. We don't want to
899 * remove the messages, so make sure active is on. */
900 e->active = true;
901 }
902 }
903
904#ifdef USE_HCACHE
905 imap_hcache_close(mdata);
906#endif
907
909 if (resort)
910 {
912 }
913}
void mailbox_changed(struct Mailbox *m, enum NotifyMailbox action)
Notify observers of a change to a Mailbox.
Definition mailbox.c:232
@ NT_MAILBOX_RESORT
Email list needs resorting.
Definition mailbox.h:183
@ NT_MAILBOX_UPDATE
Update internal tables.
Definition mailbox.h:184
void imap_edata_free(void **ptr)
Free the private Email data - Implements Email::edata_free() -.
Definition edata.c:39
void mutt_hash_int_delete(struct HashTable *table, unsigned int intkey, const void *data)
Remove an element from a Hash Table.
Definition hash.c:446
int imap_cache_del(struct Mailbox *m, struct Email *e)
Delete an email from the body cache.
Definition message.c:1901
void imap_hcache_open(struct ImapAccountData *adata, struct ImapMboxData *mdata, bool create)
Open a header cache.
Definition util.c:307
void imap_hcache_close(struct ImapMboxData *mdata)
Close the header cache.
Definition util.c:348
int imap_hcache_del(struct ImapMboxData *mdata, unsigned int uid)
Delete an item from the header cache.
Definition util.c:406
struct HashTable * uid_hash
Hash Table: "uid" -> Email.
Definition mdata.h:60
int msg_count
Total number of messages.
Definition mailbox.h:90
struct Email ** emails
Array of Emails.
Definition mailbox.h:98
+ 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 921 of file imap.c.

922{
923 if (mutt_socket_open(adata->conn) < 0)
924 return -1;
925
926 adata->state = IMAP_CONNECTED;
927
928 if (imap_cmd_step(adata) != IMAP_RES_OK)
929 {
931 return -1;
932 }
933
934 if (mutt_istr_startswith(adata->buf, "* OK"))
935 {
936 if (!mutt_istr_startswith(adata->buf, "* OK [CAPABILITY") && check_capabilities(adata))
937 {
938 goto bail;
939 }
940#ifdef USE_SSL
941 /* Attempt STARTTLS if available and desired. */
942 const bool c_ssl_force_tls = cs_subset_bool(NeoMutt->sub, "ssl_force_tls");
943 if ((adata->conn->ssf == 0) &&
944 (c_ssl_force_tls || (adata->capabilities & IMAP_CAP_STARTTLS)))
945 {
946 enum QuadOption ans;
947
948 if (c_ssl_force_tls)
949 {
950 ans = MUTT_YES;
951 }
952 else if ((ans = query_quadoption(_("Secure connection with TLS?"),
953 NeoMutt->sub, "ssl_starttls")) == MUTT_ABORT)
954 {
955 goto bail;
956 }
957 if (ans == MUTT_YES)
958 {
959 enum ImapExecResult rc = imap_exec(adata, "STARTTLS", IMAP_CMD_SINGLE);
960 // Clear any data after the STARTTLS acknowledgement
961 mutt_socket_empty(adata->conn);
962
963 if (rc == IMAP_EXEC_FATAL)
964 goto bail;
965 if (rc != IMAP_EXEC_ERROR)
966 {
967 if (mutt_ssl_starttls(adata->conn) == 0)
968 {
969 /* RFC2595 demands we recheck CAPABILITY after TLS completes. */
970 if (imap_exec(adata, "CAPABILITY", IMAP_CMD_NONE) != IMAP_EXEC_SUCCESS)
971 goto bail;
972 }
973 else
974 {
975 mutt_error(_("Could not negotiate TLS connection"));
976 goto bail;
977 }
978 }
979 }
980 }
981
982 if (c_ssl_force_tls && (adata->conn->ssf == 0))
983 {
984 mutt_error(_("Encrypted connection unavailable"));
985 goto bail;
986 }
987#endif
988 }
989 else if (mutt_istr_startswith(adata->buf, "* PREAUTH"))
990 {
991#ifdef USE_SSL
992 /* Unless using a secure $tunnel, an unencrypted PREAUTH response may be a
993 * MITM attack. The only way to stop "STARTTLS" MITM attacks is via
994 * $ssl_force_tls: an attacker can easily spoof "* OK" and strip the
995 * STARTTLS capability. So consult $ssl_force_tls, not $ssl_starttls, to
996 * decide whether to abort. Note that if using $tunnel and
997 * $tunnel_is_secure, adata->conn->ssf will be set to 1. */
998 const bool c_ssl_force_tls = cs_subset_bool(NeoMutt->sub, "ssl_force_tls");
999 if ((adata->conn->ssf == 0) && c_ssl_force_tls)
1000 {
1001 mutt_error(_("Encrypted connection unavailable"));
1002 goto bail;
1003 }
1004#endif
1005
1006 adata->state = IMAP_AUTHENTICATED;
1007 if (check_capabilities(adata) != 0)
1008 goto bail;
1009 FREE(&adata->capstr);
1010 }
1011 else
1012 {
1013 imap_error("imap_open_connection()", adata->buf);
1014 goto bail;
1015 }
1016
1017 return 0;
1018
1019bail:
1020 imap_close_connection(adata);
1021 FREE(&adata->capstr);
1022 return -1;
1023}
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition helpers.c:47
int mutt_ssl_starttls(struct Connection *conn)
Negotiate TLS over an already opened connection.
Definition gnutls.c:1172
@ IMAP_AUTHENTICATED
Connection is authenticated.
Definition private.h:120
@ IMAP_CONNECTED
Connected to server.
Definition private.h:119
#define IMAP_RES_OK
<tag> OK ...
Definition private.h:54
ImapExecResult
Imap_exec return code.
Definition private.h:94
@ IMAP_EXEC_ERROR
Imap command failure.
Definition private.h:96
@ IMAP_EXEC_FATAL
Imap connection failure.
Definition private.h:97
@ IMAP_CAP_STARTTLS
RFC2595: STARTTLS.
Definition private.h:145
@ IMAP_CMD_SINGLE
Run a single command.
Definition private.h:86
void imap_close_connection(struct ImapAccountData *adata)
Close an IMAP connection.
Definition imap.c:1029
static int check_capabilities(struct ImapAccountData *adata)
Make sure we can log in to this server.
Definition imap.c:207
#define FREE(x)
Free memory and set the pointer to NULL.
Definition memory.h:68
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(const char *prompt, struct ConfigSubset *sub, const char *name)
Ask the user a quad-question.
Definition question.c:384
void mutt_socket_empty(struct Connection *conn)
Clear out any queued data.
Definition socket.c:306
int mutt_socket_open(struct Connection *conn)
Simple wrapper.
Definition socket.c:76
unsigned int ssf
Security strength factor, in bits (see notes)
Definition connection.h:50
char * capstr
Capability string from the server.
Definition adata.h:55
+ 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 1029 of file imap.c.

1030{
1031 if (adata->state != IMAP_DISCONNECTED)
1032 {
1033 mutt_socket_close(adata->conn);
1034 adata->state = IMAP_DISCONNECTED;
1035 }
1036 adata->seqno = 0;
1037 adata->nextcmd = 0;
1038 adata->lastcmd = 0;
1039 adata->status = 0;
1040 memset(adata->cmds, 0, sizeof(struct ImapCommand) * adata->cmdslots);
1041}
int lastcmd
Last command in the queue.
Definition adata.h:76
int nextcmd
Next command to be sent.
Definition adata.h:75
struct ImapCommand * cmds
Queue of commands for the server.
Definition adata.h:73
int cmdslots
Size of the command queue.
Definition adata.h:74
unsigned int seqno
tag sequence number, e.g. '{seqid}0001'
Definition adata.h:58
IMAP command structure.
Definition private.h:176
+ 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 1054 of file imap.c.

1055{
1056 if (STAILQ_EMPTY(flag_list))
1057 return false;
1058
1059 const size_t flaglen = mutt_str_len(flag);
1060 struct ListNode *np = NULL;
1061 STAILQ_FOREACH(np, flag_list, entries)
1062 {
1063 const size_t nplen = strlen(np->data);
1064 if ((flaglen >= nplen) && ((flag[nplen] == '\0') || (flag[nplen] == ' ')) &&
1065 mutt_istrn_equal(np->data, flag, nplen))
1066 {
1067 return true;
1068 }
1069
1070 if (mutt_str_equal(np->data, "\\*"))
1071 return true;
1072 }
1073
1074 return false;
1075}
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition string.c:665
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition string.c:503
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:457
#define STAILQ_FOREACH(var, head, field)
Definition queue.h:390
#define STAILQ_EMPTY(head)
Definition queue.h:382
A List node for strings.
Definition list.h:37
char * data
String.
Definition list.h:38
+ 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 1114 of file imap.c.

1116{
1118 struct ImapEmailData *edata = imap_edata_get(e);
1119
1120 if (!adata || (adata->mailbox != m) || !e)
1121 return -1;
1122
1123 if (!compare_flags_for_copy(e))
1124 {
1125 if (e->deleted == edata->deleted)
1126 e->changed = false;
1127 return 0;
1128 }
1129
1130 buf_printf(cmd, "UID STORE %u", edata->uid);
1131
1132 struct Buffer *flags = buf_pool_get();
1133
1134 set_flag(m, MUTT_ACL_SEEN, e->read, "\\Seen ", flags);
1135 set_flag(m, MUTT_ACL_WRITE, e->old, "Old ", flags);
1136 set_flag(m, MUTT_ACL_WRITE, e->flagged, "\\Flagged ", flags);
1137 set_flag(m, MUTT_ACL_WRITE, e->replied, "\\Answered ", flags);
1138 set_flag(m, MUTT_ACL_DELETE, edata->deleted, "\\Deleted ", flags);
1139
1140 if (m->rights & MUTT_ACL_WRITE)
1141 {
1142 /* restore system flags */
1143 if (edata->flags_system)
1144 buf_addstr(flags, edata->flags_system);
1145
1146 /* set custom flags */
1147 struct Buffer *tags = buf_pool_get();
1149 if (!buf_is_empty(tags))
1150 buf_addstr(flags, buf_string(tags));
1151 buf_pool_release(&tags);
1152 }
1153
1155 buf_fix_dptr(flags);
1156
1157 /* UW-IMAP is OK with null flags, Cyrus isn't. The only solution is to
1158 * explicitly revoke all system flags (if we have permission) */
1159 if (buf_is_empty(flags))
1160 {
1161 set_flag(m, MUTT_ACL_SEEN, true, "\\Seen ", flags);
1162 set_flag(m, MUTT_ACL_WRITE, true, "Old ", flags);
1163 set_flag(m, MUTT_ACL_WRITE, true, "\\Flagged ", flags);
1164 set_flag(m, MUTT_ACL_WRITE, true, "\\Answered ", flags);
1165 set_flag(m, MUTT_ACL_DELETE, !edata->deleted, "\\Deleted ", flags);
1166
1167 /* erase custom flags */
1168 if ((m->rights & MUTT_ACL_WRITE) && edata->flags_remote)
1169 buf_addstr(flags, edata->flags_remote);
1170
1172 buf_fix_dptr(flags);
1173
1174 buf_addstr(cmd, " -FLAGS.SILENT (");
1175 }
1176 else
1177 {
1178 buf_addstr(cmd, " FLAGS.SILENT (");
1179 }
1180
1181 buf_addstr(cmd, buf_string(flags));
1182 buf_addstr(cmd, ")");
1183
1184 int rc = -1;
1185
1186 /* after all this it's still possible to have no flags, if you
1187 * have no ACL rights */
1188 if (!buf_is_empty(flags) &&
1189 (imap_exec(adata, cmd->data, IMAP_CMD_NONE) != IMAP_EXEC_SUCCESS) &&
1190 err_continue && (*err_continue != MUTT_YES))
1191 {
1192 *err_continue = imap_continue("imap_sync_message: STORE failed", adata->buf);
1193 if (*err_continue != MUTT_YES)
1194 goto done;
1195 }
1196
1197 /* server have now the updated flags */
1198 FREE(&edata->flags_remote);
1199 struct Buffer *flags_remote = buf_pool_get();
1200 driver_tags_get_with_hidden(&e->tags, flags_remote);
1201 edata->flags_remote = buf_strdup(flags_remote);
1202 buf_pool_release(&flags_remote);
1203
1204 if (e->deleted == edata->deleted)
1205 e->changed = false;
1206
1207 rc = 0;
1208
1209done:
1210 buf_pool_release(&flags);
1211 return rc;
1212}
bool buf_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition buffer.c:291
char * buf_strdup(const struct Buffer *buf)
Copy a Buffer's string.
Definition buffer.c:571
@ MUTT_ACL_DELETE
Delete a message.
Definition mailbox.h:63
@ MUTT_ACL_SEEN
Change the 'seen' status of a message.
Definition mailbox.h:70
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:654
static void set_flag(struct Mailbox *m, AclFlags aclflag, bool flag, const char *str, struct Buffer *flags)
Append str to flags if we currently have permission according to aclflag.
Definition imap.c:287
static bool compare_flags_for_copy(struct Email *e)
Compare local flags against the server.
Definition imap.c:307
void mutt_str_remove_trailing_ws(char *s)
Trim trailing whitespace from a string.
Definition string.c:570
struct TagList tags
For drivers that support server tagging.
Definition email.h:72
struct Mailbox * mailbox
Current selected mailbox.
Definition adata.h:80
char * flags_remote
Remote flags.
Definition edata.h:49
bool deleted
Email has been deleted.
Definition edata.h:39
char * flags_system
System flags.
Definition edata.h:48
void driver_tags_get_with_hidden(struct TagList *tl, struct Buffer *tags)
Get all tags, also hidden ones, separated by space.
Definition tags.c:175
+ 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 1220 of file imap.c.

1221{
1222 if (!m || !m->account)
1223 return MX_STATUS_ERROR;
1224
1226 struct ImapMboxData *mdata = imap_mdata_get(m);
1227 if (!adata || !mdata)
1228 return MX_STATUS_ERROR;
1229
1230 /* overload keyboard timeout to avoid many mailbox checks in a row.
1231 * Most users don't like having to wait exactly when they press a key. */
1232 int rc = 0;
1233
1234 /* try IDLE first, unless force is set */
1235 const bool c_imap_idle = cs_subset_bool(NeoMutt->sub, "imap_idle");
1236 const short c_imap_keep_alive = cs_subset_number(NeoMutt->sub, "imap_keep_alive");
1237 if (!force && c_imap_idle && (adata->capabilities & IMAP_CAP_IDLE) &&
1238 ((adata->state != IMAP_IDLE) || (mutt_date_now() >= adata->lastread + c_imap_keep_alive)))
1239 {
1240 if (imap_cmd_idle(adata) < 0)
1241 return MX_STATUS_ERROR;
1242 }
1243 if (adata->state == IMAP_IDLE)
1244 {
1245 while ((rc = mutt_socket_poll(adata->conn, 0)) > 0)
1246 {
1247 if (imap_cmd_step(adata) != IMAP_RES_CONTINUE)
1248 {
1249 mutt_debug(LL_DEBUG1, "Error reading IDLE response\n");
1250 return MX_STATUS_ERROR;
1251 }
1252 }
1253 if (rc < 0)
1254 {
1255 mutt_debug(LL_DEBUG1, "Poll failed, disabling IDLE\n");
1256 adata->capabilities &= ~IMAP_CAP_IDLE; // Clear the flag
1257 }
1258 }
1259
1260 const short c_timeout = cs_subset_number(NeoMutt->sub, "timeout");
1261 if ((force || ((adata->state != IMAP_IDLE) && (mutt_date_now() >= adata->lastread + c_timeout))) &&
1262 (imap_exec(adata, "NOOP", IMAP_CMD_POLL) != IMAP_EXEC_SUCCESS))
1263 {
1264 return MX_STATUS_ERROR;
1265 }
1266
1267 /* We call this even when we haven't run NOOP in case we have pending
1268 * changes to process, since we can reopen here. */
1269 imap_cmd_finish(adata);
1270
1271 enum MxStatus check = MX_STATUS_OK;
1272 if (mdata->check_status & IMAP_EXPUNGE_PENDING)
1273 check = MX_STATUS_REOPENED;
1274 else if (mdata->check_status & IMAP_NEWMAIL_PENDING)
1275 check = MX_STATUS_NEW_MAIL;
1276 else if (mdata->check_status & IMAP_FLAGS_PENDING)
1277 check = MX_STATUS_FLAGS;
1278 else if (rc < 0)
1279 check = MX_STATUS_ERROR;
1280
1281 mdata->check_status = IMAP_OPEN_NONE;
1282
1283 if (force)
1284 m->last_checked = 0; // force a check on the next mx_mbox_check() call
1285 return check;
1286}
int imap_cmd_idle(struct ImapAccountData *adata)
Enter the IDLE state.
Definition command.c:1586
void imap_cmd_finish(struct ImapAccountData *adata)
Attempt to perform cleanup.
Definition command.c:1511
@ IMAP_IDLE
Connection is idle.
Definition private.h:124
@ IMAP_EXPUNGE_PENDING
Messages on the server have been expunged.
Definition private.h:70
@ IMAP_NEWMAIL_PENDING
New mail is waiting on the server.
Definition private.h:71
@ IMAP_OPEN_NONE
No flags are set.
Definition private.h:67
@ IMAP_FLAGS_PENDING
Flags have changed on the server.
Definition private.h:72
@ IMAP_CAP_IDLE
RFC2177: IDLE.
Definition private.h:147
@ IMAP_CMD_POLL
Poll the tcp connection before running the imap command.
Definition private.h:85
MxStatus
Return values from mbox_check(), mbox_check_stats(), mbox_sync(), and mbox_close()
Definition mxapi.h:70
@ MX_STATUS_ERROR
An error occurred.
Definition mxapi.h:71
@ MX_STATUS_OK
No changes.
Definition mxapi.h:72
@ MX_STATUS_FLAGS
Nondestructive flags change (IMAP)
Definition mxapi.h:76
@ MX_STATUS_REOPENED
Mailbox was reopened.
Definition mxapi.h:75
@ MX_STATUS_NEW_MAIL
New mail received in Mailbox.
Definition mxapi.h:73
time_t lastread
last time we read a command for the server
Definition adata.h:59
time_t last_checked
Last time we checked this mailbox for new mail.
Definition mailbox.h:107
+ 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 1295 of file imap.c.

1296{
1297 char *uidvalidity_flag = NULL;
1298 char cmd[2048] = { 0 };
1299
1300 if (!adata || !mdata)
1301 return -1;
1302
1303 /* Don't issue STATUS on the selected mailbox, it will be NOOPed or
1304 * IDLEd elsewhere.
1305 * adata->mailbox may be NULL for connections other than the current
1306 * mailbox's. */
1307 if (adata->mailbox && (adata->mailbox->mdata == mdata))
1308 {
1309 adata->mailbox->has_new = false;
1310 return mdata->messages;
1311 }
1312
1313 if (adata->mailbox && !adata->mailbox->poll_new_mail)
1314 return mdata->messages;
1315
1316 if (adata->capabilities & IMAP_CAP_IMAP4REV1)
1317 {
1318 uidvalidity_flag = "UIDVALIDITY";
1319 }
1320 else if (adata->capabilities & IMAP_CAP_STATUS)
1321 {
1322 uidvalidity_flag = "UID-VALIDITY";
1323 }
1324 else
1325 {
1326 mutt_debug(LL_DEBUG2, "Server doesn't support STATUS\n");
1327 return -1;
1328 }
1329
1330 snprintf(cmd, sizeof(cmd), "STATUS %s (UIDNEXT %s UNSEEN RECENT MESSAGES)",
1331 mdata->munge_name, uidvalidity_flag);
1332
1333 int rc = imap_exec(adata, cmd, queue ? IMAP_CMD_QUEUE : IMAP_CMD_POLL);
1334 if (rc != IMAP_EXEC_SUCCESS)
1335 {
1336 mutt_debug(LL_DEBUG1, "Error queueing command\n");
1337 return rc;
1338 }
1339 return mdata->messages;
1340}
@ IMAP_CAP_STATUS
Server supports STATUS command.
Definition private.h:137
@ IMAP_CMD_QUEUE
Queue a command, do not execute.
Definition private.h:84
unsigned int messages
Number of messages.
Definition mdata.h:54
char * munge_name
Munged version of the mailbox name.
Definition mdata.h:42
bool has_new
Mailbox has new mail.
Definition mailbox.h:87
bool poll_new_mail
Check for new mail.
Definition mailbox.h:117
+ 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 1362 of file imap.c.

1363{
1364 struct Mailbox *m = mx_mbox_find2(path);
1365
1366 const bool is_temp = !m;
1367 if (is_temp)
1368 {
1369 m = mx_path_resolve(path);
1370 if (!mx_mbox_ac_link(m))
1371 {
1372 mailbox_free(&m);
1373 return 0;
1374 }
1375 }
1376
1377 int rc = imap_mailbox_status(m, queue);
1378
1379 if (is_temp)
1380 {
1381 mx_ac_remove(m, false);
1382 mailbox_free(&m);
1383 }
1384
1385 return rc;
1386}
void mailbox_free(struct Mailbox **ptr)
Free a Mailbox.
Definition mailbox.c:90
int imap_mailbox_status(struct Mailbox *m, bool queue)
Refresh the number of total and new messages.
Definition imap.c:1397
int mx_ac_remove(struct Mailbox *m, bool keep_account)
Remove a Mailbox from an Account and delete Account if empty.
Definition mx.c:1754
struct Mailbox * mx_mbox_find2(const char *path)
Find a Mailbox on an Account.
Definition mx.c:1615
bool mx_mbox_ac_link(struct Mailbox *m)
Link a Mailbox to an existing or new Account.
Definition mx.c:248
struct Mailbox * mx_path_resolve(const char *path)
Get a Mailbox for a path.
Definition mx.c:1647
+ 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 1397 of file imap.c.

1398{
1400 struct ImapMboxData *mdata = imap_mdata_get(m);
1401 if (!adata || !mdata)
1402 return -1;
1403 return imap_status(adata, mdata, queue);
1404}
static int imap_status(struct ImapAccountData *adata, struct ImapMboxData *mdata, bool queue)
Refresh the number of total and new messages.
Definition imap.c:1295
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_subscribe()

int imap_subscribe ( const char * path,
bool subscribe )

Subscribe to a mailbox.

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

Definition at line 1413 of file imap.c.

1414{
1415 struct ImapAccountData *adata = NULL;
1416 struct ImapMboxData *mdata = NULL;
1417
1418 if (imap_adata_find(path, &adata, &mdata) < 0)
1419 return -1;
1420
1421 if (subscribe)
1422 mutt_message(_("Subscribing to %s..."), mdata->name);
1423 else
1424 mutt_message(_("Unsubscribing from %s..."), mdata->name);
1425
1426 char buf[2048] = { 0 };
1427 snprintf(buf, sizeof(buf), "%sSUBSCRIBE %s", subscribe ? "" : "UN", mdata->munge_name);
1428
1429 if (imap_exec(adata, buf, IMAP_CMD_NONE) != IMAP_EXEC_SUCCESS)
1430 {
1431 imap_mdata_free((void *) &mdata);
1432 return -1;
1433 }
1434
1435 const bool c_imap_check_subscribed = cs_subset_bool(NeoMutt->sub, "imap_check_subscribed");
1436 if (c_imap_check_subscribed)
1437 {
1438 if (subscribe)
1439 {
1440 struct Buffer *err = buf_pool_get();
1441 if (!mailbox_add_simple(path, err))
1442 mutt_debug(LL_DEBUG1, "Error adding subscribed mailbox: %s\n", buf_string(err));
1443 buf_pool_release(&err);
1444 }
1445 else
1446 {
1447 if (!mailbox_remove_simple(path))
1448 mutt_debug(LL_DEBUG1, "Error removing subscribed mailbox: %s\n", path);
1449 }
1450 }
1451
1452 if (subscribe)
1453 mutt_message(_("Subscribed to %s"), mdata->name);
1454 else
1455 mutt_message(_("Unsubscribed from %s"), mdata->name);
1456 imap_mdata_free((void *) &mdata);
1457 return 0;
1458}
void imap_mdata_free(void **ptr)
Free the private Mailbox data - Implements Mailbox::mdata_free() -.
Definition mdata.c:40
int imap_adata_find(const char *path, struct ImapAccountData **adata, struct ImapMboxData **mdata)
Find the Account data for this path.
Definition util.c:73
bool mailbox_remove_simple(const char *mailbox)
Remove a Mailbox.
Definition mailboxes.c:397
bool mailbox_add_simple(const char *mailbox, struct Buffer *err)
Add a new Mailbox.
Definition mailboxes.c:157
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_complete()

int imap_complete ( struct Buffer * buf,
const char * path )

Try to complete an IMAP folder path.

Parameters
bufBuffer for result
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 1470 of file imap.c.

1471{
1472 struct ImapAccountData *adata = NULL;
1473 struct ImapMboxData *mdata = NULL;
1474 char tmp[2048] = { 0 };
1475 struct ImapList listresp = { 0 };
1476 struct Buffer *completion_buf = NULL;
1477 size_t clen;
1478 int completions = 0;
1479 int rc;
1480
1481 if (imap_adata_find(path, &adata, &mdata) < 0)
1482 {
1483 buf_strcpy(buf, path);
1484 return complete_hosts(buf);
1485 }
1486
1487 /* fire off command */
1488 const bool c_imap_list_subscribed = cs_subset_bool(NeoMutt->sub, "imap_list_subscribed");
1489 snprintf(tmp, sizeof(tmp), "%s \"\" \"%s%%\"",
1490 c_imap_list_subscribed ? "LSUB" : "LIST", mdata->real_name);
1491
1492 imap_cmd_start(adata, tmp);
1493
1494 /* and see what the results are */
1495 completion_buf = buf_pool_get();
1496 buf_strcpy(completion_buf, mdata->name);
1497 imap_mdata_free((void *) &mdata);
1498
1499 adata->cmdresult = &listresp;
1500 do
1501 {
1502 listresp.name = NULL;
1503 rc = imap_cmd_step(adata);
1504
1505 if ((rc == IMAP_RES_CONTINUE) && listresp.name)
1506 {
1507 /* if the folder isn't selectable, append delimiter to force browse
1508 * to enter it on second tab. */
1509 if (listresp.noselect)
1510 {
1511 clen = strlen(listresp.name);
1512 listresp.name[clen++] = listresp.delim;
1513 listresp.name[clen] = '\0';
1514 }
1515 /* copy in first word */
1516 if (!completions)
1517 {
1518 buf_strcpy(completion_buf, listresp.name);
1519 completions++;
1520 continue;
1521 }
1522
1523 longest_common_prefix(completion_buf, listresp.name, 0);
1524 completions++;
1525 }
1526 } while (rc == IMAP_RES_CONTINUE);
1527 adata->cmdresult = NULL;
1528
1529 if (completions)
1530 {
1531 /* reformat output */
1532 imap_buf_qualify_path(buf, &adata->conn->account, completion_buf->data);
1533 pretty_mailbox(buf);
1534 buf_fix_dptr(buf);
1535 buf_pool_release(&completion_buf);
1536 return 0;
1537 }
1538
1539 buf_pool_release(&completion_buf);
1540 return -1;
1541}
void imap_buf_qualify_path(struct Buffer *buf, struct ConnAccount *conn_account, char *path)
Make an absolute IMAP folder target to a buffer.
Definition util.c:874
static int complete_hosts(struct Buffer *buf)
Look for completion matches for mailboxes.
Definition imap.c:476
void pretty_mailbox(struct Buffer *buf)
Shorten a mailbox path using '~' or '='.
Definition muttlib.c:428
struct ImapList * cmdresult
Resuls of complicated commands.
Definition adata.h:70
Items in an IMAP browser.
Definition private.h:165
bool noselect
Mailbox is not selectable.
Definition private.h:168
char * name
Mailbox name.
Definition private.h:166
char delim
Hierarchy delimiter.
Definition private.h:167
char * real_name
Original Mailbox name, e.g.: INBOX can be just \0.
Definition mdata.h:43
+ 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 1551 of file imap.c.

1552{
1553 char prompt[1024] = { 0 };
1554 int rc = -1;
1555 bool triedcreate = false;
1556 enum QuadOption err_continue = MUTT_NO;
1557
1559 if (!adata)
1560 return -1;
1561
1562 struct ImapAccountData *dest_adata = NULL;
1563 struct ImapMboxData *dest_mdata = NULL;
1564
1565 if (imap_adata_find(dest, &dest_adata, &dest_mdata) < 0)
1566 return -1;
1567
1568 struct Buffer *sync_cmd = buf_pool_get();
1569
1570 /* check that the save-to folder is in the same account */
1571 if (!imap_account_match(&(adata->conn->account), &(dest_adata->conn->account)))
1572 {
1573 mutt_debug(LL_DEBUG3, "%s not same server as %s\n", dest, mailbox_path(m));
1574 goto out;
1575 }
1576
1577 for (int i = 0; i < m->msg_count; i++)
1578 {
1579 struct Email *e = m->emails[i];
1580 if (!e)
1581 break;
1582
1583 /* If message has already been checkpoint-deleted server-side, don't use
1584 * UID COPY because that can propagate the deleted flag to trash too. */
1585 struct ImapEmailData *edata = imap_edata_get(e);
1586 if (e->active && e->deleted && !e->purge && edata && edata->deleted)
1587 {
1588 mutt_debug(LL_DEBUG1, "imap_fast_trash: server-side delete flag set. aborting.\n");
1589 goto out;
1590 }
1591
1592 if (e->active && e->changed && e->deleted && !e->purge)
1593 {
1594 rc = imap_sync_message_for_copy(m, e, sync_cmd, &err_continue);
1595 if (rc < 0)
1596 {
1597 mutt_debug(LL_DEBUG1, "could not sync\n");
1598 goto out;
1599 }
1600 }
1601 }
1602
1603 /* loop in case of TRYCREATE */
1604 do
1605 {
1606 struct UidArray uida = ARRAY_HEAD_INITIALIZER;
1607 select_email_uids(m->emails, m->msg_count, MUTT_TRASH, false, false, &uida);
1608 ARRAY_SORT(&uida, imap_sort_uid, NULL);
1609 rc = imap_exec_msg_set(adata, "UID COPY", dest_mdata->munge_name, &uida);
1610 if (rc == 0)
1611 {
1612 mutt_debug(LL_DEBUG1, "No messages to trash\n");
1613 rc = -1;
1614 goto out;
1615 }
1616 else if (rc < 0)
1617 {
1618 mutt_debug(LL_DEBUG1, "could not queue copy\n");
1619 goto out;
1620 }
1621 else if (m->verbose)
1622 {
1623 mutt_message(ngettext("Copying %d message to %s...", "Copying %d messages to %s...", rc),
1624 rc, dest_mdata->name);
1625 }
1626 ARRAY_FREE(&uida);
1627
1628 /* let's get it on */
1629 rc = imap_exec(adata, NULL, IMAP_CMD_NONE);
1630 if (rc == IMAP_EXEC_ERROR)
1631 {
1632 if (triedcreate)
1633 {
1634 mutt_debug(LL_DEBUG1, "Already tried to create mailbox %s\n", dest_mdata->name);
1635 break;
1636 }
1637 /* bail out if command failed for reasons other than nonexistent target */
1638 if (!mutt_istr_startswith(imap_get_qualifier(adata->buf), "[TRYCREATE]"))
1639 break;
1640 mutt_debug(LL_DEBUG3, "server suggests TRYCREATE\n");
1641 snprintf(prompt, sizeof(prompt), _("Create %s?"), dest_mdata->name);
1642 const bool c_confirm_create = cs_subset_bool(NeoMutt->sub, "confirm_create");
1643 if (c_confirm_create &&
1644 (query_yesorno_help(prompt, MUTT_YES, NeoMutt->sub, "confirm_create") != MUTT_YES))
1645 {
1647 goto out;
1648 }
1649 if (imap_create_mailbox(adata, dest_mdata->name) < 0)
1650 break;
1651 triedcreate = true;
1652 }
1653 } while (rc == IMAP_EXEC_ERROR);
1654
1655 if (rc != IMAP_EXEC_SUCCESS)
1656 {
1657 imap_error("imap_fast_trash", adata->buf);
1658 goto out;
1659 }
1660
1661 rc = IMAP_EXEC_SUCCESS;
1662
1663out:
1664 buf_pool_release(&sync_cmd);
1665 imap_mdata_free((void *) &dest_mdata);
1666
1667 return ((rc == IMAP_EXEC_SUCCESS) ? 0 : -1);
1668}
#define ARRAY_SORT(head, fn, sdata)
Sort an array.
Definition array.h:373
int imap_sort_uid(const void *a, const void *b, void *sdata)
Compare two UIDs - Implements sort_t -.
Definition msg_set.c:48
bool imap_account_match(const struct ConnAccount *a1, const struct ConnAccount *a2)
Compare two Accounts.
Definition util.c:1106
char * imap_get_qualifier(char *buf)
Get the qualifier from a tagged response.
Definition util.c:812
int imap_create_mailbox(struct ImapAccountData *adata, const char *mailbox)
Create a new mailbox.
Definition imap.c:542
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:1114
@ LL_DEBUG3
Log at debug level 3.
Definition logging2.h:47
@ MUTT_NO
User answered 'No', or assume 'No'.
Definition quad.h:38
enum QuadOption query_yesorno_help(const char *prompt, enum QuadOption def, struct ConfigSubset *sub, const char *name)
Ask the user a Yes/No question offering help.
Definition question.c:357
bool verbose
Display status messages?
Definition mailbox.h:119
+ 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 1679 of file imap.c.

1680{
1681 if (!m)
1682 return -1;
1683
1684 struct Email **emails = NULL;
1685 int rc;
1686
1688 struct ImapMboxData *mdata = imap_mdata_get(m);
1689 if (!adata || !mdata)
1690 return MX_STATUS_ERROR;
1691
1692 if (adata->state < IMAP_SELECTED)
1693 {
1694 mutt_debug(LL_DEBUG2, "no mailbox selected\n");
1695 return -1;
1696 }
1697
1698 /* Mark sync in progress to prevent expunge/newmail processing mid-sync.
1699 * Do NOT set IMAP_REOPEN_ALLOW here — it makes timeouts destructive
1700 * by allowing imap_cmd_finish to process expunges during sync commands. */
1701 mdata->reopen |= IMAP_SYNC_IN_PROGRESS;
1702
1703 enum MxStatus check = imap_check_mailbox(m, false);
1704 if (check == MX_STATUS_ERROR)
1705 {
1706 /* Persist pending flag changes to hcache before aborting, so they
1707 * survive a reconnect and can be replayed on next sync. */
1708#ifdef USE_HCACHE
1709 imap_hcache_open(adata, mdata, true);
1710 for (int i = 0; i < m->msg_count; i++)
1711 {
1712 struct Email *e = m->emails[i];
1713 if (!e)
1714 break;
1715 if (e->active && e->changed)
1716 imap_hcache_put(mdata, e);
1717 }
1718 imap_hcache_close(mdata);
1719#endif
1721 return check;
1722 }
1723
1724 /* if we are expunging anyway, we can do deleted messages very quickly... */
1725 if (expunge && (m->rights & MUTT_ACL_DELETE))
1726 {
1727 struct UidArray uida = ARRAY_HEAD_INITIALIZER;
1728 select_email_uids(m->emails, m->msg_count, MUTT_DELETED, true, false, &uida);
1729 ARRAY_SORT(&uida, imap_sort_uid, NULL);
1730 rc = imap_exec_msg_set(adata, "UID STORE", "+FLAGS.SILENT (\\Deleted)", &uida);
1731 ARRAY_FREE(&uida);
1732 if (rc < 0)
1733 {
1734 mutt_error(_("Expunge failed"));
1736 return rc;
1737 }
1738
1739 if (rc > 0)
1740 {
1741 /* mark these messages as unchanged so second pass ignores them. Done
1742 * here so BOGUS UW-IMAP 4.7 SILENT FLAGS updates are ignored. */
1743 for (int i = 0; i < m->msg_count; i++)
1744 {
1745 struct Email *e = m->emails[i];
1746 if (!e)
1747 break;
1748 if (e->deleted && e->changed)
1749 e->active = false;
1750 }
1751 if (m->verbose)
1752 {
1753 mutt_message(ngettext("Marking %d message deleted...",
1754 "Marking %d messages deleted...", rc),
1755 rc);
1756 }
1757 }
1758 }
1759
1760#ifdef USE_HCACHE
1761 imap_hcache_open(adata, mdata, true);
1762#endif
1763
1764 /* save messages with real (non-flag) changes */
1765 for (int i = 0; i < m->msg_count; i++)
1766 {
1767 struct Email *e = m->emails[i];
1768 if (!e)
1769 break;
1770
1771 if (e->deleted)
1772 {
1773 imap_cache_del(m, e);
1774#ifdef USE_HCACHE
1775 imap_hcache_del(mdata, imap_edata_get(e)->uid);
1776#endif
1777 }
1778
1779 if (e->active && e->changed)
1780 {
1781#ifdef USE_HCACHE
1782 imap_hcache_put(mdata, e);
1783#endif
1784 /* if the message has been rethreaded or attachments have been deleted
1785 * we delete the message and reupload it.
1786 * This works better if we're expunging, of course. */
1787 if (e->env->changed || e->attach_del)
1788 {
1789 /* L10N: The plural is chosen by the last %d, i.e. the total number */
1790 if (m->verbose)
1791 {
1792 mutt_message(ngettext("Saving changed message... [%d/%d]",
1793 "Saving changed messages... [%d/%d]", m->msg_count),
1794 i + 1, m->msg_count);
1795 }
1796 bool save_append = m->append;
1797 m->append = true;
1799 m->append = save_append;
1800 e->env->changed = false;
1801 }
1802 }
1803 }
1804
1805#ifdef USE_HCACHE
1806 imap_hcache_close(mdata);
1807#endif
1808
1809 /* presort here to avoid doing 10 resorts in imap_exec_msg_set */
1810 emails = MUTT_MEM_MALLOC(m->msg_count, struct Email *);
1811 memcpy(emails, m->emails, m->msg_count * sizeof(struct Email *));
1812 mutt_qsort_r(emails, m->msg_count, sizeof(struct Email *), imap_sort_email_uid, NULL);
1813
1814 rc = sync_helper(m, emails, m->msg_count, MUTT_ACL_DELETE, MUTT_DELETED, "\\Deleted");
1815 if (rc >= 0)
1816 rc |= sync_helper(m, emails, m->msg_count, MUTT_ACL_WRITE, MUTT_FLAG, "\\Flagged");
1817 if (rc >= 0)
1818 rc |= sync_helper(m, emails, m->msg_count, MUTT_ACL_WRITE, MUTT_OLD, "Old");
1819 if (rc >= 0)
1820 rc |= sync_helper(m, emails, m->msg_count, MUTT_ACL_SEEN, MUTT_READ, "\\Seen");
1821 if (rc >= 0)
1822 rc |= sync_helper(m, emails, m->msg_count, MUTT_ACL_WRITE, MUTT_REPLIED, "\\Answered");
1823
1824 FREE(&emails);
1825
1826 /* Flush the queued flags if any were changed in sync_helper. */
1827 if (rc > 0)
1828 if (imap_exec(adata, NULL, IMAP_CMD_NONE) != IMAP_EXEC_SUCCESS)
1829 rc = -1;
1830
1831 if (rc < 0)
1832 {
1833 if (close)
1834 {
1835 if (query_yesorno(_("Error saving flags. Close anyway?"), MUTT_NO) == MUTT_YES)
1836 {
1837 adata->state = IMAP_AUTHENTICATED;
1839 return 0;
1840 }
1841 }
1842 else
1843 {
1844 mutt_error(_("Error saving flags"));
1845 }
1847 return -1;
1848 }
1849
1850 /* Update local record of server state to reflect the synchronization just
1851 * completed. imap_read_headers always overwrites hcache-origin flags, so
1852 * there is no need to mutate the hcache after flag-only changes. */
1853 for (int i = 0; i < m->msg_count; i++)
1854 {
1855 struct Email *e = m->emails[i];
1856 if (!e)
1857 break;
1858 struct ImapEmailData *edata = imap_edata_get(e);
1859 edata->deleted = e->deleted;
1860 edata->flagged = e->flagged;
1861 edata->old = e->old;
1862 edata->read = e->read;
1863 edata->replied = e->replied;
1864 e->changed = false;
1865 }
1866 m->changed = false;
1867
1868 /* Now that sync is done, allow reopen for the EXPUNGE phase. */
1871
1872 /* We must send an EXPUNGE command if we're not closing. */
1873 if (expunge && !close && (m->rights & MUTT_ACL_DELETE))
1874 {
1875 if (m->verbose)
1876 mutt_message(_("Expunging messages from server..."));
1877 /* Set expunge bit so we don't get spurious reopened messages */
1878 mdata->reopen |= IMAP_EXPUNGE_EXPECTED;
1879 if (imap_exec(adata, "EXPUNGE", IMAP_CMD_NONE) != IMAP_EXEC_SUCCESS)
1880 {
1882 imap_error(_("imap_sync_mailbox: EXPUNGE failed"), adata->buf);
1884 return -1;
1885 }
1887 }
1888
1889 if (expunge && close)
1890 {
1891 adata->closing = true;
1892 imap_exec(adata, "CLOSE", IMAP_CMD_NONE);
1893 adata->state = IMAP_AUTHENTICATED;
1894 }
1895
1896 const bool c_message_cache_clean = cs_subset_bool(NeoMutt->sub, "message_cache_clean");
1897 if (c_message_cache_clean)
1899
1900 return check;
1901}
int mutt_save_message_mbox(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:740
@ TRANSFORM_NONE
No transformation.
Definition external.h:43
@ SAVE_MOVE
Move message to another mailbox, removing the original.
Definition external.h:54
static int imap_sort_email_uid(const void *a, const void *b, void *sdata)
Compare two Emails by UID - Implements sort_t -.
Definition imap.c:1080
int imap_cache_clean(struct Mailbox *m)
Delete all the entries in the message cache.
Definition message.c:1920
void imap_allow_reopen(struct Mailbox *m)
Allow re-opening a folder upon expunge.
Definition util.c:1078
@ IMAP_SELECTED
Mailbox is selected.
Definition private.h:121
int imap_hcache_put(struct ImapMboxData *mdata, struct Email *e)
Add an entry to the header cache.
Definition util.c:388
@ IMAP_SYNC_IN_PROGRESS
Sync is in progress, block expunge/newmail processing.
Definition private.h:73
@ IMAP_EXPUNGE_EXPECTED
Messages will be expunged from the server.
Definition private.h:69
static int sync_helper(struct Mailbox *m, struct Email **emails, int num_emails, AclFlags right, enum MessageType flag, const char *name)
Sync flag changes to the server.
Definition imap.c:404
enum MxStatus imap_check_mailbox(struct Mailbox *m, bool force)
Use the NOOP or IDLE command to poll for new mail.
Definition imap.c:1220
#define MUTT_MEM_MALLOC(n, type)
Definition memory.h:53
void mutt_qsort_r(void *base, size_t nmemb, size_t size, sort_t compar, void *sdata)
Sort an array, where the comparator has access to opaque data rather than requiring global variables.
Definition qsort_r.c:72
enum QuadOption query_yesorno(const char *prompt, enum QuadOption def)
Ask the user a Yes/No question.
Definition question.c:329
struct Envelope * env
Envelope information.
Definition email.h:68
bool attach_del
Has an attachment marked for deletion.
Definition email.h:99
unsigned char changed
Changed fields, e.g. MUTT_ENV_CHANGED_SUBJECT.
Definition envelope.h:90
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:45
bool changed
Mailbox has been modified.
Definition mailbox.h:112
bool append
Mailbox is opened in append mode.
Definition mailbox.h:111
+ 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 1980 of file imap.c.

1981{
1983 struct ImapMboxData *mdata = imap_mdata_get(m);
1984 if (!adata || !mdata)
1985 return;
1986
1987 const char *condstore = NULL;
1988#ifdef USE_HCACHE
1989 const bool c_imap_condstore = cs_subset_bool(NeoMutt->sub, "imap_condstore");
1990 if ((adata->capabilities & IMAP_CAP_CONDSTORE) && c_imap_condstore)
1991 condstore = " (CONDSTORE)";
1992 else
1993#endif
1994 condstore = "";
1995
1996 char buf[PATH_MAX] = { 0 };
1997 snprintf(buf, sizeof(buf), "%s %s%s", m->readonly ? "EXAMINE" : "SELECT",
1998 mdata->munge_name, condstore);
1999
2000 adata->state = IMAP_SELECTED;
2001
2002 imap_cmd_start(adata, buf);
2003}
@ IMAP_CAP_CONDSTORE
RFC7162.
Definition private.h:150
bool readonly
Don't allow changes to the mailbox.
Definition mailbox.h:118
+ 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 2013 of file imap.c.

2014{
2015 if (!adata)
2016 return -1;
2017
2018 if (adata->state == IMAP_DISCONNECTED)
2019 {
2020 buf_reset(&adata->cmdbuf); // purge outstanding queued commands
2021 imap_open_connection(adata);
2022 }
2023 if (adata->state == IMAP_CONNECTED)
2024 {
2026 {
2027 adata->state = IMAP_AUTHENTICATED;
2028 FREE(&adata->capstr);
2029 if (adata->conn->ssf != 0)
2030 {
2031 mutt_debug(LL_DEBUG2, "Communication encrypted at %d bits\n",
2032 adata->conn->ssf);
2033 }
2034 }
2035 else
2036 {
2038 }
2039 }
2040 if (adata->state == IMAP_AUTHENTICATED)
2041 {
2042 /* capabilities may have changed */
2043 imap_exec(adata, "CAPABILITY", IMAP_CMD_PASS);
2044
2045#ifdef USE_ZLIB
2046 /* RFC4978 */
2047 const bool c_imap_deflate = cs_subset_bool(NeoMutt->sub, "imap_deflate");
2048 if ((adata->capabilities & IMAP_CAP_COMPRESS) && c_imap_deflate &&
2049 (imap_exec(adata, "COMPRESS DEFLATE", IMAP_CMD_PASS) == IMAP_EXEC_SUCCESS))
2050 {
2051 mutt_debug(LL_DEBUG2, "IMAP compression is enabled on connection to %s\n",
2052 adata->conn->account.host);
2053 mutt_zstrm_wrap_conn(adata->conn);
2054 }
2055#endif
2056
2057 /* enable RFC2971, if the server supports that */
2058 const bool c_imap_send_id = cs_subset_bool(NeoMutt->sub, "imap_send_id");
2059 if (c_imap_send_id && (adata->capabilities & IMAP_CAP_ID))
2060 {
2061 imap_exec(adata, "ID (\"name\" \"NeoMutt\" \"version\" \"" PACKAGE_VERSION "\")",
2063 }
2064
2065 /* enable RFC6855, if the server supports that */
2066 const bool c_imap_rfc5161 = cs_subset_bool(NeoMutt->sub, "imap_rfc5161");
2067 if (c_imap_rfc5161 && (adata->capabilities & IMAP_CAP_ENABLE))
2068 imap_exec(adata, "ENABLE UTF8=ACCEPT", IMAP_CMD_QUEUE);
2069
2070 /* enable QRESYNC. Advertising QRESYNC also means CONDSTORE
2071 * is supported (even if not advertised), so flip that bit. */
2072 if (adata->capabilities & IMAP_CAP_QRESYNC)
2073 {
2075 const bool c_imap_qresync = cs_subset_bool(NeoMutt->sub, "imap_qresync");
2076 if (c_imap_rfc5161 && c_imap_qresync)
2077 imap_exec(adata, "ENABLE QRESYNC", IMAP_CMD_QUEUE);
2078 }
2079
2080 /* get root delimiter, '/' as default */
2081 adata->delim = '/';
2082 imap_exec(adata, "LIST \"\" \"\"", IMAP_CMD_QUEUE);
2083
2084 /* we may need the root delimiter before we open a mailbox */
2085 imap_exec(adata, NULL, IMAP_CMD_NONE);
2086
2087 /* select the mailbox that used to be open before disconnect */
2088 if (adata->mailbox)
2089 {
2090 imap_mbox_select(adata->mailbox);
2091 }
2092 }
2093
2094 if (adata->state < IMAP_AUTHENTICATED)
2095 return -1;
2096
2097 return 0;
2098}
@ IMAP_AUTH_SUCCESS
Authentication successful.
Definition auth.h:40
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition buffer.c:76
void mutt_account_unsetpass(struct ConnAccount *cac)
Unset ConnAccount's password.
int imap_authenticate(struct ImapAccountData *adata)
Authenticate to an IMAP server.
Definition auth.c:116
@ IMAP_CAP_QRESYNC
RFC7162.
Definition private.h:151
@ IMAP_CAP_COMPRESS
RFC4978: COMPRESS=DEFLATE.
Definition private.h:153
@ IMAP_CAP_ENABLE
RFC5161.
Definition private.h:149
@ IMAP_CAP_ID
RFC2971: IMAP4 ID extension.
Definition private.h:155
@ IMAP_CMD_PASS
Command contains a password. Suppress logging.
Definition private.h:83
int imap_open_connection(struct ImapAccountData *adata)
Open an IMAP connection.
Definition imap.c:921
static void imap_mbox_select(struct Mailbox *m)
Select a Mailbox.
Definition imap.c:1980
char delim
Path delimiter.
Definition adata.h:79
struct Buffer cmdbuf
Command queue.
Definition adata.h:77
void mutt_zstrm_wrap_conn(struct Connection *conn)
Wrap a compression layer around a Connection.
Definition zstrm.c:297
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_select_and_poll()

static int imap_select_and_poll ( struct Mailbox * m,
int * countp )
static

Send SELECT and parse the untagged responses.

Parameters
mMailbox
[out]countpNumber of messages reported by EXISTS
Return values
0Success
-1Failure (protocol error or parse error)

Issues the SELECT command via imap_mbox_select() and then reads the untagged responses, populating mdata fields (flags, uidvalidity, uid_next, modseq, readonly) and returning the EXISTS count. Both imap_mbox_open and imap_reopen_mailbox share this code path.

Definition at line 2112 of file imap.c.

2113{
2115 struct ImapMboxData *mdata = imap_mdata_get(m);
2116
2118
2119 int count = 0;
2120 int rc;
2121 do
2122 {
2123 char *pc = NULL;
2124
2125 rc = imap_cmd_step(adata);
2126 if (rc != IMAP_RES_CONTINUE)
2127 break;
2128
2129 if (!mutt_strn_equal(adata->buf, "* ", 2))
2130 continue;
2131 pc = imap_next_word(adata->buf);
2132
2133 /* Obtain list of available flags here, may be overridden by a
2134 * PERMANENTFLAGS tag in the OK response */
2135 if (mutt_istr_startswith(pc, "FLAGS"))
2136 {
2137 /* don't override PERMANENTFLAGS */
2138 if (STAILQ_EMPTY(&mdata->flags))
2139 {
2140 mutt_debug(LL_DEBUG3, "Getting mailbox FLAGS\n");
2141 pc = get_flags(&mdata->flags, pc);
2142 if (!pc)
2143 return -1;
2144 }
2145 }
2146 else if (mutt_istr_startswith(pc, "OK [PERMANENTFLAGS"))
2147 {
2148 /* PERMANENTFLAGS are massaged to look like FLAGS, then override FLAGS */
2149 mutt_debug(LL_DEBUG3, "Getting mailbox PERMANENTFLAGS\n");
2150 /* safe to call on NULL */
2151 mutt_list_free(&mdata->flags);
2152 /* skip "OK [PERMANENT" so syntax is the same as FLAGS */
2153 pc += 13;
2154 pc = get_flags(&(mdata->flags), pc);
2155 if (!pc)
2156 return -1;
2157 }
2158 else if (mutt_istr_startswith(pc, "OK [UIDVALIDITY"))
2159 {
2160 /* save UIDVALIDITY for the header cache */
2161 mutt_debug(LL_DEBUG3, "Getting mailbox UIDVALIDITY\n");
2162 pc += 3;
2163 pc = imap_next_word(pc);
2164 if (!mutt_str_atoui(pc, &mdata->uidvalidity))
2165 return -1;
2166 }
2167 else if (mutt_istr_startswith(pc, "OK [UIDNEXT"))
2168 {
2169 mutt_debug(LL_DEBUG3, "Getting mailbox UIDNEXT\n");
2170 pc += 3;
2171 pc = imap_next_word(pc);
2172 if (!mutt_str_atoui(pc, &mdata->uid_next))
2173 return -1;
2174 }
2175 else if (mutt_istr_startswith(pc, "OK [HIGHESTMODSEQ"))
2176 {
2177 mutt_debug(LL_DEBUG3, "Getting mailbox HIGHESTMODSEQ\n");
2178 pc += 3;
2179 pc = imap_next_word(pc);
2180 if (!mutt_str_atoull(pc, &mdata->modseq))
2181 return -1;
2182 }
2183 else if (mutt_istr_startswith(pc, "OK [NOMODSEQ"))
2184 {
2185 mutt_debug(LL_DEBUG3, "Mailbox has NOMODSEQ set\n");
2186 mdata->modseq = 0;
2187 }
2188 else
2189 {
2190 pc = imap_next_word(pc);
2191 if (mutt_istr_startswith(pc, "EXISTS"))
2192 {
2193 count = mdata->new_mail_count;
2194 mdata->new_mail_count = 0;
2195 }
2196 }
2197 } while (rc == IMAP_RES_CONTINUE);
2198
2199 if (rc == IMAP_RES_NO)
2200 {
2201 char *s = imap_next_word(adata->buf); /* skip seq */
2202 s = imap_next_word(s); /* Skip response */
2203 mutt_error("%s", s);
2204 return -1;
2205 }
2206
2207 if (rc != IMAP_RES_OK)
2208 return -1;
2209
2210 /* check for READ-ONLY notification */
2211 if (mutt_istr_startswith(imap_get_qualifier(adata->buf), "[READ-ONLY]") &&
2212 !(adata->capabilities & IMAP_CAP_ACL))
2213 {
2214 mutt_debug(LL_DEBUG2, "Mailbox is read-only\n");
2215 m->readonly = true;
2216 }
2217
2218 /* dump the mailbox flags we've found */
2219 const short c_debug_level = cs_subset_number(NeoMutt->sub, "debug_level");
2220 if (c_debug_level > LL_DEBUG2)
2221 {
2222 if (STAILQ_EMPTY(&mdata->flags))
2223 {
2224 mutt_debug(LL_DEBUG3, "No folder flags found\n");
2225 }
2226 else
2227 {
2228 struct ListNode *np = NULL;
2229 struct Buffer *flag_buffer = buf_pool_get();
2230 buf_printf(flag_buffer, "Mailbox flags: ");
2231 STAILQ_FOREACH(np, &mdata->flags, entries)
2232 {
2233 buf_add_printf(flag_buffer, "[%s] ", np->data);
2234 }
2235 mutt_debug(LL_DEBUG3, "%s\n", buf_string(flag_buffer));
2236 buf_pool_release(&flag_buffer);
2237 }
2238 }
2239
2240 if (!((m->rights & MUTT_ACL_DELETE) || (m->rights & MUTT_ACL_SEEN) ||
2241 (m->rights & MUTT_ACL_WRITE) || (m->rights & MUTT_ACL_INSERT)))
2242 {
2243 m->readonly = true;
2244 }
2245
2246 *countp = count;
2247 return 0;
2248}
const char * mutt_str_atoull(const char *str, unsigned long long *dst)
Convert ASCII string to an unsigned long long.
Definition atoi.c:295
const char * mutt_str_atoui(const char *str, unsigned int *dst)
Convert ASCII string to an unsigned integer.
Definition atoi.c:217
int buf_add_printf(struct Buffer *buf, const char *fmt,...)
Format a string appending a Buffer.
Definition buffer.c:204
@ MUTT_ACL_INSERT
Add/copy into the mailbox (used when editing a message)
Definition mailbox.h:66
#define IMAP_RES_NO
<tag> NO ...
Definition private.h:52
@ IMAP_CAP_ACL
RFC2086: IMAP4 ACL extension.
Definition private.h:138
char * imap_next_word(char *s)
Find where the next IMAP word begins.
Definition util.c:829
static char * get_flags(struct ListHead *hflags, char *s)
Make a simple list out of a FLAGS response.
Definition imap.c:233
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_reopen_mailbox()

int imap_reopen_mailbox ( struct ImapAccountData * adata)

Re-SELECT the current mailbox after reconnecting.

Parameters
adataImap Account data (must be in IMAP_AUTHENTICATED state)
Return values
0Success
-1Failure

After a connection drop and successful reconnect, re-SELECT the previously open mailbox, clear stale message state, and re-fetch all headers so the index is repopulated. This avoids the destructive mx_fastclose_mailbox() path that used to leave the user with an empty screen.

Definition at line 2261 of file imap.c.

2262{
2263 if (!adata || !adata->mailbox)
2264 return -1;
2265
2266 struct Mailbox *m = adata->mailbox;
2267 struct ImapMboxData *mdata = imap_mdata_get(m);
2268 if (!mdata)
2269 return -1;
2270
2271 mutt_debug(LL_DEBUG1, "Re-selecting mailbox %s after reconnect\n", mdata->name);
2272
2273 for (int i = 0; i < m->msg_count; i++)
2274 {
2275 struct Email *e = m->emails[i];
2276 if (!e)
2277 continue;
2278 imap_edata_free((void **) &e->edata);
2279 email_free(&m->emails[i]);
2280 }
2281 m->msg_count = 0;
2282 m->msg_unread = 0;
2283 m->msg_flagged = 0;
2284 m->msg_new = 0;
2285 m->msg_deleted = 0;
2286 m->size = 0;
2287 m->vcount = 0;
2288
2290
2291 mdata->new_mail_count = 0;
2292 mdata->reopen = IMAP_OPEN_NONE;
2294
2295 int count = 0;
2296 if (imap_select_and_poll(m, &count) < 0)
2297 goto fail;
2298
2299 mx_alloc_memory(m, count);
2300
2301 if ((count > 0) && (imap_read_headers(m, 1, count, true) < 0))
2302 goto fail;
2303
2304 mutt_debug(LL_DEBUG1, "Reopened mailbox %s with %d messages\n", mdata->name, m->msg_count);
2307 return 0;
2308
2309fail:
2310 mutt_debug(LL_DEBUG1, "Failed to reopen mailbox %s\n", mdata->name);
2311 if (adata->state == IMAP_SELECTED)
2312 adata->state = IMAP_AUTHENTICATED;
2313 return -1;
2314}
void email_free(struct Email **ptr)
Free an Email.
Definition email.c:46
int imap_read_headers(struct Mailbox *m, unsigned int msn_begin, unsigned int msn_end, bool initial_download)
Read headers from the server.
Definition message.c:1366
void imap_mdata_cache_reset(struct ImapMboxData *mdata)
Release and clear cache data of ImapMboxData structure.
Definition util.c:111
static int imap_select_and_poll(struct Mailbox *m, int *countp)
Send SELECT and parse the untagged responses.
Definition imap.c:2112
void mx_alloc_memory(struct Mailbox *m, int req_size)
Create storage for the emails.
Definition mx.c:1208
unsigned int new_mail_count
Set when EXISTS notifies of new mail.
Definition mdata.h:47
ImapOpenFlags check_status
Flags, e.g. IMAP_NEWMAIL_PENDING.
Definition mdata.h:46
int vcount
The number of virtual messages.
Definition mailbox.h:101
int msg_new
Number of new messages.
Definition mailbox.h:94
int msg_deleted
Number of deleted messages.
Definition mailbox.h:95
off_t size
Size of the Mailbox.
Definition mailbox.h:86
int msg_flagged
Number of flagged messages.
Definition mailbox.h:92
int msg_unread
Number of unread messages.
Definition mailbox.h:91
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

Variable Documentation

◆ ImapCommands

const struct Command ImapCommands[]
Initial value:
= {
N_("Subscribe to an IMAP mailbox"),
N_("subscribe-to <imap-folder-uri>"),
"optionalfeatures.html#imap" },
N_("Unsubscribe from an IMAP mailbox"),
N_("unsubscribe-from <imap-folder-uri>"),
"optionalfeatures.html#imap" },
{ NULL, CMD_NONE, NULL, NULL, NULL, NULL, CF_NONE },
}
@ CF_NONE
No flags are set.
Definition command.h:49
@ CMD_UNSUBSCRIBE_FROM
:unsubscribe-from
Definition command.h:149
@ CMD_SUBSCRIBE_TO
:subscribe-to
Definition command.h:121
@ CMD_NONE
No Command.
Definition command.h:62
enum CommandResult parse_subscribe_to(const struct Command *cmd, struct Buffer *line, const struct ParseContext *pc, struct ParseError *pe)
Parse the 'subscribe-to' command - Implements Command::parse() -.
Definition imap.c:93
enum CommandResult parse_unsubscribe_from(const struct Command *cmd, struct Buffer *line, const struct ParseContext *pc, struct ParseError *pe)
Parse the 'unsubscribe-from' command - Implements Command::parse() -.
Definition imap.c:143
#define N_(a)
Definition message.h:32

Imap Commands.

Definition at line 186 of file imap.c.

186 {
187 // clang-format off
188 { "subscribe-to", CMD_SUBSCRIBE_TO, parse_subscribe_to,
189 N_("Subscribe to an IMAP mailbox"),
190 N_("subscribe-to <imap-folder-uri>"),
191 "optionalfeatures.html#imap" },
192 { "unsubscribe-from", CMD_UNSUBSCRIBE_FROM, parse_unsubscribe_from,
193 N_("Unsubscribe from an IMAP mailbox"),
194 N_("unsubscribe-from <imap-folder-uri>"),
195 "optionalfeatures.html#imap" },
196
197 { NULL, CMD_NONE, NULL, NULL, NULL, NULL, CF_NONE },
198 // clang-format on
199};