NeoMutt  2025-12-11-58-g09398d
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
command.c File Reference

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

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

Variables

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

Detailed Description

Send/receive commands to/from an IMAP server.

Authors
  • Michael R. Elkins
  • Brandon Long
  • Brendan Cully
  • Richard Russon
  • Mehdi Abaakouk
  • Pietro Cerutti
  • Fabian Groffen
  • Federico Kircheis
  • Ian Zimmerman

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 59 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 96 of file command.c.

97{
98 if (((adata->nextcmd + 1) % adata->cmdslots) == adata->lastcmd)
99 return true;
100
101 return false;
102}
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 110 of file command.c.

111{
112 struct ImapCommand *cmd = NULL;
113
114 if (cmd_queue_full(adata))
115 {
116 mutt_debug(LL_DEBUG3, "IMAP command queue full\n");
117 return NULL;
118 }
119
120 cmd = adata->cmds + adata->nextcmd;
121 adata->nextcmd = (adata->nextcmd + 1) % adata->cmdslots;
122
123 snprintf(cmd->seq, sizeof(cmd->seq), "%c%04u", adata->seqid, adata->seqno++);
124 if (adata->seqno > 9999)
125 adata->seqno = 0;
126
127 cmd->state = IMAP_RES_NEW;
128
129 return cmd;
130}
#define mutt_debug(LEVEL,...)
Definition logging2.h:90
static bool cmd_queue_full(struct ImapAccountData *adata)
Is the IMAP command queue full?
Definition command.c:96
#define IMAP_RES_NEW
ImapCommand.state additions.
Definition private.h:58
@ LL_DEBUG3
Log at debug level 3.
Definition logging2.h:46
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:160
int state
Command state, e.g. IMAP_RES_NEW.
Definition private.h:162
char seq[SEQ_LEN+1]
Command tag, e.g. 'a0001'.
Definition private.h:161
+ 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 142 of file command.c.

143{
144 if (cmd_queue_full(adata))
145 {
146 mutt_debug(LL_DEBUG3, "Draining IMAP command pipeline\n");
147
148 const int rc = imap_exec(adata, NULL, flags & IMAP_CMD_POLL);
149
150 if (rc == IMAP_EXEC_ERROR)
151 return IMAP_RES_BAD;
152 }
153
154 struct ImapCommand *cmd = cmd_new(adata);
155 if (!cmd)
156 return IMAP_RES_BAD;
157
158 if (buf_add_printf(&adata->cmdbuf, "%s %s\r\n", cmd->seq, cmdstr) < 0)
159 return IMAP_RES_BAD;
160
161 return 0;
162}
int buf_add_printf(struct Buffer *buf, const char *fmt,...)
Format a string appending a Buffer.
Definition buffer.c:204
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:1322
static struct ImapCommand * cmd_new(struct ImapAccountData *adata)
Create and queue a new command control block.
Definition command.c:110
#define IMAP_CMD_POLL
Poll the tcp connection before running the imap command.
Definition private.h:74
@ IMAP_EXEC_ERROR
Imap command failure.
Definition private.h:83
#define IMAP_RES_BAD
<tag> BAD ...
Definition private.h:54
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 168 of file command.c.

169{
170 adata->status = IMAP_FATAL;
171
172 if (!adata->mailbox)
173 return;
174
175 struct ImapMboxData *mdata = adata->mailbox->mdata;
176 if (!mdata)
177 return;
178
179 if ((adata->state >= IMAP_SELECTED) && (mdata->reopen & IMAP_REOPEN_ALLOW))
180 {
181 mx_fastclose_mailbox(adata->mailbox, true);
182 mutt_error(_("Mailbox %s@%s closed"), adata->conn->account.user,
183 adata->conn->account.host);
184 }
185
187 if (!adata->recovering)
188 {
189 adata->recovering = true;
190 if (imap_login(adata))
192 adata->recovering = false;
193 }
194}
#define mutt_error(...)
Definition logging2.h:93
@ IMAP_SELECTED
Mailbox is selected.
Definition private.h:108
#define IMAP_REOPEN_ALLOW
Allow re-opening a folder upon expunge.
Definition private.h:64
@ IMAP_FATAL
Unrecoverable error occurred.
Definition private.h:95
void imap_close_connection(struct ImapAccountData *adata)
Close an IMAP connection.
Definition imap.c:869
int imap_login(struct ImapAccountData *adata)
Open an IMAP connection.
Definition imap.c:1808
#define _(a)
Definition message.h:28
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
void mx_fastclose_mailbox(struct Mailbox *m, bool keep_account)
Free up memory associated with the Mailbox.
Definition mx.c:414
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:49
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:40
void * mdata
Driver specific data.
Definition mailbox.h:132
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

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

