NeoMutt  2023-05-17-56-ga67199
Teaching an old dog new tricks
DOXYGEN
command.c File Reference

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

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

Variables

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

Detailed Description

Send/receive commands to/from an IMAP server.

Authors
  • Michael R. Elkins
  • Brandon Long
  • Brendan Cully
  • Richard Russon

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.

Definition in file command.c.

Macro Definition Documentation

◆ IMAP_CMD_BUFSIZE

#define IMAP_CMD_BUFSIZE   512

Definition at line 56 of file command.c.

Function Documentation

◆ cmd_queue_full()

static bool cmd_queue_full ( struct ImapAccountData adata)
static

Is the IMAP command queue full?

Parameters
adataImap Account data
Return values
trueQueue is full

Definition at line 93 of file command.c.

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

108{
109 struct ImapCommand *cmd = NULL;
110
111 if (cmd_queue_full(adata))
112 {
113 mutt_debug(LL_DEBUG3, "IMAP command queue full\n");
114 return NULL;
115 }
116
117 cmd = adata->cmds + adata->nextcmd;
118 adata->nextcmd = (adata->nextcmd + 1) % adata->cmdslots;
119
120 snprintf(cmd->seq, sizeof(cmd->seq), "%c%04u", adata->seqid, adata->seqno++);
121 if (adata->seqno > 9999)
122 adata->seqno = 0;
123
124 cmd->state = IMAP_RES_NEW;
125
126 return cmd;
127}
#define mutt_debug(LEVEL,...)
Definition: logging2.h:87
static bool cmd_queue_full(struct ImapAccountData *adata)
Is the IMAP command queue full?
Definition: command.c:93
#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 139 of file command.c.

140{
141 if (cmd_queue_full(adata))
142 {
143 mutt_debug(LL_DEBUG3, "Draining IMAP command pipeline\n");
144
145 const int rc = imap_exec(adata, NULL, flags & IMAP_CMD_POLL);
146
147 if (rc == IMAP_EXEC_ERROR)
148 return IMAP_RES_BAD;
149 }
150
151 struct ImapCommand *cmd = cmd_new(adata);
152 if (!cmd)
153 return IMAP_RES_BAD;
154
155 if (buf_add_printf(&adata->cmdbuf, "%s %s\r\n", cmd->seq, cmdstr) < 0)
156 return IMAP_RES_BAD;
157
158 return 0;
159}
int buf_add_printf(struct Buffer *buf, const char *fmt,...)
Format a string appending a Buffer.
Definition: buffer.c:216
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:1284
static struct ImapCommand * cmd_new(struct ImapAccountData *adata)
Create and queue a new command control block.
Definition: command.c:107
#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 165 of file command.c.

166{
167 adata->status = IMAP_FATAL;
168
169 if (!adata->mailbox)
170 return;
171
172 struct ImapMboxData *mdata = adata->mailbox->mdata;
173
174 if ((adata->state >= IMAP_SELECTED) && (mdata->reopen & IMAP_REOPEN_ALLOW))
175 {
176 mx_fastclose_mailbox(adata->mailbox, true);
177 mutt_error(_("Mailbox %s@%s closed"), adata->conn->account.user,
178 adata->conn->account.host);
179 }
180
182 if (!adata->recovering)
183 {
184 adata->recovering = true;
185 if (imap_login(adata))
187 adata->recovering = false;
188 }
189}
#define mutt_error(...)
Definition: logging2.h:90
@ 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:840
int imap_login(struct ImapAccountData *adata)
Open an IMAP connection.
Definition: imap.c:1762
#define _(a)
Definition: message.h:28
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
Definition: mutt_logging.c:73
void mx_fastclose_mailbox(struct Mailbox *m, bool keep_account)
Free up memory associated with the Mailbox.
Definition: mx.c:433
char user[128]
Username.
Definition: connaccount.h:56
char host[128]
Server to login to.
Definition: connaccount.h:54
struct ConnAccount account
Account details: username, password, etc.
Definition: connection.h:50
unsigned char state
ImapState, e.g. IMAP_AUTHENTICATED.
Definition: adata.h:44
struct Mailbox * mailbox
Current selected mailbox.
Definition: adata.h:76
unsigned char status
ImapFlags, e.g. IMAP_FATAL.
Definition: adata.h:45
bool recovering
Definition: adata.h:42
struct Connection * conn
Connection to IMAP server.
Definition: adata.h:41
IMAP-specific Mailbox data -.
Definition: mdata.h:39
void * mdata
Driver specific data.
Definition: mailbox.h:132
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ cmd_start()

