NeoMutt  2022-04-29-145-g9b6a0e
Teaching an old dog new tricks
DOXYGEN
imap.c File Reference

IMAP network mailbox. More...

#include "config.h"
#include <limits.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.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 "enter/lib.h"
#include "progress/lib.h"
#include "question/lib.h"
#include "adata.h"
#include "auth.h"
#include "command_parse.h"
#include "commands.h"
#include "edata.h"
#include "hook.h"
#include "init.h"
#include "mdata.h"
#include "msn.h"
#include "mutt_commands.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. More...
 
static int check_capabilities (struct ImapAccountData *adata)
 Make sure we can log in to this server. More...
 
static char * get_flags (struct ListHead *hflags, char *s)
 Make a simple list out of a FLAGS response. More...
 
static void set_flag (struct Mailbox *m, AclFlags aclflag, bool flag, const char *str, char *flags, size_t flsize)
 Append str to flags if we currently have permission according to aclflag. More...
 
static int make_msg_set (struct Mailbox *m, struct Buffer *buf, enum MessageType flag, bool changed, bool invert, int *pos)
 Make a message set. More...
 
static bool compare_flags_for_copy (struct Email *e)
 Compare local flags against the server. More...
 
static int sync_helper (struct Mailbox *m, AclFlags right, enum MessageType flag, const char *name)
 Sync flag changes to the server. More...
 
static size_t longest_common_prefix (char *dest, const char *src, size_t start, size_t dlen)
 Find longest prefix common to two strings. More...
 
static int complete_hosts (char *buf, size_t buflen)
 Look for completion matches for mailboxes. More...
 
int imap_create_mailbox (struct ImapAccountData *adata, const char *mailbox)
 Create a new mailbox. More...
 
int imap_access (const char *path)
 Check permissions on an IMAP mailbox with a new connection. More...
 
int imap_rename_mailbox (struct ImapAccountData *adata, char *oldname, const char *newname)
 Rename a mailbox. More...
 
int imap_delete_mailbox (struct Mailbox *m, char *path)
 Delete a mailbox. More...
 
static void imap_logout (struct ImapAccountData *adata)
 Gracefully log out of server. More...
 
void imap_logout_all (void)
 Close all open connections. More...
 
int imap_read_literal (FILE *fp, struct ImapAccountData *adata, unsigned long bytes, struct Progress *progress)
 Read bytes bytes from server into file. More...
 
void imap_notify_delete_email (struct Mailbox *m, struct Email *e)
 Inform IMAP that an Email has been deleted. More...
 
void imap_expunge_mailbox (struct Mailbox *m, bool resort)
 Purge messages from the server. More...
 
int imap_open_connection (struct ImapAccountData *adata)
 Open an IMAP connection. More...
 
void imap_close_connection (struct ImapAccountData *adata)
 Close an IMAP connection. More...
 
bool imap_has_flag (struct ListHead *flag_list, const char *flag)
 Does the flag exist in the list. More...
 
static int compare_uid (const void *a, const void *b)
 Compare two Emails by UID - Implements sort_t -. More...
 
int imap_exec_msgset (struct Mailbox *m, const char *pre, const char *post, enum MessageType flag, bool changed, bool invert)
 Prepare commands for all messages matching conditions. More...
 
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. More...
 
enum MxStatus imap_check_mailbox (struct Mailbox *m, bool force)
 Use the NOOP or IDLE command to poll for new mail. More...
 
static int imap_status (struct ImapAccountData *adata, struct ImapMboxData *mdata, bool queue)
 Refresh the number of total and new messages. More...
 
static enum MxStatus imap_mbox_check_stats (struct Mailbox *m, uint8_t flags)
 Check the Mailbox statistics - Implements MxOps::mbox_check_stats() -. More...
 
int imap_path_status (const char *path, bool queue)
 Refresh the number of total and new messages. More...
 
int imap_mailbox_status (struct Mailbox *m, bool queue)
 Refresh the number of total and new messages. More...
 
int imap_subscribe (char *path, bool subscribe)
 Subscribe to a mailbox. More...
 
int imap_complete (char *buf, size_t buflen, const char *path)
 Try to complete an IMAP folder path. More...
 
int imap_fast_trash (struct Mailbox *m, const char *dest)
 Use server COPY command to copy deleted messages to trash. More...
 
enum MxStatus imap_sync_mailbox (struct Mailbox *m, bool expunge, bool close)
 Sync all the changes to the server. More...
 
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() -. More...
 
static bool imap_ac_add (struct Account *a, struct Mailbox *m)
 Add a Mailbox to an Account - Implements MxOps::ac_add() -. More...
 
static void imap_mbox_select (struct Mailbox *m)
 Select a Mailbox. More...
 
int imap_login (struct ImapAccountData *adata)
 Open an IMAP connection. More...
 
static enum MxOpenReturns imap_mbox_open (struct Mailbox *m)
 Open a mailbox - Implements MxOps::mbox_open() -. More...
 
static bool imap_mbox_open_append (struct Mailbox *m, OpenMailboxFlags flags)
 Open a Mailbox for appending - Implements MxOps::mbox_open_append() -. More...
 
static enum MxStatus imap_mbox_check (struct Mailbox *m)
 Check for new mail - Implements MxOps::mbox_check() -. More...
 
static enum MxStatus imap_mbox_close (struct Mailbox *m)
 Close a Mailbox - Implements MxOps::mbox_close() -. More...
 
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() -. More...
 
static int imap_tags_edit (struct Mailbox *m, const char *tags, struct Buffer *buf)
 Prompt and validate new messages tags - Implements MxOps::tags_edit() -. More...
 
static int imap_tags_commit (struct Mailbox *m, struct Email *e, const char *buf)
 Save the tags to a message - Implements MxOps::tags_commit() -. More...
 
enum MailboxType imap_path_probe (const char *path, const struct stat *st)
 Is this an IMAP Mailbox? - Implements MxOps::path_probe() -. More...
 
int imap_path_canon (char *buf, size_t buflen)
 Canonicalise a Mailbox path - Implements MxOps::path_canon() -. More...
 
int imap_expand_path (struct Buffer *buf)
 Buffer wrapper around imap_path_canon() More...
 
static int imap_path_pretty (char *buf, size_t buflen, const char *folder)
 Abbreviate a Mailbox path - Implements MxOps::path_pretty() -. More...
 
static int imap_path_parent (char *buf, size_t buflen)
 Find the parent of a Mailbox path - Implements MxOps::path_parent() -. More...
 
static int imap_path_is_empty (const char *path)
 Is the mailbox empty - Implements MxOps::path_is_empty() -. More...
 

Variables

static const struct Command imap_commands []
 
struct MxOps MxImapOps
 IMAP Mailbox - Implements MxOps -. More...
 

Detailed Description

IMAP network mailbox.

Authors
  • Michael R. Elkins
  • Brandon Long
  • Brendan Cully
  • Richard Russon

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

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

Definition in file imap.c.

Function Documentation

◆ imap_init()

void imap_init ( void  )

Setup feature commands.

Definition at line 84 of file imap.c.

85 {
87 }
static const struct Command imap_commands[]
Definition: imap.c:74
#define COMMANDS_REGISTER(cmds)
Definition: mutt_commands.h:47
+ 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 95 of file imap.c.

