NeoMutt  2025-09-05-7-geaa2bd
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? - Implements MxOps::path_probe() -.
 
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 95 of file imap.c.

96{
98}
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 106 of file imap.c.

107{
108 if (imap_exec(adata, "CAPABILITY", IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS)
109 {
110 imap_error("check_capabilities", adata->buf);
111 return -1;
112 }
113
114 if (!((adata->capabilities & IMAP_CAP_IMAP4) || (adata->capabilities & IMAP_CAP_IMAP4REV1)))
115 {
116 mutt_error(_("This IMAP server is ancient. NeoMutt does not work with it."));
117 return -1;
118 }
119
120 return 0;
121}
#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:1302
#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:659
#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 132 of file imap.c.

133{
134 /* sanity-check string */
135 const size_t plen = mutt_istr_startswith(s, "FLAGS");
136 if (plen == 0)
137 {
138 mutt_debug(LL_DEBUG1, "not a FLAGS response: %s\n", s);
139 return NULL;
140 }
141 s += plen;
142 SKIPWS(s);
143 if (*s != '(')
144 {
145 mutt_debug(LL_DEBUG1, "bogus FLAGS response: %s\n", s);
146 return NULL;
147 }
148
149 /* update caller's flags handle */
150 while (*s && (*s != ')'))
151 {
152 s++;
153 SKIPWS(s);
154 const char *flag_word = s;
155 while (*s && (*s != ')') && !mutt_isspace(*s))
156 s++;
157 const char ctmp = *s;
158 *s = '\0';
159 if (*flag_word)
160 mutt_list_insert_tail(hflags, mutt_str_dup(flag_word));
161 *s = ctmp;
162 }
163
164 /* note bad flags response */
165 if (*s != ')')
166 {
167 mutt_debug(LL_DEBUG1, "Unterminated FLAGS response: %s\n", s);
168 mutt_list_free(hflags);
169
170 return NULL;
171 }
172
173 s++;
174
175 return s;
176}
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:254
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition: string.c:243
#define SKIPWS(ch)
Definition: string2.h:44
+ 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 186 of file imap.c.

188{
189 if (m->rights & aclflag)
190 if (flag && imap_has_flag(&imap_mdata_get(m)->flags, str))
191 buf_addstr(flags, str);
192}
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:876
AclFlags rights
ACL bits, see AclFlags.
Definition: mailbox.h:119
+ 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 202 of file imap.c.

203{
204 struct ImapEmailData *edata = e->edata;
205
206 if (e->read != edata->read)
207 return true;
208 if (e->old != edata->old)
209 return true;
210 if (e->flagged != edata->flagged)
211 return true;
212 if (e->replied != edata->replied)
213 return true;
214
215 return false;
216}
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 229 of file imap.c.

231{
232 if (!emails || !uida)
233 return -1;
234
235 for (int i = 0; i < num_emails; i++)
236 {
237 struct Email *e = emails[i];
238 if (changed && !e->changed)
239 continue;
240
241 /* don't include pending expunged messages.
242 *
243 * TODO: can we unset active in cmd_parse_expunge() and
244 * cmd_parse_vanished() instead of checking for index != INT_MAX. */
245 if (!e || !e->active || (e->index == INT_MAX))
246 continue;
247
249
250 bool match = false;
251 switch (flag)
252 {
253 case MUTT_DELETED:
254 if (e->deleted != edata->deleted)
255 match = invert ^ e->deleted;
256 break;
257 case MUTT_FLAG:
258 if (e->flagged != edata->flagged)
259 match = invert ^ e->flagged;
260 break;
261 case MUTT_OLD:
262 if (e->old != edata->old)
263 match = invert ^ e->old;
264 break;
265 case MUTT_READ:
266 if (e->read != edata->read)
267 match = invert ^ e->read;
268 break;
269 case MUTT_REPLIED:
270 if (e->replied != edata->replied)
271 match = invert ^ e->replied;
272 break;
273 case MUTT_TRASH:
274 if (e->deleted && !e->purge)
275 match = true;
276 break;
277 default:
278 break;
279 }
280
281 if (match)
282 ARRAY_ADD(uida, edata->uid);
283 }
284
285 return ARRAY_SIZE(uida);
286}
#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:67
@ 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 299 of file imap.c.

301{
303 if (!adata)
304 return -1;
305
306 if ((m->rights & right) == 0)
307 return 0;
308
309 if ((right == MUTT_ACL_WRITE) && !imap_has_flag(&imap_mdata_get(m)->flags, name))
310 return 0;
311
312 int count = 0;
313 char buf[1024] = { 0 };
314
315 struct UidArray uida = ARRAY_HEAD_INITIALIZER;
316
317 // Set the flag (+FLAGS) on matching emails
318 select_email_uids(emails, num_emails, flag, true, false, &uida);
319 snprintf(buf, sizeof(buf), "+FLAGS.SILENT (%s)", name);
320 int rc = imap_exec_msg_set(adata, "UID STORE", buf, &uida);
321 if (rc < 0)
322 return rc;
323 count += rc;
324 ARRAY_FREE(&uida);
325
326 // Clear the flag (-FLAGS) on non-matching emails
327 select_email_uids(emails, num_emails, flag, true, true, &uida);
328 buf[0] = '-';
329 rc = imap_exec_msg_set(adata, "UID STORE", buf, &uida);
330 if (rc < 0)
331 return rc;
332 count += rc;
333 ARRAY_FREE(&uida);
334
335 return count;
336}
#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:229
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:38
void * adata
Private data (for Mailbox backends)
Definition: account.h:42
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 347 of file imap.c.

348{
349 size_t pos = start;
350
351 size_t len = buf_len(buf);
352 while ((pos < len) && buf->data[pos] && (buf->data[pos] == src[pos]))
353 pos++;
354 buf->data[pos] = '\0';
355
356 buf_fix_dptr(buf);
357
358 return pos;
359}
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 * 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 370 of file imap.c.

371{
372 int rc = -1;
373 size_t matchlen;
374
375 matchlen = buf_len(buf);
376 struct MailboxList ml = STAILQ_HEAD_INITIALIZER(ml);
378 struct MailboxNode *np = NULL;
379 STAILQ_FOREACH(np, &ml, entries)
380 {
382 continue;
383
384 if (rc)
385 {
387 rc = 0;
388 }
389 else
390 {
391 longest_common_prefix(buf, mailbox_path(np->mailbox), matchlen);
392 }
393 }
395
396#if 0
397 TAILQ_FOREACH(conn, mutt_socket_head(), entries)
398 {
399 struct Url url = { 0 };
400 char urlstr[1024] = { 0 };
401
402 if (conn->account.type != MUTT_ACCT_TYPE_IMAP)
403 continue;
404
405 account_to_url(&conn->account, &url);
406 /* FIXME: how to handle multiple users on the same host? */
407 url.user = NULL;
408 url.path = NULL;
409 url_tostring(&url, urlstr, sizeof(urlstr), U_NO_FLAGS);
410 if (mutt_strn_equal(buf, urlstr, matchlen))
411 {
412 if (rc)
413 {
414 mutt_str_copy(buf, urlstr, buflen);
415 rc = 0;
416 }
417 else
418 {
419 longest_common_prefix(buf, urlstr, matchlen);
420 }
421 }
422 }
423#endif
424
425 return rc;
426}
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:223
@ 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:347
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:426
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:231
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:580
void account_to_url(struct ConnAccount *cac, struct Url *url)
Fill URL with info from account.
Definition: mutt_account.c:80
@ MUTT_ACCT_TYPE_IMAP
Imap Account.
Definition: mutt_account.h:36
void neomutt_mailboxlist_clear(struct MailboxList *ml)
Free a Mailbox List.
Definition: neomutt.c:173
size_t neomutt_mailboxlist_get_all(struct MailboxList *head, struct NeoMutt *n, enum MailboxType type)
Get a List of all Mailboxes.
Definition: neomutt.c:196
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:782
#define STAILQ_HEAD_INITIALIZER(head)
Definition: queue.h:324
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:390
List of Mailboxes.
Definition: mailbox.h:166
struct Mailbox * mailbox
Mailbox in the list.
Definition: mailbox.h:167
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 435 of file imap.c.

436{
437 char buf[2048] = { 0 };
438 char mbox[1024] = { 0 };
439
440 imap_munge_mbox_name(adata->unicode, mbox, sizeof(mbox), mailbox);
441 snprintf(buf, sizeof(buf), "CREATE %s", mbox);
442
444 {
445 mutt_error(_("CREATE failed: %s"), imap_cmd_trailer(adata));
446 return -1;
447 }
448
449 return 0;
450}
const char * imap_cmd_trailer(struct ImapAccountData *adata)
Extra information after tagged command response if any.
Definition: command.c:1265
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:959
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 462 of file imap.c.

463{
464 if (imap_path_status(path, false) >= 0)
465 return 0;
466 return -1;
467}
int imap_path_status(const char *path, bool queue)
Refresh the number of total and new messages.
Definition: imap.c:1174
+ 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 477 of file imap.c.

478{
479 char oldmbox[1024] = { 0 };
480 char newmbox[1024] = { 0 };
481 int rc = 0;
482
483 imap_munge_mbox_name(adata->unicode, oldmbox, sizeof(oldmbox), oldname);
484 imap_munge_mbox_name(adata->unicode, newmbox, sizeof(newmbox), newname);
485
486 struct Buffer *buf = buf_pool_get();
487 buf_printf(buf, "RENAME %s %s", oldmbox, newmbox);
488
490 rc = -1;
491
492 buf_pool_release(&buf);
493
494 return rc;
495}
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 504 of file imap.c.

505{
506 char buf[PATH_MAX + 7];
507 char mbox[PATH_MAX] = { 0 };
508 struct Url *url = url_parse(path);
509 if (!url)
510 return -1;
511
513 imap_munge_mbox_name(adata->unicode, mbox, sizeof(mbox), url->path);
514 url_free(&url);
515 snprintf(buf, sizeof(buf), "DELETE %s", mbox);
517 return -1;
518
519 return 0;
520}
#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 526 of file imap.c.

527{
528 if (adata->status != IMAP_FATAL)
529 {
530 /* we set status here to let imap_handle_untagged know we _expect_ to
531 * receive a bye response (so it doesn't freak out and close the conn) */
532 if (adata->state == IMAP_DISCONNECTED)
533 {
534 return;
535 }
536
537 adata->status = IMAP_BYE;
538 imap_cmd_start(adata, "LOGOUT");
539 const short c_imap_poll_timeout = cs_subset_number(NeoMutt->sub, "imap_poll_timeout");
540 if ((c_imap_poll_timeout <= 0) ||
541 (mutt_socket_poll(adata->conn, c_imap_poll_timeout) != 0))
542 {
543 while (imap_cmd_step(adata) == IMAP_RES_CONTINUE)
544 ; // do nothing
545 }
546 }
547 mutt_socket_close(adata->conn);
548 adata->state = IMAP_DISCONNECTED;
549}
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:1113
int imap_cmd_step(struct ImapAccountData *adata)
Reads server responses from an IMAP command.
Definition: command.c:1127
@ 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 556 of file imap.c.

557{
558 struct Account *np = NULL;
559 TAILQ_FOREACH(np, &NeoMutt->accounts, entries)
560 {
561 if (np->type != MUTT_IMAP)
562 continue;
563
564 struct ImapAccountData *adata = np->adata;
565 if (!adata)
566 continue;
567
568 struct Connection *conn = adata->conn;
569 if (!conn || (conn->fd < 0))
570 continue;
571
572 mutt_message(_("Closing connection to %s..."), conn->account.host);
573 imap_logout(np->adata);
575 }
576}
@ 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:526
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
Definition: mutt_logging.c:74
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 AccountList accounts
List of 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 592 of file imap.c.

594{
595 char c;
596 bool r = false;
597 struct Buffer buf = { 0 }; // Do not allocate, maybe it won't be used
598
599 const short c_debug_level = cs_subset_number(NeoMutt->sub, "debug_level");
600 if (c_debug_level >= IMAP_LOG_LTRL)
601 buf_alloc(&buf, bytes + 1);
602
603 mutt_debug(LL_DEBUG2, "reading %lu bytes\n", bytes);
604
605 for (unsigned long pos = 0; pos < bytes; pos++)
606 {
607 if (mutt_socket_readchar(adata->conn, &c) != 1)
608 {
609 mutt_debug(LL_DEBUG1, "error during read, %lu bytes read\n", pos);
610 adata->status = IMAP_FATAL;
611
612 buf_dealloc(&buf);
613 return -1;
614 }
615
616 if (r && (c != '\n'))
617 fputc('\r', fp);
618
619 if (c == '\r')
620 {
621 r = true;
622 continue;
623 }
624 else
625 {
626 r = false;
627 }
628
629 fputc(c, fp);
630
631 if ((pos % 1024) == 0)
632 progress_update(progress, pos, -1);
633 if (c_debug_level >= IMAP_LOG_LTRL)
634 buf_addch(&buf, c);
635 }
636
637 if (c_debug_level >= IMAP_LOG_LTRL)
638 {
639 mutt_debug(IMAP_LOG_LTRL, "\n%s", buf.data);
640 buf_dealloc(&buf);
641 }
642 return 0;
643}
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 650 of file imap.c.

651{
652 struct ImapMboxData *mdata = imap_mdata_get(m);
654
655 if (!mdata || !edata)
656 return;
657
658 imap_msn_remove(&mdata->msn, edata->msn - 1);
659 edata->msn = 0;
660}
void imap_msn_remove(struct MSNArray *msn, int idx)
Remove an entry from the cache.
Definition: msn.c:116
IMAP-specific Mailbox data -.
Definition: mdata.h:40
void * mdata
Driver specific data.
Definition: mailbox.h:132
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_expunge_mailbox()

void imap_expunge_mailbox ( struct Mailbox m,
bool  resort 
)

Purge messages from the server.

Parameters
mMailbox
resortTrigger a resort?

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

Definition at line 671 of file imap.c.

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

744{
745 if (mutt_socket_open(adata->conn) < 0)
746 return -1;
747
748 adata->state = IMAP_CONNECTED;
749
750 if (imap_cmd_step(adata) != IMAP_RES_OK)
751 {
753 return -1;
754 }
755
756 if (mutt_istr_startswith(adata->buf, "* OK"))
757 {
758 if (!mutt_istr_startswith(adata->buf, "* OK [CAPABILITY") && check_capabilities(adata))
759 {
760 goto bail;
761 }
762#ifdef USE_SSL
763 /* Attempt STARTTLS if available and desired. */
764 const bool c_ssl_force_tls = cs_subset_bool(NeoMutt->sub, "ssl_force_tls");
765 if ((adata->conn->ssf == 0) &&
766 (c_ssl_force_tls || (adata->capabilities & IMAP_CAP_STARTTLS)))
767 {
768 enum QuadOption ans;
769
770 if (c_ssl_force_tls)
771 {
772 ans = MUTT_YES;
773 }
774 else if ((ans = query_quadoption(_("Secure connection with TLS?"),
775 NeoMutt->sub, "ssl_starttls")) == MUTT_ABORT)
776 {
777 goto bail;
778 }
779 if (ans == MUTT_YES)
780 {
781 enum ImapExecResult rc = imap_exec(adata, "STARTTLS", IMAP_CMD_SINGLE);
782 // Clear any data after the STARTTLS acknowledgement
783 mutt_socket_empty(adata->conn);
784
785 if (rc == IMAP_EXEC_FATAL)
786 goto bail;
787 if (rc != IMAP_EXEC_ERROR)
788 {
789 if (mutt_ssl_starttls(adata->conn))
790 {
791 mutt_error(_("Could not negotiate TLS connection"));
792 goto bail;
793 }
794 else
795 {
796 /* RFC2595 demands we recheck CAPABILITY after TLS completes. */
797 if (imap_exec(adata, "CAPABILITY", IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS)
798 goto bail;
799 }
800 }
801 }
802 }
803
804 if (c_ssl_force_tls && (adata->conn->ssf == 0))
805 {
806 mutt_error(_("Encrypted connection unavailable"));
807 goto bail;
808 }
809#endif
810 }
811 else if (mutt_istr_startswith(adata->buf, "* PREAUTH"))
812 {
813#ifdef USE_SSL
814 /* Unless using a secure $tunnel, an unencrypted PREAUTH response may be a
815 * MITM attack. The only way to stop "STARTTLS" MITM attacks is via
816 * $ssl_force_tls: an attacker can easily spoof "* OK" and strip the
817 * STARTTLS capability. So consult $ssl_force_tls, not $ssl_starttls, to
818 * decide whether to abort. Note that if using $tunnel and
819 * $tunnel_is_secure, adata->conn->ssf will be set to 1. */
820 const bool c_ssl_force_tls = cs_subset_bool(NeoMutt->sub, "ssl_force_tls");
821 if ((adata->conn->ssf == 0) && c_ssl_force_tls)
822 {
823 mutt_error(_("Encrypted connection unavailable"));
824 goto bail;
825 }
826#endif
827
828 adata->state = IMAP_AUTHENTICATED;
829 if (check_capabilities(adata) != 0)
830 goto bail;
831 FREE(&adata->capstr);
832 }
833 else
834 {
835 imap_error("imap_open_connection()", adata->buf);
836 goto bail;
837 }
838
839 return 0;
840
841bail:
843 FREE(&adata->capstr);
844 return -1;
845}
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:1149
@ 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:851
static int check_capabilities(struct ImapAccountData *adata)
Make sure we can log in to this server.
Definition: imap.c:106
#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:378
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 851 of file imap.c.

852{
853 if (adata->state != IMAP_DISCONNECTED)
854 {
855 mutt_socket_close(adata->conn);
856 adata->state = IMAP_DISCONNECTED;
857 }
858 adata->seqno = 0;
859 adata->nextcmd = 0;
860 adata->lastcmd = 0;
861 adata->status = 0;
862 memset(adata->cmds, 0, sizeof(struct ImapCommand) * adata->cmdslots);
863}
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 876 of file imap.c.

877{
878 if (STAILQ_EMPTY(flag_list))
879 return false;
880
881 const size_t flaglen = mutt_str_len(flag);
882 struct ListNode *np = NULL;
883 STAILQ_FOREACH(np, flag_list, entries)
884 {
885 const size_t nplen = strlen(np->data);
886 if ((flaglen >= nplen) && ((flag[nplen] == '\0') || (flag[nplen] == ' ')) &&
887 mutt_istrn_equal(np->data, flag, nplen))
888 {
889 return true;
890 }
891
892 if (mutt_str_equal(np->data, "\\*"))
893 return true;
894 }
895
896 return false;
897}
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:659
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:497
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:454
#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 928 of file imap.c.

930{
933
934 if (!adata || (adata->mailbox != m) || !e)
935 return -1;
936
938 {
939 if (e->deleted == edata->deleted)
940 e->changed = false;
941 return 0;
942 }
943
944 buf_printf(cmd, "UID STORE %u", edata->uid);
945
946 struct Buffer *flags = buf_pool_get();
947
948 set_flag(m, MUTT_ACL_SEEN, e->read, "\\Seen ", flags);
949 set_flag(m, MUTT_ACL_WRITE, e->old, "Old ", flags);
950 set_flag(m, MUTT_ACL_WRITE, e->flagged, "\\Flagged ", flags);
951 set_flag(m, MUTT_ACL_WRITE, e->replied, "\\Answered ", flags);
952 set_flag(m, MUTT_ACL_DELETE, edata->deleted, "\\Deleted ", flags);
953
954 if (m->rights & MUTT_ACL_WRITE)
955 {
956 /* restore system flags */
957 if (edata->flags_system)
958 buf_addstr(flags, edata->flags_system);
959
960 /* set custom flags */
961 struct Buffer *tags = buf_pool_get();
963 if (!buf_is_empty(tags))
964 buf_addstr(flags, buf_string(tags));
965 buf_pool_release(&tags);
966 }
967
969 buf_fix_dptr(flags);
970
971 /* UW-IMAP is OK with null flags, Cyrus isn't. The only solution is to
972 * explicitly revoke all system flags (if we have permission) */
973 if (buf_is_empty(flags))
974 {
975 set_flag(m, MUTT_ACL_SEEN, true, "\\Seen ", flags);
976 set_flag(m, MUTT_ACL_WRITE, true, "Old ", flags);
977 set_flag(m, MUTT_ACL_WRITE, true, "\\Flagged ", flags);
978 set_flag(m, MUTT_ACL_WRITE, true, "\\Answered ", flags);
979 set_flag(m, MUTT_ACL_DELETE, !edata->deleted, "\\Deleted ", flags);
980
981 /* erase custom flags */
982 if ((m->rights & MUTT_ACL_WRITE) && edata->flags_remote)
983 buf_addstr(flags, edata->flags_remote);
984
986 buf_fix_dptr(flags);
987
988 buf_addstr(cmd, " -FLAGS.SILENT (");
989 }
990 else
991 {
992 buf_addstr(cmd, " FLAGS.SILENT (");
993 }
994
995 buf_addstr(cmd, buf_string(flags));
996 buf_addstr(cmd, ")");
997
998 int rc = -1;
999
1000 /* after all this it's still possible to have no flags, if you
1001 * have no ACL rights */
1002 if (!buf_is_empty(flags) &&
1004 err_continue && (*err_continue != MUTT_YES))
1005 {
1006 *err_continue = imap_continue("imap_sync_message: STORE failed", adata->buf);
1007 if (*err_continue != MUTT_YES)
1008 goto done;
1009 }
1010
1011 /* server have now the updated flags */
1012 FREE(&edata->flags_remote);
1013 struct Buffer *flags_remote = buf_pool_get();
1014 driver_tags_get_with_hidden(&e->tags, flags_remote);
1015 edata->flags_remote = buf_strdup(flags_remote);
1016 buf_pool_release(&flags_remote);
1017
1018 if (e->deleted == edata->deleted)
1019 e->changed = false;
1020
1021 rc = 0;
1022
1023done:
1024 buf_pool_release(&flags);
1025 return rc;
1026}
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:648
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:186
static bool compare_flags_for_copy(struct Email *e)
Compare local flags against the server.
Definition: imap.c:202
void mutt_str_remove_trailing_ws(char *s)
Trim trailing whitespace from a string.
Definition: string.c:564
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 1034 of file imap.c.

1035{
1036 if (!m || !m->account)
1037 return MX_STATUS_ERROR;
1038
1040 struct ImapMboxData *mdata = imap_mdata_get(m);
1041
1042 /* overload keyboard timeout to avoid many mailbox checks in a row.
1043 * Most users don't like having to wait exactly when they press a key. */
1044 int rc = 0;
1045
1046 /* try IDLE first, unless force is set */
1047 const bool c_imap_idle = cs_subset_bool(NeoMutt->sub, "imap_idle");
1048 const short c_imap_keep_alive = cs_subset_number(NeoMutt->sub, "imap_keep_alive");
1049 if (!force && c_imap_idle && (adata->capabilities & IMAP_CAP_IDLE) &&
1050 ((adata->state != IMAP_IDLE) || (mutt_date_now() >= adata->lastread + c_imap_keep_alive)))
1051 {
1052 if (imap_cmd_idle(adata) < 0)
1053 return MX_STATUS_ERROR;
1054 }
1055 if (adata->state == IMAP_IDLE)
1056 {
1057 while ((rc = mutt_socket_poll(adata->conn, 0)) > 0)
1058 {
1059 if (imap_cmd_step(adata) != IMAP_RES_CONTINUE)
1060 {
1061 mutt_debug(LL_DEBUG1, "Error reading IDLE response\n");
1062 return MX_STATUS_ERROR;
1063 }
1064 }
1065 if (rc < 0)
1066 {
1067 mutt_debug(LL_DEBUG1, "Poll failed, disabling IDLE\n");
1068 adata->capabilities &= ~IMAP_CAP_IDLE; // Clear the flag
1069 }
1070 }
1071
1072 const short c_timeout = cs_subset_number(NeoMutt->sub, "timeout");
1073 if ((force || ((adata->state != IMAP_IDLE) && (mutt_date_now() >= adata->lastread + c_timeout))) &&
1074 (imap_exec(adata, "NOOP", IMAP_CMD_POLL) != IMAP_EXEC_SUCCESS))
1075 {
1076 return MX_STATUS_ERROR;
1077 }
1078
1079 /* We call this even when we haven't run NOOP in case we have pending
1080 * changes to process, since we can reopen here. */
1081 imap_cmd_finish(adata);
1082
1083 enum MxStatus check = MX_STATUS_OK;
1084 if (mdata->check_status & IMAP_EXPUNGE_PENDING)
1085 check = MX_STATUS_REOPENED;
1086 else if (mdata->check_status & IMAP_NEWMAIL_PENDING)
1087 check = MX_STATUS_NEW_MAIL;
1088 else if (mdata->check_status & IMAP_FLAGS_PENDING)
1089 check = MX_STATUS_FLAGS;
1090 else if (rc < 0)
1091 check = MX_STATUS_ERROR;
1092
1093 mdata->check_status = IMAP_OPEN_NO_FLAGS;
1094
1095 if (force)
1096 m->last_checked = 0; // force a check on the next mx_mbox_check() call
1097 return check;
1098}
int imap_cmd_idle(struct ImapAccountData *adata)
Enter the IDLE state.
Definition: command.c:1434
void imap_cmd_finish(struct ImapAccountData *adata)
Attempt to perform cleanup.
Definition: command.c:1367
#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:456
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 1107 of file imap.c.

1108{
1109 char *uidvalidity_flag = NULL;
1110 char cmd[2048] = { 0 };
1111
1112 if (!adata || !mdata)
1113 return -1;
1114
1115 /* Don't issue STATUS on the selected mailbox, it will be NOOPed or
1116 * IDLEd elsewhere.
1117 * adata->mailbox may be NULL for connections other than the current
1118 * mailbox's. */
1119 if (adata->mailbox && (adata->mailbox->mdata == mdata))
1120 {
1121 adata->mailbox->has_new = false;
1122 return mdata->messages;
1123 }
1124
1125 if (adata->mailbox && !adata->mailbox->poll_new_mail)
1126 return mdata->messages;
1127
1128 if (adata->capabilities & IMAP_CAP_IMAP4REV1)
1129 {
1130 uidvalidity_flag = "UIDVALIDITY";
1131 }
1132 else if (adata->capabilities & IMAP_CAP_STATUS)
1133 {
1134 uidvalidity_flag = "UID-VALIDITY";
1135 }
1136 else
1137 {
1138 mutt_debug(LL_DEBUG2, "Server doesn't support STATUS\n");
1139 return -1;
1140 }
1141
1142 snprintf(cmd, sizeof(cmd), "STATUS %s (UIDNEXT %s UNSEEN RECENT MESSAGES)",
1143 mdata->munge_name, uidvalidity_flag);
1144
1145 int rc = imap_exec(adata, cmd, queue ? IMAP_CMD_QUEUE : IMAP_CMD_POLL);
1146 if (rc != IMAP_EXEC_SUCCESS)
1147 {
1148 mutt_debug(LL_DEBUG1, "Error queueing command\n");
1149 return rc;
1150 }
1151 return mdata->messages;
1152}
#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 1174 of file imap.c.

1175{
1176 struct Mailbox *m = mx_mbox_find2(path);
1177
1178 const bool is_temp = !m;
1179 if (is_temp)
1180 {
1181 m = mx_path_resolve(path);
1182 if (!mx_mbox_ac_link(m))
1183 {
1184 mailbox_free(&m);
1185 return 0;
1186 }
1187 }
1188
1189 int rc = imap_mailbox_status(m, queue);
1190
1191 if (is_temp)
1192 {
1193 mx_ac_remove(m, false);
1194 mailbox_free(&m);
1195 }
1196
1197 return rc;
1198}
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:1209
int mx_ac_remove(struct Mailbox *m, bool keep_account)
Remove a Mailbox from an Account and delete Account if empty.
Definition: mx.c:1745
struct Mailbox * mx_mbox_find2(const char *path)
Find a Mailbox on an Account.
Definition: mx.c:1610
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:1641
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 1209 of file imap.c.

1210{
1212 struct ImapMboxData *mdata = imap_mdata_get(m);
1213 if (!adata || !mdata)
1214 return -1;
1215 return imap_status(adata, mdata, queue);
1216}
static int imap_status(struct ImapAccountData *adata, struct ImapMboxData *mdata, bool queue)
Refresh the number of total and new messages.
Definition: imap.c:1107
+ 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 1225 of file imap.c.

1226{
1227 struct ImapAccountData *adata = NULL;
1228 struct ImapMboxData *mdata = NULL;
1229
1230 if (imap_adata_find(path, &adata, &mdata) < 0)
1231 return -1;
1232
1233 if (subscribe)
1234 mutt_message(_("Subscribing to %s..."), mdata->name);
1235 else
1236 mutt_message(_("Unsubscribing from %s..."), mdata->name);
1237
1238 char buf[2048] = { 0 };
1239 snprintf(buf, sizeof(buf), "%sSUBSCRIBE %s", subscribe ? "" : "UN", mdata->munge_name);
1240
1241 if (imap_exec(adata, buf, IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS)
1242 {
1243 imap_mdata_free((void *) &mdata);
1244 return -1;
1245 }
1246
1247 const bool c_imap_check_subscribed = cs_subset_bool(NeoMutt->sub, "imap_check_subscribed");
1248 if (c_imap_check_subscribed)
1249 {
1250 char mbox[1024] = { 0 };
1251 size_t len = snprintf(mbox, sizeof(mbox), "%smailboxes ", subscribe ? "" : "un");
1252 imap_quote_string(mbox + len, sizeof(mbox) - len, path, true);
1253 struct Buffer *err = buf_pool_get();
1254 if (parse_rc_line(mbox, err))
1255 mutt_debug(LL_DEBUG1, "Error adding subscribed mailbox: %s\n", buf_string(err));
1256 buf_pool_release(&err);
1257 }
1258
1259 if (subscribe)
1260 mutt_message(_("Subscribed to %s"), mdata->name);
1261 else
1262 mutt_message(_("Unsubscribed from %s"), mdata->name);
1263 imap_mdata_free((void *) &mdata);
1264 return 0;
1265}
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:885
int imap_adata_find(const char *path, struct ImapAccountData **adata, struct ImapMboxData **mdata)
Find the Account data for this path.
Definition: util.c:71
enum CommandResult parse_rc_line(const char *line, struct Buffer *err)
Parse a line of user config.
Definition: rc.c:109
char * name
Mailbox name.
Definition: mdata.h: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 1277 of file imap.c.

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

1359{
1360 char prompt[1024] = { 0 };
1361 int rc = -1;
1362 bool triedcreate = false;
1363 enum QuadOption err_continue = MUTT_NO;
1364
1366 struct ImapAccountData *dest_adata = NULL;
1367 struct ImapMboxData *dest_mdata = NULL;
1368
1369 if (imap_adata_find(dest, &dest_adata, &dest_mdata) < 0)
1370 return -1;
1371
1372 struct Buffer *sync_cmd = buf_pool_get();
1373
1374 /* check that the save-to folder is in the same account */
1375 if (!imap_account_match(&(adata->conn->account), &(dest_adata->conn->account)))
1376 {
1377 mutt_debug(LL_DEBUG3, "%s not same server as %s\n", dest, mailbox_path(m));
1378 goto out;
1379 }
1380
1381 for (int i = 0; i < m->msg_count; i++)
1382 {
1383 struct Email *e = m->emails[i];
1384 if (!e)
1385 break;
1386 if (e->active && e->changed && e->deleted && !e->purge)
1387 {
1388 rc = imap_sync_message_for_copy(m, e, sync_cmd, &err_continue);
1389 if (rc < 0)
1390 {
1391 mutt_debug(LL_DEBUG1, "could not sync\n");
1392 goto out;
1393 }
1394 }
1395 }
1396
1397 /* loop in case of TRYCREATE */
1398 do
1399 {
1400 struct UidArray uida = ARRAY_HEAD_INITIALIZER;
1401 select_email_uids(m->emails, m->msg_count, MUTT_TRASH, false, false, &uida);
1402 ARRAY_SORT(&uida, imap_sort_uid, NULL);
1403 rc = imap_exec_msg_set(adata, "UID COPY", dest_mdata->munge_name, &uida);
1404 if (rc == 0)
1405 {
1406 mutt_debug(LL_DEBUG1, "No messages to trash\n");
1407 rc = -1;
1408 goto out;
1409 }
1410 else if (rc < 0)
1411 {
1412 mutt_debug(LL_DEBUG1, "could not queue copy\n");
1413 goto out;
1414 }
1415 else if (m->verbose)
1416 {
1417 mutt_message(ngettext("Copying %d message to %s...", "Copying %d messages to %s...", rc),
1418 rc, dest_mdata->name);
1419 }
1420 ARRAY_FREE(&uida);
1421
1422 /* let's get it on */
1423 rc = imap_exec(adata, NULL, IMAP_CMD_NO_FLAGS);
1424 if (rc == IMAP_EXEC_ERROR)
1425 {
1426 if (triedcreate)
1427 {
1428 mutt_debug(LL_DEBUG1, "Already tried to create mailbox %s\n", dest_mdata->name);
1429 break;
1430 }
1431 /* bail out if command failed for reasons other than nonexistent target */
1432 if (!mutt_istr_startswith(imap_get_qualifier(adata->buf), "[TRYCREATE]"))
1433 break;
1434 mutt_debug(LL_DEBUG3, "server suggests TRYCREATE\n");
1435 snprintf(prompt, sizeof(prompt), _("Create %s?"), dest_mdata->name);
1436 const bool c_confirm_create = cs_subset_bool(NeoMutt->sub, "confirm_create");
1437 if (c_confirm_create &&
1438 (query_yesorno_help(prompt, MUTT_YES, NeoMutt->sub, "confirm_create") != MUTT_YES))
1439 {
1441 goto out;
1442 }
1443 if (imap_create_mailbox(adata, dest_mdata->name) < 0)
1444 break;
1445 triedcreate = true;
1446 }
1447 } while (rc == IMAP_EXEC_ERROR);
1448
1449 if (rc != IMAP_EXEC_SUCCESS)
1450 {
1451 imap_error("imap_fast_trash", adata->buf);
1452 goto out;
1453 }
1454
1455 rc = IMAP_EXEC_SUCCESS;
1456
1457out:
1458 buf_pool_release(&sync_cmd);
1459 imap_mdata_free((void *) &dest_mdata);
1460
1461 return ((rc == IMAP_EXEC_SUCCESS) ? 0 : -1);
1462}
#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:1092
char * imap_get_qualifier(char *buf)
Get the qualifier from a tagged response.
Definition: util.c:806
int imap_create_mailbox(struct ImapAccountData *adata, const char *mailbox)
Create a new mailbox.
Definition: imap.c:435
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:928
@ 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:354
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 1473 of file imap.c.

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

1749{
1751 struct ImapMboxData *mdata = imap_mdata_get(m);
1752 if (!adata || !mdata)
1753 return;
1754
1755 const char *condstore = NULL;
1756#ifdef USE_HCACHE
1757 const bool c_imap_condstore = cs_subset_bool(NeoMutt->sub, "imap_condstore");
1758 if ((adata->capabilities & IMAP_CAP_CONDSTORE) && c_imap_condstore)
1759 condstore = " (CONDSTORE)";
1760 else
1761#endif
1762 condstore = "";
1763
1764 char buf[PATH_MAX] = { 0 };
1765 snprintf(buf, sizeof(buf), "%s %s%s", m->readonly ? "EXAMINE" : "SELECT",
1766 mdata->munge_name, condstore);
1767
1768 adata->state = IMAP_SELECTED;
1769
1770 imap_cmd_start(adata, buf);
1771}
#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 1781 of file imap.c.

1782{
1783 if (!adata)
1784 return -1;
1785
1786 if (adata->state == IMAP_DISCONNECTED)
1787 {
1788 buf_reset(&adata->cmdbuf); // purge outstanding queued commands
1789 imap_open_connection(adata);
1790 }
1791 if (adata->state == IMAP_CONNECTED)
1792 {
1794 {
1795 adata->state = IMAP_AUTHENTICATED;
1796 FREE(&adata->capstr);
1797 if (adata->conn->ssf != 0)
1798 {
1799 mutt_debug(LL_DEBUG2, "Communication encrypted at %d bits\n",
1800 adata->conn->ssf);
1801 }
1802 }
1803 else
1804 {
1806 }
1807 }
1808 if (adata->state == IMAP_AUTHENTICATED)
1809 {
1810 /* capabilities may have changed */
1811 imap_exec(adata, "CAPABILITY", IMAP_CMD_PASS);
1812
1813#ifdef USE_ZLIB
1814 /* RFC4978 */
1815 const bool c_imap_deflate = cs_subset_bool(NeoMutt->sub, "imap_deflate");
1816 if ((adata->capabilities & IMAP_CAP_COMPRESS) && c_imap_deflate &&
1817 (imap_exec(adata, "COMPRESS DEFLATE", IMAP_CMD_PASS) == IMAP_EXEC_SUCCESS))
1818 {
1819 mutt_debug(LL_DEBUG2, "IMAP compression is enabled on connection to %s\n",
1820 adata->conn->account.host);
1821 mutt_zstrm_wrap_conn(adata->conn);
1822 }
1823#endif
1824
1825 /* enable RFC2971, if the server supports that */
1826 const bool c_imap_send_id = cs_subset_bool(NeoMutt->sub, "imap_send_id");
1827 if (c_imap_send_id && (adata->capabilities & IMAP_CAP_ID))
1828 {
1829 imap_exec(adata, "ID (\"name\" \"NeoMutt\" \"version\" \"" PACKAGE_VERSION "\")",
1831 }
1832
1833 /* enable RFC6855, if the server supports that */
1834 const bool c_imap_rfc5161 = cs_subset_bool(NeoMutt->sub, "imap_rfc5161");
1835 if (c_imap_rfc5161 && (adata->capabilities & IMAP_CAP_ENABLE))
1836 imap_exec(adata, "ENABLE UTF8=ACCEPT", IMAP_CMD_QUEUE);
1837
1838 /* enable QRESYNC. Advertising QRESYNC also means CONDSTORE
1839 * is supported (even if not advertised), so flip that bit. */
1840 if (adata->capabilities & IMAP_CAP_QRESYNC)
1841 {
1843 const bool c_imap_qresync = cs_subset_bool(NeoMutt->sub, "imap_qresync");
1844 if (c_imap_rfc5161 && c_imap_qresync)
1845 imap_exec(adata, "ENABLE QRESYNC", IMAP_CMD_QUEUE);
1846 }
1847
1848 /* get root delimiter, '/' as default */
1849 adata->delim = '/';
1850 imap_exec(adata, "LIST \"\" \"\"", IMAP_CMD_QUEUE);
1851
1852 /* we may need the root delimiter before we open a mailbox */
1853 imap_exec(adata, NULL, IMAP_CMD_NO_FLAGS);
1854
1855 /* select the mailbox that used to be open before disconnect */
1856 if (adata->mailbox)
1857 {
1858 imap_mbox_select(adata->mailbox);
1859 }
1860 }
1861
1862 if (adata->state < IMAP_AUTHENTICATED)
1863 return -1;
1864
1865 return 0;
1866}
@ 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.
Definition: connaccount.c:178
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:743
static void imap_mbox_select(struct Mailbox *m)
Select a Mailbox.
Definition: imap.c:1748
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 },
{ "unsubscribe-from", parse_unsubscribe_from, 0 },
{ NULL, NULL, 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:1590
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:1252

Imap Commands.

Definition at line 84 of file imap.c.