static int cmd_start ( struct ImapAccountData adata,
const char *  cmdstr,
ImapCmdFlags  flags 
)
static

Start a new IMAP command.

Parameters
adataImap Account data
cmdstrCommand string
flagsCommand flags, see ImapCmdFlags
Return values
0Success
<0Failure, e.g. IMAP_RES_BAD

Definition at line 199 of file command.c.

200{
201 int rc;
202
203 if (adata->status == IMAP_FATAL)
204 {
205 cmd_handle_fatal(adata);
206 return -1;
207 }
208
209 if (cmdstr && ((rc = cmd_queue(adata, cmdstr, flags)) < 0))
210 return rc;
211
212 if (flags & IMAP_CMD_QUEUE)
213 return 0;
214
215 if (buf_is_empty(&adata->cmdbuf))
216 return IMAP_RES_BAD;
217
218 rc = mutt_socket_send_d(adata->conn, adata->cmdbuf.data,
220 buf_reset(&adata->cmdbuf);
221
222 /* unidle when command queue is flushed */
223 if (adata->state == IMAP_IDLE)
224 adata->state = IMAP_SELECTED;
225
226 return (rc < 0) ? IMAP_RES_BAD : 0;
227}
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:88
bool buf_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:303
static int cmd_queue(struct ImapAccountData *adata, const char *cmdstr, ImapCmdFlags flags)
Add a IMAP command to the queue.
Definition: command.c:139
static void cmd_handle_fatal(struct ImapAccountData *adata)
When ImapAccountData is in fatal state, do what we can.
Definition: command.c:165
@ 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:60
char * data
Pointer to data.
Definition: buffer.h:35
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ cmd_status()

static int cmd_status ( const char *  s)
static

Parse response line for tagged OK/NO/BAD.

Parameters
sStatus string from server
Return values
0Success
<0Failure, e.g. IMAP_RES_BAD

Definition at line 235 of file command.c.

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

256{
257 unsigned int exp_msn;
258 struct Email *e = NULL;
259
260 mutt_debug(LL_DEBUG2, "Handling EXPUNGE\n");
261
262 struct ImapMboxData *mdata = adata->mailbox->mdata;
263
264 if (!mutt_str_atoui(s, &exp_msn) || (exp_msn < 1) ||
265 (exp_msn > imap_msn_highest(&mdata->msn)))
266 {
267 return;
268 }
269
270 e = imap_msn_get(&mdata->msn, exp_msn - 1);
271 if (e)
272 {
273 /* imap_expunge_mailbox() will rewrite e->index.
274 * It needs to resort using SORT_ORDER anyway, so setting to INT_MAX
275 * makes the code simpler and possibly more efficient. */
276 e->index = INT_MAX;
277 imap_edata_get(e)->msn = 0;
278 }
279
280 /* decrement seqno of those above. */
281 const size_t max_msn = imap_msn_highest(&mdata->msn);
282 for (unsigned int cur = exp_msn; cur < max_msn; cur++)
283 {
284 e = imap_msn_get(&mdata->msn, cur);
285 if (e)
286 imap_edata_get(e)->msn--;
287 imap_msn_set(&mdata->msn, cur - 1, e);
288 }
289 imap_msn_shrink(&mdata->msn, 1);
290
291 mdata->reopen |= IMAP_EXPUNGE_PENDING;
292}
const char * mutt_str_atoui(const char *str, unsigned int *dst)
Convert ASCII string to an unsigned integer.
Definition: atoi.c:203
struct ImapEmailData * imap_edata_get(struct Email *e)
Get the private data for this Email.
Definition: edata.c:65
#define IMAP_EXPUNGE_PENDING
Messages on the server have been expunged.
Definition: private.h:66
@ LL_DEBUG2
Log at debug level 2.
Definition: logging2.h:44
void imap_msn_set(struct MSN *msn, size_t idx, struct Email *e)
Cache an Email into a given position.
Definition: msn.c:92
size_t imap_msn_shrink(struct MSN *msn, size_t num)
Remove a number of entries from the end of the cache.
Definition: msn.c:103
size_t imap_msn_highest(const struct MSN *msn)
Return the highest MSN in use.
Definition: msn.c:69
struct Email * imap_msn_get(const struct MSN *msn, size_t idx)
Return the Email associated with an msn.
Definition: msn.c:80
The envelope/body of an email.
Definition: email.h:37
int index
The absolute (unsorted) message number.
Definition: email.h:109
unsigned int msn
Message Sequence Number.
Definition: edata.h:45
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ cmd_parse_vanished()

static void cmd_parse_vanished ( struct ImapAccountData adata,
char *  s 
)
static

Parse vanished command.

Parameters
adataImap Account data
sString containing MSN of message to expunge

Handle VANISHED (RFC7162), which is like expunge, but passes a seqset of UIDs. An optional (EARLIER) argument specifies not to decrement subsequent MSNs.

Definition at line 302 of file command.c.

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

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

◆ cmd_parse_capability()

static void cmd_parse_capability ( struct ImapAccountData adata,
char *  s 
)
static

Set capability bits according to CAPABILITY response.

Parameters
adataImap Account data
sCommand string with capabilities

Definition at line 540 of file command.c.

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

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

669{
670 char buf[256] = { 0 };
671 char quoted_name[256] = { 0 };
672 struct Buffer err;
673 struct Url url = { 0 };
674 struct ImapList list = { 0 };
675
676 if (adata->cmdresult)
677 {
678 /* caller will handle response itself */
679 cmd_parse_list(adata, s);
680 return;
681 }
682
683 const bool c_imap_check_subscribed = cs_subset_bool(NeoMutt->sub, "imap_check_subscribed");
684 if (!c_imap_check_subscribed)
685 return;
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 mutt_str_copy(buf, "mailboxes \"", sizeof(buf));
697 mutt_account_tourl(&adata->conn->account, &url);
698 /* escape \ and " */
699 imap_quote_string(quoted_name, sizeof(quoted_name), list.name, true);
700 url.path = quoted_name + 1;
701 url.path[strlen(url.path) - 1] = '\0';
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_tostring(&url, buf + 11, sizeof(buf) - 11, U_NO_FLAGS);
706 mutt_str_cat(buf, sizeof(buf), "\"");
707 buf_init(&err);
708 err.dsize = 256;
709 err.data = mutt_mem_malloc(err.dsize);
710 if (parse_rc_line(buf, &err))
711 mutt_debug(LL_DEBUG1, "Error adding subscribed mailbox: %s\n", err.data);
712 FREE(&err.data);
713}
struct Buffer * buf_init(struct Buffer *buf)
Initialise a new Buffer.
Definition: buffer.c:55
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:317
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
static void cmd_parse_list(struct ImapAccountData *adata, char *s)
Parse a server LIST command (list mailboxes)
Definition: command.c:573
void imap_quote_string(char *dest, size_t dlen, const char *src, bool quote_backtick)
Quote string according to IMAP rules.
Definition: util.c:832
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:798
size_t mutt_str_copy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:653
void mutt_account_tourl(struct ConnAccount *cac, struct Url *url)
Fill URL with info from account.
Definition: mutt_account.c:79
enum CommandResult parse_rc_line(const char *line, struct Buffer *err)
Parse a line of user config.
Definition: rc.c:108
String manipulation buffer.
Definition: buffer.h:34
Container for Accounts, Notifications.
Definition: neomutt.h:37
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
A parsed URL proto://user:password@host:port/path?a=1&b=2
Definition: url.h:69
char * user
Username.
Definition: url.h:71
char * path
Path.
Definition: url.h:75
int url_tostring(struct Url *url, char *dest, size_t len, uint8_t flags)
Output the URL string for a given Url object.
Definition: url.c:422
#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:117
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ find_mailbox()

static struct Mailbox * find_mailbox ( struct ImapAccountData adata,
const char *  name 
)
static

Find a Mailbox by its name.

Parameters
adataImap Account data
nameMailbox to find
Return values
ptrMailbox

Definition at line 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:41
struct Account * account
Parent Account.
Definition: adata.h:78
char * name
Mailbox name.
Definition: mdata.h:40
List of Mailboxes.
Definition: mailbox.h:152
struct Mailbox * mailbox
Mailbox in the list.
Definition: mailbox.h:153
+ 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 if (((errno == ERANGE) && (ulcount == ULONG_MAX)) || ((unsigned int) ulcount != ulcount))
868 {
869 mutt_debug(LL_DEBUG1, "Error parsing STATUS number\n");
870 return;
871 }
872 const unsigned int count = (unsigned int) ulcount;
873
874 if (mutt_str_startswith(s, "MESSAGES"))
875 mdata->messages = count;
876 else if (mutt_str_startswith(s, "RECENT"))
877 mdata->recent = count;
878 else if (mutt_str_startswith(s, "UIDNEXT"))
879 mdata->uid_next = count;
880 else if (mutt_str_startswith(s, "UIDVALIDITY"))
881 mdata->uidvalidity = count;
882 else if (mutt_str_startswith(s, "UNSEEN"))
883 mdata->unseen = count;
884
885 s = value;
886 if ((s[0] != '\0') && (*s != ')'))
887 s = imap_next_word(s);
888 }
889 mutt_debug(LL_DEBUG3, "%s (UIDVALIDITY: %u, UIDNEXT: %u) %d messages, %d recent, %d unseen\n",
890 mdata->name, mdata->uidvalidity, mdata->uid_next, mdata->messages,
891 mdata->recent, mdata->unseen);
892
893 mutt_debug(LL_DEBUG3, "Running default STATUS handler\n");
894
895 mutt_debug(LL_DEBUG3, "Found %s in mailbox list (OV: %u ON: %u U: %d)\n",
896 mailbox, olduv, oldun, mdata->unseen);
897
898 bool new_mail = false;
899 const bool c_mail_check_recent = cs_subset_bool(NeoMutt->sub, "mail_check_recent");
900 if (c_mail_check_recent)
901 {
902 if ((olduv != 0) && (olduv == mdata->uidvalidity))
903 {
904 if (oldun < mdata->uid_next)
905 new_mail = (mdata->unseen > 0);
906 }
907 else if ((olduv == 0) && (oldun == 0))
908 {
909 /* first check per session, use recent. might need a flag for this. */
910 new_mail = (mdata->recent > 0);
911 }
912 else
913 {
914 new_mail = (mdata->unseen > 0);
915 }
916 }
917 else
918 {
919 new_mail = (mdata->unseen > 0);
920 }
921
922 m->has_new = new_mail;
923 m->msg_count = mdata->messages;
924 m->msg_unread = mdata->unseen;
925
926 // force back to keep detecting new mail until the mailbox is opened
927 if (m->has_new)
928 mdata->uid_next = oldun;
929
930 struct EventMailbox ev_m = { m };
932}
static struct Mailbox * find_mailbox(struct ImapAccountData *adata, const char *name)
Find a Mailbox by its name.
Definition: command.c:788
@ NT_MAILBOX_CHANGE
Mailbox has been changed.
Definition: mailbox.h:171
bool notify_send(struct Notify *notify, enum NotifyType event_type, int event_subtype, void *event_data)
Send out a notification message.
Definition: notify.c:171
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:228
@ NT_MAILBOX
Mailbox has changed, NotifyMailbox, EventMailbox.
Definition: notify_type.h:49
An Event that happened to a Mailbox.
Definition: mailbox.h:185
unsigned int uid_next
Definition: mdata.h:51
A mailbox.
Definition: mailbox.h:79
bool has_new
Mailbox has new mail.
Definition: mailbox.h:85
int msg_count
Total number of messages.
Definition: mailbox.h:88
struct Notify * notify
Notifications: NotifyMailbox, EventMailbox.
Definition: mailbox.h:143
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 939 of file command.c.

