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

IMAP network mailbox. More...

#include "config.h"
#include <limits.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "private.h"
#include "mutt/lib.h"
#include "config/lib.h"
#include "email/lib.h"
#include "core/lib.h"
#include "conn/lib.h"
#include "mutt.h"
#include "lib.h"
#include "editor/lib.h"
#include "history/lib.h"
#include "parse/lib.h"
#include "progress/lib.h"
#include "question/lib.h"
#include "adata.h"
#include "auth.h"
#include "commands.h"
#include "edata.h"
#include "external.h"
#include "hook.h"
#include "mdata.h"
#include "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

void imap_init (void)
 Setup feature commands.
 
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 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

static 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

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.

Definition in file imap.c.

Function Documentation

◆ imap_init()

void imap_init ( void )

Setup feature commands.

Definition at line 102 of file imap.c.

103{
105}
bool commands_register(struct CommandArray *ca, const struct Command *cmds)
Add commands to Commands array.
Definition command.c:51
static const struct Command ImapCommands[]
Imap Commands.
Definition imap.c:84
Container for Accounts, Notifications.
Definition neomutt.h:43
struct CommandArray commands
NeoMutt commands.
Definition neomutt.h:51
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ check_capabilities()

static int check_capabilities ( struct ImapAccountData * adata)
static

Make sure we can log in to this server.

Parameters
adataImap Account data
Return values
0Success
-1Failure

Definition at line 113 of file imap.c.

