NeoMutt  2025-12-11-58-g09398d
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
message.c File Reference

Manage IMAP messages. More...

#include "config.h"
#include <limits.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.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 "gui/lib.h"
#include "mutt.h"
#include "message.h"
#include "lib.h"
#include "bcache/lib.h"
#include "key/lib.h"
#include "progress/lib.h"
#include "question/lib.h"
#include "adata.h"
#include "edata.h"
#include "external.h"
#include "mdata.h"
#include "msg_set.h"
#include "msn.h"
#include "mutt_logging.h"
#include "mx.h"
#include "protos.h"
#include <libintl.h>
#include "hcache/lib.h"
+ Include dependency graph for message.c:

Go to the source code of this file.

Functions

static struct BodyCacheimap_bcache_open (struct Mailbox *m)
 Open a message cache.
 
static FILE * msg_cache_get (struct Mailbox *m, struct Email *e)
 Get the message cache entry for an email.
 
static FILE * msg_cache_put (struct Mailbox *m, struct Email *e)
 Put an email into the message cache.
 
static int msg_cache_commit (struct Mailbox *m, struct Email *e)
 Add to the message cache.
 
static int imap_bcache_delete (const char *id, struct BodyCache *bcache, void *data)
 Delete an entry from the message cache - Implements bcache_list_t -.
 
static char * msg_parse_flags (struct ImapHeader *h, char *s)
 Read a FLAGS token into an ImapHeader.
 
static int msg_parse_fetch (struct ImapHeader *h, char *s)
 Handle headers returned from header fetch.
 
static int msg_fetch_header (struct Mailbox *m, struct ImapHeader *ih, char *buf, FILE *fp)
 Import IMAP FETCH response into an ImapHeader.
 
static int flush_buffer (char *buf, size_t *len, struct Connection *conn)
 Write data to a connection.
 
static bool query_abort_header_download (struct ImapAccountData *adata)
 Ask the user whether to abort the download.
 
static void imap_alloc_uid_hash (struct ImapAccountData *adata, unsigned int msn_count)
 Create a Hash Table for the UIDs.
 
static unsigned int imap_fetch_msn_seqset (struct Buffer *buf, struct ImapAccountData *adata, bool evalhc, unsigned int msn_begin, unsigned int msn_end, unsigned int *fetch_msn_end)
 Generate a sequence set.
 
static void set_changed_flag (struct Mailbox *m, struct Email *e, int local_changes, bool *server_changes, enum MessageType flag_name, bool old_hd_flag, bool new_hd_flag, bool h_flag)
 Have the flags of an email changed.
 
static int read_headers_normal_eval_cache (struct ImapAccountData *adata, unsigned int msn_end, unsigned int uid_next, bool store_flag_updates, bool eval_condstore)
 Retrieve data from the header cache.
 
static int read_headers_qresync_eval_cache (struct ImapAccountData *adata, char *uid_seqset)
 Retrieve data from the header cache.
 
static int read_headers_condstore_qresync_updates (struct ImapAccountData *adata, unsigned int msn_end, unsigned int uid_next, unsigned long long hc_modseq, bool eval_qresync)
 Retrieve updates from the server.
 
static int imap_verify_qresync (struct Mailbox *m)
 Check to see if QRESYNC got jumbled.
 
static int read_headers_fetch_new (struct Mailbox *m, unsigned int msn_begin, unsigned int msn_end, bool evalhc, unsigned int *maxuid, bool initial_download)
 Retrieve new messages from the server.
 
int imap_read_headers (struct Mailbox *m, unsigned int msn_begin, unsigned int msn_end, bool initial_download)
 Read headers from the server.
 
int imap_append_message (struct Mailbox *m, struct Message *msg)
 Write an email back to the server.
 
static int emails_to_uid_array (struct EmailArray *ea, struct UidArray *uida)
 Extract IMAP UIDs from Emails.
 
int imap_copy_messages (struct Mailbox *m, struct EmailArray *ea, const char *dest, enum MessageSaveOpt save_opt)
 Server COPY messages to another folder.
 
int imap_cache_del (struct Mailbox *m, struct Email *e)
 Delete an email from the body cache.
 
int imap_cache_clean (struct Mailbox *m)
 Delete all the entries in the message cache.
 
char * imap_set_flags (struct Mailbox *m, struct Email *e, char *s, bool *server_changes)
 Fill the message header according to the server flags.
 
bool imap_msg_open (struct Mailbox *m, struct Message *msg, struct Email *e)
 Open an email message in a Mailbox - Implements MxOps::msg_open() -.
 
int imap_msg_commit (struct Mailbox *m, struct Message *msg)
 Save changes to an email - Implements MxOps::msg_commit() -.
 
int imap_msg_close (struct Mailbox *m, struct Message *msg)
 Close an email - Implements MxOps::msg_close() -.
 
int imap_msg_save_hcache (struct Mailbox *m, struct Email *e)
 Save message to the header cache - Implements MxOps::msg_save_hcache() -.
 

Detailed Description

Manage IMAP messages.

Authors
  • Brandon Long
  • Brendan Cully
  • Richard Russon
  • Mehdi Abaakouk
  • Pietro Cerutti
  • Ian Zimmerman
  • Ihor Antonov
  • Dennis Schön

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 message.c.

Function Documentation

◆ imap_bcache_open()

static struct BodyCache * imap_bcache_open ( struct Mailbox * m)
static

Open a message cache.

Parameters
mSelected Imap Mailbox
Return values
ptrSuccess, using existing cache (or opened new cache)
NULLFailure

Definition at line 81 of file message.c.

82{
85
86 if (!adata || (adata->mailbox != m) || !mdata)
87 return NULL;
88
89 if (mdata->bcache)
90 return mdata->bcache;
91
92 struct Buffer *mailbox = buf_pool_get();
93 imap_cachepath(adata->delim, mdata->name, mailbox);
94
95 struct BodyCache *bc = mutt_bcache_open(&adata->conn->account, buf_string(mailbox));
96 buf_pool_release(&mailbox);
97
98 return bc;
99}
struct BodyCache * mutt_bcache_open(struct ConnAccount *account, const char *mailbox)
Open an Email-Body Cache.
Definition bcache.c:146
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition buffer.h:96
struct ImapAccountData * imap_adata_get(struct Mailbox *m)
Get the Account data for this mailbox.
Definition adata.c:123
struct ImapMboxData * imap_mdata_get(struct Mailbox *m)
Get the Mailbox data for this mailbox.
Definition mdata.c:61
void imap_cachepath(char delim, const char *mailbox, struct Buffer *dest)
Generate a cache path for a mailbox.
Definition util.c:749
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition pool.c:82
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition pool.c:96
void * adata
Private data (for Mailbox backends)
Definition account.h:42
Local cache of email bodies.
Definition bcache.c:49
String manipulation buffer.
Definition buffer.h:36
struct ConnAccount account
Account details: username, password, etc.
Definition connection.h:49
IMAP-specific Account data -.
Definition adata.h:40
char delim
Path delimiter.
Definition adata.h:75
struct Mailbox * mailbox
Current selected mailbox.
Definition adata.h:76
struct Connection * conn
Connection to IMAP server.
Definition adata.h:41
IMAP-specific Mailbox data -.
Definition mdata.h:40
char * name
Mailbox name.
Definition mdata.h:41
void * mdata
Driver specific data.
Definition mailbox.h:132
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ msg_cache_get()

static FILE * msg_cache_get ( struct Mailbox * m,
struct Email * e )
static

Get the message cache entry for an email.

Parameters
mSelected Imap Mailbox
eEmail
Return values
ptrSuccess, handle of cache entry
NULLFailure

Definition at line 108 of file message.c.

109{
111 struct ImapMboxData *mdata = imap_mdata_get(m);
112
113 if (!e || !adata || (adata->mailbox != m) || !mdata)
114 return NULL;
115
116 mdata->bcache = imap_bcache_open(m);
117 char id[64] = { 0 };
118 snprintf(id, sizeof(id), "%u-%u", mdata->uidvalidity, imap_edata_get(e)->uid);
119 return mutt_bcache_get(mdata->bcache, id);
120}
FILE * mutt_bcache_get(struct BodyCache *bcache, const char *id)
Open a file in the Body Cache.
Definition bcache.c:185
struct ImapEmailData * imap_edata_get(struct Email *e)
Get the private data for this Email.
Definition edata.c:66
static struct BodyCache * imap_bcache_open(struct Mailbox *m)
Open a message cache.
Definition message.c:81
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ msg_cache_put()

static FILE * msg_cache_put ( struct Mailbox * m,
struct Email * e )
static

Put an email into the message cache.

Parameters
mSelected Imap Mailbox
eEmail
Return values
ptrSuccess, handle of cache entry
NULLFailure

Definition at line 129 of file message.c.

130{
132 struct ImapMboxData *mdata = imap_mdata_get(m);
133
134 if (!e || !adata || (adata->mailbox != m) || !mdata)
135 return NULL;
136
137 mdata->bcache = imap_bcache_open(m);
138 char id[64] = { 0 };
139 snprintf(id, sizeof(id), "%u-%u", mdata->uidvalidity, imap_edata_get(e)->uid);
140 return mutt_bcache_put(mdata->bcache, id);
141}
FILE * mutt_bcache_put(struct BodyCache *bcache, const char *id)
Create a file in the Body Cache.
Definition bcache.c:212
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ msg_cache_commit()

static int msg_cache_commit ( struct Mailbox * m,
struct Email * e )
static

Add to the message cache.

Parameters
mSelected Imap Mailbox
eEmail
Return values
0Success
-1Failure

Definition at line 150 of file message.c.

151{
153 struct ImapMboxData *mdata = imap_mdata_get(m);
154
155 if (!e || !adata || (adata->mailbox != m) || !mdata)
156 return -1;
157
158 mdata->bcache = imap_bcache_open(m);
159 char id[64] = { 0 };
160 snprintf(id, sizeof(id), "%u-%u", mdata->uidvalidity, imap_edata_get(e)->uid);
161
162 return mutt_bcache_commit(mdata->bcache, id);
163}
int mutt_bcache_commit(struct BodyCache *bcache, const char *id)
Move a temporary file into the Body Cache.
Definition bcache.c:252
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ msg_parse_flags()

static char * msg_parse_flags ( struct ImapHeader * h,
char * s )
static

Read a FLAGS token into an ImapHeader.

Parameters
hHeader to store flags
sCommand string containing flags
Return values
ptrThe end of flags string
NULLFailure

Definition at line 194 of file message.c.

