NeoMutt  2025-12-11-1052-ga8f1ca
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 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 ImapCommand * cmd_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 Mailbox * find_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 62 of file command.c.

◆ IMAP_MAX_RETRIES

#define IMAP_MAX_RETRIES   5

Maximum number of reconnection attempts before giving up.

Definition at line 65 of file command.c.

◆ IMAP_MAX_BACKOFF

#define IMAP_MAX_BACKOFF   30

Maximum backoff delay in seconds between reconnection attempts.

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

Function Documentation

◆ cmd_queue_full()

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

109{
110 if (((adata->nextcmd + 1) % adata->cmdslots) == adata->lastcmd)
111 return true;
112
113 return false;
114}
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()

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

123{
124 struct ImapCommand *cmd = NULL;
125
126 if (cmd_queue_full(adata))
127 {
128 mutt_debug(LL_DEBUG3, "IMAP command queue full\n");
129 return NULL;
130 }
131
132 cmd = adata->cmds + adata->nextcmd;
133 adata->nextcmd = (adata->nextcmd + 1) % adata->cmdslots;
134
135 snprintf(cmd->seq, sizeof(cmd->seq), "%c%04u", adata->seqid, adata->seqno++);
136 if (adata->seqno > 9999)
137 adata->seqno = 0;
138
139 cmd->state = IMAP_RES_NEW;
140
141 return cmd;
142}
#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:108
#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()

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

155{
156 if (cmd_queue_full(adata))
157 {
158 mutt_debug(LL_DEBUG3, "Draining IMAP command pipeline\n");
159
160 const int rc = imap_exec(adata, NULL, IMAP_CMD_POLL);
161
162 if (rc == IMAP_EXEC_ERROR)
163 return IMAP_RES_BAD;
164 }
165
166 struct ImapCommand *cmd = cmd_new(adata);
167 if (!cmd)
168 return IMAP_RES_BAD;
169
170 if (buf_add_printf(&adata->cmdbuf, "%s %s\r\n", cmd->seq, cmdstr) < 0)
171 return IMAP_RES_BAD;
172
173 return 0;
174}
int buf_add_printf(struct Buffer *buf, const char *fmt,...)
Format a string appending a Buffer.
Definition buffer.c:211
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:1423
static struct ImapCommand * cmd_new(struct ImapAccountData *adata)
Create and queue a new command control block.
Definition command.c:122
@ 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()

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

