NeoMutt  2024-11-14-138-ge5ca67
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 <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}
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:85
#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 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: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:1303
#define IMAP_CMD_NO_FLAGS
No flags are set.
Definition: private.h:71
#define IMAP_CAP_IMAP4
Server supports IMAP4.
Definition: private.h:121
#define IMAP_CAP_IMAP4REV1
Server supports IMAP4rev1.
Definition: private.h:122
@ IMAP_EXEC_SUCCESS
Imap command executed or queued successfully.
Definition: private.h:82
void imap_error(const char *where, const char *msg)
Show an error and abort.
Definition: util.c:660
#define _(a)
Definition: message.h:28
ImapCapFlags capabilities
Capability flags.
Definition: adata.h:55
char * buf
Definition: adata.h:59
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ get_flags()

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

Make a simple list out of a FLAGS response.

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

return stream following FLAGS response

Definition at line 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 != ')') && !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}
#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: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: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 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:873
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: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 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:425
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:581
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:168
size_t neomutt_mailboxlist_get_all(struct MailboxList *head, struct NeoMutt *n, enum MailboxType type)
Get a List of all Mailboxes.
Definition: neomutt.c:191
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:743
#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:42
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 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:1266
void imap_munge_mbox_name(bool unicode, char *dest, size_t dlen, const char *src)
Quote awkward characters in a mailbox name.
Definition: util.c:960
bool unicode
If true, we can send UTF-8, and the server will use UTF8 rather than mUTF7.
Definition: adata.h:62
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_access()

int imap_access ( const char *  path)

Check permissions on an IMAP mailbox with a new connection.

Parameters
pathMailbox path
Return values
0Success
<0Failure

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

Definition at line 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:1171
+ 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: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 526 of file imap.c.

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

554{
555 struct Account *np = NULL;
556 TAILQ_FOREACH(np, &NeoMutt->accounts, entries)
557 {
558 if (np->type != MUTT_IMAP)
559 continue;
560
561 struct ImapAccountData *adata = np->adata;
562 if (!adata)
563 continue;
564
565 struct Connection *conn = adata->conn;
566 if (!conn || (conn->fd < 0))
567 continue;
568
569 mutt_message(_("Closing connection to %s..."), conn->account.host);
570 imap_logout(np->adata);
572 }
573}
@ 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: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:47
+ 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 589 of file imap.c.

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

