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

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

#include "config.h"
#include <errno.h>
#include <limits.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.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 "commands/lib.h"
#include "adata.h"
#include "edata.h"
#include "mdata.h"
#include "msn.h"
#include "mx.h"
+ Include dependency graph for command.c:

Go to the source code of this file.

Macros

#define IMAP_CMD_BUFSIZE   512
 Default buffer size for IMAP commands.
 
#define IMAP_MAX_RETRIES   5
 Maximum number of reconnection attempts before giving up.
 
#define IMAP_MAX_BACKOFF   30
 Maximum backoff delay in seconds between reconnection attempts.
 
#define IMAP_CONN_STALE_THRESHOLD   300
 Threshold in seconds after which to consider a connection potentially stale.
 

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
  • Thomas Adam
  • Thomas Klausner

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

Default buffer size for IMAP commands.

Definition at line 63 of file command.c.

◆ IMAP_MAX_RETRIES

#define IMAP_MAX_RETRIES   5

Maximum number of reconnection attempts before giving up.

Definition at line 66 of file command.c.

◆ IMAP_MAX_BACKOFF

#define IMAP_MAX_BACKOFF   30

Maximum backoff delay in seconds between reconnection attempts.

Definition at line 69 of file command.c.

◆ IMAP_CONN_STALE_THRESHOLD

#define IMAP_CONN_STALE_THRESHOLD   300

Threshold in seconds after which to consider a connection potentially stale.

Definition at line 72 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 109 of file command.c.

110{
111 if (((adata->nextcmd + 1) % adata->cmdslots) == adata->lastcmd)
112 return true;
113
114 return false;
115}
int lastcmd
Last command in the queue.
Definition adata.h:76
int nextcmd
Next command to be sent.
Definition adata.h:75
int cmdslots
Size of the command queue.
Definition adata.h:74
+ 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 123 of file command.c.

124{
125 struct ImapCommand *cmd = NULL;
126
127 if (cmd_queue_full(adata))
128 {
129 mutt_debug(LL_DEBUG3, "IMAP command queue full\n");
130 return NULL;
131 }
132
133 cmd = adata->cmds + adata->nextcmd;
134 adata->nextcmd = (adata->nextcmd + 1) % adata->cmdslots;
135
136 snprintf(cmd->seq, sizeof(cmd->seq), "%c%04u", adata->seqid, adata->seqno++);
137 if (adata->seqno > 9999)
138 adata->seqno = 0;
139
140 cmd->state = IMAP_RES_NEW;
141
142 return cmd;
143}
#define mutt_debug(LEVEL,...)
Definition logging2.h:91
static bool cmd_queue_full(struct ImapAccountData *adata)
Is the IMAP command queue full?
Definition command.c:109
#define IMAP_RES_NEW
ImapCommand.state additions.
Definition private.h:57
@ LL_DEBUG3
Log at debug level 3.
Definition logging2.h:47
unsigned char seqid
tag sequence prefix
Definition adata.h:57
struct ImapCommand * cmds
Queue of commands for the server.
Definition adata.h:73
unsigned int seqno
tag sequence number, e.g. '{seqid}0001'
Definition adata.h:58
IMAP command structure.
Definition private.h:176
int state
Command state, e.g. IMAP_RES_NEW.
Definition private.h:178
char seq[SEQ_LEN+1]
Command tag, e.g. 'a0001'.
Definition private.h:177
+ 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 155 of file command.c.

156{
157 if (cmd_queue_full(adata))
158 {
159 mutt_debug(LL_DEBUG3, "Draining IMAP command pipeline\n");
160
161 const int rc = imap_exec(adata, NULL, IMAP_CMD_POLL);
162
163 if (rc == IMAP_EXEC_ERROR)
164 return IMAP_RES_BAD;
165 }
166
167 struct ImapCommand *cmd = cmd_new(adata);
168 if (!cmd)
169 return IMAP_RES_BAD;
170
171 if (buf_add_printf(&adata->cmdbuf, "%s %s\r\n", cmd->seq, cmdstr) < 0)
172 return IMAP_RES_BAD;
173
174 return 0;
175}
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:1420
static struct ImapCommand * cmd_new(struct ImapAccountData *adata)
Create and queue a new command control block.
Definition command.c:123
@ IMAP_EXEC_ERROR
Imap command failure.
Definition private.h:96
#define IMAP_RES_BAD
<tag> BAD ...
Definition private.h:53
@ IMAP_CMD_POLL
Poll the tcp connection before running the imap command.
Definition private.h:85
struct Buffer cmdbuf
Command queue.
Definition adata.h:77
+ 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

Attempts to reconnect using exponential backoff (1s, 2s, 4s, 8s... max 30s). Gives up after IMAP_MAX_RETRIES consecutive failures.

Definition at line 184 of file command.c.