184{
185 adata->status = IMAP_FATAL;
186
187 mutt_debug(LL_DEBUG1, "state=%d, status=%d, recovering=%d, retries=%d\n",
188 adata->state, adata->status, adata->recovering, adata->retry_count);
189 mutt_debug(LL_DEBUG1, "Connection: fd=%d, host=%s\n",
190 adata->conn ? adata->conn->fd : -1,
191 adata->conn ? adata->conn->account.host : "NULL");
192
193 if (!adata->mailbox)
194 return;
195
196 mutt_debug(LL_DEBUG1, "adata->mailbox=%p, opened=%d\n",
197 (void *) adata->mailbox, adata->mailbox->opened);
198
199 struct ImapMboxData *mdata = adata->mailbox->mdata;
200 if (!mdata)
201 return;
202
203 bool was_selected = (adata->state >= IMAP_SELECTED);
204
206 if (!adata->recovering)
207 {
208 adata->recovering = true;
209
210 /* Exponential backoff: 1s, 2s, 4s, 8s, 16s, max 30s */
211 if (adata->retry_count > 0)
212 {
213 unsigned int delay = (adata->retry_count < 32) ?
214 (1U << (adata->retry_count - 1)) :
216 if (delay > IMAP_MAX_BACKOFF)
217 delay = IMAP_MAX_BACKOFF;
218 mutt_message(_("Connection lost. Retrying in %u seconds..."), delay);
219 sleep(delay);
220 }
221
222 mutt_debug(LL_DEBUG1, "Attempting to reconnect to %s (attempt %d)\n",
223 adata->conn ? adata->conn->account.host : "NULL", adata->retry_count + 1);
224
225 if (imap_login(adata))
226 {
227 mutt_message(_("Reconnected to %s"), adata->conn->account.host);
228 adata->retry_count = 0; // Reset on success
229
230 if (was_selected && (imap_reopen_mailbox(adata) == 0))
231 {
232 mutt_message(_("Reopened mailbox on %s"), adata->conn->account.host);
233 }
234 else if (was_selected)
235 {
236 mutt_error(_("Reconnected to %s but failed to reopen mailbox"),
237 adata->conn->account.host);
238 }
239 }
240 else
241 {
242 adata->retry_count++;
243 mutt_debug(LL_DEBUG1, "Reconnection failed (attempt %d/%d)\n",
245
246 if (adata->retry_count >= IMAP_MAX_RETRIES)
247 {
248 mutt_error(_("Failed to reconnect to %s after %d attempts"),
249 adata->conn->account.host, adata->retry_count);
250 adata->retry_count = 0; // Reset for future attempts
251 }
252 else
253 {
254 mutt_error(_("Reconnection to %s failed. Will retry automatically."),
255 adata->conn->account.host);
256 }
257 }
258 adata->recovering = false;
259 }
260}
#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:65
#define IMAP_MAX_BACKOFF
Maximum backoff delay in seconds between reconnection attempts.
Definition command.c:68
@ 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:2263
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()

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

271{
272 int rc;
273
274 if (adata->status == IMAP_FATAL)
275 {
276 cmd_handle_fatal(adata);
277 return -1;
278 }
279
280 if (cmdstr && ((rc = cmd_queue(adata, cmdstr, flags)) < 0))
281 return rc;
282
283 if (flags & IMAP_CMD_QUEUE)
284 return 0;
285
286 if (buf_is_empty(&adata->cmdbuf))
287 return IMAP_RES_BAD;
288
289 rc = mutt_socket_send_d(adata->conn, adata->cmdbuf.data,
291 buf_reset(&adata->cmdbuf);
292
293 /* unidle when command queue is flushed */
294 if (adata->state == IMAP_IDLE)
295 adata->state = IMAP_SELECTED;
296
297 return (rc < 0) ? IMAP_RES_BAD : 0;
298}
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition buffer.c:89
bool buf_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition buffer.c:298
static int cmd_queue(struct ImapAccountData *adata, const char *cmdstr, ImapCmdFlags flags)
Add a IMAP command to the queue.
Definition command.c:154
static void cmd_handle_fatal(struct ImapAccountData *adata)
When ImapAccountData is in fatal state, do what we can.
Definition command.c:183
@ 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:38
Here is the call graph for this function:
Here is the caller graph for this function:

◆ cmd_status()

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

307{
308 s = imap_next_word((char *) s);
309
310 if (mutt_istr_startswith(s, "OK"))
311 return IMAP_RES_OK;
312 if (mutt_istr_startswith(s, "NO"))
313 return IMAP_RES_NO;
314
315 return IMAP_RES_BAD;
316}
#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:831
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()

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

327{
328 unsigned int exp_msn = 0;
329 struct Email *e = NULL;
330
331 mutt_debug(LL_DEBUG2, "Handling EXPUNGE\n");
332
333 if (!adata->mailbox)
334 return;
335
336 struct ImapMboxData *mdata = adata->mailbox->mdata;
337 if (!mdata)
338 return;
339
340 if (!mutt_str_atoui(s, &exp_msn) || (exp_msn < 1) ||
341 (exp_msn > imap_msn_highest(&mdata->msn)))
342 {
343 return;
344 }
345
346 e = imap_msn_get(&mdata->msn, exp_msn - 1);
347 if (e)
348 {
349 /* imap_expunge_mailbox() will rewrite e->index.
350 * It needs to resort using EMAIL_SORT_UNSORTED anyway, so setting to INT_MAX
351 * makes the code simpler and possibly more efficient. */
352 e->index = INT_MAX;
353
355 if (edata)
356 edata->msn = 0;
357 }
358
359 /* decrement seqno of those above. */
360 const size_t max_msn = imap_msn_highest(&mdata->msn);
361 for (unsigned int cur = exp_msn; cur < max_msn; cur++)
362 {
363 e = imap_msn_get(&mdata->msn, cur);
364 if (e)
365 {
367 if (edata)
368 edata->msn--;
369 }
370 imap_msn_set(&mdata->msn, cur - 1, e);
371 }
372 imap_msn_shrink(&mdata->msn, 1);
373
375}
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()

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

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

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

485{
486 unsigned int msn = 0;
487 unsigned int uid = 0;
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:1958
@ 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()

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:78
#define FREE(x)
Free memory and set the pointer to NULL.
Definition memory.h:92
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()

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 = 0;
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:1233
void imap_unmunge_mbox_name(bool unicode, char *s)
Remove quoting from a mailbox name.
Definition util.c:990
void imap_unquote_string(char *s)
Equally stupid unquoting routine.
Definition util.c:936
int imap_get_literal_count(char *buf, unsigned int *bytes)
Write number of bytes in an IMAP literal into bytes.
Definition util.c:787
#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()

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:99
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:666
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:37
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()

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()

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()

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 if (s[0] != '\0')
932 {
933 s[0] = '\0';
934 s++;
935 SKIPWS(s);
936 }
937 }
938 else
939 {
940 s = imap_next_word(mailbox);
941 s[-1] = '\0';
942 imap_unmunge_mbox_name(adata->unicode, mailbox);
943 }
944
945 struct Mailbox *m = find_mailbox(adata, mailbox);
946 struct ImapMboxData *mdata = imap_mdata_get(m);
947 if (!mdata)
948 {
949 mutt_debug(LL_DEBUG3, "Received status for an unexpected mailbox: %s\n", mailbox);
950 return;
951 }
952 uint32_t olduv = mdata->uidvalidity;
953 unsigned int oldun = mdata->uid_next;
954
955 if (*s++ != '(')
956 {
957 mutt_debug(LL_DEBUG1, "Error parsing STATUS\n");
958 return;
959 }
960 while ((s[0] != '\0') && (s[0] != ')'))
961 {
962 char *value = imap_next_word(s);
963
964 errno = 0;
965 const unsigned long ulcount = strtoul(value, &value, 10);
966 const bool truncated = ((errno == ERANGE) && (ulcount == ULONG_MAX)) ||
967 ((unsigned int) ulcount != ulcount);
968 const unsigned int count = (unsigned int) ulcount;
969
970 // we accept truncating a larger value only for UIDVALIDITY, to accommodate
971 // IMAP servers that use 64-bits for it. This seems to be what Thunderbird
972 // is also doing, see #3830
973 if (mutt_str_startswith(s, "UIDVALIDITY"))
974 {
975 if (truncated)
976 {
978 "UIDVALIDITY [%lu] exceeds 32 bits, "
979 "truncated to [%u]\n",
980 ulcount, count);
981 }
982 mdata->uidvalidity = count;
983 }
984 else
985 {
986 if (truncated)
987 {
988 mutt_debug(LL_DEBUG1, "Number in [%s] exceeds 32 bits\n", s);
989 return;
990 }
991 else
992 {
993 if (mutt_str_startswith(s, "MESSAGES"))
994 mdata->messages = count;
995 else if (mutt_str_startswith(s, "RECENT"))
996 mdata->recent = count;
997 else if (mutt_str_startswith(s, "UIDNEXT"))
998 mdata->uid_next = count;
999 else if (mutt_str_startswith(s, "UNSEEN"))
1000 mdata->unseen = count;
1001 }
1002 }
1003
1004 s = value;
1005 if ((s[0] != '\0') && (*s != ')'))
1006 s = imap_next_word(s);
1007 }
1008 mutt_debug(LL_DEBUG3, "%s (UIDVALIDITY: %u, UIDNEXT: %u) %d messages, %d recent, %d unseen\n",
1009 mdata->name, mdata->uidvalidity, mdata->uid_next, mdata->messages,
1010 mdata->recent, mdata->unseen);
1011
1012 mutt_debug(LL_DEBUG3, "Running default STATUS handler\n");
1013
1014 mutt_debug(LL_DEBUG3, "Found %s in mailbox list (OV: %u ON: %u U: %d)\n",
1015 mailbox, olduv, oldun, mdata->unseen);
1016
1017 bool new_mail = false;
1018 const bool c_mail_check_recent = cs_subset_bool(NeoMutt->sub, "mail_check_recent");
1019 if (c_mail_check_recent)
1020 {
1021 if ((olduv != 0) && (olduv == mdata->uidvalidity))
1022 {
1023 if (oldun < mdata->uid_next)
1024 new_mail = (mdata->unseen > 0);
1025 }
1026 else if ((olduv == 0) && (oldun == 0))
1027 {
1028 /* first check per session, use recent. might need a flag for this. */
1029 new_mail = (mdata->recent > 0);
1030 }
1031 else
1032 {
1033 new_mail = (mdata->unseen > 0);
1034 }
1035 }
1036 else
1037 {
1038 new_mail = (mdata->unseen > 0);
1039 }
1040
1041 m->has_new = new_mail;
1042 m->msg_count = mdata->messages;
1043 m->msg_unread = mdata->unseen;
1044
1045 // force back to keep detecting new mail until the mailbox is opened
1046 if (m->has_new)
1047 mdata->uid_next = oldun;
1048
1049 struct EventMailbox ev_m = { m };
1051}
@ 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()

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