114{
115 if (imap_exec(adata, "CAPABILITY", IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS)
116 {
117 imap_error("check_capabilities", adata->buf);
118 return -1;
119 }
120
121 if (!((adata->capabilities & IMAP_CAP_IMAP4) || (adata->capabilities & IMAP_CAP_IMAP4REV1)))
122 {
123 mutt_error(_("This IMAP server is ancient. NeoMutt does not work with it."));
124 return -1;
125 }
126
127 return 0;
128}
#define mutt_error(...)
Definition logging2.h:93
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:1322
#define IMAP_CMD_NO_FLAGS
No flags are set.
Definition private.h:71
#define IMAP_CAP_IMAP4
Server supports IMAP4.
Definition private.h:121
#define IMAP_CAP_IMAP4REV1
Server supports IMAP4rev1.
Definition private.h:122
@ IMAP_EXEC_SUCCESS
Imap command executed or queued successfully.
Definition private.h:82
void imap_error(const char *where, const char *msg)
Show an error and abort.
Definition util.c:660
#define _(a)
Definition message.h:28
ImapCapFlags capabilities
Capability flags.
Definition adata.h:55
char * buf
Definition adata.h:59
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ get_flags()

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

Make a simple list out of a FLAGS response.

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

return stream following FLAGS response

Definition at line 139 of file imap.c.

140{
141 /* sanity-check string */
142 const size_t plen = mutt_istr_startswith(s, "FLAGS");
143 if (plen == 0)
144 {
145 mutt_debug(LL_DEBUG1, "not a FLAGS response: %s\n", s);
146 return NULL;
147 }
148 s += plen;
149 SKIPWS(s);
150 if (*s != '(')
151 {
152 mutt_debug(LL_DEBUG1, "bogus FLAGS response: %s\n", s);
153 return NULL;
154 }
155
156 /* update caller's flags handle */
157 while (*s && (*s != ')'))
158 {
159 s++;
160 SKIPWS(s);
161 const char *flag_word = s;
162 while (*s && (*s != ')') && !mutt_isspace(*s))
163 s++;
164 const char ctmp = *s;
165 *s = '\0';
166 if (*flag_word)
167 mutt_list_insert_tail(hflags, mutt_str_dup(flag_word));
168 *s = ctmp;
169 }
170
171 /* note bad flags response */
172 if (*s != ')')
173 {
174 mutt_debug(LL_DEBUG1, "Unterminated FLAGS response: %s\n", s);
175 mutt_list_free(hflags);
176
177 return NULL;
178 }
179
180 s++;
181
182 return s;
183}
bool mutt_isspace(int arg)
Wrapper for isspace(3)
Definition ctype.c:95
#define mutt_debug(LEVEL,...)
Definition logging2.h:90
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:44
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition string.c:255
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition string.c:244
#define SKIPWS(ch)
Definition string2.h:51
+ 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 193 of file imap.c.

195{
196 struct ImapMboxData *mdata = imap_mdata_get(m);
197 if (!mdata)
198 return;
199
200 if (m->rights & aclflag)
201 if (flag && imap_has_flag(&mdata->flags, str))
202 buf_addstr(flags, str);
203}
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:894
IMAP-specific Mailbox data -.
Definition mdata.h:40
struct ListHead flags
Definition mdata.h:50
AclFlags rights
ACL bits, see AclFlags.
Definition mailbox.h:119
void * mdata
Driver specific data.
Definition mailbox.h:132
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

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

214{
215 struct ImapEmailData *edata = e->edata;
216
217 if (e->read != edata->read)
218 return true;
219 if (e->old != edata->old)
220 return true;
221 if (e->flagged != edata->flagged)
222 return true;
223 if (e->replied != edata->replied)
224 return true;
225
226 return false;
227}
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 240 of file imap.c.

242{
243 if (!emails || !uida)
244 return -1;
245
246 for (int i = 0; i < num_emails; i++)
247 {
248 struct Email *e = emails[i];
249 if (changed && !e->changed)
250 continue;
251
252 /* don't include pending expunged messages.
253 *
254 * TODO: can we unset active in cmd_parse_expunge() and
255 * cmd_parse_vanished() instead of checking for index != INT_MAX. */
256 if (!e || !e->active || (e->index == INT_MAX))
257 continue;
258
260
261 bool match = false;
262 switch (flag)
263 {
264 case MUTT_DELETED:
265 if (e->deleted != edata->deleted)
266 match = invert ^ e->deleted;
267 break;
268 case MUTT_FLAG:
269 if (e->flagged != edata->flagged)
270 match = invert ^ e->flagged;
271 break;
272 case MUTT_OLD:
273 if (e->old != edata->old)
274 match = invert ^ e->old;
275 break;
276 case MUTT_READ:
277 if (e->read != edata->read)
278 match = invert ^ e->read;
279 break;
280 case MUTT_REPLIED:
281 if (e->replied != edata->replied)
282 match = invert ^ e->replied;
283 break;
284 case MUTT_TRASH:
285 if (e->deleted && !e->purge)
286 match = true;
287 break;
288 default:
289 break;
290 }
291
292 if (match)
293 ARRAY_ADD(uida, edata->uid);
294 }
295
296 return ARRAY_SIZE(uida);
297}
#define ARRAY_ADD(head, elem)
Add an element at the end of the array.
Definition array.h:156
#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:85
@ MUTT_READ
Messages that have been read.
Definition mutt.h:73
@ MUTT_OLD
Old messages.
Definition mutt.h:71
@ MUTT_FLAG
Flagged messages.
Definition mutt.h:79
@ MUTT_DELETED
Deleted messages.
Definition mutt.h:78
@ MUTT_REPLIED
Messages that have been replied to.
Definition mutt.h:72
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 310 of file imap.c.

312{
314 struct ImapMboxData *mdata = imap_mdata_get(m);
315 if (!adata || !mdata)
316 return -1;
317
318 if ((m->rights & right) == 0)
319 return 0;
320
321 if ((right == MUTT_ACL_WRITE) && !imap_has_flag(&mdata->flags, name))
322 return 0;
323
324 int count = 0;
325 char buf[1024] = { 0 };
326
327 struct UidArray uida = ARRAY_HEAD_INITIALIZER;
328
329 // Set the flag (+FLAGS) on matching emails
330 select_email_uids(emails, num_emails, flag, true, false, &uida);
331 snprintf(buf, sizeof(buf), "+FLAGS.SILENT (%s)", name);
332 int rc = imap_exec_msg_set(adata, "UID STORE", buf, &uida);
333 if (rc < 0)
334 return rc;
335 count += rc;
336 ARRAY_FREE(&uida);
337
338 // Clear the flag (-FLAGS) on non-matching emails
339 select_email_uids(emails, num_emails, flag, true, true, &uida);
340 buf[0] = '-';
341 rc = imap_exec_msg_set(adata, "UID STORE", buf, &uida);
342 if (rc < 0)
343 return rc;
344 count += rc;
345 ARRAY_FREE(&uida);
346
347 return count;
348}
#define ARRAY_FREE(head)
Release all memory.
Definition array.h:204
#define ARRAY_HEAD_INITIALIZER
Static initializer for arrays.
Definition array.h:58
#define 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:123
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:240
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:132
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 359 of file imap.c.

360{
361 size_t pos = start;
362
363 size_t len = buf_len(buf);
364 while ((pos < len) && (buf_at(buf, pos) != '\0') && (buf_at(buf, pos) == src[pos]))
365 pos++;
366 buf->data[pos] = '\0';
367
368 buf_fix_dptr(buf);
369
370 return pos;
371}
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 382 of file imap.c.

383{
384 int rc = -1;
385 size_t matchlen;
386
387 matchlen = buf_len(buf);
388 struct MailboxArray ma = neomutt_mailboxes_get(NeoMutt, MUTT_MAILBOX_ANY);
389 struct Mailbox **mp = NULL;
390 ARRAY_FOREACH(mp, &ma)
391 {
392 struct Mailbox *m = *mp;
393
395 continue;
396
397 if (rc)
398 {
399 buf_strcpy(buf, mailbox_path(m));
400 rc = 0;
401 }
402 else
403 {
404 longest_common_prefix(buf, mailbox_path(m), matchlen);
405 }
406 }
407 ARRAY_FREE(&ma); // Clean up the ARRAY, but not the Mailboxes
408
409#if 0
410 TAILQ_FOREACH(conn, mutt_socket_head(), entries)
411 {
412 struct Url url = { 0 };
413 char urlstr[1024] = { 0 };
414
415 if (conn->account.type != MUTT_ACCT_TYPE_IMAP)
416 continue;
417
418 account_to_url(&conn->account, &url);
419 /* FIXME: how to handle multiple users on the same host? */
420 url.user = NULL;
421 url.path = NULL;
422 url_tostring(&url, urlstr, sizeof(urlstr), U_NO_FLAGS);
423 if (mutt_strn_equal(buf, urlstr, matchlen))
424 {
425 if (rc)
426 {
427 mutt_str_copy(buf, urlstr, buflen);
428 rc = 0;
429 }
430 else
431 {
432 longest_common_prefix(buf, urlstr, matchlen);
433 }
434 }
435 }
436#endif
437
438 return rc;
439}
#define ARRAY_FOREACH(elem, head)
Iterate over all elements of the array.
Definition array.h:214
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:214
@ MUTT_MAILBOX_ANY
Match any Mailbox type.
Definition mailbox.h:42
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:359
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:427
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition string.c:232
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:581
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:189
#define TAILQ_FOREACH(var, head, field)
Definition queue.h:782
A mailbox.
Definition mailbox.h:79
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:422
#define U_NO_FLAGS
Definition url.h:49
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_create_mailbox()

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

Create a new mailbox.

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

Definition at line 448 of file imap.c.

449{
450 char buf[2048] = { 0 };
451 char mbox[1024] = { 0 };
452
453 imap_munge_mbox_name(adata->unicode, mbox, sizeof(mbox), mailbox);
454 snprintf(buf, sizeof(buf), "CREATE %s", mbox);
455
457 {
458 mutt_error(_("CREATE failed: %s"), imap_cmd_trailer(adata));
459 return -1;
460 }
461
462 return 0;
463}
const char * imap_cmd_trailer(struct ImapAccountData *adata)
Extra information after tagged command response if any.
Definition command.c:1285
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:960
bool unicode
If true, we can send UTF-8, and the server will use UTF8 rather than mUTF7.
Definition adata.h:62
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_access()

int imap_access ( const char * path)

Check permissions on an IMAP mailbox with a new connection.

Parameters
pathMailbox path
Return values
0Success
<0Failure

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

Definition at line 475 of file imap.c.

476{
477 if (imap_path_status(path, false) >= 0)
478 return 0;
479 return -1;
480}
int imap_path_status(const char *path, bool queue)
Refresh the number of total and new messages.
Definition imap.c:1194
+ 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 490 of file imap.c.

491{
492 char oldmbox[1024] = { 0 };
493 char newmbox[1024] = { 0 };
494 int rc = 0;
495
496 imap_munge_mbox_name(adata->unicode, oldmbox, sizeof(oldmbox), oldname);
497 imap_munge_mbox_name(adata->unicode, newmbox, sizeof(newmbox), newname);
498
499 struct Buffer *buf = buf_pool_get();
500 buf_printf(buf, "RENAME %s %s", oldmbox, newmbox);
501
503 rc = -1;
504
505 buf_pool_release(&buf);
506
507 return rc;
508}
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:82
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition pool.c:96
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 517 of file imap.c.

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

◆ imap_logout()

static void imap_logout ( struct ImapAccountData * adata)
static

Gracefully log out of server.

Parameters
adataImap Account data

Definition at line 543 of file imap.c.

544{
545 if (adata->status != IMAP_FATAL)
546 {
547 /* we set status here to let imap_handle_untagged know we _expect_ to
548 * receive a bye response (so it doesn't freak out and close the conn) */
549 if (adata->state == IMAP_DISCONNECTED)
550 {
551 return;
552 }
553
554 adata->status = IMAP_BYE;
555 imap_cmd_start(adata, "LOGOUT");
556 const short c_imap_poll_timeout = cs_subset_number(NeoMutt->sub, "imap_poll_timeout");
557 if ((c_imap_poll_timeout <= 0) ||
558 (mutt_socket_poll(adata->conn, c_imap_poll_timeout) != 0))
559 {
560 while (imap_cmd_step(adata) == IMAP_RES_CONTINUE)
561 ; // do nothing
562 }
563 }
564 mutt_socket_close(adata->conn);
565 adata->state = IMAP_DISCONNECTED;
566}
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:1133
int imap_cmd_step(struct ImapAccountData *adata)
Reads server responses from an IMAP command.
Definition command.c:1147
@ IMAP_DISCONNECTED
Disconnected from server.
Definition private.h:105
@ IMAP_BYE
Logged out from server.
Definition private.h:96
@ IMAP_FATAL
Unrecoverable error occurred.
Definition private.h:95
#define IMAP_RES_CONTINUE
* ...
Definition private.h:56
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:44
unsigned char status
ImapFlags, e.g. IMAP_FATAL.
Definition adata.h:45
struct Connection * conn
Connection to IMAP server.
Definition adata.h:41
struct ConfigSubset * sub
Inherited config items.
Definition neomutt.h:47
+ 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 573 of file imap.c.

574{
575 struct Account **ap = NULL;
577 {
578 struct Account *a = *ap;
579 if (a->type != MUTT_IMAP)
580 continue;
581
582 struct ImapAccountData *adata = a->adata;
583 if (!adata)
584 continue;
585
586 struct Connection *conn = adata->conn;
587 if (!conn || (conn->fd < 0))
588 continue;
589
590 mutt_message(_("Closing connection to %s..."), conn->account.host);
591 imap_logout(a->adata);
593 }
594}
@ MUTT_IMAP
'IMAP' Mailbox type
Definition mailbox.h:50
#define mutt_message(...)
Definition logging2.h:92
static void imap_logout(struct ImapAccountData *adata)
Gracefully log out of server.
Definition imap.c:543
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:54
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:48
+ 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 610 of file imap.c.

612{
613 char c;
614 bool r = false;
615 struct Buffer buf = { 0 }; // Do not allocate, maybe it won't be used
616
617 const short c_debug_level = cs_subset_number(NeoMutt->sub, "debug_level");
618 if (c_debug_level >= IMAP_LOG_LTRL)
619 buf_alloc(&buf, bytes + 1);
620
621 mutt_debug(LL_DEBUG2, "reading %lu bytes\n", bytes);
622
623 for (unsigned long pos = 0; pos < bytes; pos++)
624 {
625 if (mutt_socket_readchar(adata->conn, &c) != 1)
626 {
627 mutt_debug(LL_DEBUG1, "error during read, %lu bytes read\n", pos);
628 adata->status = IMAP_FATAL;
629
630 buf_dealloc(&buf);
631 return -1;
632 }
633
634 if (r && (c != '\n'))
635 fputc('\r', fp);
636
637 if (c == '\r')
638 {
639 r = true;
640 continue;
641 }
642 else
643 {
644 r = false;
645 }
646
647 fputc(c, fp);
648
649 if ((pos % 1024) == 0)
650 progress_update(progress, pos, -1);
651 if (c_debug_level >= IMAP_LOG_LTRL)
652 buf_addch(&buf, c);
653 }
654
655 if (c_debug_level >= IMAP_LOG_LTRL)
656 {
657 mutt_debug(IMAP_LOG_LTRL, "\n%s", buf.data);
658 buf_dealloc(&buf);
659 }
660 return 0;
661}
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
Definition private.h:49
@ LL_DEBUG2
Log at debug level 2.
Definition logging2.h:45
bool progress_update(struct Progress *progress, size_t pos, int percent)
Update the state of the progress bar.
Definition progress.c:80
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 668 of file imap.c.

669{
670 struct ImapMboxData *mdata = imap_mdata_get(m);
672
673 if (!mdata || !edata)
674 return;
675
676 imap_msn_remove(&mdata->msn, edata->msn - 1);
677 edata->msn = 0;
678}
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 689 of file imap.c.

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

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

762{
763 if (mutt_socket_open(adata->conn) < 0)
764 return -1;
765
766 adata->state = IMAP_CONNECTED;
767
768 if (imap_cmd_step(adata) != IMAP_RES_OK)
769 {
771 return -1;
772 }
773
774 if (mutt_istr_startswith(adata->buf, "* OK"))
775 {
776 if (!mutt_istr_startswith(adata->buf, "* OK [CAPABILITY") && check_capabilities(adata))
777 {
778 goto bail;
779 }
780#ifdef USE_SSL
781 /* Attempt STARTTLS if available and desired. */
782 const bool c_ssl_force_tls = cs_subset_bool(NeoMutt->sub, "ssl_force_tls");
783 if ((adata->conn->ssf == 0) &&
784 (c_ssl_force_tls || (adata->capabilities & IMAP_CAP_STARTTLS)))
785 {
786 enum QuadOption ans;
787
788 if (c_ssl_force_tls)
789 {
790 ans = MUTT_YES;
791 }
792 else if ((ans = query_quadoption(_("Secure connection with TLS?"),
793 NeoMutt->sub, "ssl_starttls")) == MUTT_ABORT)
794 {
795 goto bail;
796 }
797 if (ans == MUTT_YES)
798 {
799 enum ImapExecResult rc = imap_exec(adata, "STARTTLS", IMAP_CMD_SINGLE);
800 // Clear any data after the STARTTLS acknowledgement
801 mutt_socket_empty(adata->conn);
802
803 if (rc == IMAP_EXEC_FATAL)
804 goto bail;
805 if (rc != IMAP_EXEC_ERROR)
806 {
807 if (mutt_ssl_starttls(adata->conn))
808 {
809 mutt_error(_("Could not negotiate TLS connection"));
810 goto bail;
811 }
812 else
813 {
814 /* RFC2595 demands we recheck CAPABILITY after TLS completes. */
815 if (imap_exec(adata, "CAPABILITY", IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS)
816 goto bail;
817 }
818 }
819 }
820 }
821
822 if (c_ssl_force_tls && (adata->conn->ssf == 0))
823 {
824 mutt_error(_("Encrypted connection unavailable"));
825 goto bail;
826 }
827#endif
828 }
829 else if (mutt_istr_startswith(adata->buf, "* PREAUTH"))
830 {
831#ifdef USE_SSL
832 /* Unless using a secure $tunnel, an unencrypted PREAUTH response may be a
833 * MITM attack. The only way to stop "STARTTLS" MITM attacks is via
834 * $ssl_force_tls: an attacker can easily spoof "* OK" and strip the
835 * STARTTLS capability. So consult $ssl_force_tls, not $ssl_starttls, to
836 * decide whether to abort. Note that if using $tunnel and
837 * $tunnel_is_secure, adata->conn->ssf will be set to 1. */
838 const bool c_ssl_force_tls = cs_subset_bool(NeoMutt->sub, "ssl_force_tls");
839 if ((adata->conn->ssf == 0) && c_ssl_force_tls)
840 {
841 mutt_error(_("Encrypted connection unavailable"));
842 goto bail;
843 }
844#endif
845
846 adata->state = IMAP_AUTHENTICATED;
847 if (check_capabilities(adata) != 0)
848 goto bail;
849 FREE(&adata->capstr);
850 }
851 else
852 {
853 imap_error("imap_open_connection()", adata->buf);
854 goto bail;
855 }
856
857 return 0;
858
859bail:
861 FREE(&adata->capstr);
862 return -1;
863}
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:1171
@ IMAP_AUTHENTICATED
Connection is authenticated.
Definition private.h:107
@ IMAP_CONNECTED
Connected to server.
Definition private.h:106
#define IMAP_RES_OK
<tag> OK ...
Definition private.h:55
#define IMAP_CAP_STARTTLS
RFC2595: STARTTLS.
Definition private.h:131
ImapExecResult
Imap_exec return code.
Definition private.h:81
@ IMAP_EXEC_ERROR
Imap command failure.
Definition private.h:83
@ IMAP_EXEC_FATAL
Imap connection failure.
Definition private.h:84
#define IMAP_CMD_SINGLE
Run a single command.
Definition private.h:75
void imap_close_connection(struct ImapAccountData *adata)
Close an IMAP connection.
Definition imap.c:869
static int check_capabilities(struct ImapAccountData *adata)
Make sure we can log in to this server.
Definition imap.c:113
#define FREE(x)
Definition memory.h:62
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:377
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:54
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_close_connection()

void imap_close_connection ( struct ImapAccountData * adata)

Close an IMAP connection.

Parameters
adataImap Account data

Definition at line 869 of file imap.c.

870{
871 if (adata->state != IMAP_DISCONNECTED)
872 {
873 mutt_socket_close(adata->conn);
874 adata->state = IMAP_DISCONNECTED;
875 }
876 adata->seqno = 0;
877 adata->nextcmd = 0;
878 adata->lastcmd = 0;
879 adata->status = 0;
880 memset(adata->cmds, 0, sizeof(struct ImapCommand) * adata->cmdslots);
881}
int lastcmd
Last command in the queue.
Definition adata.h:72
int nextcmd
Next command to be sent.
Definition adata.h:71
struct ImapCommand * cmds
Queue of commands for the server.
Definition adata.h:69
int cmdslots
Size of the command queue.
Definition adata.h:70
unsigned int seqno
tag sequence number, e.g. '{seqid}0001'
Definition adata.h:57
IMAP command structure.
Definition private.h:160
+ 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 894 of file imap.c.

895{
896 if (STAILQ_EMPTY(flag_list))
897 return false;
898
899 const size_t flaglen = mutt_str_len(flag);
900 struct ListNode *np = NULL;
901 STAILQ_FOREACH(np, flag_list, entries)
902 {
903 const size_t nplen = strlen(np->data);
904 if ((flaglen >= nplen) && ((flag[nplen] == '\0') || (flag[nplen] == ' ')) &&
905 mutt_istrn_equal(np->data, flag, nplen))
906 {
907 return true;
908 }
909
910 if (mutt_str_equal(np->data, "\\*"))
911 return true;
912 }
913
914 return false;
915}
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition string.c:660
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition string.c:498
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:455
#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 946 of file imap.c.

948{
951
952 if (!adata || (adata->mailbox != m) || !e)
953 return -1;
954
956 {
957 if (e->deleted == edata->deleted)
958 e->changed = false;
959 return 0;
960 }
961
962 buf_printf(cmd, "UID STORE %u", edata->uid);
963
964 struct Buffer *flags = buf_pool_get();
965
966 set_flag(m, MUTT_ACL_SEEN, e->read, "\\Seen ", flags);
967 set_flag(m, MUTT_ACL_WRITE, e->old, "Old ", flags);
968 set_flag(m, MUTT_ACL_WRITE, e->flagged, "\\Flagged ", flags);
969 set_flag(m, MUTT_ACL_WRITE, e->replied, "\\Answered ", flags);
970 set_flag(m, MUTT_ACL_DELETE, edata->deleted, "\\Deleted ", flags);
971
972 if (m->rights & MUTT_ACL_WRITE)
973 {
974 /* restore system flags */
975 if (edata->flags_system)
976 buf_addstr(flags, edata->flags_system);
977
978 /* set custom flags */
979 struct Buffer *tags = buf_pool_get();
981 if (!buf_is_empty(tags))
982 buf_addstr(flags, buf_string(tags));
983 buf_pool_release(&tags);
984 }
985
987 buf_fix_dptr(flags);
988
989 /* UW-IMAP is OK with null flags, Cyrus isn't. The only solution is to
990 * explicitly revoke all system flags (if we have permission) */
991 if (buf_is_empty(flags))
992 {
993 set_flag(m, MUTT_ACL_SEEN, true, "\\Seen ", flags);
994 set_flag(m, MUTT_ACL_WRITE, true, "Old ", flags);
995 set_flag(m, MUTT_ACL_WRITE, true, "\\Flagged ", flags);
996 set_flag(m, MUTT_ACL_WRITE, true, "\\Answered ", flags);
997 set_flag(m, MUTT_ACL_DELETE, !edata->deleted, "\\Deleted ", flags);
998
999 /* erase custom flags */
1000 if ((m->rights & MUTT_ACL_WRITE) && edata->flags_remote)
1001 buf_addstr(flags, edata->flags_remote);
1002
1004 buf_fix_dptr(flags);
1005
1006 buf_addstr(cmd, " -FLAGS.SILENT (");
1007 }
1008 else
1009 {
1010 buf_addstr(cmd, " FLAGS.SILENT (");
1011 }
1012
1013 buf_addstr(cmd, buf_string(flags));
1014 buf_addstr(cmd, ")");
1015
1016 int rc = -1;
1017
1018 /* after all this it's still possible to have no flags, if you
1019 * have no ACL rights */
1020 if (!buf_is_empty(flags) &&
1022 err_continue && (*err_continue != MUTT_YES))
1023 {
1024 *err_continue = imap_continue("imap_sync_message: STORE failed", adata->buf);
1025 if (*err_continue != MUTT_YES)
1026 goto done;
1027 }
1028
1029 /* server have now the updated flags */
1030 FREE(&edata->flags_remote);
1031 struct Buffer *flags_remote = buf_pool_get();
1032 driver_tags_get_with_hidden(&e->tags, flags_remote);
1033 edata->flags_remote = buf_strdup(flags_remote);
1034 buf_pool_release(&flags_remote);
1035
1036 if (e->deleted == edata->deleted)
1037 e->changed = false;
1038
1039 rc = 0;
1040
1041done:
1042 buf_pool_release(&flags);
1043 return rc;
1044}
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
#define MUTT_ACL_DELETE
Delete a message.
Definition mailbox.h:63
#define MUTT_ACL_SEEN
Change the 'seen' status of a message.
Definition mailbox.h:70
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:649
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:193
static bool compare_flags_for_copy(struct Email *e)
Compare local flags against the server.
Definition imap.c:213
void mutt_str_remove_trailing_ws(char *s)
Trim trailing whitespace from a string.
Definition string.c:565
struct TagList tags
For drivers that support server tagging.
Definition email.h:72
struct Mailbox * mailbox
Current selected mailbox.
Definition adata.h:76
char * flags_remote
Definition edata.h:49
bool deleted
Email has been deleted.
Definition edata.h:39
char * flags_system
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:174
+ 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 1052 of file imap.c.

1053{
1054 if (!m || !m->account)
1055 return MX_STATUS_ERROR;
1056
1058 struct ImapMboxData *mdata = imap_mdata_get(m);
1059 if (!adata || !mdata)
1060 return MX_STATUS_ERROR;
1061
1062 /* overload keyboard timeout to avoid many mailbox checks in a row.
1063 * Most users don't like having to wait exactly when they press a key. */
1064 int rc = 0;
1065
1066 /* try IDLE first, unless force is set */
1067 const bool c_imap_idle = cs_subset_bool(NeoMutt->sub, "imap_idle");
1068 const short c_imap_keep_alive = cs_subset_number(NeoMutt->sub, "imap_keep_alive");
1069 if (!force && c_imap_idle && (adata->capabilities & IMAP_CAP_IDLE) &&
1070 ((adata->state != IMAP_IDLE) || (mutt_date_now() >= adata->lastread + c_imap_keep_alive)))
1071 {
1072 if (imap_cmd_idle(adata) < 0)
1073 return MX_STATUS_ERROR;
1074 }
1075 if (adata->state == IMAP_IDLE)
1076 {
1077 while ((rc = mutt_socket_poll(adata->conn, 0)) > 0)
1078 {
1079 if (imap_cmd_step(adata) != IMAP_RES_CONTINUE)
1080 {
1081 mutt_debug(LL_DEBUG1, "Error reading IDLE response\n");
1082 return MX_STATUS_ERROR;
1083 }
1084 }
1085 if (rc < 0)
1086 {
1087 mutt_debug(LL_DEBUG1, "Poll failed, disabling IDLE\n");
1088 adata->capabilities &= ~IMAP_CAP_IDLE; // Clear the flag
1089 }
1090 }
1091
1092 const short c_timeout = cs_subset_number(NeoMutt->sub, "timeout");
1093 if ((force || ((adata->state != IMAP_IDLE) && (mutt_date_now() >= adata->lastread + c_timeout))) &&
1094 (imap_exec(adata, "NOOP", IMAP_CMD_POLL) != IMAP_EXEC_SUCCESS))
1095 {
1096 return MX_STATUS_ERROR;
1097 }
1098
1099 /* We call this even when we haven't run NOOP in case we have pending
1100 * changes to process, since we can reopen here. */
1101 imap_cmd_finish(adata);
1102
1103 enum MxStatus check = MX_STATUS_OK;
1104 if (mdata->check_status & IMAP_EXPUNGE_PENDING)
1105 check = MX_STATUS_REOPENED;
1106 else if (mdata->check_status & IMAP_NEWMAIL_PENDING)
1107 check = MX_STATUS_NEW_MAIL;
1108 else if (mdata->check_status & IMAP_FLAGS_PENDING)
1109 check = MX_STATUS_FLAGS;
1110 else if (rc < 0)
1111 check = MX_STATUS_ERROR;
1112
1113 mdata->check_status = IMAP_OPEN_NO_FLAGS;
1114
1115 if (force)
1116 m->last_checked = 0; // force a check on the next mx_mbox_check() call
1117 return check;
1118}
int imap_cmd_idle(struct ImapAccountData *adata)
Enter the IDLE state.
Definition command.c:1455
void imap_cmd_finish(struct ImapAccountData *adata)
Attempt to perform cleanup.
Definition command.c:1388
#define IMAP_CAP_IDLE
RFC2177: IDLE.
Definition private.h:133
@ IMAP_IDLE
Connection is idle.
Definition private.h:111
#define IMAP_EXPUNGE_PENDING
Messages on the server have been expunged.
Definition private.h:66
#define IMAP_OPEN_NO_FLAGS
No flags are set.
Definition private.h:63
#define IMAP_CMD_POLL
Poll the tcp connection before running the imap command.
Definition private.h:74
#define IMAP_NEWMAIL_PENDING
New mail is waiting on the server.
Definition private.h:67
#define IMAP_FLAGS_PENDING
Flags have changed on the server.
Definition private.h:68
time_t mutt_date_now(void)
Return the number of seconds since the Unix epoch.
Definition date.c:455
MxStatus
Return values from mbox_check(), mbox_check_stats(), mbox_sync(), and mbox_close()
Definition mxapi.h:60
@ MX_STATUS_ERROR
An error occurred.
Definition mxapi.h:61
@ MX_STATUS_OK
No changes.
Definition mxapi.h:62
@ MX_STATUS_FLAGS
Nondestructive flags change (IMAP)
Definition mxapi.h:66
@ MX_STATUS_REOPENED
Mailbox was reopened.
Definition mxapi.h:65
@ MX_STATUS_NEW_MAIL
New mail received in Mailbox.
Definition mxapi.h:63
time_t lastread
last time we read a command for the server
Definition adata.h:58
time_t last_checked
Last time we checked this mailbox for new mail.
Definition mailbox.h:105
+ 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 1127 of file imap.c.

1128{
1129 char *uidvalidity_flag = NULL;
1130 char cmd[2048] = { 0 };
1131
1132 if (!adata || !mdata)
1133 return -1;
1134
1135 /* Don't issue STATUS on the selected mailbox, it will be NOOPed or
1136 * IDLEd elsewhere.
1137 * adata->mailbox may be NULL for connections other than the current
1138 * mailbox's. */
1139 if (adata->mailbox && (adata->mailbox->mdata == mdata))
1140 {
1141 adata->mailbox->has_new = false;
1142 return mdata->messages;
1143 }
1144
1145 if (adata->mailbox && !adata->mailbox->poll_new_mail)
1146 return mdata->messages;
1147
1148 if (adata->capabilities & IMAP_CAP_IMAP4REV1)
1149 {
1150 uidvalidity_flag = "UIDVALIDITY";
1151 }
1152 else if (adata->capabilities & IMAP_CAP_STATUS)
1153 {
1154 uidvalidity_flag = "UID-VALIDITY";
1155 }
1156 else
1157 {
1158 mutt_debug(LL_DEBUG2, "Server doesn't support STATUS\n");
1159 return -1;
1160 }
1161
1162 snprintf(cmd, sizeof(cmd), "STATUS %s (UIDNEXT %s UNSEEN RECENT MESSAGES)",
1163 mdata->munge_name, uidvalidity_flag);
1164
1165 int rc = imap_exec(adata, cmd, queue ? IMAP_CMD_QUEUE : IMAP_CMD_POLL);
1166 if (rc != IMAP_EXEC_SUCCESS)
1167 {
1168 mutt_debug(LL_DEBUG1, "Error queueing command\n");
1169 return rc;
1170 }
1171 return mdata->messages;
1172}
#define IMAP_CAP_STATUS
Server supports STATUS command.
Definition private.h:123
#define IMAP_CMD_QUEUE
Queue a command, do not execute.
Definition private.h:73
unsigned int 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:85
bool poll_new_mail
Check for new mail.
Definition mailbox.h:115
+ 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 1194 of file imap.c.

1195{
1196 struct Mailbox *m = mx_mbox_find2(path);
1197
1198 const bool is_temp = !m;
1199 if (is_temp)
1200 {
1201 m = mx_path_resolve(path);
1202 if (!mx_mbox_ac_link(m))
1203 {
1204 mailbox_free(&m);
1205 return 0;
1206 }
1207 }
1208
1209 int rc = imap_mailbox_status(m, queue);
1210
1211 if (is_temp)
1212 {
1213 mx_ac_remove(m, false);
1214 mailbox_free(&m);
1215 }
1216
1217 return rc;
1218}
void mailbox_free(struct Mailbox **ptr)
Free a Mailbox.
Definition mailbox.c:89
int imap_mailbox_status(struct Mailbox *m, bool queue)
Refresh the number of total and new messages.
Definition imap.c:1229
int mx_ac_remove(struct Mailbox *m, bool keep_account)
Remove a Mailbox from an Account and delete Account if empty.
Definition mx.c:1757
struct Mailbox * mx_mbox_find2(const char *path)
Find a Mailbox on an Account.
Definition mx.c:1618
bool mx_mbox_ac_link(struct Mailbox *m)
Link a Mailbox to an existing or new Account.
Definition mx.c:251
struct Mailbox * mx_path_resolve(const char *path)
Get a Mailbox for a path.
Definition mx.c:1650
+ 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 1229 of file imap.c.

1230{
1232 struct ImapMboxData *mdata = imap_mdata_get(m);
1233 if (!adata || !mdata)
1234 return -1;
1235 return imap_status(adata, mdata, queue);
1236}
static int imap_status(struct ImapAccountData *adata, struct ImapMboxData *mdata, bool queue)
Refresh the number of total and new messages.
Definition imap.c:1127
+ 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 1245 of file imap.c.

1246{
1247 struct ImapAccountData *adata = NULL;
1248 struct ImapMboxData *mdata = NULL;
1249
1250 if (imap_adata_find(path, &adata, &mdata) < 0)
1251 return -1;
1252
1253 if (subscribe)
1254 mutt_message(_("Subscribing to %s..."), mdata->name);
1255 else
1256 mutt_message(_("Unsubscribing from %s..."), mdata->name);
1257
1258 char buf[2048] = { 0 };
1259 snprintf(buf, sizeof(buf), "%sSUBSCRIBE %s", subscribe ? "" : "UN", mdata->munge_name);
1260
1261 if (imap_exec(adata, buf, IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS)
1262 {
1263 imap_mdata_free((void *) &mdata);
1264 return -1;
1265 }
1266
1267 const bool c_imap_check_subscribed = cs_subset_bool(NeoMutt->sub, "imap_check_subscribed");
1268 if (c_imap_check_subscribed)
1269 {
1270 struct Buffer *err = buf_pool_get();
1271 struct Buffer *mbox = buf_pool_get();
1272
1273 size_t len = buf_printf(mbox, "%smailboxes ", subscribe ? "" : "un");
1274 imap_quote_string(mbox->data + len, mbox->dsize - len, path, true);
1275
1276 if (parse_rc_line(mbox, err) != MUTT_CMD_SUCCESS)
1277 mutt_debug(LL_DEBUG1, "Error adding subscribed mailbox: %s\n", buf_string(err));
1278
1279 buf_pool_release(&mbox);
1280 buf_pool_release(&err);
1281 }
1282
1283 if (subscribe)
1284 mutt_message(_("Subscribed to %s"), mdata->name);
1285 else
1286 mutt_message(_("Unsubscribed from %s"), mdata->name);
1287 imap_mdata_free((void *) &mdata);
1288 return 0;
1289}
@ MUTT_CMD_SUCCESS
Success: Command worked.
Definition command.h:38
void imap_mdata_free(void **ptr)
Free the private Mailbox data - Implements Mailbox::mdata_free() -.
Definition mdata.c:40
void imap_quote_string(char *dest, size_t dlen, const char *src, bool quote_backtick)
Quote string according to IMAP rules.
Definition util.c:886
int imap_adata_find(const char *path, struct ImapAccountData **adata, struct ImapMboxData **mdata)
Find the Account data for this path.
Definition util.c:71
enum CommandResult parse_rc_line(struct Buffer *line, struct Buffer *err)
Parse a line of user config.
Definition rc.c:45
size_t dsize
Length of data.
Definition buffer.h:39
+ 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 1301 of file imap.c.

1302{
1303 struct ImapAccountData *adata = NULL;
1304 struct ImapMboxData *mdata = NULL;
1305 char tmp[2048] = { 0 };
1306 struct ImapList listresp = { 0 };
1307 struct Buffer *completion_buf = NULL;
1308 size_t clen;
1309 int completions = 0;
1310 int rc;
1311
1312 if (imap_adata_find(path, &adata, &mdata) < 0)
1313 {
1314 buf_strcpy(buf, path);
1315 return complete_hosts(buf);
1316 }
1317
1318 /* fire off command */
1319 const bool c_imap_list_subscribed = cs_subset_bool(NeoMutt->sub, "imap_list_subscribed");
1320 snprintf(tmp, sizeof(tmp), "%s \"\" \"%s%%\"",
1321 c_imap_list_subscribed ? "LSUB" : "LIST", mdata->real_name);
1322
1323 imap_cmd_start(adata, tmp);
1324
1325 /* and see what the results are */
1326 completion_buf = buf_pool_get();
1327 buf_strcpy(completion_buf, mdata->name);
1328 imap_mdata_free((void *) &mdata);
1329
1330 adata->cmdresult = &listresp;
1331 do
1332 {
1333 listresp.name = NULL;
1334 rc = imap_cmd_step(adata);
1335
1336 if ((rc == IMAP_RES_CONTINUE) && listresp.name)
1337 {
1338 /* if the folder isn't selectable, append delimiter to force browse
1339 * to enter it on second tab. */
1340 if (listresp.noselect)
1341 {
1342 clen = strlen(listresp.name);
1343 listresp.name[clen++] = listresp.delim;
1344 listresp.name[clen] = '\0';
1345 }
1346 /* copy in first word */
1347 if (!completions)
1348 {
1349 buf_strcpy(completion_buf, listresp.name);
1350 completions++;
1351 continue;
1352 }
1353
1354 longest_common_prefix(completion_buf, listresp.name, 0);
1355 completions++;
1356 }
1357 } while (rc == IMAP_RES_CONTINUE);
1358 adata->cmdresult = NULL;
1359
1360 if (completions)
1361 {
1362 /* reformat output */
1363 imap_buf_qualify_path(buf, &adata->conn->account, completion_buf->data);
1364 buf_pretty_mailbox(buf);
1365 buf_fix_dptr(buf);
1366 buf_pool_release(&completion_buf);
1367 return 0;
1368 }
1369
1370 buf_pool_release(&completion_buf);
1371 return -1;
1372}
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:869
static int complete_hosts(struct Buffer *buf)
Look for completion matches for mailboxes.
Definition imap.c:382
void buf_pretty_mailbox(struct Buffer *buf)
Shorten a mailbox path using '~' or '='.
Definition muttlib.c:518
struct ImapList * cmdresult
Definition adata.h:66
Items in an IMAP browser.
Definition private.h:149
bool noselect
Definition private.h:152
char * name
Definition private.h:150
char delim
Definition private.h:151
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 1382 of file imap.c.

1383{
1384 char prompt[1024] = { 0 };
1385 int rc = -1;
1386 bool triedcreate = false;
1387 enum QuadOption err_continue = MUTT_NO;
1388
1390 if (!adata)
1391 return -1;
1392
1393 struct ImapAccountData *dest_adata = NULL;
1394 struct ImapMboxData *dest_mdata = NULL;
1395
1396 if (imap_adata_find(dest, &dest_adata, &dest_mdata) < 0)
1397 return -1;
1398
1399 struct Buffer *sync_cmd = buf_pool_get();
1400
1401 /* check that the save-to folder is in the same account */
1402 if (!imap_account_match(&(adata->conn->account), &(dest_adata->conn->account)))
1403 {
1404 mutt_debug(LL_DEBUG3, "%s not same server as %s\n", dest, mailbox_path(m));
1405 goto out;
1406 }
1407
1408 for (int i = 0; i < m->msg_count; i++)
1409 {
1410 struct Email *e = m->emails[i];
1411 if (!e)
1412 break;
1413 if (e->active && e->changed && e->deleted && !e->purge)
1414 {
1415 rc = imap_sync_message_for_copy(m, e, sync_cmd, &err_continue);
1416 if (rc < 0)
1417 {
1418 mutt_debug(LL_DEBUG1, "could not sync\n");
1419 goto out;
1420 }
1421 }
1422 }
1423
1424 /* loop in case of TRYCREATE */
1425 do
1426 {
1427 struct UidArray uida = ARRAY_HEAD_INITIALIZER;
1428 select_email_uids(m->emails, m->msg_count, MUTT_TRASH, false, false, &uida);
1429 ARRAY_SORT(&uida, imap_sort_uid, NULL);
1430 rc = imap_exec_msg_set(adata, "UID COPY", dest_mdata->munge_name, &uida);
1431 if (rc == 0)
1432 {
1433 mutt_debug(LL_DEBUG1, "No messages to trash\n");
1434 rc = -1;
1435 goto out;
1436 }
1437 else if (rc < 0)
1438 {
1439 mutt_debug(LL_DEBUG1, "could not queue copy\n");
1440 goto out;
1441 }
1442 else if (m->verbose)
1443 {
1444 mutt_message(ngettext("Copying %d message to %s...", "Copying %d messages to %s...", rc),
1445 rc, dest_mdata->name);
1446 }
1447 ARRAY_FREE(&uida);
1448
1449 /* let's get it on */
1450 rc = imap_exec(adata, NULL, IMAP_CMD_NO_FLAGS);
1451 if (rc == IMAP_EXEC_ERROR)
1452 {
1453 if (triedcreate)
1454 {
1455 mutt_debug(LL_DEBUG1, "Already tried to create mailbox %s\n", dest_mdata->name);
1456 break;
1457 }
1458 /* bail out if command failed for reasons other than nonexistent target */
1459 if (!mutt_istr_startswith(imap_get_qualifier(adata->buf), "[TRYCREATE]"))
1460 break;
1461 mutt_debug(LL_DEBUG3, "server suggests TRYCREATE\n");
1462 snprintf(prompt, sizeof(prompt), _("Create %s?"), dest_mdata->name);
1463 const bool c_confirm_create = cs_subset_bool(NeoMutt->sub, "confirm_create");
1464 if (c_confirm_create &&
1465 (query_yesorno_help(prompt, MUTT_YES, NeoMutt->sub, "confirm_create") != MUTT_YES))
1466 {
1468 goto out;
1469 }
1470 if (imap_create_mailbox(adata, dest_mdata->name) < 0)
1471 break;
1472 triedcreate = true;
1473 }
1474 } while (rc == IMAP_EXEC_ERROR);
1475
1476 if (rc != IMAP_EXEC_SUCCESS)
1477 {
1478 imap_error("imap_fast_trash", adata->buf);
1479 goto out;
1480 }
1481
1482 rc = IMAP_EXEC_SUCCESS;
1483
1484out:
1485 buf_pool_release(&sync_cmd);
1486 imap_mdata_free((void *) &dest_mdata);
1487
1488 return ((rc == IMAP_EXEC_SUCCESS) ? 0 : -1);
1489}
#define ARRAY_SORT(head, fn, sdata)
Sort an array.
Definition array.h:335
int imap_sort_uid(const void *a, const void *b, void *sdata)
Compare two UIDs - Implements sort_t -.
Definition msg_set.c:54
bool imap_account_match(const struct ConnAccount *a1, const struct ConnAccount *a2)
Compare two Accounts.
Definition util.c:1095
char * imap_get_qualifier(char *buf)
Get the qualifier from a tagged response.
Definition util.c:807
int imap_create_mailbox(struct ImapAccountData *adata, const char *mailbox)
Create a new mailbox.
Definition imap.c:448
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:946
@ LL_DEBUG3
Log at debug level 3.
Definition logging2.h:46
@ 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:353
bool verbose
Display status messages?
Definition mailbox.h:117
+ 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 1500 of file imap.c.

1501{
1502 if (!m)
1503 return -1;
1504
1505 struct Email **emails = NULL;
1506 int rc;
1507
1509 struct ImapMboxData *mdata = imap_mdata_get(m);
1510 if (!adata || !mdata)
1511 return MX_STATUS_ERROR;
1512
1513 if (adata->state < IMAP_SELECTED)
1514 {
1515 mutt_debug(LL_DEBUG2, "no mailbox selected\n");
1516 return -1;
1517 }
1518
1519 /* This function is only called when the calling code expects the context
1520 * to be changed. */
1522
1523 enum MxStatus check = imap_check_mailbox(m, false);
1524 if (check == MX_STATUS_ERROR)
1525 return check;
1526
1527 /* if we are expunging anyway, we can do deleted messages very quickly... */
1528 if (expunge && (m->rights & MUTT_ACL_DELETE))
1529 {
1530 struct UidArray uida = ARRAY_HEAD_INITIALIZER;
1531 select_email_uids(m->emails, m->msg_count, MUTT_DELETED, true, false, &uida);
1532 ARRAY_SORT(&uida, imap_sort_uid, NULL);
1533 rc = imap_exec_msg_set(adata, "UID STORE", "+FLAGS.SILENT (\\Deleted)", &uida);
1534 ARRAY_FREE(&uida);
1535 if (rc < 0)
1536 {
1537 mutt_error(_("Expunge failed"));
1538 return rc;
1539 }
1540
1541 if (rc > 0)
1542 {
1543 /* mark these messages as unchanged so second pass ignores them. Done
1544 * here so BOGUS UW-IMAP 4.7 SILENT FLAGS updates are ignored. */
1545 for (int i = 0; i < m->msg_count; i++)
1546 {
1547 struct Email *e = m->emails[i];
1548 if (!e)
1549 break;
1550 if (e->deleted && e->changed)
1551 e->active = false;
1552 }
1553 if (m->verbose)
1554 {
1555 mutt_message(ngettext("Marking %d message deleted...",
1556 "Marking %d messages deleted...", rc),
1557 rc);
1558 }
1559 }
1560 }
1561
1562#ifdef USE_HCACHE
1563 imap_hcache_open(adata, mdata, true);
1564#endif
1565
1566 /* save messages with real (non-flag) changes */
1567 for (int i = 0; i < m->msg_count; i++)
1568 {
1569 struct Email *e = m->emails[i];
1570 if (!e)
1571 break;
1572
1573 if (e->deleted)
1574 {
1575 imap_cache_del(m, e);
1576#ifdef USE_HCACHE
1577 imap_hcache_del(mdata, imap_edata_get(e)->uid);
1578#endif
1579 }
1580
1581 if (e->active && e->changed)
1582 {
1583#ifdef USE_HCACHE
1584 imap_hcache_put(mdata, e);
1585#endif
1586 /* if the message has been rethreaded or attachments have been deleted
1587 * we delete the message and reupload it.
1588 * This works better if we're expunging, of course. */
1589 if (e->env->changed || e->attach_del)
1590 {
1591 /* L10N: The plural is chosen by the last %d, i.e. the total number */
1592 if (m->verbose)
1593 {
1594 mutt_message(ngettext("Saving changed message... [%d/%d]",
1595 "Saving changed messages... [%d/%d]", m->msg_count),
1596 i + 1, m->msg_count);
1597 }
1598 bool save_append = m->append;
1599 m->append = true;
1601 m->append = save_append;
1602 e->env->changed = false;
1603 }
1604 }
1605 }
1606
1607#ifdef USE_HCACHE
1608 imap_hcache_close(mdata);
1609#endif
1610
1611 /* presort here to avoid doing 10 resorts in imap_exec_msg_set */
1612 emails = MUTT_MEM_MALLOC(m->msg_count, struct Email *);
1613 memcpy(emails, m->emails, m->msg_count * sizeof(struct Email *));
1614 mutt_qsort_r(emails, m->msg_count, sizeof(struct Email *), imap_sort_email_uid, NULL);
1615
1616 rc = sync_helper(m, emails, m->msg_count, MUTT_ACL_DELETE, MUTT_DELETED, "\\Deleted");
1617 if (rc >= 0)
1618 rc |= sync_helper(m, emails, m->msg_count, MUTT_ACL_WRITE, MUTT_FLAG, "\\Flagged");
1619 if (rc >= 0)
1620 rc |= sync_helper(m, emails, m->msg_count, MUTT_ACL_WRITE, MUTT_OLD, "Old");
1621 if (rc >= 0)
1622 rc |= sync_helper(m, emails, m->msg_count, MUTT_ACL_SEEN, MUTT_READ, "\\Seen");
1623 if (rc >= 0)
1624 rc |= sync_helper(m, emails, m->msg_count, MUTT_ACL_WRITE, MUTT_REPLIED, "\\Answered");
1625
1626 FREE(&emails);
1627
1628 /* Flush the queued flags if any were changed in sync_helper. */
1629 if (rc > 0)
1630 if (imap_exec(adata, NULL, IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS)
1631 rc = -1;
1632
1633 if (rc < 0)
1634 {
1635 if (close)
1636 {
1637 if (query_yesorno(_("Error saving flags. Close anyway?"), MUTT_NO) == MUTT_YES)
1638 {
1639 adata->state = IMAP_AUTHENTICATED;
1640 return 0;
1641 }
1642 }
1643 else
1644 {
1645 mutt_error(_("Error saving flags"));
1646 }
1647 return -1;
1648 }
1649
1650 /* Update local record of server state to reflect the synchronization just
1651 * completed. imap_read_headers always overwrites hcache-origin flags, so
1652 * there is no need to mutate the hcache after flag-only changes. */
1653 for (int i = 0; i < m->msg_count; i++)
1654 {
1655 struct Email *e = m->emails[i];
1656 if (!e)
1657 break;
1658 struct ImapEmailData *edata = imap_edata_get(e);
1659 edata->deleted = e->deleted;
1660 edata->flagged = e->flagged;
1661 edata->old = e->old;
1662 edata->read = e->read;
1663 edata->replied = e->replied;
1664 e->changed = false;
1665 }
1666 m->changed = false;
1667
1668 /* We must send an EXPUNGE command if we're not closing. */
1669 if (expunge && !close && (m->rights & MUTT_ACL_DELETE))
1670 {
1671 if (m->verbose)
1672 mutt_message(_("Expunging messages from server..."));
1673 /* Set expunge bit so we don't get spurious reopened messages */
1674 mdata->reopen |= IMAP_EXPUNGE_EXPECTED;
1675 if (imap_exec(adata, "EXPUNGE", IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS)
1676 {
1678 imap_error(_("imap_sync_mailbox: EXPUNGE failed"), adata->buf);
1679 return -1;
1680 }
1682 }
1683
1684 if (expunge && close)
1685 {
1686 adata->closing = true;
1687 imap_exec(adata, "CLOSE", IMAP_CMD_NO_FLAGS);
1688 adata->state = IMAP_AUTHENTICATED;
1689 }
1690
1691 const bool c_message_cache_clean = cs_subset_bool(NeoMutt->sub, "message_cache_clean");
1692 if (c_message_cache_clean)
1694
1695 return check;
1696}
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:920
int imap_cache_clean(struct Mailbox *m)
Delete all the entries in the message cache.
Definition message.c:1907
void imap_allow_reopen(struct Mailbox *m)
Allow re-opening a folder upon expunge.
Definition util.c:1067
@ IMAP_SELECTED
Mailbox is selected.
Definition private.h:108
#define IMAP_EXPUNGE_EXPECTED
Messages will be expunged from the server.
Definition private.h:65
int imap_hcache_put(struct ImapMboxData *mdata, struct Email *e)
Add an entry to the header cache.
Definition util.c:383
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:310
enum MxStatus imap_check_mailbox(struct Mailbox *m, bool force)
Use the NOOP or IDLE command to poll for new mail.
Definition imap.c:1052
#define MUTT_MEM_MALLOC(n, type)
Definition memory.h:48
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:67
enum QuadOption query_yesorno(const char *prompt, enum QuadOption def)
Ask the user a Yes/No question.
Definition question.c:325
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:110
bool append
Mailbox is opened in append mode.
Definition mailbox.h:109
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_mbox_select()

static void imap_mbox_select ( struct Mailbox * m)
static

Select a Mailbox.

Parameters
mMailbox

Definition at line 1775 of file imap.c.

1776{
1778 struct ImapMboxData *mdata = imap_mdata_get(m);
1779 if (!adata || !mdata)
1780 return;
1781
1782 const char *condstore = NULL;
1783#ifdef USE_HCACHE
1784 const bool c_imap_condstore = cs_subset_bool(NeoMutt->sub, "imap_condstore");
1785 if ((adata->capabilities & IMAP_CAP_CONDSTORE) && c_imap_condstore)
1786 condstore = " (CONDSTORE)";
1787 else
1788#endif
1789 condstore = "";
1790
1791 char buf[PATH_MAX] = { 0 };
1792 snprintf(buf, sizeof(buf), "%s %s%s", m->readonly ? "EXAMINE" : "SELECT",
1793 mdata->munge_name, condstore);
1794
1795 adata->state = IMAP_SELECTED;
1796
1797 imap_cmd_start(adata, buf);
1798}
#define IMAP_CAP_CONDSTORE
RFC7162.
Definition private.h:136
bool readonly
Don't allow changes to the mailbox.
Definition mailbox.h:116
+ 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 1808 of file imap.c.

1809{
1810 if (!adata)
1811 return -1;
1812
1813 if (adata->state == IMAP_DISCONNECTED)
1814 {
1815 buf_reset(&adata->cmdbuf); // purge outstanding queued commands
1816 imap_open_connection(adata);
1817 }
1818 if (adata->state == IMAP_CONNECTED)
1819 {
1821 {
1822 adata->state = IMAP_AUTHENTICATED;
1823 FREE(&adata->capstr);
1824 if (adata->conn->ssf != 0)
1825 {
1826 mutt_debug(LL_DEBUG2, "Communication encrypted at %d bits\n",
1827 adata->conn->ssf);
1828 }
1829 }
1830 else
1831 {
1833 }
1834 }
1835 if (adata->state == IMAP_AUTHENTICATED)
1836 {
1837 /* capabilities may have changed */
1838 imap_exec(adata, "CAPABILITY", IMAP_CMD_PASS);
1839
1840#ifdef USE_ZLIB
1841 /* RFC4978 */
1842 const bool c_imap_deflate = cs_subset_bool(NeoMutt->sub, "imap_deflate");
1843 if ((adata->capabilities & IMAP_CAP_COMPRESS) && c_imap_deflate &&
1844 (imap_exec(adata, "COMPRESS DEFLATE", IMAP_CMD_PASS) == IMAP_EXEC_SUCCESS))
1845 {
1846 mutt_debug(LL_DEBUG2, "IMAP compression is enabled on connection to %s\n",
1847 adata->conn->account.host);
1848 mutt_zstrm_wrap_conn(adata->conn);
1849 }
1850#endif
1851
1852 /* enable RFC2971, if the server supports that */
1853 const bool c_imap_send_id = cs_subset_bool(NeoMutt->sub, "imap_send_id");
1854 if (c_imap_send_id && (adata->capabilities & IMAP_CAP_ID))
1855 {
1856 imap_exec(adata, "ID (\"name\" \"NeoMutt\" \"version\" \"" PACKAGE_VERSION "\")",
1858 }
1859
1860 /* enable RFC6855, if the server supports that */
1861 const bool c_imap_rfc5161 = cs_subset_bool(NeoMutt->sub, "imap_rfc5161");
1862 if (c_imap_rfc5161 && (adata->capabilities & IMAP_CAP_ENABLE))
1863 imap_exec(adata, "ENABLE UTF8=ACCEPT", IMAP_CMD_QUEUE);
1864
1865 /* enable QRESYNC. Advertising QRESYNC also means CONDSTORE
1866 * is supported (even if not advertised), so flip that bit. */
1867 if (adata->capabilities & IMAP_CAP_QRESYNC)
1868 {
1870 const bool c_imap_qresync = cs_subset_bool(NeoMutt->sub, "imap_qresync");
1871 if (c_imap_rfc5161 && c_imap_qresync)
1872 imap_exec(adata, "ENABLE QRESYNC", IMAP_CMD_QUEUE);
1873 }
1874
1875 /* get root delimiter, '/' as default */
1876 adata->delim = '/';
1877 imap_exec(adata, "LIST \"\" \"\"", IMAP_CMD_QUEUE);
1878
1879 /* we may need the root delimiter before we open a mailbox */
1880 imap_exec(adata, NULL, IMAP_CMD_NO_FLAGS);
1881
1882 /* select the mailbox that used to be open before disconnect */
1883 if (adata->mailbox)
1884 {
1885 imap_mbox_select(adata->mailbox);
1886 }
1887 }
1888
1889 if (adata->state < IMAP_AUTHENTICATED)
1890 return -1;
1891
1892 return 0;
1893}
@ 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:115
#define IMAP_CAP_ENABLE
RFC5161.
Definition private.h:135
#define IMAP_CAP_ID
RFC2971: IMAP4 ID extension.
Definition private.h:141
#define IMAP_CMD_PASS
Command contains a password. Suppress logging.
Definition private.h:72
#define IMAP_CAP_QRESYNC
RFC7162.
Definition private.h:137
#define IMAP_CAP_COMPRESS
RFC4978: COMPRESS=DEFLATE.
Definition private.h:139
int imap_open_connection(struct ImapAccountData *adata)
Open an IMAP connection.
Definition imap.c:761
static void imap_mbox_select(struct Mailbox *m)
Select a Mailbox.
Definition imap.c:1775
char delim
Path delimiter.
Definition adata.h:75
struct Buffer cmdbuf
Definition adata.h:73
void mutt_zstrm_wrap_conn(struct Connection *conn)
Wrap a compression layer around a Connection.
Definition zstrm.c:291
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

Variable Documentation

◆ ImapCommands

const struct Command ImapCommands[]
static
Initial value:
= {
{ "subscribe-to", parse_subscribe_to, 0,
N_("Subscribe to an IMAP mailbox"),
N_("subscribe-to <imap-folder-uri>"),
"optionalfeatures.html#imap" },
{ "unsubscribe-from", parse_unsubscribe_from, 0,
N_("Unsubscribe from an IMAP mailbox"),
N_("unsubscribe-from <imap-folder-uri>"),
"optionalfeatures.html#imap" },
{ NULL, NULL, 0, NULL, NULL, NULL, CF_NO_FLAGS },
}
#define CF_NO_FLAGS
No flags are set.
Definition command.h:46
enum CommandResult parse_unsubscribe_from(const struct Command *cmd, struct Buffer *line, struct Buffer *err)
Parse the 'unsubscribe-from' command - Implements Command::parse() -.
Definition commands.c:1844
enum CommandResult parse_subscribe_to(const struct Command *cmd, struct Buffer *line, struct Buffer *err)
Parse the 'subscribe-to' command - Implements Command::parse() -.
Definition commands.c:1414
#define N_(a)
Definition message.h:32

Imap Commands.

Definition at line 84 of file imap.c.

84 {
85 // clang-format off
86 { "subscribe-to", parse_subscribe_to, 0,
87 N_("Subscribe to an IMAP mailbox"),
88 N_("subscribe-to <imap-folder-uri>"),
89 "optionalfeatures.html#imap" },
90 { "unsubscribe-from", parse_unsubscribe_from, 0,
91 N_("Unsubscribe from an IMAP mailbox"),
92 N_("unsubscribe-from <imap-folder-uri>"),
93 "optionalfeatures.html#imap" },
94
95 { NULL, NULL, 0, NULL, NULL, NULL, CF_NO_FLAGS },
96 // clang-format on
97};