NeoMutt  2024-04-16-36-g75b6fb
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 <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 "commands.h"
#include "edata.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?
 
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 61 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 98 of file command.c.

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

113{
114 struct ImapCommand *cmd = NULL;
115
116 if (cmd_queue_full(adata))
117 {
118 mutt_debug(LL_DEBUG3, "IMAP command queue full\n");
119 return NULL;
120 }
121
122 cmd = adata->cmds + adata->nextcmd;
123 adata->nextcmd = (adata->nextcmd + 1) % adata->cmdslots;
124
125 snprintf(cmd->seq, sizeof(cmd->seq), "%c%04u", adata->seqid, adata->seqno++);
126 if (adata->seqno > 9999)
127 adata->seqno = 0;
128
129 cmd->state = IMAP_RES_NEW;
130
131 return cmd;
132}
#define mutt_debug(LEVEL,...)
Definition: logging2.h:89
static bool cmd_queue_full(struct ImapAccountData *adata)
Is the IMAP command queue full?
Definition: command.c:98
#define IMAP_RES_NEW
ImapCommand.state additions.
Definition: private.h:58
@ LL_DEBUG3
Log at debug level 3.
Definition: logging2.h:45
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 144 of file command.c.

145{
146 if (cmd_queue_full(adata))
147 {
148 mutt_debug(LL_DEBUG3, "Draining IMAP command pipeline\n");
149
150 const int rc = imap_exec(adata, NULL, flags & IMAP_CMD_POLL);
151
152 if (rc == IMAP_EXEC_ERROR)
153 return IMAP_RES_BAD;
154 }
155
156 struct ImapCommand *cmd = cmd_new(adata);
157 if (!cmd)
158 return IMAP_RES_BAD;
159
160 if (buf_add_printf(&adata->cmdbuf, "%s %s\r\n", cmd->seq, cmdstr) < 0)
161 return IMAP_RES_BAD;
162
163 return 0;
164}
int buf_add_printf(struct Buffer *buf, const char *fmt,...)
Format a string appending a Buffer.
Definition: buffer.c:203
int imap_exec(struct ImapAccountData *adata, const char *cmdstr, ImapCmdFlags flags)
Execute a command and wait for the response from the server.
Definition: command.c:1304
static struct ImapCommand * cmd_new(struct ImapAccountData *adata)
Create and queue a new command control block.
Definition: command.c:112
#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 170 of file command.c.

171{
172 adata->status = IMAP_FATAL;
173
174 if (!adata->mailbox)
175 return;
176
177 struct ImapMboxData *mdata = adata->mailbox->mdata;
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:92
@ 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:849
int imap_login(struct ImapAccountData *adata)
Open an IMAP connection.
Definition: imap.c:1777
#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:412
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:75
bool buf_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:290
static int cmd_queue(struct ImapAccountData *adata, const char *cmdstr, ImapCmdFlags flags)
Add a IMAP command to the queue.
Definition: command.c:144
static void cmd_handle_fatal(struct ImapAccountData *adata)
When ImapAccountData is in fatal state, do what we can.
Definition: command.c:170
@ 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:785
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition: string.c:242
+ 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;
263 struct Email *e = NULL;
264
265 mutt_debug(LL_DEBUG2, "Handling EXPUNGE\n");
266
267 struct ImapMboxData *mdata = adata->mailbox->mdata;
268
269 if (!mutt_str_atoui(s, &exp_msn) || (exp_msn < 1) ||
270 (exp_msn > imap_msn_highest(&mdata->msn)))
271 {
272 return;
273 }
274
275 e = imap_msn_get(&mdata->msn, exp_msn - 1);
276 if (e)
277 {
278 /* imap_expunge_mailbox() will rewrite e->index.
279 * It needs to resort using SORT_ORDER anyway, so setting to INT_MAX
280 * makes the code simpler and possibly more efficient. */
281 e->index = INT_MAX;
282 imap_edata_get(e)->msn = 0;
283 }
284
285 /* decrement seqno of those above. */
286 const size_t max_msn = imap_msn_highest(&mdata->msn);
287 for (unsigned int cur = exp_msn; cur < max_msn; cur++)
288 {
289 e = imap_msn_get(&mdata->msn, cur);
290 if (e)
291 imap_edata_get(e)->msn--;
292 imap_msn_set(&mdata->msn, cur - 1, e);
293 }
294 imap_msn_shrink(&mdata->msn, 1);
295
296 mdata->reopen |= IMAP_EXPUNGE_PENDING;
297}
const char * mutt_str_atoui(const char *str, unsigned int *dst)
Convert ASCII string to an unsigned integer.
Definition: atoi.c:214
struct ImapEmailData * imap_edata_get(struct Email *e)
Get the private data for this Email.
Definition: edata.c:67
#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:44
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:104
size_t imap_msn_highest(const struct MSNArray *msn)
Return the highest MSN in use.
Definition: msn.c:70
struct Email * imap_msn_get(const struct MSNArray *msn, size_t idx)
Return the Email associated with an msn.
Definition: msn.c:81
void imap_msn_set(struct MSNArray *msn, size_t idx, struct Email *e)
Cache an Email into a given position.
Definition: msn.c:93
The envelope/body of an email.
Definition: email.h:39
int index
The absolute (unsorted) message number.
Definition: email.h:113
unsigned int msn
Message Sequence Number.
Definition: edata.h:46
+ 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 307 of file command.c.

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

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