1059{
1060 mutt_debug(LL_DEBUG2, "Handling ENABLED\n");
1061
1062 while ((s = imap_next_word((char *) s)) && (*s != '\0'))
1063 {
1064 if (mutt_istr_startswith(s, "UTF8=ACCEPT") || mutt_istr_startswith(s, "UTF8=ONLY"))
1065 {
1066 adata->unicode = true;
1067 }
1068 if (mutt_istr_startswith(s, "QRESYNC"))
1069 adata->qresync = true;
1070 }
1071}
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()

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

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

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

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

1220{
1221 return cmd_start(adata, cmdstr, IMAP_CMD_NONE);
1222}
static int cmd_start(struct ImapAccountData *adata, const char *cmdstr, ImapCmdFlags flags)
Start a new IMAP command.
Definition command.c:270
@ 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 1233 of file command.c.

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

1376{
1377 return cmd_status(s) == IMAP_RES_OK;
1378}
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 1386 of file command.c.

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

1424{
1425 if (!adata)
1426 return IMAP_EXEC_ERROR;
1427
1428 /* Check connection health before executing command */
1429 if ((adata->state >= IMAP_AUTHENTICATED) && (adata->last_success > 0))
1430 {
1431 time_t now = mutt_date_now();
1432 time_t idle_time = now - adata->last_success;
1433
1434 if (idle_time > IMAP_CONN_STALE_THRESHOLD)
1435 {
1436 static bool probing = false;
1437 if (!probing)
1438 {
1439 mutt_debug(LL_DEBUG2, "Connection idle for %ld seconds, sending NOOP probe\n",
1440 (long) idle_time);
1441 probing = true;
1442 int noop_rc = imap_exec(adata, "NOOP", IMAP_CMD_POLL);
1443 probing = false;
1444 if (noop_rc == IMAP_EXEC_FATAL)
1445 return IMAP_EXEC_FATAL;
1446 }
1447 }
1448 }
1449
1450 if (flags & IMAP_CMD_SINGLE)
1451 {
1452 // Process any existing commands
1453 if (adata->nextcmd != adata->lastcmd)
1454 imap_exec(adata, NULL, IMAP_CMD_POLL);
1455 }
1456
1457 int rc = cmd_start(adata, cmdstr, flags);
1458 if (rc < 0)
1459 {
1460 cmd_handle_fatal(adata);
1461 return IMAP_EXEC_FATAL;
1462 }
1463
1464 if (flags & IMAP_CMD_QUEUE)
1465 return IMAP_EXEC_SUCCESS;
1466
1467 const short c_imap_poll_timeout = cs_subset_number(NeoMutt->sub, "imap_poll_timeout");
1468 if ((flags & IMAP_CMD_POLL) && (c_imap_poll_timeout > 0) &&
1469 ((mutt_socket_poll(adata->conn, c_imap_poll_timeout)) == 0))
1470 {
1471 mutt_error(_("Connection to %s timed out"), adata->conn->account.host);
1472 cmd_handle_fatal(adata);
1473 return IMAP_EXEC_FATAL;
1474 }
1475
1476 /* Allow interruptions, particularly useful if there are network problems. */
1478 do
1479 {
1480 rc = imap_cmd_step(adata);
1481 // The queue is empty, so the single command has been processed
1482 if ((flags & IMAP_CMD_SINGLE) && (adata->nextcmd == adata->lastcmd))
1483 break;
1484 } while (rc == IMAP_RES_CONTINUE);
1486
1487 if (rc == IMAP_RES_NO)
1488 return IMAP_EXEC_ERROR;
1489 if (rc != IMAP_RES_OK)
1490 {
1491 if (adata->status != IMAP_FATAL)
1492 return IMAP_EXEC_ERROR;
1493
1494 mutt_debug(LL_DEBUG1, "command failed: %s\n", adata->buf);
1495 return IMAP_EXEC_FATAL;
1496 }
1497
1498 /* Track successful command completion for connection health monitoring */
1499 adata->last_success = mutt_date_now();
1500
1501 return IMAP_EXEC_SUCCESS;
1502}
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:71
@ 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 1514 of file command.c.

