NeoMutt  2022-04-29-249-gaae397
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 "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 56 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
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 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}
#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:92
#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 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}
int mutt_buffer_add_printf(struct Buffer *buf, const char *fmt,...)
Format a string appending a Buffer.
Definition: buffer.c:211
int imap_exec(struct ImapAccountData *adata, const char *cmdstr, ImapCmdFlags flags)
Execute a command and wait for the response from the server.
Definition: command.c:1247
static struct ImapCommand * cmd_new(struct ImapAccountData *adata)
Create and queue a new command control block.
Definition: command.c:106
#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 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 {
175 mx_fastclose_mailbox(adata->mailbox, true);
176 mutt_error(_("Mailbox %s@%s closed"), adata->conn->account.user,
177 adata->conn->account.host);
178 }
179
181 if (!adata->recovering)
182 {
183 adata->recovering = true;
184 if (imap_login(adata))
186 adata->recovering = false;
187 }
188}
#define mutt_error(...)
Definition: logging.h:87
@ 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:1848
#define _(a)
Definition: message.h:28
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
Definition: mutt_logging.c:74
void mx_fastclose_mailbox(struct Mailbox *m, bool keep_account)
Free up memory associated with the Mailbox.
Definition: mx.c:430
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:132
+ 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 198 of file command.c.