546{
547 mutt_debug(LL_DEBUG3, "Handling CAPABILITY\n");
548
549 s = imap_next_word(s);
550 char *bracket = strchr(s, ']');
551 if (bracket)
552 *bracket = '\0';
553 FREE(&adata->capstr);
554 adata->capstr = mutt_str_dup(s);
555 adata->capabilities = 0;
556
557 while (*s)
558 {
559 for (size_t i = 0; Capabilities[i]; i++)
560 {
561 size_t len = mutt_istr_startswith(s, Capabilities[i]);
562 if (len != 0 && ((s[len] == '\0') || isspace(s[len])))
563 {
564 adata->capabilities |= (1 << i);
565 mutt_debug(LL_DEBUG3, " Found capability \"%s\": %zu\n", Capabilities[i], i);
566 break;
567 }
568 }
569 s = imap_next_word(s);
570 }
571}
static const char *const Capabilities[]
Server capabilities strings that we understand.
Definition: command.c:68
#define FREE(x)
Definition: memory.h:45
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:253
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 578 of file command.c.

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

673{
674 if (adata->cmdresult)
675 {
676 /* caller will handle response itself */
677 cmd_parse_list(adata, s);
678 return;
679 }
680
681 const bool c_imap_check_subscribed = cs_subset_bool(NeoMutt->sub, "imap_check_subscribed");
682 if (!c_imap_check_subscribed)
683 return;
684
685 struct ImapList list = { 0 };
686
687 adata->cmdresult = &list;
688 cmd_parse_list(adata, s);
689 adata->cmdresult = NULL;
690 /* noselect is for a gmail quirk */
691 if (!list.name || list.noselect)
692 return;
693
694 mutt_debug(LL_DEBUG3, "Subscribing to %s\n", list.name);
695
696 struct Buffer *buf = buf_pool_get();
697 struct Buffer *err = buf_pool_get();
698 struct Url url = { 0 };
699
700 mutt_account_tourl(&adata->conn->account, &url);
701 url.path = list.name;
702
703 const char *const c_imap_user = cs_subset_string(NeoMutt->sub, "imap_user");
704 if (mutt_str_equal(url.user, c_imap_user))
705 url.user = NULL;
706 url_tobuffer(&url, buf, U_NO_FLAGS);
707
708 if (!mailbox_add_simple(buf_string(buf), err))
709 mutt_debug(LL_DEBUG1, "Error adding subscribed mailbox: %s\n", buf_string(err));
710
711 buf_pool_release(&buf);
712 buf_pool_release(&err);
713}
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:726
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:292
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:48
static void cmd_parse_list(struct ImapAccountData *adata, char *s)
Parse a server LIST command (list mailboxes)
Definition: command.c:578
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:654
void mutt_account_tourl(struct ConnAccount *cac, struct Url *url)
Fill URL with info from account.
Definition: mutt_account.c:80
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:81
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition: pool.c:94
String manipulation buffer.
Definition: buffer.h:36
Container for Accounts, Notifications.
Definition: neomutt.h:41
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:45
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:358
#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 720 of file command.c.

