NeoMutt  2024-04-16-36-g75b6fb
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, 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 (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_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 96 of file imap.c.

97{
99}
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:86
#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 107 of file imap.c.

108{
109 if (imap_exec(adata, "CAPABILITY", IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS)
110 {
111 imap_error("check_capabilities", adata->buf);
112 return -1;
113 }
114
115 if (!((adata->capabilities & IMAP_CAP_IMAP4) || (adata->capabilities & IMAP_CAP_IMAP4REV1)))
116 {
117 mutt_error(_("This IMAP server is ancient. NeoMutt does not work with it."));
118 return -1;
119 }
120
121 return 0;
122}
#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:657
#define _(a)
Definition: message.h:28
ImapCapFlags capabilities
Capability flags.
Definition: adata.h:55
char * buf
Definition: adata.h:59
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ get_flags()

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

Make a simple list out of a FLAGS response.

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

return stream following FLAGS response

Definition at line 133 of file imap.c.

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

189{
190 if (m->rights & aclflag)
191 if (flag && imap_has_flag(&imap_mdata_get(m)->flags, str))
192 buf_addstr(flags, str);
193}
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:225
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:874
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 203 of file imap.c.

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

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

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

349{
350 size_t pos = start;
351
352 size_t len = buf_len(buf);
353 while ((pos < len) && buf->data[pos] && (buf->data[pos] == src[pos]))
354 pos++;
355 buf->data[pos] = '\0';
356
357 buf_fix_dptr(buf);
358
359 return pos;
360}
size_t buf_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition: buffer.c:490
void buf_fix_dptr(struct Buffer *buf)
Move the dptr to end of the Buffer.
Definition: buffer.c:181
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 371 of file imap.c.

372{
373 int rc = -1;
374 size_t matchlen;
375
376 matchlen = buf_len(buf);
377 struct MailboxList ml = STAILQ_HEAD_INITIALIZER(ml);
379 struct MailboxNode *np = NULL;
380 STAILQ_FOREACH(np, &ml, entries)
381 {
383 continue;
384
385 if (rc)
386 {
388 rc = 0;
389 }
390 else
391 {
392 longest_common_prefix(buf, mailbox_path(np->mailbox), matchlen);
393 }
394 }
396
397#if 0
398 TAILQ_FOREACH(conn, mutt_socket_head(), entries)
399 {
400 struct Url url = { 0 };
401 char urlstr[1024] = { 0 };
402
403 if (conn->account.type != MUTT_ACCT_TYPE_IMAP)
404 continue;
405
406 mutt_account_tourl(&conn->account, &url);
407 /* FIXME: how to handle multiple users on the same host? */
408 url.user = NULL;
409 url.path = NULL;
410 url_tostring(&url, urlstr, sizeof(urlstr), U_NO_FLAGS);
411 if (mutt_strn_equal(buf, urlstr, matchlen))
412 {
413 if (rc)
414 {
415 mutt_str_copy(buf, urlstr, buflen);
416 rc = 0;
417 }
418 else
419 {
420 longest_common_prefix(buf, urlstr, matchlen);
421 }
422 }
423 }
424#endif
425
426 return rc;
427}
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:394
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:348
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:419
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:230
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:575
void mutt_account_tourl(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:38
void neomutt_mailboxlist_clear(struct MailboxList *ml)
Free a Mailbox List.
Definition: neomutt.c:163
size_t neomutt_mailboxlist_get_all(struct MailboxList *head, struct NeoMutt *n, enum MailboxType type)
Get a List of all Mailboxes.
Definition: neomutt.c:186
#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:166
struct Mailbox * mailbox
Mailbox in the list.
Definition: mailbox.h:167
Container for Accounts, Notifications.
Definition: neomutt.h:41
A parsed URL proto://user:password@host:port/path?a=1&b=2
Definition: url.h:69
char * user
Username.
Definition: url.h:71
char * path
Path.
Definition: url.h:75
int url_tostring(const struct Url *url, char *dest, size_t len, uint8_t flags)
Output the URL string for a given Url object.
Definition: url.c:423
#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 436 of file imap.c.

437{
438 char buf[2048] = { 0 };
439 char mbox[1024] = { 0 };
440
441 imap_munge_mbox_name(adata->unicode, mbox, sizeof(mbox), mailbox);
442 snprintf(buf, sizeof(buf), "CREATE %s", mbox);
443
445 {
446 mutt_error(_("CREATE failed: %s"), imap_cmd_trailer(adata));
447 return -1;
448 }
449
450 return 0;
451}
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:921
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 463 of file imap.c.

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

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

506{
507 char buf[PATH_MAX + 7];
508 char mbox[PATH_MAX] = { 0 };
509 struct Url *url = url_parse(path);
510 if (!url)
511 return -1;
512
514 imap_munge_mbox_name(adata->unicode, mbox, sizeof(mbox), url->path);
515 url_free(&url);
516 snprintf(buf, sizeof(buf), "DELETE %s", mbox);
518 return -1;
519
520 return 0;
521}
#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:239
void url_free(struct Url **ptr)
Free the contents of a URL.
Definition: url.c:124
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_logout()

static void imap_logout ( struct ImapAccountData adata)
static

Gracefully log out of server.

Parameters
adataImap Account data

Definition at line 527 of file imap.c.

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

555{
556 struct Account *np = NULL;
557 TAILQ_FOREACH(np, &NeoMutt->accounts, entries)
558 {
559 if (np->type != MUTT_IMAP)
560 continue;
561
562 struct ImapAccountData *adata = np->adata;
563 if (!adata)
564 continue;
565
566 struct Connection *conn = adata->conn;
567 if (!conn || (conn->fd < 0))
568 continue;
569
570 mutt_message(_("Closing connection to %s..."), conn->account.host);
571 imap_logout(np->adata);
573 }
574}
@ MUTT_IMAP
'IMAP' Mailbox type
Definition: mailbox.h:50
#define mutt_message(...)
Definition: logging2.h:91
static void imap_logout(struct ImapAccountData *adata)
Gracefully log out of server.
Definition: imap.c:527
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: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 590 of file imap.c.

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

649{
650 struct ImapMboxData *mdata = imap_mdata_get(m);
652
653 if (!mdata || !edata)
654 return;
655
656 imap_msn_remove(&mdata->msn, edata->msn - 1);
657 edata->msn = 0;
658}
void imap_msn_remove(struct MSNArray *msn, size_t idx)
Remove an entry from the cache.
Definition: msn.c:114
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 669 of file imap.c.

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

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

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

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

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

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

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

1173{
1174 struct Mailbox *m = mx_mbox_find2(path);
1175
1176 const bool is_temp = !m;
1177 if (is_temp)
1178 {
1179 m = mx_path_resolve(path);
1180 if (!mx_mbox_ac_link(m))
1181 {
1182 mailbox_free(&m);
1183 return 0;
1184 }
1185 }
1186
1187 int rc = imap_mailbox_status(m, queue);
1188
1189 if (is_temp)
1190 {
1191 mx_ac_remove(m, false);
1192 mailbox_free(&m);
1193 }
1194
1195 return rc;
1196}
void mailbox_free(struct Mailbox **ptr)
Free a Mailbox.
Definition: mailbox.c:90
int imap_mailbox_status(struct Mailbox *m, bool queue)
Refresh the number of total and new messages.
Definition: imap.c:1207
int mx_ac_remove(struct Mailbox *m, bool keep_account)
Remove a Mailbox from an Account and delete Account if empty.
Definition: mx.c:1738
struct Mailbox * mx_mbox_find2(const char *path)
Find a Mailbox on an Account.
Definition: mx.c:1603
bool mx_mbox_ac_link(struct Mailbox *m)
Link a Mailbox to an existing or new Account.
Definition: mx.c:249
struct Mailbox * mx_path_resolve(const char *path)
Get a Mailbox for a path.
Definition: mx.c:1634
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 1207 of file imap.c.

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

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

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

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

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

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

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

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

2386{
2387 buf_alloc(path, PATH_MAX);
2388 return imap_path_canon(path);
2389}
int imap_path_canon(struct Buffer *path)
Canonicalise a Mailbox path - Implements MxOps::path_canon() -.
Definition: imap.c:2359
+ 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:1567
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:1230

Imap Commands.

Definition at line 86 of file imap.c.