940{
941 mutt_debug(LL_DEBUG2, "Handling ENABLED\n");
942
943 while ((s = imap_next_word((char *) s)) && (*s != '\0'))
944 {
945 if (mutt_istr_startswith(s, "UTF8=ACCEPT") || mutt_istr_startswith(s, "UTF8=ONLY"))
946 {
947 adata->unicode = true;
948 }
949 if (mutt_istr_startswith(s, "QRESYNC"))
950 adata->qresync = true;
951 }
952}
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 959 of file command.c.

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

1000{
1001 char *s = imap_next_word(adata->buf);
1002 char *pn = imap_next_word(s);
1003
1004 const bool c_imap_server_noise = cs_subset_bool(NeoMutt->sub, "imap_server_noise");
1005 if ((adata->state >= IMAP_SELECTED) && isdigit((unsigned char) *s))
1006 {
1007 /* pn vs. s: need initial seqno */
1008 pn = s;
1009 s = imap_next_word(s);
1010
1011 /* EXISTS, EXPUNGE, FETCH are always related to the SELECTED mailbox */
1012 if (mutt_istr_startswith(s, "EXISTS"))
1013 cmd_parse_exists(adata, pn);
1014 else if (mutt_istr_startswith(s, "EXPUNGE"))
1015 cmd_parse_expunge(adata, pn);
1016 else if (mutt_istr_startswith(s, "FETCH"))
1017 cmd_parse_fetch(adata, pn);
1018 }
1019 else if ((adata->state >= IMAP_SELECTED) && mutt_istr_startswith(s, "VANISHED"))
1020 {
1021 cmd_parse_vanished(adata, pn);
1022 }
1023 else if (mutt_istr_startswith(s, "CAPABILITY"))
1024 {
1025 cmd_parse_capability(adata, s);
1026 }
1027 else if (mutt_istr_startswith(s, "OK [CAPABILITY"))
1028 {
1029 cmd_parse_capability(adata, pn);
1030 }
1031 else if (mutt_istr_startswith(pn, "OK [CAPABILITY"))
1032 {
1034 }
1035 else if (mutt_istr_startswith(s, "LIST"))
1036 {
1037 cmd_parse_list(adata, s);
1038 }
1039 else if (mutt_istr_startswith(s, "LSUB"))
1040 {
1041 cmd_parse_lsub(adata, s);
1042 }
1043 else if (mutt_istr_startswith(s, "MYRIGHTS"))
1044 {
1045 cmd_parse_myrights(adata, s);
1046 }
1047 else if (mutt_istr_startswith(s, "SEARCH"))
1048 {
1049 cmd_parse_search(adata, s);
1050 }
1051 else if (mutt_istr_startswith(s, "STATUS"))
1052 {
1053 cmd_parse_status(adata, s);
1054 }
1055 else if (mutt_istr_startswith(s, "ENABLED"))
1056 {
1057 cmd_parse_enabled(adata, s);
1058 }
1059 else if (mutt_istr_startswith(s, "BYE"))
1060 {
1061 mutt_debug(LL_DEBUG2, "Handling BYE\n");
1062
1063 /* check if we're logging out */
1064 if (adata->status == IMAP_BYE)
1065 return 0;
1066
1067 /* server shut down our connection */
1068 s += 3;
1069 SKIPWS(s);
1070 mutt_error("%s", s);
1071 cmd_handle_fatal(adata);
1072
1073 return -1;
1074 }
1075 else if (c_imap_server_noise && mutt_istr_startswith(s, "NO"))
1076 {
1077 mutt_debug(LL_DEBUG2, "Handling untagged NO\n");
1078
1079 /* Display the warning message from the server */
1080 mutt_error("%s", s + 2);
1081 }
1082
1083 return 0;
1084}
static void cmd_parse_capability(struct ImapAccountData *adata, char *s)
Set capability bits according to CAPABILITY response.
Definition: command.c:540
static void cmd_parse_lsub(struct ImapAccountData *adata, char *s)
Parse a server LSUB (list subscribed mailboxes)
Definition: command.c:668
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:959
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:255
static void cmd_parse_enabled(struct ImapAccountData *adata, const char *s)
Record what the server has enabled.
Definition: command.c:939
static void cmd_parse_vanished(struct ImapAccountData *adata, char *s)
Parse vanished command.
Definition: command.c:302
static void cmd_parse_fetch(struct ImapAccountData *adata, char *s)
Load fetch response into ImapAccountData.
Definition: command.c:396
@ IMAP_BYE
Logged out from server.
Definition: private.h:96
void cmd_parse_search(struct ImapAccountData *adata, const char *s)
Store SEARCH response for later use.
Definition: search.c:259
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_cmd_start()