205{
206 int rc;
207
208 if (adata->status == IMAP_FATAL)
209 {
210 cmd_handle_fatal(adata);
211 return -1;
212 }
213
214 if (cmdstr && ((rc = cmd_queue(adata, cmdstr, flags)) < 0))
215 return rc;
216
217 if (flags & IMAP_CMD_QUEUE)
218 return 0;
219
220 if (buf_is_empty(&adata->cmdbuf))
221 return IMAP_RES_BAD;
222
223 rc = mutt_socket_send_d(adata->conn, adata->cmdbuf.data,
225 buf_reset(&adata->cmdbuf);
226
227 /* unidle when command queue is flushed */
228 if (adata->state == IMAP_IDLE)
229 adata->state = IMAP_SELECTED;
230
231 return (rc < 0) ? IMAP_RES_BAD : 0;
232}
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition buffer.c:76
bool buf_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition buffer.c:291
static int cmd_queue(struct ImapAccountData *adata, const char *cmdstr, ImapCmdFlags flags)
Add a IMAP command to the queue.
Definition command.c:142
static void cmd_handle_fatal(struct ImapAccountData *adata)
When ImapAccountData is in fatal state, do what we can.
Definition command.c:168
@ IMAP_IDLE
Connection is idle.
Definition private.h:111
#define IMAP_CMD_PASS
Command contains a password. Suppress logging.
Definition private.h:72
#define IMAP_LOG_PASS
Definition private.h:50
#define IMAP_LOG_CMD
Definition private.h:48
#define IMAP_CMD_QUEUE
Queue a command, do not execute.
Definition private.h:73
#define mutt_socket_send_d(conn, buf, dbg)
Definition socket.h:58
char * data
Pointer to data.
Definition buffer.h:37
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

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

241{
242 s = imap_next_word((char *) s);
243
244 if (mutt_istr_startswith(s, "OK"))
245 return IMAP_RES_OK;
246 if (mutt_istr_startswith(s, "NO"))
247 return IMAP_RES_NO;
248
249 return IMAP_RES_BAD;
250}
#define IMAP_RES_OK
<tag> OK ...
Definition private.h:55
#define IMAP_RES_NO
<tag> NO ...
Definition private.h:53
char * imap_next_word(char *s)
Find where the next IMAP word begins.
Definition util.c:824
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition string.c:244
+ 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 260 of file command.c.

261{
262 unsigned int exp_msn = 0;
263 struct Email *e = NULL;
264
265 mutt_debug(LL_DEBUG2, "Handling EXPUNGE\n");
266
267 struct ImapMboxData *mdata = adata->mailbox->mdata;
268 if (!mdata)
269 return;
270
271 if (!mutt_str_atoui(s, &exp_msn) || (exp_msn < 1) ||
272 (exp_msn > imap_msn_highest(&mdata->msn)))
273 {
274 return;
275 }
276
277 e = imap_msn_get(&mdata->msn, exp_msn - 1);
278 if (e)
279 {
280 /* imap_expunge_mailbox() will rewrite e->index.
281 * It needs to resort using EMAIL_SORT_UNSORTED anyway, so setting to INT_MAX
282 * makes the code simpler and possibly more efficient. */
283 e->index = INT_MAX;
284
286 if (edata)
287 edata->msn = 0;
288 }
289
290 /* decrement seqno of those above. */
291 const size_t max_msn = imap_msn_highest(&mdata->msn);
292 for (unsigned int cur = exp_msn; cur < max_msn; cur++)
293 {
294 e = imap_msn_get(&mdata->msn, cur);
295 if (e)
296 imap_edata_get(e)->msn--;
297 imap_msn_set(&mdata->msn, cur - 1, e);
298 }
299 imap_msn_shrink(&mdata->msn, 1);
300
302}
const char * mutt_str_atoui(const char *str, unsigned int *dst)
Convert ASCII string to an unsigned integer.
Definition atoi.c:217
struct ImapEmailData * imap_edata_get(struct Email *e)
Get the private data for this Email.
Definition edata.c:66
#define IMAP_EXPUNGE_PENDING
Messages on the server have been expunged.
Definition private.h:66
@ LL_DEBUG2
Log at debug level 2.
Definition logging2.h:45
size_t imap_msn_shrink(struct MSNArray *msn, size_t num)
Remove a number of entries from the end of the cache.
Definition msn.c:106
struct Email * imap_msn_get(const struct MSNArray *msn, int idx)
Return the Email associated with an msn.
Definition msn.c:83
size_t imap_msn_highest(const struct MSNArray *msn)
Return the highest MSN in use.
Definition msn.c:72
void imap_msn_set(struct MSNArray *msn, size_t idx, struct Email *e)
Cache an Email into a given position.
Definition msn.c:95
The envelope/body of an email.
Definition email.h:39
void * edata
Driver-specific data.
Definition email.h:74
int index
The absolute (unsorted) message number.
Definition email.h:110
IMAP-specific Email data -.
Definition edata.h:35
unsigned int msn
Message Sequence Number.
Definition edata.h:46
ImapOpenFlags reopen
Flags, e.g. IMAP_REOPEN_ALLOW.
Definition mdata.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 312 of file command.c.

313{
314 bool earlier = false;
315 int rc;
316 unsigned int uid = 0;
317
318 struct ImapMboxData *mdata = adata->mailbox->mdata;
319 if (!mdata)
320 return;
321
322 mutt_debug(LL_DEBUG2, "Handling VANISHED\n");
323
324 if (mutt_istr_startswith(s, "(EARLIER)"))
325 {
326 /* The RFC says we should not decrement msns with the VANISHED EARLIER tag.
327 * My experimentation says that's crap. */
328 earlier = true;
329 s = imap_next_word(s);
330 }
331
332 char *end_of_seqset = s;
333 while (*end_of_seqset)
334 {
335 if (!strchr("0123456789:,", *end_of_seqset))
336 *end_of_seqset = '\0';
337 else
338 end_of_seqset++;
339 }
340
342 if (!iter)
343 {
344 mutt_debug(LL_DEBUG2, "VANISHED: empty seqset [%s]?\n", s);
345 return;
346 }
347
348 while ((rc = mutt_seqset_iterator_next(iter, &uid)) == 0)
349 {
350 struct Email *e = mutt_hash_int_find(mdata->uid_hash, uid);
351 if (!e)
352 continue;
353
354 unsigned int exp_msn = imap_edata_get(e)->msn;
355
356 /* imap_expunge_mailbox() will rewrite e->index.
357 * It needs to resort using EMAIL_SORT_UNSORTED anyway, so setting to INT_MAX
358 * makes the code simpler and possibly more efficient. */
359 e->index = INT_MAX;
360 imap_edata_get(e)->msn = 0;
361
362 if ((exp_msn < 1) || (exp_msn > imap_msn_highest(&mdata->msn)))
363 {
364 mutt_debug(LL_DEBUG1, "VANISHED: msn for UID %u is incorrect\n", uid);
365 continue;
366 }
367 if (imap_msn_get(&mdata->msn, exp_msn - 1) != e)
368 {
369 mutt_debug(LL_DEBUG1, "VANISHED: msn_index for UID %u is incorrect\n", uid);
370 continue;
371 }
372
373 imap_msn_remove(&mdata->msn, exp_msn - 1);
374
375 if (!earlier)
376 {
377 /* decrement seqno of those above. */
378 const size_t max_msn = imap_msn_highest(&mdata->msn);
379 for (unsigned int cur = exp_msn; cur < max_msn; cur++)
380 {
381 e = imap_msn_get(&mdata->msn, cur);
382 if (e)
383 imap_edata_get(e)->msn--;
384 imap_msn_set(&mdata->msn, cur - 1, e);
385 }
386
387 imap_msn_shrink(&mdata->msn, 1);
388 }
389 }
390
391 if (rc < 0)
392 mutt_debug(LL_DEBUG1, "VANISHED: illegal seqset %s\n", s);
393
395
397}
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:1127
int mutt_seqset_iterator_next(struct SeqsetIterator *iter, unsigned int *next)
Get the next UID from a Sequence Set.
Definition util.c:1148
void mutt_seqset_iterator_free(struct SeqsetIterator **ptr)
Free a Sequence Set Iterator.
Definition util.c:1207
@ LL_DEBUG1
Log at debug level 1.
Definition logging2.h:44
void imap_msn_remove(struct MSNArray *msn, int idx)
Remove an entry from the cache.
Definition msn.c:116
struct HashTable * uid_hash
Hash Table: "uid" -> Email.
Definition mdata.h:59
UID Sequence Set Iterator.
Definition private.h:169
+ 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 408 of file command.c.

409{
410 unsigned int msn, uid;
411 struct Email *e = NULL;
412 char *flags = NULL;
413 int uid_checked = 0;
414 bool server_changes = false;
415
416 struct ImapMboxData *mdata = imap_mdata_get(adata->mailbox);
417 if (!mdata)
418 return;
419
420 mutt_debug(LL_DEBUG3, "Handling FETCH\n");
421
422 if (!mutt_str_atoui(s, &msn))
423 {
424 mutt_debug(LL_DEBUG3, "Skipping FETCH response - illegal MSN\n");
425 return;
426 }
427
428 if ((msn < 1) || (msn > imap_msn_highest(&mdata->msn)))
429 {
430 mutt_debug(LL_DEBUG3, "Skipping FETCH response - MSN %u out of range\n", msn);
431 return;
432 }
433
434 e = imap_msn_get(&mdata->msn, msn - 1);
435 if (!e || !e->active)
436 {
437 mutt_debug(LL_DEBUG3, "Skipping FETCH response - MSN %u not in msn_index\n", msn);
438 return;
439 }
440
442 if (edata)
443 {
444 mutt_debug(LL_DEBUG2, "Message UID %u updated\n", edata->uid);
445 return;
446 }
447 /* skip FETCH */
448 s = imap_next_word(s);
449 s = imap_next_word(s);
450
451 if (*s != '(')
452 {
453 mutt_debug(LL_DEBUG1, "Malformed FETCH response\n");
454 return;
455 }
456 s++;
457
458 while (*s)
459 {
460 SKIPWS(s);
461 size_t plen = mutt_istr_startswith(s, "FLAGS");
462 if (plen != 0)
463 {
464 flags = s;
465 if (uid_checked)
466 break;
467
468 s += plen;
469 SKIPWS(s);
470 if (*s != '(')
471 {
472 mutt_debug(LL_DEBUG1, "bogus FLAGS response: %s\n", s);
473 return;
474 }
475 s++;
476 while (*s && (*s != ')'))
477 s++;
478 if (*s == ')')
479 {
480 s++;
481 }
482 else
483 {
484 mutt_debug(LL_DEBUG1, "Unterminated FLAGS response: %s\n", s);
485 return;
486 }
487 }
488 else if ((plen = mutt_istr_startswith(s, "UID")))
489 {
490 s += plen;
491 SKIPWS(s);
492 if (!mutt_str_atoui(s, &uid))
493 {
494 mutt_debug(LL_DEBUG1, "Illegal UID. Skipping update\n");
495 return;
496 }
497 if (uid != imap_edata_get(e)->uid)
498 {
499 mutt_debug(LL_DEBUG1, "UID vs MSN mismatch. Skipping update\n");
500 return;
501 }
502 uid_checked = 1;
503 if (flags)
504 break;
505 s = imap_next_word(s);
506 }
507 else if ((plen = mutt_istr_startswith(s, "MODSEQ")))
508 {
509 s += plen;
510 SKIPWS(s);
511 if (*s != '(')
512 {
513 mutt_debug(LL_DEBUG1, "bogus MODSEQ response: %s\n", s);
514 return;
515 }
516 s++;
517 while (*s && (*s != ')'))
518 s++;
519 if (*s == ')')
520 {
521 s++;
522 }
523 else
524 {
525 mutt_debug(LL_DEBUG1, "Unterminated MODSEQ response: %s\n", s);
526 return;
527 }
528 }
529 else if (*s == ')')
530 {
531 break; /* end of request */
532 }
533 else if (*s)
534 {
535 mutt_debug(LL_DEBUG2, "Only handle FLAGS updates\n");
536 break;
537 }
538 }
539
540 if (flags)
541 {
542 imap_set_flags(adata->mailbox, e, flags, &server_changes);
543 if (server_changes)
544 {
545 /* If server flags could conflict with NeoMutt's flags, reopen the mailbox. */
546 if (e->changed)
548 else
550 }
551 }
552}
struct ImapMboxData * imap_mdata_get(struct Mailbox *m)
Get the Mailbox data for this mailbox.
Definition mdata.c:61
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:1939
#define IMAP_FLAGS_PENDING
Flags have changed on the server.
Definition private.h:68
#define SKIPWS(ch)
Definition string2.h:51
bool active
Message is not to be removed.
Definition email.h:76
bool changed
Email has been edited.
Definition email.h:77
unsigned int uid
32-bit Message UID
Definition edata.h:45
ImapOpenFlags check_status
Flags, e.g. IMAP_NEWMAIL_PENDING.
Definition mdata.h:46
+ 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 559 of file command.c.

560{
561 mutt_debug(LL_DEBUG3, "Handling CAPABILITY\n");
562
563 s = imap_next_word(s);
564 char *bracket = strchr(s, ']');
565 if (bracket)
566 *bracket = '\0';
567 FREE(&adata->capstr);
568 adata->capstr = mutt_str_dup(s);
569 adata->capabilities = 0;
570
571 while (*s)
572 {
573 for (size_t i = 0; Capabilities[i]; i++)
574 {
575 size_t len = mutt_istr_startswith(s, Capabilities[i]);
576 if (len != 0 && ((s[len] == '\0') || mutt_isspace(s[len])))
577 {
578 adata->capabilities |= (1 << i);
579 mutt_debug(LL_DEBUG3, " Found capability \"%s\": %zu\n", Capabilities[i], i);
580 break;
581 }
582 }
583 s = imap_next_word(s);
584 }
585}
bool mutt_isspace(int arg)
Wrapper for isspace(3)
Definition ctype.c:95
static const char *const Capabilities[]
Server capabilities strings that we understand.
Definition command.c:66
#define FREE(x)
Definition memory.h:62
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition string.c:255
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 592 of file command.c.

593{
594 struct ImapList *list = NULL;
595 struct ImapList lb = { 0 };
596 unsigned int litlen;
597
598 if (adata->cmdresult)
599 list = adata->cmdresult;
600 else
601 list = &lb;
602
603 memset(list, 0, sizeof(struct ImapList));
604
605 /* flags */
606 s = imap_next_word(s);
607 if (*s != '(')
608 {
609 mutt_debug(LL_DEBUG1, "Bad LIST response\n");
610 return;
611 }
612 s++;
613 while (*s)
614 {
615 if (mutt_istr_startswith(s, "\\NoSelect"))
616 list->noselect = true;
617 else if (mutt_istr_startswith(s, "\\NonExistent")) /* rfc5258 */
618 list->noselect = true;
619 else if (mutt_istr_startswith(s, "\\NoInferiors"))
620 list->noinferiors = true;
621 else if (mutt_istr_startswith(s, "\\HasNoChildren")) /* rfc5258*/
622 list->noinferiors = true;
623
624 s = imap_next_word(s);
625 if (*(s - 2) == ')')
626 break;
627 }
628
629 /* Delimiter */
630 if (!mutt_istr_startswith(s, "NIL"))
631 {
632 char delimbuf[5] = { 0 }; // worst case: "\\"\0
633 snprintf(delimbuf, sizeof(delimbuf), "%s", s);
634 imap_unquote_string(delimbuf);
635 list->delim = delimbuf[0];
636 }
637
638 /* Name */
639 s = imap_next_word(s);
640 /* Notes often responds with literals here. We need a real tokenizer. */
641 if (imap_get_literal_count(s, &litlen) == 0)
642 {
643 if (imap_cmd_step(adata) != IMAP_RES_CONTINUE)
644 {
645 adata->status = IMAP_FATAL;
646 return;
647 }
648
649 if (strlen(adata->buf) < litlen)
650 {
651 mutt_debug(LL_DEBUG1, "Error parsing LIST mailbox\n");
652 return;
653 }
654
655 list->name = adata->buf;
656 s = list->name + litlen;
657 if (s[0] != '\0')
658 {
659 s[0] = '\0';
660 s++;
661 SKIPWS(s);
662 }
663 }
664 else
665 {
666 list->name = s;
667 /* Exclude rfc5258 RECURSIVEMATCH CHILDINFO suffix */
668 s = imap_next_word(s);
669 if (s[0] != '\0')
670 s[-1] = '\0';
671 imap_unmunge_mbox_name(adata->unicode, list->name);
672 }
673
674 if (list->name[0] == '\0')
675 {
676 adata->delim = list->delim;
677 mutt_debug(LL_DEBUG3, "Root delimiter: %c\n", adata->delim);
678 }
679}
int imap_cmd_step(struct ImapAccountData *adata)
Reads server responses from an IMAP command.
Definition command.c:1147
void imap_unmunge_mbox_name(bool unicode, char *s)
Remove quoting from a mailbox name.
Definition util.c:977
int imap_get_literal_count(const char *buf, unsigned int *bytes)
Write number of bytes in an IMAP literal into bytes.
Definition util.c:780
void imap_unquote_string(char *s)
Equally stupid unquoting routine.
Definition util.c:923
#define IMAP_RES_CONTINUE
* ...
Definition private.h:56
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:149
bool noselect
Definition private.h:152
bool noinferiors
Definition private.h:153
char * name
Definition private.h:150
char delim
Definition private.h:151
+ 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 686 of file command.c.

687{
688 if (adata->cmdresult)
689 {
690 /* caller will handle response itself */
691 cmd_parse_list(adata, s);
692 return;
693 }
694
695 const bool c_imap_check_subscribed = cs_subset_bool(NeoMutt->sub, "imap_check_subscribed");
696 if (!c_imap_check_subscribed)
697 return;
698
699 struct ImapList list = { 0 };
700
701 adata->cmdresult = &list;
702 cmd_parse_list(adata, s);
703 adata->cmdresult = NULL;
704 /* noselect is for a gmail quirk */
705 if (!list.name || list.noselect)
706 return;
707
708 mutt_debug(LL_DEBUG3, "Subscribing to %s\n", list.name);
709
710 struct Buffer *buf = buf_pool_get();
711 struct Buffer *err = buf_pool_get();
712 struct Url url = { 0 };
713
714 account_to_url(&adata->conn->account, &url);
715 url.path = list.name;
716
717 const char *const c_imap_user = cs_subset_string(NeoMutt->sub, "imap_user");
718 if (mutt_str_equal(url.user, c_imap_user))
719 url.user = NULL;
720 url_tobuffer(&url, buf, U_NO_FLAGS);
721
722 if (!mailbox_add_simple(buf_string(buf), err))
723 mutt_debug(LL_DEBUG1, "Error adding subscribed mailbox: %s\n", buf_string(err));
724
725 buf_pool_release(&buf);
726 buf_pool_release(&err);
727}
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition buffer.h:96
bool mailbox_add_simple(const char *mailbox, struct Buffer *err)
Add a new Mailbox.
Definition commands.c:796
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition helpers.c:291
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition helpers.c:47
static void cmd_parse_list(struct ImapAccountData *adata, char *s)
Parse a server LIST command (list mailboxes)
Definition command.c:592
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition string.c:660
void account_to_url(struct ConnAccount *cac, struct Url *url)
Fill URL with info from account.
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition pool.c:82
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition pool.c:96
String manipulation buffer.
Definition buffer.h:36
Container for Accounts, Notifications.
Definition neomutt.h:43
struct ConfigSubset * sub
Inherited config items.
Definition neomutt.h:47
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_tobuffer(const struct Url *url, struct Buffer *buf, uint8_t flags)
Output the URL string for a given Url object.
Definition url.c:357
#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 734 of file command.c.

735{
736 mutt_debug(LL_DEBUG2, "Handling MYRIGHTS\n");
737
738 s = imap_next_word((char *) s);
739 s = imap_next_word((char *) s);
740
741 /* zero out current rights set */
742 adata->mailbox->rights = 0;
743
744 while (*s && !mutt_isspace(*s))
745 {
746 switch (*s)
747 {
748 case 'a':
749 adata->mailbox->rights |= MUTT_ACL_ADMIN;
750 break;
751 case 'e':
753 break;
754 case 'i':
755 adata->mailbox->rights |= MUTT_ACL_INSERT;
756 break;
757 case 'k':
758 adata->mailbox->rights |= MUTT_ACL_CREATE;
759 break;
760 case 'l':
761 adata->mailbox->rights |= MUTT_ACL_LOOKUP;
762 break;
763 case 'p':
764 adata->mailbox->rights |= MUTT_ACL_POST;
765 break;
766 case 'r':
767 adata->mailbox->rights |= MUTT_ACL_READ;
768 break;
769 case 's':
770 adata->mailbox->rights |= MUTT_ACL_SEEN;
771 break;
772 case 't':
773 adata->mailbox->rights |= MUTT_ACL_DELETE;
774 break;
775 case 'w':
776 adata->mailbox->rights |= MUTT_ACL_WRITE;
777 break;
778 case 'x':
779 adata->mailbox->rights |= MUTT_ACL_DELMX;
780 break;
781
782 /* obsolete rights */
783 case 'c':
785 break;
786 case 'd':
788 break;
789 default:
790 mutt_debug(LL_DEBUG1, "Unknown right: %c\n", *s);
791 }
792 s++;
793 }
794}
#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:119
+ 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 802 of file command.c.

803{
804 if (!adata || !adata->account || !name)
805 return NULL;
806
807 struct Mailbox **mp = NULL;
808 ARRAY_FOREACH(mp, &adata->account->mailboxes)
809 {
810 struct Mailbox *m = *mp;
811
812 struct ImapMboxData *mdata = imap_mdata_get(m);
813 if (mdata && mutt_str_equal(name, mdata->name))
814 return m;
815 }
816
817 return NULL;
818}
#define ARRAY_FOREACH(elem, head)
Iterate over all elements of the array.
Definition array.h:214
struct MailboxArray mailboxes
All Mailboxes.
Definition account.h:40
struct Account * account
Parent Account.
Definition adata.h:78
char * name
Mailbox name.
Definition mdata.h:41
A mailbox.
Definition mailbox.h:79
+ 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 828 of file command.c.

829{
830 unsigned int litlen = 0;
831
832 char *mailbox = imap_next_word(s);
833
834 /* We need a real tokenizer. */
835 if (imap_get_literal_count(mailbox, &litlen) == 0)
836 {
837 if (imap_cmd_step(adata) != IMAP_RES_CONTINUE)
838 {
839 adata->status = IMAP_FATAL;
840 return;
841 }
842
843 if (strlen(adata->buf) < litlen)
844 {
845 mutt_debug(LL_DEBUG1, "Error parsing STATUS mailbox\n");
846 return;
847 }
848
849 mailbox = adata->buf;
850 s = mailbox + litlen;
851 s[0] = '\0';
852 s++;
853 SKIPWS(s);
854 }
855 else
856 {
857 s = imap_next_word(mailbox);
858 s[-1] = '\0';
859 imap_unmunge_mbox_name(adata->unicode, mailbox);
860 }
861
862 struct Mailbox *m = find_mailbox(adata, mailbox);
863 struct ImapMboxData *mdata = imap_mdata_get(m);
864 if (!mdata)
865 {
866 mutt_debug(LL_DEBUG3, "Received status for an unexpected mailbox: %s\n", mailbox);
867 return;
868 }
869 uint32_t olduv = mdata->uidvalidity;
870 unsigned int oldun = mdata->uid_next;
871
872 if (*s++ != '(')
873 {
874 mutt_debug(LL_DEBUG1, "Error parsing STATUS\n");
875 return;
876 }
877 while ((s[0] != '\0') && (s[0] != ')'))
878 {
879 char *value = imap_next_word(s);
880
881 errno = 0;
882 const unsigned long ulcount = strtoul(value, &value, 10);
883 const bool truncated = ((errno == ERANGE) && (ulcount == ULONG_MAX)) ||
884 ((unsigned int) ulcount != ulcount);
885 const unsigned int count = (unsigned int) ulcount;
886
887 // we accept truncating a larger value only for UIDVALIDITY, to accommodate
888 // IMAP servers that use 64-bits for it. This seems to be what Thunderbird
889 // is also doing, see #3830
890 if (mutt_str_startswith(s, "UIDVALIDITY"))
891 {
892 if (truncated)
893 {
895 "UIDVALIDITY [%lu] exceeds 32 bits, "
896 "truncated to [%u]\n",
897 ulcount, count);
898 }
899 mdata->uidvalidity = count;
900 }
901 else
902 {
903 if (truncated)
904 {
905 mutt_debug(LL_DEBUG1, "Number in [%s] exceeds 32 bits\n", s);
906 return;
907 }
908 else
909 {
910 if (mutt_str_startswith(s, "MESSAGES"))
911 mdata->messages = count;
912 else if (mutt_str_startswith(s, "RECENT"))
913 mdata->recent = count;
914 else if (mutt_str_startswith(s, "UIDNEXT"))
915 mdata->uid_next = count;
916 else if (mutt_str_startswith(s, "UNSEEN"))
917 mdata->unseen = count;
918 }
919 }
920
921 s = value;
922 if ((s[0] != '\0') && (*s != ')'))
923 s = imap_next_word(s);
924 }
925 mutt_debug(LL_DEBUG3, "%s (UIDVALIDITY: %u, UIDNEXT: %u) %d messages, %d recent, %d unseen\n",
926 mdata->name, mdata->uidvalidity, mdata->uid_next, mdata->messages,
927 mdata->recent, mdata->unseen);
928
929 mutt_debug(LL_DEBUG3, "Running default STATUS handler\n");
930
931 mutt_debug(LL_DEBUG3, "Found %s in mailbox list (OV: %u ON: %u U: %d)\n",
932 mailbox, olduv, oldun, mdata->unseen);
933
934 bool new_mail = false;
935 const bool c_mail_check_recent = cs_subset_bool(NeoMutt->sub, "mail_check_recent");
936 if (c_mail_check_recent)
937 {
938 if ((olduv != 0) && (olduv == mdata->uidvalidity))
939 {
940 if (oldun < mdata->uid_next)
941 new_mail = (mdata->unseen > 0);
942 }
943 else if ((olduv == 0) && (oldun == 0))
944 {
945 /* first check per session, use recent. might need a flag for this. */
946 new_mail = (mdata->recent > 0);
947 }
948 else
949 {
950 new_mail = (mdata->unseen > 0);
951 }
952 }
953 else
954 {
955 new_mail = (mdata->unseen > 0);
956 }
957
958 m->has_new = new_mail;
959 m->msg_count = mdata->messages;
960 m->msg_unread = mdata->unseen;
961
962 // force back to keep detecting new mail until the mailbox is opened
963 if (m->has_new)
964 mdata->uid_next = oldun;
965
966 struct EventMailbox ev_m = { m };
968}
@ NT_MAILBOX_CHANGE
Mailbox has been changed.
Definition mailbox.h:176
static struct Mailbox * find_mailbox(struct ImapAccountData *adata, const char *name)
Find a Mailbox by its name.
Definition command.c:802
bool notify_send(struct Notify *notify, enum NotifyType event_type, int event_subtype, void *event_data)
Send out a notification message.
Definition notify.c:173
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition string.c:232
@ NT_MAILBOX
Mailbox has changed, NotifyMailbox, EventMailbox.
Definition notify_type.h:49
An Event that happened to a Mailbox.
Definition mailbox.h:190
unsigned int uid_next
Definition mdata.h:52
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:145
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 975 of file command.c.

