NeoMutt  2024-11-14-138-ge5ca67
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_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 60 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 97 of file command.c.

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

112{
113 struct ImapCommand *cmd = NULL;
114
115 if (cmd_queue_full(adata))
116 {
117 mutt_debug(LL_DEBUG3, "IMAP command queue full\n");
118 return NULL;
119 }
120
121 cmd = adata->cmds + adata->nextcmd;
122 adata->nextcmd = (adata->nextcmd + 1) % adata->cmdslots;
123
124 snprintf(cmd->seq, sizeof(cmd->seq), "%c%04u", adata->seqid, adata->seqno++);
125 if (adata->seqno > 9999)
126 adata->seqno = 0;
127
128 cmd->state = IMAP_RES_NEW;
129
130 return cmd;
131}
#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:97
#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 143 of file command.c.

144{
145 if (cmd_queue_full(adata))
146 {
147 mutt_debug(LL_DEBUG3, "Draining IMAP command pipeline\n");
148
149 const int rc = imap_exec(adata, NULL, flags & IMAP_CMD_POLL);
150
151 if (rc == IMAP_EXEC_ERROR)
152 return IMAP_RES_BAD;
153 }
154
155 struct ImapCommand *cmd = cmd_new(adata);
156 if (!cmd)
157 return IMAP_RES_BAD;
158
159 if (buf_add_printf(&adata->cmdbuf, "%s %s\r\n", cmd->seq, cmdstr) < 0)
160 return IMAP_RES_BAD;
161
162 return 0;
163}
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:1303
static struct ImapCommand * cmd_new(struct ImapAccountData *adata)
Create and queue a new command control block.
Definition: command.c:111
#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 169 of file command.c.

170{
171 adata->status = IMAP_FATAL;
172
173 if (!adata->mailbox)
174 return;
175
176 struct ImapMboxData *mdata = adata->mailbox->mdata;
177
178 if ((adata->state >= IMAP_SELECTED) && (mdata->reopen & IMAP_REOPEN_ALLOW))
179 {
180 mx_fastclose_mailbox(adata->mailbox, true);
181 mutt_error(_("Mailbox %s@%s closed"), adata->conn->account.user,
182 adata->conn->account.host);
183 }
184
186 if (!adata->recovering)
187 {
188 adata->recovering = true;
189 if (imap_login(adata))
191 adata->recovering = false;
192 }
193}
#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:848
int imap_login(struct ImapAccountData *adata)
Open an IMAP connection.
Definition: imap.c:1776
#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: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 203 of file command.c.

