NeoMutt  2021-02-05-666-ge300cd
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 92 of file command.c.

93 {
94  if (((adata->nextcmd + 1) % adata->cmdslots) == adata->lastcmd)
95  return true;
96 
97  return false;
98 }
int lastcmd
Definition: adata.h:72
int nextcmd
Definition: adata.h:71
int cmdslots
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 106 of file command.c.

107 {
108  struct ImapCommand *cmd = NULL;
109 
110  if (cmd_queue_full(adata))
111  {
112  mutt_debug(LL_DEBUG3, "IMAP command queue full\n");
113  return NULL;
114  }
115 
116  cmd = adata->cmds + adata->nextcmd;
117  adata->nextcmd = (adata->nextcmd + 1) % adata->cmdslots;
118 
119  snprintf(cmd->seq, sizeof(cmd->seq), "%c%04u", adata->seqid, adata->seqno++);
120  if (adata->seqno > 9999)
121  adata->seqno = 0;
122 
123  cmd->state = IMAP_RES_NEW;
124 
125  return cmd;
126 }
struct ImapCommand * cmds
Definition: adata.h:69
#define IMAP_RES_NEW
ImapCommand.state additions.
Definition: private.h:59
unsigned int seqno
tag sequence number, e.g. &#39;{seqid}0001&#39;
Definition: adata.h:57
unsigned char seqid
tag sequence prefix
Definition: adata.h:56
int nextcmd
Definition: adata.h:71
IMAP command structure.
Definition: private.h:159
int cmdslots
Definition: adata.h:70
char seq[SEQ_LEN+1]
Command tag, e.g. &#39;a0001&#39;.
Definition: private.h:161
#define mutt_debug(LEVEL,...)
Definition: logging.h:85
int state
Command state, e.g. IMAP_RES_NEW.
Definition: private.h:162
Log at debug level 3.
Definition: logging.h:42
static bool cmd_queue_full(struct ImapAccountData *adata)
Is the IMAP command queue full?
Definition: command.c:92
+ 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 138 of file command.c.

139 {
140  if (cmd_queue_full(adata))
141  {
142  mutt_debug(LL_DEBUG3, "Draining IMAP command pipeline\n");
143 
144  const int rc = imap_exec(adata, NULL, flags & IMAP_CMD_POLL);
145 
146  if (rc == IMAP_EXEC_ERROR)
147  return IMAP_RES_BAD;
148  }
149 
150  struct ImapCommand *cmd = cmd_new(adata);
151  if (!cmd)
152  return IMAP_RES_BAD;
153 
154  if (mutt_buffer_add_printf(&adata->cmdbuf, "%s %s\r\n", cmd->seq, cmdstr) < 0)
155  return IMAP_RES_BAD;
156 
157  return 0;
158 }
#define IMAP_CMD_POLL
Poll the tcp connection before running the imap command.
Definition: private.h:76
static struct ImapCommand * cmd_new(struct ImapAccountData *adata)
Create and queue a new command control block.
Definition: command.c:106
struct Buffer cmdbuf
Definition: adata.h:73
IMAP command structure.
Definition: private.h:159
int mutt_buffer_add_printf(struct Buffer *buf, const char *fmt,...)
Format a string appending a Buffer.
Definition: buffer.c:203
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:1250
char seq[SEQ_LEN+1]
Command tag, e.g. &#39;a0001&#39;.
Definition: private.h:161
#define mutt_debug(LEVEL,...)
Definition: logging.h:85
Imap command failure.
Definition: private.h:85
Log at debug level 3.
Definition: logging.h:42
static bool cmd_queue_full(struct ImapAccountData *adata)
Is the IMAP command queue full?
Definition: command.c:92
#define IMAP_RES_BAD
<tag> BAD ...
Definition: private.h:55
+ 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 164 of file command.c.

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

