NeoMutt  2025-12-11-276-g10b23b
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, flags & 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:1400
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 struct ImapMboxData *mdata = adata->mailbox->mdata;
198 if (!mdata)
199 return;
200
201 if ((adata->state >= IMAP_SELECTED) && (mdata->reopen & IMAP_REOPEN_ALLOW))
202 {
203 mx_fastclose_mailbox(adata->mailbox, true);
204 mutt_error(_("Mailbox %s@%s closed"), adata->conn->account.user,
205 adata->conn->account.host);
206 }
207
209 if (!adata->recovering)
210 {
211 adata->recovering = true;
212
213 /* Exponential backoff: 1s, 2s, 4s, 8s, 16s, max 30s */
214 if (adata->retry_count > 0)
215 {
216 unsigned int delay = (adata->retry_count < 32) ?
217 (1U << (adata->retry_count - 1)) :
219 if (delay > IMAP_MAX_BACKOFF)
220 delay = IMAP_MAX_BACKOFF;
221 mutt_message(_("Connection lost. Retrying in %u seconds..."), delay);
222 sleep(delay);
223 }
224
225 mutt_debug(LL_DEBUG1, "Attempting to reconnect to %s (attempt %d)\n",
226 adata->conn ? adata->conn->account.host : "NULL", adata->retry_count + 1);
227
228 if (imap_login(adata))
229 {
230 mutt_message(_("Reconnected to %s"), adata->conn->account.host);
231 adata->retry_count = 0; // Reset on success
232 }
233 else
234 {
235 adata->retry_count++;
236 mutt_debug(LL_DEBUG1, "Reconnection failed (attempt %d/%d)\n",
238
239 if (adata->retry_count >= IMAP_MAX_RETRIES)
240 {
241 mutt_error(_("Failed to reconnect to %s after %d attempts"),
242 adata->conn->account.host, adata->retry_count);
243 adata->retry_count = 0; // Reset for future attempts
244 }
245 else
246 {
247 mutt_error(_("Reconnection to %s failed. Will retry automatically."),
248 adata->conn->account.host);
249 }
250 }
251 adata->recovering = false;
252 }
253}
#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
+ 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 263 of file command.c.

