NeoMutt  2021-10-29-43-g6b8931
Teaching an old dog new tricks
DOXYGEN
command.c File Reference

Send/receive commands to/from an IMAP server. More...

#include "config.h"
#include <ctype.h>
#include <errno.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 "adata.h"
#include "edata.h"
#include "init.h"
#include "mdata.h"
#include "msn.h"
#include "mutt_account.h"
#include "mutt_logging.h"
#include "mutt_socket.h"
#include "mx.h"
+ Include dependency graph for command.c:

Go to the source code of this file.

Macros

#define IMAP_CMD_BUFSIZE   512
 

Functions

static bool cmd_queue_full (struct ImapAccountData *adata)
 Is the IMAP command queue full? More...
 
static struct ImapCommandcmd_new (struct ImapAccountData *adata)
 Create and queue a new command control block. More...
 
static int cmd_queue (struct ImapAccountData *adata, const char *cmdstr, ImapCmdFlags flags)
 Add a IMAP command to the queue. More...
 
static void cmd_handle_fatal (struct ImapAccountData *adata)
 When ImapAccountData is in fatal state, do what we can. More...
 
static int cmd_start (struct ImapAccountData *adata, const char *cmdstr, ImapCmdFlags flags)
 Start a new IMAP command. More...
 
static int cmd_status (const char *s)
 Parse response line for tagged OK/NO/BAD. More...
 
static void cmd_parse_expunge (struct ImapAccountData *adata, const char *s)
 Parse expunge command. More...
 
static void cmd_parse_vanished (struct ImapAccountData *adata, char *s)
 Parse vanished command. More...
 
static void cmd_parse_fetch (struct ImapAccountData *adata, char *s)
 Load fetch response into ImapAccountData. More...
 
static void cmd_parse_capability (struct ImapAccountData *adata, char *s)
 Set capability bits according to CAPABILITY response. More...
 
static void cmd_parse_list (struct ImapAccountData *adata, char *s)
 Parse a server LIST command (list mailboxes) More...
 
static void cmd_parse_lsub (struct ImapAccountData *adata, char *s)
 Parse a server LSUB (list subscribed mailboxes) More...
 
static void cmd_parse_myrights (struct ImapAccountData *adata, const char *s)
 Set rights bits according to MYRIGHTS response. More...
 
static struct Mailboxfind_mailbox (struct ImapAccountData *adata, const char *name)
 Find a Mailbox by its name. More...
 
static void cmd_parse_status (struct ImapAccountData *adata, char *s)
 Parse status from server. More...
 
static void cmd_parse_enabled (struct ImapAccountData *adata, const char *s)
 Record what the server has enabled. More...
 
static void cmd_parse_exists (struct ImapAccountData *adata, const char *pn)
 Parse EXISTS message from serer. More...
 
static int cmd_handle_untagged (struct ImapAccountData *adata)
 Fallback parser for otherwise unhandled messages. More...
 
int imap_cmd_start (struct ImapAccountData *adata, const char *cmdstr)
 Given an IMAP command, send it to the server. More...
 
int imap_cmd_step (struct ImapAccountData *adata)
 Reads server responses from an IMAP command. More...
 
bool imap_code (const char *s)
 Was the command successful. More...
 
const char * imap_cmd_trailer (struct ImapAccountData *adata)
 Extra information after tagged command response if any. More...
 
int imap_exec (struct ImapAccountData *adata, const char *cmdstr, ImapCmdFlags flags)
 Execute a command and wait for the response from the server. More...
 
void imap_cmd_finish (struct ImapAccountData *adata)
 Attempt to perform cleanup. More...
 
int imap_cmd_idle (struct ImapAccountData *adata)
 Enter the IDLE state. More...
 

Variables

static const char *const Capabilities []
 Server capabilities strings that we understand. More...
 

Detailed Description

Send/receive commands to/from an IMAP server.

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 command.c.

Macro Definition Documentation

◆ IMAP_CMD_BUFSIZE

#define IMAP_CMD_BUFSIZE   512

Definition at line 57 of file command.c.

Function Documentation

◆ cmd_queue_full()

static bool cmd_queue_full ( struct ImapAccountData adata)
static

Is the IMAP command queue full?

Parameters
adataImap Account data
Return values
trueQueue is full

Definition at line 93 of file command.c.

94 {
95  if (((adata->nextcmd + 1) % adata->cmdslots) == adata->lastcmd)
96  return true;
97 
98  return false;
99 }
int lastcmd
Last command in the queue.
Definition: adata.h:72
int nextcmd
Next command to be sent.
Definition: adata.h:71
int cmdslots
Size of the command queue.
Definition: adata.h:70
+ Here is the caller graph for this function:

◆ cmd_new()

static struct ImapCommand* cmd_new ( struct ImapAccountData adata)
static

Create and queue a new command control block.

Parameters
adataImap Account data
Return values
NULLThe pipeline is full
ptrNew command

Definition at line 107 of file command.c.

108 {
109  struct ImapCommand *cmd = NULL;
110 
111  if (cmd_queue_full(adata))
112  {
113  mutt_debug(LL_DEBUG3, "IMAP command queue full\n");
114  return NULL;
115  }
116 
117  cmd = adata->cmds + adata->nextcmd;
118  adata->nextcmd = (adata->nextcmd + 1) % adata->cmdslots;
119 
120  snprintf(cmd->seq, sizeof(cmd->seq), "%c%04u", adata->seqid, adata->seqno++);
121  if (adata->seqno > 9999)
122  adata->seqno = 0;
123 
124  cmd->state = IMAP_RES_NEW;
125 
126  return cmd;
127 }
#define mutt_debug(LEVEL,...)
Definition: logging.h:84
static bool cmd_queue_full(struct ImapAccountData *adata)
Is the IMAP command queue full?
Definition: command.c:93
#define IMAP_RES_NEW
ImapCommand.state additions.
Definition: private.h:59
@ LL_DEBUG3
Log at debug level 3.
Definition: logging.h:42
unsigned char seqid
tag sequence prefix
Definition: adata.h:56
struct ImapCommand * cmds
Queue of commands for the server.
Definition: adata.h:69
unsigned int seqno
tag sequence number, e.g. '{seqid}0001'
Definition: adata.h:57
IMAP command structure.
Definition: private.h:161
int state
Command state, e.g. IMAP_RES_NEW.
Definition: private.h:163
char seq[SEQ_LEN+1]
Command tag, e.g. 'a0001'.
Definition: private.h:162
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ cmd_queue()

static int cmd_queue ( struct ImapAccountData adata,
const char *  cmdstr,
ImapCmdFlags  flags 
)
static

Add a IMAP command to the queue.

Parameters
adataImap Account data
cmdstrCommand string
flagsServer flags, see ImapCmdFlags
Return values
0Success
<0Failure, e.g. IMAP_RES_BAD

If the queue is full, attempts to drain it.

Definition at line 139 of file command.c.

140 {
141  if (cmd_queue_full(adata))
142  {
143  mutt_debug(LL_DEBUG3, "Draining IMAP command pipeline\n");
144 
145  const int rc = imap_exec(adata, NULL, flags & IMAP_CMD_POLL);
146 
147  if (rc == IMAP_EXEC_ERROR)
148  return IMAP_RES_BAD;
149  }
150 
151  struct ImapCommand *cmd = cmd_new(adata);
152  if (!cmd)
153  return IMAP_RES_BAD;
154 
155  if (mutt_buffer_add_printf(&adata->cmdbuf, "%s %s\r\n", cmd->seq, cmdstr) < 0)
156  return IMAP_RES_BAD;
157 
158  return 0;
159 }
int mutt_buffer_add_printf(struct Buffer *buf, const char *fmt,...)
Format a string appending a Buffer.
Definition: buffer.c:203
static struct ImapCommand * cmd_new(struct ImapAccountData *adata)
Create and queue a new command control block.
Definition: command.c:107
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:1255
#define IMAP_CMD_POLL
Poll the tcp connection before running the imap command.
Definition: private.h:76
@ IMAP_EXEC_ERROR
Imap command failure.
Definition: private.h:85
#define IMAP_RES_BAD
<tag> BAD ...
Definition: private.h:55
struct Buffer cmdbuf
Definition: adata.h:73
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ cmd_handle_fatal()

static void cmd_handle_fatal ( struct ImapAccountData adata)
static

When ImapAccountData is in fatal state, do what we can.

Parameters
adataImap Account data

Definition at line 165 of file command.c.