721{
722 mutt_debug(LL_DEBUG2, "Handling MYRIGHTS\n");
723
724 s = imap_next_word((char *) s);
725 s = imap_next_word((char *) s);
726
727 /* zero out current rights set */
728 adata->mailbox->rights = 0;
729
730 while (*s && !isspace((unsigned char) *s))
731 {
732 switch (*s)
733 {
734 case 'a':
735 adata->mailbox->rights |= MUTT_ACL_ADMIN;
736 break;
737 case 'e':
739 break;
740 case 'i':
741 adata->mailbox->rights |= MUTT_ACL_INSERT;
742 break;
743 case 'k':
744 adata->mailbox->rights |= MUTT_ACL_CREATE;
745 break;
746 case 'l':
747 adata->mailbox->rights |= MUTT_ACL_LOOKUP;
748 break;
749 case 'p':
750 adata->mailbox->rights |= MUTT_ACL_POST;
751 break;
752 case 'r':
753 adata->mailbox->rights |= MUTT_ACL_READ;
754 break;
755 case 's':
756 adata->mailbox->rights |= MUTT_ACL_SEEN;
757 break;
758 case 't':
759 adata->mailbox->rights |= MUTT_ACL_DELETE;
760 break;
761 case 'w':
762 adata->mailbox->rights |= MUTT_ACL_WRITE;
763 break;
764 case 'x':
765 adata->mailbox->rights |= MUTT_ACL_DELMX;
766 break;
767
768 /* obsolete rights */
769 case 'c':
771 break;
772 case 'd':
774 break;
775 default:
776 mutt_debug(LL_DEBUG1, "Unknown right: %c\n", *s);
777 }
778 s++;
779 }
780}
#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 788 of file command.c.

789{
790 if (!adata || !adata->account || !name)
791 return NULL;
792
793 struct MailboxNode *np = NULL;
794 STAILQ_FOREACH(np, &adata->account->mailboxes, entries)
795 {
797 if (mutt_str_equal(name, mdata->name))
798 return np->mailbox;
799 }
800
801 return NULL;
802}
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:352
struct MailboxList mailboxes
List of Mailboxes.
Definition: account.h:40
struct Account * account
Parent Account.
Definition: adata.h:78
char * name
Mailbox name.
Definition: mdata.h:41
List of Mailboxes.
Definition: mailbox.h:166
struct Mailbox * mailbox
Mailbox in the list.
Definition: mailbox.h:167
+ 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 812 of file command.c.

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

960{
961 mutt_debug(LL_DEBUG2, "Handling ENABLED\n");
962
963 while ((s = imap_next_word((char *) s)) && (*s != '\0'))
964 {
965 if (mutt_istr_startswith(s, "UTF8=ACCEPT") || mutt_istr_startswith(s, "UTF8=ONLY"))
966 {
967 adata->unicode = true;
968 }
969 if (mutt_istr_startswith(s, "QRESYNC"))
970 adata->qresync = true;
971 }
972}
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 979 of file command.c.

980{
981 unsigned int count = 0;
982 mutt_debug(LL_DEBUG2, "Handling EXISTS\n");
983
984 if (!mutt_str_atoui(pn, &count))
985 {
986 mutt_debug(LL_DEBUG1, "Malformed EXISTS: '%s'\n", pn);
987 return;
988 }
989
990 struct ImapMboxData *mdata = adata->mailbox->mdata;
991
992 /* new mail arrived */
993 if (count < imap_msn_highest(&mdata->msn))
994 {
995 /* Notes 6.0.3 has a tendency to report fewer messages exist than
996 * it should. */
997 mutt_debug(LL_DEBUG1, "Message count is out of sync\n");
998 }
999 else if (count == imap_msn_highest(&mdata->msn))
1000 {
1001 /* at least the InterChange server sends EXISTS messages freely,
1002 * even when there is no new mail */
1003 mutt_debug(LL_DEBUG3, "superfluous EXISTS message\n");
1004 }
1005 else
1006 {
1007 mutt_debug(LL_DEBUG2, "New mail in %s - %d messages total\n", mdata->name, count);
1008 mdata->reopen |= IMAP_NEWMAIL_PENDING;
1009 mdata->new_mail_count = count;
1010 }
1011}
#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 1019 of file command.c.

