NeoMutt  2025-12-11-694-ga89709
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:75
int nextcmd
Next command to be sent.
Definition adata.h:74
int cmdslots
Size of the command queue.
Definition adata.h:73
+ 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:56
struct ImapCommand * cmds
Queue of commands for the server.
Definition adata.h:72
unsigned int seqno
tag sequence number, e.g. '{seqid}0001'
Definition adata.h:57
IMAP command structure.
Definition private.h:160
int state
Command state, e.g. IMAP_RES_NEW.
Definition private.h:162
char seq[SEQ_LEN+1]
Command tag, e.g. 'a0001'.
Definition private.h:161
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ cmd_queue()

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

Add a IMAP command to the queue.

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

If the queue is full, attempts to drain it.

Definition at line 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:1415
static struct ImapCommand * cmd_new(struct ImapAccountData *adata)
Create and queue a new command control block.
Definition command.c:123
#define IMAP_CMD_POLL
Poll the tcp connection before running the imap command.
Definition private.h:74
@ IMAP_EXEC_ERROR
Imap command failure.
Definition private.h:83
#define IMAP_RES_BAD
<tag> BAD ...
Definition private.h:53
struct Buffer cmdbuf
Command queue.
Definition adata.h:76
+ 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 if ((adata->state >= IMAP_SELECTED) && (mdata->reopen & IMAP_REOPEN_ALLOW))
205 {
206 mx_fastclose_mailbox(adata->mailbox, true);
207 mutt_error(_("Mailbox %s@%s closed"), adata->conn->account.user,
208 adata->conn->account.host);
209 }
210
212 if (!adata->recovering)
213 {
214 adata->recovering = true;
215
216 /* Exponential backoff: 1s, 2s, 4s, 8s, 16s, max 30s */
217 if (adata->retry_count > 0)
218 {
219 unsigned int delay = (adata->retry_count < 32) ?
220 (1U << (adata->retry_count - 1)) :
222 if (delay > IMAP_MAX_BACKOFF)
223 delay = IMAP_MAX_BACKOFF;
224 mutt_message(_("Connection lost. Retrying in %u seconds..."), delay);
225 sleep(delay);
226 }
227
228 mutt_debug(LL_DEBUG1, "Attempting to reconnect to %s (attempt %d)\n",
229 adata->conn ? adata->conn->account.host : "NULL", adata->retry_count + 1);
230
231 if (imap_login(adata))
232 {
233 mutt_message(_("Reconnected to %s"), adata->conn->account.host);
234 adata->retry_count = 0; // Reset on success
235 }
236 else
237 {
238 adata->retry_count++;
239 mutt_debug(LL_DEBUG1, "Reconnection failed (attempt %d/%d)\n",
241
242 if (adata->retry_count >= IMAP_MAX_RETRIES)
243 {
244 mutt_error(_("Failed to reconnect to %s after %d attempts"),
245 adata->conn->account.host, adata->retry_count);
246 adata->retry_count = 0; // Reset for future attempts
247 }
248 else
249 {
250 mutt_error(_("Reconnection to %s failed. Will retry automatically."),
251 adata->conn->account.host);
252 }
253 }
254 adata->recovering = false;
255 }
256}
#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:108
#define IMAP_REOPEN_ALLOW
Allow re-opening a folder upon expunge.
Definition private.h:64
@ IMAP_FATAL
Unrecoverable error occurred.
Definition private.h:95
void imap_close_connection(struct ImapAccountData *adata)
Close an IMAP connection.
Definition imap.c:1029
int imap_login(struct ImapAccountData *adata)
Open an IMAP connection.
Definition imap.c:1969
@ LL_DEBUG1
Log at debug level 1.
Definition logging2.h:45
#define _(a)
Definition message.h:28
void mx_fastclose_mailbox(struct Mailbox *m, bool keep_account)
Free up memory associated with the Mailbox.
Definition mx.c:411
char user[128]
Username.
Definition connaccount.h:56
char host[128]
Server to login to.
Definition connaccount.h:54
struct ConnAccount account
Account details: username, password, etc.
Definition connection.h:49
int fd
Socket file descriptor.
Definition connection.h:53
unsigned char state
ImapState, e.g. IMAP_AUTHENTICATED.
Definition adata.h:44
struct Mailbox * mailbox
Current selected mailbox.
Definition adata.h:79
unsigned char status
ImapFlags, e.g. IMAP_FATAL.
Definition adata.h:45
unsigned char retry_count
Number of consecutive reconnection attempts.
Definition adata.h:67
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:131
int opened
Number of times mailbox is opened.
Definition mailbox.h:127
+ 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 266 of file command.c.