976{
977 mutt_debug(LL_DEBUG2, "Handling ENABLED\n");
978
979 while ((s = imap_next_word((char *) s)) && (*s != '\0'))
980 {
981 if (mutt_istr_startswith(s, "UTF8=ACCEPT") || mutt_istr_startswith(s, "UTF8=ONLY"))
982 {
983 adata->unicode = true;
984 }
985 if (mutt_istr_startswith(s, "QRESYNC"))
986 adata->qresync = true;
987 }
988}
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 995 of file command.c.

996{
997 unsigned int count = 0;
998 mutt_debug(LL_DEBUG2, "Handling EXISTS\n");
999
1000 if (!mutt_str_atoui(pn, &count))
1001 {
1002 mutt_debug(LL_DEBUG1, "Malformed EXISTS: '%s'\n", pn);
1003 return;
1004 }
1005
1006 struct ImapMboxData *mdata = adata->mailbox->mdata;
1007 if (!mdata)
1008 return;
1009
1010 /* new mail arrived */
1011 if (count < imap_msn_highest(&mdata->msn))
1012 {
1013 /* Notes 6.0.3 has a tendency to report fewer messages exist than
1014 * it should. */
1015 mutt_debug(LL_DEBUG1, "Message count is out of sync\n");
1016 }
1017 else if (count == imap_msn_highest(&mdata->msn))
1018 {
1019 /* at least the InterChange server sends EXISTS messages freely,
1020 * even when there is no new mail */
1021 mutt_debug(LL_DEBUG3, "superfluous EXISTS message\n");
1022 }
1023 else
1024 {
1025 mutt_debug(LL_DEBUG2, "New mail in %s - %d messages total\n", mdata->name, count);
1026 mdata->reopen |= IMAP_NEWMAIL_PENDING;
1027 mdata->new_mail_count = count;
1028 }
1029}
#define IMAP_NEWMAIL_PENDING
New mail is waiting on the server.
Definition private.h:67
+ 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 1037 of file command.c.