264{
265 int rc;
266
267 if (adata->status == IMAP_FATAL)
268 {
269 cmd_handle_fatal(adata);
270 return -1;
271 }
272
273 if (cmdstr && ((rc = cmd_queue(adata, cmdstr, flags)) < 0))
274 return rc;
275
276 if (flags & IMAP_CMD_QUEUE)
277 return 0;
278
279 if (buf_is_empty(&adata->cmdbuf))
280 return IMAP_RES_BAD;
281
282 rc = mutt_socket_send_d(adata->conn, adata->cmdbuf.data,
284 buf_reset(&adata->cmdbuf);
285
286 /* unidle when command queue is flushed */
287 if (adata->state == IMAP_IDLE)
288 adata->state = IMAP_SELECTED;
289
290 return (rc < 0) ? IMAP_RES_BAD : 0;
291}
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 299 of file command.c.

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

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

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

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

623{
624 mutt_debug(LL_DEBUG3, "Handling CAPABILITY\n");
625
626 s = imap_next_word(s);
627 char *bracket = strchr(s, ']');
628 if (bracket)
629 *bracket = '\0';
630 FREE(&adata->capstr);
631 adata->capstr = mutt_str_dup(s);
632 adata->capabilities = 0;
633
634 while (*s)
635 {
636 for (size_t i = 0; Capabilities[i]; i++)
637 {
638 size_t len = mutt_istr_startswith(s, Capabilities[i]);
639 if (len != 0 && ((s[len] == '\0') || mutt_isspace(s[len])))
640 {
641 adata->capabilities |= (1 << i);
642 mutt_debug(LL_DEBUG3, " Found capability \"%s\": %zu\n", Capabilities[i], i);
643 break;
644 }
645 }
646 s = imap_next_word(s);
647 }
648}
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 655 of file command.c.

656{
657 struct ImapList *list = NULL;
658 struct ImapList lb = { 0 };
659 unsigned int litlen;
660
661 if (adata->cmdresult)
662 list = adata->cmdresult;
663 else
664 list = &lb;
665
666 memset(list, 0, sizeof(struct ImapList));
667
668 /* flags */
669 s = imap_next_word(s);
670 if (*s != '(')
671 {
672 mutt_debug(LL_DEBUG1, "Bad LIST response\n");
673 return;
674 }
675 s++;
676 while (*s)
677 {
678 if (mutt_istr_startswith(s, "\\NoSelect"))
679 list->noselect = true;
680 else if (mutt_istr_startswith(s, "\\NonExistent")) /* rfc5258 */
681 list->noselect = true;
682 else if (mutt_istr_startswith(s, "\\NoInferiors"))
683 list->noinferiors = true;
684 else if (mutt_istr_startswith(s, "\\HasNoChildren")) /* rfc5258*/
685 list->noinferiors = true;
686
687 s = imap_next_word(s);
688 if (*(s - 2) == ')')
689 break;
690 }
691
692 /* Delimiter */
693 if (!mutt_istr_startswith(s, "NIL"))
694 {
695 char delimbuf[5] = { 0 }; // worst case: "\\"\0
696 snprintf(delimbuf, sizeof(delimbuf), "%s", s);
697 imap_unquote_string(delimbuf);
698 list->delim = delimbuf[0];
699 }
700
701 /* Name */
702 s = imap_next_word(s);
703 /* Notes often responds with literals here. We need a real tokenizer. */
704 if (imap_get_literal_count(s, &litlen) == 0)
705 {
706 if (imap_cmd_step(adata) != IMAP_RES_CONTINUE)
707 {
708 adata->status = IMAP_FATAL;
709 return;
710 }
711
712 if (strlen(adata->buf) < litlen)
713 {
714 mutt_debug(LL_DEBUG1, "Error parsing LIST mailbox\n");
715 return;
716 }
717
718 list->name = adata->buf;
719 s = list->name + litlen;
720 if (s[0] != '\0')
721 {
722 s[0] = '\0';
723 s++;
724 SKIPWS(s);
725 }
726 }
727 else
728 {
729 list->name = s;
730 /* Exclude rfc5258 RECURSIVEMATCH CHILDINFO suffix */
731 s = imap_next_word(s);
732 if (s[0] != '\0')
733 s[-1] = '\0';
734 imap_unmunge_mbox_name(adata->unicode, list->name);
735 }
736
737 if (list->name[0] == '\0')
738 {
739 adata->delim = list->delim;
740 mutt_debug(LL_DEBUG3, "Root delimiter: %c\n", adata->delim);
741 }
742}
int imap_cmd_step(struct ImapAccountData *adata)
Reads server responses from an IMAP command.
Definition command.c:1210
void imap_unmunge_mbox_name(bool unicode, char *s)
Remove quoting from a mailbox name.
Definition util.c:978
int imap_get_literal_count(const char *buf, unsigned int *bytes)
Write number of bytes in an IMAP literal into bytes.
Definition util.c:781
void imap_unquote_string(char *s)
Equally stupid unquoting routine.
Definition util.c:924
#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 749 of file command.c.

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

798{
799 mutt_debug(LL_DEBUG2, "Handling MYRIGHTS\n");
800
801 s = imap_next_word((char *) s);
802 s = imap_next_word((char *) s);
803
804 /* zero out current rights set */
805 adata->mailbox->rights = 0;
806
807 while (*s && !mutt_isspace(*s))
808 {
809 switch (*s)
810 {
811 case 'a':
812 adata->mailbox->rights |= MUTT_ACL_ADMIN;
813 break;
814 case 'e':
816 break;
817 case 'i':
818 adata->mailbox->rights |= MUTT_ACL_INSERT;
819 break;
820 case 'k':
821 adata->mailbox->rights |= MUTT_ACL_CREATE;
822 break;
823 case 'l':
824 adata->mailbox->rights |= MUTT_ACL_LOOKUP;
825 break;
826 case 'p':
827 adata->mailbox->rights |= MUTT_ACL_POST;
828 break;
829 case 'r':
830 adata->mailbox->rights |= MUTT_ACL_READ;
831 break;
832 case 's':
833 adata->mailbox->rights |= MUTT_ACL_SEEN;
834 break;
835 case 't':
836 adata->mailbox->rights |= MUTT_ACL_DELETE;
837 break;
838 case 'w':
839 adata->mailbox->rights |= MUTT_ACL_WRITE;
840 break;
841 case 'x':
842 adata->mailbox->rights |= MUTT_ACL_DELMX;
843 break;
844
845 /* obsolete rights */
846 case 'c':
848 break;
849 case 'd':
851 break;
852 default:
853 mutt_debug(LL_DEBUG1, "Unknown right: %c\n", *s);
854 }
855 s++;
856 }
857}
#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 865 of file command.c.

866{
867 if (!adata || !adata->account || !name)
868 return NULL;
869
870 struct Mailbox **mp = NULL;
871 ARRAY_FOREACH(mp, &adata->account->mailboxes)
872 {
873 struct Mailbox *m = *mp;
874
875 struct ImapMboxData *mdata = imap_mdata_get(m);
876 if (mdata && mutt_str_equal(name, mdata->name))
877 return m;
878 }
879
880 return NULL;
881}
#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 891 of file command.c.

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

1039{
1040 mutt_debug(LL_DEBUG2, "Handling ENABLED\n");
1041
1042 while ((s = imap_next_word((char *) s)) && (*s != '\0'))
1043 {
1044 if (mutt_istr_startswith(s, "UTF8=ACCEPT") || mutt_istr_startswith(s, "UTF8=ONLY"))
1045 {
1046 adata->unicode = true;
1047 }
1048 if (mutt_istr_startswith(s, "QRESYNC"))
1049 adata->qresync = true;
1050 }
1051}
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 1058 of file command.c.

1059{
1060 unsigned int count = 0;
1061 mutt_debug(LL_DEBUG2, "Handling EXISTS\n");
1062
1063 if (!mutt_str_atoui(pn, &count))
1064 {
1065 mutt_debug(LL_DEBUG1, "Malformed EXISTS: '%s'\n", pn);
1066 return;
1067 }
1068
1069 struct ImapMboxData *mdata = adata->mailbox->mdata;
1070 if (!mdata)
1071 return;
1072
1073 /* new mail arrived */
1074 if (count < imap_msn_highest(&mdata->msn))
1075 {
1076 /* Notes 6.0.3 has a tendency to report fewer messages exist than
1077 * it should. */
1078 mutt_debug(LL_DEBUG1, "Message count is out of sync\n");
1079 }
1080 else if (count == imap_msn_highest(&mdata->msn))
1081 {
1082 /* at least the InterChange server sends EXISTS messages freely,
1083 * even when there is no new mail */
1084 mutt_debug(LL_DEBUG3, "superfluous EXISTS message\n");
1085 }
1086 else
1087 {
1088 mutt_debug(LL_DEBUG2, "New mail in %s - %d messages total\n", mdata->name, count);
1089 mdata->reopen |= IMAP_NEWMAIL_PENDING;
1090 mdata->new_mail_count = count;
1091 }
1092}
#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 1100 of file command.c.

1101{
1102 char *s = imap_next_word(adata->buf);
1103 char *pn = imap_next_word(s);
1104
1105 const bool c_imap_server_noise = cs_subset_bool(NeoMutt->sub, "imap_server_noise");
1106 if ((adata->state >= IMAP_SELECTED) && mutt_isdigit(*s))
1107 {
1108 /* pn vs. s: need initial seqno */
1109 pn = s;
1110 s = imap_next_word(s);
1111
1112 /* EXISTS, EXPUNGE, FETCH are always related to the SELECTED mailbox */
1113 if (mutt_istr_startswith(s, "EXISTS"))
1114 cmd_parse_exists(adata, pn);
1115 else if (mutt_istr_startswith(s, "EXPUNGE"))
1116 cmd_parse_expunge(adata, pn);
1117 else if (mutt_istr_startswith(s, "FETCH"))
1118 cmd_parse_fetch(adata, pn);
1119 }
1120 else if ((adata->state >= IMAP_SELECTED) && mutt_istr_startswith(s, "VANISHED"))
1121 {
1122 cmd_parse_vanished(adata, pn);
1123 }
1124 else if (mutt_istr_startswith(s, "CAPABILITY"))
1125 {
1126 cmd_parse_capability(adata, s);
1127 }
1128 else if (mutt_istr_startswith(s, "OK [CAPABILITY"))
1129 {
1130 cmd_parse_capability(adata, pn);
1131 }
1132 else if (mutt_istr_startswith(pn, "OK [CAPABILITY"))
1133 {
1135 }
1136 else if (mutt_istr_startswith(s, "LIST"))
1137 {
1138 cmd_parse_list(adata, s);
1139 }
1140 else if (mutt_istr_startswith(s, "LSUB"))
1141 {
1142 cmd_parse_lsub(adata, s);
1143 }
1144 else if (mutt_istr_startswith(s, "MYRIGHTS"))
1145 {
1146 cmd_parse_myrights(adata, s);
1147 }
1148 else if (mutt_istr_startswith(s, "SEARCH"))
1149 {
1150 cmd_parse_search(adata, s);
1151 }
1152 else if (mutt_istr_startswith(s, "STATUS"))
1153 {
1154 cmd_parse_status(adata, s);
1155 }
1156 else if (mutt_istr_startswith(s, "ENABLED"))
1157 {
1158 cmd_parse_enabled(adata, s);
1159 }
1160 else if (mutt_istr_startswith(s, "BYE"))
1161 {
1162 mutt_debug(LL_DEBUG2, "Handling BYE\n");
1163
1164 /* check if we're logging out */
1165 if (adata->status == IMAP_BYE)
1166 return 0;
1167
1168 /* server shut down our connection */
1169 s += 3;
1170 SKIPWS(s);
1171 mutt_error("%s", s);
1172 cmd_handle_fatal(adata);
1173
1174 return -1;
1175 }
1176 else if (c_imap_server_noise && mutt_istr_startswith(s, "NO"))
1177 {
1178 mutt_debug(LL_DEBUG2, "Handling untagged NO\n");
1179
1180 /* Display the warning message from the server */
1181 mutt_error("%s", s + 2);
1182 }
1183
1184 return 0;
1185}
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:622
static void cmd_parse_lsub(struct ImapAccountData *adata, char *s)
Parse a server LSUB (list subscribed mailboxes)
Definition command.c:749
static void cmd_parse_status(struct ImapAccountData *adata, char *s)
Parse status from server.
Definition command.c:891
static void cmd_parse_exists(struct ImapAccountData *adata, const char *pn)
Parse EXISTS message from serer.
Definition command.c:1058
static void cmd_parse_myrights(struct ImapAccountData *adata, const char *s)
Set rights bits according to MYRIGHTS response.
Definition command.c:797
static void cmd_parse_expunge(struct ImapAccountData *adata, const char *s)
Parse expunge command.
Definition command.c:319
static void cmd_parse_enabled(struct ImapAccountData *adata, const char *s)
Record what the server has enabled.
Definition command.c:1038
static void cmd_parse_vanished(struct ImapAccountData *adata, char *s)
Parse vanished command.
Definition command.c:375
static void cmd_parse_fetch(struct ImapAccountData *adata, char *s)
Load fetch response into ImapAccountData.
Definition command.c:471
@ 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 1196 of file command.c.

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

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

1353{
1354 return cmd_status(s) == IMAP_RES_OK;
1355}
+ 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 1363 of file command.c.

1364{
1365 static const char *notrailer = "";
1366 const char *s = adata->buf;
1367
1368 if (!s)
1369 {
1370 mutt_debug(LL_DEBUG2, "not a tagged response\n");
1371 return notrailer;
1372 }
1373
1374 s = imap_next_word((char *) s);
1375 if (!s || (!mutt_istr_startswith(s, "OK") && !mutt_istr_startswith(s, "NO") &&
1376 !mutt_istr_startswith(s, "BAD")))
1377 {
1378 mutt_debug(LL_DEBUG2, "not a command completion: %s\n", adata->buf);
1379 return notrailer;
1380 }
1381
1382 s = imap_next_word((char *) s);
1383 if (!s)
1384 return notrailer;
1385
1386 return s;
1387}
+ 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 1400 of file command.c.

1401{
1402 if (!adata)
1403 return IMAP_EXEC_ERROR;
1404
1405 /* Check connection health before executing command */
1406 if ((adata->state >= IMAP_AUTHENTICATED) && (adata->last_success > 0))
1407 {
1408 time_t now = mutt_date_now();
1409 time_t idle_time = now - adata->last_success;
1410
1411 if (idle_time > IMAP_CONN_STALE_THRESHOLD)
1412 {
1413 mutt_debug(LL_DEBUG2, "Connection idle for %ld seconds, sending NOOP to verify\n",
1414 (long) idle_time);
1415 /* Connection may be stale - let the command proceed and handle any error */
1416 }
1417 }
1418
1419 if (flags & IMAP_CMD_SINGLE)
1420 {
1421 // Process any existing commands
1422 if (adata->nextcmd != adata->lastcmd)
1423 imap_exec(adata, NULL, IMAP_CMD_POLL);
1424 }
1425
1426 int rc = cmd_start(adata, cmdstr, flags);
1427 if (rc < 0)
1428 {
1429 cmd_handle_fatal(adata);
1430 return IMAP_EXEC_FATAL;
1431 }
1432
1433 if (flags & IMAP_CMD_QUEUE)
1434 return IMAP_EXEC_SUCCESS;
1435
1436 const short c_imap_poll_timeout = cs_subset_number(NeoMutt->sub, "imap_poll_timeout");
1437 if ((flags & IMAP_CMD_POLL) && (c_imap_poll_timeout > 0) &&
1438 ((mutt_socket_poll(adata->conn, c_imap_poll_timeout)) == 0))
1439 {
1440 mutt_error(_("Connection to %s timed out"), adata->conn->account.host);
1441 cmd_handle_fatal(adata);
1442 return IMAP_EXEC_FATAL;
1443 }
1444
1445 /* Allow interruptions, particularly useful if there are network problems. */
1447 do
1448 {
1449 rc = imap_cmd_step(adata);
1450 // The queue is empty, so the single command has been processed
1451 if ((flags & IMAP_CMD_SINGLE) && (adata->nextcmd == adata->lastcmd))
1452 break;
1453 } while (rc == IMAP_RES_CONTINUE);
1455
1456 if (rc == IMAP_RES_NO)
1457 return IMAP_EXEC_ERROR;
1458 if (rc != IMAP_RES_OK)
1459 {
1460 if (adata->status != IMAP_FATAL)
1461 return IMAP_EXEC_ERROR;
1462
1463 mutt_debug(LL_DEBUG1, "command failed: %s\n", adata->buf);
1464 return IMAP_EXEC_FATAL;
1465 }
1466
1467 /* Track successful command completion for connection health monitoring */
1468 adata->last_success = mutt_date_now();
1469
1470 return IMAP_EXEC_SUCCESS;
1471}
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 1483 of file command.c.

1484{
1485 if (!adata)
1486 return;
1487
1488 if (adata->status == IMAP_FATAL)
1489 {
1490 adata->closing = false;
1491 cmd_handle_fatal(adata);
1492 return;
1493 }
1494
1495 if (!(adata->state >= IMAP_SELECTED) || (adata->mailbox && adata->closing))
1496 {
1497 adata->closing = false;
1498 return;
1499 }
1500
1501 adata->closing = false;
1502
1503 struct ImapMboxData *mdata = imap_mdata_get(adata->mailbox);
1504
1505 if (mdata && mdata->reopen & IMAP_REOPEN_ALLOW)
1506 {
1507 // First remove expunged emails from the msn_index
1508 if (mdata->reopen & IMAP_EXPUNGE_PENDING)
1509 {
1510 mutt_debug(LL_DEBUG2, "Expunging mailbox\n");
1511 imap_expunge_mailbox(adata->mailbox, true);
1512 /* Detect whether we've gotten unexpected EXPUNGE messages */
1513 if (!(mdata->reopen & IMAP_EXPUNGE_EXPECTED))
1514 mdata->check_status |= IMAP_EXPUNGE_PENDING;
1516 }
1517
1518 // Then add new emails to it
1519 if (mdata->reopen & IMAP_NEWMAIL_PENDING)
1520 {
1521 const size_t max_msn = imap_msn_highest(&mdata->msn);
1522 if (mdata->new_mail_count > max_msn)
1523 {
1524 if (!(mdata->reopen & IMAP_EXPUNGE_PENDING))
1525 mdata->check_status |= IMAP_NEWMAIL_PENDING;
1526
1527 mutt_debug(LL_DEBUG2, "Fetching new mails from %zd to %u\n",
1528 max_msn + 1, mdata->new_mail_count);
1529 imap_read_headers(adata->mailbox, max_msn + 1, mdata->new_mail_count, false);
1530 }
1531 }
1532
1533 // And to finish inform about MUTT_REOPEN if needed
1534 if (mdata->reopen & IMAP_EXPUNGE_PENDING && !(mdata->reopen & IMAP_EXPUNGE_EXPECTED))
1535 mdata->check_status |= IMAP_EXPUNGE_PENDING;
1536
1537 if (mdata->reopen & IMAP_EXPUNGE_PENDING)
1539 }
1540
1541 adata->status = 0;
1542}
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:1354
#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 1550 of file command.c.

1551{
1552 int rc;
1553
1554 mutt_debug(LL_DEBUG2, "Entering IDLE mode for %s\n",
1555 adata->conn ? adata->conn->account.host : "NULL");
1556
1557 if (cmd_start(adata, "IDLE", IMAP_CMD_POLL) < 0)
1558 {
1559 mutt_debug(LL_DEBUG1, "Failed to send IDLE command\n");
1560 cmd_handle_fatal(adata);
1561 return -1;
1562 }
1563
1564 const short c_imap_poll_timeout = cs_subset_number(NeoMutt->sub, "imap_poll_timeout");
1565 mutt_debug(LL_DEBUG2, "Waiting for IDLE continuation (timeout=%d)\n", c_imap_poll_timeout);
1566
1567 if ((c_imap_poll_timeout > 0) &&
1568 ((mutt_socket_poll(adata->conn, c_imap_poll_timeout)) == 0))
1569 {
1570 mutt_debug(LL_DEBUG1, "IDLE timed out waiting for server continuation response\n");
1571 mutt_error(_("Connection to %s timed out waiting for IDLE response"),
1572 adata->conn->account.host);
1573 cmd_handle_fatal(adata);
1574 return -1;
1575 }
1576
1577 do
1578 {
1579 rc = imap_cmd_step(adata);
1580 } while (rc == IMAP_RES_CONTINUE);
1581
1582 if (rc == IMAP_RES_RESPOND)
1583 {
1584 /* successfully entered IDLE state */
1585 adata->state = IMAP_IDLE;
1586 /* queue automatic exit when next command is issued */
1587 buf_addstr(&adata->cmdbuf, "DONE\r\n");
1588 mutt_debug(LL_DEBUG2, "Successfully entered IDLE state\n");
1589 rc = IMAP_RES_OK;
1590 }
1591 if (rc != IMAP_RES_OK)
1592 {
1593 mutt_debug(LL_DEBUG1, "IDLE command failed with rc=%d (expected RESPOND=%d)\n",
1594 rc, IMAP_RES_RESPOND);
1595 mutt_error(_("IDLE command failed for %s"), adata->conn->account.host);
1596 return -1;
1597 }
1598
1599 return 0;
1600}
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};