1020{
1021 char *s = imap_next_word(adata->buf);
1022 char *pn = imap_next_word(s);
1023
1024 const bool c_imap_server_noise = cs_subset_bool(NeoMutt->sub, "imap_server_noise");
1025 if ((adata->state >= IMAP_SELECTED) && isdigit((unsigned char) *s))
1026 {
1027 /* pn vs. s: need initial seqno */
1028 pn = s;
1029 s = imap_next_word(s);
1030
1031 /* EXISTS, EXPUNGE, FETCH are always related to the SELECTED mailbox */
1032 if (mutt_istr_startswith(s, "EXISTS"))
1033 cmd_parse_exists(adata, pn);
1034 else if (mutt_istr_startswith(s, "EXPUNGE"))
1035 cmd_parse_expunge(adata, pn);
1036 else if (mutt_istr_startswith(s, "FETCH"))
1037 cmd_parse_fetch(adata, pn);
1038 }
1039 else if ((adata->state >= IMAP_SELECTED) && mutt_istr_startswith(s, "VANISHED"))
1040 {
1041 cmd_parse_vanished(adata, pn);
1042 }
1043 else if (mutt_istr_startswith(s, "CAPABILITY"))
1044 {
1045 cmd_parse_capability(adata, s);
1046 }
1047 else if (mutt_istr_startswith(s, "OK [CAPABILITY"))
1048 {
1049 cmd_parse_capability(adata, pn);
1050 }
1051 else if (mutt_istr_startswith(pn, "OK [CAPABILITY"))
1052 {
1054 }
1055 else if (mutt_istr_startswith(s, "LIST"))
1056 {
1057 cmd_parse_list(adata, s);
1058 }
1059 else if (mutt_istr_startswith(s, "LSUB"))
1060 {
1061 cmd_parse_lsub(adata, s);
1062 }
1063 else if (mutt_istr_startswith(s, "MYRIGHTS"))
1064 {
1065 cmd_parse_myrights(adata, s);
1066 }
1067 else if (mutt_istr_startswith(s, "SEARCH"))
1068 {
1069 cmd_parse_search(adata, s);
1070 }
1071 else if (mutt_istr_startswith(s, "STATUS"))
1072 {
1073 cmd_parse_status(adata, s);
1074 }
1075 else if (mutt_istr_startswith(s, "ENABLED"))
1076 {
1077 cmd_parse_enabled(adata, s);
1078 }
1079 else if (mutt_istr_startswith(s, "BYE"))
1080 {
1081 mutt_debug(LL_DEBUG2, "Handling BYE\n");
1082
1083 /* check if we're logging out */
1084 if (adata->status == IMAP_BYE)
1085 return 0;
1086
1087 /* server shut down our connection */
1088 s += 3;
1089 SKIPWS(s);
1090 mutt_error("%s", s);
1091 cmd_handle_fatal(adata);
1092
1093 return -1;
1094 }
1095 else if (c_imap_server_noise && mutt_istr_startswith(s, "NO"))
1096 {
1097 mutt_debug(LL_DEBUG2, "Handling untagged NO\n");
1098
1099 /* Display the warning message from the server */
1100 mutt_error("%s", s + 2);
1101 }
1102
1103 return 0;
1104}
static void cmd_parse_capability(struct ImapAccountData *adata, char *s)
Set capability bits according to CAPABILITY response.
Definition: command.c:545
static void cmd_parse_lsub(struct ImapAccountData *adata, char *s)
Parse a server LSUB (list subscribed mailboxes)
Definition: command.c:672
static void cmd_parse_status(struct ImapAccountData *adata, char *s)
Parse status from server.
Definition: command.c:812
static void cmd_parse_exists(struct ImapAccountData *adata, const char *pn)
Parse EXISTS message from serer.
Definition: command.c:979
static void cmd_parse_myrights(struct ImapAccountData *adata, const char *s)
Set rights bits according to MYRIGHTS response.
Definition: command.c:720
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:959
static void cmd_parse_vanished(struct ImapAccountData *adata, char *s)
Parse vanished command.
Definition: command.c:307
static void cmd_parse_fetch(struct ImapAccountData *adata, char *s)
Load fetch response into ImapAccountData.
Definition: command.c:401
@ 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:256
+ 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 1115 of file command.c.