1038{
1039 char *s = imap_next_word(adata->buf);
1040 char *pn = imap_next_word(s);
1041
1042 const bool c_imap_server_noise = cs_subset_bool(NeoMutt->sub, "imap_server_noise");
1043 if ((adata->state >= IMAP_SELECTED) && mutt_isdigit(*s))
1044 {
1045 /* pn vs. s: need initial seqno */
1046 pn = s;
1047 s = imap_next_word(s);
1048
1049 /* EXISTS, EXPUNGE, FETCH are always related to the SELECTED mailbox */
1050 if (mutt_istr_startswith(s, "EXISTS"))
1051 cmd_parse_exists(adata, pn);
1052 else if (mutt_istr_startswith(s, "EXPUNGE"))
1053 cmd_parse_expunge(adata, pn);
1054 else if (mutt_istr_startswith(s, "FETCH"))
1055 cmd_parse_fetch(adata, pn);
1056 }
1057 else if ((adata->state >= IMAP_SELECTED) && mutt_istr_startswith(s, "VANISHED"))
1058 {
1059 cmd_parse_vanished(adata, pn);
1060 }
1061 else if (mutt_istr_startswith(s, "CAPABILITY"))
1062 {
1063 cmd_parse_capability(adata, s);
1064 }
1065 else if (mutt_istr_startswith(s, "OK [CAPABILITY"))
1066 {
1067 cmd_parse_capability(adata, pn);
1068 }
1069 else if (mutt_istr_startswith(pn, "OK [CAPABILITY"))
1070 {
1072 }
1073 else if (mutt_istr_startswith(s, "LIST"))
1074 {
1075 cmd_parse_list(adata, s);
1076 }
1077 else if (mutt_istr_startswith(s, "LSUB"))
1078 {
1079 cmd_parse_lsub(adata, s);
1080 }
1081 else if (mutt_istr_startswith(s, "MYRIGHTS"))
1082 {
1083 cmd_parse_myrights(adata, s);
1084 }
1085 else if (mutt_istr_startswith(s, "SEARCH"))
1086 {
1087 cmd_parse_search(adata, s);
1088 }
1089 else if (mutt_istr_startswith(s, "STATUS"))
1090 {
1091 cmd_parse_status(adata, s);
1092 }
1093 else if (mutt_istr_startswith(s, "ENABLED"))
1094 {
1095 cmd_parse_enabled(adata, s);
1096 }
1097 else if (mutt_istr_startswith(s, "BYE"))
1098 {
1099 mutt_debug(LL_DEBUG2, "Handling BYE\n");
1100
1101 /* check if we're logging out */
1102 if (adata->status == IMAP_BYE)
1103 return 0;
1104
1105 /* server shut down our connection */
1106 s += 3;
1107 SKIPWS(s);
1108 mutt_error("%s", s);
1109 cmd_handle_fatal(adata);
1110
1111 return -1;
1112 }
1113 else if (c_imap_server_noise && mutt_istr_startswith(s, "NO"))
1114 {
1115 mutt_debug(LL_DEBUG2, "Handling untagged NO\n");
1116
1117 /* Display the warning message from the server */
1118 mutt_error("%s", s + 2);
1119 }
1120
1121 return 0;
1122}
bool mutt_isdigit(int arg)
Wrapper for isdigit(3)
Definition ctype.c:65
static void cmd_parse_capability(struct ImapAccountData *adata, char *s)
Set capability bits according to CAPABILITY response.
Definition command.c:559
static void cmd_parse_lsub(struct ImapAccountData *adata, char *s)
Parse a server LSUB (list subscribed mailboxes)
Definition command.c:686
static void cmd_parse_status(struct ImapAccountData *adata, char *s)
Parse status from server.
Definition command.c:828
static void cmd_parse_exists(struct ImapAccountData *adata, const char *pn)
Parse EXISTS message from serer.
Definition command.c:995
static void cmd_parse_myrights(struct ImapAccountData *adata, const char *s)
Set rights bits according to MYRIGHTS response.
Definition command.c:734
static void cmd_parse_expunge(struct ImapAccountData *adata, const char *s)
Parse expunge command.
Definition command.c:260
static void cmd_parse_enabled(struct ImapAccountData *adata, const char *s)
Record what the server has enabled.
Definition command.c:975
static void cmd_parse_vanished(struct ImapAccountData *adata, char *s)
Parse vanished command.
Definition command.c:312
static void cmd_parse_fetch(struct ImapAccountData *adata, char *s)
Load fetch response into ImapAccountData.
Definition command.c:408
@ IMAP_BYE
Logged out from server.
Definition private.h:96
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 1133 of file command.c.