185{
186 adata->status = IMAP_FATAL;
187
188 mutt_debug(LL_DEBUG1, "state=%d, status=%d, recovering=%d, retries=%d\n",
189 adata->state, adata->status, adata->recovering, adata->retry_count);
190 mutt_debug(LL_DEBUG1, "Connection: fd=%d, host=%s\n",
191 adata->conn ? adata->conn->fd : -1,
192 adata->conn ? adata->conn->account.host : "NULL");
193
194 if (!adata->mailbox)
195 return;
196
197 mutt_debug(LL_DEBUG1, "adata->mailbox=%p, opened=%d\n",
198 (void *) adata->mailbox, adata->mailbox->opened);
199
200 struct ImapMboxData *mdata = adata->mailbox->mdata;
201 if (!mdata)
202 return;
203
204 bool was_selected = (adata->state >= IMAP_SELECTED);
205
207 if (!adata->recovering)
208 {
209 adata->recovering = true;
210
211 /* Exponential backoff: 1s, 2s, 4s, 8s, 16s, max 30s */
212 if (adata->retry_count > 0)
213 {
214 unsigned int delay = (adata->retry_count < 32) ?
215 (1U << (adata->retry_count - 1)) :
217 if (delay > IMAP_MAX_BACKOFF)
218 delay = IMAP_MAX_BACKOFF;
219 mutt_message(_("Connection lost. Retrying in %u seconds..."), delay);
220 sleep(delay);
221 }
222
223 mutt_debug(LL_DEBUG1, "Attempting to reconnect to %s (attempt %d)\n",
224 adata->conn ? adata->conn->account.host : "NULL", adata->retry_count + 1);
225
226 if (imap_login(adata))
227 {
228 mutt_message(_("Reconnected to %s"), adata->conn->account.host);
229 adata->retry_count = 0; // Reset on success
230
231 if (was_selected && (imap_reopen_mailbox(adata) == 0))
232 {
233 mutt_message(_("Reopened mailbox on %s"), adata->conn->account.host);
234 }
235 else if (was_selected)
236 {
237 mutt_error(_("Reconnected to %s but failed to reopen mailbox"),
238 adata->conn->account.host);
239 }
240 }
241 else
242 {
243 adata->retry_count++;
244 mutt_debug(LL_DEBUG1, "Reconnection failed (attempt %d/%d)\n",
246
247 if (adata->retry_count >= IMAP_MAX_RETRIES)
248 {
249 mutt_error(_("Failed to reconnect to %s after %d attempts"),
250 adata->conn->account.host, adata->retry_count);
251 adata->retry_count = 0; // Reset for future attempts
252 }
253 else
254 {
255 mutt_error(_("Reconnection to %s failed. Will retry automatically."),
256 adata->conn->account.host);
257 }
258 }
259 adata->recovering = false;
260 }
261}
#define mutt_error(...)
Definition logging2.h:94
#define mutt_message(...)
Definition logging2.h:93
#define IMAP_MAX_RETRIES
Maximum number of reconnection attempts before giving up.
Definition command.c:66
#define IMAP_MAX_BACKOFF
Maximum backoff delay in seconds between reconnection attempts.
Definition command.c:69
@ IMAP_SELECTED
Mailbox is selected.
Definition private.h:121
@ IMAP_FATAL
Unrecoverable error occurred.
Definition private.h:108
void imap_close_connection(struct ImapAccountData *adata)
Close an IMAP connection.
Definition imap.c:1029
int imap_reopen_mailbox(struct ImapAccountData *adata)
Re-SELECT the current mailbox after reconnecting.
Definition imap.c:2261
int imap_login(struct ImapAccountData *adata)
Open an IMAP connection.
Definition imap.c:2013
@ LL_DEBUG1
Log at debug level 1.
Definition logging2.h:45
#define _(a)
Definition message.h:28
char host[128]
Server to login to.
Definition connaccount.h:60
struct ConnAccount account
Account details: username, password, etc.
Definition connection.h:49
int fd
Socket file descriptor.
Definition connection.h:53
unsigned char state
ImapState, e.g. IMAP_AUTHENTICATED.
Definition adata.h:45
struct Mailbox * mailbox
Current selected mailbox.
Definition adata.h:80
unsigned char status
ImapFlags, e.g. IMAP_FATAL.
Definition adata.h:46
unsigned char retry_count
Number of consecutive reconnection attempts.
Definition adata.h:68
bool recovering
Recovering after a fatal error.
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:134
int opened
Number of times mailbox is opened.
Definition mailbox.h:130
+ 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 271 of file command.c.

272{
273 int rc;
274
275 if (adata->status == IMAP_FATAL)
276 {
277 cmd_handle_fatal(adata);
278 return -1;
279 }
280
281 if (cmdstr && ((rc = cmd_queue(adata, cmdstr, flags)) < 0))
282 return rc;
283
284 if (flags & IMAP_CMD_QUEUE)
285 return 0;
286
287 if (buf_is_empty(&adata->cmdbuf))
288 return IMAP_RES_BAD;
289
290 rc = mutt_socket_send_d(adata->conn, adata->cmdbuf.data,
292 buf_reset(&adata->cmdbuf);
293
294 /* unidle when command queue is flushed */
295 if (adata->state == IMAP_IDLE)
296 adata->state = IMAP_SELECTED;
297
298 return (rc < 0) ? IMAP_RES_BAD : 0;
299}
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:155
static void cmd_handle_fatal(struct ImapAccountData *adata)
When ImapAccountData is in fatal state, do what we can.
Definition command.c:184
@ IMAP_IDLE
Connection is idle.
Definition private.h:124
#define IMAP_LOG_PASS
Log passwords (dangerous!)
Definition private.h:49
@ IMAP_CMD_QUEUE
Queue a command, do not execute.
Definition private.h:84
@ IMAP_CMD_PASS
Command contains a password. Suppress logging.
Definition private.h:83
#define IMAP_LOG_CMD
Log commands only.
Definition private.h:47
#define mutt_socket_send_d(conn, buf, dbg)
Definition socket.h:57
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 307 of file command.c.

308{
309 s = imap_next_word((char *) s);
310
311 if (mutt_istr_startswith(s, "OK"))
312 return IMAP_RES_OK;
313 if (mutt_istr_startswith(s, "NO"))
314 return IMAP_RES_NO;
315
316 return IMAP_RES_BAD;
317}
#define IMAP_RES_OK
<tag> OK ...
Definition private.h:54
#define IMAP_RES_NO
<tag> NO ...
Definition private.h:52
char * imap_next_word(char *s)
Find where the next IMAP word begins.
Definition util.c:829
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition string.c:246
+ 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 327 of file command.c.

328{
329 unsigned int exp_msn = 0;
330 struct Email *e = NULL;
331
332 mutt_debug(LL_DEBUG2, "Handling EXPUNGE\n");
333
334 if (!adata->mailbox)
335 return;
336
337 struct ImapMboxData *mdata = adata->mailbox->mdata;
338 if (!mdata)
339 return;
340
341 if (!mutt_str_atoui(s, &exp_msn) || (exp_msn < 1) ||
342 (exp_msn > imap_msn_highest(&mdata->msn)))
343 {
344 return;
345 }
346
347 e = imap_msn_get(&mdata->msn, exp_msn - 1);
348 if (e)
349 {
350 /* imap_expunge_mailbox() will rewrite e->index.
351 * It needs to resort using EMAIL_SORT_UNSORTED anyway, so setting to INT_MAX
352 * makes the code simpler and possibly more efficient. */
353 e->index = INT_MAX;
354
356 if (edata)
357 edata->msn = 0;
358 }
359
360 /* decrement seqno of those above. */
361 const size_t max_msn = imap_msn_highest(&mdata->msn);
362 for (unsigned int cur = exp_msn; cur < max_msn; cur++)
363 {
364 e = imap_msn_get(&mdata->msn, cur);
365 if (e)
366 {
368 if (edata)
369 edata->msn--;
370 }
371 imap_msn_set(&mdata->msn, cur - 1, e);
372 }
373 imap_msn_shrink(&mdata->msn, 1);
374
376}
const char * mutt_str_atoui(const char *str, unsigned int *dst)
Convert ASCII string to an unsigned integer.
Definition atoi.c:217
struct ImapEmailData * imap_edata_get(struct Email *e)
Get the private data for this Email.
Definition edata.c:66
@ IMAP_EXPUNGE_PENDING
Messages on the server have been expunged.
Definition private.h:70
@ LL_DEBUG2
Log at debug level 2.
Definition logging2.h:46
size_t imap_msn_shrink(struct MSNArray *msn, size_t num)
Remove a number of entries from the end of the cache.
Definition msn.c:106
struct Email * imap_msn_get(const struct MSNArray *msn, int idx)
Return the Email associated with an msn.
Definition msn.c:83
size_t imap_msn_highest(const struct MSNArray *msn)
Return the highest MSN in use.
Definition msn.c:72
void imap_msn_set(struct MSNArray *msn, size_t idx, struct Email *e)
Cache an Email into a given position.
Definition msn.c:95
The envelope/body of an email.
Definition email.h:39
void * edata
Driver-specific data.
Definition email.h:74
int index
The absolute (unsorted) message number.
Definition email.h:110
IMAP-specific Email data -.
Definition edata.h:35
ImapOpenFlags reopen
Flags, e.g. IMAP_REOPEN_ALLOW.
Definition mdata.h:45
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ cmd_parse_vanished()

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

Parse vanished command.

Parameters
adataImap Account data
sString containing MSN of message to expunge

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

Definition at line 386 of file command.c.

387{
388 bool earlier = false;
389 int rc;
390 unsigned int uid = 0;
391
392 if (!adata->mailbox)
393 return;
394
395 struct ImapMboxData *mdata = adata->mailbox->mdata;
396 if (!mdata)
397 return;
398
399 mutt_debug(LL_DEBUG2, "Handling VANISHED\n");
400
401 if (mutt_istr_startswith(s, "(EARLIER)"))
402 {
403 /* The RFC says we should not decrement msns with the VANISHED EARLIER tag.
404 * My experimentation says that's crap. */
405 earlier = true;
406 s = imap_next_word(s);
407 }
408
409 char *end_of_seqset = s;
410 while (*end_of_seqset)
411 {
412 if (!strchr("0123456789:,", *end_of_seqset))
413 *end_of_seqset = '\0';
414 else
415 end_of_seqset++;
416 }
417
419 if (!iter)
420 {
421 mutt_debug(LL_DEBUG2, "VANISHED: empty seqset [%s]?\n", s);
422 return;
423 }
424
425 while ((rc = mutt_seqset_iterator_next(iter, &uid)) == 0)
426 {
427 struct Email *e = mutt_hash_int_find(mdata->uid_hash, uid);
428 if (!e)
429 continue;
430
431 unsigned int exp_msn = imap_edata_get(e)->msn;
432
433 /* imap_expunge_mailbox() will rewrite e->index.
434 * It needs to resort using EMAIL_SORT_UNSORTED anyway, so setting to INT_MAX
435 * makes the code simpler and possibly more efficient. */
436 e->index = INT_MAX;
437 imap_edata_get(e)->msn = 0;
438
439 if ((exp_msn < 1) || (exp_msn > imap_msn_highest(&mdata->msn)))
440 {
441 mutt_debug(LL_DEBUG1, "VANISHED: msn for UID %u is incorrect\n", uid);
442 continue;
443 }
444 if (imap_msn_get(&mdata->msn, exp_msn - 1) != e)
445 {
446 mutt_debug(LL_DEBUG1, "VANISHED: msn_index for UID %u is incorrect\n", uid);
447 continue;
448 }
449
450 imap_msn_remove(&mdata->msn, exp_msn - 1);
451
452 if (!earlier)
453 {
454 /* decrement seqno of those above. */
455 const size_t max_msn = imap_msn_highest(&mdata->msn);
456 for (unsigned int cur = exp_msn; cur < max_msn; cur++)
457 {
458 e = imap_msn_get(&mdata->msn, cur);
459 if (e)
460 imap_edata_get(e)->msn--;
461 imap_msn_set(&mdata->msn, cur - 1, e);
462 }
463
464 imap_msn_shrink(&mdata->msn, 1);
465 }
466 }
467
468 if (rc < 0)
469 mutt_debug(LL_DEBUG1, "VANISHED: illegal seqset %s\n", s);
470
472
474}
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:394
struct SeqsetIterator * mutt_seqset_iterator_new(const char *seqset)
Create a new Sequence Set Iterator.
Definition util.c:1138
int mutt_seqset_iterator_next(struct SeqsetIterator *iter, unsigned int *next)
Get the next UID from a Sequence Set.
Definition util.c:1159
void mutt_seqset_iterator_free(struct SeqsetIterator **ptr)
Free a Sequence Set Iterator.
Definition util.c:1218
void imap_msn_remove(struct MSNArray *msn, int idx)
Remove an entry from the cache.
Definition msn.c:116
unsigned int msn
Message Sequence Number.
Definition edata.h:46
struct HashTable * uid_hash
Hash Table: "uid" -> Email.
Definition mdata.h:60
UID Sequence Set Iterator.
Definition private.h:185
+ 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 485 of file command.c.

486{
487 unsigned int msn, uid;
488 struct Email *e = NULL;
489 char *flags = NULL;
490 int uid_checked = 0;
491 bool server_changes = false;
492
493 struct ImapMboxData *mdata = imap_mdata_get(adata->mailbox);
494 if (!mdata)
495 return;
496
497 mutt_debug(LL_DEBUG3, "Handling FETCH\n");
498
499 if (!mutt_str_atoui(s, &msn))
500 {
501 mutt_debug(LL_DEBUG3, "Skipping FETCH response - illegal MSN\n");
502 return;
503 }
504
505 if ((msn < 1) || (msn > imap_msn_highest(&mdata->msn)))
506 {
507 mutt_debug(LL_DEBUG3, "Skipping FETCH response - MSN %u out of range\n", msn);
508 return;
509 }
510
511 e = imap_msn_get(&mdata->msn, msn - 1);
512 if (!e || !e->active)
513 {
514 mutt_debug(LL_DEBUG3, "Skipping FETCH response - MSN %u not in msn_index\n", msn);
515 return;
516 }
517
519 if (!edata)
520 {
521 mutt_debug(LL_DEBUG3, "Skipping FETCH response - MSN %u missing edata\n", msn);
522 return;
523 }
524 /* skip FETCH */
525 s = imap_next_word(s);
526 s = imap_next_word(s);
527
528 if (*s != '(')
529 {
530 mutt_debug(LL_DEBUG1, "Malformed FETCH response\n");
531 return;
532 }
533 s++;
534
535 while (*s)
536 {
537 SKIPWS(s);
538 size_t plen = mutt_istr_startswith(s, "FLAGS");
539 if (plen != 0)
540 {
541 flags = s;
542 if (uid_checked)
543 break;
544
545 s += plen;
546 SKIPWS(s);
547 if (*s != '(')
548 {
549 mutt_debug(LL_DEBUG1, "bogus FLAGS response: %s\n", s);
550 return;
551 }
552 s++;
553 while (*s && (*s != ')'))
554 s++;
555 if (*s == ')')
556 {
557 s++;
558 }
559 else
560 {
561 mutt_debug(LL_DEBUG1, "Unterminated FLAGS response: %s\n", s);
562 return;
563 }
564 }
565 else if ((plen = mutt_istr_startswith(s, "UID")))
566 {
567 s += plen;
568 SKIPWS(s);
569 if (!mutt_str_atoui(s, &uid))
570 {
571 mutt_debug(LL_DEBUG1, "Illegal UID. Skipping update\n");
572 return;
573 }
574 if (uid != edata->uid)
575 {
576 mutt_debug(LL_DEBUG1, "UID vs MSN mismatch. Skipping update\n");
577 return;
578 }
579 uid_checked = 1;
580 if (flags)
581 break;
582 s = imap_next_word(s);
583 }
584 else if ((plen = mutt_istr_startswith(s, "MODSEQ")))
585 {
586 s += plen;
587 SKIPWS(s);
588 if (*s != '(')
589 {
590 mutt_debug(LL_DEBUG1, "bogus MODSEQ response: %s\n", s);
591 return;
592 }
593 s++;
594 while (*s && (*s != ')'))
595 s++;
596 if (*s == ')')
597 {
598 s++;
599 }
600 else
601 {
602 mutt_debug(LL_DEBUG1, "Unterminated MODSEQ response: %s\n", s);
603 return;
604 }
605 }
606 else if (*s == ')')
607 {
608 break; /* end of request */
609 }
610 else if (*s)
611 {
612 mutt_debug(LL_DEBUG2, "Only handle FLAGS updates\n");
613 break;
614 }
615 }
616
617 if (flags)
618 {
619 imap_set_flags(adata->mailbox, e, flags, &server_changes);
620 if (server_changes)
621 {
622 /* If server flags could conflict with NeoMutt's flags, reopen the mailbox. */
623 if (e->changed)
625 else
627 }
628 }
629}
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:1952
@ IMAP_FLAGS_PENDING
Flags have changed on the server.
Definition private.h:72
#define SKIPWS(ch)
Definition string2.h:52
bool active
Message is not to be removed.
Definition email.h:76
bool changed
Email has been edited.
Definition email.h:77
unsigned int uid
32-bit Message UID
Definition edata.h:45
ImapOpenFlags check_status
Flags, e.g. IMAP_NEWMAIL_PENDING.
Definition mdata.h:46
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ cmd_parse_capability()

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

Set capability bits according to CAPABILITY response.

Parameters
adataImap Account data
sCommand string with capabilities

Definition at line 636 of file command.c.

637{
638 mutt_debug(LL_DEBUG3, "Handling CAPABILITY\n");
639
640 s = imap_next_word(s);
641 char *bracket = strchr(s, ']');
642 if (bracket)
643 *bracket = '\0';
644 FREE(&adata->capstr);
645 adata->capstr = mutt_str_dup(s);
646 adata->capabilities = 0;
647
648 while (*s)
649 {
650 for (size_t i = 0; Capabilities[i]; i++)
651 {
652 size_t len = mutt_istr_startswith(s, Capabilities[i]);
653 if (len != 0 && ((s[len] == '\0') || mutt_isspace(s[len])))
654 {
655 adata->capabilities |= (1 << i);
656 mutt_debug(LL_DEBUG3, " Found capability \"%s\": %zu\n", Capabilities[i], i);
657 break;
658 }
659 }
660 s = imap_next_word(s);
661 }
662}
bool mutt_isspace(int arg)
Wrapper for isspace(3)
Definition ctype.c:96
static const char *const Capabilities[]
Server capabilities strings that we understand.
Definition command.c:79
#define FREE(x)
Free memory and set the pointer to NULL.
Definition memory.h:68
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition string.c:257
ImapCapFlags capabilities
Capability flags.
Definition adata.h:56
char * capstr
Capability string from the server.
Definition adata.h:55
+ 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 669 of file command.c.

670{
671 struct ImapList *list = NULL;
672 struct ImapList lb = { 0 };
673 unsigned int litlen;
674
675 if (adata->cmdresult)
676 list = adata->cmdresult;
677 else
678 list = &lb;
679
680 memset(list, 0, sizeof(struct ImapList));
681
682 /* flags */
683 s = imap_next_word(s);
684 if (*s != '(')
685 {
686 mutt_debug(LL_DEBUG1, "Bad LIST response\n");
687 return;
688 }
689 s++;
690 while (*s)
691 {
692 if (mutt_istr_startswith(s, "\\NoSelect"))
693 list->noselect = true;
694 else if (mutt_istr_startswith(s, "\\NonExistent")) /* rfc5258 */
695 list->noselect = true;
696 else if (mutt_istr_startswith(s, "\\NoInferiors"))
697 list->noinferiors = true;
698 else if (mutt_istr_startswith(s, "\\HasNoChildren")) /* rfc5258*/
699 list->noinferiors = true;
700
701 s = imap_next_word(s);
702 if (*(s - 2) == ')')
703 break;
704 }
705
706 /* Delimiter */
707 if (!mutt_istr_startswith(s, "NIL"))
708 {
709 char delimbuf[5] = { 0 }; // worst case: "\\"\0
710 snprintf(delimbuf, sizeof(delimbuf), "%s", s);
711 imap_unquote_string(delimbuf);
712 list->delim = delimbuf[0];
713 }
714
715 /* Name */
716 s = imap_next_word(s);
717 /* Notes often responds with literals here. We need a real tokenizer. */
718 if (imap_get_literal_count(s, &litlen) == 0)
719 {
720 if (imap_cmd_step(adata) != IMAP_RES_CONTINUE)
721 {
722 adata->status = IMAP_FATAL;
723 return;
724 }
725
726 if (strlen(adata->buf) < litlen)
727 {
728 mutt_debug(LL_DEBUG1, "Error parsing LIST mailbox\n");
729 return;
730 }
731
732 list->name = adata->buf;
733 s = list->name + litlen;
734 if (s[0] != '\0')
735 {
736 s[0] = '\0';
737 s++;
738 SKIPWS(s);
739 }
740 }
741 else
742 {
743 list->name = s;
744 /* Exclude rfc5258 RECURSIVEMATCH CHILDINFO suffix */
745 s = imap_next_word(s);
746 if (s[0] != '\0')
747 s[-1] = '\0';
748 imap_unmunge_mbox_name(adata->unicode, list->name);
749 }
750
751 if (list->name[0] == '\0')
752 {
753 adata->delim = list->delim;
754 mutt_debug(LL_DEBUG3, "Root delimiter: %c\n", adata->delim);
755 }
756}
int imap_cmd_step(struct ImapAccountData *adata)
Reads server responses from an IMAP command.
Definition command.c:1230
void imap_unmunge_mbox_name(bool unicode, char *s)
Remove quoting from a mailbox name.
Definition util.c:988
void imap_unquote_string(char *s)
Equally stupid unquoting routine.
Definition util.c:934
int imap_get_literal_count(char *buf, unsigned int *bytes)
Write number of bytes in an IMAP literal into bytes.
Definition util.c:785
#define IMAP_RES_CONTINUE
* ...
Definition private.h:55
char delim
Path delimiter.
Definition adata.h:79
struct ImapList * cmdresult
Resuls of complicated commands.
Definition adata.h:70
bool unicode
If true, we can send UTF-8, and the server will use UTF8 rather than mUTF7.
Definition adata.h:64
char * buf
Command buffer.
Definition adata.h:61
Items in an IMAP browser.
Definition private.h:165
bool noselect
Mailbox is not selectable.
Definition private.h:168
bool noinferiors
Mailbox has no children.
Definition private.h:169
char * name
Mailbox name.
Definition private.h:166
char delim
Hierarchy delimiter.
Definition private.h:167
+ 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 763 of file command.c.

764{
765 if (adata->cmdresult)
766 {
767 /* caller will handle response itself */
768 cmd_parse_list(adata, s);
769 return;
770 }
771
772 const bool c_imap_check_subscribed = cs_subset_bool(NeoMutt->sub, "imap_check_subscribed");
773 if (!c_imap_check_subscribed)
774 return;
775
776 struct ImapList list = { 0 };
777
778 adata->cmdresult = &list;
779 cmd_parse_list(adata, s);
780 adata->cmdresult = NULL;
781 /* noselect is for a gmail quirk */
782 if (!list.name || list.noselect)
783 return;
784
785 mutt_debug(LL_DEBUG3, "Subscribing to %s\n", list.name);
786
787 struct Buffer *buf = buf_pool_get();
788 struct Buffer *err = buf_pool_get();
789 struct Url url = { 0 };
790
791 account_to_url(&adata->conn->account, &url);
792 url.path = list.name;
793
794 const char *const c_imap_user = cs_subset_string(NeoMutt->sub, "imap_user");
795 if (mutt_str_equal(url.user, c_imap_user))
796 url.user = NULL;
797 url_tobuffer(&url, buf, U_NONE);
798
799 if (!mailbox_add_simple(buf_string(buf), err))
800 mutt_debug(LL_DEBUG1, "Error adding subscribed mailbox: %s\n", buf_string(err));
801
802 buf_pool_release(&buf);
803 buf_pool_release(&err);
804}
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition buffer.h:96
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:669
bool mailbox_add_simple(const char *mailbox, struct Buffer *err)
Add a new Mailbox.
Definition mailboxes.c:157
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition string.c:665
void account_to_url(struct ConnAccount *cac, struct Url *url)
Fill URL with info from account.
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition pool.c:91
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition pool.c:111
String manipulation buffer.
Definition buffer.h:36
Container for Accounts, Notifications.
Definition neomutt.h:41
struct ConfigSubset * sub
Inherited config items.
Definition neomutt.h:49
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:361
#define U_NONE
No flags are set for URL parsing.
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 811 of file command.c.

812{
813 mutt_debug(LL_DEBUG2, "Handling MYRIGHTS\n");
814
815 if (!adata->mailbox)
816 return;
817
818 s = imap_next_word((char *) s);
819 s = imap_next_word((char *) s);
820
821 /* zero out current rights set */
822 adata->mailbox->rights = 0;
823
824 while (*s && !mutt_isspace(*s))
825 {
826 switch (*s)
827 {
828 case 'a':
829 adata->mailbox->rights |= MUTT_ACL_ADMIN;
830 break;
831 case 'e':
833 break;
834 case 'i':
835 adata->mailbox->rights |= MUTT_ACL_INSERT;
836 break;
837 case 'k':
838 adata->mailbox->rights |= MUTT_ACL_CREATE;
839 break;
840 case 'l':
841 adata->mailbox->rights |= MUTT_ACL_LOOKUP;
842 break;
843 case 'p':
844 adata->mailbox->rights |= MUTT_ACL_POST;
845 break;
846 case 'r':
847 adata->mailbox->rights |= MUTT_ACL_READ;
848 break;
849 case 's':
850 adata->mailbox->rights |= MUTT_ACL_SEEN;
851 break;
852 case 't':
853 adata->mailbox->rights |= MUTT_ACL_DELETE;
854 break;
855 case 'w':
856 adata->mailbox->rights |= MUTT_ACL_WRITE;
857 break;
858 case 'x':
859 adata->mailbox->rights |= MUTT_ACL_DELMX;
860 break;
861
862 /* obsolete rights */
863 case 'c':
865 break;
866 case 'd':
868 break;
869 default:
870 mutt_debug(LL_DEBUG1, "Unknown right: %c\n", *s);
871 }
872 s++;
873 }
874}
@ MUTT_ACL_WRITE
Write to a message (for flagging or linking threads)
Definition mailbox.h:71
@ MUTT_ACL_INSERT
Add/copy into the mailbox (used when editing a message)
Definition mailbox.h:66
@ MUTT_ACL_DELMX
Delete a mailbox.
Definition mailbox.h:64
@ MUTT_ACL_ADMIN
Administer the account (get/set permissions)
Definition mailbox.h:61
@ MUTT_ACL_READ
Read the mailbox.
Definition mailbox.h:69
@ MUTT_ACL_POST
Post (submit messages to the server)
Definition mailbox.h:68
@ MUTT_ACL_CREATE
Create a mailbox.
Definition mailbox.h:62
@ MUTT_ACL_DELETE
Delete a message.
Definition mailbox.h:63
@ MUTT_ACL_EXPUNGE
Expunge messages.
Definition mailbox.h:65
@ MUTT_ACL_SEEN
Change the 'seen' status of a message.
Definition mailbox.h:70
@ MUTT_ACL_LOOKUP
Lookup mailbox (visible to 'list')
Definition mailbox.h:67
AclFlags rights
ACL bits, see AclFlags.
Definition mailbox.h:121
+ 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 882 of file command.c.

883{
884 if (!adata || !adata->account || !name)
885 return NULL;
886
887 struct Mailbox **mp = NULL;
888 ARRAY_FOREACH(mp, &adata->account->mailboxes)
889 {
890 struct Mailbox *m = *mp;
891
892 struct ImapMboxData *mdata = imap_mdata_get(m);
893 if (mdata && mutt_str_equal(name, mdata->name))
894 return m;
895 }
896
897 return NULL;
898}
#define ARRAY_FOREACH(elem, head)
Iterate over all elements of the array.
Definition array.h:223
struct MailboxArray mailboxes
All Mailboxes.
Definition account.h:40
struct Account * account
Parent Account.
Definition adata.h:82
char * name
Mailbox name.
Definition mdata.h:41
A mailbox.
Definition mailbox.h:81
+ 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 908 of file command.c.

909{
910 unsigned int litlen = 0;
911
912 char *mailbox = imap_next_word(s);
913
914 /* We need a real tokenizer. */
915 if (imap_get_literal_count(mailbox, &litlen) == 0)
916 {
917 if (imap_cmd_step(adata) != IMAP_RES_CONTINUE)
918 {
919 adata->status = IMAP_FATAL;
920 return;
921 }
922
923 if (strlen(adata->buf) < litlen)
924 {
925 mutt_debug(LL_DEBUG1, "Error parsing STATUS mailbox\n");
926 return;
927 }
928
929 mailbox = adata->buf;
930 s = mailbox + litlen;
931 s[0] = '\0';
932 s++;
933 SKIPWS(s);
934 }
935 else
936 {
937 s = imap_next_word(mailbox);
938 s[-1] = '\0';
939 imap_unmunge_mbox_name(adata->unicode, mailbox);
940 }
941
942 struct Mailbox *m = find_mailbox(adata, mailbox);
943 struct ImapMboxData *mdata = imap_mdata_get(m);
944 if (!mdata)
945 {
946 mutt_debug(LL_DEBUG3, "Received status for an unexpected mailbox: %s\n", mailbox);
947 return;
948 }
949 uint32_t olduv = mdata->uidvalidity;
950 unsigned int oldun = mdata->uid_next;
951
952 if (*s++ != '(')
953 {
954 mutt_debug(LL_DEBUG1, "Error parsing STATUS\n");
955 return;
956 }
957 while ((s[0] != '\0') && (s[0] != ')'))
958 {
959 char *value = imap_next_word(s);
960
961 errno = 0;
962 const unsigned long ulcount = strtoul(value, &value, 10);
963 const bool truncated = ((errno == ERANGE) && (ulcount == ULONG_MAX)) ||
964 ((unsigned int) ulcount != ulcount);
965 const unsigned int count = (unsigned int) ulcount;
966
967 // we accept truncating a larger value only for UIDVALIDITY, to accommodate
968 // IMAP servers that use 64-bits for it. This seems to be what Thunderbird
969 // is also doing, see #3830
970 if (mutt_str_startswith(s, "UIDVALIDITY"))
971 {
972 if (truncated)
973 {
975 "UIDVALIDITY [%lu] exceeds 32 bits, "
976 "truncated to [%u]\n",
977 ulcount, count);
978 }
979 mdata->uidvalidity = count;
980 }
981 else
982 {
983 if (truncated)
984 {
985 mutt_debug(LL_DEBUG1, "Number in [%s] exceeds 32 bits\n", s);
986 return;
987 }
988 else
989 {
990 if (mutt_str_startswith(s, "MESSAGES"))
991 mdata->messages = count;
992 else if (mutt_str_startswith(s, "RECENT"))
993 mdata->recent = count;
994 else if (mutt_str_startswith(s, "UIDNEXT"))
995 mdata->uid_next = count;
996 else if (mutt_str_startswith(s, "UNSEEN"))
997 mdata->unseen = count;
998 }
999 }
1000
1001 s = value;
1002 if ((s[0] != '\0') && (*s != ')'))
1003 s = imap_next_word(s);
1004 }
1005 mutt_debug(LL_DEBUG3, "%s (UIDVALIDITY: %u, UIDNEXT: %u) %d messages, %d recent, %d unseen\n",
1006 mdata->name, mdata->uidvalidity, mdata->uid_next, mdata->messages,
1007 mdata->recent, mdata->unseen);
1008
1009 mutt_debug(LL_DEBUG3, "Running default STATUS handler\n");
1010
1011 mutt_debug(LL_DEBUG3, "Found %s in mailbox list (OV: %u ON: %u U: %d)\n",
1012 mailbox, olduv, oldun, mdata->unseen);
1013
1014 bool new_mail = false;
1015 const bool c_mail_check_recent = cs_subset_bool(NeoMutt->sub, "mail_check_recent");
1016 if (c_mail_check_recent)
1017 {
1018 if ((olduv != 0) && (olduv == mdata->uidvalidity))
1019 {
1020 if (oldun < mdata->uid_next)
1021 new_mail = (mdata->unseen > 0);
1022 }
1023 else if ((olduv == 0) && (oldun == 0))
1024 {
1025 /* first check per session, use recent. might need a flag for this. */
1026 new_mail = (mdata->recent > 0);
1027 }
1028 else
1029 {
1030 new_mail = (mdata->unseen > 0);
1031 }
1032 }
1033 else
1034 {
1035 new_mail = (mdata->unseen > 0);
1036 }
1037
1038 m->has_new = new_mail;
1039 m->msg_count = mdata->messages;
1040 m->msg_unread = mdata->unseen;
1041
1042 // force back to keep detecting new mail until the mailbox is opened
1043 if (m->has_new)
1044 mdata->uid_next = oldun;
1045
1046 struct EventMailbox ev_m = { m };
1048}
@ NT_MAILBOX_CHANGE
Mailbox has been changed.
Definition mailbox.h:178
static struct Mailbox * find_mailbox(struct ImapAccountData *adata, const char *name)
Find a Mailbox by its name.
Definition command.c:882
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:234
@ NT_MAILBOX
Mailbox has changed, NotifyMailbox, EventMailbox.
Definition notify_type.h:50
An Event that happened to a Mailbox.
Definition mailbox.h:192
unsigned int uid_next
Next UID for new message.
Definition mdata.h:52
bool has_new
Mailbox has new mail.
Definition mailbox.h:87
int msg_count
Total number of messages.
Definition mailbox.h:90
struct Notify * notify
Notifications: NotifyMailbox, EventMailbox.
Definition mailbox.h:147
int msg_unread
Number of unread messages.
Definition mailbox.h:91
+ 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 1055 of file command.c.

1056{
1057 mutt_debug(LL_DEBUG2, "Handling ENABLED\n");
1058
1059 while ((s = imap_next_word((char *) s)) && (*s != '\0'))
1060 {
1061 if (mutt_istr_startswith(s, "UTF8=ACCEPT") || mutt_istr_startswith(s, "UTF8=ONLY"))
1062 {
1063 adata->unicode = true;
1064 }
1065 if (mutt_istr_startswith(s, "QRESYNC"))
1066 adata->qresync = true;
1067 }
1068}
bool qresync
true, if QRESYNC is successfully ENABLE'd
Definition adata.h:65
+ 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 1075 of file command.c.

1076{
1077 unsigned int count = 0;
1078 mutt_debug(LL_DEBUG2, "Handling EXISTS\n");
1079
1080 if (!mutt_str_atoui(pn, &count))
1081 {
1082 mutt_debug(LL_DEBUG1, "Malformed EXISTS: '%s'\n", pn);
1083 return;
1084 }
1085
1086 if (!adata->mailbox)
1087 return;
1088
1089 struct ImapMboxData *mdata = adata->mailbox->mdata;
1090 if (!mdata)
1091 return;
1092
1093 /* new mail arrived */
1094 if (count < imap_msn_highest(&mdata->msn))
1095 {
1096 /* Notes 6.0.3 has a tendency to report fewer messages exist than
1097 * it should. */
1098 mutt_debug(LL_DEBUG1, "Message count is out of sync\n");
1099 }
1100 else if (count == imap_msn_highest(&mdata->msn))
1101 {
1102 /* at least the InterChange server sends EXISTS messages freely,
1103 * even when there is no new mail */
1104 mutt_debug(LL_DEBUG3, "superfluous EXISTS message\n");
1105 }
1106 else
1107 {
1108 mutt_debug(LL_DEBUG2, "New mail in %s - %d messages total\n", mdata->name, count);
1109 mdata->reopen |= IMAP_NEWMAIL_PENDING;
1110 mdata->new_mail_count = count;
1111 }
1112}
@ IMAP_NEWMAIL_PENDING
New mail is waiting on the server.
Definition private.h:71
+ 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 1120 of file command.c.

1121{
1122 char *s = imap_next_word(adata->buf);
1123 char *pn = imap_next_word(s);
1124
1125 const bool c_imap_server_noise = cs_subset_bool(NeoMutt->sub, "imap_server_noise");
1126 if ((adata->state >= IMAP_SELECTED) && mutt_isdigit(*s))
1127 {
1128 /* pn vs. s: need initial seqno */
1129 pn = s;
1130 s = imap_next_word(s);
1131
1132 /* EXISTS, EXPUNGE, FETCH are always related to the SELECTED mailbox */
1133 if (mutt_istr_startswith(s, "EXISTS"))
1134 cmd_parse_exists(adata, pn);
1135 else if (mutt_istr_startswith(s, "EXPUNGE"))
1136 cmd_parse_expunge(adata, pn);
1137 else if (mutt_istr_startswith(s, "FETCH"))
1138 cmd_parse_fetch(adata, pn);
1139 }
1140 else if ((adata->state >= IMAP_SELECTED) && mutt_istr_startswith(s, "VANISHED"))
1141 {
1142 cmd_parse_vanished(adata, pn);
1143 }
1144 else if (mutt_istr_startswith(s, "CAPABILITY"))
1145 {
1146 cmd_parse_capability(adata, s);
1147 }
1148 else if (mutt_istr_startswith(s, "OK [CAPABILITY"))
1149 {
1150 cmd_parse_capability(adata, pn);
1151 }
1152 else if (mutt_istr_startswith(pn, "OK [CAPABILITY"))
1153 {
1155 }
1156 else if (mutt_istr_startswith(s, "LIST"))
1157 {
1158 cmd_parse_list(adata, s);
1159 }
1160 else if (mutt_istr_startswith(s, "LSUB"))
1161 {
1162 cmd_parse_lsub(adata, s);
1163 }
1164 else if (mutt_istr_startswith(s, "MYRIGHTS"))
1165 {
1166 cmd_parse_myrights(adata, s);
1167 }
1168 else if (mutt_istr_startswith(s, "SEARCH"))
1169 {
1170 cmd_parse_search(adata, s);
1171 }
1172 else if (mutt_istr_startswith(s, "STATUS"))
1173 {
1174 cmd_parse_status(adata, s);
1175 }
1176 else if (mutt_istr_startswith(s, "ENABLED"))
1177 {
1178 cmd_parse_enabled(adata, s);
1179 }
1180 else if (mutt_istr_startswith(s, "BYE"))
1181 {
1182 mutt_debug(LL_DEBUG2, "Handling BYE\n");
1183
1184 /* check if we're logging out */
1185 if (adata->status == IMAP_BYE)
1186 return 0;
1187
1188 /* server shut down our connection */
1189 s += 3;
1190 SKIPWS(s);
1191 mutt_error("%s", s);
1192 cmd_handle_fatal(adata);
1193
1194 return -1;
1195 }
1196 else if (c_imap_server_noise && mutt_istr_startswith(s, "NO"))
1197 {
1198 mutt_debug(LL_DEBUG2, "Handling untagged NO\n");
1199
1200 /* Display the warning message from the server */
1201 mutt_error("%s", s + 2);
1202 }
1203
1204 return 0;
1205}
bool mutt_isdigit(int arg)
Wrapper for isdigit(3)
Definition ctype.c:66
static void cmd_parse_capability(struct ImapAccountData *adata, char *s)
Set capability bits according to CAPABILITY response.
Definition command.c:636
static void cmd_parse_lsub(struct ImapAccountData *adata, char *s)
Parse a server LSUB (list subscribed mailboxes)
Definition command.c:763
static void cmd_parse_status(struct ImapAccountData *adata, char *s)
Parse status from server.
Definition command.c:908
static void cmd_parse_exists(struct ImapAccountData *adata, const char *pn)
Parse EXISTS message from serer.
Definition command.c:1075
static void cmd_parse_myrights(struct ImapAccountData *adata, const char *s)
Set rights bits according to MYRIGHTS response.
Definition command.c:811
static void cmd_parse_expunge(struct ImapAccountData *adata, const char *s)
Parse expunge command.
Definition command.c:327
static void cmd_parse_enabled(struct ImapAccountData *adata, const char *s)
Record what the server has enabled.
Definition command.c:1055
static void cmd_parse_vanished(struct ImapAccountData *adata, char *s)
Parse vanished command.
Definition command.c:386
static void cmd_parse_fetch(struct ImapAccountData *adata, char *s)
Load fetch response into ImapAccountData.
Definition command.c:485
@ IMAP_BYE
Logged out from server.
Definition private.h:109
void cmd_parse_search(struct ImapAccountData *adata, const char *s)
Store SEARCH response for later use.
Definition search.c:260
+ 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 1216 of file command.c.

1217{
1218 return cmd_start(adata, cmdstr, IMAP_CMD_NONE);
1219}
static int cmd_start(struct ImapAccountData *adata, const char *cmdstr, ImapCmdFlags flags)
Start a new IMAP command.
Definition command.c:271
@ IMAP_CMD_NONE
No flags are set.
Definition private.h:82
+ 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 1230 of file command.c.

1231{
1232 if (!adata)
1233 return -1;
1234
1235 size_t len = 0;
1236 int c;
1237 int rc;
1238 int stillrunning = 0;
1239 struct ImapCommand *cmd = NULL;
1240
1241 if (adata->status == IMAP_FATAL)
1242 {
1243 cmd_handle_fatal(adata);
1244 return IMAP_RES_BAD;
1245 }
1246
1247 /* read into buffer, expanding buffer as necessary until we have a full
1248 * line */
1249 do
1250 {
1251 if (len == adata->blen)
1252 {
1253 MUTT_MEM_REALLOC(&adata->buf, adata->blen + IMAP_CMD_BUFSIZE, char);
1254 adata->blen = adata->blen + IMAP_CMD_BUFSIZE;
1255 mutt_debug(LL_DEBUG3, "grew buffer to %zu bytes\n", adata->blen);
1256 }
1257
1258 /* back up over '\0' */
1259 if (len)
1260 len--;
1261
1262 mutt_debug(LL_DEBUG3, "reading from socket (fd=%d, state=%d)\n",
1263 adata->conn ? adata->conn->fd : -1, adata->state);
1264 time_t read_start = mutt_date_now();
1265
1266 c = mutt_socket_readln_d(adata->buf + len, adata->blen - len, adata->conn, MUTT_SOCK_LOG_FULL);
1267
1268 time_t read_duration = mutt_date_now() - read_start;
1269 if (read_duration > 1)
1270 {
1271 mutt_debug(LL_DEBUG1, "socket read took %ld seconds\n", (long) read_duration);
1272 }
1273
1274 if (c <= 0)
1275 {
1276 mutt_debug(LL_DEBUG1, "Error reading server response (rc=%d, errno=%d: %s)\n",
1277 c, errno, strerror(errno));
1278 mutt_debug(LL_DEBUG1, "Connection state: fd=%d, state=%d, status=%d\n",
1279 adata->conn ? adata->conn->fd : -1, adata->state, adata->status);
1280 cmd_handle_fatal(adata);
1281 return IMAP_RES_BAD;
1282 }
1283
1284 len += c;
1285 }
1286 /* if we've read all the way to the end of the buffer, we haven't read a
1287 * full line (mutt_socket_readln strips the \r, so we always have at least
1288 * one character free when we've read a full line) */
1289 while (len == adata->blen);
1290
1291 /* don't let one large string make cmd->buf hog memory forever */
1292 if ((adata->blen > IMAP_CMD_BUFSIZE) && (len <= IMAP_CMD_BUFSIZE))
1293 {
1294 MUTT_MEM_REALLOC(&adata->buf, IMAP_CMD_BUFSIZE, char);
1295 adata->blen = IMAP_CMD_BUFSIZE;
1296 mutt_debug(LL_DEBUG3, "shrank buffer to %zu bytes\n", adata->blen);
1297 }
1298
1299 adata->lastread = mutt_date_now();
1300
1301 /* handle untagged messages. The caller still gets its shot afterwards. */
1302 if ((mutt_str_startswith(adata->buf, "* ") ||
1303 mutt_str_startswith(imap_next_word(adata->buf), "OK [")) &&
1304 cmd_handle_untagged(adata))
1305 {
1306 return IMAP_RES_BAD;
1307 }
1308
1309 /* server demands a continuation response from us */
1310 if (adata->buf[0] == '+')
1311 return IMAP_RES_RESPOND;
1312
1313 /* Look for tagged command completions.
1314 *
1315 * Some response handlers can end up recursively calling
1316 * imap_cmd_step() and end up handling all tagged command
1317 * completions.
1318 * (e.g. FETCH->set_flag->set_header_color->~h pattern match.)
1319 *
1320 * Other callers don't even create an adata->cmds entry.
1321 *
1322 * For both these cases, we default to returning OK */
1323 rc = IMAP_RES_OK;
1324 c = adata->lastcmd;
1325 do
1326 {
1327 cmd = &adata->cmds[c];
1328 if (cmd->state == IMAP_RES_NEW)
1329 {
1330 if (mutt_str_startswith(adata->buf, cmd->seq))
1331 {
1332 if (!stillrunning)
1333 {
1334 /* first command in queue has finished - move queue pointer up */
1335 adata->lastcmd = (adata->lastcmd + 1) % adata->cmdslots;
1336 }
1337 cmd->state = cmd_status(adata->buf);
1338 rc = cmd->state;
1339 if (cmd->state == IMAP_RES_NO || cmd->state == IMAP_RES_BAD)
1340 {
1341 mutt_message(_("IMAP command failed: %s"), adata->buf);
1342 }
1343 }
1344 else
1345 {
1346 stillrunning++;
1347 }
1348 }
1349
1350 c = (c + 1) % adata->cmdslots;
1351 } while (c != adata->nextcmd);
1352
1353 if (stillrunning)
1354 {
1355 rc = IMAP_RES_CONTINUE;
1356 }
1357 else
1358 {
1359 mutt_debug(LL_DEBUG3, "IMAP queue drained\n");
1360 imap_cmd_finish(adata);
1361 }
1362
1363 return rc;
1364}
static int cmd_handle_untagged(struct ImapAccountData *adata)
Fallback parser for otherwise unhandled messages.
Definition command.c:1120
static int cmd_status(const char *s)
Parse response line for tagged OK/NO/BAD.
Definition command.c:307
#define IMAP_CMD_BUFSIZE
Default buffer size for IMAP commands.
Definition command.c:63
void imap_cmd_finish(struct ImapAccountData *adata)
Attempt to perform cleanup.
Definition command.c:1511
#define IMAP_RES_RESPOND
+
Definition private.h:56
#define MUTT_MEM_REALLOC(pptr, n, type)
Definition memory.h:55
time_t mutt_date_now(void)
Return the number of seconds since the Unix epoch.
Definition date.c:457
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
Log everything including full protocol.
Definition socket.h:53
time_t lastread
last time we read a command for the server
Definition adata.h:59
size_t blen
Command buffer length.
Definition adata.h:62
+ 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 1372 of file command.c.

1373{
1374 return cmd_status(s) == IMAP_RES_OK;
1375}
+ 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 1383 of file command.c.

1384{
1385 static const char *notrailer = "";
1386 const char *s = adata->buf;
1387
1388 if (!s)
1389 {
1390 mutt_debug(LL_DEBUG2, "not a tagged response\n");
1391 return notrailer;
1392 }
1393
1394 s = imap_next_word((char *) s);
1395 if (!s || (!mutt_istr_startswith(s, "OK") && !mutt_istr_startswith(s, "NO") &&
1396 !mutt_istr_startswith(s, "BAD")))
1397 {
1398 mutt_debug(LL_DEBUG2, "not a command completion: %s\n", adata->buf);
1399 return notrailer;
1400 }
1401
1402 s = imap_next_word((char *) s);
1403 if (!s)
1404 return notrailer;
1405
1406 return s;
1407}
+ 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 1420 of file command.c.

1421{
1422 if (!adata)
1423 return IMAP_EXEC_ERROR;
1424
1425 /* Check connection health before executing command */
1426 if ((adata->state >= IMAP_AUTHENTICATED) && (adata->last_success > 0))
1427 {
1428 time_t now = mutt_date_now();
1429 time_t idle_time = now - adata->last_success;
1430
1431 if (idle_time > IMAP_CONN_STALE_THRESHOLD)
1432 {
1433 static bool probing = false;
1434 if (!probing)
1435 {
1436 mutt_debug(LL_DEBUG2, "Connection idle for %ld seconds, sending NOOP probe\n",
1437 (long) idle_time);
1438 probing = true;
1439 int noop_rc = imap_exec(adata, "NOOP", IMAP_CMD_POLL);
1440 probing = false;
1441 if (noop_rc == IMAP_EXEC_FATAL)
1442 return IMAP_EXEC_FATAL;
1443 }
1444 }
1445 }
1446
1447 if (flags & IMAP_CMD_SINGLE)
1448 {
1449 // Process any existing commands
1450 if (adata->nextcmd != adata->lastcmd)
1451 imap_exec(adata, NULL, IMAP_CMD_POLL);
1452 }
1453
1454 int rc = cmd_start(adata, cmdstr, flags);
1455 if (rc < 0)
1456 {
1457 cmd_handle_fatal(adata);
1458 return IMAP_EXEC_FATAL;
1459 }
1460
1461 if (flags & IMAP_CMD_QUEUE)
1462 return IMAP_EXEC_SUCCESS;
1463
1464 const short c_imap_poll_timeout = cs_subset_number(NeoMutt->sub, "imap_poll_timeout");
1465 if ((flags & IMAP_CMD_POLL) && (c_imap_poll_timeout > 0) &&
1466 ((mutt_socket_poll(adata->conn, c_imap_poll_timeout)) == 0))
1467 {
1468 mutt_error(_("Connection to %s timed out"), adata->conn->account.host);
1469 cmd_handle_fatal(adata);
1470 return IMAP_EXEC_FATAL;
1471 }
1472
1473 /* Allow interruptions, particularly useful if there are network problems. */
1475 do
1476 {
1477 rc = imap_cmd_step(adata);
1478 // The queue is empty, so the single command has been processed
1479 if ((flags & IMAP_CMD_SINGLE) && (adata->nextcmd == adata->lastcmd))
1480 break;
1481 } while (rc == IMAP_RES_CONTINUE);
1483
1484 if (rc == IMAP_RES_NO)
1485 return IMAP_EXEC_ERROR;
1486 if (rc != IMAP_RES_OK)
1487 {
1488 if (adata->status != IMAP_FATAL)
1489 return IMAP_EXEC_ERROR;
1490
1491 mutt_debug(LL_DEBUG1, "command failed: %s\n", adata->buf);
1492 return IMAP_EXEC_FATAL;
1493 }
1494
1495 /* Track successful command completion for connection health monitoring */
1496 adata->last_success = mutt_date_now();
1497
1498 return IMAP_EXEC_SUCCESS;
1499}
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition helpers.c:143
#define IMAP_CONN_STALE_THRESHOLD
Threshold in seconds after which to consider a connection potentially stale.
Definition command.c:72
@ IMAP_AUTHENTICATED
Connection is authenticated.
Definition private.h:120
@ IMAP_EXEC_SUCCESS
Imap command executed or queued successfully.
Definition private.h:95
@ IMAP_EXEC_FATAL
Imap connection failure.
Definition private.h:97
@ IMAP_CMD_SINGLE
Run a single command.
Definition private.h:86
void mutt_sig_allow_interrupt(bool allow)
Allow/disallow Ctrl-C (SIGINT)
Definition signal.c:315
int mutt_socket_poll(struct Connection *conn, time_t wait_secs)
Checks whether reads would block.
Definition socket.c:182
time_t last_success
last time a command completed successfully
Definition adata.h:60
+ 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 1511 of file command.c.

1512{
1513 if (!adata)
1514 return;
1515
1516 if (adata->status == IMAP_FATAL)
1517 {
1518 adata->closing = false;
1519 cmd_handle_fatal(adata);
1520 return;
1521 }
1522
1523 if (!(adata->state >= IMAP_SELECTED) || !adata->mailbox || adata->closing)
1524 {
1525 adata->closing = false;
1526 return;
1527 }
1528
1529 adata->closing = false;
1530
1531 struct ImapMboxData *mdata = imap_mdata_get(adata->mailbox);
1532
1533 if (mdata && (mdata->reopen & IMAP_REOPEN_ALLOW) && !(mdata->reopen & IMAP_SYNC_IN_PROGRESS))
1534 {
1535 // First remove expunged emails from the msn_index
1536 if (mdata->reopen & IMAP_EXPUNGE_PENDING)
1537 {
1538 mutt_debug(LL_DEBUG2, "Expunging mailbox\n");
1539 imap_expunge_mailbox(adata->mailbox, true);
1540 /* Detect whether we've gotten unexpected EXPUNGE messages */
1541 if (!(mdata->reopen & IMAP_EXPUNGE_EXPECTED))
1542 mdata->check_status |= IMAP_EXPUNGE_PENDING;
1544 }
1545
1546 // Then add new emails to it
1547 if (mdata->reopen & IMAP_NEWMAIL_PENDING)
1548 {
1549 const size_t max_msn = imap_msn_highest(&mdata->msn);
1550 if (mdata->new_mail_count > max_msn)
1551 {
1552 if (!(mdata->reopen & IMAP_EXPUNGE_PENDING))
1553 mdata->check_status |= IMAP_NEWMAIL_PENDING;
1554
1555 mutt_debug(LL_DEBUG2, "Fetching new mails from %zd to %u\n",
1556 max_msn + 1, mdata->new_mail_count);
1557 imap_read_headers(adata->mailbox, max_msn + 1, mdata->new_mail_count, false);
1558 }
1559 }
1560
1561 /* imap_read_headers may have triggered a fatal error that NULLed
1562 * adata->mailbox. Re-check before accessing mdata further. */
1563 if (!adata->mailbox)
1564 {
1565 adata->status = 0;
1566 return;
1567 }
1568
1569 // And to finish inform about MUTT_REOPEN if needed
1570 if (mdata->reopen & IMAP_EXPUNGE_PENDING && !(mdata->reopen & IMAP_EXPUNGE_EXPECTED))
1571 mdata->check_status |= IMAP_EXPUNGE_PENDING;
1572
1573 if (mdata->reopen & IMAP_EXPUNGE_PENDING)
1575 }
1576
1577 adata->status = 0;
1578}
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:1366
@ IMAP_SYNC_IN_PROGRESS
Sync is in progress, block expunge/newmail processing.
Definition private.h:73
@ IMAP_EXPUNGE_EXPECTED
Messages will be expunged from the server.
Definition private.h:69
@ IMAP_REOPEN_ALLOW
Allow re-opening a folder upon expunge.
Definition private.h:68
void imap_expunge_mailbox(struct Mailbox *m, bool resort)
Purge messages from the server.
Definition imap.c:849
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 1586 of file command.c.

1587{
1588 int rc;
1589
1590 mutt_debug(LL_DEBUG2, "Entering IDLE mode for %s\n",
1591 adata->conn ? adata->conn->account.host : "NULL");
1592
1593 if (cmd_start(adata, "IDLE", IMAP_CMD_POLL) < 0)
1594 {
1595 mutt_debug(LL_DEBUG1, "Failed to send IDLE command\n");
1596 cmd_handle_fatal(adata);
1597 return -1;
1598 }
1599
1600 const short c_imap_poll_timeout = cs_subset_number(NeoMutt->sub, "imap_poll_timeout");
1601 mutt_debug(LL_DEBUG2, "Waiting for IDLE continuation (timeout=%d)\n", c_imap_poll_timeout);
1602
1603 if ((c_imap_poll_timeout > 0) &&
1604 ((mutt_socket_poll(adata->conn, c_imap_poll_timeout)) == 0))
1605 {
1606 mutt_debug(LL_DEBUG1, "IDLE timed out waiting for server continuation response\n");
1607 mutt_error(_("Connection to %s timed out waiting for IDLE response"),
1608 adata->conn->account.host);
1609 cmd_handle_fatal(adata);
1610 return -1;
1611 }
1612
1613 do
1614 {
1615 rc = imap_cmd_step(adata);
1616 } while (rc == IMAP_RES_CONTINUE);
1617
1618 if (rc == IMAP_RES_RESPOND)
1619 {
1620 /* successfully entered IDLE state */
1621 adata->state = IMAP_IDLE;
1622 /* queue automatic exit when next command is issued */
1623 buf_addstr(&adata->cmdbuf, "DONE\r\n");
1624 mutt_debug(LL_DEBUG2, "Successfully entered IDLE state\n");
1625 rc = IMAP_RES_OK;
1626 }
1627 if (rc != IMAP_RES_OK)
1628 {
1629 mutt_debug(LL_DEBUG1, "IDLE command failed with rc=%d (expected RESPOND=%d)\n",
1630 rc, IMAP_RES_RESPOND);
1631 mutt_error(_("IDLE command failed for %s"), adata->conn->account.host);
1632 return -1;
1633 }
1634
1635 return 0;
1636}
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 79 of file command.c.

79 {
80 "IMAP4",
81 "IMAP4rev1",
82 "STATUS",
83 "ACL",
84 "NAMESPACE",
85 "AUTH=CRAM-MD5",
86 "AUTH=GSSAPI",
87 "AUTH=ANONYMOUS",
88 "AUTH=OAUTHBEARER",
89 "AUTH=XOAUTH2",
90 "STARTTLS",
91 "LOGINDISABLED",
92 "IDLE",
93 "SASL-IR",
94 "ENABLE",
95 "CONDSTORE",
96 "QRESYNC",
97 "LIST-EXTENDED",
98 "COMPRESS=DEFLATE",
99 "X-GM-EXT-1",
100 "ID",
101 NULL,
102};