204{
205 int rc;
206
207 if (adata->status == IMAP_FATAL)
208 {
209 cmd_handle_fatal(adata);
210 return -1;
211 }
212
213 if (cmdstr && ((rc = cmd_queue(adata, cmdstr, flags)) < 0))
214 return rc;
215
216 if (flags & IMAP_CMD_QUEUE)
217 return 0;
218
219 if (buf_is_empty(&adata->cmdbuf))
220 return IMAP_RES_BAD;
221
222 rc = mutt_socket_send_d(adata->conn, adata->cmdbuf.data,
224 buf_reset(&adata->cmdbuf);
225
226 /* unidle when command queue is flushed */
227 if (adata->state == IMAP_IDLE)
228 adata->state = IMAP_SELECTED;
229
230 return (rc < 0) ? IMAP_RES_BAD : 0;
231}
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:143
static void cmd_handle_fatal(struct ImapAccountData *adata)
When ImapAccountData is in fatal state, do what we can.
Definition: command.c:169
@ 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 239 of file command.c.

240{
241 s = imap_next_word((char *) s);
242
243 if (mutt_istr_startswith(s, "OK"))
244 return IMAP_RES_OK;
245 if (mutt_istr_startswith(s, "NO"))
246 return IMAP_RES_NO;
247
248 return IMAP_RES_BAD;
249}
#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: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 259 of file command.c.

260{
261 unsigned int exp_msn;
262 struct Email *e = NULL;
263
264 mutt_debug(LL_DEBUG2, "Handling EXPUNGE\n");
265
266 struct ImapMboxData *mdata = adata->mailbox->mdata;
267
268 if (!mutt_str_atoui(s, &exp_msn) || (exp_msn < 1) ||
269 (exp_msn > imap_msn_highest(&mdata->msn)))
270 {
271 return;
272 }
273
274 e = imap_msn_get(&mdata->msn, exp_msn - 1);
275 if (e)
276 {
277 /* imap_expunge_mailbox() will rewrite e->index.
278 * It needs to resort using EMAIL_SORT_UNSORTED anyway, so setting to INT_MAX
279 * makes the code simpler and possibly more efficient. */
280 e->index = INT_MAX;
281 imap_edata_get(e)->msn = 0;
282 }
283
284 /* decrement seqno of those above. */
285 const size_t max_msn = imap_msn_highest(&mdata->msn);
286 for (unsigned int cur = exp_msn; cur < max_msn; cur++)
287 {
288 e = imap_msn_get(&mdata->msn, cur);
289 if (e)
290 imap_edata_get(e)->msn--;
291 imap_msn_set(&mdata->msn, cur - 1, e);
292 }
293 imap_msn_shrink(&mdata->msn, 1);
294
295 mdata->reopen |= IMAP_EXPUNGE_PENDING;
296}
const char * mutt_str_atoui(const char *str, unsigned int *dst)
Convert ASCII string to an unsigned integer.
Definition: atoi.c:218
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:106
size_t imap_msn_highest(const struct MSNArray *msn)
Return the highest MSN in use.
Definition: msn.c:72
struct Email * imap_msn_get(const struct MSNArray *msn, size_t idx)
Return the Email associated with an msn.
Definition: msn.c:83
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
int index
The absolute (unsorted) message number.
Definition: email.h:110
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 306 of file command.c.

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

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

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

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

672{
673 if (adata->cmdresult)
674 {
675 /* caller will handle response itself */
676 cmd_parse_list(adata, s);
677 return;
678 }
679
680 const bool c_imap_check_subscribed = cs_subset_bool(NeoMutt->sub, "imap_check_subscribed");
681 if (!c_imap_check_subscribed)
682 return;
683
684 struct ImapList list = { 0 };
685
686 adata->cmdresult = &list;
687 cmd_parse_list(adata, s);
688 adata->cmdresult = NULL;
689 /* noselect is for a gmail quirk */
690 if (!list.name || list.noselect)
691 return;
692
693 mutt_debug(LL_DEBUG3, "Subscribing to %s\n", list.name);
694
695 struct Buffer *buf = buf_pool_get();
696 struct Buffer *err = buf_pool_get();
697 struct Url url = { 0 };
698
699 account_to_url(&adata->conn->account, &url);
700 url.path = list.name;
701
702 const char *const c_imap_user = cs_subset_string(NeoMutt->sub, "imap_user");
703 if (mutt_str_equal(url.user, c_imap_user))
704 url.user = NULL;
705 url_tobuffer(&url, buf, U_NO_FLAGS);
706
707 if (!mailbox_add_simple(buf_string(buf), err))
708 mutt_debug(LL_DEBUG1, "Error adding subscribed mailbox: %s\n", buf_string(err));
709
710 buf_pool_release(&buf);
711 buf_pool_release(&err);
712}
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:727
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:577
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.
Definition: mutt_account.c:80
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:42
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:46
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 719 of file command.c.

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

788{
789 if (!adata || !adata->account || !name)
790 return NULL;
791
792 struct MailboxNode *np = NULL;
793 STAILQ_FOREACH(np, &adata->account->mailboxes, entries)
794 {
796 if (mutt_str_equal(name, mdata->name))
797 return np->mailbox;
798 }
799
800 return NULL;
801}
#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 811 of file command.c.

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

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

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

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

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

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

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

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

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

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

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