1134{
1135 return cmd_start(adata, cmdstr, IMAP_CMD_NO_FLAGS);
1136}
static int cmd_start(struct ImapAccountData *adata, const char *cmdstr, ImapCmdFlags flags)
Start a new IMAP command.
Definition command.c:204
#define IMAP_CMD_NO_FLAGS
No flags are set.
Definition private.h:71
+ 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 1147 of file command.c.

1148{
1149 if (!adata)
1150 return -1;
1151
1152 size_t len = 0;
1153 int c;
1154 int rc;
1155 int stillrunning = 0;
1156 struct ImapCommand *cmd = NULL;
1157
1158 if (adata->status == IMAP_FATAL)
1159 {
1160 cmd_handle_fatal(adata);
1161 return IMAP_RES_BAD;
1162 }
1163
1164 /* read into buffer, expanding buffer as necessary until we have a full
1165 * line */
1166 do
1167 {
1168 if (len == adata->blen)
1169 {
1170 MUTT_MEM_REALLOC(&adata->buf, adata->blen + IMAP_CMD_BUFSIZE, char);
1171 adata->blen = adata->blen + IMAP_CMD_BUFSIZE;
1172 mutt_debug(LL_DEBUG3, "grew buffer to %zu bytes\n", adata->blen);
1173 }
1174
1175 /* back up over '\0' */
1176 if (len)
1177 len--;
1178 c = mutt_socket_readln_d(adata->buf + len, adata->blen - len, adata->conn, MUTT_SOCK_LOG_FULL);
1179 if (c <= 0)
1180 {
1181 mutt_debug(LL_DEBUG1, "Error reading server response\n");
1182 cmd_handle_fatal(adata);
1183 return IMAP_RES_BAD;
1184 }
1185
1186 len += c;
1187 }
1188 /* if we've read all the way to the end of the buffer, we haven't read a
1189 * full line (mutt_socket_readln strips the \r, so we always have at least
1190 * one character free when we've read a full line) */
1191 while (len == adata->blen);
1192
1193 /* don't let one large string make cmd->buf hog memory forever */
1194 if ((adata->blen > IMAP_CMD_BUFSIZE) && (len <= IMAP_CMD_BUFSIZE))
1195 {
1196 MUTT_MEM_REALLOC(&adata->buf, IMAP_CMD_BUFSIZE, char);
1197 adata->blen = IMAP_CMD_BUFSIZE;
1198 mutt_debug(LL_DEBUG3, "shrank buffer to %zu bytes\n", adata->blen);
1199 }
1200
1201 adata->lastread = mutt_date_now();
1202
1203 /* handle untagged messages. The caller still gets its shot afterwards. */
1204 if ((mutt_str_startswith(adata->buf, "* ") ||
1205 mutt_str_startswith(imap_next_word(adata->buf), "OK [")) &&
1206 cmd_handle_untagged(adata))
1207 {
1208 return IMAP_RES_BAD;
1209 }
1210
1211 /* server demands a continuation response from us */
1212 if (adata->buf[0] == '+')
1213 return IMAP_RES_RESPOND;
1214
1215 /* Look for tagged command completions.
1216 *
1217 * Some response handlers can end up recursively calling
1218 * imap_cmd_step() and end up handling all tagged command
1219 * completions.
1220 * (e.g. FETCH->set_flag->set_header_color->~h pattern match.)
1221 *
1222 * Other callers don't even create an adata->cmds entry.
1223 *
1224 * For both these cases, we default to returning OK */
1225 rc = IMAP_RES_OK;
1226 c = adata->lastcmd;
1227 do
1228 {
1229 cmd = &adata->cmds[c];
1230 if (cmd->state == IMAP_RES_NEW)
1231 {
1232 if (mutt_str_startswith(adata->buf, cmd->seq))
1233 {
1234 if (!stillrunning)
1235 {
1236 /* first command in queue has finished - move queue pointer up */
1237 adata->lastcmd = (adata->lastcmd + 1) % adata->cmdslots;
1238 }
1239 cmd->state = cmd_status(adata->buf);
1240 rc = cmd->state;
1241 if (cmd->state == IMAP_RES_NO || cmd->state == IMAP_RES_BAD)
1242 {
1243 mutt_message(_("IMAP command failed: %s"), adata->buf);
1244 }
1245 }
1246 else
1247 {
1248 stillrunning++;
1249 }
1250 }
1251
1252 c = (c + 1) % adata->cmdslots;
1253 } while (c != adata->nextcmd);
1254
1255 if (stillrunning)
1256 {
1257 rc = IMAP_RES_CONTINUE;
1258 }
1259 else
1260 {
1261 mutt_debug(LL_DEBUG3, "IMAP queue drained\n");
1262 imap_cmd_finish(adata);
1263 }
1264
1265 return rc;
1266}
#define mutt_message(...)
Definition logging2.h:92
static int cmd_handle_untagged(struct ImapAccountData *adata)
Fallback parser for otherwise unhandled messages.
Definition command.c:1037
static int cmd_status(const char *s)
Parse response line for tagged OK/NO/BAD.
Definition command.c:240
#define IMAP_CMD_BUFSIZE
Definition command.c:59
void imap_cmd_finish(struct ImapAccountData *adata)
Attempt to perform cleanup.
Definition command.c:1388
#define IMAP_RES_RESPOND
+
Definition private.h:57
#define MUTT_MEM_REALLOC(pptr, n, type)
Definition memory.h:50
time_t mutt_date_now(void)
Return the number of seconds since the Unix epoch.
Definition date.c:455
int mutt_socket_readln_d(char *buf, size_t buflen, struct Connection *conn, int dbg)
Read a line from a socket.
Definition socket.c:238
#define MUTT_SOCK_LOG_FULL
Definition socket.h:54
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 1274 of file command.c.