int imap_cmd_start ( struct ImapAccountData adata,
const char *  cmdstr 
)

Given an IMAP command, send it to the server.

Parameters
adataImap Account data
cmdstrCommand string to send
Return values
0Success
<0Failure, e.g. IMAP_RES_BAD

If cmdstr is NULL, sends queued commands.

Definition at line 1095 of file command.c.

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

1110{
1111 if (!adata)
1112 return -1;
1113
1114 size_t len = 0;
1115 int c;
1116 int rc;
1117 int stillrunning = 0;
1118 struct ImapCommand *cmd = NULL;
1119
1120 if (adata->status == IMAP_FATAL)
1121 {
1122 cmd_handle_fatal(adata);
1123 return IMAP_RES_BAD;
1124 }
1125
1126 /* read into buffer, expanding buffer as necessary until we have a full
1127 * line */
1128 do
1129 {
1130 if (len == adata->blen)
1131 {
1132 mutt_mem_realloc(&adata->buf, adata->blen + IMAP_CMD_BUFSIZE);
1133 adata->blen = adata->blen + IMAP_CMD_BUFSIZE;
1134 mutt_debug(LL_DEBUG3, "grew buffer to %lu bytes\n", adata->blen);
1135 }
1136
1137 /* back up over '\0' */
1138 if (len)
1139 len--;
1140 c = mutt_socket_readln_d(adata->buf + len, adata->blen - len, adata->conn, MUTT_SOCK_LOG_FULL);
1141 if (c <= 0)
1142 {
1143 mutt_debug(LL_DEBUG1, "Error reading server response\n");
1144 cmd_handle_fatal(adata);
1145 return IMAP_RES_BAD;
1146 }
1147
1148 len += c;
1149 }
1150 /* if we've read all the way to the end of the buffer, we haven't read a
1151 * full line (mutt_socket_readln strips the \r, so we always have at least
1152 * one character free when we've read a full line) */
1153 while (len == adata->blen);
1154
1155 /* don't let one large string make cmd->buf hog memory forever */
1156 if ((adata->blen > IMAP_CMD_BUFSIZE) && (len <= IMAP_CMD_BUFSIZE))
1157 {
1159 adata->blen = IMAP_CMD_BUFSIZE;
1160 mutt_debug(LL_DEBUG3, "shrank buffer to %lu bytes\n", adata->blen);
1161 }
1162
1163 adata->lastread = mutt_date_now();
1164
1165 /* handle untagged messages. The caller still gets its shot afterwards. */
1166 if ((mutt_str_startswith(adata->buf, "* ") ||
1167 mutt_str_startswith(imap_next_word(adata->buf), "OK [")) &&
1168 cmd_handle_untagged(adata))
1169 {
1170 return IMAP_RES_BAD;
1171 }
1172
1173 /* server demands a continuation response from us */
1174 if (adata->buf[0] == '+')
1175 return IMAP_RES_RESPOND;
1176
1177 /* Look for tagged command completions.
1178 *
1179 * Some response handlers can end up recursively calling
1180 * imap_cmd_step() and end up handling all tagged command
1181 * completions.
1182 * (e.g. FETCH->set_flag->set_header_color->~h pattern match.)
1183 *
1184 * Other callers don't even create an adata->cmds entry.
1185 *
1186 * For both these cases, we default to returning OK */
1187 rc = IMAP_RES_OK;
1188 c = adata->lastcmd;
1189 do
1190 {
1191 cmd = &adata->cmds[c];
1192 if (cmd->state == IMAP_RES_NEW)
1193 {
1194 if (mutt_str_startswith(adata->buf, cmd->seq))
1195 {
1196 if (!stillrunning)
1197 {
1198 /* first command in queue has finished - move queue pointer up */
1199 adata->lastcmd = (adata->lastcmd + 1) % adata->cmdslots;
1200 }
1201 cmd->state = cmd_status(adata->buf);
1202 rc = cmd->state;
1203 if (cmd->state == IMAP_RES_NO || cmd->state == IMAP_RES_BAD)
1204 {
1205 mutt_message(_("IMAP command failed: %s"), adata->buf);
1206 }
1207 }
1208 else
1209 {
1210 stillrunning++;
1211 }
1212 }
1213
1214 c = (c + 1) % adata->cmdslots;
1215 } while (c != adata->nextcmd);
1216
1217 if (stillrunning)
1218 {
1219 rc = IMAP_RES_CONTINUE;
1220 }
1221 else
1222 {
1223 mutt_debug(LL_DEBUG3, "IMAP queue drained\n");
1224 imap_cmd_finish(adata);
1225 }
1226
1227 return rc;
1228}
#define mutt_message(...)
Definition: logging2.h:89
static int cmd_handle_untagged(struct ImapAccountData *adata)
Fallback parser for otherwise unhandled messages.
Definition: command.c:999
static int cmd_status(const char *s)
Parse response line for tagged OK/NO/BAD.
Definition: command.c:235
#define IMAP_CMD_BUFSIZE
Definition: command.c:56
void imap_cmd_finish(struct ImapAccountData *adata)
Attempt to perform cleanup.
Definition: command.c:1349
#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:446
int mutt_socket_readln_d(char *buf, size_t buflen, struct Connection *conn, int dbg)
Read a line from a socket.
Definition: socket.c:252
#define MUTT_SOCK_LOG_FULL
Definition: socket.h:56
time_t lastread
last time we read a command for the server
Definition: adata.h:58
size_t blen
Definition: adata.h:60
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_code()