96 {
97  if (imap_exec(adata, "CAPABILITY", IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS)
98  {
99  imap_error("check_capabilities", adata->buf);
100  return -1;
101  }
102 
103  if (!((adata->capabilities & IMAP_CAP_IMAP4) || (adata->capabilities & IMAP_CAP_IMAP4REV1)))
104  {
105  mutt_error(_("This IMAP server is ancient. NeoMutt does not work with it."));
106  return -1;
107  }
108 
109  return 0;
110 }
#define mutt_error(...)
Definition: logging.h:87
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:1247
#define IMAP_CMD_NO_FLAGS
No flags are set.
Definition: private.h:73
#define IMAP_CAP_IMAP4
Server supports IMAP4.
Definition: private.h:123
#define IMAP_CAP_IMAP4REV1
Server supports IMAP4rev1.
Definition: private.h:124
@ IMAP_EXEC_SUCCESS
Imap command executed or queued successfully.
Definition: private.h:84
void imap_error(const char *where, const char *msg)
Show an error and abort.
Definition: util.c:663
#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 121 of file imap.c.

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

◆ set_flag()

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

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

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

Definition at line 176 of file imap.c.

178 {
179  if (m->rights & aclflag)
180  if (flag && imap_has_flag(&imap_mdata_get(m)->flags, str))
181  mutt_str_cat(flags, flsize, str);
182 }
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:878
char * mutt_str_cat(char *buf, size_t buflen, const char *s)
Concatenate two strings.
Definition: string.c:265
AclFlags rights
ACL bits, see AclFlags.
Definition: mailbox.h:118
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ make_msg_set()

static int make_msg_set ( struct Mailbox m,
struct Buffer buf,
enum MessageType  flag,
bool  changed,
bool  invert,
int *  pos 
)
static

Make a message set.

Parameters
[in]mSelected Imap Mailbox
[in]bufBuffer to store message set
[in]flagFlags to match, e.g. MUTT_DELETED
[in]changedMatched messages that have been altered
[in]invertFlag matches should be inverted
[out]posCursor used for multiple calls to this function
Return values
numMessages in the set
Note
Headers must be in SORT_ORDER. See imap_exec_msgset() for args. Pos is an opaque pointer a la strtok(). It should be 0 at first call.

Definition at line 197 of file imap.c.

199 {
200  int count = 0; /* number of messages in message set */
201  unsigned int setstart = 0; /* start of current message range */
202  int n;
203  bool started = false;
204 
205  struct ImapAccountData *adata = imap_adata_get(m);
206  if (!adata || (adata->mailbox != m))
207  return -1;
208 
209  for (n = *pos; (n < m->msg_count) && (mutt_buffer_len(buf) < IMAP_MAX_CMDLEN); n++)
210  {
211  struct Email *e = m->emails[n];
212  if (!e)
213  break;
214  bool match = false; /* whether current message matches flag condition */
215  /* don't include pending expunged messages.
216  *
217  * TODO: can we unset active in cmd_parse_expunge() and
218  * cmd_parse_vanished() instead of checking for index != INT_MAX. */
219  if (e->active && (e->index != INT_MAX))
220  {
221  switch (flag)
222  {
223  case MUTT_DELETED:
224  if (e->deleted != imap_edata_get(e)->deleted)
225  match = invert ^ e->deleted;
226  break;
227  case MUTT_FLAG:
228  if (e->flagged != imap_edata_get(e)->flagged)
229  match = invert ^ e->flagged;
230  break;
231  case MUTT_OLD:
232  if (e->old != imap_edata_get(e)->old)
233  match = invert ^ e->old;
234  break;
235  case MUTT_READ:
236  if (e->read != imap_edata_get(e)->read)
237  match = invert ^ e->read;
238  break;
239  case MUTT_REPLIED:
240  if (e->replied != imap_edata_get(e)->replied)
241  match = invert ^ e->replied;
242  break;
243  case MUTT_TAG:
244  if (e->tagged)
245  match = true;
246  break;
247  case MUTT_TRASH:
248  if (e->deleted && !e->purge)
249  match = true;
250  break;
251  default:
252  break;
253  }
254  }
255 
256  if (match && (!changed || e->changed))
257  {
258  count++;
259  if (setstart == 0)
260  {
261  setstart = imap_edata_get(e)->uid;
262  if (started)
263  {
264  mutt_buffer_add_printf(buf, ",%u", imap_edata_get(e)->uid);
265  }
266  else
267  {
268  mutt_buffer_add_printf(buf, "%u", imap_edata_get(e)->uid);
269  started = true;
270  }
271  }
272  /* tie up if the last message also matches */
273  else if (n == (m->msg_count - 1))
274  mutt_buffer_add_printf(buf, ":%u", imap_edata_get(e)->uid);
275  }
276  /* End current set if message doesn't match. */
277  else if (setstart)
278  {
279  if (imap_edata_get(m->emails[n - 1])->uid > setstart)
280  mutt_buffer_add_printf(buf, ":%u", imap_edata_get(m->emails[n - 1])->uid);
281  setstart = 0;
282  }
283  }
284 
285  *pos = n;
286 
287  return count;
288 }
size_t mutt_buffer_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition: buffer.c:354
int mutt_buffer_add_printf(struct Buffer *buf, const char *fmt,...)
Format a string appending a Buffer.
Definition: buffer.c:201
struct ImapAccountData * imap_adata_get(struct Mailbox *m)
Get the Account data for this mailbox.
Definition: adata.c:89
struct ImapEmailData * imap_edata_get(struct Email *e)
Get the private data for this Email.
Definition: edata.c:65
#define IMAP_MAX_CMDLEN
Maximum length of command lines before they must be split (for lazy servers)
Definition: private.h:62
@ MUTT_TRASH
Trashed messages.
Definition: mutt.h:105
@ MUTT_READ
Messages that have been read.
Definition: mutt.h:93
@ MUTT_OLD
Old messages.
Definition: mutt.h:91
@ MUTT_TAG
Tagged messages.
Definition: mutt.h:100
@ MUTT_FLAG
Flagged messages.
Definition: mutt.h:99
@ MUTT_DELETED
Deleted messages.
Definition: mutt.h:98
@ MUTT_REPLIED
Messages that have been replied to.
Definition: mutt.h:92
void * adata
Private data (for Mailbox backends)
Definition: account.h:43
The envelope/body of an email.
Definition: email.h:37
bool read
Email is read.
Definition: email.h:48
bool purge
Skip trash folder when deleting.
Definition: email.h:77
bool active
Message is not to be removed.
Definition: email.h:74
bool old
Email is seen, but unread.
Definition: email.h:47
bool changed
Email has been edited.
Definition: email.h:75
bool flagged
Marked important?
Definition: email.h:45
bool replied
Email has been replied to.
Definition: email.h:49
bool deleted
Email is deleted.
Definition: email.h:76
int index
The absolute (unsorted) message number.
Definition: email.h:110
bool tagged
Email is tagged.
Definition: email.h:107
IMAP-specific Account data -.
Definition: adata.h:40
unsigned int uid
32-bit Message UID
Definition: edata.h:44
bool deleted
Email has been deleted.
Definition: edata.h:38
bool old
Email has been seen.
Definition: edata.h:37
bool read
Email has been read.
Definition: edata.h:36
bool flagged
Email has been flagged.
Definition: edata.h:39
bool replied
Email has been replied to.
Definition: edata.h:40
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:

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

299 {
300  struct ImapEmailData *edata = e->edata;
301 
302  if (e->read != edata->read)
303  return true;
304  if (e->old != edata->old)
305  return true;
306  if (e->flagged != edata->flagged)
307  return true;
308  if (e->replied != edata->replied)
309  return true;
310 
311  return false;
312 }
void * edata
Driver-specific data.
Definition: email.h:72
IMAP-specific Email data -.
Definition: edata.h:34
+ Here is the caller graph for this function:

◆ sync_helper()

static int sync_helper ( struct Mailbox m,
AclFlags  right,
enum MessageType  flag,
const char *  name 
)
static

Sync flag changes to the server.

Parameters
mSelected Imap Mailbox
rightACL, see AclFlags
flagNeoMutt flag, e.g. MUTT_DELETED
nameName of server flag
Return values
>=0Success, number of messages
-1Failure

Definition at line 323 of file imap.c.

325 {
326  int count = 0;
327  int rc;
328  char buf[1024];
329 
330  if (!m)
331  return -1;
332 
333  if ((m->rights & right) == 0)
334  return 0;
335 
336  if ((right == MUTT_ACL_WRITE) && !imap_has_flag(&imap_mdata_get(m)->flags, name))
337  return 0;
338 
339  snprintf(buf, sizeof(buf), "+FLAGS.SILENT (%s)", name);
340  rc = imap_exec_msgset(m, "UID STORE", buf, flag, true, false);
341  if (rc < 0)
342  return rc;
343  count += rc;
344 
345  buf[0] = '-';
346  rc = imap_exec_msgset(m, "UID STORE", buf, flag, true, true);
347  if (rc < 0)
348  return rc;
349  count += rc;
350 
351  return count;
352 }
int imap_exec_msgset(struct Mailbox *m, const char *pre, const char *post, enum MessageType flag, bool changed, bool invert)
Prepare commands for all messages matching conditions.
Definition: imap.c:930
#define MUTT_ACL_WRITE
Write to a message (for flagging or linking threads)
Definition: mailbox.h:71
+ Here is the call graph for this function:

◆ longest_common_prefix()

static size_t longest_common_prefix ( char *  dest,
const char *  src,
size_t  start,
size_t  dlen 
)
static

Find longest prefix common to two strings.

Parameters
destDestination buffer
srcSource buffer
startStarting offset into string
dlenDestination buffer length
Return values
numLength of the common string

Trim dest to the length of the longest prefix it shares with src.

Definition at line 364 of file imap.c.

365 {
366  size_t pos = start;
367 
368  while ((pos < dlen) && dest[pos] && (dest[pos] == src[pos]))
369  pos++;
370  dest[pos] = '\0';
371 
372  return pos;
373 }
+ Here is the caller graph for this function:

◆ complete_hosts()

static int complete_hosts ( char *  buf,
size_t  buflen 
)
static

Look for completion matches for mailboxes.

Parameters
bufPartial mailbox name to complete
buflenLength of buffer
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 385 of file imap.c.

386 {
387  // struct Connection *conn = NULL;
388  int rc = -1;
389  size_t matchlen;
390 
391  matchlen = mutt_str_len(buf);
392  struct MailboxList ml = STAILQ_HEAD_INITIALIZER(ml);
394  struct MailboxNode *np = NULL;
395  STAILQ_FOREACH(np, &ml, entries)
396  {
397  if (!mutt_str_startswith(mailbox_path(np->mailbox), buf))
398  continue;
399 
400  if (rc)
401  {
402  mutt_str_copy(buf, mailbox_path(np->mailbox), buflen);
403  rc = 0;
404  }
405  else
406  longest_common_prefix(buf, mailbox_path(np->mailbox), matchlen, buflen);
407  }
409 
410 #if 0
411  TAILQ_FOREACH(conn, mutt_socket_head(), entries)
412  {
413  struct Url url = { 0 };
414  char urlstr[1024];
415 
416  if (conn->account.type != MUTT_ACCT_TYPE_IMAP)
417  continue;
418 
419  mutt_account_tourl(&conn->account, &url);
420  /* FIXME: how to handle multiple users on the same host? */
421  url.user = NULL;
422  url.path = NULL;
423  url_tostring(&url, urlstr, sizeof(urlstr), U_NO_FLAGS);
424  if (mutt_strn_equal(buf, urlstr, matchlen))
425  {
426  if (rc)
427  {
428  mutt_str_copy(buf, urlstr, buflen);
429  rc = 0;
430  }
431  else
432  longest_common_prefix(buf, urlstr, matchlen, buflen);
433  }
434  }
435 #endif
436 
437  return rc;
438 }
static size_t longest_common_prefix(char *dest, const char *src, size_t start, size_t dlen)
Find longest prefix common to two strings.
Definition: imap.c:364
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox's path string.
Definition: mailbox.h:211
@ MUTT_MAILBOX_ANY
Match any Mailbox type.
Definition: mailbox.h:42
bool mutt_strn_equal(const char *a, const char *b, size_t num)
Check for equality of two strings (to a maximum), safely.
Definition: string.c:473
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:227
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:544
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:629
void mutt_account_tourl(struct ConnAccount *cac, struct Url *url)
Fill URL with info from account.
Definition: mutt_account.c:79
@ MUTT_ACCT_TYPE_IMAP
Imap Account.
Definition: mutt_account.h:37
void neomutt_mailboxlist_clear(struct MailboxList *ml)
Free a Mailbox List.
Definition: neomutt.c:141
size_t neomutt_mailboxlist_get_all(struct MailboxList *head, struct NeoMutt *n, enum MailboxType type)
Get a List of all Mailboxes.
Definition: neomutt.c:164
#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:154
struct Mailbox * mailbox
Mailbox in the list.
Definition: mailbox.h:155
Container for Accounts, Notifications.
Definition: neomutt.h:37
A parsed URL proto://user:password@host:port/path?a=1&b=2
Definition: url.h:69
char * user
Username.
Definition: url.h:71
char * path
Path.
Definition: url.h:75
int url_tostring(struct Url *url, char *dest, size_t len, uint8_t flags)
Output the URL string for a given Url object.
Definition: url.c:418
#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 447 of file imap.c.

448 {
449  char buf[2048], mbox[1024];
450 
451  imap_munge_mbox_name(adata->unicode, mbox, sizeof(mbox), mailbox);
452  snprintf(buf, sizeof(buf), "CREATE %s", mbox);
453 
454  if (imap_exec(adata, buf, IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS)
455  {
456  mutt_error(_("CREATE failed: %s"), imap_cmd_trailer(adata));
457  return -1;
458  }
459 
460  return 0;
461 }
const char * imap_cmd_trailer(struct ImapAccountData *adata)
Extra information after tagged command response if any.
Definition: command.c:1210
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:911
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 473 of file imap.c.

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

489 {
490  char oldmbox[1024];
491  char newmbox[1024];
492  int rc = 0;
493 
494  imap_munge_mbox_name(adata->unicode, oldmbox, sizeof(oldmbox), oldname);
495  imap_munge_mbox_name(adata->unicode, newmbox, sizeof(newmbox), newname);
496 
497  struct Buffer *buf = mutt_buffer_pool_get();
498  mutt_buffer_printf(buf, "RENAME %s %s", oldmbox, newmbox);
499 
501  rc = -1;
502 
504 
505  return rc;
506 }
int mutt_buffer_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:158
static const char * mutt_buffer_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:77
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
String manipulation buffer.
Definition: buffer.h:34
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_delete_mailbox()

int imap_delete_mailbox ( struct Mailbox m,
char *  path 
)

Delete a mailbox.

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

Definition at line 515 of file imap.c.

516 {
517  char buf[PATH_MAX + 7];
518  char mbox[PATH_MAX];
519  struct Url *url = url_parse(path);
520 
521  struct ImapAccountData *adata = imap_adata_get(m);
522  imap_munge_mbox_name(adata->unicode, mbox, sizeof(mbox), url->path);
523  url_free(&url);
524  snprintf(buf, sizeof(buf), "DELETE %s", mbox);
526  return -1;
527 
528  return 0;
529 }
#define PATH_MAX
Definition: mutt.h:40
struct Account * account
Account that owns this Mailbox.
Definition: mailbox.h:128
struct Url * url_parse(const char *src)
Fill in Url.
Definition: url.c:234
void url_free(struct Url **ptr)
Free the contents of a URL.
Definition: url.c:123
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_logout()

static void imap_logout ( struct ImapAccountData adata)
static

Gracefully log out of server.

Parameters
adataImap Account data

Definition at line 535 of file imap.c.

536 {
537  /* we set status here to let imap_handle_untagged know we _expect_ to
538  * receive a bye response (so it doesn't freak out and close the conn) */
539  if (adata->state == IMAP_DISCONNECTED)
540  {
541  return;
542  }
543 
544  adata->status = IMAP_BYE;
545  imap_cmd_start(adata, "LOGOUT");
546  const short c_imap_poll_timeout = cs_subset_number(NeoMutt->sub, "imap_poll_timeout");
547  if ((c_imap_poll_timeout <= 0) ||
548  (mutt_socket_poll(adata->conn, c_imap_poll_timeout) != 0))
549  {
550  while (imap_cmd_step(adata) == IMAP_RES_CONTINUE)
551  ; // do nothing
552  }
553  mutt_socket_close(adata->conn);
554  adata->state = IMAP_DISCONNECTED;
555 }
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition: helpers.c:169
int imap_cmd_start(struct ImapAccountData *adata, const char *cmdstr)
Given an IMAP command, send it to the server.
Definition: command.c:1062
int imap_cmd_step(struct ImapAccountData *adata)
Reads server responses from an IMAP command.
Definition: command.c:1076
@ IMAP_DISCONNECTED
Disconnected from server.
Definition: private.h:107
@ IMAP_BYE
Logged out from server.
Definition: private.h:98
#define IMAP_RES_CONTINUE
* ...
Definition: private.h:57
int mutt_socket_close(struct Connection *conn)
Close a socket.
Definition: socket.c:97
int mutt_socket_poll(struct Connection *conn, time_t wait_secs)
Checks whether reads would block.
Definition: socket.c:192
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:39
+ 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 562 of file imap.c.

563 {
564  struct Account *np = NULL;
565  TAILQ_FOREACH(np, &NeoMutt->accounts, entries)
566  {
567  if (np->type != MUTT_IMAP)
568  continue;
569 
570  struct ImapAccountData *adata = np->adata;
571  if (!adata)
572  continue;
573 
574  struct Connection *conn = adata->conn;
575  if (!conn || (conn->fd < 0))
576  continue;
577 
578  mutt_message(_("Closing connection to %s..."), conn->account.host);
579  imap_logout(np->adata);
581  }
582 }
#define mutt_message(...)
Definition: logging.h:86
static void imap_logout(struct ImapAccountData *adata)
Gracefully log out of server.
Definition: imap.c:535
@ MUTT_IMAP
'IMAP' Mailbox type
Definition: mailbox.h:50
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
Definition: mutt_logging.c:74
A group of associated Mailboxes.
Definition: account.h:37
enum MailboxType type
Type of Mailboxes this Account contains.
Definition: account.h:38
char host[128]
Server to login to.
Definition: connaccount.h:54
struct ConnAccount account
Account details: username, password, etc.
Definition: connection.h:50
int fd
Socket file descriptor.
Definition: connection.h:54
struct AccountList accounts
List of all Accounts.
Definition: neomutt.h:40
+ 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 598 of file imap.c.

600 {
601  char c;
602  bool r = false;
603  struct Buffer buf = { 0 }; // Do not allocate, maybe it won't be used
604 
605  const short c_debug_level = cs_subset_number(NeoMutt->sub, "debug_level");
606  if (c_debug_level >= IMAP_LOG_LTRL)
607  mutt_buffer_alloc(&buf, bytes + 10);
608 
609  mutt_debug(LL_DEBUG2, "reading %ld bytes\n", bytes);
610 
611  for (unsigned long pos = 0; pos < bytes; pos++)
612  {
613  if (mutt_socket_readchar(adata->conn, &c) != 1)
614  {
615  mutt_debug(LL_DEBUG1, "error during read, %ld bytes read\n", pos);
616  adata->status = IMAP_FATAL;
617 
618  mutt_buffer_dealloc(&buf);
619  return -1;
620  }
621 
622  if (r && (c != '\n'))
623  fputc('\r', fp);
624 
625  if (c == '\r')
626  {
627  r = true;
628  continue;
629  }
630  else
631  r = false;
632 
633  fputc(c, fp);
634 
635  if (progress && !(pos % 1024))
636  progress_update(progress, pos, -1);
637  if (c_debug_level >= IMAP_LOG_LTRL)
638  mutt_buffer_addch(&buf, c);
639  }
640 
641  if (c_debug_level >= IMAP_LOG_LTRL)
642  {
643  mutt_debug(IMAP_LOG_LTRL, "\n%s", buf.data);
644  mutt_buffer_dealloc(&buf);
645  }
646  return 0;
647 }
void mutt_buffer_alloc(struct Buffer *buf, size_t new_size)
Make sure a buffer can store at least new_size bytes.
Definition: buffer.c:265
void mutt_buffer_dealloc(struct Buffer *buf)
Release the memory allocated by a buffer.
Definition: buffer.c:292
size_t mutt_buffer_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:238
#define IMAP_LOG_LTRL
Definition: private.h:50
@ IMAP_FATAL
Unrecoverable error occurred.
Definition: private.h:97
@ LL_DEBUG2
Log at debug level 2.
Definition: logging.h:41
bool progress_update(struct Progress *progress, size_t pos, int percent)
Update the state of the progress bar.
Definition: progress.c:73
int mutt_socket_readchar(struct Connection *conn, char *c)
Simple read buffering to speed things up.
Definition: socket.c:210
char * data
Pointer to data.
Definition: buffer.h:35
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

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

655 {
656  struct ImapMboxData *mdata = imap_mdata_get(m);
657  struct ImapEmailData *edata = imap_edata_get(e);
658 
659  if (!mdata || !edata)
660  return;
661 
662  imap_msn_remove(&mdata->msn, edata->msn - 1);
663  edata->msn = 0;
664 }
void imap_msn_remove(struct MSN *msn, size_t idx)
Remove an entry from the cache.
Definition: msn.c:113
IMAP-specific Mailbox data -.
Definition: mdata.h:39
void * mdata
Driver specific data.
Definition: mailbox.h:133
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_expunge_mailbox()

void imap_expunge_mailbox ( struct Mailbox m,
bool  resort 
)

Purge messages from the server.

Parameters
mMailbox
resortTrigger a resort?

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

Definition at line 675 of file imap.c.

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

748 {
749  if (mutt_socket_open(adata->conn) < 0)
750  return -1;
751 
752  adata->state = IMAP_CONNECTED;
753 
754  if (imap_cmd_step(adata) != IMAP_RES_OK)
755  {
756  imap_close_connection(adata);
757  return -1;
758  }
759 
760  if (mutt_istr_startswith(adata->buf, "* OK"))
761  {
762  if (!mutt_istr_startswith(adata->buf, "* OK [CAPABILITY") && check_capabilities(adata))
763  {
764  goto bail;
765  }
766 #ifdef USE_SSL
767  /* Attempt STARTTLS if available and desired. */
768  const bool c_ssl_force_tls = cs_subset_bool(NeoMutt->sub, "ssl_force_tls");
769  if ((adata->conn->ssf == 0) &&
770  (c_ssl_force_tls || (adata->capabilities & IMAP_CAP_STARTTLS)))
771  {
772  enum QuadOption ans;
773 
774  const enum QuadOption c_ssl_starttls = cs_subset_quad(NeoMutt->sub, "ssl_starttls");
775  if (c_ssl_force_tls)
776  ans = MUTT_YES;
777  else if ((ans = query_quadoption(c_ssl_starttls, _("Secure connection with TLS?"))) == MUTT_ABORT)
778  {
779  goto bail;
780  }
781  if (ans == MUTT_YES)
782  {
783  enum ImapExecResult rc = imap_exec(adata, "STARTTLS", IMAP_CMD_SINGLE);
784  // Clear any data after the STARTTLS acknowledgement
785  mutt_socket_empty(adata->conn);
786 
787  if (rc == IMAP_EXEC_FATAL)
788  goto bail;
789  if (rc != IMAP_EXEC_ERROR)
790  {
791  if (mutt_ssl_starttls(adata->conn))
792  {
793  mutt_error(_("Could not negotiate TLS connection"));
794  goto bail;
795  }
796  else
797  {
798  /* RFC2595 demands we recheck CAPABILITY after TLS completes. */
799  if (imap_exec(adata, "CAPABILITY", IMAP_CMD_NO_FLAGS))
800  goto bail;
801  }
802  }
803  }
804  }
805 
806  if (c_ssl_force_tls && (adata->conn->ssf == 0))
807  {
808  mutt_error(_("Encrypted connection unavailable"));
809  goto bail;
810  }
811 #endif
812  }
813  else if (mutt_istr_startswith(adata->buf, "* PREAUTH"))
814  {
815 #ifdef USE_SSL
816  /* Unless using a secure $tunnel, an unencrypted PREAUTH response may be a
817  * MITM attack. The only way to stop "STARTTLS" MITM attacks is via
818  * $ssl_force_tls: an attacker can easily spoof "* OK" and strip the
819  * STARTTLS capability. So consult $ssl_force_tls, not $ssl_starttls, to
820  * decide whether to abort. Note that if using $tunnel and
821  * $tunnel_is_secure, adata->conn->ssf will be set to 1. */
822  const bool c_ssl_force_tls = cs_subset_bool(NeoMutt->sub, "ssl_force_tls");
823  if ((adata->conn->ssf == 0) && c_ssl_force_tls)
824  {
825  mutt_error(_("Encrypted connection unavailable"));
826  goto bail;
827  }
828 #endif
829 
830  adata->state = IMAP_AUTHENTICATED;
831  if (check_capabilities(adata) != 0)
832  goto bail;
833  FREE(&adata->capstr);
834  }
835  else
836  {
837  imap_error("imap_open_connection()", adata->buf);
838  goto bail;
839  }
840 
841  return 0;
842 
843 bail:
844  imap_close_connection(adata);
845  FREE(&adata->capstr);
846  return -1;
847 }
enum QuadOption cs_subset_quad(const struct ConfigSubset *sub, const char *name)
Get a quad-value config item by name.
Definition: helpers.c:218
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
int mutt_ssl_starttls(struct Connection *conn)
Negotiate TLS over an already opened connection.
Definition: gnutls.c:1143
@ IMAP_AUTHENTICATED
Connection is authenticated.
Definition: private.h:109
@ IMAP_CONNECTED
Connected to server.
Definition: private.h:108
#define IMAP_RES_OK
<tag> OK ...
Definition: private.h:56
#define IMAP_CAP_STARTTLS
RFC2595: STARTTLS.
Definition: private.h:133
ImapExecResult
Imap_exec return code.
Definition: private.h:83
@ IMAP_EXEC_ERROR
Imap command failure.
Definition: private.h:85
@ IMAP_EXEC_FATAL
Imap connection failure.
Definition: private.h:86
#define IMAP_CMD_SINGLE
Run a single command.
Definition: private.h:77
void imap_close_connection(struct ImapAccountData *adata)
Close an IMAP connection.
Definition: imap.c:853
static int check_capabilities(struct ImapAccountData *adata)
Make sure we can log in to this server.
Definition: imap.c:95
#define FREE(x)
Definition: memory.h:43
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(enum QuadOption opt, const char *prompt)
Ask the user a quad-question.
Definition: question.c:386
void mutt_socket_empty(struct Connection *conn)
Clear out any queued data.
Definition: socket.c:313
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:51
char * capstr
Capability string from the server.
Definition: adata.h:54
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_close_connection()

void imap_close_connection ( struct ImapAccountData adata)

Close an IMAP connection.

Parameters
adataImap Account data

Definition at line 853 of file imap.c.

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

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

int imap_exec_msgset ( struct Mailbox m,
const char *  pre,
const char *  post,
enum MessageType  flag,
bool  changed,
bool  invert 
)

Prepare commands for all messages matching conditions.

Parameters
mSelected Imap Mailbox
preprefix commands
postpostfix commands
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
Return values
numMatched messages
-1Failure

pre/post: commands are of the form "%s %s %s %s", tag, pre, message set, post Prepares commands for all messages matching conditions (must be flushed with imap_exec)

Definition at line 930 of file imap.c.

932 {
933  struct ImapAccountData *adata = imap_adata_get(m);
934  if (!adata || (adata->mailbox != m))
935  return -1;
936 
937  struct Email **emails = NULL;
938  int pos;
939  int rc;
940  int count = 0;
941 
942  struct Buffer cmd = mutt_buffer_make(0);
943 
944  /* We make a copy of the headers just in case resorting doesn't give
945  exactly the original order (duplicate messages?), because other parts of
946  the mv are tied to the header order. This may be overkill. */
947  const short c_sort = cs_subset_sort(NeoMutt->sub, "sort");
948  if (c_sort != SORT_ORDER)
949  {
950  emails = m->emails;
951  // We overcommit here, just in case new mail arrives whilst we're sync-ing
952  m->emails = mutt_mem_malloc(m->email_max * sizeof(struct Email *));
953  memcpy(m->emails, emails, m->email_max * sizeof(struct Email *));
954 
956  qsort(m->emails, m->msg_count, sizeof(struct Email *), compare_uid);
957  }
958 
959  pos = 0;
960 
961  do
962  {
963  mutt_buffer_reset(&cmd);
964  mutt_buffer_add_printf(&cmd, "%s ", pre);
965  rc = make_msg_set(m, &cmd, flag, changed, invert, &pos);
966  if (rc > 0)
967  {
968  mutt_buffer_add_printf(&cmd, " %s", post);
969  if (imap_exec(adata, cmd.data, IMAP_CMD_QUEUE) != IMAP_EXEC_SUCCESS)
970  {
971  rc = -1;
972  goto out;
973  }
974  count += rc;
975  }
976  } while (rc > 0);
977 
978  rc = count;
979 
980 out:
981  mutt_buffer_dealloc(&cmd);
982  if (c_sort != SORT_ORDER)
983  {
984  cs_subset_str_native_set(NeoMutt->sub, "sort", c_sort, NULL);
985  FREE(&m->emails);
986  m->emails = emails;
987  }
988 
989  return rc;
990 }
struct Buffer mutt_buffer_make(size_t size)
Make a new buffer on the stack.
Definition: buffer.c:63
void mutt_buffer_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:81
short cs_subset_sort(const struct ConfigSubset *sub, const char *name)
Get a sort config item by name.
Definition: helpers.c:292
static int compare_uid(const void *a, const void *b)
Compare two Emails by UID - Implements sort_t -.
Definition: imap.c:904
#define IMAP_CMD_QUEUE
Queue a command, do not execute.
Definition: private.h:75
static int make_msg_set(struct Mailbox *m, struct Buffer *buf, enum MessageType flag, bool changed, bool invert, int *pos)
Make a message set.
Definition: imap.c:197
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
@ SORT_ORDER
Sort by the order the messages appear in the mailbox.
Definition: sort2.h:48
int email_max
Number of pointers in emails.
Definition: mailbox.h:97
int cs_subset_str_native_set(const struct ConfigSubset *sub, const char *name, intptr_t value, struct Buffer *err)
Natively set the value of a string config item.
Definition: subset.c:305
+ 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 1007 of file imap.c.

1009 {
1010  struct ImapAccountData *adata = imap_adata_get(m);
1011  if (!adata || (adata->mailbox != m))
1012  return -1;
1013 
1014  char flags[1024];
1015  char *tags = NULL;
1016  char uid[11];
1017 
1018  if (!compare_flags_for_copy(e))
1019  {
1020  if (e->deleted == imap_edata_get(e)->deleted)
1021  e->changed = false;
1022  return 0;
1023  }
1024 
1025  snprintf(uid, sizeof(uid), "%u", imap_edata_get(e)->uid);
1026  mutt_buffer_reset(cmd);
1027  mutt_buffer_addstr(cmd, "UID STORE ");
1028  mutt_buffer_addstr(cmd, uid);
1029 
1030  flags[0] = '\0';
1031 
1032  set_flag(m, MUTT_ACL_SEEN, e->read, "\\Seen ", flags, sizeof(flags));
1033  set_flag(m, MUTT_ACL_WRITE, e->old, "Old ", flags, sizeof(flags));
1034  set_flag(m, MUTT_ACL_WRITE, e->flagged, "\\Flagged ", flags, sizeof(flags));
1035  set_flag(m, MUTT_ACL_WRITE, e->replied, "\\Answered ", flags, sizeof(flags));
1036  set_flag(m, MUTT_ACL_DELETE, imap_edata_get(e)->deleted, "\\Deleted ", flags,
1037  sizeof(flags));
1038 
1039  if (m->rights & MUTT_ACL_WRITE)
1040  {
1041  /* restore system flags */
1042  if (imap_edata_get(e)->flags_system)
1043  mutt_str_cat(flags, sizeof(flags), imap_edata_get(e)->flags_system);
1044  /* set custom flags */
1045  tags = driver_tags_get_with_hidden(&e->tags);
1046  if (tags)
1047  {
1048  mutt_str_cat(flags, sizeof(flags), tags);
1049  FREE(&tags);
1050  }
1051  }
1052 
1054 
1055  /* UW-IMAP is OK with null flags, Cyrus isn't. The only solution is to
1056  * explicitly revoke all system flags (if we have permission) */
1057  if (*flags == '\0')
1058  {
1059  set_flag(m, MUTT_ACL_SEEN, true, "\\Seen ", flags, sizeof(flags));
1060  set_flag(m, MUTT_ACL_WRITE, true, "Old ", flags, sizeof(flags));
1061  set_flag(m, MUTT_ACL_WRITE, true, "\\Flagged ", flags, sizeof(flags));
1062  set_flag(m, MUTT_ACL_WRITE, true, "\\Answered ", flags, sizeof(flags));
1063  set_flag(m, MUTT_ACL_DELETE, !imap_edata_get(e)->deleted, "\\Deleted ",
1064  flags, sizeof(flags));
1065 
1066  /* erase custom flags */
1067  if ((m->rights & MUTT_ACL_WRITE) && imap_edata_get(e)->flags_remote)
1068  mutt_str_cat(flags, sizeof(flags), imap_edata_get(e)->flags_remote);
1069 
1071 
1072  mutt_buffer_addstr(cmd, " -FLAGS.SILENT (");
1073  }
1074  else
1075  mutt_buffer_addstr(cmd, " FLAGS.SILENT (");
1076 
1077  mutt_buffer_addstr(cmd, flags);
1078  mutt_buffer_addstr(cmd, ")");
1079 
1080  /* after all this it's still possible to have no flags, if you
1081  * have no ACL rights */
1082  if (*flags && (imap_exec(adata, cmd->data, IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS) &&
1083  err_continue && (*err_continue != MUTT_YES))
1084  {
1085  *err_continue = imap_continue("imap_sync_message: STORE failed", adata->buf);
1086  if (*err_continue != MUTT_YES)
1087  return -1;
1088  }
1089 
1090  /* server have now the updated flags */
1091  FREE(&imap_edata_get(e)->flags_remote);
1093 
1094  if (e->deleted == imap_edata_get(e)->deleted)
1095  e->changed = false;
1096 
1097  return 0;
1098 }
size_t mutt_buffer_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:223
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:652
static void set_flag(struct Mailbox *m, AclFlags aclflag, bool flag, const char *str, char *flags, size_t flsize)
Append str to flags if we currently have permission according to aclflag.
Definition: imap.c:176
static bool compare_flags_for_copy(struct Email *e)
Compare local flags against the server.
Definition: imap.c:298
#define MUTT_ACL_DELETE
Delete a message.
Definition: mailbox.h:63
#define MUTT_ACL_SEEN
Change the 'seen' status of a message.
Definition: mailbox.h:70
void mutt_str_remove_trailing_ws(char *s)
Trim trailing whitespace from a string.
Definition: string.c:613
struct TagList tags
For drivers that support server tagging.
Definition: email.h:70
char * flags_remote
Definition: edata.h:48
char * driver_tags_get_with_hidden(struct TagList *list)
Get tags with hiddens.
Definition: tags.c:158
+ Here is the 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 1007 of file imap.c.

1107 {
1108  if (!m || !m->account)
1109  return MX_STATUS_ERROR;
1110 
1111  struct ImapAccountData *adata = imap_adata_get(m);
1112  struct ImapMboxData *mdata = imap_mdata_get(m);
1113 
1114  /* overload keyboard timeout to avoid many mailbox checks in a row.
1115  * Most users don't like having to wait exactly when they press a key. */
1116  int rc = 0;
1117 
1118  /* try IDLE first, unless force is set */
1119  const bool c_imap_idle = cs_subset_bool(NeoMutt->sub, "imap_idle");
1120  const short c_imap_keepalive = cs_subset_number(NeoMutt->sub, "imap_keepalive");
1121  if (!force && c_imap_idle && (adata->capabilities & IMAP_CAP_IDLE) &&
1122  ((adata->state != IMAP_IDLE) || (mutt_date_epoch() >= adata->lastread + c_imap_keepalive)))
1123  {
1124  if (imap_cmd_idle(adata) < 0)
1125  return MX_STATUS_ERROR;
1126  }
1127  if (adata->state == IMAP_IDLE)
1128  {
1129  while ((rc = mutt_socket_poll(adata->conn, 0)) > 0)
1130  {
1131  if (imap_cmd_step(adata) != IMAP_RES_CONTINUE)
1132  {
1133  mutt_debug(LL_DEBUG1, "Error reading IDLE response\n");
1134  return MX_STATUS_ERROR;
1135  }
1136  }
1137  if (rc < 0)
1138  {
1139  mutt_debug(LL_DEBUG1, "Poll failed, disabling IDLE\n");
1140  adata->capabilities &= ~IMAP_CAP_IDLE; // Clear the flag
1141  }
1142  }
1143 
1144  const short c_timeout = cs_subset_number(NeoMutt->sub, "timeout");
1145  if ((force || ((adata->state != IMAP_IDLE) &&
1146  (mutt_date_epoch() >= adata->lastread + c_timeout))) &&
1147  (imap_exec(adata, "NOOP", IMAP_CMD_POLL) != IMAP_EXEC_SUCCESS))
1148  {
1149  return MX_STATUS_ERROR;
1150  }
1151 
1152  /* We call this even when we haven't run NOOP in case we have pending
1153  * changes to process, since we can reopen here. */
1154  imap_cmd_finish(adata);
1155 
1156  enum MxStatus check = MX_STATUS_OK;
1157  if (mdata->check_status & IMAP_EXPUNGE_PENDING)
1158  check = MX_STATUS_REOPENED;
1159  else if (mdata->check_status & IMAP_NEWMAIL_PENDING)
1160  check = MX_STATUS_NEW_MAIL;
1161  else if (mdata->check_status & IMAP_FLAGS_PENDING)
1162  check = MX_STATUS_FLAGS;
1163  else if (rc < 0)
1164  check = MX_STATUS_ERROR;
1165 
1166  mdata->check_status = IMAP_OPEN_NO_FLAGS;
1167 
1168  return check;
1169 }
time_t mutt_date_epoch(void)
Return the number of seconds since the Unix epoch.
Definition: date.c:427
int imap_cmd_idle(struct ImapAccountData *adata)
Enter the IDLE state.
Definition: command.c:1379
void imap_cmd_finish(struct ImapAccountData *adata)
Attempt to perform cleanup.
Definition: command.c:1312
#define IMAP_CAP_IDLE
RFC2177: IDLE.
Definition: private.h:135
@ IMAP_IDLE
Connection is idle.
Definition: private.h:113
#define IMAP_EXPUNGE_PENDING
Messages on the server have been expunged.
Definition: private.h:68
#define IMAP_OPEN_NO_FLAGS
No flags are set.
Definition: private.h:65
#define IMAP_CMD_POLL
Poll the tcp connection before running the imap command.
Definition: private.h:76
#define IMAP_NEWMAIL_PENDING
New mail is waiting on the server.
Definition: private.h:69
#define IMAP_FLAGS_PENDING
Flags have changed on the server.
Definition: private.h:70
MxStatus
Return values from mbox_check(), mbox_check_stats(), mbox_snc(), and mbox_close()
Definition: mxapi.h:84
@ MX_STATUS_ERROR
An error occurred.
Definition: mxapi.h:85
@ MX_STATUS_OK
No changes.
Definition: mxapi.h:86
@ MX_STATUS_FLAGS
Nondestructive flags change (IMAP)
Definition: mxapi.h:90
@ MX_STATUS_REOPENED
Mailbox was reopened.
Definition: mxapi.h:89
@ MX_STATUS_NEW_MAIL
New mail received in Mailbox.
Definition: mxapi.h:87
time_t lastread
last time we read a command for the server
Definition: adata.h:58
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_status()

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

Refresh the number of total and new messages.

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

Definition at line 1178 of file imap.c.

1179 {
1180  char *uidvalidity_flag = NULL;
1181  char cmd[2048];
1182 
1183  if (!adata || !mdata)
1184  return -1;
1185 
1186  /* Don't issue STATUS on the selected mailbox, it will be NOOPed or
1187  * IDLEd elsewhere.
1188  * adata->mailbox may be NULL for connections other than the current
1189  * mailbox's. */
1190  if (adata->mailbox && (adata->mailbox->mdata == mdata))
1191  {
1192  adata->mailbox->has_new = false;
1193  return mdata->messages;
1194  }
1195 
1196  if (adata->capabilities & IMAP_CAP_IMAP4REV1)
1197  uidvalidity_flag = "UIDVALIDITY";
1198  else if (adata->capabilities & IMAP_CAP_STATUS)
1199  uidvalidity_flag = "UID-VALIDITY";
1200  else
1201  {
1202  mutt_debug(LL_DEBUG2, "Server doesn't support STATUS\n");
1203  return -1;
1204  }
1205 
1206  snprintf(cmd, sizeof(cmd), "STATUS %s (UIDNEXT %s UNSEEN RECENT MESSAGES)",
1207  mdata->munge_name, uidvalidity_flag);
1208 
1209  int rc = imap_exec(adata, cmd, queue ? IMAP_CMD_QUEUE : IMAP_CMD_NO_FLAGS | IMAP_CMD_POLL);
1210  if (rc < 0)
1211  {
1212  mutt_debug(LL_DEBUG1, "Error queueing command\n");
1213  return rc;
1214  }
1215  return mdata->messages;
1216 }
#define IMAP_CAP_STATUS
Server supports STATUS command.
Definition: private.h:125
struct Mailbox * mailbox
Current selected mailbox.
Definition: adata.h:76
unsigned int messages
Definition: mdata.h:53
char * munge_name
Munged version of the mailbox name.
Definition: mdata.h:41
bool has_new
Mailbox has new mail.
Definition: mailbox.h:85
+ 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 1238 of file imap.c.

1239 {
1240  struct Mailbox *m = mx_mbox_find2(path);
1241 
1242  const bool is_temp = !m;
1243  if (is_temp)
1244  {
1245  m = mx_path_resolve(path);
1246  if (!mx_mbox_ac_link(m))
1247  {
1248  mailbox_free(&m);
1249  return 0;
1250  }
1251  }
1252 
1253  int rc = imap_mailbox_status(m, queue);
1254 
1255  if (is_temp)
1256  {
1257  mx_ac_remove(m, false);
1258  mailbox_free(&m);
1259  }
1260 
1261  return rc;
1262 }
int imap_mailbox_status(struct Mailbox *m, bool queue)
Refresh the number of total and new messages.
Definition: imap.c:1273
void mailbox_free(struct Mailbox **ptr)
Free a Mailbox.
Definition: mailbox.c:87
int mx_ac_remove(struct Mailbox *m, bool keep_account)
Remove a Mailbox from an Account and delete Account if empty.
Definition: mx.c:1781
struct Mailbox * mx_mbox_find2(const char *path)
Find a Mailbox on an Account.
Definition: mx.c:1649
struct Mailbox * mx_path_resolve(const char *path)
Get a Mailbox for a path.
Definition: mx.c:1677
bool mx_mbox_ac_link(struct Mailbox *m)
Link a Mailbox to an existing or new Account.
Definition: mx.c:267
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 1273 of file imap.c.

1274 {
1275  struct ImapAccountData *adata = imap_adata_get(m);
1276  struct ImapMboxData *mdata = imap_mdata_get(m);
1277  if (!adata || !mdata)
1278  return -1;
1279  return imap_status(adata, mdata, queue);
1280 }
static int imap_status(struct ImapAccountData *adata, struct ImapMboxData *mdata, bool queue)
Refresh the number of total and new messages.
Definition: imap.c:1178
+ 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 1289 of file imap.c.

1290 {
1291  struct ImapAccountData *adata = NULL;
1292  struct ImapMboxData *mdata = NULL;
1293  char buf[2048];
1294  struct Buffer err;
1295 
1296  if (imap_adata_find(path, &adata, &mdata) < 0)
1297  return -1;
1298 
1299  if (subscribe)
1300  mutt_message(_("Subscribing to %s..."), mdata->name);
1301  else
1302  mutt_message(_("Unsubscribing from %s..."), mdata->name);
1303 
1304  snprintf(buf, sizeof(buf), "%sSUBSCRIBE %s", subscribe ? "" : "UN", mdata->munge_name);
1305 
1306  if (imap_exec(adata, buf, IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS)
1307  {
1308  imap_mdata_free((void *) &mdata);
1309  return -1;
1310  }
1311 
1312  const bool c_imap_check_subscribed = cs_subset_bool(NeoMutt->sub, "imap_check_subscribed");
1313  if (c_imap_check_subscribed)
1314  {
1315  char mbox[1024];
1316  mutt_buffer_init(&err);
1317  err.dsize = 256;
1318  err.data = mutt_mem_malloc(err.dsize);
1319  size_t len = snprintf(mbox, sizeof(mbox), "%smailboxes ", subscribe ? "" : "un");
1320  imap_quote_string(mbox + len, sizeof(mbox) - len, path, true);
1321  if (mutt_parse_rc_line(mbox, &err))
1322  mutt_debug(LL_DEBUG1, "Error adding subscribed mailbox: %s\n", err.data);
1323  FREE(&err.data);
1324  }
1325 
1326  if (subscribe)
1327  mutt_message(_("Subscribed to %s"), mdata->name);
1328  else
1329  mutt_message(_("Unsubscribed from %s"), mdata->name);
1330  imap_mdata_free((void *) &mdata);
1331  return 0;
1332 }
struct Buffer * mutt_buffer_init(struct Buffer *buf)
Initialise a new Buffer.
Definition: buffer.c:48
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:837
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 mutt_parse_rc_line(const char *line, struct Buffer *err)
Parse a line of user config.
Definition: init.c:894
char * name
Mailbox name.
Definition: mdata.h:40
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_complete()

int imap_complete ( char *  buf,
size_t  buflen,
const char *  path 
)

Try to complete an IMAP folder path.

Parameters
bufBuffer for result
buflenLength of buffer
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 1345 of file imap.c.

1346 {
1347  struct ImapAccountData *adata = NULL;
1348  struct ImapMboxData *mdata = NULL;
1349  char tmp[2048];
1350  struct ImapList listresp = { 0 };
1351  char completion[1024];
1352  size_t clen;
1353  size_t matchlen = 0;
1354  int completions = 0;
1355  int rc;
1356 
1357  if (imap_adata_find(path, &adata, &mdata) < 0)
1358  {
1359  mutt_str_copy(buf, path, buflen);
1360  return complete_hosts(buf, buflen);
1361  }
1362 
1363  /* fire off command */
1364  const bool c_imap_list_subscribed = cs_subset_bool(NeoMutt->sub, "imap_list_subscribed");
1365  snprintf(tmp, sizeof(tmp), "%s \"\" \"%s%%\"",
1366  c_imap_list_subscribed ? "LSUB" : "LIST", mdata->real_name);
1367 
1368  imap_cmd_start(adata, tmp);
1369 
1370  /* and see what the results are */
1371  mutt_str_copy(completion, mdata->name, sizeof(completion));
1372  imap_mdata_free((void *) &mdata);
1373 
1374  adata->cmdresult = &listresp;
1375  do
1376  {
1377  listresp.name = NULL;
1378  rc = imap_cmd_step(adata);
1379 
1380  if ((rc == IMAP_RES_CONTINUE) && listresp.name)
1381  {
1382  /* if the folder isn't selectable, append delimiter to force browse
1383  * to enter it on second tab. */
1384  if (listresp.noselect)
1385  {
1386  clen = strlen(listresp.name);
1387  listresp.name[clen++] = listresp.delim;
1388  listresp.name[clen] = '\0';
1389  }
1390  /* copy in first word */
1391  if (!completions)
1392  {
1393  mutt_str_copy(completion, listresp.name, sizeof(completion));
1394  matchlen = strlen(completion);
1395  completions++;
1396  continue;
1397  }
1398 
1399  matchlen = longest_common_prefix(completion, listresp.name, 0, matchlen);
1400  completions++;
1401  }
1402  } while (rc == IMAP_RES_CONTINUE);
1403  adata->cmdresult = NULL;
1404 
1405  if (completions)
1406  {
1407  /* reformat output */
1408  imap_qualify_path(buf, buflen, &adata->conn->account, completion);
1409  mutt_pretty_mailbox(buf, buflen);
1410  return 0;
1411  }
1412 
1413  return -1;
1414 }
void mutt_pretty_mailbox(char *buf, size_t buflen)
Shorten a mailbox path using '~' or '='.
Definition: muttlib.c:522
void imap_qualify_path(char *buf, size_t buflen, struct ConnAccount *conn_account, char *path)
Make an absolute IMAP folder target.
Definition: util.c:820
static int complete_hosts(char *buf, size_t buflen)
Look for completion matches for mailboxes.
Definition: imap.c:385
struct ImapList * cmdresult
Definition: adata.h:66
Items in an IMAP browser.
Definition: private.h:150
bool noselect
Definition: private.h:153
char * name
Definition: private.h:151
char delim
Definition: private.h:152
char * real_name
Original Mailbox name, e.g.: INBOX can be just \0.
Definition: mdata.h:42
+ 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 1424 of file imap.c.

1425 {
1426  char prompt[1024];
1427  int rc = -1;
1428  bool triedcreate = false;
1429  enum QuadOption err_continue = MUTT_NO;
1430 
1431  struct ImapAccountData *adata = imap_adata_get(m);
1432  struct ImapAccountData *dest_adata = NULL;
1433  struct ImapMboxData *dest_mdata = NULL;
1434 
1435  if (imap_adata_find(dest, &dest_adata, &dest_mdata) < 0)
1436  return -1;
1437 
1438  struct Buffer sync_cmd = mutt_buffer_make(0);
1439 
1440  /* check that the save-to folder is in the same account */
1441  if (!imap_account_match(&(adata->conn->account), &(dest_adata->conn->account)))
1442  {
1443  mutt_debug(LL_DEBUG3, "%s not same server as %s\n", dest, mailbox_path(m));
1444  goto out;
1445  }
1446 
1447  for (int i = 0; i < m->msg_count; i++)
1448  {
1449  struct Email *e = m->emails[i];
1450  if (!e)
1451  break;
1452  if (e->active && e->changed && e->deleted && !e->purge)
1453  {
1454  rc = imap_sync_message_for_copy(m, e, &sync_cmd, &err_continue);
1455  if (rc < 0)
1456  {
1457  mutt_debug(LL_DEBUG1, "could not sync\n");
1458  goto out;
1459  }
1460  }
1461  }
1462 
1463  /* loop in case of TRYCREATE */
1464  do
1465  {
1466  rc = imap_exec_msgset(m, "UID COPY", dest_mdata->munge_name, MUTT_TRASH, false, false);
1467  if (rc == 0)
1468  {
1469  mutt_debug(LL_DEBUG1, "No messages to trash\n");
1470  rc = -1;
1471  goto out;
1472  }
1473  else if (rc < 0)
1474  {
1475  mutt_debug(LL_DEBUG1, "could not queue copy\n");
1476  goto out;
1477  }
1478  else if (m->verbose)
1479  {
1480  mutt_message(ngettext("Copying %d message to %s...", "Copying %d messages to %s...", rc),
1481  rc, dest_mdata->name);
1482  }
1483 
1484  /* let's get it on */
1485  rc = imap_exec(adata, NULL, IMAP_CMD_NO_FLAGS);
1486  if (rc == IMAP_EXEC_ERROR)
1487  {
1488  if (triedcreate)
1489  {
1490  mutt_debug(LL_DEBUG1, "Already tried to create mailbox %s\n", dest_mdata->name);
1491  break;
1492  }
1493  /* bail out if command failed for reasons other than nonexistent target */
1494  if (!mutt_istr_startswith(imap_get_qualifier(adata->buf), "[TRYCREATE]"))
1495  break;
1496  mutt_debug(LL_DEBUG3, "server suggests TRYCREATE\n");
1497  snprintf(prompt, sizeof(prompt), _("Create %s?"), dest_mdata->name);
1498  const bool c_confirm_create = cs_subset_bool(NeoMutt->sub, "confirm_create");
1499  if (c_confirm_create && (mutt_yesorno(prompt, MUTT_YES) != MUTT_YES))
1500  {
1501  mutt_clear_error();
1502  goto out;
1503  }
1504  if (imap_create_mailbox(adata, dest_mdata->name) < 0)
1505  break;
1506  triedcreate = true;
1507  }
1508  } while (rc == IMAP_EXEC_ERROR);
1509 
1510  if (rc != IMAP_EXEC_SUCCESS)
1511  {
1512  imap_error("imap_fast_trash", adata->buf);
1513  goto out;
1514  }
1515 
1516  rc = IMAP_EXEC_SUCCESS;
1517 
1518 out:
1519  mutt_buffer_dealloc(&sync_cmd);
1520  imap_mdata_free((void *) &dest_mdata);
1521 
1522  return ((rc == IMAP_EXEC_SUCCESS) ? 0 : -1);
1523 }
bool imap_account_match(const struct ConnAccount *a1, const struct ConnAccount *a2)
Compare two Accounts.
Definition: util.c:1044
char * imap_get_qualifier(char *buf)
Get the qualifier from a tagged response.
Definition: util.c:772
int imap_create_mailbox(struct ImapAccountData *adata, const char *mailbox)
Create a new mailbox.
Definition: imap.c:447
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:1007
@ LL_DEBUG3
Log at debug level 3.
Definition: logging.h:42
@ MUTT_NO
User answered 'No', or assume 'No'.
Definition: quad.h:38
enum QuadOption mutt_yesorno(const char *msg, enum QuadOption def)
Ask the user a Yes/No question.
Definition: question.c:194
bool verbose
Display status messages?
Definition: mailbox.h:115
+ 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 1424 of file imap.c.

1535 {
1536  if (!m)
1537  return -1;
1538 
1539  struct Email **emails = NULL;
1540  int rc;
1541 
1542  struct ImapAccountData *adata = imap_adata_get(m);
1543  struct ImapMboxData *mdata = imap_mdata_get(m);
1544 
1545  if (adata->state < IMAP_SELECTED)
1546  {
1547  mutt_debug(LL_DEBUG2, "no mailbox selected\n");
1548  return -1;
1549  }
1550 
1551  /* This function is only called when the calling code expects the context
1552  * to be changed. */
1553  imap_allow_reopen(m);
1554 
1555  enum MxStatus check = imap_check_mailbox(m, false);
1556  if (check == MX_STATUS_ERROR)
1557  return check;
1558 
1559  /* if we are expunging anyway, we can do deleted messages very quickly... */
1560  if (expunge && (m->rights & MUTT_ACL_DELETE))
1561  {
1562  rc = imap_exec_msgset(m, "UID STORE", "+FLAGS.SILENT (\\Deleted)",
1563  MUTT_DELETED, true, false);
1564  if (rc < 0)
1565  {
1566  mutt_error(_("Expunge failed"));
1567  return rc;
1568  }
1569 
1570  if (rc > 0)
1571  {
1572  /* mark these messages as unchanged so second pass ignores them. Done
1573  * here so BOGUS UW-IMAP 4.7 SILENT FLAGS updates are ignored. */
1574  for (int i = 0; i < m->msg_count; i++)
1575  {
1576  struct Email *e = m->emails[i];
1577  if (!e)
1578  break;
1579  if (e->deleted && e->changed)
1580  e->active = false;
1581  }
1582  if (m->verbose)
1583  {
1584  mutt_message(ngettext("Marking %d message deleted...",
1585  "Marking %d messages deleted...", rc),
1586  rc);
1587  }
1588  }
1589  }
1590 
1591 #ifdef USE_HCACHE
1592  imap_hcache_open(adata, mdata);
1593 #endif
1594 
1595  /* save messages with real (non-flag) changes */
1596  for (int i = 0; i < m->msg_count; i++)
1597  {
1598  struct Email *e = m->emails[i];
1599  if (!e)
1600  break;
1601 
1602  if (e->deleted)
1603  {
1604  imap_cache_del(m, e);
1605 #ifdef USE_HCACHE
1606  imap_hcache_del(mdata, imap_edata_get(e)->uid);
1607 #endif
1608  }
1609 
1610  if (e->active && e->changed)
1611  {
1612 #ifdef USE_HCACHE
1613  imap_hcache_put(mdata, e);
1614 #endif
1615  /* if the message has been rethreaded or attachments have been deleted
1616  * we delete the message and reupload it.
1617  * This works better if we're expunging, of course. */
1618  /* TODO: why the e->env check? */
1619  if ((e->env && e->env->changed) || e->attach_del)
1620  {
1621  /* L10N: The plural is chosen by the last %d, i.e. the total number */
1622  if (m->verbose)
1623  {
1624  mutt_message(ngettext("Saving changed message... [%d/%d]",
1625  "Saving changed messages... [%d/%d]", m->msg_count),
1626  i + 1, m->msg_count);
1627  }
1628  bool save_append = m->append;
1629  m->append = true;
1631  m->append = save_append;
1632  /* TODO: why the check for e->env? Is this possible? */
1633  if (e->env)
1634  e->env->changed = 0;
1635  }
1636  }
1637  }
1638 
1639 #ifdef USE_HCACHE
1640  imap_hcache_close(mdata);
1641 #endif
1642 
1643  /* presort here to avoid doing 10 resorts in imap_exec_msgset */
1644  const short c_sort = cs_subset_sort(NeoMutt->sub, "sort");
1645  if (c_sort != SORT_ORDER)
1646  {
1647  emails = m->emails;
1648  m->emails = mutt_mem_malloc(m->msg_count * sizeof(struct Email *));
1649  memcpy(m->emails, emails, m->msg_count * sizeof(struct Email *));
1650 
1652  qsort(m->emails, m->msg_count, sizeof(struct Email *), compare_uid);
1653  }
1654 
1655  rc = sync_helper(m, MUTT_ACL_DELETE, MUTT_DELETED, "\\Deleted");
1656  if (rc >= 0)
1657  rc |= sync_helper(m, MUTT_ACL_WRITE, MUTT_FLAG, "\\Flagged");
1658  if (rc >= 0)
1659  rc |= sync_helper(m, MUTT_ACL_WRITE, MUTT_OLD, "Old");
1660  if (rc >= 0)
1661  rc |= sync_helper(m, MUTT_ACL_SEEN, MUTT_READ, "\\Seen");
1662  if (rc >= 0)
1663  rc |= sync_helper(m, MUTT_ACL_WRITE, MUTT_REPLIED, "\\Answered");
1664 
1665  if (c_sort != SORT_ORDER)
1666  {
1667  cs_subset_str_native_set(NeoMutt->sub, "sort", c_sort, NULL);
1668  FREE(&m->emails);
1669  m->emails = emails;
1670  }
1671 
1672  /* Flush the queued flags if any were changed in sync_helper. */
1673  if (rc > 0)
1674  if (imap_exec(adata, NULL, IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS)
1675  rc = -1;
1676 
1677  if (rc < 0)
1678  {
1679  if (close)
1680  {
1681  if (mutt_yesorno(_("Error saving flags. Close anyway?"), MUTT_NO) == MUTT_YES)
1682  {
1683  adata->state = IMAP_AUTHENTICATED;
1684  return 0;
1685  }
1686  }
1687  else
1688  mutt_error(_("Error saving flags"));
1689  return -1;
1690  }
1691 
1692  /* Update local record of server state to reflect the synchronization just
1693  * completed. imap_read_headers always overwrites hcache-origin flags, so
1694  * there is no need to mutate the hcache after flag-only changes. */
1695  for (int i = 0; i < m->msg_count; i++)
1696  {
1697  struct Email *e = m->emails[i];
1698  if (!e)
1699  break;
1700  struct ImapEmailData *edata = imap_edata_get(e);
1701  edata->deleted = e->deleted;
1702  edata->flagged = e->flagged;
1703  edata->old = e->old;
1704  edata->read = e->read;
1705  edata->replied = e->replied;
1706  e->changed = false;
1707  }
1708  m->changed = false;
1709 
1710  /* We must send an EXPUNGE command if we're not closing. */
1711  if (expunge && !close && (m->rights & MUTT_ACL_DELETE))
1712  {
1713  if (m->verbose)
1714  mutt_message(_("Expunging messages from server..."));
1715  /* Set expunge bit so we don't get spurious reopened messages */
1716  mdata->reopen |= IMAP_EXPUNGE_EXPECTED;
1717  if (imap_exec(adata, "EXPUNGE", IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS)
1718  {
1719  mdata->reopen &= ~IMAP_EXPUNGE_EXPECTED;
1720  imap_error(_("imap_sync_mailbox: EXPUNGE failed"), adata->buf);
1721  return -1;
1722  }
1723  mdata->reopen &= ~IMAP_EXPUNGE_EXPECTED;
1724  }
1725 
1726  if (expunge && close)
1727  {
1728  adata->closing = true;
1729  imap_exec(adata, "CLOSE", IMAP_CMD_NO_FLAGS);
1730  adata->state = IMAP_AUTHENTICATED;
1731  }
1732 
1733  const bool c_message_cache_clean = cs_subset_bool(NeoMutt->sub, "message_cache_clean");
1734  if (c_message_cache_clean)
1735  imap_cache_clean(m);
1736 
1737  return check;
1738 }
int mutt_save_message_ctx(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: commands.c:772
@ TRANSFORM_NONE
No transformation.
Definition: commands.h:40
@ SAVE_MOVE
Move message to another mailbox, removing the original.
Definition: commands.h:51
int imap_cache_clean(struct Mailbox *m)
Delete all the entries in the message cache.
Definition: message.c:1865
void imap_allow_reopen(struct Mailbox *m)
Allow re-opening a folder upon expunge.
Definition: util.c:1016
@ IMAP_SELECTED
Mailbox is selected.
Definition: private.h:110
#define IMAP_EXPUNGE_EXPECTED
Messages will be expunged from the server.
Definition: private.h:67
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, AclFlags right, enum MessageType flag, const char *name)
Sync flag changes to the server.
Definition: imap.c:323
enum MxStatus imap_check_mailbox(struct Mailbox *m, bool force)
Use the NOOP or IDLE command to poll for new mail.
Definition: imap.c:1106
struct Envelope * env
Envelope information.
Definition: email.h:66
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:92
bool closing
If true, we are waiting for CLOSE completion.
Definition: adata.h:43
ImapOpenFlags reopen
Flags, e.g. IMAP_REOPEN_ALLOW.
Definition: mdata.h:44
bool changed
Mailbox has been modified.
Definition: mailbox.h:111
bool append
Mailbox is opened in append mode.
Definition: mailbox.h:110

◆ imap_mbox_select()

static void imap_mbox_select ( struct Mailbox m)
static

Select a Mailbox.

Parameters
mMailbox

Definition at line 1815 of file imap.c.

1816 {
1817  struct ImapAccountData *adata = imap_adata_get(m);
1818  struct ImapMboxData *mdata = imap_mdata_get(m);
1819  if (!adata || !mdata)
1820  return;
1821 
1822  const char *condstore = NULL;
1823 #ifdef USE_HCACHE
1824  const bool c_imap_condstore = cs_subset_bool(NeoMutt->sub, "imap_condstore");
1825  if ((adata->capabilities & IMAP_CAP_CONDSTORE) && c_imap_condstore)
1826  condstore = " (CONDSTORE)";
1827  else
1828 #endif
1829  condstore = "";
1830 
1831  char buf[PATH_MAX];
1832  snprintf(buf, sizeof(buf), "%s %s%s", m->readonly ? "EXAMINE" : "SELECT",
1833  mdata->munge_name, condstore);
1834 
1835  adata->state = IMAP_SELECTED;
1836 
1837  imap_cmd_start(adata, buf);
1838 }
#define IMAP_CAP_CONDSTORE
RFC7162.
Definition: private.h:138
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 1848 of file imap.c.

1849 {
1850  if (!adata)
1851  return -1;
1852 
1853  if (adata->state == IMAP_DISCONNECTED)
1854  {
1855  mutt_buffer_reset(&adata->cmdbuf); // purge outstanding queued commands
1856  imap_open_connection(adata);
1857  }
1858  if (adata->state == IMAP_CONNECTED)
1859  {
1860  if (imap_authenticate(adata) == IMAP_AUTH_SUCCESS)
1861  {
1862  adata->state = IMAP_AUTHENTICATED;
1863  FREE(&adata->capstr);
1864  if (adata->conn->ssf != 0)
1865  {
1866  mutt_debug(LL_DEBUG2, "Communication encrypted at %d bits\n",
1867  adata->conn->ssf);
1868  }
1869  }
1870  else
1872  }
1873  if (adata->state == IMAP_AUTHENTICATED)
1874  {
1875  /* capabilities may have changed */
1876  imap_exec(adata, "CAPABILITY", IMAP_CMD_PASS);
1877 
1878 #ifdef USE_ZLIB
1879  /* RFC4978 */
1880  const bool c_imap_deflate = cs_subset_bool(NeoMutt->sub, "imap_deflate");
1881  if ((adata->capabilities & IMAP_CAP_COMPRESS) && c_imap_deflate &&
1882  (imap_exec(adata, "COMPRESS DEFLATE", IMAP_CMD_PASS) == IMAP_EXEC_SUCCESS))
1883  {
1884  mutt_debug(LL_DEBUG2, "IMAP compression is enabled on connection to %s\n",
1885  adata->conn->account.host);
1886  mutt_zstrm_wrap_conn(adata->conn);
1887  }
1888 #endif
1889 
1890  /* enable RFC6855, if the server supports that */
1891  const bool c_imap_rfc5161 = cs_subset_bool(NeoMutt->sub, "imap_rfc5161");
1892  if (c_imap_rfc5161 && (adata->capabilities & IMAP_CAP_ENABLE))
1893  imap_exec(adata, "ENABLE UTF8=ACCEPT", IMAP_CMD_QUEUE);
1894 
1895  /* enable QRESYNC. Advertising QRESYNC also means CONDSTORE
1896  * is supported (even if not advertised), so flip that bit. */
1897  if (adata->capabilities & IMAP_CAP_QRESYNC)
1898  {
1899  adata->capabilities |= IMAP_CAP_CONDSTORE;
1900  const bool c_imap_qresync = cs_subset_bool(NeoMutt->sub, "imap_qresync");
1901  if (c_imap_rfc5161 && c_imap_qresync)
1902  imap_exec(adata, "ENABLE QRESYNC", IMAP_CMD_QUEUE);
1903  }
1904 
1905  /* get root delimiter, '/' as default */
1906  adata->delim = '/';
1907  imap_exec(adata, "LIST \"\" \"\"", IMAP_CMD_QUEUE);
1908 
1909  /* we may need the root delimiter before we open a mailbox */
1910  imap_exec(adata, NULL, IMAP_CMD_NO_FLAGS);
1911 
1912  /* select the mailbox that used to be open before disconnect */
1913  if (adata->mailbox)
1914  {
1915  imap_mbox_select(adata->mailbox);
1916  }
1917  }
1918 
1919  if (adata->state < IMAP_AUTHENTICATED)
1920  return -1;
1921 
1922  return 0;
1923 }
@ IMAP_AUTH_SUCCESS
Authentication successful.
Definition: auth.h:39
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:108
#define IMAP_CAP_ENABLE
RFC5161.
Definition: private.h:137
#define IMAP_CMD_PASS
Command contains a password. Suppress logging.
Definition: private.h:74
#define IMAP_CAP_QRESYNC
RFC7162.
Definition: private.h:139
#define IMAP_CAP_COMPRESS
RFC4978: COMPRESS=DEFLATE.
Definition: private.h:141
int imap_open_connection(struct ImapAccountData *adata)
Open an IMAP connection.
Definition: imap.c:747
static void imap_mbox_select(struct Mailbox *m)
Select a Mailbox.
Definition: imap.c:1815
char delim
Path delimiter.
Definition: adata.h:75
struct Buffer cmdbuf
Definition: adata.h:73
void mutt_zstrm_wrap_conn(struct Connection *conn)
Wrap a compression layer around a Connection.
Definition: zstrm.c:288
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_expand_path()

int imap_expand_path ( struct Buffer buf)

Buffer wrapper around imap_path_canon()

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

Definition at line 2440 of file imap.c.

2441 {
2443  return imap_path_canon(buf->data, PATH_MAX);
2444 }
int imap_path_canon(char *buf, size_t buflen)
Canonicalise a Mailbox path - Implements MxOps::path_canon() -.
Definition: imap.c:2414
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

Variable Documentation

◆ imap_commands

const struct Command imap_commands[]
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() -.
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 at line 1 of file imap.c.