1275{
1276 return cmd_status(s) == IMAP_RES_OK;
1277}
+ 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 1285 of file command.c.

1286{
1287 static const char *notrailer = "";
1288 const char *s = adata->buf;
1289
1290 if (!s)
1291 {
1292 mutt_debug(LL_DEBUG2, "not a tagged response\n");
1293 return notrailer;
1294 }
1295
1296 s = imap_next_word((char *) s);
1297 if (!s || (!mutt_istr_startswith(s, "OK") && !mutt_istr_startswith(s, "NO") &&
1298 !mutt_istr_startswith(s, "BAD")))
1299 {
1300 mutt_debug(LL_DEBUG2, "not a command completion: %s\n", adata->buf);
1301 return notrailer;
1302 }
1303
1304 s = imap_next_word((char *) s);
1305 if (!s)
1306 return notrailer;
1307
1308 return s;
1309}
+ 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 1322 of file command.c.

1323{
1324 if (!adata)
1325 return IMAP_EXEC_ERROR;
1326
1327 if (flags & IMAP_CMD_SINGLE)
1328 {
1329 // Process any existing commands
1330 if (adata->nextcmd != adata->lastcmd)
1331 imap_exec(adata, NULL, IMAP_CMD_POLL);
1332 }
1333
1334 int rc = cmd_start(adata, cmdstr, flags);
1335 if (rc < 0)
1336 {
1337 cmd_handle_fatal(adata);
1338 return IMAP_EXEC_FATAL;
1339 }
1340
1341 if (flags & IMAP_CMD_QUEUE)
1342 return IMAP_EXEC_SUCCESS;
1343
1344 const short c_imap_poll_timeout = cs_subset_number(NeoMutt->sub, "imap_poll_timeout");
1345 if ((flags & IMAP_CMD_POLL) && (c_imap_poll_timeout > 0) &&
1346 ((mutt_socket_poll(adata->conn, c_imap_poll_timeout)) == 0))
1347 {
1348 mutt_error(_("Connection to %s timed out"), adata->conn->account.host);
1349 cmd_handle_fatal(adata);
1350 return IMAP_EXEC_FATAL;
1351 }
1352
1353 /* Allow interruptions, particularly useful if there are network problems. */
1355 do
1356 {
1357 rc = imap_cmd_step(adata);
1358 // The queue is empty, so the single command has been processed
1359 if ((flags & IMAP_CMD_SINGLE) && (adata->nextcmd == adata->lastcmd))
1360 break;
1361 } while (rc == IMAP_RES_CONTINUE);
1363
1364 if (rc == IMAP_RES_NO)
1365 return IMAP_EXEC_ERROR;
1366 if (rc != IMAP_RES_OK)
1367 {
1368 if (adata->status != IMAP_FATAL)
1369 return IMAP_EXEC_ERROR;
1370
1371 mutt_debug(LL_DEBUG1, "command failed: %s\n", adata->buf);
1372 return IMAP_EXEC_FATAL;
1373 }
1374
1375 return IMAP_EXEC_SUCCESS;
1376}
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition helpers.c:143
@ IMAP_EXEC_SUCCESS
Imap command executed or queued successfully.
Definition private.h:82
@ IMAP_EXEC_FATAL
Imap connection failure.
Definition private.h:84
#define IMAP_CMD_SINGLE
Run a single command.
Definition private.h:75
void mutt_sig_allow_interrupt(bool allow)
Allow/disallow Ctrl-C (SIGINT)
Definition signal.c:315
int mutt_socket_poll(struct Connection *conn, time_t wait_secs)
Checks whether reads would block.
Definition socket.c:182
+ 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 1388 of file command.c.