166 {
167  adata->status = IMAP_FATAL;
168 
169  if (!adata->mailbox)
170  return;
171 
172  struct ImapMboxData *mdata = adata->mailbox->mdata;
173 
174  if ((adata->state >= IMAP_SELECTED) && (mdata->reopen & IMAP_REOPEN_ALLOW))
175  {
177  mutt_socket_close(adata->conn);
178  mutt_error(_("Mailbox %s@%s closed"), adata->conn->account.user,
179  adata->conn->account.host);
180  adata->state = IMAP_DISCONNECTED;
181  }
182 
183  imap_close_connection(adata);
184  if (!adata->recovering)
185  {
186  adata->recovering = true;
187  if (imap_login(adata))
189  adata->recovering = false;
190  }
191 }
#define mutt_error(...)
Definition: logging.h:87
@ IMAP_DISCONNECTED
Disconnected from server.
Definition: private.h:107
@ IMAP_SELECTED
Mailbox is selected.
Definition: private.h:110
#define IMAP_REOPEN_ALLOW
Allow re-opening a folder upon expunge.
Definition: private.h:66
@ IMAP_FATAL
Unrecoverable error occurred.
Definition: private.h:97
void imap_close_connection(struct ImapAccountData *adata)
Close an IMAP connection.
Definition: imap.c:853
int imap_login(struct ImapAccountData *adata)
Open an IMAP connection.
Definition: imap.c:1849
#define _(a)
Definition: message.h:28
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
Definition: mutt_logging.c:113
void mx_fastclose_mailbox(struct Mailbox *m)
Free up memory associated with the Mailbox.
Definition: mx.c:429
int mutt_socket_close(struct Connection *conn)
Close a socket.
Definition: socket.c:97
char user[128]
Username.
Definition: connaccount.h:56
char host[128]
Server to login to.
Definition: connaccount.h:54
struct ConnAccount account
Account details: username, password, etc.
Definition: connection.h:50
unsigned char state
ImapState, e.g. IMAP_AUTHENTICATED.
Definition: adata.h:44
struct Mailbox * mailbox
Current selected mailbox.
Definition: adata.h:76
unsigned char status
ImapFlags, e.g. IMAP_FATAL.
Definition: adata.h:45
bool recovering
Definition: adata.h:42
struct Connection * conn
Connection to IMAP server.
Definition: adata.h:41
IMAP-specific Mailbox data -.
Definition: mdata.h:39
void * mdata
Driver specific data.
Definition: mailbox.h:136
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ cmd_start()

static int cmd_start ( struct ImapAccountData adata,
const char *  cmdstr,
ImapCmdFlags  flags 
)
static

Start a new IMAP command.

Parameters
adataImap Account data
cmdstrCommand string
flagsCommand flags, see ImapCmdFlags
Return values
0Success
<0Failure, e.g. IMAP_RES_BAD

Definition at line 201 of file command.c.