648{
649 struct ImapMboxData *mdata = imap_mdata_get(m);
651
652 if (!mdata || !edata)
653 return;
654
655 imap_msn_remove(&mdata->msn, edata->msn - 1);
656 edata->msn = 0;
657}
void imap_msn_remove(struct MSNArray *msn, size_t 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 668 of file imap.c.

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

◆ imap_open_connection()

int imap_open_connection ( struct ImapAccountData adata)

Open an IMAP connection.

Parameters
adataImap Account data
Return values
0Success
-1Failure

Definition at line 740 of file imap.c.

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

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

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

927{
930
931 if (!adata || (adata->mailbox != m) || !e)
932 return -1;
933
935 {
936 if (e->deleted == edata->deleted)
937 e->changed = false;
938 return 0;
939 }
940
941 buf_printf(cmd, "UID STORE %u", edata->uid);
942
943 struct Buffer *flags = buf_pool_get();
944
945 set_flag(m, MUTT_ACL_SEEN, e->read, "\\Seen ", flags);
946 set_flag(m, MUTT_ACL_WRITE, e->old, "Old ", flags);
947 set_flag(m, MUTT_ACL_WRITE, e->flagged, "\\Flagged ", flags);
948 set_flag(m, MUTT_ACL_WRITE, e->replied, "\\Answered ", flags);
949 set_flag(m, MUTT_ACL_DELETE, edata->deleted, "\\Deleted ", flags);
950
951 if (m->rights & MUTT_ACL_WRITE)
952 {
953 /* restore system flags */
954 if (edata->flags_system)
955 buf_addstr(flags, edata->flags_system);
956
957 /* set custom flags */
958 struct Buffer *tags = buf_pool_get();
960 if (!buf_is_empty(tags))
961 buf_addstr(flags, buf_string(tags));
962 buf_pool_release(&tags);
963 }
964
966 buf_fix_dptr(flags);
967
968 /* UW-IMAP is OK with null flags, Cyrus isn't. The only solution is to
969 * explicitly revoke all system flags (if we have permission) */
970 if (buf_is_empty(flags))
971 {
972 set_flag(m, MUTT_ACL_SEEN, true, "\\Seen ", flags);
973 set_flag(m, MUTT_ACL_WRITE, true, "Old ", flags);
974 set_flag(m, MUTT_ACL_WRITE, true, "\\Flagged ", flags);
975 set_flag(m, MUTT_ACL_WRITE, true, "\\Answered ", flags);
976 set_flag(m, MUTT_ACL_DELETE, !edata->deleted, "\\Deleted ", flags);
977
978 /* erase custom flags */
979 if ((m->rights & MUTT_ACL_WRITE) && edata->flags_remote)
980 buf_addstr(flags, edata->flags_remote);
981
983 buf_fix_dptr(flags);
984
985 buf_addstr(cmd, " -FLAGS.SILENT (");
986 }
987 else
988 {
989 buf_addstr(cmd, " FLAGS.SILENT (");
990 }
991
992 buf_addstr(cmd, buf_string(flags));
993 buf_addstr(cmd, ")");
994
995 int rc = -1;
996
997 /* after all this it's still possible to have no flags, if you
998 * have no ACL rights */
999 if (!buf_is_empty(flags) &&
1001 err_continue && (*err_continue != MUTT_YES))
1002 {
1003 *err_continue = imap_continue("imap_sync_message: STORE failed", adata->buf);
1004 if (*err_continue != MUTT_YES)
1005 goto done;
1006 }
1007
1008 /* server have now the updated flags */
1009 FREE(&edata->flags_remote);
1010 struct Buffer *flags_remote = buf_pool_get();
1011 driver_tags_get_with_hidden(&e->tags, flags_remote);
1012 edata->flags_remote = buf_strdup(flags_remote);
1013 buf_pool_release(&flags_remote);
1014
1015 if (e->deleted == edata->deleted)
1016 e->changed = false;
1017
1018 rc = 0;
1019
1020done:
1021 buf_pool_release(&flags);
1022 return rc;
1023}
bool buf_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:291
char * buf_strdup(const struct Buffer *buf)
Copy a Buffer's string.
Definition: buffer.c:571
#define MUTT_ACL_DELETE
Delete a message.
Definition: mailbox.h:63
#define MUTT_ACL_SEEN
Change the 'seen' status of a message.
Definition: mailbox.h:70
enum QuadOption imap_continue(const char *msg, const char *resp)
Display a message and ask the user if they want to go on.
Definition: util.c:649
static void set_flag(struct Mailbox *m, AclFlags aclflag, bool flag, const char *str, struct Buffer *flags)
Append str to flags if we currently have permission according to aclflag.
Definition: imap.c: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:565
struct TagList tags
For drivers that support server tagging.
Definition: email.h:72
struct Mailbox * mailbox
Current selected mailbox.
Definition: adata.h:76
char * flags_remote
Definition: edata.h:49
bool deleted
Email has been deleted.
Definition: edata.h:39
char * flags_system
Definition: edata.h:48
void driver_tags_get_with_hidden(struct TagList *tl, struct Buffer *tags)
Get all tags, also hidden ones, separated by space.
Definition: tags.c:174
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_check_mailbox()

enum MxStatus imap_check_mailbox ( struct Mailbox m,
bool  force 
)

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

Parameters
mMailbox
forceDon't wait
Return values
numMxStatus

Definition at line 1031 of file imap.c.

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

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

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

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

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

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

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

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

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

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

Imap Commands.

Definition at line 85 of file imap.c.