1515{
1516 if (!adata)
1517 return;
1518
1519 if (adata->status == IMAP_FATAL)
1520 {
1521 adata->closing = false;
1522 cmd_handle_fatal(adata);
1523 return;
1524 }
1525
1526 if (!(adata->state >= IMAP_SELECTED) || !adata->mailbox || adata->closing)
1527 {
1528 adata->closing = false;
1529 return;
1530 }
1531
1532 adata->closing = false;
1533
1534 struct ImapMboxData *mdata = imap_mdata_get(adata->mailbox);
1535
1536 if (mdata && (mdata->reopen & IMAP_REOPEN_ALLOW) && !(mdata->reopen & IMAP_SYNC_IN_PROGRESS))
1537 {
1538 // First remove expunged emails from the msn_index
1539 if (mdata->reopen & IMAP_EXPUNGE_PENDING)
1540 {
1541 mutt_debug(LL_DEBUG2, "Expunging mailbox\n");
1542 imap_expunge_mailbox(adata->mailbox, true);
1543 /* Detect whether we've gotten unexpected EXPUNGE messages */
1544 if (!(mdata->reopen & IMAP_EXPUNGE_EXPECTED))
1545 mdata->check_status |= IMAP_EXPUNGE_PENDING;
1547 }
1548
1549 // Then add new emails to it
1550 if (mdata->reopen & IMAP_NEWMAIL_PENDING)
1551 {
1552 const size_t max_msn = imap_msn_highest(&mdata->msn);
1553 if (mdata->new_mail_count > max_msn)
1554 {
1555 if (!(mdata->reopen & IMAP_EXPUNGE_PENDING))
1556 mdata->check_status |= IMAP_NEWMAIL_PENDING;
1557
1558 mutt_debug(LL_DEBUG2, "Fetching new mails from %zd to %u\n",
1559 max_msn + 1, mdata->new_mail_count);
1560 imap_read_headers(adata->mailbox, max_msn + 1, mdata->new_mail_count, false);
1561 }
1562 }
1563
1564 /* imap_read_headers may have triggered a fatal error that NULLed
1565 * adata->mailbox. Re-check before accessing mdata further. */
1566 if (!adata->mailbox)
1567 {
1568 adata->status = 0;
1569 return;
1570 }
1571
1572 // And to finish inform about MUTT_REOPEN if needed
1573 if (mdata->reopen & IMAP_EXPUNGE_PENDING && !(mdata->reopen & IMAP_EXPUNGE_EXPECTED))
1574 mdata->check_status |= IMAP_EXPUNGE_PENDING;
1575
1576 if (mdata->reopen & IMAP_EXPUNGE_PENDING)
1578 }
1579
1580 adata->status = 0;
1581}
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:1370
@ 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 1589 of file command.c.

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

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