201 {
202  int rc;
203 
204  if (adata->status == IMAP_FATAL)
205  {
206  cmd_handle_fatal(adata);
207  return -1;
208  }
209 
210  if (cmdstr && ((rc = cmd_queue(adata, cmdstr, flags)) < 0))
211  return rc;
212 
213  if (flags & IMAP_CMD_QUEUE)
214  return 0;
215 
216  if (mutt_buffer_is_empty(&adata->cmdbuf))
217  return IMAP_RES_BAD;
218 
219  rc = mutt_socket_send_d(adata->conn, adata->cmdbuf.data,
221  mutt_buffer_reset(&adata->cmdbuf);
222 
223  /* unidle when command queue is flushed */
224  if (adata->state == IMAP_IDLE)
225  adata->state = IMAP_SELECTED;
226 
227  return (rc < 0) ? IMAP_RES_BAD : 0;
228 }
void mutt_buffer_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:79
Unrecoverable error occurred.
Definition: private.h:97
unsigned char state
ImapState, e.g. IMAP_AUTHENTICATED.
Definition: adata.h:44
#define IMAP_LOG_PASS
Definition: private.h:51
static void cmd_handle_fatal(struct ImapAccountData *adata)
When ImapAccountData is in fatal state, do what we can.
Definition: command.c:164
struct Buffer cmdbuf
Definition: adata.h:73
#define mutt_socket_send_d(conn, buf, dbg)
Definition: mutt_socket.h:38
Mailbox is selected.
Definition: private.h:110
static int cmd_queue(struct ImapAccountData *adata, const char *cmdstr, ImapCmdFlags flags)
Add a IMAP command to the queue.
Definition: command.c:138
struct ListHead flags
Definition: mdata.h:49
#define IMAP_CMD_PASS
Command contains a password. Suppress logging.
Definition: private.h:74
unsigned char status
ImapFlags, e.g. IMAP_FATAL.
Definition: adata.h:45
char * data
Pointer to data.
Definition: buffer.h:35
Connection is idle.
Definition: private.h:113
#define IMAP_LOG_CMD
Definition: private.h:49
#define IMAP_CMD_QUEUE
Queue a command, do not execute.
Definition: private.h:75
bool mutt_buffer_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:252
struct Connection * conn
Definition: adata.h:41
#define IMAP_RES_BAD
<tag> BAD ...
Definition: private.h:55
+ 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 236 of file command.c.

237 {
238  s = imap_next_word((char *) s);
239 
240  if (mutt_istr_startswith(s, "OK"))
241  return IMAP_RES_OK;
242  if (mutt_istr_startswith(s, "NO"))
243  return IMAP_RES_NO;
244 
245  return IMAP_RES_BAD;
246 }
#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
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition: string.c:172
#define IMAP_RES_NO
<tag> NO ...
Definition: private.h:54
#define IMAP_RES_BAD
<tag> BAD ...
Definition: private.h:55
+ 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 256 of file command.c.

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

304 {
305  bool earlier = false;
306  int rc;
307  unsigned int uid = 0;
308 
309  struct ImapMboxData *mdata = adata->mailbox->mdata;
310 
311  mutt_debug(LL_DEBUG2, "Handling VANISHED\n");
312 
313  if (mutt_istr_startswith(s, "(EARLIER)"))
314  {
315  /* The RFC says we should not decrement msns with the VANISHED EARLIER tag.
316  * My experimentation says that's crap. */
317  earlier = true;
318  s = imap_next_word(s);
319  }
320 
321  char *end_of_seqset = s;
322  while (*end_of_seqset)
323  {
324  if (!strchr("0123456789:,", *end_of_seqset))
325  *end_of_seqset = '\0';
326  else
327  end_of_seqset++;
328  }
329 
330  struct SeqsetIterator *iter = mutt_seqset_iterator_new(s);
331  if (!iter)
332  {
333  mutt_debug(LL_DEBUG2, "VANISHED: empty seqset [%s]?\n", s);
334  return;
335  }
336 
337  while ((rc = mutt_seqset_iterator_next(iter, &uid)) == 0)
338  {
339  struct Email *e = mutt_hash_int_find(mdata->uid_hash, uid);
340  if (!e)
341  continue;
342 
343  unsigned int exp_msn = imap_edata_get(e)->msn;
344 
345  /* imap_expunge_mailbox() will rewrite e->index.
346  * It needs to resort using SORT_ORDER anyway, so setting to INT_MAX
347  * makes the code simpler and possibly more efficient. */
348  e->index = INT_MAX;
349  imap_edata_get(e)->msn = 0;
350 
351  if ((exp_msn < 1) || (exp_msn > imap_msn_highest(&mdata->msn)))
352  {
353  mutt_debug(LL_DEBUG1, "VANISHED: msn for UID %u is incorrect\n", uid);
354  continue;
355  }
356  if (imap_msn_get(&mdata->msn, exp_msn - 1) != e)
357  {
358  mutt_debug(LL_DEBUG1, "VANISHED: msn_index for UID %u is incorrect\n", uid);
359  continue;
360  }
361 
362  imap_msn_remove(&mdata->msn, exp_msn - 1);
363 
364  if (!earlier)
365  {
366  /* decrement seqno of those above. */
367  const size_t max_msn = imap_msn_highest(&mdata->msn);
368  for (unsigned int cur = exp_msn; cur < max_msn; cur++)
369  {
370  e = imap_msn_get(&mdata->msn, cur);
371  if (e)
372  imap_edata_get(e)->msn--;
373  imap_msn_set(&mdata->msn, cur - 1, e);
374  }
375 
376  imap_msn_shrink(&mdata->msn, 1);
377  }
378  }
379 
380  if (rc < 0)
381  mutt_debug(LL_DEBUG1, "VANISHED: illegal seqset %s\n", s);
382 
383  mdata->reopen |= IMAP_EXPUNGE_PENDING;
384 
386 }
The envelope/body of an email.
Definition: email.h:37
ImapOpenFlags reopen
Flags, e.g. IMAP_REOPEN_ALLOW.
Definition: mdata.h:44
struct SeqsetIterator * mutt_seqset_iterator_new(const char *seqset)
Create a new Sequence Set Iterator.
Definition: util.c:1081
struct Mailbox * mailbox
Current selected mailbox.
Definition: adata.h:76
struct ImapEmailData * imap_edata_get(struct Email *e)
Get the private data for this Email.
Definition: edata.c:64
unsigned int msn
Message Sequence Number.
Definition: edata.h:45
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:384
char * imap_next_word(char *s)
Find where the next IMAP word begins.
Definition: util.c:792
void imap_msn_remove(struct MSN *msn, size_t idx)
Remove an entry from the cache.
Definition: msn.c:112
Log at debug level 2.
Definition: logging.h:41
struct Email * imap_msn_get(const struct MSN *msn, size_t idx)
Return the Email associated with an msn.
Definition: msn.c:79
void * mdata
Driver specific data.
Definition: mailbox.h:136
struct HashTable * uid_hash
Definition: mdata.h:58
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
void mutt_seqset_iterator_free(struct SeqsetIterator **ptr)
Free a Sequence Set Iterator.
Definition: util.c:1159
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition: string.c:172
#define mutt_debug(LEVEL,...)
Definition: logging.h:85
size_t imap_msn_highest(const struct MSN *msn)
Return the highest MSN in use.
Definition: msn.c:68
Log at debug level 1.
Definition: logging.h:40
IMAP-specific Mailbox data -.
Definition: mdata.h:38
int index
The absolute (unsorted) message number.
Definition: email.h:86
void imap_msn_set(struct MSN *msn, size_t idx, struct Email *e)
Cache an Email into a given position.
Definition: msn.c:91
#define IMAP_EXPUNGE_PENDING
Messages on the server have been expunged.
Definition: private.h:68
int mutt_seqset_iterator_next(struct SeqsetIterator *iter, unsigned int *next)
Get the next UID from a Sequence Set.
Definition: util.c:1102
UID Sequence Set Iterator.
Definition: private.h:168
+ 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 397 of file command.c.

398 {
399  unsigned int msn, uid;
400  struct Email *e = NULL;
401  char *flags = NULL;
402  int uid_checked = 0;
403  bool server_changes = false;
404 
405  struct ImapMboxData *mdata = imap_mdata_get(adata->mailbox);
406 
407  mutt_debug(LL_DEBUG3, "Handling FETCH\n");
408 
409  if (mutt_str_atoui(s, &msn) < 0)
410  {
411  mutt_debug(LL_DEBUG3, "Skipping FETCH response - illegal MSN\n");
412  return;
413  }
414 
415  if ((msn < 1) || (msn > imap_msn_highest(&mdata->msn)))
416  {
417  mutt_debug(LL_DEBUG3, "Skipping FETCH response - MSN %u out of range\n", msn);
418  return;
419  }
420 
421  e = imap_msn_get(&mdata->msn, msn - 1);
422  if (!e || !e->active)
423  {
424  mutt_debug(LL_DEBUG3, "Skipping FETCH response - MSN %u not in msn_index\n", msn);
425  return;
426  }
427 
428  mutt_debug(LL_DEBUG2, "Message UID %u updated\n", imap_edata_get(e)->uid);
429  /* skip FETCH */
430  s = imap_next_word(s);
431  s = imap_next_word(s);
432 
433  if (*s != '(')
434  {
435  mutt_debug(LL_DEBUG1, "Malformed FETCH response\n");
436  return;
437  }
438  s++;
439 
440  while (*s)
441  {
442  SKIPWS(s);
443  size_t plen = mutt_istr_startswith(s, "FLAGS");
444  if (plen != 0)
445  {
446  flags = s;
447  if (uid_checked)
448  break;
449 
450  s += plen;
451  SKIPWS(s);
452  if (*s != '(')
453  {
454  mutt_debug(LL_DEBUG1, "bogus FLAGS response: %s\n", s);
455  return;
456  }
457  s++;
458  while (*s && (*s != ')'))
459  s++;
460  if (*s == ')')
461  s++;
462  else
463  {
464  mutt_debug(LL_DEBUG1, "Unterminated FLAGS response: %s\n", s);
465  return;
466  }
467  }
468  else if ((plen = mutt_istr_startswith(s, "UID")))
469  {
470  s += plen;
471  SKIPWS(s);
472  if (mutt_str_atoui(s, &uid) < 0)
473  {
474  mutt_debug(LL_DEBUG1, "Illegal UID. Skipping update\n");
475  return;
476  }
477  if (uid != imap_edata_get(e)->uid)
478  {
479  mutt_debug(LL_DEBUG1, "UID vs MSN mismatch. Skipping update\n");
480  return;
481  }
482  uid_checked = 1;
483  if (flags)
484  break;
485  s = imap_next_word(s);
486  }
487  else if ((plen = mutt_istr_startswith(s, "MODSEQ")))
488  {
489  s += plen;
490  SKIPWS(s);
491  if (*s != '(')
492  {
493  mutt_debug(LL_DEBUG1, "bogus MODSEQ response: %s\n", s);
494  return;
495  }
496  s++;
497  while (*s && (*s != ')'))
498  s++;
499  if (*s == ')')
500  s++;
501  else
502  {
503  mutt_debug(LL_DEBUG1, "Unterminated MODSEQ response: %s\n", s);
504  return;
505  }
506  }
507  else if (*s == ')')
508  break; /* end of request */
509  else if (*s)
510  {
511  mutt_debug(LL_DEBUG2, "Only handle FLAGS updates\n");
512  break;
513  }
514  }
515 
516  if (flags)
517  {
518  imap_set_flags(adata->mailbox, e, flags, &server_changes);
519  if (server_changes)
520  {
521  /* If server flags could conflict with NeoMutt's flags, reopen the mailbox. */
522  if (e->changed)
523  mdata->reopen |= IMAP_EXPUNGE_PENDING;
524  else
526  }
527  }
528 }
The envelope/body of an email.
Definition: email.h:37
static size_t plen
Length of cached packet.
Definition: pgppacket.c:39
#define IMAP_FLAGS_PENDING
Flags have changed on the server.
Definition: private.h:70
ImapOpenFlags reopen
Flags, e.g. IMAP_REOPEN_ALLOW.
Definition: mdata.h:44
struct Mailbox * mailbox
Current selected mailbox.
Definition: adata.h:76
bool changed
Email has been edited.
Definition: email.h:48
struct ImapEmailData * imap_edata_get(struct Email *e)
Get the private data for this Email.
Definition: edata.c:64
char * imap_next_word(char *s)
Find where the next IMAP word begins.
Definition: util.c:792
Log at debug level 2.
Definition: logging.h:41
struct Email * imap_msn_get(const struct MSN *msn, size_t idx)
Return the Email associated with an msn.
Definition: msn.c:79
struct ImapMboxData * imap_mdata_get(struct Mailbox *m)
Get the Mailbox data for this mailbox.
Definition: mdata.c:59
ImapOpenFlags check_status
Flags, e.g. IMAP_NEWMAIL_PENDING.
Definition: mdata.h:45
#define SKIPWS(ch)
Definition: string2.h:46
void * mdata
Driver specific data.
Definition: mailbox.h:136
bool active
Message is not to be removed.
Definition: email.h:59
int mutt_str_atoui(const char *str, unsigned int *dst)
Convert ASCII string to an unsigned integer.
Definition: string.c:282
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition: string.c:172
#define mutt_debug(LEVEL,...)
Definition: logging.h:85
size_t imap_msn_highest(const struct MSN *msn)
Return the highest MSN in use.
Definition: msn.c:68
Log at debug level 1.
Definition: logging.h:40
IMAP-specific Mailbox data -.
Definition: mdata.h:38
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:1870
#define IMAP_EXPUNGE_PENDING
Messages on the server have been expunged.
Definition: private.h:68
Log at debug level 3.
Definition: logging.h:42
+ 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 535 of file command.c.

536 {
537  mutt_debug(LL_DEBUG3, "Handling CAPABILITY\n");
538 
539  s = imap_next_word(s);
540  char *bracket = strchr(s, ']');
541  if (bracket)
542  *bracket = '\0';
543  FREE(&adata->capstr);
544  adata->capstr = mutt_str_dup(s);
545  adata->capabilities = 0;
546 
547  while (*s)
548  {
549  for (size_t i = 0; Capabilities[i]; i++)
550  {
551  size_t len = mutt_istr_startswith(s, Capabilities[i]);
552  if (len != 0 && ((s[len] == '\0') || IS_SPACE(s[len])))
553  {
554  adata->capabilities |= (1 << i);
555  mutt_debug(LL_DEBUG3, " Found capability \"%s\": %lu\n", Capabilities[i], i);
556  break;
557  }
558  }
559  s = imap_next_word(s);
560  }
561 }
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:370
char * capstr
Definition: adata.h:54
char * imap_next_word(char *s)
Find where the next IMAP word begins.
Definition: util.c:792
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition: string.c:172
ImapCapFlags capabilities
Definition: adata.h:55
#define mutt_debug(LEVEL,...)
Definition: logging.h:85
#define IS_SPACE(ch)
Definition: string2.h:38
#define FREE(x)
Definition: memory.h:40
Log at debug level 3.
Definition: logging.h:42
static const char *const Capabilities[]
Server capabilities strings that we understand.
Definition: command.c:64
+ 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 568 of file command.c.

569 {
570  struct ImapList *list = NULL;
571  struct ImapList lb = { 0 };
572  char delimbuf[5]; /* worst case: "\\"\0 */
573  unsigned int litlen;
574 
575  if (adata->cmdresult)
576  list = adata->cmdresult;
577  else
578  list = &lb;
579 
580  memset(list, 0, sizeof(struct ImapList));
581 
582  /* flags */
583  s = imap_next_word(s);
584  if (*s != '(')
585  {
586  mutt_debug(LL_DEBUG1, "Bad LIST response\n");
587  return;
588  }
589  s++;
590  while (*s)
591  {
592  if (mutt_istr_startswith(s, "\\NoSelect"))
593  list->noselect = true;
594  else if (mutt_istr_startswith(s, "\\NonExistent")) /* rfc5258 */
595  list->noselect = true;
596  else if (mutt_istr_startswith(s, "\\NoInferiors"))
597  list->noinferiors = true;
598  else if (mutt_istr_startswith(s, "\\HasNoChildren")) /* rfc5258*/
599  list->noinferiors = true;
600 
601  s = imap_next_word(s);
602  if (*(s - 2) == ')')
603  break;
604  }
605 
606  /* Delimiter */
607  if (!mutt_istr_startswith(s, "NIL"))
608  {
609  delimbuf[0] = '\0';
610  mutt_str_cat(delimbuf, 5, s);
611  imap_unquote_string(delimbuf);
612  list->delim = delimbuf[0];
613  }
614 
615  /* Name */
616  s = imap_next_word(s);
617  /* Notes often responds with literals here. We need a real tokenizer. */
618  if (imap_get_literal_count(s, &litlen) == 0)
619  {
620  if (imap_cmd_step(adata) != IMAP_RES_CONTINUE)
621  {
622  adata->status = IMAP_FATAL;
623  return;
624  }
625 
626  if (strlen(adata->buf) < litlen)
627  {
628  mutt_debug(LL_DEBUG1, "Error parsing LIST mailbox\n");
629  return;
630  }
631 
632  list->name = adata->buf;
633  s = list->name + litlen;
634  if (s[0] != '\0')
635  {
636  s[0] = '\0';
637  s++;
638  SKIPWS(s);
639  }
640  }
641  else
642  {
643  list->name = s;
644  /* Exclude rfc5258 RECURSIVEMATCH CHILDINFO suffix */
645  s = imap_next_word(s);
646  if (s[0] != '\0')
647  s[-1] = '\0';
648  imap_unmunge_mbox_name(adata->unicode, list->name);
649  }
650 
651  if (list->name[0] == '\0')
652  {
653  adata->delim = list->delim;
654  mutt_debug(LL_DEBUG3, "Root delimiter: %c\n", adata->delim);
655  }
656 }
int imap_cmd_step(struct ImapAccountData *adata)
Reads server responses from an IMAP command.
Definition: command.c:1079
Unrecoverable error occurred.
Definition: private.h:97
Items in an IMAP browser.
Definition: private.h:148
char delim
Definition: private.h:151
char * imap_next_word(char *s)
Find where the next IMAP word begins.
Definition: util.c:792
#define SKIPWS(ch)
Definition: string2.h:46
char delim
Definition: adata.h:75
void imap_unquote_string(char *s)
equally stupid unquoting routine
Definition: util.c:877
char * name
Definition: private.h:150
unsigned char status
ImapFlags, e.g. IMAP_FATAL.
Definition: adata.h:45
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition: string.c:172
#define mutt_debug(LEVEL,...)
Definition: logging.h:85
bool noinferiors
Definition: private.h:153
char * mutt_str_cat(char *buf, size_t buflen, const char *s)
Concatenate two strings.
Definition: string.c:385
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
Log at debug level 1.
Definition: logging.h:40
char * buf
Definition: adata.h:59
struct ImapList * cmdresult
Definition: adata.h:66
#define IMAP_RES_CONTINUE
* ...
Definition: private.h:57
void imap_unmunge_mbox_name(bool unicode, char *s)
Remove quoting from a mailbox name.
Definition: util.c:931
Log at debug level 3.
Definition: logging.h:42
bool noselect
Definition: private.h:152
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:

◆ 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 663 of file command.c.

664 {
665  char buf[256];
666  char quoted_name[256];
667  struct Buffer err;
668  struct Url url = { 0 };
669  struct ImapList list = { 0 };
670 
671  if (adata->cmdresult)
672  {
673  /* caller will handle response itself */
674  cmd_parse_list(adata, s);
675  return;
676  }
677 
678  const bool c_imap_check_subscribed =
679  cs_subset_bool(NeoMutt->sub, "imap_check_subscribed");
680  if (!c_imap_check_subscribed)
681  return;
682 
683  adata->cmdresult = &list;
684  cmd_parse_list(adata, s);
685  adata->cmdresult = NULL;
686  /* noselect is for a gmail quirk */
687  if (!list.name || list.noselect)
688  return;
689 
690  mutt_debug(LL_DEBUG3, "Subscribing to %s\n", list.name);
691 
692  mutt_str_copy(buf, "mailboxes \"", sizeof(buf));
693  mutt_account_tourl(&adata->conn->account, &url);
694  /* escape \ and " */
695  imap_quote_string(quoted_name, sizeof(quoted_name), list.name, true);
696  url.path = quoted_name + 1;
697  url.path[strlen(url.path) - 1] = '\0';
698  const char *const c_imap_user = cs_subset_string(NeoMutt->sub, "imap_user");
699  if (mutt_str_equal(url.user, c_imap_user))
700  url.user = NULL;
701  url_tostring(&url, buf + 11, sizeof(buf) - 11, U_NO_FLAGS);
702  mutt_str_cat(buf, sizeof(buf), "\"");
703  mutt_buffer_init(&err);
704  err.dsize = 256;
705  err.data = mutt_mem_malloc(err.dsize);
706  if (mutt_parse_rc_line(buf, &err))
707  mutt_debug(LL_DEBUG1, "Error adding subscribed mailbox: %s\n", err.data);
708  FREE(&err.data);
709 }
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:904
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
struct ConnAccount account
Account details: username, password, etc.
Definition: connection.h:40
A parsed URL proto://user:password@host:port/path?a=1&b=2
Definition: url.h:68
String manipulation buffer.
Definition: buffer.h:33
enum CommandResult mutt_parse_rc_line(const char *line, struct Buffer *err)
Parse a line of user config.
Definition: init.c:1039
#define U_NO_FLAGS
Definition: url.h:49
Items in an IMAP browser.
Definition: private.h:148
Container for Accounts, Notifications.
Definition: neomutt.h:36
void mutt_account_tourl(struct ConnAccount *cac, struct Url *url)
Fill URL with info from account.
Definition: mutt_account.c:79
char * user
Username.
Definition: url.h:71
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
char * name
Definition: private.h:150
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
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:317
#define mutt_debug(LEVEL,...)
Definition: logging.h:85
char * path
Path.
Definition: url.h:75
char * mutt_str_cat(char *buf, size_t buflen, const char *s)
Concatenate two strings.
Definition: string.c:385
Log at debug level 1.
Definition: logging.h:40
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:749
#define FREE(x)
Definition: memory.h:40
struct ImapList * cmdresult
Definition: adata.h:66
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
struct Buffer * mutt_buffer_init(struct Buffer *buf)
Initialise a new Buffer.
Definition: buffer.c:46
static void cmd_parse_list(struct ImapAccountData *adata, char *s)
Parse a server LIST command (list mailboxes)
Definition: command.c:568
Log at debug level 3.
Definition: logging.h:42
bool noselect
Definition: private.h:152
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
struct Connection * conn
Definition: adata.h:41
+ 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 716 of file command.c.

717 {
718  mutt_debug(LL_DEBUG2, "Handling MYRIGHTS\n");
719 
720  s = imap_next_word((char *) s);
721  s = imap_next_word((char *) s);
722 
723  /* zero out current rights set */
724  adata->mailbox->rights = 0;
725 
726  while (*s && !isspace((unsigned char) *s))
727  {
728  switch (*s)
729  {
730  case 'a':
731  adata->mailbox->rights |= MUTT_ACL_ADMIN;
732  break;
733  case 'e':
734  adata->mailbox->rights |= MUTT_ACL_EXPUNGE;
735  break;
736  case 'i':
737  adata->mailbox->rights |= MUTT_ACL_INSERT;
738  break;
739  case 'k':
740  adata->mailbox->rights |= MUTT_ACL_CREATE;
741  break;
742  case 'l':
743  adata->mailbox->rights |= MUTT_ACL_LOOKUP;
744  break;
745  case 'p':
746  adata->mailbox->rights |= MUTT_ACL_POST;
747  break;
748  case 'r':
749  adata->mailbox->rights |= MUTT_ACL_READ;
750  break;
751  case 's':
752  adata->mailbox->rights |= MUTT_ACL_SEEN;
753  break;
754  case 't':
755  adata->mailbox->rights |= MUTT_ACL_DELETE;
756  break;
757  case 'w':
758  adata->mailbox->rights |= MUTT_ACL_WRITE;
759  break;
760  case 'x':
761  adata->mailbox->rights |= MUTT_ACL_DELMX;
762  break;
763 
764  /* obsolete rights */
765  case 'c':
767  break;
768  case 'd':
770  break;
771  default:
772  mutt_debug(LL_DEBUG1, "Unknown right: %c\n", *s);
773  }
774  s++;
775  }
776 }
#define MUTT_ACL_READ
Read the mailbox.
Definition: mailbox.h:72
#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_INSERT
Add/copy into the mailbox (used when editing a message)
Definition: mailbox.h:69
struct Mailbox * mailbox
Current selected mailbox.
Definition: adata.h:76
#define MUTT_ACL_EXPUNGE
Expunge messages.
Definition: mailbox.h:68
#define MUTT_ACL_DELETE
Delete a message.
Definition: mailbox.h:66
char * imap_next_word(char *s)
Find where the next IMAP word begins.
Definition: util.c:792
Log at debug level 2.
Definition: logging.h:41
#define MUTT_ACL_POST
Post (submit messages to the server)
Definition: mailbox.h:71
#define MUTT_ACL_WRITE
Write to a message (for flagging or linking threads)
Definition: mailbox.h:74
AclFlags rights
ACL bits, see AclFlags.
Definition: mailbox.h:121
#define mutt_debug(LEVEL,...)
Definition: logging.h:85
Log at debug level 1.
Definition: logging.h:40
#define MUTT_ACL_ADMIN
Administer the account (get/set permissions)
Definition: mailbox.h:64
#define MUTT_ACL_SEEN
Change the &#39;seen&#39; status of a message.
Definition: mailbox.h:73
#define MUTT_ACL_LOOKUP
Lookup mailbox (visible to &#39;list&#39;)
Definition: mailbox.h:70
+ 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 784 of file command.c.

785 {
786  if (!adata || !adata->account || !name)
787  return NULL;
788 
789  struct MailboxNode *np = NULL;
790  STAILQ_FOREACH(np, &adata->account->mailboxes, entries)
791  {
792  struct ImapMboxData *mdata = imap_mdata_get(np->mailbox);
793  if (mutt_str_equal(name, mdata->name))
794  return np->mailbox;
795  }
796 
797  return NULL;
798 }
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:904
struct MailboxList mailboxes
List of Mailboxes.
Definition: account.h:41
struct ImapMboxData * imap_mdata_get(struct Mailbox *m)
Get the Mailbox data for this mailbox.
Definition: mdata.c:59
void * mdata
Driver specific data.
Definition: mailbox.h:136
struct Account * account
Parent Account.
Definition: adata.h:78
char * name
Definition: private.h:150
char * name
Mailbox name.
Definition: mdata.h:40
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:352
IMAP-specific Mailbox data -.
Definition: mdata.h:38
List of Mailboxes.
Definition: mailbox.h:156
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 808 of file command.c.

809 {
810  unsigned int litlen = 0;
811 
812  char *mailbox = imap_next_word(s);
813 
814  /* We need a real tokenizer. */
815  if (imap_get_literal_count(mailbox, &litlen) == 0)
816  {
817  if (imap_cmd_step(adata) != IMAP_RES_CONTINUE)
818  {
819  adata->status = IMAP_FATAL;
820  return;
821  }
822 
823  if (strlen(adata->buf) < litlen)
824  {
825  mutt_debug(LL_DEBUG1, "Error parsing STATUS mailbox\n");
826  return;
827  }
828 
829  mailbox = adata->buf;
830  s = mailbox + litlen;
831  s[0] = '\0';
832  s++;
833  SKIPWS(s);
834  }
835  else
836  {
837  s = imap_next_word(mailbox);
838  s[-1] = '\0';
839  imap_unmunge_mbox_name(adata->unicode, mailbox);
840  }
841 
842  struct Mailbox *m = find_mailbox(adata, mailbox);
843  struct ImapMboxData *mdata = imap_mdata_get(m);
844  if (!mdata)
845  {
846  mutt_debug(LL_DEBUG3, "Received status for an unexpected mailbox: %s\n", mailbox);
847  return;
848  }
849  uint32_t olduv = mdata->uidvalidity;
850  unsigned int oldun = mdata->uid_next;
851 
852  if (*s++ != '(')
853  {
854  mutt_debug(LL_DEBUG1, "Error parsing STATUS\n");
855  return;
856  }
857  while ((s[0] != '\0') && (s[0] != ')'))
858  {
859  char *value = imap_next_word(s);
860 
861  errno = 0;
862  const unsigned long ulcount = strtoul(value, &value, 10);
863  if (((errno == ERANGE) && (ulcount == ULONG_MAX)) || ((unsigned int) ulcount != ulcount))
864  {
865  mutt_debug(LL_DEBUG1, "Error parsing STATUS number\n");
866  return;
867  }
868  const unsigned int count = (unsigned int) ulcount;
869 
870  if (mutt_str_startswith(s, "MESSAGES"))
871  mdata->messages = count;
872  else if (mutt_str_startswith(s, "RECENT"))
873  mdata->recent = count;
874  else if (mutt_str_startswith(s, "UIDNEXT"))
875  mdata->uid_next = count;
876  else if (mutt_str_startswith(s, "UIDVALIDITY"))
877  mdata->uidvalidity = count;
878  else if (mutt_str_startswith(s, "UNSEEN"))
879  mdata->unseen = count;
880 
881  s = value;
882  if ((s[0] != '\0') && (*s != ')'))
883  s = imap_next_word(s);
884  }
885  mutt_debug(LL_DEBUG3, "%s (UIDVALIDITY: %u, UIDNEXT: %u) %d messages, %d recent, %d unseen\n",
886  mdata->name, mdata->uidvalidity, mdata->uid_next, mdata->messages,
887  mdata->recent, mdata->unseen);
888 
889  mutt_debug(LL_DEBUG3, "Running default STATUS handler\n");
890 
891  mutt_debug(LL_DEBUG3, "Found %s in mailbox list (OV: %u ON: %u U: %d)\n",
892  mailbox, olduv, oldun, mdata->unseen);
893 
894  bool new_mail = false;
895  const bool c_mail_check_recent =
896  cs_subset_bool(NeoMutt->sub, "mail_check_recent");
897  if (c_mail_check_recent)
898  {
899  if ((olduv != 0) && (olduv == mdata->uidvalidity))
900  {
901  if (oldun < mdata->uid_next)
902  new_mail = (mdata->unseen > 0);
903  }
904  else if ((olduv == 0) && (oldun == 0))
905  {
906  /* first check per session, use recent. might need a flag for this. */
907  new_mail = (mdata->recent > 0);
908  }
909  else
910  new_mail = (mdata->unseen > 0);
911  }
912  else
913  new_mail = (mdata->unseen > 0);
914 
915  m->has_new = new_mail;
916  m->msg_count = mdata->messages;
917  m->msg_unread = mdata->unseen;
918 
919  // force back to keep detecting new mail until the mailbox is opened
920  if (m->has_new)
921  mdata->uid_next = oldun;
922 }
int msg_count
Total number of messages.
Definition: mailbox.h:91
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
int imap_cmd_step(struct ImapAccountData *adata)
Reads server responses from an IMAP command.
Definition: command.c:1079
uint32_t uidvalidity
Definition: mdata.h:50
int msg_unread
Number of unread messages.
Definition: mailbox.h:92
Unrecoverable error occurred.
Definition: private.h:97
Container for Accounts, Notifications.
Definition: neomutt.h:36
bool has_new
Mailbox has new mail.
Definition: mailbox.h:88
char * imap_next_word(char *s)
Find where the next IMAP word begins.
Definition: util.c:792
static struct Mailbox * find_mailbox(struct ImapAccountData *adata, const char *name)
Find a Mailbox by its name.
Definition: command.c:784
struct ImapMboxData * imap_mdata_get(struct Mailbox *m)
Get the Mailbox data for this mailbox.
Definition: mdata.c:59
#define SKIPWS(ch)
Definition: string2.h:46
void * mdata
Driver specific data.
Definition: mailbox.h:136
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:160
A mailbox.
Definition: mailbox.h:81
unsigned char status
ImapFlags, e.g. IMAP_FATAL.
Definition: adata.h:45
char * name
Mailbox name.
Definition: mdata.h:40
#define mutt_debug(LEVEL,...)
Definition: logging.h:85
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
unsigned int uid_next
Definition: mdata.h:51
Log at debug level 1.
Definition: logging.h:40
IMAP-specific Mailbox data -.
Definition: mdata.h:38
char * buf
Definition: adata.h:59
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
unsigned int unseen
Definition: mdata.h:55
unsigned int recent
Definition: mdata.h:54
#define IMAP_RES_CONTINUE
* ...
Definition: private.h:57
void imap_unmunge_mbox_name(bool unicode, char *s)
Remove quoting from a mailbox name.
Definition: util.c:931
Log at debug level 3.
Definition: logging.h:42
unsigned int messages
Definition: mdata.h:53
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:

◆ 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 929 of file command.c.

930 {
931  mutt_debug(LL_DEBUG2, "Handling ENABLED\n");
932 
933  while ((s = imap_next_word((char *) s)) && (*s != '\0'))
934  {
935  if (mutt_istr_startswith(s, "UTF8=ACCEPT") ||
936  mutt_istr_startswith(s, "UTF8=ONLY"))
937  {
938  adata->unicode = true;
939  }
940  if (mutt_istr_startswith(s, "QRESYNC"))
941  adata->qresync = true;
942  }
943 }
char * imap_next_word(char *s)
Find where the next IMAP word begins.
Definition: util.c:792
Log at debug level 2.
Definition: logging.h:41
bool qresync
true, if QRESYNC is successfully ENABLE&#39;d
Definition: adata.h:63
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition: string.c:172
#define mutt_debug(LEVEL,...)
Definition: logging.h:85
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:

◆ 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 949 of file command.c.

950 {
951  unsigned int count = 0;
952  mutt_debug(LL_DEBUG2, "Handling EXISTS\n");
953 
954  if (mutt_str_atoui(pn, &count) < 0)
955  {
956  mutt_debug(LL_DEBUG1, "Malformed EXISTS: '%s'\n", pn);
957  return;
958  }
959 
960  struct ImapMboxData *mdata = adata->mailbox->mdata;
961 
962  /* new mail arrived */
963  if (count < imap_msn_highest(&mdata->msn))
964  {
965  /* Notes 6.0.3 has a tendency to report fewer messages exist than
966  * it should. */
967  mutt_debug(LL_DEBUG1, "Message count is out of sync\n");
968  }
969  /* at least the InterChange server sends EXISTS messages freely,
970  * even when there is no new mail */
971  else if (count == imap_msn_highest(&mdata->msn))
972  mutt_debug(LL_DEBUG3, "superfluous EXISTS message\n");
973  else
974  {
975  mutt_debug(LL_DEBUG2, "New mail in %s - %d messages total\n", mdata->name, count);
976  mdata->reopen |= IMAP_NEWMAIL_PENDING;
977  mdata->new_mail_count = count;
978  }
979 }
ImapOpenFlags reopen
Flags, e.g. IMAP_REOPEN_ALLOW.
Definition: mdata.h:44
struct Mailbox * mailbox
Current selected mailbox.
Definition: adata.h:76
unsigned int new_mail_count
Set when EXISTS notifies of new mail.
Definition: mdata.h:46
Log at debug level 2.
Definition: logging.h:41
void * mdata
Driver specific data.
Definition: mailbox.h:136
char * name
Mailbox name.
Definition: mdata.h:40
int mutt_str_atoui(const char *str, unsigned int *dst)
Convert ASCII string to an unsigned integer.
Definition: string.c:282
#define mutt_debug(LEVEL,...)
Definition: logging.h:85
size_t imap_msn_highest(const struct MSN *msn)
Return the highest MSN in use.
Definition: msn.c:68
Log at debug level 1.
Definition: logging.h:40
IMAP-specific Mailbox data -.
Definition: mdata.h:38
#define IMAP_NEWMAIL_PENDING
New mail is waiting on the server.
Definition: private.h:69
Log at debug level 3.
Definition: logging.h:42
+ 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 987 of file command.c.

988 {
989  char *s = imap_next_word(adata->buf);
990  char *pn = imap_next_word(s);
991 
992  const bool c_imap_server_noise =
993  cs_subset_bool(NeoMutt->sub, "imap_server_noise");
994  if ((adata->state >= IMAP_SELECTED) && isdigit((unsigned char) *s))
995  {
996  /* pn vs. s: need initial seqno */
997  pn = s;
998  s = imap_next_word(s);
999 
1000  /* EXISTS, EXPUNGE, FETCH are always related to the SELECTED mailbox */
1001  if (mutt_istr_startswith(s, "EXISTS"))
1002  cmd_parse_exists(adata, pn);
1003  else if (mutt_istr_startswith(s, "EXPUNGE"))
1004  cmd_parse_expunge(adata, pn);
1005  else if (mutt_istr_startswith(s, "FETCH"))
1006  cmd_parse_fetch(adata, pn);
1007  }
1008  else if ((adata->state >= IMAP_SELECTED) &&
1009  mutt_istr_startswith(s, "VANISHED"))
1010  cmd_parse_vanished(adata, pn);
1011  else if (mutt_istr_startswith(s, "CAPABILITY"))
1012  cmd_parse_capability(adata, s);
1013  else if (mutt_istr_startswith(s, "OK [CAPABILITY"))
1014  cmd_parse_capability(adata, pn);
1015  else if (mutt_istr_startswith(pn, "OK [CAPABILITY"))
1017  else if (mutt_istr_startswith(s, "LIST"))
1018  cmd_parse_list(adata, s);
1019  else if (mutt_istr_startswith(s, "LSUB"))
1020  cmd_parse_lsub(adata, s);
1021  else if (mutt_istr_startswith(s, "MYRIGHTS"))
1022  cmd_parse_myrights(adata, s);
1023  else if (mutt_istr_startswith(s, "SEARCH"))
1024  cmd_parse_search(adata, s);
1025  else if (mutt_istr_startswith(s, "STATUS"))
1026  cmd_parse_status(adata, s);
1027  else if (mutt_istr_startswith(s, "ENABLED"))
1028  cmd_parse_enabled(adata, s);
1029  else if (mutt_istr_startswith(s, "BYE"))
1030  {
1031  mutt_debug(LL_DEBUG2, "Handling BYE\n");
1032 
1033  /* check if we're logging out */
1034  if (adata->status == IMAP_BYE)
1035  return 0;
1036 
1037  /* server shut down our connection */
1038  s += 3;
1039  SKIPWS(s);
1040  mutt_error("%s", s);
1041  cmd_handle_fatal(adata);
1042 
1043  return -1;
1044  }
1045  else if (c_imap_server_noise && mutt_istr_startswith(s, "NO"))
1046  {
1047  mutt_debug(LL_DEBUG2, "Handling untagged NO\n");
1048 
1049  /* Display the warning message from the server */
1050  mutt_error("%s", s + 2);
1051  }
1052 
1053  return 0;
1054 }
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
Logged out from server.
Definition: private.h:98
#define mutt_error(...)
Definition: logging.h:88
static void cmd_parse_capability(struct ImapAccountData *adata, char *s)
set capability bits according to CAPABILITY response
Definition: command.c:535
unsigned char state
ImapState, e.g. IMAP_AUTHENTICATED.
Definition: adata.h:44
static void cmd_handle_fatal(struct ImapAccountData *adata)
When ImapAccountData is in fatal state, do what we can.
Definition: command.c:164
Container for Accounts, Notifications.
Definition: neomutt.h:36
void cmd_parse_search(struct ImapAccountData *adata, const char *s)
store SEARCH response for later use
Definition: search.c:259
char * imap_next_word(char *s)
Find where the next IMAP word begins.
Definition: util.c:792
Log at debug level 2.
Definition: logging.h:41
#define SKIPWS(ch)
Definition: string2.h:46
Mailbox is selected.
Definition: private.h:110
unsigned char status
ImapFlags, e.g. IMAP_FATAL.
Definition: adata.h:45
static void cmd_parse_enabled(struct ImapAccountData *adata, const char *s)
Record what the server has enabled.
Definition: command.c:929
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition: string.c:172
#define mutt_debug(LEVEL,...)
Definition: logging.h:85
static void cmd_parse_myrights(struct ImapAccountData *adata, const char *s)
Set rights bits according to MYRIGHTS response.
Definition: command.c:716
static void cmd_parse_lsub(struct ImapAccountData *adata, char *s)
Parse a server LSUB (list subscribed mailboxes)
Definition: command.c:663
char * buf
Definition: adata.h:59
static void cmd_parse_fetch(struct ImapAccountData *adata, char *s)
Load fetch response into ImapAccountData.
Definition: command.c:397
static void cmd_parse_expunge(struct ImapAccountData *adata, const char *s)
Parse expunge command.
Definition: command.c:256
static void cmd_parse_vanished(struct ImapAccountData *adata, char *s)
Parse vanished command.
Definition: command.c:303
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
static void cmd_parse_list(struct ImapAccountData *adata, char *s)
Parse a server LIST command (list mailboxes)
Definition: command.c:568
static void cmd_parse_exists(struct ImapAccountData *adata, const char *pn)
Parse EXISTS message from serer.
Definition: command.c:949
static void cmd_parse_status(struct ImapAccountData *adata, char *s)
Parse status from server.
Definition: command.c:808
+ 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 1065 of file command.c.

1066 {
1067  return cmd_start(adata, cmdstr, IMAP_CMD_NO_FLAGS);
1068 }
#define IMAP_CMD_NO_FLAGS
No flags are set.
Definition: private.h:73
static int cmd_start(struct ImapAccountData *adata, const char *cmdstr, ImapCmdFlags flags)
Start a new IMAP command.
Definition: command.c:200
+ 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 1079 of file command.c.

1080 {
1081  if (!adata)
1082  return -1;
1083 
1084  size_t len = 0;
1085  int c;
1086  int rc;
1087  int stillrunning = 0;
1088  struct ImapCommand *cmd = NULL;
1089 
1090  if (adata->status == IMAP_FATAL)
1091  {
1092  cmd_handle_fatal(adata);
1093  return IMAP_RES_BAD;
1094  }
1095 
1096  /* read into buffer, expanding buffer as necessary until we have a full
1097  * line */
1098  do
1099  {
1100  if (len == adata->blen)
1101  {
1102  mutt_mem_realloc(&adata->buf, adata->blen + IMAP_CMD_BUFSIZE);
1103  adata->blen = adata->blen + IMAP_CMD_BUFSIZE;
1104  mutt_debug(LL_DEBUG3, "grew buffer to %lu bytes\n", adata->blen);
1105  }
1106 
1107  /* back up over '\0' */
1108  if (len)
1109  len--;
1110  c = mutt_socket_readln_d(adata->buf + len, adata->blen - len, adata->conn, MUTT_SOCK_LOG_FULL);
1111  if (c <= 0)
1112  {
1113  mutt_debug(LL_DEBUG1, "Error reading server response\n");
1114  cmd_handle_fatal(adata);
1115  return IMAP_RES_BAD;
1116  }
1117 
1118  len += c;
1119  }
1120  /* if we've read all the way to the end of the buffer, we haven't read a
1121  * full line (mutt_socket_readln strips the \r, so we always have at least
1122  * one character free when we've read a full line) */
1123  while (len == adata->blen);
1124 
1125  /* don't let one large string make cmd->buf hog memory forever */
1126  if ((adata->blen > IMAP_CMD_BUFSIZE) && (len <= IMAP_CMD_BUFSIZE))
1127  {
1129  adata->blen = IMAP_CMD_BUFSIZE;
1130  mutt_debug(LL_DEBUG3, "shrank buffer to %lu bytes\n", adata->blen);
1131  }
1132 
1133  adata->lastread = mutt_date_epoch();
1134 
1135  /* handle untagged messages. The caller still gets its shot afterwards. */
1136  if ((mutt_str_startswith(adata->buf, "* ") ||
1137  mutt_str_startswith(imap_next_word(adata->buf), "OK [")) &&
1138  cmd_handle_untagged(adata))
1139  {
1140  return IMAP_RES_BAD;
1141  }
1142 
1143  /* server demands a continuation response from us */
1144  if (adata->buf[0] == '+')
1145  return IMAP_RES_RESPOND;
1146 
1147  /* Look for tagged command completions.
1148  *
1149  * Some response handlers can end up recursively calling
1150  * imap_cmd_step() and end up handling all tagged command
1151  * completions.
1152  * (e.g. FETCH->set_flag->set_header_color->~h pattern match.)
1153  *
1154  * Other callers don't even create an adata->cmds entry.
1155  *
1156  * For both these cases, we default to returning OK */
1157  rc = IMAP_RES_OK;
1158  c = adata->lastcmd;
1159  do
1160  {
1161  cmd = &adata->cmds[c];
1162  if (cmd->state == IMAP_RES_NEW)
1163  {
1164  if (mutt_str_startswith(adata->buf, cmd->seq))
1165  {
1166  if (!stillrunning)
1167  {
1168  /* first command in queue has finished - move queue pointer up */
1169  adata->lastcmd = (adata->lastcmd + 1) % adata->cmdslots;
1170  }
1171  cmd->state = cmd_status(adata->buf);
1172  rc = cmd->state;
1173  if (cmd->state == IMAP_RES_NO || cmd->state == IMAP_RES_BAD)
1174  {
1175  mutt_message(_("IMAP command failed: %s"), adata->buf);
1176  }
1177  }
1178  else
1179  stillrunning++;
1180  }
1181 
1182  c = (c + 1) % adata->cmdslots;
1183  } while (c != adata->nextcmd);
1184 
1185  if (stillrunning)
1186  rc = IMAP_RES_CONTINUE;
1187  else
1188  {
1189  mutt_debug(LL_DEBUG3, "IMAP queue drained\n");
1190  imap_cmd_finish(adata);
1191  }
1192 
1193  return rc;
1194 }
time_t mutt_date_epoch(void)
Return the number of seconds since the Unix epoch.
Definition: date.c:427
struct ImapCommand * cmds
Definition: adata.h:69
#define IMAP_RES_RESPOND
+
Definition: private.h:58
#define IMAP_RES_OK
<tag> OK ...
Definition: private.h:56
#define IMAP_RES_NEW
ImapCommand.state additions.
Definition: private.h:59
Unrecoverable error occurred.
Definition: private.h:97
int lastcmd
Definition: adata.h:72
#define _(a)
Definition: message.h:28
static void cmd_handle_fatal(struct ImapAccountData *adata)
When ImapAccountData is in fatal state, do what we can.
Definition: command.c:164
size_t blen
Definition: adata.h:60
#define MUTT_SOCK_LOG_FULL
Definition: mutt_socket.h:31
char * imap_next_word(char *s)
Find where the next IMAP word begins.
Definition: util.c:792
int nextcmd
Definition: adata.h:71
IMAP command structure.
Definition: private.h:159
void mutt_mem_realloc(void *ptr, size_t size)
Resize a block of memory on the heap.
Definition: memory.c:114
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:160
int cmdslots
Definition: adata.h:70
unsigned char status
ImapFlags, e.g. IMAP_FATAL.
Definition: adata.h:45
void imap_cmd_finish(struct ImapAccountData *adata)
Attempt to perform cleanup.
Definition: command.c:1316
#define IMAP_CMD_BUFSIZE
Definition: command.c:57
char seq[SEQ_LEN+1]
Command tag, e.g. &#39;a0001&#39;.
Definition: private.h:161
#define IMAP_RES_NO
<tag> NO ...
Definition: private.h:54
time_t lastread
last time we read a command for the server
Definition: adata.h:58
#define mutt_debug(LEVEL,...)
Definition: logging.h:85
Log at debug level 1.
Definition: logging.h:40
int state
Command state, e.g. IMAP_RES_NEW.
Definition: private.h:162
char * buf
Definition: adata.h:59
static int cmd_handle_untagged(struct ImapAccountData *adata)
fallback parser for otherwise unhandled messages
Definition: command.c:987
#define mutt_message(...)
Definition: logging.h:87
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
#define IMAP_RES_CONTINUE
* ...
Definition: private.h:57
Log at debug level 3.
Definition: logging.h:42
struct Connection * conn
Definition: adata.h:41
#define IMAP_RES_BAD
<tag> BAD ...
Definition: private.h:55
static int cmd_status(const char *s)
parse response line for tagged OK/NO/BAD
Definition: command.c:236
+ 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 1202 of file command.c.

1203 {
1204  return cmd_status(s) == IMAP_RES_OK;
1205 }
#define IMAP_RES_OK
<tag> OK ...
Definition: private.h:56
static int cmd_status(const char *s)
parse response line for tagged OK/NO/BAD
Definition: command.c:236
+ 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 1213 of file command.c.

1214 {
1215  static const char *notrailer = "";
1216  const char *s = adata->buf;
1217 
1218  if (!s)
1219  {
1220  mutt_debug(LL_DEBUG2, "not a tagged response\n");
1221  return notrailer;
1222  }
1223 
1224  s = imap_next_word((char *) s);
1225  if (!s || (!mutt_istr_startswith(s, "OK") && !mutt_istr_startswith(s, "NO") &&
1226  !mutt_istr_startswith(s, "BAD")))
1227  {
1228  mutt_debug(LL_DEBUG2, "not a command completion: %s\n", adata->buf);
1229  return notrailer;
1230  }
1231 
1232  s = imap_next_word((char *) s);
1233  if (!s)
1234  return notrailer;
1235 
1236  return s;
1237 }
char * imap_next_word(char *s)
Find where the next IMAP word begins.
Definition: util.c:792
Log at debug level 2.
Definition: logging.h:41
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition: string.c:172
#define mutt_debug(LEVEL,...)
Definition: logging.h:85
char * buf
Definition: adata.h:59
+ 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 1250 of file command.c.

1251 {
1252  int rc;
1253 
1254  if (flags & IMAP_CMD_SINGLE)
1255  {
1256  // Process any existing commands
1257  if (adata->nextcmd != adata->lastcmd)
1258  imap_exec(adata, NULL, IMAP_CMD_POLL);
1259  }
1260 
1261  rc = cmd_start(adata, cmdstr, flags);
1262  if (rc < 0)
1263  {
1264  cmd_handle_fatal(adata);
1265  return IMAP_EXEC_FATAL;
1266  }
1267 
1268  if (flags & IMAP_CMD_QUEUE)
1269  return IMAP_EXEC_SUCCESS;
1270 
1271  const short c_imap_poll_timeout =
1272  cs_subset_number(NeoMutt->sub, "imap_poll_timeout");
1273  if ((flags & IMAP_CMD_POLL) && (c_imap_poll_timeout > 0) &&
1274  ((mutt_socket_poll(adata->conn, c_imap_poll_timeout)) == 0))
1275  {
1276  mutt_error(_("Connection to %s timed out"), adata->conn->account.host);
1277  cmd_handle_fatal(adata);
1278  return IMAP_EXEC_FATAL;
1279  }
1280 
1281  /* Allow interruptions, particularly useful if there are network problems. */
1283  do
1284  {
1285  rc = imap_cmd_step(adata);
1286  // The queue is empty, so the single command has been processed
1287  if ((flags & IMAP_CMD_SINGLE) && (adata->nextcmd == adata->lastcmd))
1288  break;
1289  } while (rc == IMAP_RES_CONTINUE);
1290  mutt_sig_allow_interrupt(false);
1291 
1292  if (rc == IMAP_RES_NO)
1293  return IMAP_EXEC_ERROR;
1294  if (rc != IMAP_RES_OK)
1295  {
1296  if (adata->status != IMAP_FATAL)
1297  return IMAP_EXEC_ERROR;
1298 
1299  mutt_debug(LL_DEBUG1, "command failed: %s\n", adata->buf);
1300  return IMAP_EXEC_FATAL;
1301  }
1302 
1303  return IMAP_EXEC_SUCCESS;
1304 }
#define IMAP_RES_OK
<tag> OK ...
Definition: private.h:56
int imap_cmd_step(struct ImapAccountData *adata)
Reads server responses from an IMAP command.
Definition: command.c:1079
struct ConnAccount account
Account details: username, password, etc.
Definition: connection.h:40
Imap connection failure.
Definition: private.h:86
#define mutt_error(...)
Definition: logging.h:88
Unrecoverable error occurred.
Definition: private.h:97
int lastcmd
Definition: adata.h:72
#define IMAP_CMD_POLL
Poll the tcp connection before running the imap command.
Definition: private.h:76
#define _(a)
Definition: message.h:28
static void cmd_handle_fatal(struct ImapAccountData *adata)
When ImapAccountData is in fatal state, do what we can.
Definition: command.c:164
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition: helpers.c:169
Container for Accounts, Notifications.
Definition: neomutt.h:36
void mutt_sig_allow_interrupt(bool allow)
Allow/disallow Ctrl-C (SIGINT)
Definition: signal.c:238
Imap command executed or queued successfully.
Definition: private.h:84
char host[128]
Server to login to.
Definition: connaccount.h:53
int mutt_socket_poll(struct Connection *conn, time_t wait_secs)
Checks whether reads would block.
Definition: socket.c:192
int nextcmd
Definition: adata.h:71
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:1250
unsigned char status
ImapFlags, e.g. IMAP_FATAL.
Definition: adata.h:45
#define IMAP_RES_NO
<tag> NO ...
Definition: private.h:54
#define mutt_debug(LEVEL,...)
Definition: logging.h:85
Imap command failure.
Definition: private.h:85
Log at debug level 1.
Definition: logging.h:40
char * buf
Definition: adata.h:59
static int cmd_start(struct ImapAccountData *adata, const char *cmdstr, ImapCmdFlags flags)
Start a new IMAP command.
Definition: command.c:200
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
#define IMAP_CMD_QUEUE
Queue a command, do not execute.
Definition: private.h:75
#define IMAP_RES_CONTINUE
* ...
Definition: private.h:57
#define IMAP_CMD_SINGLE
Run a single command.
Definition: private.h:77
struct Connection * conn
Definition: adata.h:41
+ 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 1316 of file command.c.

1317 {
1318  if (!adata)
1319  return;
1320 
1321  if (adata->status == IMAP_FATAL)
1322  {
1323  adata->closing = false;
1324  cmd_handle_fatal(adata);
1325  return;
1326  }
1327 
1328  if (!(adata->state >= IMAP_SELECTED) || (adata->mailbox && adata->closing))
1329  {
1330  adata->closing = false;
1331  return;
1332  }
1333 
1334  adata->closing = false;
1335 
1336  struct ImapMboxData *mdata = imap_mdata_get(adata->mailbox);
1337 
1338  if (mdata && mdata->reopen & IMAP_REOPEN_ALLOW)
1339  {
1340  // First remove expunged emails from the msn_index
1341  if (mdata->reopen & IMAP_EXPUNGE_PENDING)
1342  {
1343  mutt_debug(LL_DEBUG2, "Expunging mailbox\n");
1344  imap_expunge_mailbox(adata->mailbox);
1345  /* Detect whether we've gotten unexpected EXPUNGE messages */
1346  if (!(mdata->reopen & IMAP_EXPUNGE_EXPECTED))
1349  }
1350 
1351  // Then add new emails to it
1352  if (mdata->reopen & IMAP_NEWMAIL_PENDING)
1353  {
1354  const size_t max_msn = imap_msn_highest(&mdata->msn);
1355  if (mdata->new_mail_count > max_msn)
1356  {
1357  if (!(mdata->reopen & IMAP_EXPUNGE_PENDING))
1359 
1360  mutt_debug(LL_DEBUG2, "Fetching new mails from %d to %d\n", max_msn + 1,
1361  mdata->new_mail_count);
1362  imap_read_headers(adata->mailbox, max_msn + 1, mdata->new_mail_count, false);
1363  }
1364  }
1365 
1366  // And to finish inform about MUTT_REOPEN if needed
1367  if (mdata->reopen & IMAP_EXPUNGE_PENDING && !(mdata->reopen & IMAP_EXPUNGE_EXPECTED))
1369 
1370  if (mdata->reopen & IMAP_EXPUNGE_PENDING)
1372  }
1373 
1374  adata->status = 0;
1375 }
Unrecoverable error occurred.
Definition: private.h:97
void imap_expunge_mailbox(struct Mailbox *m)
Purge messages from the server.
Definition: imap.c:676
#define IMAP_REOPEN_ALLOW
Allow re-opening a folder upon expunge.
Definition: private.h:66
unsigned char state
ImapState, e.g. IMAP_AUTHENTICATED.
Definition: adata.h:44
ImapOpenFlags reopen
Flags, e.g. IMAP_REOPEN_ALLOW.
Definition: mdata.h:44
struct Mailbox * mailbox
Current selected mailbox.
Definition: adata.h:76
static void cmd_handle_fatal(struct ImapAccountData *adata)
When ImapAccountData is in fatal state, do what we can.
Definition: command.c:164
unsigned int new_mail_count
Set when EXISTS notifies of new mail.
Definition: mdata.h:46
Log at debug level 2.
Definition: logging.h:41
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:1299
struct ImapMboxData * imap_mdata_get(struct Mailbox *m)
Get the Mailbox data for this mailbox.
Definition: mdata.c:59
ImapOpenFlags check_status
Flags, e.g. IMAP_NEWMAIL_PENDING.
Definition: mdata.h:45
Mailbox is selected.
Definition: private.h:110
void * mdata
Driver specific data.
Definition: mailbox.h:136
bool closing
If true, we are waiting for CLOSE completion.
Definition: adata.h:43
unsigned char status
ImapFlags, e.g. IMAP_FATAL.
Definition: adata.h:45
#define IMAP_EXPUNGE_EXPECTED
Messages will be expunged from the server.
Definition: private.h:67
#define mutt_debug(LEVEL,...)
Definition: logging.h:85
size_t imap_msn_highest(const struct MSN *msn)
Return the highest MSN in use.
Definition: msn.c:68
IMAP-specific Mailbox data -.
Definition: mdata.h:38
#define IMAP_NEWMAIL_PENDING
New mail is waiting on the server.
Definition: private.h:69
#define IMAP_EXPUNGE_PENDING
Messages on the server have been expunged.
Definition: private.h:68
+ 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 1383 of file command.c.

1384 {
1385  int rc;
1386 
1387  if (cmd_start(adata, "IDLE", IMAP_CMD_POLL) < 0)
1388  {
1389  cmd_handle_fatal(adata);
1390  return -1;
1391  }
1392 
1393  const short c_imap_poll_timeout =
1394  cs_subset_number(NeoMutt->sub, "imap_poll_timeout");
1395  if ((c_imap_poll_timeout > 0) &&
1396  ((mutt_socket_poll(adata->conn, c_imap_poll_timeout)) == 0))
1397  {
1398  mutt_error(_("Connection to %s timed out"), adata->conn->account.host);
1399  cmd_handle_fatal(adata);
1400  return -1;
1401  }
1402 
1403  do
1404  {
1405  rc = imap_cmd_step(adata);
1406  } while (rc == IMAP_RES_CONTINUE);
1407 
1408  if (rc == IMAP_RES_RESPOND)
1409  {
1410  /* successfully entered IDLE state */
1411  adata->state = IMAP_IDLE;
1412  /* queue automatic exit when next command is issued */
1413  mutt_buffer_addstr(&adata->cmdbuf, "DONE\r\n");
1414  rc = IMAP_RES_OK;
1415  }
1416  if (rc != IMAP_RES_OK)
1417  {
1418  mutt_debug(LL_DEBUG1, "error starting IDLE\n");
1419  return -1;
1420  }
1421 
1422  return 0;
1423 }
#define IMAP_RES_RESPOND
+
Definition: private.h:58
#define IMAP_RES_OK
<tag> OK ...
Definition: private.h:56
int imap_cmd_step(struct ImapAccountData *adata)
Reads server responses from an IMAP command.
Definition: command.c:1079
struct ConnAccount account
Account details: username, password, etc.
Definition: connection.h:40
#define mutt_error(...)
Definition: logging.h:88
unsigned char state
ImapState, e.g. IMAP_AUTHENTICATED.
Definition: adata.h:44
#define IMAP_CMD_POLL
Poll the tcp connection before running the imap command.
Definition: private.h:76
#define _(a)
Definition: message.h:28
static void cmd_handle_fatal(struct ImapAccountData *adata)
When ImapAccountData is in fatal state, do what we can.
Definition: command.c:164
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition: helpers.c:169
Container for Accounts, Notifications.
Definition: neomutt.h:36
char host[128]
Server to login to.
Definition: connaccount.h:53
int mutt_socket_poll(struct Connection *conn, time_t wait_secs)
Checks whether reads would block.
Definition: socket.c:192
struct Buffer cmdbuf
Definition: adata.h:73
size_t mutt_buffer_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:225
#define mutt_debug(LEVEL,...)
Definition: logging.h:85
Connection is idle.
Definition: private.h:113
Log at debug level 1.
Definition: logging.h:40
static int cmd_start(struct ImapAccountData *adata, const char *cmdstr, ImapCmdFlags flags)
Start a new IMAP command.
Definition: command.c:200
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
#define IMAP_RES_CONTINUE
* ...
Definition: private.h:57
struct Connection * conn
Definition: adata.h:41
+ Here is the call graph for this function:
+ Here is the caller 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",
"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.