1116{
1117 return cmd_start(adata, cmdstr, IMAP_CMD_NO_FLAGS);
1118}
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 1129 of file command.c.

1130{
1131 if (!adata)
1132 return -1;
1133
1134 size_t len = 0;
1135 int c;
1136 int rc;
1137 int stillrunning = 0;
1138 struct ImapCommand *cmd = NULL;
1139
1140 if (adata->status == IMAP_FATAL)
1141 {
1142 cmd_handle_fatal(adata);
1143 return IMAP_RES_BAD;
1144 }
1145
1146 /* read into buffer, expanding buffer as necessary until we have a full
1147 * line */
1148 do
1149 {
1150 if (len == adata->blen)
1151 {
1152 mutt_mem_realloc(&adata->buf, adata->blen + IMAP_CMD_BUFSIZE);
1153 adata->blen = adata->blen + IMAP_CMD_BUFSIZE;
1154 mutt_debug(LL_DEBUG3, "grew buffer to %zu bytes\n", adata->blen);
1155 }
1156
1157 /* back up over '\0' */
1158 if (len)
1159 len--;
1160 c = mutt_socket_readln_d(adata->buf + len, adata->blen - len, adata->conn, MUTT_SOCK_LOG_FULL);
1161 if (c <= 0)
1162 {
1163 mutt_debug(LL_DEBUG1, "Error reading server response\n");
1164 cmd_handle_fatal(adata);
1165 return IMAP_RES_BAD;
1166 }
1167
1168 len += c;
1169 }
1170 /* if we've read all the way to the end of the buffer, we haven't read a
1171 * full line (mutt_socket_readln strips the \r, so we always have at least
1172 * one character free when we've read a full line) */
1173 while (len == adata->blen);
1174
1175 /* don't let one large string make cmd->buf hog memory forever */
1176 if ((adata->blen > IMAP_CMD_BUFSIZE) && (len <= IMAP_CMD_BUFSIZE))
1177 {
1179 adata->blen = IMAP_CMD_BUFSIZE;
1180 mutt_debug(LL_DEBUG3, "shrank buffer to %zu bytes\n", adata->blen);
1181 }
1182
1183 adata->lastread = mutt_date_now();
1184
1185 /* handle untagged messages. The caller still gets its shot afterwards. */
1186 if ((mutt_str_startswith(adata->buf, "* ") ||
1187 mutt_str_startswith(imap_next_word(adata->buf), "OK [")) &&
1188 cmd_handle_untagged(adata))
1189 {
1190 return IMAP_RES_BAD;
1191 }
1192
1193 /* server demands a continuation response from us */
1194 if (adata->buf[0] == '+')
1195 return IMAP_RES_RESPOND;
1196
1197 /* Look for tagged command completions.
1198 *
1199 * Some response handlers can end up recursively calling
1200 * imap_cmd_step() and end up handling all tagged command
1201 * completions.
1202 * (e.g. FETCH->set_flag->set_header_color->~h pattern match.)
1203 *
1204 * Other callers don't even create an adata->cmds entry.
1205 *
1206 * For both these cases, we default to returning OK */
1207 rc = IMAP_RES_OK;
1208 c = adata->lastcmd;
1209 do
1210 {
1211 cmd = &adata->cmds[c];
1212 if (cmd->state == IMAP_RES_NEW)
1213 {
1214 if (mutt_str_startswith(adata->buf, cmd->seq))
1215 {
1216 if (!stillrunning)
1217 {
1218 /* first command in queue has finished - move queue pointer up */
1219 adata->lastcmd = (adata->lastcmd + 1) % adata->cmdslots;
1220 }
1221 cmd->state = cmd_status(adata->buf);
1222 rc = cmd->state;
1223 if (cmd->state == IMAP_RES_NO || cmd->state == IMAP_RES_BAD)
1224 {
1225 mutt_message(_("IMAP command failed: %s"), adata->buf);
1226 }
1227 }
1228 else
1229 {
1230 stillrunning++;
1231 }
1232 }
1233
1234 c = (c + 1) % adata->cmdslots;
1235 } while (c != adata->nextcmd);
1236
1237 if (stillrunning)
1238 {
1239 rc = IMAP_RES_CONTINUE;
1240 }
1241 else
1242 {
1243 mutt_debug(LL_DEBUG3, "IMAP queue drained\n");
1244 imap_cmd_finish(adata);
1245 }
1246
1247 return rc;
1248}
#define mutt_message(...)
Definition: logging2.h:91
static int cmd_handle_untagged(struct ImapAccountData *adata)
Fallback parser for otherwise unhandled messages.
Definition: command.c:1019
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:61
void imap_cmd_finish(struct ImapAccountData *adata)
Attempt to perform cleanup.
Definition: command.c:1369
#define IMAP_RES_RESPOND
+
Definition: private.h:57
void mutt_mem_realloc(void *ptr, size_t size)
Resize a block of memory on the heap.
Definition: memory.c:114
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 1256 of file command.c.