1389{
1390 if (!adata)
1391 return;
1392
1393 if (adata->status == IMAP_FATAL)
1394 {
1395 adata->closing = false;
1396 cmd_handle_fatal(adata);
1397 return;
1398 }
1399
1400 if (!(adata->state >= IMAP_SELECTED) || (adata->mailbox && adata->closing))
1401 {
1402 adata->closing = false;
1403 return;
1404 }
1405
1406 adata->closing = false;
1407
1408 struct ImapMboxData *mdata = imap_mdata_get(adata->mailbox);
1409
1410 if (mdata && mdata->reopen & IMAP_REOPEN_ALLOW)
1411 {
1412 // First remove expunged emails from the msn_index
1413 if (mdata->reopen & IMAP_EXPUNGE_PENDING)
1414 {
1415 mutt_debug(LL_DEBUG2, "Expunging mailbox\n");
1416 imap_expunge_mailbox(adata->mailbox, true);
1417 /* Detect whether we've gotten unexpected EXPUNGE messages */
1418 if (!(mdata->reopen & IMAP_EXPUNGE_EXPECTED))
1419 mdata->check_status |= IMAP_EXPUNGE_PENDING;
1421 }
1422
1423 // Then add new emails to it
1424 if (mdata->reopen & IMAP_NEWMAIL_PENDING)
1425 {
1426 const size_t max_msn = imap_msn_highest(&mdata->msn);
1427 if (mdata->new_mail_count > max_msn)
1428 {
1429 if (!(mdata->reopen & IMAP_EXPUNGE_PENDING))
1430 mdata->check_status |= IMAP_NEWMAIL_PENDING;
1431
1432 mutt_debug(LL_DEBUG2, "Fetching new mails from %zd to %u\n",
1433 max_msn + 1, mdata->new_mail_count);
1434 imap_read_headers(adata->mailbox, max_msn + 1, mdata->new_mail_count, false);
1435 }
1436 }
1437
1438 // And to finish inform about MUTT_REOPEN if needed
1439 if (mdata->reopen & IMAP_EXPUNGE_PENDING && !(mdata->reopen & IMAP_EXPUNGE_EXPECTED))
1440 mdata->check_status |= IMAP_EXPUNGE_PENDING;
1441
1442 if (mdata->reopen & IMAP_EXPUNGE_PENDING)
1444 }
1445
1446 adata->status = 0;
1447}
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:1354
#define IMAP_EXPUNGE_EXPECTED
Messages will be expunged from the server.
Definition private.h:65
void imap_expunge_mailbox(struct Mailbox *m, bool resort)
Purge messages from the server.
Definition imap.c:689
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 1455 of file command.c.