202 {
203  int rc;
204 
205  if (adata->status == IMAP_FATAL)
206  {
207  cmd_handle_fatal(adata);
208  return -1;
209  }
210 
211  if (cmdstr && ((rc = cmd_queue(adata, cmdstr, flags)) < 0))
212  return rc;
213 
214  if (flags & IMAP_CMD_QUEUE)
215  return 0;
216 
217  if (mutt_buffer_is_empty(&adata->cmdbuf))
218  return IMAP_RES_BAD;
219 
220  rc = mutt_socket_send_d(adata->conn, adata->cmdbuf.data,
222  mutt_buffer_reset(&adata->cmdbuf);
223 
224  /* unidle when command queue is flushed */
225  if (adata->state == IMAP_IDLE)
226  adata->state = IMAP_SELECTED;
227 
228  return (rc < 0) ? IMAP_RES_BAD : 0;
229 }
bool mutt_buffer_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:252
void mutt_buffer_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:79
static int cmd_queue(struct ImapAccountData *adata, const char *cmdstr, ImapCmdFlags flags)
Add a IMAP command to the queue.
Definition: command.c:139
static void cmd_handle_fatal(struct ImapAccountData *adata)
When ImapAccountData is in fatal state, do what we can.
Definition: command.c:165
@ IMAP_IDLE
Connection is idle.
Definition: private.h:113
#define IMAP_CMD_PASS
Command contains a password. Suppress logging.
Definition: private.h:74
#define IMAP_LOG_PASS
Definition: private.h:51
#define IMAP_LOG_CMD
Definition: private.h:49
#define IMAP_CMD_QUEUE
Queue a command, do not execute.
Definition: private.h:75
#define mutt_socket_send_d(conn, buf, dbg)
Definition: mutt_socket.h:38
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:

◆ cmd_status()

static int cmd_status ( const char *  s)
static

Parse response line for tagged OK/NO/BAD.

Parameters
sStatus string from server
Return values
0Success
<0Failure, e.g. IMAP_RES_BAD

Definition at line 237 of file command.c.

238 {
239  s = imap_next_word((char *) s);
240 
241  if (mutt_istr_startswith(s, "OK"))
242  return IMAP_RES_OK;
243  if (mutt_istr_startswith(s, "NO"))
244  return IMAP_RES_NO;
245 
246  return IMAP_RES_BAD;
247 }
#define IMAP_RES_OK
<tag> OK ...
Definition: private.h:56
char * imap_next_word(char *s)
Find where the next IMAP word begins.
Definition: util.c:792
#define IMAP_RES_NO
<tag> NO ...
Definition: private.h:54
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition: string.c:170
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ cmd_parse_expunge()

static void cmd_parse_expunge ( struct ImapAccountData adata,
const char *  s 
)
static

Parse expunge command.

Parameters
adataImap Account data
sString containing MSN of message to expunge

cmd_parse_expunge: mark headers with new sequence ID and mark adata to be reopened at our earliest convenience

Definition at line 257 of file command.c.

258 {
259  unsigned int exp_msn;
260  struct Email *e = NULL;
261 
262  mutt_debug(LL_DEBUG2, "Handling EXPUNGE\n");
263 
264  struct ImapMboxData *mdata = adata->mailbox->mdata;
265 
266  if (!mutt_str_atoui(s, &exp_msn) || (exp_msn < 1) ||
267  (exp_msn > imap_msn_highest(&mdata->msn)))
268  {
269  return;
270  }
271 
272  e = imap_msn_get(&mdata->msn, exp_msn - 1);
273  if (e)
274  {
275  /* imap_expunge_mailbox() will rewrite e->index.
276  * It needs to resort using SORT_ORDER anyway, so setting to INT_MAX
277  * makes the code simpler and possibly more efficient. */
278  e->index = INT_MAX;
279  imap_edata_get(e)->msn = 0;
280  }
281 
282  /* decrement seqno of those above. */
283  const size_t max_msn = imap_msn_highest(&mdata->msn);
284  for (unsigned int cur = exp_msn; cur < max_msn; cur++)
285  {
286  e = imap_msn_get(&mdata->msn, cur);
287  if (e)
288  imap_edata_get(e)->msn--;
289  imap_msn_set(&mdata->msn, cur - 1, e);
290  }
291  imap_msn_shrink(&mdata->msn, 1);
292 
293  mdata->reopen |= IMAP_EXPUNGE_PENDING;
294 }
const char * mutt_str_atoui(const char *str, unsigned int *dst)
Convert ASCII string to an unsigned integer.
Definition: atoi.c:202
struct ImapEmailData * imap_edata_get(struct Email *e)
Get the private data for this Email.
Definition: edata.c:64
#define IMAP_EXPUNGE_PENDING
Messages on the server have been expunged.
Definition: private.h:68
@ LL_DEBUG2
Log at debug level 2.
Definition: logging.h:41
void imap_msn_set(struct MSN *msn, size_t idx, struct Email *e)
Cache an Email into a given position.
Definition: msn.c:91
size_t imap_msn_shrink(struct MSN *msn, size_t num)
Remove a number of entries from the end of the cache.
Definition: msn.c:102
struct Email * imap_msn_get(const struct MSN *msn, size_t idx)
Return the Email associated with an msn.
Definition: msn.c:79
size_t imap_msn_highest(const struct MSN *msn)
Return the highest MSN in use.
Definition: msn.c:68
The envelope/body of an email.
Definition: email.h:37
int index
The absolute (unsorted) message number.
Definition: email.h:110
unsigned int msn
Message Sequence Number.
Definition: edata.h:45
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ cmd_parse_vanished()

static void cmd_parse_vanished ( struct ImapAccountData adata,
char *  s 
)
static

Parse vanished command.

Parameters
adataImap Account data
sString containing MSN of message to expunge

Handle VANISHED (RFC7162), which is like expunge, but passes a seqset of UIDs. An optional (EARLIER) argument specifies not to decrement subsequent MSNs.

Definition at line 304 of file command.c.

305 {
306  bool earlier = false;
307  int rc;
308  unsigned int uid = 0;
309 
310  struct ImapMboxData *mdata = adata->mailbox->mdata;
311 
312  mutt_debug(LL_DEBUG2, "Handling VANISHED\n");
313 
314  if (mutt_istr_startswith(s, "(EARLIER)"))
315  {
316  /* The RFC says we should not decrement msns with the VANISHED EARLIER tag.
317  * My experimentation says that's crap. */
318  earlier = true;
319  s = imap_next_word(s);
320  }
321 
322  char *end_of_seqset = s;
323  while (*end_of_seqset)
324  {
325  if (!strchr("0123456789:,", *end_of_seqset))
326  *end_of_seqset = '\0';
327  else
328  end_of_seqset++;
329  }
330 
331  struct SeqsetIterator *iter = mutt_seqset_iterator_new(s);
332  if (!iter)
333  {
334  mutt_debug(LL_DEBUG2, "VANISHED: empty seqset [%s]?\n", s);
335  return;
336  }
337 
338  while ((rc = mutt_seqset_iterator_next(iter, &uid)) == 0)
339  {
340  struct Email *e = mutt_hash_int_find(mdata->uid_hash, uid);
341  if (!e)
342  continue;
343 
344  unsigned int exp_msn = imap_edata_get(e)->msn;
345 
346  /* imap_expunge_mailbox() will rewrite e->index.
347  * It needs to resort using SORT_ORDER anyway, so setting to INT_MAX
348  * makes the code simpler and possibly more efficient. */
349  e->index = INT_MAX;
350  imap_edata_get(e)->msn = 0;
351 
352  if ((exp_msn < 1) || (exp_msn > imap_msn_highest(&mdata->msn)))
353  {
354  mutt_debug(LL_DEBUG1, "VANISHED: msn for UID %u is incorrect\n", uid);
355  continue;
356  }
357  if (imap_msn_get(&mdata->msn, exp_msn - 1) != e)
358  {
359  mutt_debug(LL_DEBUG1, "VANISHED: msn_index for UID %u is incorrect\n", uid);
360  continue;
361  }
362 
363  imap_msn_remove(&mdata->msn, exp_msn - 1);
364 
365  if (!earlier)
366  {
367  /* decrement seqno of those above. */
368  const size_t max_msn = imap_msn_highest(&mdata->msn);
369  for (unsigned int cur = exp_msn; cur < max_msn; cur++)
370  {
371  e = imap_msn_get(&mdata->msn, cur);
372  if (e)
373  imap_edata_get(e)->msn--;
374  imap_msn_set(&mdata->msn, cur - 1, e);
375  }
376 
377  imap_msn_shrink(&mdata->msn, 1);
378  }
379  }
380 
381  if (rc < 0)
382  mutt_debug(LL_DEBUG1, "VANISHED: illegal seqset %s\n", s);
383 
384  mdata->reopen |= IMAP_EXPUNGE_PENDING;
385 
387 }
void * mutt_hash_int_find(const struct HashTable *table, unsigned int intkey)
Find the HashElem data in a Hash Table element using a key.
Definition: hash.c:392
int mutt_seqset_iterator_next(struct SeqsetIterator *iter, unsigned int *next)
Get the next UID from a Sequence Set.
Definition: util.c:1102
void mutt_seqset_iterator_free(struct SeqsetIterator **ptr)
Free a Sequence Set Iterator.
Definition: util.c:1159
struct SeqsetIterator * mutt_seqset_iterator_new(const char *seqset)
Create a new Sequence Set Iterator.
Definition: util.c:1081
@ LL_DEBUG1
Log at debug level 1.
Definition: logging.h:40
void imap_msn_remove(struct MSN *msn, size_t idx)
Remove an entry from the cache.
Definition: msn.c:112
ImapOpenFlags reopen
Flags, e.g. IMAP_REOPEN_ALLOW.
Definition: mdata.h:44
struct HashTable * uid_hash
Definition: mdata.h:58
UID Sequence Set Iterator.
Definition: private.h:170
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ cmd_parse_fetch()

static void cmd_parse_fetch ( struct ImapAccountData adata,
char *  s 
)
static

Load fetch response into ImapAccountData.

Parameters
adataImap Account data
sString containing MSN of message to fetch

Currently only handles unanticipated FETCH responses, and only FLAGS data. We get these if another client has changed flags for a mailbox we've selected. Of course, a lot of code here duplicates code in message.c.

Definition at line 398 of file command.c.

399 {
400  unsigned int msn, uid;
401  struct Email *e = NULL;
402  char *flags = NULL;
403  int uid_checked = 0;
404  bool server_changes = false;
405 
406  struct ImapMboxData *mdata = imap_mdata_get(adata->mailbox);
407 
408  mutt_debug(LL_DEBUG3, "Handling FETCH\n");
409 
410  if (!mutt_str_atoui(s, &msn))
411  {
412  mutt_debug(LL_DEBUG3, "Skipping FETCH response - illegal MSN\n");
413  return;
414  }
415 
416  if ((msn < 1) || (msn > imap_msn_highest(&mdata->msn)))
417  {
418  mutt_debug(LL_DEBUG3, "Skipping FETCH response - MSN %u out of range\n", msn);
419  return;
420  }
421 
422  e = imap_msn_get(&mdata->msn, msn - 1);
423  if (!e || !e->active)
424  {
425  mutt_debug(LL_DEBUG3, "Skipping FETCH response - MSN %u not in msn_index\n", msn);
426  return;
427  }
428 
429  mutt_debug(LL_DEBUG2, "Message UID %u updated\n", imap_edata_get(e)->uid);
430  /* skip FETCH */
431  s = imap_next_word(s);
432  s = imap_next_word(s);
433 
434  if (*s != '(')
435  {
436  mutt_debug(LL_DEBUG1, "Malformed FETCH response\n");
437  return;
438  }
439  s++;
440 
441  while (*s)
442  {
443  SKIPWS(s);
444  size_t plen = mutt_istr_startswith(s, "FLAGS");
445  if (plen != 0)
446  {
447  flags = s;
448  if (uid_checked)
449  break;
450 
451  s += plen;
452  SKIPWS(s);
453  if (*s != '(')
454  {
455  mutt_debug(LL_DEBUG1, "bogus FLAGS response: %s\n", s);
456  return;
457  }
458  s++;
459  while (*s && (*s != ')'))
460  s++;
461  if (*s == ')')
462  s++;
463  else
464  {
465  mutt_debug(LL_DEBUG1, "Unterminated FLAGS response: %s\n", s);
466  return;
467  }
468  }
469  else if ((plen = mutt_istr_startswith(s, "UID")))
470  {
471  s += plen;
472  SKIPWS(s);
473  if (!mutt_str_atoui(s, &uid))
474  {
475  mutt_debug(LL_DEBUG1, "Illegal UID. Skipping update\n");
476  return;
477  }
478  if (uid != imap_edata_get(e)->uid)
479  {
480  mutt_debug(LL_DEBUG1, "UID vs MSN mismatch. Skipping update\n");
481  return;
482  }
483  uid_checked = 1;
484  if (flags)
485  break;
486  s = imap_next_word(s);
487  }
488  else if ((plen = mutt_istr_startswith(s, "MODSEQ")))
489  {
490  s += plen;
491  SKIPWS(s);
492  if (*s != '(')
493  {
494  mutt_debug(LL_DEBUG1, "bogus MODSEQ response: %s\n", s);
495  return;
496  }
497  s++;
498  while (*s && (*s != ')'))
499  s++;
500  if (*s == ')')
501  s++;
502  else
503  {
504  mutt_debug(LL_DEBUG1, "Unterminated MODSEQ response: %s\n", s);
505  return;
506  }
507  }
508  else if (*s == ')')
509  break; /* end of request */
510  else if (*s)
511  {
512  mutt_debug(LL_DEBUG2, "Only handle FLAGS updates\n");
513  break;
514  }
515  }
516 
517  if (flags)
518  {
519  imap_set_flags(adata->mailbox, e, flags, &server_changes);
520  if (server_changes)
521  {
522  /* If server flags could conflict with NeoMutt's flags, reopen the mailbox. */
523  if (e->changed)
524  mdata->reopen |= IMAP_EXPUNGE_PENDING;
525  else
526  mdata->check_status |= IMAP_FLAGS_PENDING;
527  }
528  }
529 }
struct ImapMboxData * imap_mdata_get(struct Mailbox *m)
Get the Mailbox data for this mailbox.
Definition: mdata.c:59
char * imap_set_flags(struct Mailbox *m, struct Email *e, char *s, bool *server_changes)
Fill the message header according to the server flags.
Definition: message.c:1869
#define IMAP_FLAGS_PENDING
Flags have changed on the server.
Definition: private.h:70
static size_t plen
Length of cached packet.
Definition: pgppacket.c:39
#define SKIPWS(ch)
Definition: string2.h:46
bool active
Message is not to be removed.
Definition: email.h:74
bool changed
Email has been edited.
Definition: email.h:75
struct ListHead flags
Definition: mdata.h:49
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ cmd_parse_capability()

static void cmd_parse_capability ( struct ImapAccountData adata,
char *  s 
)
static

Set capability bits according to CAPABILITY response.

Parameters
adataImap Account data
sCommand string with capabilities

Definition at line 536 of file command.c.

537 {
538  mutt_debug(LL_DEBUG3, "Handling CAPABILITY\n");
539 
540  s = imap_next_word(s);
541  char *bracket = strchr(s, ']');
542  if (bracket)
543  *bracket = '\0';
544  FREE(&adata->capstr);
545  adata->capstr = mutt_str_dup(s);
546  adata->capabilities = 0;
547 
548  while (*s)
549  {
550  for (size_t i = 0; Capabilities[i]; i++)
551  {
552  size_t len = mutt_istr_startswith(s, Capabilities[i]);
553  if (len != 0 && ((s[len] == '\0') || IS_SPACE(s[len])))
554  {
555  adata->capabilities |= (1 << i);
556  mutt_debug(LL_DEBUG3, " Found capability \"%s\": %lu\n", Capabilities[i], i);
557  break;
558  }
559  }
560  s = imap_next_word(s);
561  }
562 }
static const char *const Capabilities[]
Server capabilities strings that we understand.
Definition: command.c:64
#define FREE(x)
Definition: memory.h:40
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:181
#define IS_SPACE(ch)
Definition: string2.h:38
ImapCapFlags capabilities
Capability flags.
Definition: adata.h:55
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:

◆ cmd_parse_list()

static void cmd_parse_list ( struct ImapAccountData adata,
char *  s 
)
static

Parse a server LIST command (list mailboxes)

Parameters
adataImap Account data
sCommand string with folder list

Definition at line 569 of file command.c.

570 {
571  struct ImapList *list = NULL;
572  struct ImapList lb = { 0 };
573  char delimbuf[5]; /* worst case: "\\"\0 */
574  unsigned int litlen;
575 
576  if (adata->cmdresult)
577  list = adata->cmdresult;
578  else
579  list = &lb;
580 
581  memset(list, 0, sizeof(struct ImapList));
582 
583  /* flags */
584  s = imap_next_word(s);
585  if (*s != '(')
586  {
587  mutt_debug(LL_DEBUG1, "Bad LIST response\n");
588  return;
589  }
590  s++;
591  while (*s)
592  {
593  if (mutt_istr_startswith(s, "\\NoSelect"))
594  list->noselect = true;
595  else if (mutt_istr_startswith(s, "\\NonExistent")) /* rfc5258 */
596  list->noselect = true;
597  else if (mutt_istr_startswith(s, "\\NoInferiors"))
598  list->noinferiors = true;
599  else if (mutt_istr_startswith(s, "\\HasNoChildren")) /* rfc5258*/
600  list->noinferiors = true;
601 
602  s = imap_next_word(s);
603  if (*(s - 2) == ')')
604  break;
605  }
606 
607  /* Delimiter */
608  if (!mutt_istr_startswith(s, "NIL"))
609  {
610  delimbuf[0] = '\0';
611  mutt_str_cat(delimbuf, 5, s);
612  imap_unquote_string(delimbuf);
613  list->delim = delimbuf[0];
614  }
615 
616  /* Name */
617  s = imap_next_word(s);
618  /* Notes often responds with literals here. We need a real tokenizer. */
619  if (imap_get_literal_count(s, &litlen) == 0)
620  {
621  if (imap_cmd_step(adata) != IMAP_RES_CONTINUE)
622  {
623  adata->status = IMAP_FATAL;
624  return;
625  }
626 
627  if (strlen(adata->buf) < litlen)
628  {
629  mutt_debug(LL_DEBUG1, "Error parsing LIST mailbox\n");
630  return;
631  }
632 
633  list->name = adata->buf;
634  s = list->name + litlen;
635  if (s[0] != '\0')
636  {
637  s[0] = '\0';
638  s++;
639  SKIPWS(s);
640  }
641  }
642  else
643  {
644  list->name = s;
645  /* Exclude rfc5258 RECURSIVEMATCH CHILDINFO suffix */
646  s = imap_next_word(s);
647  if (s[0] != '\0')
648  s[-1] = '\0';
649  imap_unmunge_mbox_name(adata->unicode, list->name);
650  }
651 
652  if (list->name[0] == '\0')
653  {
654  adata->delim = list->delim;
655  mutt_debug(LL_DEBUG3, "Root delimiter: %c\n", adata->delim);
656  }
657 }
int imap_cmd_step(struct ImapAccountData *adata)
Reads server responses from an IMAP command.
Definition: command.c:1084
void imap_unmunge_mbox_name(bool unicode, char *s)
Remove quoting from a mailbox name.
Definition: util.c:931
int imap_get_literal_count(const char *buf, unsigned int *bytes)
Write number of bytes in an IMAP literal into bytes.
Definition: util.c:748
void imap_unquote_string(char *s)
Equally stupid unquoting routine.
Definition: util.c:877
#define IMAP_RES_CONTINUE
* ...
Definition: private.h:57
char * mutt_str_cat(char *buf, size_t buflen, const char *s)
Concatenate two strings.
Definition: string.c:196
char delim
Path delimiter.
Definition: adata.h:75
struct ImapList * cmdresult
Definition: adata.h:66
bool unicode
If true, we can send UTF-8, and the server will use UTF8 rather than mUTF7.
Definition: adata.h:62
char * buf
Definition: adata.h:59
Items in an IMAP browser.
Definition: private.h:150
bool noselect
Definition: private.h:153
bool noinferiors
Definition: private.h:154
char * name
Definition: private.h:151
char delim
Definition: private.h:152
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ cmd_parse_lsub()

static void cmd_parse_lsub ( struct ImapAccountData adata,
char *  s 
)
static

Parse a server LSUB (list subscribed mailboxes)

Parameters
adataImap Account data
sCommand string with folder list

Definition at line 664 of file command.c.

665 {
666  char buf[256];
667  char quoted_name[256];
668  struct Buffer err;
669  struct Url url = { 0 };
670  struct ImapList list = { 0 };
671 
672  if (adata->cmdresult)
673  {
674  /* caller will handle response itself */
675  cmd_parse_list(adata, s);
676  return;
677  }
678 
679  const bool c_imap_check_subscribed =
680  cs_subset_bool(NeoMutt->sub, "imap_check_subscribed");
681  if (!c_imap_check_subscribed)
682  return;
683 
684  adata->cmdresult = &list;
685  cmd_parse_list(adata, s);
686  adata->cmdresult = NULL;
687  /* noselect is for a gmail quirk */
688  if (!list.name || list.noselect)
689  return;
690 
691  mutt_debug(LL_DEBUG3, "Subscribing to %s\n", list.name);
692 
693  mutt_str_copy(buf, "mailboxes \"", sizeof(buf));
694  mutt_account_tourl(&adata->conn->account, &url);
695  /* escape \ and " */
696  imap_quote_string(quoted_name, sizeof(quoted_name), list.name, true);
697  url.path = quoted_name + 1;
698  url.path[strlen(url.path) - 1] = '\0';
699  const char *const c_imap_user = cs_subset_string(NeoMutt->sub, "imap_user");
700  if (mutt_str_equal(url.user, c_imap_user))
701  url.user = NULL;
702  url_tostring(&url, buf + 11, sizeof(buf) - 11, U_NO_FLAGS);
703  mutt_str_cat(buf, sizeof(buf), "\"");
704  mutt_buffer_init(&err);
705  err.dsize = 256;
706  err.data = mutt_mem_malloc(err.dsize);
707  if (mutt_parse_rc_line(buf, &err))
708  mutt_debug(LL_DEBUG1, "Error adding subscribed mailbox: %s\n", err.data);
709  FREE(&err.data);
710 }
struct Buffer * mutt_buffer_init(struct Buffer *buf)
Initialise a new Buffer.
Definition: buffer.c:46
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:317
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
static void cmd_parse_list(struct ImapAccountData *adata, char *s)
Parse a server LIST command (list mailboxes)
Definition: command.c:569
void imap_quote_string(char *dest, size_t dlen, const char *src, bool quote_backtick)
Quote string according to IMAP rules.
Definition: util.c:840
enum CommandResult mutt_parse_rc_line(const char *line, struct Buffer *err)
Parse a line of user config.
Definition: init.c:1051
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:715
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:560
void mutt_account_tourl(struct ConnAccount *cac, struct Url *url)
Fill URL with info from account.
Definition: mutt_account.c:79
String manipulation buffer.
Definition: buffer.h:34
Container for Accounts, Notifications.
Definition: neomutt.h:37
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
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:

◆ cmd_parse_myrights()

static void cmd_parse_myrights ( struct ImapAccountData adata,
const char *  s 
)
static

Set rights bits according to MYRIGHTS response.

Parameters
adataImap Account data
sCommand string with rights info

Definition at line 717 of file command.c.

718 {
719  mutt_debug(LL_DEBUG2, "Handling MYRIGHTS\n");
720 
721  s = imap_next_word((char *) s);
722  s = imap_next_word((char *) s);
723 
724  /* zero out current rights set */
725  adata->mailbox->rights = 0;
726 
727  while (*s && !isspace((unsigned char) *s))
728  {
729  switch (*s)
730  {
731  case 'a':
732  adata->mailbox->rights |= MUTT_ACL_ADMIN;
733  break;
734  case 'e':
735  adata->mailbox->rights |= MUTT_ACL_EXPUNGE;
736  break;
737  case 'i':
738  adata->mailbox->rights |= MUTT_ACL_INSERT;
739  break;
740  case 'k':
741  adata->mailbox->rights |= MUTT_ACL_CREATE;
742  break;
743  case 'l':
744  adata->mailbox->rights |= MUTT_ACL_LOOKUP;
745  break;
746  case 'p':
747  adata->mailbox->rights |= MUTT_ACL_POST;
748  break;
749  case 'r':
750  adata->mailbox->rights |= MUTT_ACL_READ;
751  break;
752  case 's':
753  adata->mailbox->rights |= MUTT_ACL_SEEN;
754  break;
755  case 't':
756  adata->mailbox->rights |= MUTT_ACL_DELETE;
757  break;
758  case 'w':
759  adata->mailbox->rights |= MUTT_ACL_WRITE;
760  break;
761  case 'x':
762  adata->mailbox->rights |= MUTT_ACL_DELMX;
763  break;
764 
765  /* obsolete rights */
766  case 'c':
768  break;
769  case 'd':
771  break;
772  default:
773  mutt_debug(LL_DEBUG1, "Unknown right: %c\n", *s);
774  }
775  s++;
776  }
777 }
#define MUTT_ACL_CREATE
Create a mailbox.
Definition: mailbox.h:65
#define MUTT_ACL_DELMX
Delete a mailbox.
Definition: mailbox.h:67
#define MUTT_ACL_POST
Post (submit messages to the server)
Definition: mailbox.h:71
#define MUTT_ACL_LOOKUP
Lookup mailbox (visible to 'list')
Definition: mailbox.h:70
#define MUTT_ACL_INSERT
Add/copy into the mailbox (used when editing a message)
Definition: mailbox.h:69
#define MUTT_ACL_DELETE
Delete a message.
Definition: mailbox.h:66
#define MUTT_ACL_EXPUNGE
Expunge messages.
Definition: mailbox.h:68
#define MUTT_ACL_WRITE
Write to a message (for flagging or linking threads)
Definition: mailbox.h:74
#define MUTT_ACL_SEEN
Change the 'seen' status of a message.
Definition: mailbox.h:73
#define MUTT_ACL_ADMIN
Administer the account (get/set permissions)
Definition: mailbox.h:64
#define MUTT_ACL_READ
Read the mailbox.
Definition: mailbox.h:72
AclFlags rights
ACL bits, see AclFlags.
Definition: mailbox.h:121
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ find_mailbox()

static struct Mailbox* find_mailbox ( struct ImapAccountData adata,
const char *  name 
)
static

Find a Mailbox by its name.

Parameters
adataImap Account data
nameMailbox to find
Return values
ptrMailbox

Definition at line 785 of file command.c.

786 {
787  if (!adata || !adata->account || !name)
788  return NULL;
789 
790  struct MailboxNode *np = NULL;
791  STAILQ_FOREACH(np, &adata->account->mailboxes, entries)
792  {
793  struct ImapMboxData *mdata = imap_mdata_get(np->mailbox);
794  if (mutt_str_equal(name, mdata->name))
795  return np->mailbox;
796  }
797 
798  return NULL;
799 }
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:352
struct MailboxList mailboxes
List of Mailboxes.
Definition: account.h:41
struct Account * account
Parent Account.
Definition: adata.h:78
char * name
Mailbox name.
Definition: mdata.h:40
List of Mailboxes.
Definition: mailbox.h:157
struct Mailbox * mailbox
Mailbox in the list.
Definition: mailbox.h:158
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ cmd_parse_status()

static void cmd_parse_status ( struct ImapAccountData adata,
char *  s 
)
static

Parse status from server.

Parameters
adataImap Account data
sCommand string with status info

first cut: just do mailbox update. Later we may wish to cache all mailbox information, even that not desired by mailbox

Definition at line 809 of file command.c.

810 {
811  unsigned int litlen = 0;
812 
813  char *mailbox = imap_next_word(s);
814 
815  /* We need a real tokenizer. */
816  if (imap_get_literal_count(mailbox, &litlen) == 0)
817  {
818  if (imap_cmd_step(adata) != IMAP_RES_CONTINUE)
819  {
820  adata->status = IMAP_FATAL;
821  return;
822  }
823 
824  if (strlen(adata->buf) < litlen)
825  {
826  mutt_debug(LL_DEBUG1, "Error parsing STATUS mailbox\n");
827  return;
828  }
829 
830  mailbox = adata->buf;
831  s = mailbox + litlen;
832  s[0] = '\0';
833  s++;
834  SKIPWS(s);
835  }
836  else
837  {
838  s = imap_next_word(mailbox);
839  s[-1] = '\0';
840  imap_unmunge_mbox_name(adata->unicode, mailbox);
841  }
842 
843  struct Mailbox *m = find_mailbox(adata, mailbox);
844  struct ImapMboxData *mdata = imap_mdata_get(m);
845  if (!mdata)
846  {
847  mutt_debug(LL_DEBUG3, "Received status for an unexpected mailbox: %s\n", mailbox);
848  return;
849  }
850  uint32_t olduv = mdata->uidvalidity;
851  unsigned int oldun = mdata->uid_next;
852 
853  if (*s++ != '(')
854  {
855  mutt_debug(LL_DEBUG1, "Error parsing STATUS\n");
856  return;
857  }
858  while ((s[0] != '\0') && (s[0] != ')'))
859  {
860  char *value = imap_next_word(s);
861 
862  errno = 0;
863  const unsigned long ulcount = strtoul(value, &value, 10);
864  if (((errno == ERANGE) && (ulcount == ULONG_MAX)) || ((unsigned int) ulcount != ulcount))
865  {
866  mutt_debug(LL_DEBUG1, "Error parsing STATUS number\n");
867  return;
868  }
869  const unsigned int count = (unsigned int) ulcount;
870 
871  if (mutt_str_startswith(s, "MESSAGES"))
872  mdata->messages = count;
873  else if (mutt_str_startswith(s, "RECENT"))
874  mdata->recent = count;
875  else if (mutt_str_startswith(s, "UIDNEXT"))
876  mdata->uid_next = count;
877  else if (mutt_str_startswith(s, "UIDVALIDITY"))
878  mdata->uidvalidity = count;
879  else if (mutt_str_startswith(s, "UNSEEN"))
880  mdata->unseen = count;
881 
882  s = value;
883  if ((s[0] != '\0') && (*s != ')'))
884  s = imap_next_word(s);
885  }
886  mutt_debug(LL_DEBUG3, "%s (UIDVALIDITY: %u, UIDNEXT: %u) %d messages, %d recent, %d unseen\n",
887  mdata->name, mdata->uidvalidity, mdata->uid_next, mdata->messages,
888  mdata->recent, mdata->unseen);
889 
890  mutt_debug(LL_DEBUG3, "Running default STATUS handler\n");
891 
892  mutt_debug(LL_DEBUG3, "Found %s in mailbox list (OV: %u ON: %u U: %d)\n",
893  mailbox, olduv, oldun, mdata->unseen);
894 
895  bool new_mail = false;
896  const bool c_mail_check_recent =
897  cs_subset_bool(NeoMutt->sub, "mail_check_recent");
898  if (c_mail_check_recent)
899  {
900  if ((olduv != 0) && (olduv == mdata->uidvalidity))
901  {
902  if (oldun < mdata->uid_next)
903  new_mail = (mdata->unseen > 0);
904  }
905  else if ((olduv == 0) && (oldun == 0))
906  {
907  /* first check per session, use recent. might need a flag for this. */
908  new_mail = (mdata->recent > 0);
909  }
910  else
911  new_mail = (mdata->unseen > 0);
912  }
913  else
914  new_mail = (mdata->unseen > 0);
915 
916  m->has_new = new_mail;
917  m->msg_count = mdata->messages;
918  m->msg_unread = mdata->unseen;
919 
920  // force back to keep detecting new mail until the mailbox is opened
921  if (m->has_new)
922  mdata->uid_next = oldun;
923 
924  struct EventMailbox ev_m = { m };
926 }
static struct Mailbox * find_mailbox(struct ImapAccountData *adata, const char *name)
Find a Mailbox by its name.
Definition: command.c:785
@ NT_MAILBOX_CHANGE
Mailbox has been changed.
Definition: mailbox.h:176
bool notify_send(struct Notify *notify, enum NotifyType event_type, int event_subtype, void *event_data)
Send out a notification message.
Definition: notify.c:171
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:158
@ NT_MAILBOX
Mailbox has changed, NotifyMailbox, EventMailbox.
Definition: notify_type.h:48
An Event that happened to a Mailbox.
Definition: mailbox.h:191
unsigned int uid_next
Definition: mdata.h:51
A mailbox.
Definition: mailbox.h:82
bool has_new
Mailbox has new mail.
Definition: mailbox.h:88
int msg_count
Total number of messages.
Definition: mailbox.h:91
struct Notify * notify
Notifications: NotifyMailbox, EventMailbox.
Definition: mailbox.h:148
int msg_unread
Number of unread messages.
Definition: mailbox.h:92
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ cmd_parse_enabled()

static void cmd_parse_enabled ( struct ImapAccountData adata,
const char *  s 
)
static

Record what the server has enabled.

Parameters
adataImap Account data
sCommand string containing acceptable encodings

Definition at line 933 of file command.c.

934 {
935  mutt_debug(LL_DEBUG2, "Handling ENABLED\n");
936 
937  while ((s = imap_next_word((char *) s)) && (*s != '\0'))
938  {
939  if (mutt_istr_startswith(s, "UTF8=ACCEPT") ||
940  mutt_istr_startswith(s, "UTF8=ONLY"))
941  {
942  adata->unicode = true;
943  }
944  if (mutt_istr_startswith(s, "QRESYNC"))
945  adata->qresync = true;
946  }
947 }
bool qresync
true, if QRESYNC is successfully ENABLE'd
Definition: adata.h:63
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ cmd_parse_exists()

static void cmd_parse_exists ( struct ImapAccountData adata,
const char *  pn 
)
static

Parse EXISTS message from serer.

Parameters
adataImap Account data
pnString containing the total number of messages for the selected mailbox

Definition at line 954 of file command.c.

955 {
956  unsigned int count = 0;
957  mutt_debug(LL_DEBUG2, "Handling EXISTS\n");
958 
959  if (!mutt_str_atoui(pn, &count))
960  {
961  mutt_debug(LL_DEBUG1, "Malformed EXISTS: '%s'\n", pn);
962  return;
963  }
964 
965  struct ImapMboxData *mdata = adata->mailbox->mdata;
966 
967  /* new mail arrived */
968  if (count < imap_msn_highest(&mdata->msn))
969  {
970  /* Notes 6.0.3 has a tendency to report fewer messages exist than
971  * it should. */
972  mutt_debug(LL_DEBUG1, "Message count is out of sync\n");
973  }
974  /* at least the InterChange server sends EXISTS messages freely,
975  * even when there is no new mail */
976  else if (count == imap_msn_highest(&mdata->msn))
977  mutt_debug(LL_DEBUG3, "superfluous EXISTS message\n");
978  else
979  {
980  mutt_debug(LL_DEBUG2, "New mail in %s - %d messages total\n", mdata->name, count);
981  mdata->reopen |= IMAP_NEWMAIL_PENDING;
982  mdata->new_mail_count = count;
983  }
984 }
#define IMAP_NEWMAIL_PENDING
New mail is waiting on the server.
Definition: private.h:69
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ cmd_handle_untagged()

static int cmd_handle_untagged ( struct ImapAccountData adata)
static

Fallback parser for otherwise unhandled messages.

Parameters
adataImap Account data
Return values
0Success
-1Failure

Definition at line 992 of file command.c.

993 {
994  char *s = imap_next_word(adata->buf);
995  char *pn = imap_next_word(s);
996 
997  const bool c_imap_server_noise =
998  cs_subset_bool(NeoMutt->sub, "imap_server_noise");
999  if ((adata->state >= IMAP_SELECTED) && isdigit((unsigned char) *s))
1000  {
1001  /* pn vs. s: need initial seqno */
1002  pn = s;
1003  s = imap_next_word(s);
1004 
1005  /* EXISTS, EXPUNGE, FETCH are always related to the SELECTED mailbox */
1006  if (mutt_istr_startswith(s, "EXISTS"))
1007  cmd_parse_exists(adata, pn);
1008  else if (mutt_istr_startswith(s, "EXPUNGE"))
1009  cmd_parse_expunge(adata, pn);
1010  else if (mutt_istr_startswith(s, "FETCH"))
1011  cmd_parse_fetch(adata, pn);
1012  }
1013  else if ((adata->state >= IMAP_SELECTED) &&
1014  mutt_istr_startswith(s, "VANISHED"))
1015  cmd_parse_vanished(adata, pn);
1016  else if (mutt_istr_startswith(s, "CAPABILITY"))
1017  cmd_parse_capability(adata, s);
1018  else if (mutt_istr_startswith(s, "OK [CAPABILITY"))
1019  cmd_parse_capability(adata, pn);
1020  else if (mutt_istr_startswith(pn, "OK [CAPABILITY"))
1022  else if (mutt_istr_startswith(s, "LIST"))
1023  cmd_parse_list(adata, s);
1024  else if (mutt_istr_startswith(s, "LSUB"))
1025  cmd_parse_lsub(adata, s);
1026  else if (mutt_istr_startswith(s, "MYRIGHTS"))
1027  cmd_parse_myrights(adata, s);
1028  else if (mutt_istr_startswith(s, "SEARCH"))
1029  cmd_parse_search(adata, s);
1030  else if (mutt_istr_startswith(s, "STATUS"))
1031  cmd_parse_status(adata, s);
1032  else if (mutt_istr_startswith(s, "ENABLED"))
1033  cmd_parse_enabled(adata, s);
1034  else if (mutt_istr_startswith(s, "BYE"))
1035  {
1036  mutt_debug(LL_DEBUG2, "Handling BYE\n");
1037 
1038  /* check if we're logging out */
1039  if (adata->status == IMAP_BYE)
1040  return 0;
1041 
1042  /* server shut down our connection */
1043  s += 3;
1044  SKIPWS(s);
1045  mutt_error("%s", s);
1046  cmd_handle_fatal(adata);
1047 
1048  return -1;
1049  }
1050  else if (c_imap_server_noise && mutt_istr_startswith(s, "NO"))
1051  {
1052  mutt_debug(LL_DEBUG2, "Handling untagged NO\n");
1053 
1054  /* Display the warning message from the server */
1055  mutt_error("%s", s + 2);
1056  }
1057 
1058  return 0;
1059 }
static void cmd_parse_capability(struct ImapAccountData *adata, char *s)
Set capability bits according to CAPABILITY response.
Definition: command.c:536
static void cmd_parse_lsub(struct ImapAccountData *adata, char *s)
Parse a server LSUB (list subscribed mailboxes)
Definition: command.c:664
static void cmd_parse_status(struct ImapAccountData *adata, char *s)
Parse status from server.
Definition: command.c:809
static void cmd_parse_exists(struct ImapAccountData *adata, const char *pn)
Parse EXISTS message from serer.
Definition: command.c:954
static void cmd_parse_myrights(struct ImapAccountData *adata, const char *s)
Set rights bits according to MYRIGHTS response.
Definition: command.c:717
static void cmd_parse_expunge(struct ImapAccountData *adata, const char *s)
Parse expunge command.
Definition: command.c:257
static void cmd_parse_enabled(struct ImapAccountData *adata, const char *s)
Record what the server has enabled.
Definition: command.c:933
static void cmd_parse_vanished(struct ImapAccountData *adata, char *s)
Parse vanished command.
Definition: command.c:304
static void cmd_parse_fetch(struct ImapAccountData *adata, char *s)
Load fetch response into ImapAccountData.
Definition: command.c:398
@ IMAP_BYE
Logged out from server.
Definition: private.h:98
void cmd_parse_search(struct ImapAccountData *adata, const char *s)
Store SEARCH response for later use.
Definition: search.c:259
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_cmd_start()

int imap_cmd_start ( struct ImapAccountData adata,
const char *  cmdstr 
)

Given an IMAP command, send it to the server.

Parameters
adataImap Account data
cmdstrCommand string to send
Return values
0Success
<0Failure, e.g. IMAP_RES_BAD

If cmdstr is NULL, sends queued commands.

Definition at line 1070 of file command.c.

1071 {
1072  return cmd_start(adata, cmdstr, IMAP_CMD_NO_FLAGS);
1073 }
static int cmd_start(struct ImapAccountData *adata, const char *cmdstr, ImapCmdFlags flags)
Start a new IMAP command.
Definition: command.c:201
#define IMAP_CMD_NO_FLAGS
No flags are set.
Definition: private.h:73
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_cmd_step()

int imap_cmd_step ( struct ImapAccountData adata)

Reads server responses from an IMAP command.

Parameters
adataImap Account data
Return values
0Success
<0Failure, e.g. IMAP_RES_BAD

detects tagged completion response, handles untagged messages, can read arbitrarily large strings (using malloc, so don't make it too large!).

Definition at line 1084 of file command.c.

1085 {
1086  if (!adata)
1087  return -1;
1088 
1089  size_t len = 0;
1090  int c;
1091  int rc;
1092  int stillrunning = 0;
1093  struct ImapCommand *cmd = NULL;
1094 
1095  if (adata->status == IMAP_FATAL)
1096  {
1097  cmd_handle_fatal(adata);
1098  return IMAP_RES_BAD;
1099  }
1100 
1101  /* read into buffer, expanding buffer as necessary until we have a full
1102  * line */
1103  do
1104  {
1105  if (len == adata->blen)
1106  {
1107  mutt_mem_realloc(&adata->buf, adata->blen + IMAP_CMD_BUFSIZE);
1108  adata->blen = adata->blen + IMAP_CMD_BUFSIZE;
1109  mutt_debug(LL_DEBUG3, "grew buffer to %lu bytes\n", adata->blen);
1110  }
1111 
1112  /* back up over '\0' */
1113  if (len)
1114  len--;
1115  c = mutt_socket_readln_d(adata->buf + len, adata->blen - len, adata->conn, MUTT_SOCK_LOG_FULL);
1116  if (c <= 0)
1117  {
1118  mutt_debug(LL_DEBUG1, "Error reading server response\n");
1119  cmd_handle_fatal(adata);
1120  return IMAP_RES_BAD;
1121  }
1122 
1123  len += c;
1124  }
1125  /* if we've read all the way to the end of the buffer, we haven't read a
1126  * full line (mutt_socket_readln strips the \r, so we always have at least
1127  * one character free when we've read a full line) */
1128  while (len == adata->blen);
1129 
1130  /* don't let one large string make cmd->buf hog memory forever */
1131  if ((adata->blen > IMAP_CMD_BUFSIZE) && (len <= IMAP_CMD_BUFSIZE))
1132  {
1134  adata->blen = IMAP_CMD_BUFSIZE;
1135  mutt_debug(LL_DEBUG3, "shrank buffer to %lu bytes\n", adata->blen);
1136  }
1137 
1138  adata->lastread = mutt_date_epoch();
1139 
1140  /* handle untagged messages. The caller still gets its shot afterwards. */
1141  if ((mutt_str_startswith(adata->buf, "* ") ||
1142  mutt_str_startswith(imap_next_word(adata->buf), "OK [")) &&
1143  cmd_handle_untagged(adata))
1144  {
1145  return IMAP_RES_BAD;
1146  }
1147 
1148  /* server demands a continuation response from us */
1149  if (adata->buf[0] == '+')
1150  return IMAP_RES_RESPOND;
1151 
1152  /* Look for tagged command completions.
1153  *
1154  * Some response handlers can end up recursively calling
1155  * imap_cmd_step() and end up handling all tagged command
1156  * completions.
1157  * (e.g. FETCH->set_flag->set_header_color->~h pattern match.)
1158  *
1159  * Other callers don't even create an adata->cmds entry.
1160  *
1161  * For both these cases, we default to returning OK */
1162  rc = IMAP_RES_OK;
1163  c = adata->lastcmd;
1164  do
1165  {
1166  cmd = &adata->cmds[c];
1167  if (cmd->state == IMAP_RES_NEW)
1168  {
1169  if (mutt_str_startswith(adata->buf, cmd->seq))
1170  {
1171  if (!stillrunning)
1172  {
1173  /* first command in queue has finished - move queue pointer up */
1174  adata->lastcmd = (adata->lastcmd + 1) % adata->cmdslots;
1175  }
1176  cmd->state = cmd_status(adata->buf);
1177  rc = cmd->state;
1178  if (cmd->state == IMAP_RES_NO || cmd->state == IMAP_RES_BAD)
1179  {
1180  mutt_message(_("IMAP command failed: %s"), adata->buf);
1181  }
1182  }
1183  else
1184  stillrunning++;
1185  }
1186 
1187  c = (c + 1) % adata->cmdslots;
1188  } while (c != adata->nextcmd);
1189 
1190  if (stillrunning)
1191  rc = IMAP_RES_CONTINUE;
1192  else
1193  {
1194  mutt_debug(LL_DEBUG3, "IMAP queue drained\n");
1195  imap_cmd_finish(adata);
1196  }
1197 
1198  return rc;
1199 }
time_t mutt_date_epoch(void)
Return the number of seconds since the Unix epoch.
Definition: date.c:427
#define mutt_message(...)
Definition: logging.h:86
static int cmd_handle_untagged(struct ImapAccountData *adata)
Fallback parser for otherwise unhandled messages.
Definition: command.c:992
static int cmd_status(const char *s)
Parse response line for tagged OK/NO/BAD.
Definition: command.c:237
#define IMAP_CMD_BUFSIZE
Definition: command.c:57
void imap_cmd_finish(struct ImapAccountData *adata)
Attempt to perform cleanup.
Definition: command.c:1321
#define IMAP_RES_RESPOND
+
Definition: private.h:58
void mutt_mem_realloc(void *ptr, size_t size)
Resize a block of memory on the heap.
Definition: memory.c:114
#define MUTT_SOCK_LOG_FULL
Definition: mutt_socket.h:31
int mutt_socket_readln_d(char *buf, size_t buflen, struct Connection *conn, int dbg)
Read a line from a socket.
Definition: socket.c:246
time_t lastread
last time we read a command for the server
Definition: adata.h:58
size_t blen
Definition: adata.h:60
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_code()

bool imap_code ( const char *  s)

Was the command successful.

Parameters
sIMAP command status
Return values
1Command result was OK
0NO or BAD

Definition at line 1207 of file command.c.

1208 {
1209  return cmd_status(s) == IMAP_RES_OK;
1210 }
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_cmd_trailer()

const char* imap_cmd_trailer ( struct ImapAccountData adata)

Extra information after tagged command response if any.

Parameters
adataImap Account data
Return values
ptrExtra command information (pointer into adata->buf)
""Error (static string)

Definition at line 1218 of file command.c.

1219 {
1220  static const char *notrailer = "";
1221  const char *s = adata->buf;
1222 
1223  if (!s)
1224  {
1225  mutt_debug(LL_DEBUG2, "not a tagged response\n");
1226  return notrailer;
1227  }
1228 
1229  s = imap_next_word((char *) s);
1230  if (!s || (!mutt_istr_startswith(s, "OK") && !mutt_istr_startswith(s, "NO") &&
1231  !mutt_istr_startswith(s, "BAD")))
1232  {
1233  mutt_debug(LL_DEBUG2, "not a command completion: %s\n", adata->buf);
1234  return notrailer;
1235  }
1236 
1237  s = imap_next_word((char *) s);
1238  if (!s)
1239  return notrailer;
1240 
1241  return s;
1242 }
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_exec()

int imap_exec ( struct ImapAccountData adata,
const char *  cmdstr,
ImapCmdFlags  flags 
)

Execute a command and wait for the response from the server.

Parameters
adataImap Account data
cmdstrCommand to execute
flagsFlags, see ImapCmdFlags
Return values
IMAP_EXEC_SUCCESSCommand successful or queued
IMAP_EXEC_ERRORCommand returned an error
IMAP_EXEC_FATALImap connection failure

Also, handle untagged responses.

Definition at line 1255 of file command.c.

1256 {
1257  int rc;
1258 
1259  if (flags & IMAP_CMD_SINGLE)
1260  {
1261  // Process any existing commands
1262  if (adata->nextcmd != adata->lastcmd)
1263  imap_exec(adata, NULL, IMAP_CMD_POLL);
1264  }
1265 
1266  rc = cmd_start(adata, cmdstr, flags);
1267  if (rc < 0)
1268  {
1269  cmd_handle_fatal(adata);
1270  return IMAP_EXEC_FATAL;
1271  }
1272 
1273  if (flags & IMAP_CMD_QUEUE)
1274  return IMAP_EXEC_SUCCESS;
1275 
1276  const short c_imap_poll_timeout =
1277  cs_subset_number(NeoMutt->sub, "imap_poll_timeout");
1278  if ((flags & IMAP_CMD_POLL) && (c_imap_poll_timeout > 0) &&
1279  ((mutt_socket_poll(adata->conn, c_imap_poll_timeout)) == 0))
1280  {
1281  mutt_error(_("Connection to %s timed out"), adata->conn->account.host);
1282  cmd_handle_fatal(adata);
1283  return IMAP_EXEC_FATAL;
1284  }
1285 
1286  /* Allow interruptions, particularly useful if there are network problems. */
1288  do
1289  {
1290  rc = imap_cmd_step(adata);
1291  // The queue is empty, so the single command has been processed
1292  if ((flags & IMAP_CMD_SINGLE) && (adata->nextcmd == adata->lastcmd))
1293  break;
1294  } while (rc == IMAP_RES_CONTINUE);
1295  mutt_sig_allow_interrupt(false);
1296 
1297  if (rc == IMAP_RES_NO)
1298  return IMAP_EXEC_ERROR;
1299  if (rc != IMAP_RES_OK)
1300  {
1301  if (adata->status != IMAP_FATAL)
1302  return IMAP_EXEC_ERROR;
1303 
1304  mutt_debug(LL_DEBUG1, "command failed: %s\n", adata->buf);
1305  return IMAP_EXEC_FATAL;
1306  }
1307 
1308  return IMAP_EXEC_SUCCESS;
1309 }
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition: helpers.c:169
@ IMAP_EXEC_SUCCESS
Imap command executed or queued successfully.
Definition: private.h:84
@ IMAP_EXEC_FATAL
Imap connection failure.
Definition: private.h:86
#define IMAP_CMD_SINGLE
Run a single command.
Definition: private.h:77
void mutt_sig_allow_interrupt(bool allow)
Allow/disallow Ctrl-C (SIGINT)
Definition: signal.c:238
int mutt_socket_poll(struct Connection *conn, time_t wait_secs)
Checks whether reads would block.
Definition: socket.c:192
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_cmd_finish()

void imap_cmd_finish ( struct ImapAccountData adata)

Attempt to perform cleanup.

Parameters
adataImap Account data

If a reopen is allowed, it attempts to perform cleanup (eg fetch new mail if detected, do expunge). Called automatically by imap_cmd_step(), but may be called at any time.

mdata->check_status is set and will be used later by imap_check_mailbox().

Definition at line 1321 of file command.c.

1322 {
1323  if (!adata)
1324  return;
1325 
1326  if (adata->status == IMAP_FATAL)
1327  {
1328  adata->closing = false;
1329  cmd_handle_fatal(adata);
1330  return;
1331  }
1332 
1333  if (!(adata->state >= IMAP_SELECTED) || (adata->mailbox && adata->closing))
1334  {
1335  adata->closing = false;
1336  return;
1337  }
1338 
1339  adata->closing = false;
1340 
1341  struct ImapMboxData *mdata = imap_mdata_get(adata->mailbox);
1342 
1343  if (mdata && mdata->reopen & IMAP_REOPEN_ALLOW)
1344  {
1345  // First remove expunged emails from the msn_index
1346  if (mdata->reopen & IMAP_EXPUNGE_PENDING)
1347  {
1348  mutt_debug(LL_DEBUG2, "Expunging mailbox\n");
1349  imap_expunge_mailbox(adata->mailbox);
1350  /* Detect whether we've gotten unexpected EXPUNGE messages */
1351  if (!(mdata->reopen & IMAP_EXPUNGE_EXPECTED))
1352  mdata->check_status |= IMAP_EXPUNGE_PENDING;
1354  }
1355 
1356  // Then add new emails to it
1357  if (mdata->reopen & IMAP_NEWMAIL_PENDING)
1358  {
1359  const size_t max_msn = imap_msn_highest(&mdata->msn);
1360  if (mdata->new_mail_count > max_msn)
1361  {
1362  if (!(mdata->reopen & IMAP_EXPUNGE_PENDING))
1363  mdata->check_status |= IMAP_NEWMAIL_PENDING;
1364 
1365  mutt_debug(LL_DEBUG2, "Fetching new mails from %d to %d\n", max_msn + 1,
1366  mdata->new_mail_count);
1367  imap_read_headers(adata->mailbox, max_msn + 1, mdata->new_mail_count, false);
1368  }
1369  }
1370 
1371  // And to finish inform about MUTT_REOPEN if needed
1372  if (mdata->reopen & IMAP_EXPUNGE_PENDING && !(mdata->reopen & IMAP_EXPUNGE_EXPECTED))
1373  mdata->check_status |= IMAP_EXPUNGE_PENDING;
1374 
1375  if (mdata->reopen & IMAP_EXPUNGE_PENDING)
1377  }
1378 
1379  adata->status = 0;
1380 }
int imap_read_headers(struct Mailbox *m, unsigned int msn_begin, unsigned int msn_end, bool initial_download)
Read headers from the server.
Definition: message.c:1298
#define IMAP_EXPUNGE_EXPECTED
Messages will be expunged from the server.
Definition: private.h:67
void imap_expunge_mailbox(struct Mailbox *m)
Purge messages from the server.
Definition: imap.c:676
bool closing
If true, we are waiting for CLOSE completion.
Definition: adata.h:43
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_cmd_idle()

int imap_cmd_idle ( struct ImapAccountData adata)

Enter the IDLE state.

Parameters
adataImap Account data
Return values
0Success
<0Failure, e.g. IMAP_RES_BAD

Definition at line 1388 of file command.c.

1389 {
1390  int rc;
1391 
1392  if (cmd_start(adata, "IDLE", IMAP_CMD_POLL) < 0)
1393  {
1394  cmd_handle_fatal(adata);
1395  return -1;
1396  }
1397 
1398  const short c_imap_poll_timeout =
1399  cs_subset_number(NeoMutt->sub, "imap_poll_timeout");
1400  if ((c_imap_poll_timeout > 0) &&
1401  ((mutt_socket_poll(adata->conn, c_imap_poll_timeout)) == 0))
1402  {
1403  mutt_error(_("Connection to %s timed out"), adata->conn->account.host);
1404  cmd_handle_fatal(adata);
1405  return -1;
1406  }
1407 
1408  do
1409  {
1410  rc = imap_cmd_step(adata);
1411  } while (rc == IMAP_RES_CONTINUE);
1412 
1413  if (rc == IMAP_RES_RESPOND)
1414  {
1415  /* successfully entered IDLE state */
1416  adata->state = IMAP_IDLE;
1417  /* queue automatic exit when next command is issued */
1418  mutt_buffer_addstr(&adata->cmdbuf, "DONE\r\n");
1419  rc = IMAP_RES_OK;
1420  }
1421  if (rc != IMAP_RES_OK)
1422  {
1423  mutt_debug(LL_DEBUG1, "error starting IDLE\n");
1424  return -1;
1425  }
1426 
1427  return 0;
1428 }
size_t mutt_buffer_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:225
+ Here is the call graph for this function:

Variable Documentation

◆ Capabilities

const char* const Capabilities[]
static
Initial value:
= {
"IMAP4",
"IMAP4rev1",
"STATUS",
"ACL",
"NAMESPACE",
"AUTH=CRAM-MD5",
"AUTH=GSSAPI",
"AUTH=ANONYMOUS",
"AUTH=OAUTHBEARER",
"AUTH=XOAUTH2",
"STARTTLS",
"LOGINDISABLED",
"IDLE",
"SASL-IR",
"ENABLE",
"CONDSTORE",
"QRESYNC",
"LIST-EXTENDED",
"COMPRESS=DEFLATE",
"X-GM-EXT-1",
NULL,
}

Server capabilities strings that we understand.

Note
This must be kept in the same order as ImapCaps.

Definition at line 64 of file command.c.