1257{
1258 return cmd_status(s) == IMAP_RES_OK;
1259}
+ 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 1267 of file command.c.

1268{
1269 static const char *notrailer = "";
1270 const char *s = adata->buf;
1271
1272 if (!s)
1273 {
1274 mutt_debug(LL_DEBUG2, "not a tagged response\n");
1275 return notrailer;
1276 }
1277
1278 s = imap_next_word((char *) s);
1279 if (!s || (!mutt_istr_startswith(s, "OK") && !mutt_istr_startswith(s, "NO") &&
1280 !mutt_istr_startswith(s, "BAD")))
1281 {
1282 mutt_debug(LL_DEBUG2, "not a command completion: %s\n", adata->buf);
1283 return notrailer;
1284 }
1285
1286 s = imap_next_word((char *) s);
1287 if (!s)
1288 return notrailer;
1289
1290 return s;
1291}
+ 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 1304 of file command.c.

1305{
1306 int rc;
1307
1308 if (flags & IMAP_CMD_SINGLE)
1309 {
1310 // Process any existing commands
1311 if (adata->nextcmd != adata->lastcmd)
1312 imap_exec(adata, NULL, IMAP_CMD_POLL);
1313 }
1314
1315 rc = cmd_start(adata, cmdstr, flags);
1316 if (rc < 0)
1317 {
1318 cmd_handle_fatal(adata);
1319 return IMAP_EXEC_FATAL;
1320 }
1321
1322 if (flags & IMAP_CMD_QUEUE)
1323 return IMAP_EXEC_SUCCESS;
1324
1325 const short c_imap_poll_timeout = cs_subset_number(NeoMutt->sub, "imap_poll_timeout");
1326 if ((flags & IMAP_CMD_POLL) && (c_imap_poll_timeout > 0) &&
1327 ((mutt_socket_poll(adata->conn, c_imap_poll_timeout)) == 0))
1328 {
1329 mutt_error(_("Connection to %s timed out"), adata->conn->account.host);
1330 cmd_handle_fatal(adata);
1331 return IMAP_EXEC_FATAL;
1332 }
1333
1334 /* Allow interruptions, particularly useful if there are network problems. */
1336 do
1337 {
1338 rc = imap_cmd_step(adata);
1339 // The queue is empty, so the single command has been processed
1340 if ((flags & IMAP_CMD_SINGLE) && (adata->nextcmd == adata->lastcmd))
1341 break;
1342 } while (rc == IMAP_RES_CONTINUE);
1344
1345 if (rc == IMAP_RES_NO)
1346 return IMAP_EXEC_ERROR;
1347 if (rc != IMAP_RES_OK)
1348 {
1349 if (adata->status != IMAP_FATAL)
1350 return IMAP_EXEC_ERROR;
1351
1352 mutt_debug(LL_DEBUG1, "command failed: %s\n", adata->buf);
1353 return IMAP_EXEC_FATAL;
1354 }
1355
1356 return IMAP_EXEC_SUCCESS;
1357}
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition: helpers.c:144
@ 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:254
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 1369 of file command.c.