bool imap_code ( const char *  s)

Was the command successful.

Parameters
sIMAP command status
Return values
1Command result was OK
0NO or BAD

Definition at line 1236 of file command.c.

1237{
1238 return cmd_status(s) == IMAP_RES_OK;
1239}
+ 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 1247 of file command.c.

1248{
1249 static const char *notrailer = "";
1250 const char *s = adata->buf;
1251
1252 if (!s)
1253 {
1254 mutt_debug(LL_DEBUG2, "not a tagged response\n");
1255 return notrailer;
1256 }
1257
1258 s = imap_next_word((char *) s);
1259 if (!s || (!mutt_istr_startswith(s, "OK") && !mutt_istr_startswith(s, "NO") &&
1260 !mutt_istr_startswith(s, "BAD")))
1261 {
1262 mutt_debug(LL_DEBUG2, "not a command completion: %s\n", adata->buf);
1263 return notrailer;
1264 }
1265
1266 s = imap_next_word((char *) s);
1267 if (!s)
1268 return notrailer;
1269
1270 return s;
1271}
+ 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 1284 of file command.c.

1285{
1286 int rc;
1287
1288 if (flags & IMAP_CMD_SINGLE)
1289 {
1290 // Process any existing commands
1291 if (adata->nextcmd != adata->lastcmd)
1292 imap_exec(adata, NULL, IMAP_CMD_POLL);
1293 }
1294
1295 rc = cmd_start(adata, cmdstr, flags);
1296 if (rc < 0)
1297 {
1298 cmd_handle_fatal(adata);
1299 return IMAP_EXEC_FATAL;
1300 }
1301
1302 if (flags & IMAP_CMD_QUEUE)
1303 return IMAP_EXEC_SUCCESS;
1304
1305 const short c_imap_poll_timeout = cs_subset_number(NeoMutt->sub, "imap_poll_timeout");
1306 if ((flags & IMAP_CMD_POLL) && (c_imap_poll_timeout > 0) &&
1307 ((mutt_socket_poll(adata->conn, c_imap_poll_timeout)) == 0))
1308 {
1309 mutt_error(_("Connection to %s timed out"), adata->conn->account.host);
1310 cmd_handle_fatal(adata);
1311 return IMAP_EXEC_FATAL;
1312 }
1313
1314 /* Allow interruptions, particularly useful if there are network problems. */
1316 do
1317 {
1318 rc = imap_cmd_step(adata);
1319 // The queue is empty, so the single command has been processed
1320 if ((flags & IMAP_CMD_SINGLE) && (adata->nextcmd == adata->lastcmd))
1321 break;
1322 } while (rc == IMAP_RES_CONTINUE);
1324
1325 if (rc == IMAP_RES_NO)
1326 return IMAP_EXEC_ERROR;
1327 if (rc != IMAP_RES_OK)
1328 {
1329 if (adata->status != IMAP_FATAL)
1330 return IMAP_EXEC_ERROR;
1331
1332 mutt_debug(LL_DEBUG1, "command failed: %s\n", adata->buf);
1333 return IMAP_EXEC_FATAL;
1334 }
1335
1336 return IMAP_EXEC_SUCCESS;
1337}
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition: helpers.c:169
@ IMAP_EXEC_SUCCESS
Imap command executed or queued successfully.
Definition: private.h: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:252
int mutt_socket_poll(struct Connection *conn, time_t wait_secs)
Checks whether reads would block.
Definition: socket.c:196
+ 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 1349 of file command.c.