199{
200 int rc;
201
202 if (adata->status == IMAP_FATAL)
203 {
204 cmd_handle_fatal(adata);
205 return -1;
206 }
207
208 if (cmdstr && ((rc = cmd_queue(adata, cmdstr, flags)) < 0))
209 return rc;
210
211 if (flags & IMAP_CMD_QUEUE)
212 return 0;
213
214 if (mutt_buffer_is_empty(&adata->cmdbuf))
215 return IMAP_RES_BAD;
216
217 rc = mutt_socket_send_d(adata->conn, adata->cmdbuf.data,
219 mutt_buffer_reset(&adata->cmdbuf);
220
221 /* unidle when command queue is flushed */
222 if (adata->state == IMAP_IDLE)
223 adata->state = IMAP_SELECTED;
224
225 return (rc < 0) ? IMAP_RES_BAD : 0;
226}
bool mutt_buffer_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:260
void mutt_buffer_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:85
static int cmd_queue(struct ImapAccountData *adata, const char *cmdstr, ImapCmdFlags flags)
Add a IMAP command to the queue.
Definition: command.c:138
static void cmd_handle_fatal(struct ImapAccountData *adata)
When ImapAccountData is in fatal state, do what we can.
Definition: command.c:164
@ 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: socket.h:60
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 234 of file command.c.

235{
236 s = imap_next_word((char *) s);
237
238 if (mutt_istr_startswith(s, "OK"))
239 return IMAP_RES_OK;
240 if (mutt_istr_startswith(s, "NO"))
241 return IMAP_RES_NO;
242
243 return IMAP_RES_BAD;
244}
#define IMAP_RES_OK
<tag> OK ...
Definition: private.h:56
#define IMAP_RES_NO
<tag> NO ...
Definition: private.h:54
char * imap_next_word(char *s)
Find where the next IMAP word begins.
Definition: util.c:789
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition: string.c:239
+ 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 254 of file command.c.

255{
256 unsigned int exp_msn;
257 struct Email *e = NULL;
258
259 mutt_debug(LL_DEBUG2, "Handling EXPUNGE\n");
260
261 struct ImapMboxData *mdata = adata->mailbox->mdata;
262
263 if (!mutt_str_atoui(s, &exp_msn) || (exp_msn < 1) ||
264 (exp_msn > imap_msn_highest(&mdata->msn)))
265 {
266 return;
267 }
268
269 e = imap_msn_get(&mdata->msn, exp_msn - 1);
270 if (e)
271 {
272 /* imap_expunge_mailbox() will rewrite e->index.
273 * It needs to resort using SORT_ORDER anyway, so setting to INT_MAX
274 * makes the code simpler and possibly more efficient. */
275 e->index = INT_MAX;
276 imap_edata_get(e)->msn = 0;
277 }
278
279 /* decrement seqno of those above. */
280 const size_t max_msn = imap_msn_highest(&mdata->msn);
281 for (unsigned int cur = exp_msn; cur < max_msn; cur++)
282 {
283 e = imap_msn_get(&mdata->msn, cur);
284 if (e)
285 imap_edata_get(e)->msn--;
286 imap_msn_set(&mdata->msn, cur - 1, e);
287 }
288 imap_msn_shrink(&mdata->msn, 1);
289
290 mdata->reopen |= IMAP_EXPUNGE_PENDING;
291}
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:65
#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:92
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:103
size_t imap_msn_highest(const struct MSN *msn)
Return the highest MSN in use.
Definition: msn.c:69
struct Email * imap_msn_get(const struct MSN *msn, size_t idx)
Return the Email associated with an msn.
Definition: msn.c:80
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 301 of file command.c.

302{
303 bool earlier = false;
304 int rc;
305 unsigned int uid = 0;
306
307 struct ImapMboxData *mdata = adata->mailbox->mdata;
308
309 mutt_debug(LL_DEBUG2, "Handling VANISHED\n");
310
311 if (mutt_istr_startswith(s, "(EARLIER)"))
312 {
313 /* The RFC says we should not decrement msns with the VANISHED EARLIER tag.
314 * My experimentation says that's crap. */
315 earlier = true;
316 s = imap_next_word(s);
317 }
318
319 char *end_of_seqset = s;
320 while (*end_of_seqset)
321 {
322 if (!strchr("0123456789:,", *end_of_seqset))
323 *end_of_seqset = '\0';
324 else
325 end_of_seqset++;
326 }
327
329 if (!iter)
330 {
331 mutt_debug(LL_DEBUG2, "VANISHED: empty seqset [%s]?\n", s);
332 return;
333 }
334
335 while ((rc = mutt_seqset_iterator_next(iter, &uid)) == 0)
336 {
337 struct Email *e = mutt_hash_int_find(mdata->uid_hash, uid);
338 if (!e)
339 continue;
340
341 unsigned int exp_msn = imap_edata_get(e)->msn;
342
343 /* imap_expunge_mailbox() will rewrite e->index.
344 * It needs to resort using SORT_ORDER anyway, so setting to INT_MAX
345 * makes the code simpler and possibly more efficient. */
346 e->index = INT_MAX;
347 imap_edata_get(e)->msn = 0;
348
349 if ((exp_msn < 1) || (exp_msn > imap_msn_highest(&mdata->msn)))
350 {
351 mutt_debug(LL_DEBUG1, "VANISHED: msn for UID %u is incorrect\n", uid);
352 continue;
353 }
354 if (imap_msn_get(&mdata->msn, exp_msn - 1) != e)
355 {
356 mutt_debug(LL_DEBUG1, "VANISHED: msn_index for UID %u is incorrect\n", uid);
357 continue;
358 }
359
360 imap_msn_remove(&mdata->msn, exp_msn - 1);
361
362 if (!earlier)
363 {
364 /* decrement seqno of those above. */
365 const size_t max_msn = imap_msn_highest(&mdata->msn);
366 for (unsigned int cur = exp_msn; cur < max_msn; cur++)
367 {
368 e = imap_msn_get(&mdata->msn, cur);
369 if (e)
370 imap_edata_get(e)->msn--;
371 imap_msn_set(&mdata->msn, cur - 1, e);
372 }
373
374 imap_msn_shrink(&mdata->msn, 1);
375 }
376 }
377
378 if (rc < 0)
379 mutt_debug(LL_DEBUG1, "VANISHED: illegal seqset %s\n", s);
380
382
384}
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
struct SeqsetIterator * mutt_seqset_iterator_new(const char *seqset)
Create a new Sequence Set Iterator.
Definition: util.c:1076
int mutt_seqset_iterator_next(struct SeqsetIterator *iter, unsigned int *next)
Get the next UID from a Sequence Set.
Definition: util.c:1097
void mutt_seqset_iterator_free(struct SeqsetIterator **ptr)
Free a Sequence Set Iterator.
Definition: util.c:1154
@ 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:113
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 395 of file command.c.

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

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

567{
568 struct ImapList *list = NULL;
569 struct ImapList lb = { 0 };
570 char delimbuf[5] = { 0 }; /* worst case: "\\"\0 */
571 unsigned int litlen;
572
573 if (adata->cmdresult)
574 list = adata->cmdresult;
575 else
576 list = &lb;
577
578 memset(list, 0, sizeof(struct ImapList));
579
580 /* flags */
581 s = imap_next_word(s);
582 if (*s != '(')
583 {
584 mutt_debug(LL_DEBUG1, "Bad LIST response\n");
585 return;
586 }
587 s++;
588 while (*s)
589 {
590 if (mutt_istr_startswith(s, "\\NoSelect"))
591 list->noselect = true;
592 else if (mutt_istr_startswith(s, "\\NonExistent")) /* rfc5258 */
593 list->noselect = true;
594 else if (mutt_istr_startswith(s, "\\NoInferiors"))
595 list->noinferiors = true;
596 else if (mutt_istr_startswith(s, "\\HasNoChildren")) /* rfc5258*/
597 list->noinferiors = true;
598
599 s = imap_next_word(s);
600 if (*(s - 2) == ')')
601 break;
602 }
603
604 /* Delimiter */
605 if (!mutt_istr_startswith(s, "NIL"))
606 {
607 delimbuf[0] = '\0';
608 mutt_str_cat(delimbuf, 5, s);
609 imap_unquote_string(delimbuf);
610 list->delim = delimbuf[0];
611 }
612
613 /* Name */
614 s = imap_next_word(s);
615 /* Notes often responds with literals here. We need a real tokenizer. */
616 if (imap_get_literal_count(s, &litlen) == 0)
617 {
618 if (imap_cmd_step(adata) != IMAP_RES_CONTINUE)
619 {
620 adata->status = IMAP_FATAL;
621 return;
622 }
623
624 if (strlen(adata->buf) < litlen)
625 {
626 mutt_debug(LL_DEBUG1, "Error parsing LIST mailbox\n");
627 return;
628 }
629
630 list->name = adata->buf;
631 s = list->name + litlen;
632 if (s[0] != '\0')
633 {
634 s[0] = '\0';
635 s++;
636 SKIPWS(s);
637 }
638 }
639 else
640 {
641 list->name = s;
642 /* Exclude rfc5258 RECURSIVEMATCH CHILDINFO suffix */
643 s = imap_next_word(s);
644 if (s[0] != '\0')
645 s[-1] = '\0';
646 imap_unmunge_mbox_name(adata->unicode, list->name);
647 }
648
649 if (list->name[0] == '\0')
650 {
651 adata->delim = list->delim;
652 mutt_debug(LL_DEBUG3, "Root delimiter: %c\n", adata->delim);
653 }
654}
int imap_cmd_step(struct ImapAccountData *adata)
Reads server responses from an IMAP command.
Definition: command.c:1076
void imap_unmunge_mbox_name(bool unicode, char *s)
Remove quoting from a mailbox name.
Definition: util.c:928
int imap_get_literal_count(const char *buf, unsigned int *bytes)
Write number of bytes in an IMAP literal into bytes.
Definition: util.c:745
void imap_unquote_string(char *s)
Equally stupid unquoting routine.
Definition: util.c:874
#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:265
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 661 of file command.c.

662{
663 char buf[256] = { 0 };
664 char quoted_name[256] = { 0 };
665 struct Buffer err;
666 struct Url url = { 0 };
667 struct ImapList list = { 0 };
668
669 if (adata->cmdresult)
670 {
671 /* caller will handle response itself */
672 cmd_parse_list(adata, s);
673 return;
674 }
675
676 const bool c_imap_check_subscribed = cs_subset_bool(NeoMutt->sub, "imap_check_subscribed");
677 if (!c_imap_check_subscribed)
678 return;
679
680 adata->cmdresult = &list;
681 cmd_parse_list(adata, s);
682 adata->cmdresult = NULL;
683 /* noselect is for a gmail quirk */
684 if (!list.name || list.noselect)
685 return;
686
687 mutt_debug(LL_DEBUG3, "Subscribing to %s\n", list.name);
688
689 mutt_str_copy(buf, "mailboxes \"", sizeof(buf));
690 mutt_account_tourl(&adata->conn->account, &url);
691 /* escape \ and " */
692 imap_quote_string(quoted_name, sizeof(quoted_name), list.name, true);
693 url.path = quoted_name + 1;
694 url.path[strlen(url.path) - 1] = '\0';
695 const char *const c_imap_user = cs_subset_string(NeoMutt->sub, "imap_user");
696 if (mutt_str_equal(url.user, c_imap_user))
697 url.user = NULL;
698 url_tostring(&url, buf + 11, sizeof(buf) - 11, U_NO_FLAGS);
699 mutt_str_cat(buf, sizeof(buf), "\"");
700 mutt_buffer_init(&err);
701 err.dsize = 256;
702 err.data = mutt_mem_malloc(err.dsize);
703 if (mutt_parse_rc_line(buf, &err))
704 mutt_debug(LL_DEBUG1, "Error adding subscribed mailbox: %s\n", err.data);
705 FREE(&err.data);
706}
struct Buffer * mutt_buffer_init(struct Buffer *buf)
Initialise a new Buffer.
Definition: buffer.c:52
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:566
void imap_quote_string(char *dest, size_t dlen, const char *src, bool quote_backtick)
Quote string according to IMAP rules.
Definition: util.c:837
enum CommandResult mutt_parse_rc_line(const char *line, struct Buffer *err)
Parse a line of user config.
Definition: init.c:894
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:807
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:652
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 713 of file command.c.

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

782{
783 if (!adata || !adata->account || !name)
784 return NULL;
785
786 struct MailboxNode *np = NULL;
787 STAILQ_FOREACH(np, &adata->account->mailboxes, entries)
788 {
790 if (mutt_str_equal(name, mdata->name))
791 return np->mailbox;
792 }
793
794 return NULL;
795}
#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:153
struct Mailbox * mailbox
Mailbox in the list.
Definition: mailbox.h:154
+ 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 805 of file command.c.

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

929{
930 mutt_debug(LL_DEBUG2, "Handling ENABLED\n");
931
932 while ((s = imap_next_word((char *) s)) && (*s != '\0'))
933 {
934 if (mutt_istr_startswith(s, "UTF8=ACCEPT") || mutt_istr_startswith(s, "UTF8=ONLY"))
935 {
936 adata->unicode = true;
937 }
938 if (mutt_istr_startswith(s, "QRESYNC"))
939 adata->qresync = true;
940 }
941}
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 948 of file command.c.

949{
950 unsigned int count = 0;
951 mutt_debug(LL_DEBUG2, "Handling EXISTS\n");
952
953 if (!mutt_str_atoui(pn, &count))
954 {
955 mutt_debug(LL_DEBUG1, "Malformed EXISTS: '%s'\n", pn);
956 return;
957 }
958
959 struct ImapMboxData *mdata = adata->mailbox->mdata;
960
961 /* new mail arrived */
962 if (count < imap_msn_highest(&mdata->msn))
963 {
964 /* Notes 6.0.3 has a tendency to report fewer messages exist than
965 * it should. */
966 mutt_debug(LL_DEBUG1, "Message count is out of sync\n");
967 }
968 /* at least the InterChange server sends EXISTS messages freely,
969 * even when there is no new mail */
970 else if (count == imap_msn_highest(&mdata->msn))
971 mutt_debug(LL_DEBUG3, "superfluous EXISTS message\n");
972 else
973 {
974 mutt_debug(LL_DEBUG2, "New mail in %s - %d messages total\n", mdata->name, count);
975 mdata->reopen |= IMAP_NEWMAIL_PENDING;
976 mdata->new_mail_count = count;
977 }
978}
#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 986 of file command.c.

987{
988 char *s = imap_next_word(adata->buf);
989 char *pn = imap_next_word(s);
990
991 const bool c_imap_server_noise = cs_subset_bool(NeoMutt->sub, "imap_server_noise");
992 if ((adata->state >= IMAP_SELECTED) && isdigit((unsigned char) *s))
993 {
994 /* pn vs. s: need initial seqno */
995 pn = s;
996 s = imap_next_word(s);
997
998 /* EXISTS, EXPUNGE, FETCH are always related to the SELECTED mailbox */
999 if (mutt_istr_startswith(s, "EXISTS"))
1000 cmd_parse_exists(adata, pn);
1001 else if (mutt_istr_startswith(s, "EXPUNGE"))
1002 cmd_parse_expunge(adata, pn);
1003 else if (mutt_istr_startswith(s, "FETCH"))
1004 cmd_parse_fetch(adata, pn);
1005 }
1006 else if ((adata->state >= IMAP_SELECTED) && mutt_istr_startswith(s, "VANISHED"))
1007 cmd_parse_vanished(adata, pn);
1008 else if (mutt_istr_startswith(s, "CAPABILITY"))
1009 cmd_parse_capability(adata, s);
1010 else if (mutt_istr_startswith(s, "OK [CAPABILITY"))
1011 cmd_parse_capability(adata, pn);
1012 else if (mutt_istr_startswith(pn, "OK [CAPABILITY"))
1014 else if (mutt_istr_startswith(s, "LIST"))
1015 cmd_parse_list(adata, s);
1016 else if (mutt_istr_startswith(s, "LSUB"))
1017 cmd_parse_lsub(adata, s);
1018 else if (mutt_istr_startswith(s, "MYRIGHTS"))
1019 cmd_parse_myrights(adata, s);
1020 else if (mutt_istr_startswith(s, "SEARCH"))
1021 cmd_parse_search(adata, s);
1022 else if (mutt_istr_startswith(s, "STATUS"))
1023 cmd_parse_status(adata, s);
1024 else if (mutt_istr_startswith(s, "ENABLED"))
1025 cmd_parse_enabled(adata, s);
1026 else if (mutt_istr_startswith(s, "BYE"))
1027 {
1028 mutt_debug(LL_DEBUG2, "Handling BYE\n");
1029
1030 /* check if we're logging out */
1031 if (adata->status == IMAP_BYE)
1032 return 0;
1033
1034 /* server shut down our connection */
1035 s += 3;
1036 SKIPWS(s);
1037 mutt_error("%s", s);
1038 cmd_handle_fatal(adata);
1039
1040 return -1;
1041 }
1042 else if (c_imap_server_noise && mutt_istr_startswith(s, "NO"))
1043 {
1044 mutt_debug(LL_DEBUG2, "Handling untagged NO\n");
1045
1046 /* Display the warning message from the server */
1047 mutt_error("%s", s + 2);
1048 }
1049
1050 return 0;
1051}
static void cmd_parse_capability(struct ImapAccountData *adata, char *s)
Set capability bits according to CAPABILITY response.
Definition: command.c:533
static void cmd_parse_lsub(struct ImapAccountData *adata, char *s)
Parse a server LSUB (list subscribed mailboxes)
Definition: command.c:661
static void cmd_parse_status(struct ImapAccountData *adata, char *s)
Parse status from server.
Definition: command.c:805
static void cmd_parse_exists(struct ImapAccountData *adata, const char *pn)
Parse EXISTS message from serer.
Definition: command.c:948
static void cmd_parse_myrights(struct ImapAccountData *adata, const char *s)
Set rights bits according to MYRIGHTS response.
Definition: command.c:713
static void cmd_parse_expunge(struct ImapAccountData *adata, const char *s)
Parse expunge command.
Definition: command.c:254
static void cmd_parse_enabled(struct ImapAccountData *adata, const char *s)
Record what the server has enabled.
Definition: command.c:928
static void cmd_parse_vanished(struct ImapAccountData *adata, char *s)
Parse vanished command.
Definition: command.c:301
static void cmd_parse_fetch(struct ImapAccountData *adata, char *s)
Load fetch response into ImapAccountData.
Definition: command.c:395
@ 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 1062 of file command.c.

1063{
1064 return cmd_start(adata, cmdstr, IMAP_CMD_NO_FLAGS);
1065}
static int cmd_start(struct ImapAccountData *adata, const char *cmdstr, ImapCmdFlags flags)
Start a new IMAP command.
Definition: command.c:198
#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 1076 of file command.c.

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

1200{
1201 return cmd_status(s) == IMAP_RES_OK;
1202}
+ 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 1210 of file command.c.

1211{
1212 static const char *notrailer = "";
1213 const char *s = adata->buf;
1214
1215 if (!s)
1216 {
1217 mutt_debug(LL_DEBUG2, "not a tagged response\n");
1218 return notrailer;
1219 }
1220
1221 s = imap_next_word((char *) s);
1222 if (!s || (!mutt_istr_startswith(s, "OK") && !mutt_istr_startswith(s, "NO") &&
1223 !mutt_istr_startswith(s, "BAD")))
1224 {
1225 mutt_debug(LL_DEBUG2, "not a command completion: %s\n", adata->buf);
1226 return notrailer;
1227 }
1228
1229 s = imap_next_word((char *) s);
1230 if (!s)
1231 return notrailer;
1232
1233 return s;
1234}
+ 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 1247 of file command.c.

1248{
1249 int rc;
1250
1251 if (flags & IMAP_CMD_SINGLE)
1252 {
1253 // Process any existing commands
1254 if (adata->nextcmd != adata->lastcmd)
1255 imap_exec(adata, NULL, IMAP_CMD_POLL);
1256 }
1257
1258 rc = cmd_start(adata, cmdstr, flags);
1259 if (rc < 0)
1260 {
1261 cmd_handle_fatal(adata);
1262 return IMAP_EXEC_FATAL;
1263 }
1264
1265 if (flags & IMAP_CMD_QUEUE)
1266 return IMAP_EXEC_SUCCESS;
1267
1268 const short c_imap_poll_timeout = cs_subset_number(NeoMutt->sub, "imap_poll_timeout");
1269 if ((flags & IMAP_CMD_POLL) && (c_imap_poll_timeout > 0) &&
1270 ((mutt_socket_poll(adata->conn, c_imap_poll_timeout)) == 0))
1271 {
1272 mutt_error(_("Connection to %s timed out"), adata->conn->account.host);
1273 cmd_handle_fatal(adata);
1274 return IMAP_EXEC_FATAL;
1275 }
1276
1277 /* Allow interruptions, particularly useful if there are network problems. */
1279 do
1280 {
1281 rc = imap_cmd_step(adata);
1282 // The queue is empty, so the single command has been processed
1283 if ((flags & IMAP_CMD_SINGLE) && (adata->nextcmd == adata->lastcmd))
1284 break;
1285 } while (rc == IMAP_RES_CONTINUE);
1287
1288 if (rc == IMAP_RES_NO)
1289 return IMAP_EXEC_ERROR;
1290 if (rc != IMAP_RES_OK)
1291 {
1292 if (adata->status != IMAP_FATAL)
1293 return IMAP_EXEC_ERROR;
1294
1295 mutt_debug(LL_DEBUG1, "command failed: %s\n", adata->buf);
1296 return IMAP_EXEC_FATAL;
1297 }
1298
1299 return IMAP_EXEC_SUCCESS;
1300}
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:193
+ 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 1312 of file command.c.

1313{
1314 if (!adata)
1315 return;
1316
1317 if (adata->status == IMAP_FATAL)
1318 {
1319 adata->closing = false;
1320 cmd_handle_fatal(adata);
1321 return;
1322 }
1323
1324 if (!(adata->state >= IMAP_SELECTED) || (adata->mailbox && adata->closing))
1325 {
1326 adata->closing = false;
1327 return;
1328 }
1329
1330 adata->closing = false;
1331
1332 struct ImapMboxData *mdata = imap_mdata_get(adata->mailbox);
1333
1334 if (mdata && mdata->reopen & IMAP_REOPEN_ALLOW)
1335 {
1336 // First remove expunged emails from the msn_index
1337 if (mdata->reopen & IMAP_EXPUNGE_PENDING)
1338 {
1339 mutt_debug(LL_DEBUG2, "Expunging mailbox\n");
1340 imap_expunge_mailbox(adata->mailbox, true);
1341 /* Detect whether we've gotten unexpected EXPUNGE messages */
1342 if (!(mdata->reopen & IMAP_EXPUNGE_EXPECTED))
1343 mdata->check_status |= IMAP_EXPUNGE_PENDING;
1345 }
1346
1347 // Then add new emails to it
1348 if (mdata->reopen & IMAP_NEWMAIL_PENDING)
1349 {
1350 const size_t max_msn = imap_msn_highest(&mdata->msn);
1351 if (mdata->new_mail_count > max_msn)
1352 {
1353 if (!(mdata->reopen & IMAP_EXPUNGE_PENDING))
1354 mdata->check_status |= IMAP_NEWMAIL_PENDING;
1355
1356 mutt_debug(LL_DEBUG2, "Fetching new mails from %ld to %u\n",
1357 max_msn + 1, mdata->new_mail_count);
1358 imap_read_headers(adata->mailbox, max_msn + 1, mdata->new_mail_count, false);
1359 }
1360 }
1361
1362 // And to finish inform about MUTT_REOPEN if needed
1363 if (mdata->reopen & IMAP_EXPUNGE_PENDING && !(mdata->reopen & IMAP_EXPUNGE_EXPECTED))
1364 mdata->check_status |= IMAP_EXPUNGE_PENDING;
1365
1366 if (mdata->reopen & IMAP_EXPUNGE_PENDING)
1368 }
1369
1370 adata->status = 0;
1371}
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:1327
#define IMAP_EXPUNGE_EXPECTED
Messages will be expunged from the server.
Definition: private.h:67
void imap_expunge_mailbox(struct Mailbox *m, bool resort)
Purge messages from the server.
Definition: imap.c:675
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 1379 of file command.c.

1380{
1381 int rc;
1382
1383 if (cmd_start(adata, "IDLE", IMAP_CMD_POLL) < 0)
1384 {
1385 cmd_handle_fatal(adata);
1386 return -1;
1387 }
1388
1389 const short c_imap_poll_timeout = cs_subset_number(NeoMutt->sub, "imap_poll_timeout");
1390 if ((c_imap_poll_timeout > 0) &&
1391 ((mutt_socket_poll(adata->conn, c_imap_poll_timeout)) == 0))
1392 {
1393 mutt_error(_("Connection to %s timed out"), adata->conn->account.host);
1394 cmd_handle_fatal(adata);
1395 return -1;
1396 }
1397
1398 do
1399 {
1400 rc = imap_cmd_step(adata);
1401 } while (rc == IMAP_RES_CONTINUE);
1402
1403 if (rc == IMAP_RES_RESPOND)
1404 {
1405 /* successfully entered IDLE state */
1406 adata->state = IMAP_IDLE;
1407 /* queue automatic exit when next command is issued */
1408 mutt_buffer_addstr(&adata->cmdbuf, "DONE\r\n");
1409 rc = IMAP_RES_OK;
1410 }
1411 if (rc != IMAP_RES_OK)
1412 {
1413 mutt_debug(LL_DEBUG1, "error starting IDLE\n");
1414 return -1;
1415 }
1416
1417 return 0;
1418}
size_t mutt_buffer_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:233
+ 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",
"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 63 of file command.c.