1456{
1457 int rc;
1458
1459 if (cmd_start(adata, "IDLE", IMAP_CMD_POLL) < 0)
1460 {
1461 cmd_handle_fatal(adata);
1462 return -1;
1463 }
1464
1465 const short c_imap_poll_timeout = cs_subset_number(NeoMutt->sub, "imap_poll_timeout");
1466 if ((c_imap_poll_timeout > 0) &&
1467 ((mutt_socket_poll(adata->conn, c_imap_poll_timeout)) == 0))
1468 {
1469 mutt_error(_("Connection to %s timed out"), adata->conn->account.host);
1470 cmd_handle_fatal(adata);
1471 return -1;
1472 }
1473
1474 do
1475 {
1476 rc = imap_cmd_step(adata);
1477 } while (rc == IMAP_RES_CONTINUE);
1478
1479 if (rc == IMAP_RES_RESPOND)
1480 {
1481 /* successfully entered IDLE state */
1482 adata->state = IMAP_IDLE;
1483 /* queue automatic exit when next command is issued */
1484 buf_addstr(&adata->cmdbuf, "DONE\r\n");
1485 rc = IMAP_RES_OK;
1486 }
1487 if (rc != IMAP_RES_OK)
1488 {
1489 mutt_debug(LL_DEBUG1, "error starting IDLE\n");
1490 return -1;
1491 }
1492
1493 return 0;
1494}
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition buffer.c:226
+ 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",
"ID",
NULL,
}

Server capabilities strings that we understand.

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

Definition at line 66 of file command.c.

66 {
67 "IMAP4",
68 "IMAP4rev1",
69 "STATUS",
70 "ACL",
71 "NAMESPACE",
72 "AUTH=CRAM-MD5",
73 "AUTH=GSSAPI",
74 "AUTH=ANONYMOUS",
75 "AUTH=OAUTHBEARER",
76 "AUTH=XOAUTH2",
77 "STARTTLS",
78 "LOGINDISABLED",
79 "IDLE",
80 "SASL-IR",
81 "ENABLE",
82 "CONDSTORE",
83 "QRESYNC",
84 "LIST-EXTENDED",
85 "COMPRESS=DEFLATE",
86 "X-GM-EXT-1",
87 "ID",
88 NULL,
89};