1370{
1371 if (!adata)
1372 return;
1373
1374 if (adata->status == IMAP_FATAL)
1375 {
1376 adata->closing = false;
1377 cmd_handle_fatal(adata);
1378 return;
1379 }
1380
1381 if (!(adata->state >= IMAP_SELECTED) || (adata->mailbox && adata->closing))
1382 {
1383 adata->closing = false;
1384 return;
1385 }
1386
1387 adata->closing = false;
1388
1389 struct ImapMboxData *mdata = imap_mdata_get(adata->mailbox);
1390
1391 if (mdata && mdata->reopen & IMAP_REOPEN_ALLOW)
1392 {
1393 // First remove expunged emails from the msn_index
1394 if (mdata->reopen & IMAP_EXPUNGE_PENDING)
1395 {
1396 mutt_debug(LL_DEBUG2, "Expunging mailbox\n");
1397 imap_expunge_mailbox(adata->mailbox, true);
1398 /* Detect whether we've gotten unexpected EXPUNGE messages */
1399 if (!(mdata->reopen & IMAP_EXPUNGE_EXPECTED))
1400 mdata->check_status |= IMAP_EXPUNGE_PENDING;
1402 }
1403
1404 // Then add new emails to it
1405 if (mdata->reopen & IMAP_NEWMAIL_PENDING)
1406 {
1407 const size_t max_msn = imap_msn_highest(&mdata->msn);
1408 if (mdata->new_mail_count > max_msn)
1409 {
1410 if (!(mdata->reopen & IMAP_EXPUNGE_PENDING))
1411 mdata->check_status |= IMAP_NEWMAIL_PENDING;
1412
1413 mutt_debug(LL_DEBUG2, "Fetching new mails from %zd to %u\n",
1414 max_msn + 1, mdata->new_mail_count);
1415 imap_read_headers(adata->mailbox, max_msn + 1, mdata->new_mail_count, false);
1416 }
1417 }
1418
1419 // And to finish inform about MUTT_REOPEN if needed
1420 if (mdata->reopen & IMAP_EXPUNGE_PENDING && !(mdata->reopen & IMAP_EXPUNGE_EXPECTED))
1421 mdata->check_status |= IMAP_EXPUNGE_PENDING;
1422
1423 if (mdata->reopen & IMAP_EXPUNGE_PENDING)
1425 }
1426
1427 adata->status = 0;
1428}
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:1341
#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:669
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 1436 of file command.c.

1437{
1438 int rc;
1439
1440 if (cmd_start(adata, "IDLE", IMAP_CMD_POLL) < 0)
1441 {
1442 cmd_handle_fatal(adata);
1443 return -1;
1444 }
1445
1446 const short c_imap_poll_timeout = cs_subset_number(NeoMutt->sub, "imap_poll_timeout");
1447 if ((c_imap_poll_timeout > 0) &&
1448 ((mutt_socket_poll(adata->conn, c_imap_poll_timeout)) == 0))
1449 {
1450 mutt_error(_("Connection to %s timed out"), adata->conn->account.host);
1451 cmd_handle_fatal(adata);
1452 return -1;
1453 }
1454
1455 do
1456 {
1457 rc = imap_cmd_step(adata);
1458 } while (rc == IMAP_RES_CONTINUE);
1459
1460 if (rc == IMAP_RES_RESPOND)
1461 {
1462 /* successfully entered IDLE state */
1463 adata->state = IMAP_IDLE;
1464 /* queue automatic exit when next command is issued */
1465 buf_addstr(&adata->cmdbuf, "DONE\r\n");
1466 rc = IMAP_RES_OK;
1467 }
1468 if (rc != IMAP_RES_OK)
1469 {
1470 mutt_debug(LL_DEBUG1, "error starting IDLE\n");
1471 return -1;
1472 }
1473
1474 return 0;
1475}
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:225
+ Here is the call graph for this function:
+ 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 68 of file command.c.