NeoMutt
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
imap.c File Reference

IMAP network mailbox. More...

#include "config.h"
#include <ctype.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 "sort.h"
#include <libintl.h>
+ Include dependency graph for imap.c:

Go to the source code of this file.

Functions

void imap_init (void)
 Setup feature commands.
 
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, char *flags, size_t flsize)
 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 (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? - Implements MxOps::path_probe() -.
 
int imap_path_canon (struct Buffer *path)
 Canonicalise a Mailbox path - Implements MxOps::path_canon() -.
 
int imap_expand_path (struct Buffer *path)
 Buffer wrapper around imap_path_canon()
 
static int imap_path_pretty (struct Buffer *path, const char *folder)
 Abbreviate a Mailbox path - Implements MxOps::path_pretty() -.
 
static int imap_path_parent (struct Buffer *path)
 Find the parent of a Mailbox path - Implements MxOps::path_parent() -.
 
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

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

89{
91}
void commands_register(const struct Command *cmds, const size_t num_cmds)
Add commands to Commands array.
Definition: command.c:53
static const struct Command ImapCommands[]
Imap Commands.
Definition: imap.c:78
#define mutt_array_size(x)
Definition: memory.h:38
+ 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 99 of file imap.c.

100{
101 if (imap_exec(adata, "CAPABILITY", IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS)
102 {
103 imap_error("check_capabilities", adata->buf);
104 return -1;
105 }
106
107 if (!((adata->capabilities & IMAP_CAP_IMAP4) || (adata->capabilities & IMAP_CAP_IMAP4REV1)))
108 {
109 mutt_error(_("This IMAP server is ancient. NeoMutt does not work with it."));
110 return -1;
111 }
112
113 return 0;
114}
#define mutt_error(...)
Definition: logging2.h:92
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:1304
#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:654
#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 125 of file imap.c.

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

◆ set_flag()

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

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

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

Definition at line 180 of file imap.c.

182{
183 if (m->rights & aclflag)
184 if (flag && imap_has_flag(&imap_mdata_get(m)->flags, str))
185 mutt_str_cat(flags, flsize, str);
186}
struct ImapMboxData * imap_mdata_get(struct Mailbox *m)
Get the Mailbox data for this mailbox.
Definition: mdata.c:60
bool imap_has_flag(struct ListHead *flag_list, const char *flag)
Does the flag exist in the list.
Definition: imap.c:866
char * mutt_str_cat(char *buf, size_t buflen, const char *s)
Concatenate two strings.
Definition: string.c:266
AclFlags rights
ACL bits, see AclFlags.
Definition: mailbox.h:118
+ 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 196 of file imap.c.

197{
198 struct ImapEmailData *edata = e->edata;
199
200 if (e->read != edata->read)
201 return true;
202 if (e->old != edata->old)
203 return true;
204 if (e->flagged != edata->flagged)
205 return true;
206 if (e->replied != edata->replied)
207 return true;
208
209 return false;
210}
bool read
Email is read.
Definition: email.h:48
void * edata
Driver-specific data.
Definition: email.h:72
bool old
Email is seen, but unread.
Definition: email.h:47
bool flagged
Marked important?
Definition: email.h:45
bool replied
Email has been replied to.
Definition: email.h:49
IMAP-specific Email data -.
Definition: edata.h:34
+ 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 223 of file imap.c.

225{
226 if (!emails || !uida)
227 return -1;
228
229 for (int i = 0; i < num_emails; i++)
230 {
231 struct Email *e = emails[i];
232 if (changed && !e->changed)
233 continue;
234
235 /* don't include pending expunged messages.
236 *
237 * TODO: can we unset active in cmd_parse_expunge() and
238 * cmd_parse_vanished() instead of checking for index != INT_MAX. */
239 if (!e || !e->active || (e->index == INT_MAX))
240 continue;
241
243
244 bool match = false;
245 switch (flag)
246 {
247 case MUTT_DELETED:
248 if (e->deleted != edata->deleted)
249 match = invert ^ e->deleted;
250 break;
251 case MUTT_FLAG:
252 if (e->flagged != edata->flagged)
253 match = invert ^ e->flagged;
254 break;
255 case MUTT_OLD:
256 if (e->old != edata->old)
257 match = invert ^ e->old;
258 break;
259 case MUTT_READ:
260 if (e->read != edata->read)
261 match = invert ^ e->read;
262 break;
263 case MUTT_REPLIED:
264 if (e->replied != edata->replied)
265 match = invert ^ e->replied;
266 break;
267 case MUTT_TRASH:
268 if (e->deleted && !e->purge)
269 match = true;
270 break;
271 default:
272 break;
273 }
274
275 if (match)
276 ARRAY_ADD(uida, edata->uid);
277 }
278
279 return ARRAY_SIZE(uida);
280}
#define ARRAY_ADD(head, elem)
Add an element at the end of the array.
Definition: array.h:155
#define ARRAY_SIZE(head)
The number of elements stored.
Definition: array.h:86
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:84
@ MUTT_READ
Messages that have been read.
Definition: mutt.h:72
@ MUTT_OLD
Old messages.
Definition: mutt.h:70
@ MUTT_FLAG
Flagged messages.
Definition: mutt.h:78
@ MUTT_DELETED
Deleted messages.
Definition: mutt.h:77
@ MUTT_REPLIED
Messages that have been replied to.
Definition: mutt.h:71
The envelope/body of an email.
Definition: email.h:37
bool purge
Skip trash folder when deleting.
Definition: email.h:77
bool active
Message is not to be removed.
Definition: email.h:74
bool changed
Email has been edited.
Definition: email.h:75
bool deleted
Email is deleted.
Definition: email.h:76
int index
The absolute (unsorted) message number.
Definition: email.h:109
+ 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 293 of file imap.c.

295{
297 if (!adata)
298 return -1;
299
300 if ((m->rights & right) == 0)
301 return 0;
302
303 if ((right == MUTT_ACL_WRITE) && !imap_has_flag(&imap_mdata_get(m)->flags, name))
304 return 0;
305
306 int count = 0;
307 char buf[1024] = { 0 };
308
309 struct UidArray uida = ARRAY_HEAD_INITIALIZER;
310
311 // Set the flag (+FLAGS) on matching emails
312 select_email_uids(emails, num_emails, flag, true, false, &uida);
313 snprintf(buf, sizeof(buf), "+FLAGS.SILENT (%s)", name);
314 int rc = imap_exec_msg_set(adata, "UID STORE", buf, &uida);
315 if (rc < 0)
316 return rc;
317 count += rc;
318 ARRAY_FREE(&uida);
319
320 // Clear the flag (-FLAGS) on non-matching emails
321 select_email_uids(emails, num_emails, flag, true, true, &uida);
322 buf[0] = '-';
323 rc = imap_exec_msg_set(adata, "UID STORE", buf, &uida);
324 if (rc < 0)
325 return rc;
326 count += rc;
327 ARRAY_FREE(&uida);
328
329 return count;
330}
#define ARRAY_FREE(head)
Release all memory.
Definition: array.h:203
#define ARRAY_HEAD_INITIALIZER
Static initializer for arrays.
Definition: array.h:57
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:223
#define MUTT_ACL_WRITE
Write to a message (for flagging or linking threads)
Definition: mailbox.h:71
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
char * name
Name of Account.
Definition: account.h:39
void * adata
Private data (for Mailbox backends)
Definition: account.h:43
IMAP-specific Account data -.
Definition: adata.h:40
+ 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 341 of file imap.c.

342{
343 size_t pos = start;
344
345 size_t len = buf_len(buf);
346 while ((pos < len) && buf->data[pos] && (buf->data[pos] == src[pos]))
347 pos++;
348 buf->data[pos] = '\0';
349
350 buf_fix_dptr(buf);
351
352 return pos;
353}
size_t buf_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition: buffer.c:466
void buf_fix_dptr(struct Buffer *buf)
Move the dptr to end of the Buffer.
Definition: buffer.c:194
char * data
Pointer to data.
Definition: buffer.h:35
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

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

365{
366 int rc = -1;
367 size_t matchlen;
368
369 matchlen = buf_len(buf);
370 struct MailboxList ml = STAILQ_HEAD_INITIALIZER(ml);
372 struct MailboxNode *np = NULL;
373 STAILQ_FOREACH(np, &ml, entries)
374 {
376 continue;
377
378 if (rc)
379 {
381 rc = 0;
382 }
383 else
384 {
385 longest_common_prefix(buf, mailbox_path(np->mailbox), matchlen);
386 }
387 }
389
390#if 0
391 TAILQ_FOREACH(conn, mutt_socket_head(), entries)
392 {
393 struct Url url = { 0 };
394 char urlstr[1024] = { 0 };
395
396 if (conn->account.type != MUTT_ACCT_TYPE_IMAP)
397 continue;
398
399 mutt_account_tourl(&conn->account, &url);
400 /* FIXME: how to handle multiple users on the same host? */
401 url.user = NULL;
402 url.path = NULL;
403 url_tostring(&url, urlstr, sizeof(urlstr), U_NO_FLAGS);
404 if (mutt_strn_equal(buf, urlstr, matchlen))
405 {
406 if (rc)
407 {
408 mutt_str_copy(buf, urlstr, buflen);
409 rc = 0;
410 }
411 else
412 {
413 longest_common_prefix(buf, urlstr, matchlen);
414 }
415 }
416 }
417#endif
418
419 return rc;
420}
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:407
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:93
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:341
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox's path string.
Definition: mailbox.h:210
@ MUTT_MAILBOX_ANY
Match any Mailbox type.
Definition: mailbox.h:42
bool mutt_strn_equal(const char *a, const char *b, size_t num)
Check for equality of two strings (to a maximum), safely.
Definition: string.c:497
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:228
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:653
void mutt_account_tourl(struct ConnAccount *cac, struct Url *url)
Fill URL with info from account.
Definition: mutt_account.c:79
@ MUTT_ACCT_TYPE_IMAP
Imap Account.
Definition: mutt_account.h:37
void neomutt_mailboxlist_clear(struct MailboxList *ml)
Free a Mailbox List.
Definition: neomutt.c:162
size_t neomutt_mailboxlist_get_all(struct MailboxList *head, struct NeoMutt *n, enum MailboxType type)
Get a List of all Mailboxes.
Definition: neomutt.c:185
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:725
#define STAILQ_HEAD_INITIALIZER(head)
Definition: queue.h:324
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:352
List of Mailboxes.
Definition: mailbox.h:153
struct Mailbox * mailbox
Mailbox in the list.
Definition: mailbox.h:154
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(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 429 of file imap.c.

430{
431 char buf[2048], mbox[1024];
432
433 imap_munge_mbox_name(adata->unicode, mbox, sizeof(mbox), mailbox);
434 snprintf(buf, sizeof(buf), "CREATE %s", mbox);
435
437 {
438 mutt_error(_("CREATE failed: %s"), imap_cmd_trailer(adata));
439 return -1;
440 }
441
442 return 0;
443}
const char * imap_cmd_trailer(struct ImapAccountData *adata)
Extra information after tagged command response if any.
Definition: command.c:1267
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:918
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 455 of file imap.c.

456{
457 if (imap_path_status(path, false) >= 0)
458 return 0;
459 return -1;
460}
int imap_path_status(const char *path, bool queue)
Refresh the number of total and new messages.
Definition: imap.c:1157
+ 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 470 of file imap.c.

471{
472 char oldmbox[1024] = { 0 };
473 char newmbox[1024] = { 0 };
474 int rc = 0;
475
476 imap_munge_mbox_name(adata->unicode, oldmbox, sizeof(oldmbox), oldname);
477 imap_munge_mbox_name(adata->unicode, newmbox, sizeof(newmbox), newname);
478
479 struct Buffer *buf = buf_pool_get();
480 buf_printf(buf, "RENAME %s %s", oldmbox, newmbox);
481
483 rc = -1;
484
485 buf_pool_release(&buf);
486
487 return rc;
488}
int buf_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:173
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:81
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition: pool.c:94
String manipulation buffer.
Definition: buffer.h:34
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_delete_mailbox()

int imap_delete_mailbox ( struct Mailbox m,
char *  path 
)

Delete a mailbox.

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

Definition at line 497 of file imap.c.

498{
499 char buf[PATH_MAX + 7];
500 char mbox[PATH_MAX] = { 0 };
501 struct Url *url = url_parse(path);
502 if (!url)
503 return -1;
504
506 imap_munge_mbox_name(adata->unicode, mbox, sizeof(mbox), url->path);
507 url_free(&url);
508 snprintf(buf, sizeof(buf), "DELETE %s", mbox);
510 return -1;
511
512 return 0;
513}
#define PATH_MAX
Definition: mutt.h:41
struct Account * account
Account that owns this Mailbox.
Definition: mailbox.h:128
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 519 of file imap.c.

520{
521 /* we set status here to let imap_handle_untagged know we _expect_ to
522 * receive a bye response (so it doesn't freak out and close the conn) */
523 if (adata->state == IMAP_DISCONNECTED)
524 {
525 return;
526 }
527
528 adata->status = IMAP_BYE;
529 imap_cmd_start(adata, "LOGOUT");
530 const short c_imap_poll_timeout = cs_subset_number(NeoMutt->sub, "imap_poll_timeout");
531 if ((c_imap_poll_timeout <= 0) ||
532 (mutt_socket_poll(adata->conn, c_imap_poll_timeout) != 0))
533 {
534 while (imap_cmd_step(adata) == IMAP_RES_CONTINUE)
535 ; // do nothing
536 }
537 mutt_socket_close(adata->conn);
538 adata->state = IMAP_DISCONNECTED;
539}
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition: helpers.c:144
int imap_cmd_start(struct ImapAccountData *adata, const char *cmdstr)
Given an IMAP command, send it to the server.
Definition: command.c:1115
int imap_cmd_step(struct ImapAccountData *adata)
Reads server responses from an IMAP command.
Definition: command.c:1129
@ IMAP_DISCONNECTED
Disconnected from server.
Definition: private.h:105
@ IMAP_BYE
Logged out from server.
Definition: private.h:96
#define IMAP_RES_CONTINUE
* ...
Definition: private.h:56
int mutt_socket_close(struct Connection *conn)
Close a socket.
Definition: socket.c:101
int mutt_socket_poll(struct Connection *conn, time_t wait_secs)
Checks whether reads would block.
Definition: socket.c:196
unsigned char state
ImapState, e.g. IMAP_AUTHENTICATED.
Definition: adata.h:44
unsigned char status
ImapFlags, e.g. IMAP_FATAL.
Definition: adata.h:45
struct Connection * conn
Connection to IMAP server.
Definition: adata.h:41
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:45
+ 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 546 of file imap.c.

547{
548 struct Account *np = NULL;
549 TAILQ_FOREACH(np, &NeoMutt->accounts, entries)
550 {
551 if (np->type != MUTT_IMAP)
552 continue;
553
554 struct ImapAccountData *adata = np->adata;
555 if (!adata)
556 continue;
557
558 struct Connection *conn = adata->conn;
559 if (!conn || (conn->fd < 0))
560 continue;
561
562 mutt_message(_("Closing connection to %s..."), conn->account.host);
563 imap_logout(np->adata);
565 }
566}
#define mutt_message(...)
Definition: logging2.h:91
static void imap_logout(struct ImapAccountData *adata)
Gracefully log out of server.
Definition: imap.c:519
@ MUTT_IMAP
'IMAP' Mailbox type
Definition: mailbox.h:50
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
Definition: mutt_logging.c:73
A group of associated Mailboxes.
Definition: account.h:37
enum MailboxType type
Type of Mailboxes this Account contains.
Definition: account.h:38
char host[128]
Server to login to.
Definition: connaccount.h:54
struct ConnAccount account
Account details: username, password, etc.
Definition: connection.h:50
int fd
Socket file descriptor.
Definition: connection.h:54
struct AccountList accounts
List of all Accounts.
Definition: neomutt.h:46
+ 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 582 of file imap.c.

584{
585 char c;
586 bool r = false;
587 struct Buffer buf = { 0 }; // Do not allocate, maybe it won't be used
588
589 const short c_debug_level = cs_subset_number(NeoMutt->sub, "debug_level");
590 if (c_debug_level >= IMAP_LOG_LTRL)
591 buf_alloc(&buf, bytes + 1);
592
593 mutt_debug(LL_DEBUG2, "reading %lu bytes\n", bytes);
594
595 for (unsigned long pos = 0; pos < bytes; pos++)
596 {
597 if (mutt_socket_readchar(adata->conn, &c) != 1)
598 {
599 mutt_debug(LL_DEBUG1, "error during read, %lu bytes read\n", pos);
600 adata->status = IMAP_FATAL;
601
602 buf_dealloc(&buf);
603 return -1;
604 }
605
606 if (r && (c != '\n'))
607 fputc('\r', fp);
608
609 if (c == '\r')
610 {
611 r = true;
612 continue;
613 }
614 else
615 {
616 r = false;
617 }
618
619 fputc(c, fp);
620
621 if ((pos % 1024) == 0)
622 progress_update(progress, pos, -1);
623 if (c_debug_level >= IMAP_LOG_LTRL)
624 buf_addch(&buf, c);
625 }
626
627 if (c_debug_level >= IMAP_LOG_LTRL)
628 {
629 mutt_debug(IMAP_LOG_LTRL, "\n%s", buf.data);
630 buf_dealloc(&buf);
631 }
632 return 0;
633}
void buf_dealloc(struct Buffer *buf)
Release the memory allocated by a buffer.
Definition: buffer.c:389
size_t buf_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:253
void buf_alloc(struct Buffer *buf, size_t new_size)
Make sure a buffer can store at least new_size bytes.
Definition: buffer.c:349
#define IMAP_LOG_LTRL
Definition: private.h:49
@ IMAP_FATAL
Unrecoverable error occurred.
Definition: private.h:95
@ LL_DEBUG2
Log at debug level 2.
Definition: logging2.h:44
bool progress_update(struct Progress *progress, size_t pos, int percent)
Update the state of the progress bar.
Definition: progress.c:73
int mutt_socket_readchar(struct Connection *conn, char *c)
Simple read buffering to speed things up.
Definition: socket.c:214
+ 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 640 of file imap.c.

641{
642 struct ImapMboxData *mdata = imap_mdata_get(m);
644
645 if (!mdata || !edata)
646 return;
647
648 imap_msn_remove(&mdata->msn, edata->msn - 1);
649 edata->msn = 0;
650}
void imap_msn_remove(struct MSNArray *msn, size_t idx)
Remove an entry from the cache.
Definition: msn.c:113
IMAP-specific Mailbox data -.
Definition: mdata.h:40
void * mdata
Driver specific data.
Definition: mailbox.h:133
+ 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 661 of file imap.c.

662{
664 struct ImapMboxData *mdata = imap_mdata_get(m);
665 if (!adata || !mdata)
666 return;
667
668 struct Email *e = NULL;
669
670#ifdef USE_HCACHE
671 imap_hcache_open(adata, mdata);
672#endif
673
674 for (int i = 0; i < m->msg_count; i++)
675 {
676 e = m->emails[i];
677 if (!e)
678 break;
679
680 if (e->index == INT_MAX)
681 {
682 mutt_debug(LL_DEBUG2, "Expunging message UID %u\n", imap_edata_get(e)->uid);
683
684 e->deleted = true;
685
686 imap_cache_del(m, e);
687#ifdef USE_HCACHE
688 imap_hcache_del(mdata, imap_edata_get(e)->uid);
689#endif
690
691 mutt_hash_int_delete(mdata->uid_hash, imap_edata_get(e)->uid, e);
692
693 imap_edata_free((void **) &e->edata);
694 }
695 else
696 {
697 /* NeoMutt has several places where it turns off e->active as a
698 * hack. For example to avoid FLAG updates, or to exclude from
699 * imap_exec_msg_set.
700 *
701 * Unfortunately, when a reopen is allowed and the IMAP_EXPUNGE_PENDING
702 * flag becomes set (e.g. a flag update to a modified header),
703 * this function will be called by imap_cmd_finish().
704 *
705 * The ctx_update_tables() will free and remove these "inactive" headers,
706 * despite that an EXPUNGE was not received for them.
707 * This would result in memory leaks and segfaults due to dangling
708 * pointers in the msn_index and uid_hash.
709 *
710 * So this is another hack to work around the hacks. We don't want to
711 * remove the messages, so make sure active is on. */
712 e->active = true;
713 }
714 }
715
716#ifdef USE_HCACHE
717 imap_hcache_close(mdata);
718#endif
719
721 if (resort)
722 {
724 }
725}
void mutt_hash_int_delete(struct HashTable *table, unsigned int intkey, const void *data)
Remove an element from a Hash Table.
Definition: hash.c:444
void imap_edata_free(void **ptr)
Free the private Email data - Implements Email::edata_free()
Definition: edata.c:39
int imap_cache_del(struct Mailbox *m, struct Email *e)
Delete an email from the body cache.
Definition: message.c:1848
void imap_hcache_close(struct ImapMboxData *mdata)
Close the header cache.
Definition: util.c:337
int imap_hcache_del(struct ImapMboxData *mdata, unsigned int uid)
Delete an item from the header cache.
Definition: util.c:395
void imap_hcache_open(struct ImapAccountData *adata, struct ImapMboxData *mdata)
Open a header cache.
Definition: util.c:296
void mailbox_changed(struct Mailbox *m, enum NotifyMailbox action)
Notify observers of a change to a Mailbox.
Definition: mailbox.c:226
@ NT_MAILBOX_RESORT
Email list needs resorting.
Definition: mailbox.h:177
@ NT_MAILBOX_UPDATE
Update internal tables.
Definition: mailbox.h:178
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 733 of file imap.c.

734{
735 if (mutt_socket_open(adata->conn) < 0)
736 return -1;
737
738 adata->state = IMAP_CONNECTED;
739
740 if (imap_cmd_step(adata) != IMAP_RES_OK)
741 {
743 return -1;
744 }
745
746 if (mutt_istr_startswith(adata->buf, "* OK"))
747 {
748 if (!mutt_istr_startswith(adata->buf, "* OK [CAPABILITY") && check_capabilities(adata))
749 {
750 goto bail;
751 }
752#ifdef USE_SSL
753 /* Attempt STARTTLS if available and desired. */
754 const bool c_ssl_force_tls = cs_subset_bool(NeoMutt->sub, "ssl_force_tls");
755 if ((adata->conn->ssf == 0) &&
756 (c_ssl_force_tls || (adata->capabilities & IMAP_CAP_STARTTLS)))
757 {
758 enum QuadOption ans;
759
760 if (c_ssl_force_tls)
761 {
762 ans = MUTT_YES;
763 }
764 else if ((ans = query_quadoption(_("Secure connection with TLS?"),
765 NeoMutt->sub, "ssl_starttls")) == MUTT_ABORT)
766 {
767 goto bail;
768 }
769 if (ans == MUTT_YES)
770 {
771 enum ImapExecResult rc = imap_exec(adata, "STARTTLS", IMAP_CMD_SINGLE);
772 // Clear any data after the STARTTLS acknowledgement
773 mutt_socket_empty(adata->conn);
774
775 if (rc == IMAP_EXEC_FATAL)
776 goto bail;
777 if (rc != IMAP_EXEC_ERROR)
778 {
779 if (mutt_ssl_starttls(adata->conn))
780 {
781 mutt_error(_("Could not negotiate TLS connection"));
782 goto bail;
783 }
784 else
785 {
786 /* RFC2595 demands we recheck CAPABILITY after TLS completes. */
787 if (imap_exec(adata, "CAPABILITY", IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS)
788 goto bail;
789 }
790 }
791 }
792 }
793
794 if (c_ssl_force_tls && (adata->conn->ssf == 0))
795 {
796 mutt_error(_("Encrypted connection unavailable"));
797 goto bail;
798 }
799#endif
800 }
801 else if (mutt_istr_startswith(adata->buf, "* PREAUTH"))
802 {
803#ifdef USE_SSL
804 /* Unless using a secure $tunnel, an unencrypted PREAUTH response may be a
805 * MITM attack. The only way to stop "STARTTLS" MITM attacks is via
806 * $ssl_force_tls: an attacker can easily spoof "* OK" and strip the
807 * STARTTLS capability. So consult $ssl_force_tls, not $ssl_starttls, to
808 * decide whether to abort. Note that if using $tunnel and
809 * $tunnel_is_secure, adata->conn->ssf will be set to 1. */
810 const bool c_ssl_force_tls = cs_subset_bool(NeoMutt->sub, "ssl_force_tls");
811 if ((adata->conn->ssf == 0) && c_ssl_force_tls)
812 {
813 mutt_error(_("Encrypted connection unavailable"));
814 goto bail;
815 }
816#endif
817
818 adata->state = IMAP_AUTHENTICATED;
819 if (check_capabilities(adata) != 0)
820 goto bail;
821 FREE(&adata->capstr);
822 }
823 else
824 {
825 imap_error("imap_open_connection()", adata->buf);
826 goto bail;
827 }
828
829 return 0;
830
831bail:
833 FREE(&adata->capstr);
834 return -1;
835}
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:48
int mutt_ssl_starttls(struct Connection *conn)
Negotiate TLS over an already opened connection.
Definition: gnutls.c:1144
@ 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:841
static int check_capabilities(struct ImapAccountData *adata)
Make sure we can log in to this server.
Definition: imap.c:99
#define FREE(x)
Definition: memory.h:45
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:369
void mutt_socket_empty(struct Connection *conn)
Clear out any queued data.
Definition: socket.c:320
int mutt_socket_open(struct Connection *conn)
Simple wrapper.
Definition: socket.c:77
unsigned int ssf
Security strength factor, in bits (see notes)
Definition: connection.h:51
char * capstr
Capability string from the server.
Definition: adata.h:54
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_close_connection()

void imap_close_connection ( struct ImapAccountData adata)

Close an IMAP connection.

Parameters
adataImap Account data

Definition at line 841 of file imap.c.

842{
843 if (adata->state != IMAP_DISCONNECTED)
844 {
845 mutt_socket_close(adata->conn);
846 adata->state = IMAP_DISCONNECTED;
847 }
848 adata->seqno = 0;
849 adata->nextcmd = 0;
850 adata->lastcmd = 0;
851 adata->status = 0;
852 memset(adata->cmds, 0, sizeof(struct ImapCommand) * adata->cmdslots);
853}
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 866 of file imap.c.

867{
868 if (STAILQ_EMPTY(flag_list))
869 return false;
870
871 const size_t flaglen = mutt_str_len(flag);
872 struct ListNode *np = NULL;
873 STAILQ_FOREACH(np, flag_list, entries)
874 {
875 const size_t nplen = strlen(np->data);
876 if ((flaglen >= nplen) && ((flag[nplen] == '\0') || (flag[nplen] == ' ')) &&
877 mutt_istrn_equal(np->data, flag, nplen))
878 {
879 return true;
880 }
881
882 if (mutt_str_equal(np->data, "\\*"))
883 return true;
884 }
885
886 return false;
887}
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:798
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:568
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:525
#define STAILQ_EMPTY(head)
Definition: queue.h:348
A List node for strings.
Definition: list.h:35
char * data
String.
Definition: list.h:36
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

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

920{
922 if (!adata || (adata->mailbox != m))
923 return -1;
924
925 char flags[1024] = { 0 };
926 char *tags = NULL;
927 char uid[11] = { 0 };
928
930 {
931 if (e->deleted == imap_edata_get(e)->deleted)
932 e->changed = false;
933 return 0;
934 }
935
936 snprintf(uid, sizeof(uid), "%u", imap_edata_get(e)->uid);
937 buf_reset(cmd);
938 buf_addstr(cmd, "UID STORE ");
939 buf_addstr(cmd, uid);
940
941 flags[0] = '\0';
942
943 set_flag(m, MUTT_ACL_SEEN, e->read, "\\Seen ", flags, sizeof(flags));
944 set_flag(m, MUTT_ACL_WRITE, e->old, "Old ", flags, sizeof(flags));
945 set_flag(m, MUTT_ACL_WRITE, e->flagged, "\\Flagged ", flags, sizeof(flags));
946 set_flag(m, MUTT_ACL_WRITE, e->replied, "\\Answered ", flags, sizeof(flags));
947 set_flag(m, MUTT_ACL_DELETE, imap_edata_get(e)->deleted, "\\Deleted ", flags,
948 sizeof(flags));
949
950 if (m->rights & MUTT_ACL_WRITE)
951 {
952 /* restore system flags */
953 if (imap_edata_get(e)->flags_system)
954 mutt_str_cat(flags, sizeof(flags), imap_edata_get(e)->flags_system);
955 /* set custom flags */
957 if (tags)
958 {
959 mutt_str_cat(flags, sizeof(flags), tags);
960 FREE(&tags);
961 }
962 }
963
965
966 /* UW-IMAP is OK with null flags, Cyrus isn't. The only solution is to
967 * explicitly revoke all system flags (if we have permission) */
968 if (*flags == '\0')
969 {
970 set_flag(m, MUTT_ACL_SEEN, true, "\\Seen ", flags, sizeof(flags));
971 set_flag(m, MUTT_ACL_WRITE, true, "Old ", flags, sizeof(flags));
972 set_flag(m, MUTT_ACL_WRITE, true, "\\Flagged ", flags, sizeof(flags));
973 set_flag(m, MUTT_ACL_WRITE, true, "\\Answered ", flags, sizeof(flags));
974 set_flag(m, MUTT_ACL_DELETE, !imap_edata_get(e)->deleted, "\\Deleted ",
975 flags, sizeof(flags));
976
977 /* erase custom flags */
978 if ((m->rights & MUTT_ACL_WRITE) && imap_edata_get(e)->flags_remote)
979 mutt_str_cat(flags, sizeof(flags), imap_edata_get(e)->flags_remote);
980
982
983 buf_addstr(cmd, " -FLAGS.SILENT (");
984 }
985 else
986 {
987 buf_addstr(cmd, " FLAGS.SILENT (");
988 }
989
990 buf_addstr(cmd, flags);
991 buf_addstr(cmd, ")");
992
993 /* after all this it's still possible to have no flags, if you
994 * have no ACL rights */
995 if (*flags && (imap_exec(adata, cmd->data, IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS) &&
996 err_continue && (*err_continue != MUTT_YES))
997 {
998 *err_continue = imap_continue("imap_sync_message: STORE failed", adata->buf);
999 if (*err_continue != MUTT_YES)
1000 return -1;
1001 }
1002
1003 /* server have now the updated flags */
1004 FREE(&imap_edata_get(e)->flags_remote);
1006
1007 if (e->deleted == imap_edata_get(e)->deleted)
1008 e->changed = false;
1009
1010 return 0;
1011}
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:88
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:238
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:643
static void set_flag(struct Mailbox *m, AclFlags aclflag, bool flag, const char *str, char *flags, size_t flsize)
Append str to flags if we currently have permission according to aclflag.
Definition: imap.c:180
static bool compare_flags_for_copy(struct Email *e)
Compare local flags against the server.
Definition: imap.c:196
#define MUTT_ACL_DELETE
Delete a message.
Definition: mailbox.h:63
#define MUTT_ACL_SEEN
Change the 'seen' status of a message.
Definition: mailbox.h:70
void mutt_str_remove_trailing_ws(char *s)
Trim trailing whitespace from a string.
Definition: string.c:637
struct TagList tags
For drivers that support server tagging.
Definition: email.h:70
char * flags_remote
Definition: edata.h:48
char * driver_tags_get_with_hidden(struct TagList *list)
Get tags with hiddens.
Definition: tags.c:158
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_check_mailbox()

enum MxStatus imap_check_mailbox ( struct Mailbox m,
bool  force 
)

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

Parameters
mMailbox
forceDon't wait
Return values
numMxStatus

Definition at line 1019 of file imap.c.

1020{
1021 if (!m || !m->account)
1022 return MX_STATUS_ERROR;
1023
1025 struct ImapMboxData *mdata = imap_mdata_get(m);
1026
1027 /* overload keyboard timeout to avoid many mailbox checks in a row.
1028 * Most users don't like having to wait exactly when they press a key. */
1029 int rc = 0;
1030
1031 /* try IDLE first, unless force is set */
1032 const bool c_imap_idle = cs_subset_bool(NeoMutt->sub, "imap_idle");
1033 const short c_imap_keep_alive = cs_subset_number(NeoMutt->sub, "imap_keep_alive");
1034 if (!force && c_imap_idle && (adata->capabilities & IMAP_CAP_IDLE) &&
1035 ((adata->state != IMAP_IDLE) || (mutt_date_now() >= adata->lastread + c_imap_keep_alive)))
1036 {
1037 if (imap_cmd_idle(adata) < 0)
1038 return MX_STATUS_ERROR;
1039 }
1040 if (adata->state == IMAP_IDLE)
1041 {
1042 while ((rc = mutt_socket_poll(adata->conn, 0)) > 0)
1043 {
1044 if (imap_cmd_step(adata) != IMAP_RES_CONTINUE)
1045 {
1046 mutt_debug(LL_DEBUG1, "Error reading IDLE response\n");
1047 return MX_STATUS_ERROR;
1048 }
1049 }
1050 if (rc < 0)
1051 {
1052 mutt_debug(LL_DEBUG1, "Poll failed, disabling IDLE\n");
1053 adata->capabilities &= ~IMAP_CAP_IDLE; // Clear the flag
1054 }
1055 }
1056
1057 const short c_timeout = cs_subset_number(NeoMutt->sub, "timeout");
1058 if ((force || ((adata->state != IMAP_IDLE) && (mutt_date_now() >= adata->lastread + c_timeout))) &&
1059 (imap_exec(adata, "NOOP", IMAP_CMD_POLL) != IMAP_EXEC_SUCCESS))
1060 {
1061 return MX_STATUS_ERROR;
1062 }
1063
1064 /* We call this even when we haven't run NOOP in case we have pending
1065 * changes to process, since we can reopen here. */
1066 imap_cmd_finish(adata);
1067
1068 enum MxStatus check = MX_STATUS_OK;
1069 if (mdata->check_status & IMAP_EXPUNGE_PENDING)
1070 check = MX_STATUS_REOPENED;
1071 else if (mdata->check_status & IMAP_NEWMAIL_PENDING)
1072 check = MX_STATUS_NEW_MAIL;
1073 else if (mdata->check_status & IMAP_FLAGS_PENDING)
1074 check = MX_STATUS_FLAGS;
1075 else if (rc < 0)
1076 check = MX_STATUS_ERROR;
1077
1078 mdata->check_status = IMAP_OPEN_NO_FLAGS;
1079
1080 return check;
1081}
int imap_cmd_idle(struct ImapAccountData *adata)
Enter the IDLE state.
Definition: command.c:1436
void imap_cmd_finish(struct ImapAccountData *adata)
Attempt to perform cleanup.
Definition: command.c:1369
#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:446
MxStatus
Return values from mbox_check(), mbox_check_stats(), mbox_snc(), and mbox_close()
Definition: mxapi.h:63
@ MX_STATUS_ERROR
An error occurred.
Definition: mxapi.h:64
@ MX_STATUS_OK
No changes.
Definition: mxapi.h:65
@ MX_STATUS_FLAGS
Nondestructive flags change (IMAP)
Definition: mxapi.h:69
@ MX_STATUS_REOPENED
Mailbox was reopened.
Definition: mxapi.h:68
@ MX_STATUS_NEW_MAIL
New mail received in Mailbox.
Definition: mxapi.h:66
time_t lastread
last time we read a command for the server
Definition: adata.h:58
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_status()

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

Refresh the number of total and new messages.

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

Definition at line 1090 of file imap.c.

1091{
1092 char *uidvalidity_flag = NULL;
1093 char cmd[2048] = { 0 };
1094
1095 if (!adata || !mdata)
1096 return -1;
1097
1098 /* Don't issue STATUS on the selected mailbox, it will be NOOPed or
1099 * IDLEd elsewhere.
1100 * adata->mailbox may be NULL for connections other than the current
1101 * mailbox's. */
1102 if (adata->mailbox && (adata->mailbox->mdata == mdata))
1103 {
1104 adata->mailbox->has_new = false;
1105 return mdata->messages;
1106 }
1107
1108 if (adata->mailbox && !adata->mailbox->poll_new_mail)
1109 return mdata->messages;
1110
1111 if (adata->capabilities & IMAP_CAP_IMAP4REV1)
1112 {
1113 uidvalidity_flag = "UIDVALIDITY";
1114 }
1115 else if (adata->capabilities & IMAP_CAP_STATUS)
1116 {
1117 uidvalidity_flag = "UID-VALIDITY";
1118 }
1119 else
1120 {
1121 mutt_debug(LL_DEBUG2, "Server doesn't support STATUS\n");
1122 return -1;
1123 }
1124
1125 snprintf(cmd, sizeof(cmd), "STATUS %s (UIDNEXT %s UNSEEN RECENT MESSAGES)",
1126 mdata->munge_name, uidvalidity_flag);
1127
1128 int rc = imap_exec(adata, cmd, queue ? IMAP_CMD_QUEUE : IMAP_CMD_POLL);
1129 if (rc != IMAP_EXEC_SUCCESS)
1130 {
1131 mutt_debug(LL_DEBUG1, "Error queueing command\n");
1132 return rc;
1133 }
1134 return mdata->messages;
1135}
#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
struct Mailbox * mailbox
Current selected mailbox.
Definition: adata.h:76
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:114
+ 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 1157 of file imap.c.

1158{
1159 struct Mailbox *m = mx_mbox_find2(path);
1160
1161 const bool is_temp = !m;
1162 if (is_temp)
1163 {
1164 m = mx_path_resolve(path);
1165 if (!mx_mbox_ac_link(m))
1166 {
1167 mailbox_free(&m);
1168 return 0;
1169 }
1170 }
1171
1172 int rc = imap_mailbox_status(m, queue);
1173
1174 if (is_temp)
1175 {
1176 mx_ac_remove(m, false);
1177 mailbox_free(&m);
1178 }
1179
1180 return rc;
1181}
int imap_mailbox_status(struct Mailbox *m, bool queue)
Refresh the number of total and new messages.
Definition: imap.c:1192
void mailbox_free(struct Mailbox **ptr)
Free a Mailbox.
Definition: mailbox.c:90
int mx_ac_remove(struct Mailbox *m, bool keep_account)
Remove a Mailbox from an Account and delete Account if empty.
Definition: mx.c:1801
struct Mailbox * mx_mbox_find2(const char *path)
Find a Mailbox on an Account.
Definition: mx.c:1666
bool mx_mbox_ac_link(struct Mailbox *m)
Link a Mailbox to an existing or new Account.
Definition: mx.c:267
struct Mailbox * mx_path_resolve(const char *path)
Get a Mailbox for a path.
Definition: mx.c:1697
A mailbox.
Definition: mailbox.h:79
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_mailbox_status()

int imap_mailbox_status ( struct Mailbox m,
bool  queue 
)

Refresh the number of total and new messages.

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

Definition at line 1192 of file imap.c.

1193{
1195 struct ImapMboxData *mdata = imap_mdata_get(m);
1196 if (!adata || !mdata)
1197 return -1;
1198 return imap_status(adata, mdata, queue);
1199}
static int imap_status(struct ImapAccountData *adata, struct ImapMboxData *mdata, bool queue)
Refresh the number of total and new messages.
Definition: imap.c:1090
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_subscribe()

int imap_subscribe ( char *  path,
bool  subscribe 
)

Subscribe to a mailbox.

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

Definition at line 1208 of file imap.c.

1209{
1210 struct ImapAccountData *adata = NULL;
1211 struct ImapMboxData *mdata = NULL;
1212 struct Buffer err;
1213
1214 if (imap_adata_find(path, &adata, &mdata) < 0)
1215 return -1;
1216
1217 if (subscribe)
1218 mutt_message(_("Subscribing to %s..."), mdata->name);
1219 else
1220 mutt_message(_("Unsubscribing from %s..."), mdata->name);
1221
1222 char buf[2048] = { 0 };
1223 snprintf(buf, sizeof(buf), "%sSUBSCRIBE %s", subscribe ? "" : "UN", mdata->munge_name);
1224
1225 if (imap_exec(adata, buf, IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS)
1226 {
1227 imap_mdata_free((void *) &mdata);
1228 return -1;
1229 }
1230
1231 const bool c_imap_check_subscribed = cs_subset_bool(NeoMutt->sub, "imap_check_subscribed");
1232 if (c_imap_check_subscribed)
1233 {
1234 char mbox[1024] = { 0 };
1235 buf_init(&err);
1236 err.dsize = 256;
1237 err.data = mutt_mem_malloc(err.dsize);
1238 size_t len = snprintf(mbox, sizeof(mbox), "%smailboxes ", subscribe ? "" : "un");
1239 imap_quote_string(mbox + len, sizeof(mbox) - len, path, true);
1240 if (parse_rc_line(mbox, &err))
1241 mutt_debug(LL_DEBUG1, "Error adding subscribed mailbox: %s\n", err.data);
1242 FREE(&err.data);
1243 }
1244
1245 if (subscribe)
1246 mutt_message(_("Subscribed to %s"), mdata->name);
1247 else
1248 mutt_message(_("Unsubscribed from %s"), mdata->name);
1249 imap_mdata_free((void *) &mdata);
1250 return 0;
1251}
struct Buffer * buf_init(struct Buffer *buf)
Initialise a new Buffer.
Definition: buffer.c:55
void imap_mdata_free(void **ptr)
Free the private Mailbox data - Implements Mailbox::mdata_free()
Definition: mdata.c:39
void imap_quote_string(char *dest, size_t dlen, const char *src, bool quote_backtick)
Quote string according to IMAP rules.
Definition: util.c:844
int imap_adata_find(const char *path, struct ImapAccountData **adata, struct ImapMboxData **mdata)
Find the Account data for this path.
Definition: util.c:71
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
enum CommandResult parse_rc_line(const char *line, struct Buffer *err)
Parse a line of user config.
Definition: rc.c:104
char * name
Mailbox name.
Definition: mdata.h:41
+ 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 1263 of file imap.c.

1264{
1265 struct ImapAccountData *adata = NULL;
1266 struct ImapMboxData *mdata = NULL;
1267 char tmp[2048] = { 0 };
1268 struct ImapList listresp = { 0 };
1269 struct Buffer *completion_buf = NULL;
1270 size_t clen;
1271 int completions = 0;
1272 int rc;
1273
1274 if (imap_adata_find(path, &adata, &mdata) < 0)
1275 {
1276 buf_strcpy(buf, path);
1277 return complete_hosts(buf);
1278 }
1279
1280 /* fire off command */
1281 const bool c_imap_list_subscribed = cs_subset_bool(NeoMutt->sub, "imap_list_subscribed");
1282 snprintf(tmp, sizeof(tmp), "%s \"\" \"%s%%\"",
1283 c_imap_list_subscribed ? "LSUB" : "LIST", mdata->real_name);
1284
1285 imap_cmd_start(adata, tmp);
1286
1287 /* and see what the results are */
1288 completion_buf = buf_pool_get();
1289 buf_strcpy(completion_buf, mdata->name);
1290 imap_mdata_free((void *) &mdata);
1291
1292 adata->cmdresult = &listresp;
1293 do
1294 {
1295 listresp.name = NULL;
1296 rc = imap_cmd_step(adata);
1297
1298 if ((rc == IMAP_RES_CONTINUE) && listresp.name)
1299 {
1300 /* if the folder isn't selectable, append delimiter to force browse
1301 * to enter it on second tab. */
1302 if (listresp.noselect)
1303 {
1304 clen = strlen(listresp.name);
1305 listresp.name[clen++] = listresp.delim;
1306 listresp.name[clen] = '\0';
1307 }
1308 /* copy in first word */
1309 if (!completions)
1310 {
1311 buf_strcpy(completion_buf, listresp.name);
1312 completions++;
1313 continue;
1314 }
1315
1316 longest_common_prefix(completion_buf, listresp.name, 0);
1317 completions++;
1318 }
1319 } while (rc == IMAP_RES_CONTINUE);
1320 adata->cmdresult = NULL;
1321
1322 if (completions)
1323 {
1324 /* reformat output */
1325 imap_buf_qualify_path(buf, &adata->conn->account, completion_buf->data);
1326 buf_pretty_mailbox(buf);
1327 buf_fix_dptr(buf);
1328 buf_pool_release(&completion_buf);
1329 return 0;
1330 }
1331
1332 buf_pool_release(&completion_buf);
1333 return -1;
1334}
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:827
static int complete_hosts(struct Buffer *buf)
Look for completion matches for mailboxes.
Definition: imap.c:364
void buf_pretty_mailbox(struct Buffer *buf)
Shorten a mailbox path using '~' or '='.
Definition: muttlib.c:562
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 1344 of file imap.c.

1345{
1346 char prompt[1024] = { 0 };
1347 int rc = -1;
1348 bool triedcreate = false;
1349 enum QuadOption err_continue = MUTT_NO;
1350
1352 struct ImapAccountData *dest_adata = NULL;
1353 struct ImapMboxData *dest_mdata = NULL;
1354
1355 if (imap_adata_find(dest, &dest_adata, &dest_mdata) < 0)
1356 return -1;
1357
1358 struct Buffer sync_cmd = buf_make(0);
1359
1360 /* check that the save-to folder is in the same account */
1361 if (!imap_account_match(&(adata->conn->account), &(dest_adata->conn->account)))
1362 {
1363 mutt_debug(LL_DEBUG3, "%s not same server as %s\n", dest, mailbox_path(m));
1364 goto out;
1365 }
1366
1367 for (int i = 0; i < m->msg_count; i++)
1368 {
1369 struct Email *e = m->emails[i];
1370 if (!e)
1371 break;
1372 if (e->active && e->changed && e->deleted && !e->purge)
1373 {
1374 rc = imap_sync_message_for_copy(m, e, &sync_cmd, &err_continue);
1375 if (rc < 0)
1376 {
1377 mutt_debug(LL_DEBUG1, "could not sync\n");
1378 goto out;
1379 }
1380 }
1381 }
1382
1383 /* loop in case of TRYCREATE */
1384 do
1385 {
1386 struct UidArray uida = ARRAY_HEAD_INITIALIZER;
1387 select_email_uids(m->emails, m->msg_count, MUTT_TRASH, false, false, &uida);
1388 ARRAY_SORT(&uida, imap_sort_uid, NULL);
1389 rc = imap_exec_msg_set(adata, "UID COPY", dest_mdata->munge_name, &uida);
1390 if (rc == 0)
1391 {
1392 mutt_debug(LL_DEBUG1, "No messages to trash\n");
1393 rc = -1;
1394 goto out;
1395 }
1396 else if (rc < 0)
1397 {
1398 mutt_debug(LL_DEBUG1, "could not queue copy\n");
1399 goto out;
1400 }
1401 else if (m->verbose)
1402 {
1403 mutt_message(ngettext("Copying %d message to %s...", "Copying %d messages to %s...", rc),
1404 rc, dest_mdata->name);
1405 }
1406 ARRAY_FREE(&uida);
1407
1408 /* let's get it on */
1409 rc = imap_exec(adata, NULL, IMAP_CMD_NO_FLAGS);
1410 if (rc == IMAP_EXEC_ERROR)
1411 {
1412 if (triedcreate)
1413 {
1414 mutt_debug(LL_DEBUG1, "Already tried to create mailbox %s\n", dest_mdata->name);
1415 break;
1416 }
1417 /* bail out if command failed for reasons other than nonexistent target */
1418 if (!mutt_istr_startswith(imap_get_qualifier(adata->buf), "[TRYCREATE]"))
1419 break;
1420 mutt_debug(LL_DEBUG3, "server suggests TRYCREATE\n");
1421 snprintf(prompt, sizeof(prompt), _("Create %s?"), dest_mdata->name);
1422 const bool c_confirm_create = cs_subset_bool(NeoMutt->sub, "confirm_create");
1423 if (c_confirm_create &&
1424 (query_yesorno_help(prompt, MUTT_YES, NeoMutt->sub, "confirm_create") != MUTT_YES))
1425 {
1427 goto out;
1428 }
1429 if (imap_create_mailbox(adata, dest_mdata->name) < 0)
1430 break;
1431 triedcreate = true;
1432 }
1433 } while (rc == IMAP_EXEC_ERROR);
1434
1435 if (rc != IMAP_EXEC_SUCCESS)
1436 {
1437 imap_error("imap_fast_trash", adata->buf);
1438 goto out;
1439 }
1440
1441 rc = IMAP_EXEC_SUCCESS;
1442
1443out:
1444 buf_dealloc(&sync_cmd);
1445 imap_mdata_free((void *) &dest_mdata);
1446
1447 return ((rc == IMAP_EXEC_SUCCESS) ? 0 : -1);
1448}
#define ARRAY_SORT(head, fn, sdata)
Sort an array.
Definition: array.h:278
struct Buffer buf_make(size_t size)
Make a new buffer on the stack.
Definition: buffer.c:70
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:1051
char * imap_get_qualifier(char *buf)
Get the qualifier from a tagged response.
Definition: util.c:765
int imap_create_mailbox(struct ImapAccountData *adata, const char *mailbox)
Create a new mailbox.
Definition: imap.c:429
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:918
@ LL_DEBUG3
Log at debug level 3.
Definition: logging2.h:45
@ 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:345
bool verbose
Display status messages?
Definition: mailbox.h:116
+ 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 1459 of file imap.c.

1460{
1461 if (!m)
1462 return -1;
1463
1464 struct Email **emails = NULL;
1465 int rc;
1466
1468 struct ImapMboxData *mdata = imap_mdata_get(m);
1469
1470 if (adata->state < IMAP_SELECTED)
1471 {
1472 mutt_debug(LL_DEBUG2, "no mailbox selected\n");
1473 return -1;
1474 }
1475
1476 /* This function is only called when the calling code expects the context
1477 * to be changed. */
1479
1480 enum MxStatus check = imap_check_mailbox(m, false);
1481 if (check == MX_STATUS_ERROR)
1482 return check;
1483
1484 /* if we are expunging anyway, we can do deleted messages very quickly... */
1485 if (expunge && (m->rights & MUTT_ACL_DELETE))
1486 {
1487 struct UidArray uida = ARRAY_HEAD_INITIALIZER;
1488 select_email_uids(m->emails, m->msg_count, MUTT_DELETED, true, false, &uida);
1489 ARRAY_SORT(&uida, imap_sort_uid, NULL);
1490 rc = imap_exec_msg_set(adata, "UID STORE", "+FLAGS.SILENT (\\Deleted)", &uida);
1491 ARRAY_FREE(&uida);
1492 if (rc < 0)
1493 {
1494 mutt_error(_("Expunge failed"));
1495 return rc;
1496 }
1497
1498 if (rc > 0)
1499 {
1500 /* mark these messages as unchanged so second pass ignores them. Done
1501 * here so BOGUS UW-IMAP 4.7 SILENT FLAGS updates are ignored. */
1502 for (int i = 0; i < m->msg_count; i++)
1503 {
1504 struct Email *e = m->emails[i];
1505 if (!e)
1506 break;
1507 if (e->deleted && e->changed)
1508 e->active = false;
1509 }
1510 if (m->verbose)
1511 {
1512 mutt_message(ngettext("Marking %d message deleted...",
1513 "Marking %d messages deleted...", rc),
1514 rc);
1515 }
1516 }
1517 }
1518
1519#ifdef USE_HCACHE
1520 imap_hcache_open(adata, mdata);
1521#endif
1522
1523 /* save messages with real (non-flag) changes */
1524 for (int i = 0; i < m->msg_count; i++)
1525 {
1526 struct Email *e = m->emails[i];
1527 if (!e)
1528 break;
1529
1530 if (e->deleted)
1531 {
1532 imap_cache_del(m, e);
1533#ifdef USE_HCACHE
1534 imap_hcache_del(mdata, imap_edata_get(e)->uid);
1535#endif
1536 }
1537
1538 if (e->active && e->changed)
1539 {
1540#ifdef USE_HCACHE
1541 imap_hcache_put(mdata, e);
1542#endif
1543 /* if the message has been rethreaded or attachments have been deleted
1544 * we delete the message and reupload it.
1545 * This works better if we're expunging, of course. */
1546 if (e->env->changed || e->attach_del)
1547 {
1548 /* L10N: The plural is chosen by the last %d, i.e. the total number */
1549 if (m->verbose)
1550 {
1551 mutt_message(ngettext("Saving changed message... [%d/%d]",
1552 "Saving changed messages... [%d/%d]", m->msg_count),
1553 i + 1, m->msg_count);
1554 }
1555 bool save_append = m->append;
1556 m->append = true;
1558 m->append = save_append;
1559 e->env->changed = false;
1560 }
1561 }
1562 }
1563
1564#ifdef USE_HCACHE
1565 imap_hcache_close(mdata);
1566#endif
1567
1568 /* presort here to avoid doing 10 resorts in imap_exec_msg_set */
1569 emails = mutt_mem_malloc(m->msg_count * sizeof(struct Email *));
1570 memcpy(emails, m->emails, m->msg_count * sizeof(struct Email *));
1571 mutt_qsort_r(emails, m->msg_count, sizeof(struct Email *), imap_sort_email_uid, NULL);
1572
1573 rc = sync_helper(m, emails, m->msg_count, MUTT_ACL_DELETE, MUTT_DELETED, "\\Deleted");
1574 if (rc >= 0)
1575 rc |= sync_helper(m, emails, m->msg_count, MUTT_ACL_WRITE, MUTT_FLAG, "\\Flagged");
1576 if (rc >= 0)
1577 rc |= sync_helper(m, emails, m->msg_count, MUTT_ACL_WRITE, MUTT_OLD, "Old");
1578 if (rc >= 0)
1579 rc |= sync_helper(m, emails, m->msg_count, MUTT_ACL_SEEN, MUTT_READ, "\\Seen");
1580 if (rc >= 0)
1581 rc |= sync_helper(m, emails, m->msg_count, MUTT_ACL_WRITE, MUTT_REPLIED, "\\Answered");
1582
1583 FREE(&emails);
1584
1585 /* Flush the queued flags if any were changed in sync_helper. */
1586 if (rc > 0)
1587 if (imap_exec(adata, NULL, IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS)
1588 rc = -1;
1589
1590 if (rc < 0)
1591 {
1592 if (close)
1593 {
1594 if (query_yesorno(_("Error saving flags. Close anyway?"), MUTT_NO) == MUTT_YES)
1595 {
1596 adata->state = IMAP_AUTHENTICATED;
1597 return 0;
1598 }
1599 }
1600 else
1601 {
1602 mutt_error(_("Error saving flags"));
1603 }
1604 return -1;
1605 }
1606
1607 /* Update local record of server state to reflect the synchronization just
1608 * completed. imap_read_headers always overwrites hcache-origin flags, so
1609 * there is no need to mutate the hcache after flag-only changes. */
1610 for (int i = 0; i < m->msg_count; i++)
1611 {
1612 struct Email *e = m->emails[i];
1613 if (!e)
1614 break;
1615 struct ImapEmailData *edata = imap_edata_get(e);
1616 edata->deleted = e->deleted;
1617 edata->flagged = e->flagged;
1618 edata->old = e->old;
1619 edata->read = e->read;
1620 edata->replied = e->replied;
1621 e->changed = false;
1622 }
1623 m->changed = false;
1624
1625 /* We must send an EXPUNGE command if we're not closing. */
1626 if (expunge && !close && (m->rights & MUTT_ACL_DELETE))
1627 {
1628 if (m->verbose)
1629 mutt_message(_("Expunging messages from server..."));
1630 /* Set expunge bit so we don't get spurious reopened messages */
1631 mdata->reopen |= IMAP_EXPUNGE_EXPECTED;
1632 if (imap_exec(adata, "EXPUNGE", IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS)
1633 {
1634 mdata->reopen &= ~IMAP_EXPUNGE_EXPECTED;
1635 imap_error(_("imap_sync_mailbox: EXPUNGE failed"), adata->buf);
1636 return -1;
1637 }
1638 mdata->reopen &= ~IMAP_EXPUNGE_EXPECTED;
1639 }
1640
1641 if (expunge && close)
1642 {
1643 adata->closing = true;
1644 imap_exec(adata, "CLOSE", IMAP_CMD_NO_FLAGS);
1645 adata->state = IMAP_AUTHENTICATED;
1646 }
1647
1648 const bool c_message_cache_clean = cs_subset_bool(NeoMutt->sub, "message_cache_clean");
1649 if (c_message_cache_clean)
1651
1652 return check;
1653}
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:738
@ TRANSFORM_NONE
No transformation.
Definition: external.h:40
@ SAVE_MOVE
Move message to another mailbox, removing the original.
Definition: external.h:51
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:892
int imap_cache_clean(struct Mailbox *m)
Delete all the entries in the message cache.
Definition: message.c:1867
void imap_allow_reopen(struct Mailbox *m)
Allow re-opening a folder upon expunge.
Definition: util.c:1023
@ 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:377
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:293
enum MxStatus imap_check_mailbox(struct Mailbox *m, bool force)
Use the NOOP or IDLE command to poll for new mail.
Definition: imap.c:1019
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:66
enum QuadOption query_yesorno(const char *prompt, enum QuadOption def)
Ask the user a Yes/No question.
Definition: question.c:330
struct Envelope * env
Envelope information.
Definition: email.h:66
bool attach_del
Has an attachment marked for deletion.
Definition: email.h:98
unsigned char changed
Changed fields, e.g. MUTT_ENV_CHANGED_SUBJECT.
Definition: envelope.h:92
bool closing
If true, we are waiting for CLOSE completion.
Definition: adata.h:43
ImapOpenFlags reopen
Flags, e.g. IMAP_REOPEN_ALLOW.
Definition: mdata.h:45
bool changed
Mailbox has been modified.
Definition: mailbox.h:109
bool append
Mailbox is opened in append mode.
Definition: mailbox.h:108
+ 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 1732 of file imap.c.

1733{
1735 struct ImapMboxData *mdata = imap_mdata_get(m);
1736 if (!adata || !mdata)
1737 return;
1738
1739 const char *condstore = NULL;
1740#ifdef USE_HCACHE
1741 const bool c_imap_condstore = cs_subset_bool(NeoMutt->sub, "imap_condstore");
1742 if ((adata->capabilities & IMAP_CAP_CONDSTORE) && c_imap_condstore)
1743 condstore = " (CONDSTORE)";
1744 else
1745#endif
1746 condstore = "";
1747
1748 char buf[PATH_MAX] = { 0 };
1749 snprintf(buf, sizeof(buf), "%s %s%s", m->readonly ? "EXAMINE" : "SELECT",
1750 mdata->munge_name, condstore);
1751
1752 adata->state = IMAP_SELECTED;
1753
1754 imap_cmd_start(adata, buf);
1755}
#define IMAP_CAP_CONDSTORE
RFC7162.
Definition: private.h:136
bool readonly
Don't allow changes to the mailbox.
Definition: mailbox.h:115
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_login()

int imap_login ( struct ImapAccountData adata)

Open an IMAP connection.

Parameters
adataImap Account data
Return values
0Success
-1Failure

Ensure ImapAccountData is connected and logged into the imap server.

Definition at line 1765 of file imap.c.

1766{
1767 if (!adata)
1768 return -1;
1769
1770 if (adata->state == IMAP_DISCONNECTED)
1771 {
1772 buf_reset(&adata->cmdbuf); // purge outstanding queued commands
1773 imap_open_connection(adata);
1774 }
1775 if (adata->state == IMAP_CONNECTED)
1776 {
1778 {
1779 adata->state = IMAP_AUTHENTICATED;
1780 FREE(&adata->capstr);
1781 if (adata->conn->ssf != 0)
1782 {
1783 mutt_debug(LL_DEBUG2, "Communication encrypted at %d bits\n",
1784 adata->conn->ssf);
1785 }
1786 }
1787 else
1788 {
1790 }
1791 }
1792 if (adata->state == IMAP_AUTHENTICATED)
1793 {
1794 /* capabilities may have changed */
1795 imap_exec(adata, "CAPABILITY", IMAP_CMD_PASS);
1796
1797#ifdef USE_ZLIB
1798 /* RFC4978 */
1799 const bool c_imap_deflate = cs_subset_bool(NeoMutt->sub, "imap_deflate");
1800 if ((adata->capabilities & IMAP_CAP_COMPRESS) && c_imap_deflate &&
1801 (imap_exec(adata, "COMPRESS DEFLATE", IMAP_CMD_PASS) == IMAP_EXEC_SUCCESS))
1802 {
1803 mutt_debug(LL_DEBUG2, "IMAP compression is enabled on connection to %s\n",
1804 adata->conn->account.host);
1805 mutt_zstrm_wrap_conn(adata->conn);
1806 }
1807#endif
1808
1809 /* enable RFC2971, if the server supports that */
1810 const bool c_imap_send_id = cs_subset_bool(NeoMutt->sub, "imap_send_id");
1811 if (c_imap_send_id && (adata->capabilities & IMAP_CAP_ID))
1812 {
1813 imap_exec(adata, "ID (\"name\" \"NeoMutt\" \"version\" \"" PACKAGE_VERSION "\")",
1815 }
1816
1817 /* enable RFC6855, if the server supports that */
1818 const bool c_imap_rfc5161 = cs_subset_bool(NeoMutt->sub, "imap_rfc5161");
1819 if (c_imap_rfc5161 && (adata->capabilities & IMAP_CAP_ENABLE))
1820 imap_exec(adata, "ENABLE UTF8=ACCEPT", IMAP_CMD_QUEUE);
1821
1822 /* enable QRESYNC. Advertising QRESYNC also means CONDSTORE
1823 * is supported (even if not advertised), so flip that bit. */
1824 if (adata->capabilities & IMAP_CAP_QRESYNC)
1825 {
1827 const bool c_imap_qresync = cs_subset_bool(NeoMutt->sub, "imap_qresync");
1828 if (c_imap_rfc5161 && c_imap_qresync)
1829 imap_exec(adata, "ENABLE QRESYNC", IMAP_CMD_QUEUE);
1830 }
1831
1832 /* get root delimiter, '/' as default */
1833 adata->delim = '/';
1834 imap_exec(adata, "LIST \"\" \"\"", IMAP_CMD_QUEUE);
1835
1836 /* we may need the root delimiter before we open a mailbox */
1837 imap_exec(adata, NULL, IMAP_CMD_NO_FLAGS);
1838
1839 /* select the mailbox that used to be open before disconnect */
1840 if (adata->mailbox)
1841 {
1842 imap_mbox_select(adata->mailbox);
1843 }
1844 }
1845
1846 if (adata->state < IMAP_AUTHENTICATED)
1847 return -1;
1848
1849 return 0;
1850}
@ IMAP_AUTH_SUCCESS
Authentication successful.
Definition: auth.h:39
void mutt_account_unsetpass(struct ConnAccount *cac)
Unset ConnAccount's password.
Definition: connaccount.c:176
int imap_authenticate(struct ImapAccountData *adata)
Authenticate to an IMAP server.
Definition: auth.c:110
#define IMAP_CAP_ENABLE
RFC5161.
Definition: private.h: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:733
static void imap_mbox_select(struct Mailbox *m)
Select a Mailbox.
Definition: imap.c:1732
char delim
Path delimiter.
Definition: adata.h:75
struct Buffer cmdbuf
Definition: adata.h:73
void mutt_zstrm_wrap_conn(struct Connection *conn)
Wrap a compression layer around a Connection.
Definition: zstrm.c:288
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_expand_path()

int imap_expand_path ( struct Buffer path)

Buffer wrapper around imap_path_canon()

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

Definition at line 2369 of file imap.c.

2370{
2371 buf_alloc(path, PATH_MAX);
2372 return imap_path_canon(path);
2373}
int imap_path_canon(struct Buffer *path)
Canonicalise a Mailbox path - Implements MxOps::path_canon() -.
Definition: imap.c:2343
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

Variable Documentation

◆ ImapCommands

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

Imap Commands.

Definition at line 78 of file imap.c.