195{
196 struct ImapEmailData *edata = h->edata;
197
198 /* sanity-check string */
199 size_t plen = mutt_istr_startswith(s, "FLAGS");
200 if (plen == 0)
201 {
202 mutt_debug(LL_DEBUG1, "not a FLAGS response: %s\n", s);
203 return NULL;
204 }
205 s += plen;
206 SKIPWS(s);
207 if (*s != '(')
208 {
209 mutt_debug(LL_DEBUG1, "bogus FLAGS response: %s\n", s);
210 return NULL;
211 }
212 s++;
213
214 FREE(&edata->flags_system);
215 FREE(&edata->flags_remote);
216
217 edata->deleted = false;
218 edata->flagged = false;
219 edata->replied = false;
220 edata->read = false;
221 edata->old = false;
222
223 /* start parsing */
224 while (*s && (*s != ')'))
225 {
226 if ((plen = mutt_istr_startswith(s, "\\deleted")))
227 {
228 s += plen;
229 edata->deleted = true;
230 }
231 else if ((plen = mutt_istr_startswith(s, "\\flagged")))
232 {
233 s += plen;
234 edata->flagged = true;
235 }
236 else if ((plen = mutt_istr_startswith(s, "\\answered")))
237 {
238 s += plen;
239 edata->replied = true;
240 }
241 else if ((plen = mutt_istr_startswith(s, "\\seen")))
242 {
243 s += plen;
244 edata->read = true;
245 }
246 else if ((plen = mutt_istr_startswith(s, "\\recent")))
247 {
248 s += plen;
249 }
250 else if ((plen = mutt_istr_startswith(s, "old")))
251 {
252 s += plen;
253 edata->old = cs_subset_bool(NeoMutt->sub, "mark_old");
254 }
255 else
256 {
257 char ctmp;
258 char *flag_word = s;
259 bool is_system_keyword = mutt_istr_startswith(s, "\\");
260
261 while (*s && !mutt_isspace(*s) && (*s != ')'))
262 s++;
263
264 ctmp = *s;
265 *s = '\0';
266
267 struct Buffer *buf = buf_pool_get();
268 if (is_system_keyword)
269 {
270 /* store other system flags as well (mainly \\Draft) */
271 buf_addstr(buf, edata->flags_system);
272 buf_join_str(buf, flag_word, ' ');
273 edata->flags_system = buf_strdup(buf);
274 }
275 else
276 {
277 /* store custom flags as well */
278 buf_addstr(buf, edata->flags_remote);
279 buf_join_str(buf, flag_word, ' ');
280 edata->flags_remote = buf_strdup(buf);
281 }
282 buf_pool_release(&buf);
283
284 *s = ctmp;
285 }
286 SKIPWS(s);
287 }
288
289 /* wrap up, or note bad flags response */
290 if (*s == ')')
291 {
292 s++;
293 }
294 else
295 {
296 mutt_debug(LL_DEBUG1, "Unterminated FLAGS response: %s\n", s);
297 return NULL;
298 }
299
300 return s;
301}
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition buffer.c:226
void buf_join_str(struct Buffer *buf, const char *str, char sep)
Join a buffer with a string separated by sep.
Definition buffer.c:748
char * buf_strdup(const struct Buffer *buf)
Copy a Buffer's string.
Definition buffer.c:571
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition helpers.c:47
bool mutt_isspace(int arg)
Wrapper for isspace(3)
Definition ctype.c:95
#define mutt_debug(LEVEL,...)
Definition logging2.h:90
@ LL_DEBUG1
Log at debug level 1.
Definition logging2.h:44
#define FREE(x)
Definition memory.h:62
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition string.c:244
#define SKIPWS(ch)
Definition string2.h:51
void * edata
Driver-specific data.
Definition email.h:74
IMAP-specific Email data -.
Definition edata.h:35
char * flags_remote
Definition edata.h:49
char * flags_system
Definition edata.h:48
struct ImapEmailData * edata
Definition message.h:35
Container for Accounts, Notifications.
Definition neomutt.h:43
struct ConfigSubset * sub
Inherited config items.
Definition neomutt.h:47
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ msg_parse_fetch()

static int msg_parse_fetch ( struct ImapHeader * h,
char * s )
static

Handle headers returned from header fetch.

Parameters
hIMAP Header
sCommand string
Return values
0Success
-1String is corrupted
-2Fetch contains a body or header lines that still need to be parsed

Definition at line 311 of file message.c.

312{
313 if (!s)
314 return -1;
315
316 char tmp[128] = { 0 };
317 char *ptmp = NULL;
318 size_t plen = 0;
319
320 while (*s)
321 {
322 SKIPWS(s);
323
324 if (mutt_istr_startswith(s, "FLAGS"))
325 {
326 s = msg_parse_flags(h, s);
327 if (!s)
328 return -1;
329 }
330 else if ((plen = mutt_istr_startswith(s, "UID")))
331 {
332 s += plen;
333 SKIPWS(s);
334 if (!mutt_str_atoui(s, &h->edata->uid))
335 return -1;
336
337 s = imap_next_word(s);
338 }
339 else if ((plen = mutt_istr_startswith(s, "INTERNALDATE")))
340 {
341 s += plen;
342 SKIPWS(s);
343 if (*s != '\"')
344 {
345 mutt_debug(LL_DEBUG1, "bogus INTERNALDATE entry: %s\n", s);
346 return -1;
347 }
348 s++;
349 ptmp = tmp;
350 while (*s && (*s != '\"') && (ptmp != (tmp + sizeof(tmp) - 1)))
351 *ptmp++ = *s++;
352 if (*s != '\"')
353 return -1;
354 s++; /* skip past the trailing " */
355 *ptmp = '\0';
357 }
358 else if ((plen = mutt_istr_startswith(s, "RFC822.SIZE")))
359 {
360 s += plen;
361 SKIPWS(s);
362 ptmp = tmp;
363 while (mutt_isdigit(*s) && (ptmp != (tmp + sizeof(tmp) - 1)))
364 *ptmp++ = *s++;
365 *ptmp = '\0';
366 if (!mutt_str_atol(tmp, &h->content_length))
367 return -1;
368 }
369 else if (mutt_istr_startswith(s, "BODY") || mutt_istr_startswith(s, "RFC822.HEADER"))
370 {
371 /* handle above, in msg_fetch_header */
372 return -2;
373 }
374 else if ((plen = mutt_istr_startswith(s, "MODSEQ")))
375 {
376 s += plen;
377 SKIPWS(s);
378 if (*s != '(')
379 {
380 mutt_debug(LL_DEBUG1, "bogus MODSEQ response: %s\n", s);
381 return -1;
382 }
383 s++;
384 while (*s && (*s != ')'))
385 s++;
386 if (*s == ')')
387 {
388 s++;
389 }
390 else
391 {
392 mutt_debug(LL_DEBUG1, "Unterminated MODSEQ response: %s\n", s);
393 return -1;
394 }
395 }
396 else if (*s == ')')
397 {
398 s++; /* end of request */
399 }
400 else if (*s)
401 {
402 /* got something i don't understand */
403 imap_error("msg_parse_fetch", s);
404 return -1;
405 }
406 }
407
408 return 0;
409}
const char * mutt_str_atol(const char *str, long *dst)
Convert ASCII string to a long.
Definition atoi.c:142
const char * mutt_str_atoui(const char *str, unsigned int *dst)
Convert ASCII string to an unsigned integer.
Definition atoi.c:217
bool mutt_isdigit(int arg)
Wrapper for isdigit(3)
Definition ctype.c:65
static char * msg_parse_flags(struct ImapHeader *h, char *s)
Read a FLAGS token into an ImapHeader.
Definition message.c:194
void imap_error(const char *where, const char *msg)
Show an error and abort.
Definition util.c:660
char * imap_next_word(char *s)
Find where the next IMAP word begins.
Definition util.c:824
time_t mutt_date_parse_imap(const char *s)
Parse date of the form: DD-MMM-YYYY HH:MM:SS +ZZzz.
Definition date.c:853
unsigned int uid
32-bit Message UID
Definition edata.h:45
time_t received
Definition message.h:37
long content_length
Definition message.h:38
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ msg_fetch_header()

static int msg_fetch_header ( struct Mailbox * m,
struct ImapHeader * ih,
char * buf,
FILE * fp )
static

Import IMAP FETCH response into an ImapHeader.

Parameters
mMailbox
ihImapHeader
bufServer string containing FETCH response
fpConnection to server
Return values
0Success
-1String is not a fetch response
-2String is a corrupt fetch response

Expects string beginning with * n FETCH.

Definition at line 423 of file message.c.

424{
425 int rc = -1; /* default now is that string isn't FETCH response */
426
428 if (!adata)
429 return rc;
430
431 if (buf[0] != '*')
432 return rc;
433
434 /* skip to message number */
436 if (!mutt_str_atoui(buf, &ih->edata->msn))
437 return rc;
438
439 /* find FETCH tag */
441 if (!mutt_istr_startswith(buf, "FETCH"))
442 return rc;
443
444 rc = -2; /* we've got a FETCH response, for better or worse */
445 buf = strchr(buf, '(');
446 if (!buf)
447 return rc;
448 buf++;
449
450 /* FIXME: current implementation - call msg_parse_fetch - if it returns -2,
451 * read header lines and call it again. Silly. */
452 int parse_rc = msg_parse_fetch(ih, buf);
453 if (parse_rc == 0)
454 return 0;
455 if ((parse_rc != -2) || !fp)
456 return rc;
457
458 unsigned int bytes = 0;
459 if (imap_get_literal_count(buf, &bytes) == 0)
460 {
461 imap_read_literal(fp, adata, bytes, NULL);
462
463 /* we may have other fields of the FETCH _after_ the literal
464 * (eg Domino puts FLAGS here). Nothing wrong with that, either.
465 * This all has to go - we should accept literals and nonliterals
466 * interchangeably at any time. */
468 return rc;
469
470 if (msg_parse_fetch(ih, adata->buf) == -1)
471 return rc;
472 }
473
474 rc = 0; /* success */
475
476 /* subtract headers from message size - unfortunately only the subset of
477 * headers we've requested. */
478 ih->content_length -= bytes;
479
480 return rc;
481}
int imap_cmd_step(struct ImapAccountData *adata)
Reads server responses from an IMAP command.
Definition command.c:1147
static int msg_parse_fetch(struct ImapHeader *h, char *s)
Handle headers returned from header fetch.
Definition message.c:311
int imap_get_literal_count(const char *buf, unsigned int *bytes)
Write number of bytes in an IMAP literal into bytes.
Definition util.c:780
#define IMAP_RES_CONTINUE
* ...
Definition private.h:56
int imap_read_literal(FILE *fp, struct ImapAccountData *adata, unsigned long bytes, struct Progress *progress)
Read bytes bytes from server into file.
Definition imap.c:610
char * buf
Definition adata.h:59
unsigned int msn
Message Sequence Number.
Definition edata.h:46
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ flush_buffer()

static int flush_buffer ( char * buf,
size_t * len,
struct Connection * conn )
static

Write data to a connection.

Parameters
bufBuffer containing data
lenLength of buffer
connNetwork connection
Return values
>0Number of bytes written
-1Error

Definition at line 491 of file message.c.

492{
493 buf[*len] = '\0';
494 int rc = mutt_socket_write_n(conn, buf, *len);
495 *len = 0;
496 return rc;
497}
#define mutt_socket_write_n(conn, buf, len)
Definition socket.h:59
+ Here is the caller graph for this function:

◆ query_abort_header_download()

static bool query_abort_header_download ( struct ImapAccountData * adata)
static

Ask the user whether to abort the download.

Parameters
adataImap Account data
Return values
trueAbort the download

If the user hits ctrl-c during an initial header download for a mailbox, prompt whether to completely abort the download and close the mailbox.

Definition at line 507 of file message.c.

508{
509 bool abort = false;
510
512 /* L10N: This prompt is made if the user hits Ctrl-C when opening an IMAP mailbox */
513 if (query_yesorno(_("Abort download and close mailbox?"), MUTT_YES) == MUTT_YES)
514 {
515 abort = true;
517 }
518 SigInt = false;
519
520 return abort;
521}
void mutt_flushinp(void)
Empty all the keyboard buffers.
Definition get.c:58
void imap_close_connection(struct ImapAccountData *adata)
Close an IMAP connection.
Definition imap.c:869
#define _(a)
Definition message.h:28
@ MUTT_YES
User answered 'Yes', or assume 'Yes'.
Definition quad.h:39
enum QuadOption query_yesorno(const char *prompt, enum QuadOption def)
Ask the user a Yes/No question.
Definition question.c:325
volatile sig_atomic_t SigInt
true after SIGINT is received
Definition signal.c:68
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_alloc_uid_hash()