1350{
1351 if (!adata)
1352 return;
1353
1354 if (adata->status == IMAP_FATAL)
1355 {
1356 adata->closing = false;
1357 cmd_handle_fatal(adata);
1358 return;
1359 }
1360
1361 if (!(adata->state >= IMAP_SELECTED) || (adata->mailbox && adata->closing))
1362 {
1363 adata->closing = false;
1364 return;
1365 }
1366
1367 adata->closing = false;
1368
1369 struct ImapMboxData *mdata = imap_mdata_get(adata->mailbox);
1370
1371 if (mdata && mdata->reopen & IMAP_REOPEN_ALLOW)
1372 {
1373 // First remove expunged emails from the msn_index
1374 if (mdata->reopen & IMAP_EXPUNGE_PENDING)
1375 {
1376 mutt_debug(LL_DEBUG2, "Expunging mailbox\n");
1377 imap_expunge_mailbox(adata->mailbox, true);
1378 /* Detect whether we've gotten unexpected EXPUNGE messages */
1379 if (!(mdata->reopen & IMAP_EXPUNGE_EXPECTED))
1380 mdata->check_status |= IMAP_EXPUNGE_PENDING;
1382 }
1383
1384 // Then add new emails to it
1385 if (mdata->reopen & IMAP_NEWMAIL_PENDING)
1386 {
1387 const size_t max_msn = imap_msn_highest(&mdata->msn);
1388 if (mdata->new_mail_count > max_msn)
1389 {
1390 if (!(mdata->reopen & IMAP_EXPUNGE_PENDING))
1391 mdata->check_status |= IMAP_NEWMAIL_PENDING;
1392
1393 mutt_debug(LL_DEBUG2, "Fetching new mails from %ld to %u\n",
1394 max_msn + 1, mdata->new_mail_count);
1395 imap_read_headers(adata->mailbox, max_msn + 1, mdata->new_mail_count, false);
1396 }
1397 }
1398
1399 // And to finish inform about MUTT_REOPEN if needed
1400 if (mdata->reopen & IMAP_EXPUNGE_PENDING && !(mdata->reopen & IMAP_EXPUNGE_EXPECTED))
1401 mdata->check_status |= IMAP_EXPUNGE_PENDING;
1402
1403 if (mdata->reopen & IMAP_EXPUNGE_PENDING)
1405 }
1406
1407 adata->status = 0;
1408}
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:1331
#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:660
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 1416 of file command.c.