267{
268 int rc;
269
270 if (adata->status == IMAP_FATAL)
271 {
272 cmd_handle_fatal(adata);
273 return -1;
274 }
275
276 if (cmdstr && ((rc = cmd_queue(adata, cmdstr, flags)) < 0))
277 return rc;
278
279 if (flags & IMAP_CMD_QUEUE)
280 return 0;
281
282 if (buf_is_empty(&adata->cmdbuf))
283 return IMAP_RES_BAD;
284
285 rc = mutt_socket_send_d(adata->conn, adata->cmdbuf.data,
287 buf_reset(&adata->cmdbuf);
288
289 /* unidle when command queue is flushed */
290 if (adata->state == IMAP_IDLE)
291 adata->state = IMAP_SELECTED;
292
293 return (rc < 0) ? IMAP_RES_BAD : 0;
294}
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:111
#define IMAP_CMD_PASS
Command contains a password. Suppress logging.
Definition private.h:72
#define IMAP_LOG_PASS
Log passwords (dangerous!)
Definition private.h:49
#define IMAP_LOG_CMD
Log commands only.
Definition private.h:47
#define IMAP_CMD_QUEUE
Queue a command, do not execute.
Definition private.h:73
#define mutt_socket_send_d(conn, buf, dbg)
Definition socket.h: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 302 of file command.c.

303{
304 s = imap_next_word((char *) s);
305
306 if (mutt_istr_startswith(s, "OK"))
307 return IMAP_RES_OK;
308 if (mutt_istr_startswith(s, "NO"))
309 return IMAP_RES_NO;
310
311 return IMAP_RES_BAD;
312}
#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:826
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 322 of file command.c.

323{
324 unsigned int exp_msn = 0;
325 struct Email *e = NULL;
326
327 mutt_debug(LL_DEBUG2, "Handling EXPUNGE\n");
328
329 if (!adata->mailbox)
330 return;
331
332 struct ImapMboxData *mdata = adata->mailbox->mdata;
333 if (!mdata)
334 return;
335
336 if (!mutt_str_atoui(s, &exp_msn) || (exp_msn < 1) ||
337 (exp_msn > imap_msn_highest(&mdata->msn)))
338 {
339 return;
340 }
341
342 e = imap_msn_get(&mdata->msn, exp_msn - 1);
343 if (e)
344 {
345 /* imap_expunge_mailbox() will rewrite e->index.
346 * It needs to resort using EMAIL_SORT_UNSORTED anyway, so setting to INT_MAX
347 * makes the code simpler and possibly more efficient. */
348 e->index = INT_MAX;
349
351 if (edata)
352 edata->msn = 0;
353 }
354
355 /* decrement seqno of those above. */
356 const size_t max_msn = imap_msn_highest(&mdata->msn);
357 for (unsigned int cur = exp_msn; cur < max_msn; cur++)
358 {
359 e = imap_msn_get(&mdata->msn, cur);
360 if (e)
361 {
363 if (edata)
364 edata->msn--;
365 }
366 imap_msn_set(&mdata->msn, cur - 1, e);
367 }
368 imap_msn_shrink(&mdata->msn, 1);
369
371}
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
#define IMAP_EXPUNGE_PENDING
Messages on the server have been expunged.
Definition private.h:66
@ LL_DEBUG2
Log at debug level 2.
Definition logging2.h: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 381 of file command.c.

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

◆ cmd_parse_fetch()

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

Load fetch response into ImapAccountData.

Parameters
adataImap Account data
sString containing MSN of message to fetch

Currently only handles unanticipated FETCH responses, and only FLAGS data. We get these if another client has changed flags for a mailbox we've selected. Of course, a lot of code here duplicates code in message.c.

Definition at line 480 of file command.c.

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

632{
633 mutt_debug(LL_DEBUG3, "Handling CAPABILITY\n");
634
635 s = imap_next_word(s);
636 char *bracket = strchr(s, ']');
637 if (bracket)
638 *bracket = '\0';
639 FREE(&adata->capstr);
640 adata->capstr = mutt_str_dup(s);
641 adata->capabilities = 0;
642
643 while (*s)
644 {
645 for (size_t i = 0; Capabilities[i]; i++)
646 {
647 size_t len = mutt_istr_startswith(s, Capabilities[i]);
648 if (len != 0 && ((s[len] == '\0') || mutt_isspace(s[len])))
649 {
650 adata->capabilities |= (1 << i);
651 mutt_debug(LL_DEBUG3, " Found capability \"%s\": %zu\n", Capabilities[i], i);
652 break;
653 }
654 }
655 s = imap_next_word(s);
656 }
657}
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:55
char * capstr
Capability string from the server.
Definition adata.h:54
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ cmd_parse_list()

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

Parse a server LIST command (list mailboxes)

Parameters
adataImap Account data
sCommand string with folder list

Definition at line 664 of file command.c.

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

◆ cmd_parse_lsub()

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

Parse a server LSUB (list subscribed mailboxes)

Parameters
adataImap Account data
sCommand string with folder list

Definition at line 758 of file command.c.

759{
760 if (adata->cmdresult)
761 {
762 /* caller will handle response itself */
763 cmd_parse_list(adata, s);
764 return;
765 }
766
767 const bool c_imap_check_subscribed = cs_subset_bool(NeoMutt->sub, "imap_check_subscribed");
768 if (!c_imap_check_subscribed)
769 return;
770
771 struct ImapList list = { 0 };
772
773 adata->cmdresult = &list;
774 cmd_parse_list(adata, s);
775 adata->cmdresult = NULL;
776 /* noselect is for a gmail quirk */
777 if (!list.name || list.noselect)
778 return;
779
780 mutt_debug(LL_DEBUG3, "Subscribing to %s\n", list.name);
781
782 struct Buffer *buf = buf_pool_get();
783 struct Buffer *err = buf_pool_get();
784 struct Url url = { 0 };
785
786 account_to_url(&adata->conn->account, &url);
787 url.path = list.name;
788
789 const char *const c_imap_user = cs_subset_string(NeoMutt->sub, "imap_user");
790 if (mutt_str_equal(url.user, c_imap_user))
791 url.user = NULL;
792 url_tobuffer(&url, buf, U_NO_FLAGS);
793
794 if (!mailbox_add_simple(buf_string(buf), err))
795 mutt_debug(LL_DEBUG1, "Error adding subscribed mailbox: %s\n", buf_string(err));
796
797 buf_pool_release(&buf);
798 buf_pool_release(&err);
799}
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:664
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:358
#define U_NO_FLAGS
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 806 of file command.c.

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

878{
879 if (!adata || !adata->account || !name)
880 return NULL;
881
882 struct Mailbox **mp = NULL;
883 ARRAY_FOREACH(mp, &adata->account->mailboxes)
884 {
885 struct Mailbox *m = *mp;
886
887 struct ImapMboxData *mdata = imap_mdata_get(m);
888 if (mdata && mutt_str_equal(name, mdata->name))
889 return m;
890 }
891
892 return NULL;
893}
#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:81
char * name
Mailbox name.
Definition mdata.h:41
A mailbox.
Definition mailbox.h:78
+ 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 903 of file command.c.

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

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

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

◆ cmd_handle_untagged()

static int cmd_handle_untagged ( struct ImapAccountData * adata)
static

Fallback parser for otherwise unhandled messages.

Parameters
adataImap Account data
Return values
0Success
-1Failure

Definition at line 1115 of file command.c.

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

◆ imap_cmd_start()

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

Given an IMAP command, send it to the server.

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

If cmdstr is NULL, sends queued commands.

Definition at line 1211 of file command.c.

1212{
1213 return cmd_start(adata, cmdstr, IMAP_CMD_NO_FLAGS);
1214}
static int cmd_start(struct ImapAccountData *adata, const char *cmdstr, ImapCmdFlags flags)
Start a new IMAP command.
Definition command.c:266
#define IMAP_CMD_NO_FLAGS
No flags are set.
Definition private.h:71
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_cmd_step()

int imap_cmd_step ( struct ImapAccountData * adata)

Reads server responses from an IMAP command.

Parameters
adataImap Account data
Return values
0Success
<0Failure, e.g. IMAP_RES_BAD

detects tagged completion response, handles untagged messages, can read arbitrarily large strings (using malloc, so don't make it too large!).

Definition at line 1225 of file command.c.

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

1368{
1369 return cmd_status(s) == IMAP_RES_OK;
1370}
+ 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 1378 of file command.c.

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

1416{
1417 if (!adata)
1418 return IMAP_EXEC_ERROR;
1419
1420 /* Check connection health before executing command */
1421 if ((adata->state >= IMAP_AUTHENTICATED) && (adata->last_success > 0))
1422 {
1423 time_t now = mutt_date_now();
1424 time_t idle_time = now - adata->last_success;
1425
1426 if (idle_time > IMAP_CONN_STALE_THRESHOLD)
1427 {
1428 mutt_debug(LL_DEBUG2, "Connection idle for %ld seconds, sending NOOP to verify\n",
1429 (long) idle_time);
1430 /* Connection may be stale - let the command proceed and handle any error */
1431 }
1432 }
1433
1434 if (flags & IMAP_CMD_SINGLE)
1435 {
1436 // Process any existing commands
1437 if (adata->nextcmd != adata->lastcmd)
1438 imap_exec(adata, NULL, IMAP_CMD_POLL);
1439 }
1440
1441 int rc = cmd_start(adata, cmdstr, flags);
1442 if (rc < 0)
1443 {
1444 cmd_handle_fatal(adata);
1445 return IMAP_EXEC_FATAL;
1446 }
1447
1448 if (flags & IMAP_CMD_QUEUE)
1449 return IMAP_EXEC_SUCCESS;
1450
1451 const short c_imap_poll_timeout = cs_subset_number(NeoMutt->sub, "imap_poll_timeout");
1452 if ((flags & IMAP_CMD_POLL) && (c_imap_poll_timeout > 0) &&
1453 ((mutt_socket_poll(adata->conn, c_imap_poll_timeout)) == 0))
1454 {
1455 mutt_error(_("Connection to %s timed out"), adata->conn->account.host);
1456 cmd_handle_fatal(adata);
1457 return IMAP_EXEC_FATAL;
1458 }
1459
1460 /* Allow interruptions, particularly useful if there are network problems. */
1462 do
1463 {
1464 rc = imap_cmd_step(adata);
1465 // The queue is empty, so the single command has been processed
1466 if ((flags & IMAP_CMD_SINGLE) && (adata->nextcmd == adata->lastcmd))
1467 break;
1468 } while (rc == IMAP_RES_CONTINUE);
1470
1471 if (rc == IMAP_RES_NO)
1472 return IMAP_EXEC_ERROR;
1473 if (rc != IMAP_RES_OK)
1474 {
1475 if (adata->status != IMAP_FATAL)
1476 return IMAP_EXEC_ERROR;
1477
1478 mutt_debug(LL_DEBUG1, "command failed: %s\n", adata->buf);
1479 return IMAP_EXEC_FATAL;
1480 }
1481
1482 /* Track successful command completion for connection health monitoring */
1483 adata->last_success = mutt_date_now();
1484
1485 return IMAP_EXEC_SUCCESS;
1486}
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:107
@ IMAP_EXEC_SUCCESS
Imap command executed or queued successfully.
Definition private.h:82
@ IMAP_EXEC_FATAL
Imap connection failure.
Definition private.h:84
#define IMAP_CMD_SINGLE
Run a single command.
Definition private.h:75
void mutt_sig_allow_interrupt(bool allow)
Allow/disallow Ctrl-C (SIGINT)
Definition signal.c: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:59
+ 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 1498 of file command.c.

1499{
1500 if (!adata)
1501 return;
1502
1503 if (adata->status == IMAP_FATAL)
1504 {
1505 adata->closing = false;
1506 cmd_handle_fatal(adata);
1507 return;
1508 }
1509
1510 if (!(adata->state >= IMAP_SELECTED) || !adata->mailbox || adata->closing)
1511 {
1512 adata->closing = false;
1513 return;
1514 }
1515
1516 adata->closing = false;
1517
1518 struct ImapMboxData *mdata = imap_mdata_get(adata->mailbox);
1519
1520 if (mdata && mdata->reopen & IMAP_REOPEN_ALLOW)
1521 {
1522 // First remove expunged emails from the msn_index
1523 if (mdata->reopen & IMAP_EXPUNGE_PENDING)
1524 {
1525 mutt_debug(LL_DEBUG2, "Expunging mailbox\n");
1526 imap_expunge_mailbox(adata->mailbox, true);
1527 /* Detect whether we've gotten unexpected EXPUNGE messages */
1528 if (!(mdata->reopen & IMAP_EXPUNGE_EXPECTED))
1529 mdata->check_status |= IMAP_EXPUNGE_PENDING;
1531 }
1532
1533 // Then add new emails to it
1534 if (mdata->reopen & IMAP_NEWMAIL_PENDING)
1535 {
1536 const size_t max_msn = imap_msn_highest(&mdata->msn);
1537 if (mdata->new_mail_count > max_msn)
1538 {
1539 if (!(mdata->reopen & IMAP_EXPUNGE_PENDING))
1540 mdata->check_status |= IMAP_NEWMAIL_PENDING;
1541
1542 mutt_debug(LL_DEBUG2, "Fetching new mails from %zd to %u\n",
1543 max_msn + 1, mdata->new_mail_count);
1544 imap_read_headers(adata->mailbox, max_msn + 1, mdata->new_mail_count, false);
1545 }
1546 }
1547
1548 /* imap_read_headers may have triggered a fatal error that NULLed
1549 * adata->mailbox. Re-check before accessing mdata further. */
1550 if (!adata->mailbox)
1551 {
1552 adata->status = 0;
1553 return;
1554 }
1555
1556 // And to finish inform about MUTT_REOPEN if needed
1557 if (mdata->reopen & IMAP_EXPUNGE_PENDING && !(mdata->reopen & IMAP_EXPUNGE_EXPECTED))
1558 mdata->check_status |= IMAP_EXPUNGE_PENDING;
1559
1560 if (mdata->reopen & IMAP_EXPUNGE_PENDING)
1562 }
1563
1564 adata->status = 0;
1565}
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:1363
#define IMAP_EXPUNGE_EXPECTED
Messages will be expunged from the server.
Definition private.h:65
void imap_expunge_mailbox(struct Mailbox *m, bool resort)
Purge messages from the server.
Definition imap.c: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 1573 of file command.c.

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