static void imap_alloc_uid_hash ( struct ImapAccountData * adata,
unsigned int msn_count )
static

Create a Hash Table for the UIDs.

Parameters
adataImap Account data
msn_countNumber of MSNs in use

This function is run after imap_imap_msn_reserve, so we skip the malicious msn_count size check.

Definition at line 531 of file message.c.

532{
533 struct ImapMboxData *mdata = adata->mailbox->mdata;
534 if (mdata && !mdata->uid_hash)
535 mdata->uid_hash = mutt_hash_int_new(MAX(6 * msn_count / 5, 30), MUTT_HASH_NO_FLAGS);
536}
struct HashTable * mutt_hash_int_new(size_t num_elems, HashFlags flags)
Create a new Hash Table (with integer keys)
Definition hash.c:285
#define MUTT_HASH_NO_FLAGS
No flags are set.
Definition hash.h:111
#define MAX(a, b)
Definition memory.h:36
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_fetch_msn_seqset()

static unsigned int imap_fetch_msn_seqset ( struct Buffer * buf,
struct ImapAccountData * adata,
bool evalhc,
unsigned int msn_begin,
unsigned int msn_end,
unsigned int * fetch_msn_end )
static

Generate a sequence set.

Parameters
[in]bufBuffer for the result
[in]adataImap Account data
[in]evalhcIf true, check the Header Cache
[in]msn_beginFirst Message Sequence Number
[in]msn_endLast Message Sequence Number
[out]fetch_msn_endHighest Message Sequence Number fetched
Return values
numMSN count

Generates a more complicated sequence set after using the header cache, in case there are missing MSNs in the middle.

This can happen if during a sync/close, messages are deleted from the cache, but the server doesn't get the updates (via a dropped network connection, or just plain refusing the updates).

Definition at line 555 of file message.c.

558{
559 buf_reset(buf);
560 if (msn_end < msn_begin)
561 return 0;
562
563 struct ImapMboxData *mdata = adata->mailbox->mdata;
564 if (!mdata)
565 return 0;
566
567 unsigned int max_headers_per_fetch = UINT_MAX;
568 bool first_chunk = true;
569 int state = 0; /* 1: single msn, 2: range of msn */
570 unsigned int msn;
571 unsigned int range_begin = 0;
572 unsigned int range_end = 0;
573 unsigned int msn_count = 0;
574
575 const long c_imap_fetch_chunk_size = cs_subset_long(NeoMutt->sub, "imap_fetch_chunk_size");
576 if (c_imap_fetch_chunk_size > 0)
577 max_headers_per_fetch = c_imap_fetch_chunk_size;
578
579 if (!evalhc)
580 {
581 if ((msn_end - msn_begin + 1) <= max_headers_per_fetch)
582 *fetch_msn_end = msn_end;
583 else
584 *fetch_msn_end = msn_begin + max_headers_per_fetch - 1;
585 buf_printf(buf, "%u:%u", msn_begin, *fetch_msn_end);
586 return (*fetch_msn_end - msn_begin + 1);
587 }
588
589 for (msn = msn_begin; msn <= (msn_end + 1); msn++)
590 {
591 if ((msn_count < max_headers_per_fetch) && (msn <= msn_end) &&
592 !imap_msn_get(&mdata->msn, msn - 1))
593 {
594 msn_count++;
595
596 switch (state)
597 {
598 case 1: /* single: convert to a range */
599 state = 2;
601
602 case 2: /* extend range ending */
603 range_end = msn;
604 break;
605 default:
606 state = 1;
607 range_begin = msn;
608 break;
609 }
610 }
611 else if (state)
612 {
613 if (first_chunk)
614 first_chunk = false;
615 else
616 buf_addch(buf, ',');
617
618 if (state == 1)
619 buf_add_printf(buf, "%u", range_begin);
620 else if (state == 2)
621 buf_add_printf(buf, "%u:%u", range_begin, range_end);
622 state = 0;
623
624 if ((buf_len(buf) > 500) || (msn_count >= max_headers_per_fetch))
625 break;
626 }
627 }
628
629 /* The loop index goes one past to terminate the range if needed. */
630 *fetch_msn_end = msn - 1;
631
632 return msn_count;
633}
int buf_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition buffer.c:161
int buf_add_printf(struct Buffer *buf, const char *fmt,...)
Format a string appending a Buffer.
Definition buffer.c:204
size_t buf_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition buffer.c:491
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition buffer.c:76
size_t buf_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition buffer.c:241
long cs_subset_long(const struct ConfigSubset *sub, const char *name)
Get a long config item by name.
Definition helpers.c:95
struct Email * imap_msn_get(const struct MSNArray *msn, int idx)
Return the Email associated with an msn.
Definition msn.c:83
#define FALLTHROUGH
Definition lib.h:113
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ set_changed_flag()

static void set_changed_flag ( struct Mailbox * m,
struct Email * e,
int local_changes,
bool * server_changes,
enum MessageType flag_name,
bool old_hd_flag,
bool new_hd_flag,
bool h_flag )
static

Have the flags of an email changed.

Parameters
[in]mMailbox
[in]eEmail
[in]local_changesHas the local mailbox been changed?
[out]server_changesSet to true if the flag has changed
[in]flag_nameFlag to check, e.g. MUTT_FLAG
[in]old_hd_flagOld header flags
[in]new_hd_flagNew header flags
[in]h_flagEmail's value for flag_name

Sets server_changes to 1 if a change to a flag is made, or in the case of local_changes, if a change to a flag would have been made.

Definition at line 650 of file message.c.

653{
654 /* If there are local_changes, we only want to note if the server
655 * flags have changed, so we can set a reopen flag in
656 * cmd_parse_fetch(). We don't want to count a local modification
657 * to the header flag as a "change". */
658 if ((old_hd_flag == new_hd_flag) && (local_changes != 0))
659 return;
660
661 if (new_hd_flag == h_flag)
662 return;
663
664 if (server_changes)
665 *server_changes = true;
666
667 /* Local changes have priority */
668 if (local_changes == 0)
669 mutt_set_flag(m, e, flag_name, new_hd_flag, true);
670}
void mutt_set_flag(struct Mailbox *m, struct Email *e, enum MessageType flag, bool bf, bool upd_mbox)
Set a flag on an email.
Definition flags.c:56
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ read_headers_normal_eval_cache()

static int read_headers_normal_eval_cache ( struct ImapAccountData * adata,
unsigned int msn_end,
unsigned int uid_next,
bool store_flag_updates,
bool eval_condstore )
static

Retrieve data from the header cache.

Parameters
adataImap Account data
msn_endLast Message Sequence number
uid_nextUID of next email
store_flag_updatesif true, save flags to the header cache
eval_condstoreif true, use CONDSTORE to fetch flags
Return values
0Success
-1Error

Without CONDSTORE or QRESYNC, we need to query all the current UIDs and update their flag state and current MSN.

For CONDSTORE, we still need to grab the existing UIDs and their MSN. The current flag state will be queried in read_headers_condstore_qresync_updates().

Definition at line 690 of file message.c.