1417{
1418 int rc;
1419
1420 if (cmd_start(adata, "IDLE", IMAP_CMD_POLL) < 0)
1421 {
1422 cmd_handle_fatal(adata);
1423 return -1;
1424 }
1425
1426 const short c_imap_poll_timeout = cs_subset_number(NeoMutt->sub, "imap_poll_timeout");
1427 if ((c_imap_poll_timeout > 0) &&
1428 ((mutt_socket_poll(adata->conn, c_imap_poll_timeout)) == 0))
1429 {
1430 mutt_error(_("Connection to %s timed out"), adata->conn->account.host);
1431 cmd_handle_fatal(adata);
1432 return -1;
1433 }
1434
1435 do
1436 {
1437 rc = imap_cmd_step(adata);
1438 } while (rc == IMAP_RES_CONTINUE);
1439
1440 if (rc == IMAP_RES_RESPOND)
1441 {
1442 /* successfully entered IDLE state */
1443 adata->state = IMAP_IDLE;
1444 /* queue automatic exit when next command is issued */
1445 buf_addstr(&adata->cmdbuf, "DONE\r\n");
1446 rc = IMAP_RES_OK;
1447 }
1448 if (rc != IMAP_RES_OK)
1449 {
1450 mutt_debug(LL_DEBUG1, "error starting IDLE\n");
1451 return -1;
1452 }
1453
1454 return 0;
1455}
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:238
+ 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 63 of file command.c.