693{
694 struct Progress *progress = NULL;
695 char buf[1024] = { 0 };
696 int rc = -1;
697
698 struct Mailbox *m = adata->mailbox;
699 struct ImapMboxData *mdata = imap_mdata_get(m);
700 if (!mdata)
701 return -1;
702
703 int idx = m->msg_count;
704
705 if (m->verbose)
706 {
707 /* L10N: Comparing the cached data with the IMAP server's data */
708 progress = progress_new(MUTT_PROGRESS_READ, msn_end);
709 progress_set_message(progress, _("Evaluating cache..."));
710 }
711
712 /* If we are using CONDSTORE's "FETCH CHANGEDSINCE", then we keep
713 * the flags in the header cache, and update them further below.
714 * Otherwise, we fetch the current state of the flags here. */
715 snprintf(buf, sizeof(buf), "UID FETCH 1:%u (UID%s)", uid_next - 1,
716 eval_condstore ? "" : " FLAGS");
717
718 imap_cmd_start(adata, buf);
719
721 int mfhrc = 0;
722 struct ImapHeader h = { 0 };
723 for (int msgno = 1; rc == IMAP_RES_CONTINUE; msgno++)
724 {
726 goto fail;
727
728 progress_update(progress, msgno, -1);
729
730 memset(&h, 0, sizeof(h));
731 h.edata = imap_edata_new();
732 do
733 {
734 rc = imap_cmd_step(adata);
735 if (rc != IMAP_RES_CONTINUE)
736 break;
737
738 mfhrc = msg_fetch_header(m, &h, adata->buf, NULL);
739 if (mfhrc < 0)
740 continue;
741
742 if (!h.edata->uid)
743 {
744 mutt_debug(LL_DEBUG2, "skipping hcache FETCH response for message number %d missing a UID\n",
745 h.edata->msn);
746 continue;
747 }
748
749 if ((h.edata->msn < 1) || (h.edata->msn > msn_end))
750 {
751 mutt_debug(LL_DEBUG1, "skipping hcache FETCH response for unknown message number %d\n",
752 h.edata->msn);
753 continue;
754 }
755
756 if (imap_msn_get(&mdata->msn, h.edata->msn - 1))
757 {
758 mutt_debug(LL_DEBUG2, "skipping hcache FETCH for duplicate message %d\n",
759 h.edata->msn);
760 continue;
761 }
762
763 struct Email *e = imap_hcache_get(mdata, h.edata->uid);
764 m->emails[idx] = e;
765 if (e)
766 {
767 imap_msn_set(&mdata->msn, h.edata->msn - 1, e);
768 mutt_hash_int_insert(mdata->uid_hash, h.edata->uid, e);
769
770 e->index = h.edata->uid;
771 /* messages which have not been expunged are ACTIVE (borrowed from mh
772 * folders) */
773 e->active = true;
774 e->changed = false;
775 if (eval_condstore)
776 {
777 h.edata->read = e->read;
778 h.edata->old = e->old;
779 h.edata->deleted = e->deleted;
780 h.edata->flagged = e->flagged;
781 h.edata->replied = e->replied;
782 }
783 else
784 {
785 e->read = h.edata->read;
786 e->old = h.edata->old;
787 e->deleted = h.edata->deleted;
788 e->flagged = h.edata->flagged;
789 e->replied = h.edata->replied;
790 }
791
792 /* mailbox->emails[msgno]->received is restored from hcache_fetch_email() */
793 e->edata = h.edata;
795
796 /* We take a copy of the tags so we can split the string */
797 char *tags_copy = mutt_str_dup(h.edata->flags_remote);
798 driver_tags_replace(&e->tags, tags_copy);
799 FREE(&tags_copy);
800
801 m->msg_count++;
802 mailbox_size_add(m, e);
803
804 /* If this is the first time we are fetching, we need to
805 * store the current state of flags back into the header cache */
806 if (!eval_condstore && store_flag_updates)
807 imap_hcache_put(mdata, e);
808
809 h.edata = NULL;
810 idx++;
811 }
812 } while (mfhrc == -1);
813
814 imap_edata_free((void **) &h.edata);
815
816 if ((mfhrc < -1) || ((rc != IMAP_RES_CONTINUE) && (rc != IMAP_RES_OK)))
817 goto fail;
818 }
819
820 rc = 0;
821fail:
822 progress_free(&progress);
823 return rc;
824}
void mailbox_size_add(struct Mailbox *m, const struct Email *e)
Add an email's size to the total size of a Mailbox.
Definition mailbox.c:247
void imap_edata_free(void **ptr)
Free the private Email data - Implements Email::edata_free() -.
Definition edata.c:39
struct HashElem * mutt_hash_int_insert(struct HashTable *table, unsigned int intkey, void *data)
Add a new element to the Hash Table (with integer keys)
Definition hash.c:347
int imap_cmd_start(struct ImapAccountData *adata, const char *cmdstr)
Given an IMAP command, send it to the server.
Definition command.c:1133
struct ImapEmailData * imap_edata_new(void)
Create a new ImapEmailData.
Definition edata.c:56
static int msg_fetch_header(struct Mailbox *m, struct ImapHeader *ih, char *buf, FILE *fp)
Import IMAP FETCH response into an ImapHeader.
Definition message.c:423
static bool query_abort_header_download(struct ImapAccountData *adata)
Ask the user whether to abort the download.
Definition message.c:507
#define IMAP_RES_OK
<tag> OK ...
Definition private.h:55
int imap_hcache_put(struct ImapMboxData *mdata, struct Email *e)
Add an entry to the header cache.
Definition util.c:383
struct Email * imap_hcache_get(struct ImapMboxData *mdata, unsigned int uid)
Get a header cache entry by its UID.
Definition util.c:358
@ LL_DEBUG2
Log at debug level 2.
Definition logging2.h:45
void imap_msn_set(struct MSNArray *msn, size_t idx, struct Email *e)
Cache an Email into a given position.
Definition msn.c:95
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition string.c:255
@ MUTT_PROGRESS_READ
Progress tracks elements, according to $read_inc
Definition lib.h:83
struct Progress * progress_new(enum ProgressType type, size_t size)
Create a new Progress Bar.
Definition progress.c:139
void progress_free(struct Progress **ptr)
Free a Progress Bar.
Definition progress.c:110
void progress_set_message(struct Progress *progress, const char *fmt,...) __attribute__((__format__(__printf__
bool progress_update(struct Progress *progress, size_t pos, int percent)
Update the state of the progress bar.
Definition progress.c:80
The envelope/body of an email.
Definition email.h:39
bool read
Email is read.
Definition email.h:50
bool active
Message is not to be removed.
Definition email.h:76
bool old
Email is seen, but unread.
Definition email.h:49
void(* edata_free)(void **ptr)
Definition email.h:90
bool changed
Email has been edited.
Definition email.h:77
bool flagged
Marked important?
Definition email.h:47
bool replied
Email has been replied to.
Definition email.h:51
struct TagList tags
For drivers that support server tagging.
Definition email.h:72
bool deleted
Email is deleted.
Definition email.h:78
int index
The absolute (unsorted) message number.
Definition email.h:110
bool deleted
Email has been deleted.
Definition edata.h:39
bool old
Email has been seen.
Definition edata.h:38
bool read
Email has been read.
Definition edata.h:37
bool flagged
Email has been flagged.
Definition edata.h:40
bool replied
Email has been replied to.
Definition edata.h:41
IMAP-specific header.
Definition message.h:34
unsigned int uid_next
Definition mdata.h:52
struct HashTable * uid_hash
Hash Table: "uid" -> Email.
Definition mdata.h:59
A mailbox.
Definition mailbox.h:79
int msg_count
Total number of messages.
Definition mailbox.h:88
struct Email ** emails
Array of Emails.
Definition mailbox.h:96
bool verbose
Display status messages?
Definition mailbox.h:117
bool driver_tags_replace(struct TagList *tl, const char *tags)
Replace all tags.
Definition tags.c:201
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ read_headers_qresync_eval_cache()

static int read_headers_qresync_eval_cache ( struct ImapAccountData * adata,
char * uid_seqset )
static

Retrieve data from the header cache.

Parameters
adataImap Account data
uid_seqsetSequence Set of UIDs
Return values
>=0Success
-1Error

For QRESYNC, we grab the UIDs in order by MSN from the header cache.

In read_headers_condstore_qresync_updates(). We will update change flags using CHANGEDSINCE and find out what UIDs have been expunged using VANISHED.

Definition at line 838 of file message.c.

839{
840 int rc;
841 unsigned int uid = 0;
842
843 mutt_debug(LL_DEBUG2, "Reading uid seqset from header cache\n");
844 struct Mailbox *m = adata->mailbox;
845 unsigned int msn = 1;
846 struct ImapMboxData *mdata = adata->mailbox->mdata;
847 if (!mdata)
848 return -1;
849
850 if (m->verbose)
851 mutt_message(_("Evaluating cache..."));
852
853 struct SeqsetIterator *iter = mutt_seqset_iterator_new(uid_seqset);
854 if (!iter)
855 return -1;
856
857 while ((rc = mutt_seqset_iterator_next(iter, &uid)) == 0)
858 {
859 /* The seqset may contain more headers than the fetch request, so
860 * we need to watch and reallocate the context and msn_index */
861 imap_msn_reserve(&mdata->msn, msn);
862
863 struct Email *e = imap_hcache_get(mdata, uid);
864 if (e)
865 {
866 imap_msn_set(&mdata->msn, msn - 1, e);
867
869
871 e->edata = edata;
873
874 e->index = uid;
875 e->active = true;
876 e->changed = false;
877 edata->read = e->read;
878 edata->old = e->old;
879 edata->deleted = e->deleted;
880 edata->flagged = e->flagged;
881 edata->replied = e->replied;
882
883 edata->msn = msn;
884 edata->uid = uid;
886
887 mailbox_size_add(m, e);
888 m->emails[m->msg_count++] = e;
889
890 msn++;
891 }
892 else if (!uid)
893 {
894 /* A non-zero uid missing from the header cache is either the
895 * result of an expunged message (not recorded in the uid seqset)
896 * or a hole in the header cache.
897 *
898 * We have to assume it's an earlier expunge and compact the msn's
899 * in that case, because cmd_parse_vanished() won't find it in the
900 * uid_hash and decrement later msn's there.
901 *
902 * Thus we only increment the uid if the uid was 0: an actual
903 * stored "blank" in the uid seqset.
904 */
905 msn++;
906 }
907 }
908
910
911 return rc;
912}
#define mutt_message(...)
Definition logging2.h:92
struct SeqsetIterator * mutt_seqset_iterator_new(const char *seqset)
Create a new Sequence Set Iterator.
Definition util.c:1127
int mutt_seqset_iterator_next(struct SeqsetIterator *iter, unsigned int *next)
Get the next UID from a Sequence Set.
Definition util.c:1148
void mutt_seqset_iterator_free(struct SeqsetIterator **ptr)
Free a Sequence Set Iterator.
Definition util.c:1207
void imap_msn_reserve(struct MSNArray *msn, size_t num)
Create / reallocate the cache.
Definition msn.c:44
void mx_alloc_memory(struct Mailbox *m, int req_size)
Create storage for the emails.
Definition mx.c:1211
UID Sequence Set Iterator.
Definition private.h:169
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ read_headers_condstore_qresync_updates()

static int read_headers_condstore_qresync_updates ( struct ImapAccountData * adata,
unsigned int msn_end,
unsigned int uid_next,
unsigned long long hc_modseq,
bool eval_qresync )
static

Retrieve updates from the server.

Parameters
adataImap Account data
msn_endLast Message Sequence number
uid_nextUID of next email
hc_modseqTimestamp of last Header Cache update
eval_qresyncIf true, use QRESYNC
Return values
0Success
-1Error

CONDSTORE and QRESYNC use FETCH extensions to grab updates.

Definition at line 926 of file message.c.

929{
930 struct Progress *progress = NULL;
931 char buf[1024] = { 0 };
932 unsigned int header_msn = 0;
933
934 struct Mailbox *m = adata->mailbox;
935 struct ImapMboxData *mdata = imap_mdata_get(m);
936 if (!mdata)
937 return -1;
938
939 if (m->verbose)
940 {
941 /* L10N: Fetching IMAP flag changes, using the CONDSTORE extension */
942 progress = progress_new(MUTT_PROGRESS_READ, msn_end);
943 progress_set_message(progress, _("Fetching flag updates..."));
944 }
945
946 snprintf(buf, sizeof(buf), "UID FETCH 1:%u (FLAGS) (CHANGEDSINCE %llu%s)",
947 uid_next - 1, hc_modseq, eval_qresync ? " VANISHED" : "");
948
949 imap_cmd_start(adata, buf);
950
951 int rc = IMAP_RES_CONTINUE;
952 for (int msgno = 1; rc == IMAP_RES_CONTINUE; msgno++)
953 {
955 goto fail;
956
957 progress_update(progress, msgno, -1);
958
959 /* cmd_parse_fetch will update the flags */
960 rc = imap_cmd_step(adata);
961 if (rc != IMAP_RES_CONTINUE)
962 break;
963
964 /* so we just need to grab the header and persist it back into
965 * the header cache */
966 char *fetch_buf = adata->buf;
967 if (fetch_buf[0] != '*')
968 continue;
969
970 fetch_buf = imap_next_word(fetch_buf);
971 if (!mutt_isdigit(*fetch_buf) || !mutt_str_atoui(fetch_buf, &header_msn))
972 continue;
973
974 if ((header_msn < 1) || (header_msn > msn_end) ||
975 !imap_msn_get(&mdata->msn, header_msn - 1))
976 {
977 mutt_debug(LL_DEBUG1, "skipping CONDSTORE flag update for unknown message number %u\n",
978 header_msn);
979 continue;
980 }
981
982 imap_hcache_put(mdata, imap_msn_get(&mdata->msn, header_msn - 1));
983 }
984
985 if (rc != IMAP_RES_OK)
986 goto fail;
987
988 /* The IMAP flag setting as part of cmd_parse_fetch() ends up
989 * flipping these on. */
990 mdata->check_status &= ~IMAP_FLAGS_PENDING;
991 m->changed = false;
992
993 /* VANISHED handling: we need to empty out the messages */
994 if (mdata->reopen & IMAP_EXPUNGE_PENDING)
995 {
997 imap_expunge_mailbox(m, false);
998
999 imap_hcache_open(adata, mdata, false);
1000 mdata->reopen &= ~IMAP_EXPUNGE_PENDING;
1001 }
1002
1003 /* undo expunge count updates.
1004 * mview_update() will do this at the end of the header fetch. */
1005 m->vcount = 0;
1006 m->msg_tagged = 0;
1007 m->msg_deleted = 0;
1008 m->msg_new = 0;
1009 m->msg_unread = 0;
1010 m->msg_flagged = 0;
1011 m->changed = false;
1012
1013 rc = 0;
1014fail:
1015 progress_free(&progress);
1016 return rc;
1017}
#define IMAP_EXPUNGE_PENDING
Messages on the server have been expunged.
Definition private.h:66
void imap_hcache_open(struct ImapAccountData *adata, struct ImapMboxData *mdata, bool create)
Open a header cache.
Definition util.c:302
#define IMAP_FLAGS_PENDING
Flags have changed on the server.
Definition private.h:68
void imap_hcache_close(struct ImapMboxData *mdata)
Close the header cache.
Definition util.c:343
void imap_expunge_mailbox(struct Mailbox *m, bool resort)
Purge messages from the server.
Definition imap.c:689
int vcount
The number of virtual messages.
Definition mailbox.h:99
bool changed
Mailbox has been modified.
Definition mailbox.h:110
int msg_new
Number of new messages.
Definition mailbox.h:92
int msg_deleted
Number of deleted messages.
Definition mailbox.h:93
int msg_flagged
Number of flagged messages.
Definition mailbox.h:90
int msg_tagged
How many messages are tagged?
Definition mailbox.h:94
int msg_unread
Number of unread messages.
Definition mailbox.h:89
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_verify_qresync()

static int imap_verify_qresync ( struct Mailbox * m)
static

Check to see if QRESYNC got jumbled.

Parameters
mImap Selected Mailbox
Return values
0Success
-1Error

If so, wipe the context and try again with a normal download.

Definition at line 1027 of file message.c.

1028{
1029 ASSERT(m);
1031 struct ImapMboxData *mdata = imap_mdata_get(m);
1032 if (!adata || (adata->mailbox != m) || !mdata)
1033 return -1;
1034
1035 const size_t max_msn = imap_msn_highest(&mdata->msn);
1036
1037 unsigned int msn;
1038 unsigned int uid;
1039 struct Email *e = NULL;
1040 struct Email *uidh = NULL;
1041
1042 for (int i = 0; i < m->msg_count; i++)
1043 {
1044 e = m->emails[i];
1045 const struct ImapEmailData *edata = imap_edata_get(e);
1046 if (!edata)
1047 goto fail;
1048
1049 msn = imap_edata_get(e)->msn;
1050 uid = imap_edata_get(e)->uid;
1051
1052 if ((msn < 1) || (msn > max_msn) || imap_msn_get(&mdata->msn, msn - 1) != e)
1053 goto fail;
1054
1055 uidh = (struct Email *) mutt_hash_int_find(mdata->uid_hash, uid);
1056 if (uidh != e)
1057 goto fail;
1058 }
1059
1060 return 0;
1061
1062fail:
1063 imap_msn_free(&mdata->msn);
1064 mutt_hash_free(&mdata->uid_hash);
1068
1069 for (int i = 0; i < m->msg_count; i++)
1070 {
1071 if (m->emails[i] && m->emails[i]->edata)
1072 imap_edata_free(&m->emails[i]->edata);
1073 email_free(&m->emails[i]);
1074 }
1075 m->msg_count = 0;
1076 m->size = 0;
1077 hcache_delete_raw(mdata->hcache, "MODSEQ", 6);
1079 imap_hcache_close(mdata);
1080
1081 if (m->verbose)
1082 {
1083 /* L10N: After opening an IMAP mailbox using QRESYNC, Mutt performs a quick
1084 sanity check. If that fails, Mutt reopens the mailbox using a normal
1085 download. */
1086 mutt_error(_("QRESYNC failed. Reopening mailbox."));
1087 }
1088 return -1;
1089}
void email_free(struct Email **ptr)
Free an Email.
Definition email.c:46
#define mutt_error(...)
Definition logging2.h:93
void * mutt_hash_int_find(const struct HashTable *table, unsigned int intkey)
Find the HashElem data in a Hash Table element using a key.
Definition hash.c:392
void mutt_hash_free(struct HashTable **ptr)
Free a hash table.
Definition hash.c:457
int hcache_delete_raw(struct HeaderCache *hc, const char *key, size_t keylen)
Multiplexor for StoreOps::delete_record.
Definition hcache.c:754
int imap_hcache_clear_uid_seqset(struct ImapMboxData *mdata)
Delete a UID Sequence Set from the header cache.
Definition util.c:439
void imap_msn_free(struct MSNArray *msn)
Free the cache.
Definition msn.c:62
size_t imap_msn_highest(const struct MSNArray *msn)
Return the highest MSN in use.
Definition msn.c:72
#define ASSERT(COND)
Definition signal2.h:60
struct HeaderCache * hcache
Email header cache.
Definition mdata.h:63
struct HashTable * subj_hash
Hash Table: "subject" -> Email.
Definition mailbox.h:124
struct HashTable * id_hash
Hash Table: "message-id" -> Email.
Definition mailbox.h:123
off_t size
Size of the Mailbox.
Definition mailbox.h:84
struct HashTable * label_hash
Hash Table: "x-labels" -> Email.
Definition mailbox.h:125
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ read_headers_fetch_new()

static int read_headers_fetch_new ( struct Mailbox * m,
unsigned int msn_begin,
unsigned int msn_end,
bool evalhc,
unsigned int * maxuid,
bool initial_download )
static

Retrieve new messages from the server.

Parameters
[in]mImap Selected Mailbox
[in]msn_beginFirst Message Sequence number
[in]msn_endLast Message Sequence number
[in]evalhcIf true, check the Header Cache
[out]maxuidHighest UID seen
[in]initial_downloadtrue, if this is the first opening of the mailbox
Return values
0Success
-1Error

Definition at line 1104 of file message.c.

1107{
1108 int rc = -1;
1109 unsigned int fetch_msn_end = 0;
1110 struct Progress *progress = NULL;
1111 char *hdrreq = NULL;
1112 struct Buffer *tempfile = NULL;
1113 FILE *fp = NULL;
1114 struct ImapHeader h = { 0 };
1115 struct Buffer *buf = NULL;
1116 static const char *const want_headers = "DATE FROM SENDER SUBJECT TO CC MESSAGE-ID REFERENCES "
1117 "CONTENT-TYPE CONTENT-DESCRIPTION IN-REPLY-TO REPLY-TO "
1118 "LINES LIST-POST LIST-SUBSCRIBE LIST-UNSUBSCRIBE X-LABEL "
1119 "X-ORIGINAL-TO";
1120
1122 struct ImapMboxData *mdata = imap_mdata_get(m);
1123 struct ImapEmailData *edata = NULL;
1124
1125 if (!adata || (adata->mailbox != m) || !mdata)
1126 return -1;
1127
1128 struct Buffer *hdr_list = buf_pool_get();
1129 buf_strcpy(hdr_list, want_headers);
1130 const char *const c_imap_headers = cs_subset_string(NeoMutt->sub, "imap_headers");
1131 if (c_imap_headers)
1132 {
1133 buf_addch(hdr_list, ' ');
1134 buf_addstr(hdr_list, c_imap_headers);
1135 }
1136#ifdef USE_AUTOCRYPT
1137 const bool c_autocrypt = cs_subset_bool(NeoMutt->sub, "autocrypt");
1138 if (c_autocrypt)
1139 {
1140 buf_addch(hdr_list, ' ');
1141 buf_addstr(hdr_list, "AUTOCRYPT");
1142 }
1143#endif
1144
1145 if (adata->capabilities & IMAP_CAP_IMAP4REV1)
1146 {
1147 mutt_str_asprintf(&hdrreq, "BODY.PEEK[HEADER.FIELDS (%s)]", buf_string(hdr_list));
1148 }
1149 else if (adata->capabilities & IMAP_CAP_IMAP4)
1150 {
1151 mutt_str_asprintf(&hdrreq, "RFC822.HEADER.LINES (%s)", buf_string(hdr_list));
1152 }
1153 else
1154 { /* Unable to fetch headers for lower versions */
1155 mutt_error(_("Unable to fetch headers from this IMAP server version"));
1156 goto bail;
1157 }
1158
1159 buf_pool_release(&hdr_list);
1160
1161 /* instead of downloading all headers and then parsing them, we parse them
1162 * as they come in. */
1163 tempfile = buf_pool_get();
1164 buf_mktemp(tempfile);
1165 fp = mutt_file_fopen(buf_string(tempfile), "w+");
1166 if (!fp)
1167 {
1168 mutt_error(_("Could not create temporary file %s"), buf_string(tempfile));
1169 goto bail;
1170 }
1171 unlink(buf_string(tempfile));
1172 buf_pool_release(&tempfile);
1173
1174 if (m->verbose)
1175 {
1176 progress = progress_new(MUTT_PROGRESS_READ, msn_end);
1177 progress_set_message(progress, _("Fetching message headers..."));
1178 }
1179
1180 buf = buf_pool_get();
1181
1182 /* NOTE:
1183 * The (fetch_msn_end < msn_end) used to be important to prevent
1184 * an infinite loop, in the event the server did not return all
1185 * the headers (due to a pending expunge, for example).
1186 *
1187 * I believe the new chunking imap_fetch_msn_seqset()
1188 * implementation and "msn_begin = fetch_msn_end + 1" assignment
1189 * at the end of the loop makes the comparison unneeded, but to be
1190 * cautious I'm keeping it.
1191 */
1192 edata = imap_edata_new();
1193 while ((fetch_msn_end < msn_end) &&
1194 imap_fetch_msn_seqset(buf, adata, evalhc, msn_begin, msn_end, &fetch_msn_end))
1195 {
1196 char *cmd = NULL;
1197 mutt_str_asprintf(&cmd, "FETCH %s (UID FLAGS INTERNALDATE RFC822.SIZE %s)",
1198 buf_string(buf), hdrreq);
1199 imap_cmd_start(adata, cmd);
1200 FREE(&cmd);
1201
1202 int msgno = msn_begin;
1203
1204 while (true)
1205 {
1206 rewind(fp);
1207 memset(&h, 0, sizeof(h));
1208 h.edata = edata;
1209
1210 if (initial_download && SigInt && query_abort_header_download(adata))
1211 {
1212 goto bail;
1213 }
1214
1215 const int rc2 = imap_cmd_step(adata);
1216 if (rc2 != IMAP_RES_CONTINUE)
1217 {
1218 if (rc2 != IMAP_RES_OK)
1219 {
1220 goto bail;
1221 }
1222 break;
1223 }
1224
1225 switch (msg_fetch_header(m, &h, adata->buf, fp))
1226 {
1227 case 0:
1228 break;
1229 case -1:
1230 continue;
1231 case -2:
1232 goto bail;
1233 }
1234
1235 if (!ftello(fp))
1236 {
1237 mutt_debug(LL_DEBUG2, "ignoring fetch response with no body\n");
1238 continue;
1239 }
1240
1241 /* make sure we don't get remnants from older larger message headers */
1242 fputs("\n\n", fp);
1243
1244 if ((h.edata->msn < 1) || (h.edata->msn > fetch_msn_end))
1245 {
1246 mutt_debug(LL_DEBUG1, "skipping FETCH response for unknown message number %d\n",
1247 h.edata->msn);
1248 continue;
1249 }
1250
1251 /* May receive FLAGS updates in a separate untagged response */
1252 if (imap_msn_get(&mdata->msn, h.edata->msn - 1))
1253 {
1254 mutt_debug(LL_DEBUG2, "skipping FETCH response for duplicate message %d\n",
1255 h.edata->msn);
1256 continue;
1257 }
1258
1259 progress_update(progress, msgno++, -1);
1260
1261 struct Email *e = email_new();
1263
1264 m->emails[m->msg_count++] = e;
1265
1266 imap_msn_set(&mdata->msn, h.edata->msn - 1, e);
1267 mutt_hash_int_insert(mdata->uid_hash, h.edata->uid, e);
1268
1269 e->index = h.edata->uid;
1270 /* messages which have not been expunged are ACTIVE (borrowed from mh
1271 * folders) */
1272 e->active = true;
1273 e->changed = false;
1274 e->read = h.edata->read;
1275 e->old = h.edata->old;
1276 e->deleted = h.edata->deleted;
1277 e->flagged = h.edata->flagged;
1278 e->replied = h.edata->replied;
1279 e->received = h.received;
1280 e->edata = (void *) imap_edata_clone(h.edata);
1282 STAILQ_INIT(&e->tags);
1283
1284 /* We take a copy of the tags so we can split the string */
1285 char *tags_copy = mutt_str_dup(h.edata->flags_remote);
1286 driver_tags_replace(&e->tags, tags_copy);
1287 FREE(&tags_copy);
1288
1289 if (*maxuid < h.edata->uid)
1290 *maxuid = h.edata->uid;
1291
1292 rewind(fp);
1293 /* NOTE: if Date: header is missing, mutt_rfc822_read_header depends
1294 * on h.received being set */
1295 e->env = mutt_rfc822_read_header(fp, e, false, false);
1296 /* body built as a side-effect of mutt_rfc822_read_header */
1297 e->body->length = h.content_length;
1298 mailbox_size_add(m, e);
1299
1300#ifdef USE_HCACHE
1301 imap_hcache_put(mdata, e);
1302#endif /* USE_HCACHE */
1303 }
1304
1305 /* In case we get new mail while fetching the headers. */
1306 if (mdata->reopen & IMAP_NEWMAIL_PENDING)
1307 {
1308 msn_end = mdata->new_mail_count;
1309 mx_alloc_memory(m, msn_end);
1310 imap_msn_reserve(&mdata->msn, msn_end);
1311 mdata->reopen &= ~IMAP_NEWMAIL_PENDING;
1312 mdata->new_mail_count = 0;
1313 }
1314
1315 /* Note: RFC3501 section 7.4.1 and RFC7162 section 3.2.10.2 say we
1316 * must not get any EXPUNGE/VANISHED responses in the middle of a
1317 * FETCH, nor when no command is in progress (e.g. between the
1318 * chunked FETCH commands). We previously tried to be robust by
1319 * setting:
1320 * msn_begin = mdata->max_msn + 1;
1321 * but with chunking and header cache holes this
1322 * may not be correct. So here we must assume the msn values have
1323 * not been altered during or after the fetch. */
1324 msn_begin = fetch_msn_end + 1;
1325 }
1326
1327 rc = 0;
1328
1329bail:
1330 buf_pool_release(&hdr_list);
1331 buf_pool_release(&buf);
1332 buf_pool_release(&tempfile);
1333 mutt_file_fclose(&fp);
1334 FREE(&hdrreq);
1335 imap_edata_free((void **) &edata);
1336 progress_free(&progress);
1337
1338 return rc;
1339}
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition buffer.c:395
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition helpers.c:291
struct Email * email_new(void)
Create a new Email.
Definition email.c:77
struct Envelope * mutt_rfc822_read_header(FILE *fp, struct Email *e, bool user_hdrs, bool weed)
Parses an RFC822 header.
Definition parse.c:1204
#define mutt_file_fclose(FP)
Definition file.h:139
#define mutt_file_fopen(PATH, MODE)
Definition file.h:138
struct ImapEmailData * imap_edata_clone(struct ImapEmailData *src)
Clone an ImapEmailData.
Definition edata.c:78
static unsigned int imap_fetch_msn_seqset(struct Buffer *buf, struct ImapAccountData *adata, bool evalhc, unsigned int msn_begin, unsigned int msn_end, unsigned int *fetch_msn_end)
Generate a sequence set.
Definition message.c:555
#define IMAP_CAP_IMAP4
Server supports IMAP4.
Definition private.h:121
#define IMAP_CAP_IMAP4REV1
Server supports IMAP4rev1.
Definition private.h:122
#define IMAP_NEWMAIL_PENDING
New mail is waiting on the server.
Definition private.h:67
int mutt_str_asprintf(char **strp, const char *fmt,...)
Definition string.c:803
#define STAILQ_INIT(head)
Definition queue.h:410
LOFF_T length
length (in bytes) of attachment
Definition body.h:53
struct Envelope * env
Envelope information.
Definition email.h:68
struct Body * body
List of MIME parts.
Definition email.h:69
time_t received
Time when the message was placed in the mailbox.
Definition email.h:61
ImapCapFlags capabilities
Capability flags.
Definition adata.h:55
ImapOpenFlags reopen
Flags, e.g. IMAP_REOPEN_ALLOW.
Definition mdata.h:45
unsigned int new_mail_count
Set when EXISTS notifies of new mail.
Definition mdata.h:47
#define buf_mktemp(buf)
Definition tmp.h:33
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_read_headers()

int imap_read_headers ( struct Mailbox * m,
unsigned int msn_begin,
unsigned int msn_end,
bool initial_download )

Read headers from the server.

Parameters
mImap Selected Mailbox
msn_beginFirst Message Sequence Number
msn_endLast Message Sequence Number
initial_downloadtrue, if this is the first opening of the mailbox
Return values
numLast MSN
-1Failure

Changed to read many headers instead of just one. It will return the msn of the last message read. It will return a value other than msn_end if mail comes in while downloading headers (in theory).

Definition at line 1354 of file message.c.

1356{
1357 unsigned int maxuid = 0;
1358 int rc = -1;
1359 bool evalhc = false;
1360
1361#ifdef USE_HCACHE
1362 uint32_t uidvalidity = 0;
1363 unsigned int uid_next = 0;
1364 unsigned long long modseq = 0;
1365 bool has_condstore = false;
1366 bool has_qresync = false;
1367 bool eval_condstore = false;
1368 bool eval_qresync = false;
1369 char *uid_seqset = NULL;
1370 const unsigned int msn_begin_save = msn_begin;
1371#endif /* USE_HCACHE */
1372
1374 struct ImapMboxData *mdata = imap_mdata_get(m);
1375 if (!adata || (adata->mailbox != m) || !mdata)
1376 return -1;
1377
1378#ifdef USE_HCACHE
1379retry:
1380#endif /* USE_HCACHE */
1381
1382 /* make sure context has room to hold the mailbox */
1383 mx_alloc_memory(m, msn_end);
1384 imap_msn_reserve(&mdata->msn, msn_end);
1385 imap_alloc_uid_hash(adata, msn_end);
1386
1388 mdata->new_mail_count = 0;
1389
1390#ifdef USE_HCACHE
1391 imap_hcache_open(adata, mdata, true);
1392
1393 if (mdata->hcache && initial_download)
1394 {
1395 hcache_fetch_raw_obj(mdata->hcache, "UIDVALIDITY", 11, &uidvalidity);
1396 hcache_fetch_raw_obj(mdata->hcache, "UIDNEXT", 7, &uid_next);
1397 if (mdata->modseq)
1398 {
1399 const bool c_imap_condstore = cs_subset_bool(NeoMutt->sub, "imap_condstore");
1400 if ((adata->capabilities & IMAP_CAP_CONDSTORE) && c_imap_condstore)
1401 has_condstore = true;
1402
1403 /* If IMAP_CAP_QRESYNC and ImapQResync then NeoMutt sends ENABLE QRESYNC.
1404 * If we receive an ENABLED response back, then adata->qresync is set. */
1405 if (adata->qresync)
1406 has_qresync = true;
1407 }
1408
1409 if (uidvalidity && uid_next && (uidvalidity == mdata->uidvalidity))
1410 {
1411 evalhc = true;
1412 if (hcache_fetch_raw_obj(mdata->hcache, "MODSEQ", 6, &modseq))
1413 {
1414 if (has_qresync)
1415 {
1416 uid_seqset = imap_hcache_get_uid_seqset(mdata);
1417 if (uid_seqset)
1418 eval_qresync = true;
1419 }
1420
1421 if (!eval_qresync && has_condstore)
1422 eval_condstore = true;
1423 }
1424 }
1425 }
1426 if (evalhc)
1427 {
1428 if (eval_qresync)
1429 {
1430 if (read_headers_qresync_eval_cache(adata, uid_seqset) < 0)
1431 goto bail;
1432 }
1433 else
1434 {
1435 if (read_headers_normal_eval_cache(adata, msn_end, uid_next, has_condstore || has_qresync,
1436 eval_condstore) < 0)
1437 goto bail;
1438 }
1439
1440 if ((eval_condstore || eval_qresync) && (modseq != mdata->modseq))
1441 {
1443 modseq, eval_qresync) < 0)
1444 {
1445 goto bail;
1446 }
1447 }
1448
1449 /* Look for the first empty MSN and start there */
1450 while (msn_begin <= msn_end)
1451 {
1452 if (!imap_msn_get(&mdata->msn, msn_begin - 1))
1453 break;
1454 msn_begin++;
1455 }
1456 }
1457#endif /* USE_HCACHE */
1458
1459 if (read_headers_fetch_new(m, msn_begin, msn_end, evalhc, &maxuid, initial_download) < 0)
1460 goto bail;
1461
1462#ifdef USE_HCACHE
1463 if (eval_qresync && initial_download)
1464 {
1465 if (imap_verify_qresync(m) != 0)
1466 {
1467 eval_qresync = false;
1468 eval_condstore = false;
1469 evalhc = false;
1470 modseq = 0;
1471 maxuid = 0;
1472 FREE(&uid_seqset);
1473 uidvalidity = 0;
1474 uid_next = 0;
1475 msn_begin = msn_begin_save;
1476
1477 goto retry;
1478 }
1479 }
1480#endif /* USE_HCACHE */
1481
1482 if (maxuid && (mdata->uid_next < maxuid + 1))
1483 mdata->uid_next = maxuid + 1;
1484
1485#ifdef USE_HCACHE
1486 hcache_store_raw(mdata->hcache, "UIDVALIDITY", 11, &mdata->uidvalidity,
1487 sizeof(mdata->uidvalidity));
1488 if (maxuid && (mdata->uid_next < maxuid + 1))
1489 {
1490 mutt_debug(LL_DEBUG2, "Overriding UIDNEXT: %u -> %u\n", mdata->uid_next, maxuid + 1);
1491 mdata->uid_next = maxuid + 1;
1492 }
1493 if (mdata->uid_next > 1)
1494 {
1495 hcache_store_raw(mdata->hcache, "UIDNEXT", 7, &mdata->uid_next, sizeof(mdata->uid_next));
1496 }
1497
1498 /* We currently only sync CONDSTORE and QRESYNC on the initial download.
1499 * To do it more often, we'll need to deal with flag updates combined with
1500 * unsync'ed local flag changes. We'll also need to properly sync flags to
1501 * the header cache on close. I'm not sure it's worth the added complexity. */
1502 if (initial_download)
1503 {
1504 if (has_condstore || has_qresync)
1505 {
1506 hcache_store_raw(mdata->hcache, "MODSEQ", 6, &mdata->modseq, sizeof(mdata->modseq));
1507 }
1508 else
1509 {
1510 hcache_delete_raw(mdata->hcache, "MODSEQ", 6);
1511 }
1512
1513 if (has_qresync)
1515 else
1517 }
1518#endif /* USE_HCACHE */
1519
1520 /* TODO: it's not clear to me why we are calling mx_alloc_memory yet again. */
1522
1523 mdata->reopen |= IMAP_REOPEN_ALLOW;
1524
1525 rc = msn_end;
1526
1527bail:
1528#ifdef USE_HCACHE
1530 FREE(&uid_seqset);
1531#endif /* USE_HCACHE */
1532
1533 return rc;
1534}
int hcache_store_raw(struct HeaderCache *hc, const char *key, size_t keylen, void *data, size_t dlen)
Store a key / data pair.
Definition hcache.c:726
#define hcache_fetch_raw_obj(hc, key, keylen, dst)
Definition lib.h:162
static int imap_verify_qresync(struct Mailbox *m)
Check to see if QRESYNC got jumbled.
Definition message.c:1027
static int read_headers_condstore_qresync_updates(struct ImapAccountData *adata, unsigned int msn_end, unsigned int uid_next, unsigned long long hc_modseq, bool eval_qresync)
Retrieve updates from the server.
Definition message.c:926
static int read_headers_fetch_new(struct Mailbox *m, unsigned int msn_begin, unsigned int msn_end, bool evalhc, unsigned int *maxuid, bool initial_download)
Retrieve new messages from the server.
Definition message.c:1104
static int read_headers_normal_eval_cache(struct ImapAccountData *adata, unsigned int msn_end, unsigned int uid_next, bool store_flag_updates, bool eval_condstore)
Retrieve data from the header cache.
Definition message.c:690
static int read_headers_qresync_eval_cache(struct ImapAccountData *adata, char *uid_seqset)
Retrieve data from the header cache.
Definition message.c:838
static void imap_alloc_uid_hash(struct ImapAccountData *adata, unsigned int msn_count)
Create a Hash Table for the UIDs.
Definition message.c:531
int imap_hcache_store_uid_seqset(struct ImapMboxData *mdata)
Store a UID Sequence Set in the header cache.
Definition util.c:418
#define IMAP_REOPEN_ALLOW
Allow re-opening a folder upon expunge.
Definition private.h:64
char * imap_hcache_get_uid_seqset(struct ImapMboxData *mdata)
Get a UID Sequence Set from the header cache.
Definition util.c:453
#define IMAP_CAP_CONDSTORE
RFC7162.
Definition private.h:136
bool qresync
true, if QRESYNC is successfully ENABLE'd
Definition adata.h:63
unsigned long long modseq
Definition mdata.h:53
uint32_t uidvalidity
Definition mdata.h:51
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_append_message()

int imap_append_message ( struct Mailbox * m,
struct Message * msg )

Write an email back to the server.

Parameters
mMailbox
msgMessage to save
Return values
0Success
-1Failure

Definition at line 1543 of file message.c.

1544{
1545 if (!m || !msg)
1546 return -1;
1547
1549 struct ImapMboxData *mdata = imap_mdata_get(m);
1550 if (!adata || !mdata)
1551 return -1;
1552
1553 FILE *fp = NULL;
1554 char buf[2048] = { 0 };
1555 struct Buffer *internaldate = NULL;
1556 struct Buffer *imap_flags = NULL;
1557 size_t len;
1558 struct Progress *progress = NULL;
1559 size_t sent;
1560 int c, last;
1561 int rc;
1562
1563 fp = mutt_file_fopen(msg->path, "r");
1564 if (!fp)
1565 {
1566 mutt_perror("%s", msg->path);
1567 goto fail;
1568 }
1569
1570 /* currently we set the \Seen flag on all messages, but probably we
1571 * should scan the message Status header for flag info. Since we're
1572 * already rereading the whole file for length it isn't any more
1573 * expensive (it'd be nice if we had the file size passed in already
1574 * by the code that writes the file, but that's a lot of changes.
1575 * Ideally we'd have an Email structure with flag info here... */
1576 for (last = EOF, len = 0; (c = fgetc(fp)) != EOF; last = c)
1577 {
1578 if ((c == '\n') && (last != '\r'))
1579 len++;
1580
1581 len++;
1582 }
1583 rewind(fp);
1584
1585 if (m->verbose)
1586 {
1587 progress = progress_new(MUTT_PROGRESS_NET, len);
1588 progress_set_message(progress, _("Uploading message..."));
1589 }
1590
1591 internaldate = buf_pool_get();
1592 mutt_date_make_imap(internaldate, msg->received);
1593
1594 imap_flags = buf_pool_get();
1595
1596 if (msg->flags.read)
1597 buf_addstr(imap_flags, " \\Seen");
1598 if (msg->flags.replied)
1599 buf_addstr(imap_flags, " \\Answered");
1600 if (msg->flags.flagged)
1601 buf_addstr(imap_flags, " \\Flagged");
1602 if (msg->flags.draft)
1603 buf_addstr(imap_flags, " \\Draft");
1604
1605 snprintf(buf, sizeof(buf), "APPEND %s (%s) \"%s\" {%lu}", mdata->munge_name,
1606 imap_flags->data + 1, buf_string(internaldate), (unsigned long) len);
1607 buf_pool_release(&internaldate);
1608
1609 imap_cmd_start(adata, buf);
1610
1611 do
1612 {
1613 rc = imap_cmd_step(adata);
1614 } while (rc == IMAP_RES_CONTINUE);
1615
1616 if (rc != IMAP_RES_RESPOND)
1617 goto cmd_step_fail;
1618
1619 for (last = EOF, sent = len = 0; (c = fgetc(fp)) != EOF; last = c)
1620 {
1621 if ((c == '\n') && (last != '\r'))
1622 buf[len++] = '\r';
1623
1624 buf[len++] = c;
1625
1626 if (len > sizeof(buf) - 3)
1627 {
1628 sent += len;
1629 if (flush_buffer(buf, &len, adata->conn) < 0)
1630 goto fail;
1631 progress_update(progress, sent, -1);
1632 }
1633 }
1634
1635 if (len > 0)
1636 if (flush_buffer(buf, &len, adata->conn) < 0)
1637 goto fail;
1638
1639 if (mutt_socket_send(adata->conn, "\r\n") < 0)
1640 goto fail;
1641 mutt_file_fclose(&fp);
1642
1643 do
1644 {
1645 rc = imap_cmd_step(adata);
1646 } while (rc == IMAP_RES_CONTINUE);
1647
1648 if (rc != IMAP_RES_OK)
1649 goto cmd_step_fail;
1650
1651 progress_free(&progress);
1652 buf_pool_release(&imap_flags);
1653 return 0;
1654
1655cmd_step_fail:
1656 mutt_debug(LL_DEBUG1, "command failed: %s\n", adata->buf);
1657 if (rc != IMAP_RES_BAD)
1658 {
1659 char *pc = imap_next_word(adata->buf); /* skip sequence number or token */
1660 pc = imap_next_word(pc); /* skip response code */
1661 if (*pc != '\0')
1662 mutt_error("%s", pc);
1663 }
1664
1665fail:
1666 mutt_file_fclose(&fp);
1667 progress_free(&progress);
1668 buf_pool_release(&imap_flags);
1669 return -1;
1670}
#define mutt_perror(...)
Definition logging2.h:94
static int flush_buffer(char *buf, size_t *len, struct Connection *conn)
Write data to a connection.
Definition message.c:491
#define IMAP_RES_RESPOND
+
Definition private.h:57
#define IMAP_RES_BAD
<tag> BAD ...
Definition private.h:54
int mutt_date_make_imap(struct Buffer *buf, time_t timestamp)
Format date in IMAP style: DD-MMM-YYYY HH:MM:SS +ZZzz.
Definition date.c:810
@ MUTT_PROGRESS_NET
Progress tracks bytes, according to $net_inc
Definition lib.h:82
#define mutt_socket_send(conn, buf)
Definition socket.h:57
char * data
Pointer to data.
Definition buffer.h:37
char * munge_name
Munged version of the mailbox name.
Definition mdata.h:42
char * path
path to temp file
Definition message.h:36
struct Message::@264267271004327071125374067057142037276212342100 flags
Flags for the Message.
bool draft
Message has been read.
Definition message.h:44
bool replied
Message has been replied to.
Definition message.h:43
time_t received
Time at which this message was received.
Definition message.h:46
bool flagged
Message is flagged.
Definition message.h:42
bool read
Message has been read.
Definition message.h:41
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ emails_to_uid_array()

static int emails_to_uid_array ( struct EmailArray * ea,
struct UidArray * uida )
static

Extract IMAP UIDs from Emails.

Parameters
eaArray of Emails
uidaEmpty UID array
Return values
numNumber of UIDs in the array

Definition at line 1678 of file message.c.

1679{
1680 struct Email **ep = NULL;
1681 ARRAY_FOREACH(ep, ea)
1682 {
1683 struct Email *e = *ep;
1684 struct ImapEmailData *edata = imap_edata_get(e);
1685
1686 ARRAY_ADD(uida, edata->uid);
1687 }
1688 ARRAY_SORT(uida, imap_sort_uid, NULL);
1689
1690 return ARRAY_SIZE(uida);
1691}
#define ARRAY_SORT(head, fn, sdata)
Sort an array.
Definition array.h:335
#define ARRAY_ADD(head, elem)
Add an element at the end of the array.
Definition array.h:156
#define ARRAY_FOREACH(elem, head)
Iterate over all elements of the array.
Definition array.h:214
#define ARRAY_SIZE(head)
The number of elements stored.
Definition array.h:87
int imap_sort_uid(const void *a, const void *b, void *sdata)
Compare two UIDs - Implements sort_t -.
Definition msg_set.c:54
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_copy_messages()

int imap_copy_messages ( struct Mailbox * m,
struct EmailArray * ea,
const char * dest,
enum MessageSaveOpt save_opt )

Server COPY messages to another folder.

Parameters
mMailbox
eaArray of Emails to copy
destDestination folder
save_optCopy or move, e.g. SAVE_MOVE
Return values
-1Error
0Success
1Non-fatal error - try fetch/append

Definition at line 1703 of file message.c.

1705{
1706 if (!m || !ea || ARRAY_EMPTY(ea) || !dest)
1707 return -1;
1708
1710 if (!adata)
1711 return -1;
1712
1713 char buf[PATH_MAX] = { 0 };
1714 char mbox[PATH_MAX] = { 0 };
1715 char mmbox[PATH_MAX] = { 0 };
1716 char prompt[PATH_MAX + 64];
1717 int rc;
1718 struct ConnAccount cac = { { 0 } };
1719 enum QuadOption err_continue = MUTT_NO;
1720 int triedcreate = 0;
1721 struct Email *e_cur = *ARRAY_GET(ea, 0);
1722 bool single = (ARRAY_SIZE(ea) == 1);
1723
1724 if (single && e_cur->attach_del)
1725 {
1726 mutt_debug(LL_DEBUG3, "#1 Message contains attachments to be deleted\n");
1727 return 1;
1728 }
1729
1730 if (imap_parse_path(dest, &cac, buf, sizeof(buf)))
1731 {
1732 mutt_debug(LL_DEBUG1, "bad destination %s\n", dest);
1733 return -1;
1734 }
1735
1736 /* check that the save-to folder is in the same account */
1737 if (!imap_account_match(&adata->conn->account, &cac))
1738 {
1739 mutt_debug(LL_DEBUG3, "%s not same server as %s\n", dest, mailbox_path(m));
1740 return 1;
1741 }
1742
1743 imap_fix_path_with_delim(adata->delim, buf, mbox, sizeof(mbox));
1744 if (*mbox == '\0')
1745 mutt_str_copy(mbox, "INBOX", sizeof(mbox));
1746 imap_munge_mbox_name(adata->unicode, mmbox, sizeof(mmbox), mbox);
1747
1748 /* loop in case of TRYCREATE */
1749 struct Buffer *cmd = buf_pool_get();
1750 struct Buffer *sync_cmd = buf_pool_get();
1751 do
1752 {
1753 buf_reset(sync_cmd);
1754 buf_reset(cmd);
1755
1756 if (single)
1757 {
1758 mutt_message(_("Copying message %d to %s..."), e_cur->index + 1, mbox);
1759 buf_add_printf(cmd, "UID COPY %u %s", imap_edata_get(e_cur)->uid, mmbox);
1760
1761 if (e_cur->active && e_cur->changed)
1762 {
1763 rc = imap_sync_message_for_copy(m, e_cur, sync_cmd, &err_continue);
1764 if (rc < 0)
1765 {
1766 mutt_debug(LL_DEBUG1, "#2 could not sync\n");
1767 goto out;
1768 }
1769 }
1770 rc = imap_exec(adata, buf_string(cmd), IMAP_CMD_QUEUE);
1771 if (rc != IMAP_EXEC_SUCCESS)
1772 {
1773 mutt_debug(LL_DEBUG1, "#2 could not queue copy\n");
1774 goto out;
1775 }
1776 }
1777 else /* copy tagged messages */
1778 {
1779 /* if any messages have attachments to delete, fall through to FETCH
1780 * and APPEND. TODO: Copy what we can with COPY, fall through for the
1781 * remainder. */
1782 struct Email **ep = NULL;
1783 ARRAY_FOREACH(ep, ea)
1784 {
1785 struct Email *e = *ep;
1786 if (e->attach_del)
1787 {
1788 mutt_debug(LL_DEBUG3, "#2 Message contains attachments to be deleted\n");
1789 rc = 1;
1790 goto out;
1791 }
1792
1793 if (e->active && e->changed)
1794 {
1795 rc = imap_sync_message_for_copy(m, e, sync_cmd, &err_continue);
1796 if (rc < 0)
1797 {
1798 mutt_debug(LL_DEBUG1, "#1 could not sync\n");
1799 goto out;
1800 }
1801 }
1802 }
1803
1804 struct UidArray uida = ARRAY_HEAD_INITIALIZER;
1805 emails_to_uid_array(ea, &uida);
1806 rc = imap_exec_msg_set(adata, "UID COPY", mmbox, &uida);
1807 ARRAY_FREE(&uida);
1808
1809 if (rc == 0)
1810 {
1811 mutt_debug(LL_DEBUG1, "No messages tagged\n");
1812 rc = -1;
1813 goto out;
1814 }
1815 else if (rc < 0)
1816 {
1817 mutt_debug(LL_DEBUG1, "#1 could not queue copy\n");
1818 goto out;
1819 }
1820 else
1821 {
1822 mutt_message(ngettext("Copying %d message to %s...", "Copying %d messages to %s...", rc),
1823 rc, mbox);
1824 }
1825 }
1826
1827 /* let's get it on */
1828 rc = imap_exec(adata, NULL, IMAP_CMD_NO_FLAGS);
1829 if (rc == IMAP_EXEC_ERROR)
1830 {
1831 if (triedcreate)
1832 {
1833 mutt_debug(LL_DEBUG1, "Already tried to create mailbox %s\n", mbox);
1834 break;
1835 }
1836 /* bail out if command failed for reasons other than nonexistent target */
1837 if (!mutt_istr_startswith(imap_get_qualifier(adata->buf), "[TRYCREATE]"))
1838 break;
1839 mutt_debug(LL_DEBUG3, "server suggests TRYCREATE\n");
1840 snprintf(prompt, sizeof(prompt), _("Create %s?"), mbox);
1841 const bool c_confirm_create = cs_subset_bool(NeoMutt->sub, "confirm_create");
1842 if (c_confirm_create &&
1843 (query_yesorno_help(prompt, MUTT_YES, NeoMutt->sub, "confirm_create") != MUTT_YES))
1844 {
1846 goto out;
1847 }
1848 if (imap_create_mailbox(adata, mbox) < 0)
1849 break;
1850 triedcreate = 1;
1851 }
1852 } while (rc == IMAP_EXEC_ERROR);
1853
1854 if (rc != 0)
1855 {
1856 imap_error("imap_copy_messages", adata->buf);
1857 goto out;
1858 }
1859
1860 /* cleanup */
1861 if (save_opt == SAVE_MOVE)
1862 {
1863 struct Email **ep = NULL;
1864 ARRAY_FOREACH(ep, ea)
1865 {
1866 struct Email *e = *ep;
1867 mutt_set_flag(m, e, MUTT_DELETE, true, true);
1868 mutt_set_flag(m, e, MUTT_PURGE, true, true);
1869 }
1870 }
1871
1872 rc = 0;
1873
1874out:
1875 buf_pool_release(&cmd);
1876 buf_pool_release(&sync_cmd);
1877
1878 return (rc < 0) ? -1 : rc;
1879}
#define ARRAY_EMPTY(head)
Check if an array is empty.
Definition array.h:74
#define ARRAY_FREE(head)
Release all memory.
Definition array.h:204
#define ARRAY_GET(head, idx)
Return the element at index.
Definition array.h:109
#define ARRAY_HEAD_INITIALIZER
Static initializer for arrays.
Definition array.h:58
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox's path string.
Definition mailbox.h:214
@ SAVE_MOVE
Move message to another mailbox, removing the original.
Definition external.h:54
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:1322
int imap_parse_path(const char *path, struct ConnAccount *cac, char *mailbox, size_t mailboxlen)
Parse an IMAP mailbox name into ConnAccount, name.
Definition util.c:477
static int emails_to_uid_array(struct EmailArray *ea, struct UidArray *uida)
Extract IMAP UIDs from Emails.
Definition message.c:1678
#define IMAP_CMD_NO_FLAGS
No flags are set.
Definition private.h:71
@ IMAP_EXEC_SUCCESS
Imap command executed or queued successfully.
Definition private.h:82
@ IMAP_EXEC_ERROR
Imap command failure.
Definition private.h:83
char * imap_fix_path_with_delim(char delim, const char *mailbox, char *path, size_t plen)
Fix up the imap path.
Definition util.c:713
bool imap_account_match(const struct ConnAccount *a1, const struct ConnAccount *a2)
Compare two Accounts.
Definition util.c:1095
void imap_munge_mbox_name(bool unicode, char *dest, size_t dlen, const char *src)
Quote awkward characters in a mailbox name.
Definition util.c:960
#define IMAP_CMD_QUEUE
Queue a command, do not execute.
Definition private.h:73
char * imap_get_qualifier(char *buf)
Get the qualifier from a tagged response.
Definition util.c:807
int imap_create_mailbox(struct ImapAccountData *adata, const char *mailbox)
Create a new mailbox.
Definition imap.c:448
int imap_sync_message_for_copy(struct Mailbox *m, struct Email *e, struct Buffer *cmd, enum QuadOption *err_continue)
Update server to reflect the flags of a single message.
Definition imap.c:946
@ LL_DEBUG3
Log at debug level 3.
Definition logging2.h:46
int imap_exec_msg_set(struct ImapAccountData *adata, const char *pre, const char *post, struct UidArray *uida)
Execute a command using a set of UIDs.
Definition msg_set.c:132
size_t mutt_str_copy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition string.c:581
@ MUTT_PURGE
Messages to be purged (bypass trash)
Definition mutt.h:77
@ MUTT_DELETE
Messages to be deleted.
Definition mutt.h:75
#define PATH_MAX
Definition mutt.h:42
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
QuadOption
Possible values for a quad-option.
Definition quad.h:36
@ MUTT_NO
User answered 'No', or assume 'No'.
Definition quad.h:38
enum QuadOption query_yesorno_help(const char *prompt, enum QuadOption def, struct ConfigSubset *sub, const char *name)
Ask the user a Yes/No question offering help.
Definition question.c:353
Login details for a remote server.
Definition connaccount.h:53
bool attach_del
Has an attachment marked for deletion.
Definition email.h:99
bool unicode
If true, we can send UTF-8, and the server will use UTF8 rather than mUTF7.
Definition adata.h:62
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_cache_del()

int imap_cache_del ( struct Mailbox * m,
struct Email * e )

Delete an email from the body cache.

Parameters
mSelected Imap Mailbox
eEmail
Return values
0Success
-1Failure

Definition at line 1888 of file message.c.

1889{
1891 struct ImapMboxData *mdata = imap_mdata_get(m);
1892
1893 if (!e || !adata || (adata->mailbox != m) || !mdata)
1894 return -1;
1895
1896 mdata->bcache = imap_bcache_open(m);
1897 char id[64] = { 0 };
1898 snprintf(id, sizeof(id), "%u-%u", mdata->uidvalidity, imap_edata_get(e)->uid);
1899 return mutt_bcache_del(mdata->bcache, id);
1900}
int mutt_bcache_del(struct BodyCache *bcache, const char *id)
Delete a file from the Body Cache.
Definition bcache.c:269
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_cache_clean()

int imap_cache_clean ( struct Mailbox * m)

Delete all the entries in the message cache.

Parameters
mSelectedImap Mailbox
Return values
0Always

Definition at line 1907 of file message.c.

1908{
1910 struct ImapMboxData *mdata = imap_mdata_get(m);
1911
1912 if (!adata || (adata->mailbox != m) || !mdata)
1913 return -1;
1914
1915 mdata->bcache = imap_bcache_open(m);
1917
1918 return 0;
1919}
int mutt_bcache_list(struct BodyCache *bcache, bcache_list_t want_id, void *data)
Find matching entries in the Body Cache.
Definition bcache.c:334
static int imap_bcache_delete(const char *id, struct BodyCache *bcache, void *data)
Delete an entry from the message cache - Implements bcache_list_t -.
Definition message.c:169
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_set_flags()

char * imap_set_flags ( struct Mailbox * m,
struct Email * e,
char * s,
bool * server_changes )

Fill the message header according to the server flags.

Parameters
[in]mImap Selected Mailbox
[in]eEmail
[in]sCommand string
[out]server_changesSet to true if the flags have changed
Return values
ptrThe end of flags string
NULLFailure

Expects a flags line of the form "FLAGS (flag flag ...)"

imap_set_flags: fill out the message header according to the flags from the server. Expects a flags line of the form "FLAGS (flag flag ...)"

Sets server_changes to 1 if a change to a flag is made, or in the case of e->changed, if a change to a flag would have been made.

Definition at line 1939 of file message.c.

1940{
1942 if (!adata || (adata->mailbox != m))
1943 return NULL;
1944
1945 struct ImapHeader newh = { 0 };
1946 struct ImapEmailData old_edata = { 0 };
1947 int local_changes = e->changed;
1948
1949 struct ImapEmailData *edata = e->edata;
1950 newh.edata = edata;
1951
1952 mutt_debug(LL_DEBUG2, "parsing FLAGS\n");
1953 s = msg_parse_flags(&newh, s);
1954 if (!s)
1955 return NULL;
1956
1957 /* Update tags system */
1958 /* We take a copy of the tags so we can split the string */
1959 char *tags_copy = mutt_str_dup(edata->flags_remote);
1960 driver_tags_replace(&e->tags, tags_copy);
1961 FREE(&tags_copy);
1962
1963 /* YAUH (yet another ugly hack): temporarily set context to
1964 * read-write even if it's read-only, so *server* updates of
1965 * flags can be processed by mutt_set_flag. mailbox->changed must
1966 * be restored afterwards */
1967 bool readonly = m->readonly;
1968 m->readonly = false;
1969
1970 /* This is redundant with the following two checks. Removing:
1971 * mutt_set_flag (m, e, MUTT_NEW, !(edata->read || edata->old), true); */
1972 set_changed_flag(m, e, local_changes, server_changes, MUTT_OLD, old_edata.old,
1973 edata->old, e->old);
1974 set_changed_flag(m, e, local_changes, server_changes, MUTT_READ,
1975 old_edata.read, edata->read, e->read);
1976 set_changed_flag(m, e, local_changes, server_changes, MUTT_DELETE,
1977 old_edata.deleted, edata->deleted, e->deleted);
1978 set_changed_flag(m, e, local_changes, server_changes, MUTT_FLAG,
1979 old_edata.flagged, edata->flagged, e->flagged);
1980 set_changed_flag(m, e, local_changes, server_changes, MUTT_REPLIED,
1981 old_edata.replied, edata->replied, e->replied);
1982
1983 /* this message is now definitively *not* changed (mutt_set_flag
1984 * marks things changed as a side-effect) */
1985 if (local_changes == 0)
1986 e->changed = false;
1987 m->changed &= !readonly;
1988 m->readonly = readonly;
1989
1990 return s;
1991}
static void set_changed_flag(struct Mailbox *m, struct Email *e, int local_changes, bool *server_changes, enum MessageType flag_name, bool old_hd_flag, bool new_hd_flag, bool h_flag)
Have the flags of an email changed.
Definition message.c:650
@ MUTT_READ
Messages that have been read.
Definition mutt.h:73
@ MUTT_OLD
Old messages.
Definition mutt.h:71
@ MUTT_FLAG
Flagged messages.
Definition mutt.h:79
@ MUTT_REPLIED
Messages that have been replied to.
Definition mutt.h:72
bool readonly
Don't allow changes to the mailbox.
Definition mailbox.h:116
+ Here is the call graph for this function:
+ Here is the caller graph for this function: