NeoMutt  2025-09-05-7-geaa2bd
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 "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 80 of file message.c.

81{
84
85 if (!adata || (adata->mailbox != m))
86 return NULL;
87
88 if (mdata->bcache)
89 return mdata->bcache;
90
91 struct Buffer *mailbox = buf_pool_get();
92 imap_cachepath(adata->delim, mdata->name, mailbox);
93
94 struct BodyCache *bc = mutt_bcache_open(&adata->conn->account, buf_string(mailbox));
95 buf_pool_release(&mailbox);
96
97 return bc;
98}
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:748
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 107 of file message.c.

108{
110 struct ImapMboxData *mdata = imap_mdata_get(m);
111
112 if (!e || !adata || (adata->mailbox != m))
113 return NULL;
114
115 mdata->bcache = imap_bcache_open(m);
116 char id[64] = { 0 };
117 snprintf(id, sizeof(id), "%u-%u", mdata->uidvalidity, imap_edata_get(e)->uid);
118 return mutt_bcache_get(mdata->bcache, id);
119}
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:67
static struct BodyCache * imap_bcache_open(struct Mailbox *m)
Open a message cache.
Definition: message.c:80
+ 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 128 of file message.c.

129{
131 struct ImapMboxData *mdata = imap_mdata_get(m);
132
133 if (!e || !adata || (adata->mailbox != m))
134 return NULL;
135
136 mdata->bcache = imap_bcache_open(m);
137 char id[64] = { 0 };
138 snprintf(id, sizeof(id), "%u-%u", mdata->uidvalidity, imap_edata_get(e)->uid);
139 return mutt_bcache_put(mdata->bcache, id);
140}
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 149 of file message.c.

150{
152 struct ImapMboxData *mdata = imap_mdata_get(m);
153
154 if (!e || !adata || (adata->mailbox != m))
155 return -1;
156
157 mdata->bcache = imap_bcache_open(m);
158 char id[64] = { 0 };
159 snprintf(id, sizeof(id), "%u-%u", mdata->uidvalidity, imap_edata_get(e)->uid);
160
161 return mutt_bcache_commit(mdata->bcache, id);
162}
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 191 of file message.c.

192{
193 struct ImapEmailData *edata = h->edata;
194
195 /* sanity-check string */
196 size_t plen = mutt_istr_startswith(s, "FLAGS");
197 if (plen == 0)
198 {
199 mutt_debug(LL_DEBUG1, "not a FLAGS response: %s\n", s);
200 return NULL;
201 }
202 s += plen;
203 SKIPWS(s);
204 if (*s != '(')
205 {
206 mutt_debug(LL_DEBUG1, "bogus FLAGS response: %s\n", s);
207 return NULL;
208 }
209 s++;
210
211 FREE(&edata->flags_system);
212 FREE(&edata->flags_remote);
213
214 edata->deleted = false;
215 edata->flagged = false;
216 edata->replied = false;
217 edata->read = false;
218 edata->old = false;
219
220 /* start parsing */
221 while (*s && (*s != ')'))
222 {
223 if ((plen = mutt_istr_startswith(s, "\\deleted")))
224 {
225 s += plen;
226 edata->deleted = true;
227 }
228 else if ((plen = mutt_istr_startswith(s, "\\flagged")))
229 {
230 s += plen;
231 edata->flagged = true;
232 }
233 else if ((plen = mutt_istr_startswith(s, "\\answered")))
234 {
235 s += plen;
236 edata->replied = true;
237 }
238 else if ((plen = mutt_istr_startswith(s, "\\seen")))
239 {
240 s += plen;
241 edata->read = true;
242 }
243 else if ((plen = mutt_istr_startswith(s, "\\recent")))
244 {
245 s += plen;
246 }
247 else if ((plen = mutt_istr_startswith(s, "old")))
248 {
249 s += plen;
250 edata->old = cs_subset_bool(NeoMutt->sub, "mark_old");
251 }
252 else
253 {
254 char ctmp;
255 char *flag_word = s;
256 bool is_system_keyword = mutt_istr_startswith(s, "\\");
257
258 while (*s && !mutt_isspace(*s) && (*s != ')'))
259 s++;
260
261 ctmp = *s;
262 *s = '\0';
263
264 struct Buffer *buf = buf_pool_get();
265 if (is_system_keyword)
266 {
267 /* store other system flags as well (mainly \\Draft) */
268 buf_addstr(buf, edata->flags_system);
269 buf_join_str(buf, flag_word, ' ');
270 edata->flags_system = buf_strdup(buf);
271 }
272 else
273 {
274 /* store custom flags as well */
275 buf_addstr(buf, edata->flags_remote);
276 buf_join_str(buf, flag_word, ' ');
277 edata->flags_remote = buf_strdup(buf);
278 }
279 buf_pool_release(&buf);
280
281 *s = ctmp;
282 }
283 SKIPWS(s);
284 }
285
286 /* wrap up, or note bad flags response */
287 if (*s == ')')
288 {
289 s++;
290 }
291 else
292 {
293 mutt_debug(LL_DEBUG1, "Unterminated FLAGS response: %s\n", s);
294 return NULL;
295 }
296
297 return s;
298}
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:243
#define SKIPWS(ch)
Definition: string2.h:44
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 308 of file message.c.

309{
310 if (!s)
311 return -1;
312
313 char tmp[128] = { 0 };
314 char *ptmp = NULL;
315 size_t plen = 0;
316
317 while (*s)
318 {
319 SKIPWS(s);
320
321 if (mutt_istr_startswith(s, "FLAGS"))
322 {
323 s = msg_parse_flags(h, s);
324 if (!s)
325 return -1;
326 }
327 else if ((plen = mutt_istr_startswith(s, "UID")))
328 {
329 s += plen;
330 SKIPWS(s);
331 if (!mutt_str_atoui(s, &h->edata->uid))
332 return -1;
333
334 s = imap_next_word(s);
335 }
336 else if ((plen = mutt_istr_startswith(s, "INTERNALDATE")))
337 {
338 s += plen;
339 SKIPWS(s);
340 if (*s != '\"')
341 {
342 mutt_debug(LL_DEBUG1, "bogus INTERNALDATE entry: %s\n", s);
343 return -1;
344 }
345 s++;
346 ptmp = tmp;
347 while (*s && (*s != '\"') && (ptmp != (tmp + sizeof(tmp) - 1)))
348 *ptmp++ = *s++;
349 if (*s != '\"')
350 return -1;
351 s++; /* skip past the trailing " */
352 *ptmp = '\0';
354 }
355 else if ((plen = mutt_istr_startswith(s, "RFC822.SIZE")))
356 {
357 s += plen;
358 SKIPWS(s);
359 ptmp = tmp;
360 while (mutt_isdigit(*s) && (ptmp != (tmp + sizeof(tmp) - 1)))
361 *ptmp++ = *s++;
362 *ptmp = '\0';
363 if (!mutt_str_atol(tmp, &h->content_length))
364 return -1;
365 }
366 else if (mutt_istr_startswith(s, "BODY") || mutt_istr_startswith(s, "RFC822.HEADER"))
367 {
368 /* handle above, in msg_fetch_header */
369 return -2;
370 }
371 else if ((plen = mutt_istr_startswith(s, "MODSEQ")))
372 {
373 s += plen;
374 SKIPWS(s);
375 if (*s != '(')
376 {
377 mutt_debug(LL_DEBUG1, "bogus MODSEQ response: %s\n", s);
378 return -1;
379 }
380 s++;
381 while (*s && (*s != ')'))
382 s++;
383 if (*s == ')')
384 {
385 s++;
386 }
387 else
388 {
389 mutt_debug(LL_DEBUG1, "Unterminated MODSEQ response: %s\n", s);
390 return -1;
391 }
392 }
393 else if (*s == ')')
394 {
395 s++; /* end of request */
396 }
397 else if (*s)
398 {
399 /* got something i don't understand */
400 imap_error("msg_parse_fetch", s);
401 return -1;
402 }
403 }
404
405 return 0;
406}
const char * mutt_str_atol(const char *str, long *dst)
Convert ASCII string to a long.
Definition: atoi.c:143
const char * mutt_str_atoui(const char *str, unsigned int *dst)
Convert ASCII string to an unsigned integer.
Definition: atoi.c:218
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:191
void imap_error(const char *where, const char *msg)
Show an error and abort.
Definition: util.c:659
char * imap_next_word(char *s)
Find where the next IMAP word begins.
Definition: util.c:823
time_t mutt_date_parse_imap(const char *s)
Parse date of the form: DD-MMM-YYYY HH:MM:SS +ZZzz.
Definition: date.c:854
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 420 of file message.c.

421{
422 int rc = -1; /* default now is that string isn't FETCH response */
423
425
426 if (buf[0] != '*')
427 return rc;
428
429 /* skip to message number */
431 if (!mutt_str_atoui(buf, &ih->edata->msn))
432 return rc;
433
434 /* find FETCH tag */
436 if (!mutt_istr_startswith(buf, "FETCH"))
437 return rc;
438
439 rc = -2; /* we've got a FETCH response, for better or worse */
440 buf = strchr(buf, '(');
441 if (!buf)
442 return rc;
443 buf++;
444
445 /* FIXME: current implementation - call msg_parse_fetch - if it returns -2,
446 * read header lines and call it again. Silly. */
447 int parse_rc = msg_parse_fetch(ih, buf);
448 if (parse_rc == 0)
449 return 0;
450 if ((parse_rc != -2) || !fp)
451 return rc;
452
453 unsigned int bytes = 0;
454 if (imap_get_literal_count(buf, &bytes) == 0)
455 {
456 imap_read_literal(fp, adata, bytes, NULL);
457
458 /* we may have other fields of the FETCH _after_ the literal
459 * (eg Domino puts FLAGS here). Nothing wrong with that, either.
460 * This all has to go - we should accept literals and nonliterals
461 * interchangeably at any time. */
463 return rc;
464
465 if (msg_parse_fetch(ih, adata->buf) == -1)
466 return rc;
467 }
468
469 rc = 0; /* success */
470
471 /* subtract headers from message size - unfortunately only the subset of
472 * headers we've requested. */
473 ih->content_length -= bytes;
474
475 return rc;
476}
int imap_cmd_step(struct ImapAccountData *adata)
Reads server responses from an IMAP command.
Definition: command.c:1127
static int msg_parse_fetch(struct ImapHeader *h, char *s)
Handle headers returned from header fetch.
Definition: message.c:308
int imap_get_literal_count(const char *buf, unsigned int *bytes)
Write number of bytes in an IMAP literal into bytes.
Definition: util.c:779
#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:592
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 486 of file message.c.

487{
488 buf[*len] = '\0';
489 int rc = mutt_socket_write_n(conn, buf, *len);
490 *len = 0;
491 return rc;
492}
#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 502 of file message.c.

503{
504 bool abort = false;
505
507 /* L10N: This prompt is made if the user hits Ctrl-C when opening an IMAP mailbox */
508 if (query_yesorno(_("Abort download and close mailbox?"), MUTT_YES) == MUTT_YES)
509 {
510 abort = true;
512 }
513 SigInt = false;
514
515 return abort;
516}
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:851
#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:326
volatile sig_atomic_t SigInt
true after SIGINT is received
Definition: signal.c:69
+ 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 526 of file message.c.

527{
528 struct ImapMboxData *mdata = adata->mailbox->mdata;
529 if (!mdata->uid_hash)
530 mdata->uid_hash = mutt_hash_int_new(MAX(6 * msn_count / 5, 30), MUTT_HASH_NO_FLAGS);
531}
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 550 of file message.c.

553{
554 struct ImapMboxData *mdata = adata->mailbox->mdata;
555 unsigned int max_headers_per_fetch = UINT_MAX;
556 bool first_chunk = true;
557 int state = 0; /* 1: single msn, 2: range of msn */
558 unsigned int msn;
559 unsigned int range_begin = 0;
560 unsigned int range_end = 0;
561 unsigned int msn_count = 0;
562
563 buf_reset(buf);
564 if (msn_end < msn_begin)
565 return 0;
566
567 const long c_imap_fetch_chunk_size = cs_subset_long(NeoMutt->sub, "imap_fetch_chunk_size");
568 if (c_imap_fetch_chunk_size > 0)
569 max_headers_per_fetch = c_imap_fetch_chunk_size;
570
571 if (!evalhc)
572 {
573 if ((msn_end - msn_begin + 1) <= max_headers_per_fetch)
574 *fetch_msn_end = msn_end;
575 else
576 *fetch_msn_end = msn_begin + max_headers_per_fetch - 1;
577 buf_printf(buf, "%u:%u", msn_begin, *fetch_msn_end);
578 return (*fetch_msn_end - msn_begin + 1);
579 }
580
581 for (msn = msn_begin; msn <= (msn_end + 1); msn++)
582 {
583 if ((msn_count < max_headers_per_fetch) && (msn <= msn_end) &&
584 !imap_msn_get(&mdata->msn, msn - 1))
585 {
586 msn_count++;
587
588 switch (state)
589 {
590 case 1: /* single: convert to a range */
591 state = 2;
593
594 case 2: /* extend range ending */
595 range_end = msn;
596 break;
597 default:
598 state = 1;
599 range_begin = msn;
600 break;
601 }
602 }
603 else if (state)
604 {
605 if (first_chunk)
606 first_chunk = false;
607 else
608 buf_addch(buf, ',');
609
610 if (state == 1)
611 buf_add_printf(buf, "%u", range_begin);
612 else if (state == 2)
613 buf_add_printf(buf, "%u:%u", range_begin, range_end);
614 state = 0;
615
616 if ((buf_len(buf) > 500) || (msn_count >= max_headers_per_fetch))
617 break;
618 }
619 }
620
621 /* The loop index goes one past to terminate the range if needed. */
622 *fetch_msn_end = msn - 1;
623
624 return msn_count;
625}
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 642 of file message.c.

645{
646 /* If there are local_changes, we only want to note if the server
647 * flags have changed, so we can set a reopen flag in
648 * cmd_parse_fetch(). We don't want to count a local modification
649 * to the header flag as a "change". */
650 if ((old_hd_flag == new_hd_flag) && (local_changes != 0))
651 return;
652
653 if (new_hd_flag == h_flag)
654 return;
655
656 if (server_changes)
657 *server_changes = true;
658
659 /* Local changes have priority */
660 if (local_changes == 0)
661 mutt_set_flag(m, e, flag_name, new_hd_flag, true);
662}
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:57
+ 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 682 of file message.c.

685{
686 struct Progress *progress = NULL;
687 char buf[1024] = { 0 };
688 int rc = -1;
689
690 struct Mailbox *m = adata->mailbox;
691 struct ImapMboxData *mdata = imap_mdata_get(m);
692 int idx = m->msg_count;
693
694 if (m->verbose)
695 {
696 /* L10N: Comparing the cached data with the IMAP server's data */
697 progress = progress_new(MUTT_PROGRESS_READ, msn_end);
698 progress_set_message(progress, _("Evaluating cache..."));
699 }
700
701 /* If we are using CONDSTORE's "FETCH CHANGEDSINCE", then we keep
702 * the flags in the header cache, and update them further below.
703 * Otherwise, we fetch the current state of the flags here. */
704 snprintf(buf, sizeof(buf), "UID FETCH 1:%u (UID%s)", uid_next - 1,
705 eval_condstore ? "" : " FLAGS");
706
707 imap_cmd_start(adata, buf);
708
710 int mfhrc = 0;
711 struct ImapHeader h = { 0 };
712 for (int msgno = 1; rc == IMAP_RES_CONTINUE; msgno++)
713 {
715 goto fail;
716
717 progress_update(progress, msgno, -1);
718
719 memset(&h, 0, sizeof(h));
720 h.edata = imap_edata_new();
721 do
722 {
723 rc = imap_cmd_step(adata);
724 if (rc != IMAP_RES_CONTINUE)
725 break;
726
727 mfhrc = msg_fetch_header(m, &h, adata->buf, NULL);
728 if (mfhrc < 0)
729 continue;
730
731 if (!h.edata->uid)
732 {
733 mutt_debug(LL_DEBUG2, "skipping hcache FETCH response for message number %d missing a UID\n",
734 h.edata->msn);
735 continue;
736 }
737
738 if ((h.edata->msn < 1) || (h.edata->msn > msn_end))
739 {
740 mutt_debug(LL_DEBUG1, "skipping hcache FETCH response for unknown message number %d\n",
741 h.edata->msn);
742 continue;
743 }
744
745 if (imap_msn_get(&mdata->msn, h.edata->msn - 1))
746 {
747 mutt_debug(LL_DEBUG2, "skipping hcache FETCH for duplicate message %d\n",
748 h.edata->msn);
749 continue;
750 }
751
752 struct Email *e = imap_hcache_get(mdata, h.edata->uid);
753 m->emails[idx] = e;
754 if (e)
755 {
756 imap_msn_set(&mdata->msn, h.edata->msn - 1, e);
757 mutt_hash_int_insert(mdata->uid_hash, h.edata->uid, e);
758
759 e->index = h.edata->uid;
760 /* messages which have not been expunged are ACTIVE (borrowed from mh
761 * folders) */
762 e->active = true;
763 e->changed = false;
764 if (eval_condstore)
765 {
766 h.edata->read = e->read;
767 h.edata->old = e->old;
768 h.edata->deleted = e->deleted;
769 h.edata->flagged = e->flagged;
770 h.edata->replied = e->replied;
771 }
772 else
773 {
774 e->read = h.edata->read;
775 e->old = h.edata->old;
776 e->deleted = h.edata->deleted;
777 e->flagged = h.edata->flagged;
778 e->replied = h.edata->replied;
779 }
780
781 /* mailbox->emails[msgno]->received is restored from hcache_fetch_email() */
782 e->edata = h.edata;
784
785 /* We take a copy of the tags so we can split the string */
786 char *tags_copy = mutt_str_dup(h.edata->flags_remote);
787 driver_tags_replace(&e->tags, tags_copy);
788 FREE(&tags_copy);
789
790 m->msg_count++;
791 mailbox_size_add(m, e);
792
793 /* If this is the first time we are fetching, we need to
794 * store the current state of flags back into the header cache */
795 if (!eval_condstore && store_flag_updates)
796 imap_hcache_put(mdata, e);
797
798 h.edata = NULL;
799 idx++;
800 }
801 } while (mfhrc == -1);
802
803 imap_edata_free((void **) &h.edata);
804
805 if ((mfhrc < -1) || ((rc != IMAP_RES_CONTINUE) && (rc != IMAP_RES_OK)))
806 goto fail;
807 }
808
809 rc = 0;
810fail:
811 progress_free(&progress);
812 return rc;
813}
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:249
void imap_edata_free(void **ptr)
Free the private Email data - Implements Email::edata_free() -.
Definition: edata.c:40
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:1113
struct ImapEmailData * imap_edata_new(void)
Create a new ImapEmailData.
Definition: edata.c:57
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:420
static bool query_abort_header_download(struct ImapAccountData *adata)
Ask the user whether to abort the download.
Definition: message.c:502
#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:382
struct Email * imap_hcache_get(struct ImapMboxData *mdata, unsigned int uid)
Get a header cache entry by its UID.
Definition: util.c:357
@ 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:254
@ 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 827 of file message.c.

828{
829 int rc;
830 unsigned int uid = 0;
831
832 mutt_debug(LL_DEBUG2, "Reading uid seqset from header cache\n");
833 struct Mailbox *m = adata->mailbox;
834 struct ImapMboxData *mdata = adata->mailbox->mdata;
835 unsigned int msn = 1;
836
837 if (m->verbose)
838 mutt_message(_("Evaluating cache..."));
839
840 struct SeqsetIterator *iter = mutt_seqset_iterator_new(uid_seqset);
841 if (!iter)
842 return -1;
843
844 while ((rc = mutt_seqset_iterator_next(iter, &uid)) == 0)
845 {
846 /* The seqset may contain more headers than the fetch request, so
847 * we need to watch and reallocate the context and msn_index */
848 imap_msn_reserve(&mdata->msn, msn);
849
850 struct Email *e = imap_hcache_get(mdata, uid);
851 if (e)
852 {
853 imap_msn_set(&mdata->msn, msn - 1, e);
854
856
858 e->edata = edata;
860
861 e->index = uid;
862 e->active = true;
863 e->changed = false;
864 edata->read = e->read;
865 edata->old = e->old;
866 edata->deleted = e->deleted;
867 edata->flagged = e->flagged;
868 edata->replied = e->replied;
869
870 edata->msn = msn;
871 edata->uid = uid;
873
874 mailbox_size_add(m, e);
875 m->emails[m->msg_count++] = e;
876
877 msn++;
878 }
879 else if (!uid)
880 {
881 /* A non-zero uid missing from the header cache is either the
882 * result of an expunged message (not recorded in the uid seqset)
883 * or a hole in the header cache.
884 *
885 * We have to assume it's an earlier expunge and compact the msn's
886 * in that case, because cmd_parse_vanished() won't find it in the
887 * uid_hash and decrement later msn's there.
888 *
889 * Thus we only increment the uid if the uid was 0: an actual
890 * stored "blank" in the uid seqset.
891 */
892 msn++;
893 }
894 }
895
897
898 return rc;
899}
#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:1124
int mutt_seqset_iterator_next(struct SeqsetIterator *iter, unsigned int *next)
Get the next UID from a Sequence Set.
Definition: util.c:1145
void mutt_seqset_iterator_free(struct SeqsetIterator **ptr)
Free a Sequence Set Iterator.
Definition: util.c:1204
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 913 of file message.c.

916{
917 struct Progress *progress = NULL;
918 char buf[1024] = { 0 };
919 unsigned int header_msn = 0;
920
921 struct Mailbox *m = adata->mailbox;
922 struct ImapMboxData *mdata = imap_mdata_get(m);
923
924 if (m->verbose)
925 {
926 /* L10N: Fetching IMAP flag changes, using the CONDSTORE extension */
927 progress = progress_new(MUTT_PROGRESS_READ, msn_end);
928 progress_set_message(progress, _("Fetching flag updates..."));
929 }
930
931 snprintf(buf, sizeof(buf), "UID FETCH 1:%u (FLAGS) (CHANGEDSINCE %llu%s)",
932 uid_next - 1, hc_modseq, eval_qresync ? " VANISHED" : "");
933
934 imap_cmd_start(adata, buf);
935
936 int rc = IMAP_RES_CONTINUE;
937 for (int msgno = 1; rc == IMAP_RES_CONTINUE; msgno++)
938 {
940 goto fail;
941
942 progress_update(progress, msgno, -1);
943
944 /* cmd_parse_fetch will update the flags */
945 rc = imap_cmd_step(adata);
946 if (rc != IMAP_RES_CONTINUE)
947 break;
948
949 /* so we just need to grab the header and persist it back into
950 * the header cache */
951 char *fetch_buf = adata->buf;
952 if (fetch_buf[0] != '*')
953 continue;
954
955 fetch_buf = imap_next_word(fetch_buf);
956 if (!mutt_isdigit(*fetch_buf) || !mutt_str_atoui(fetch_buf, &header_msn))
957 continue;
958
959 if ((header_msn < 1) || (header_msn > msn_end) ||
960 !imap_msn_get(&mdata->msn, header_msn - 1))
961 {
962 mutt_debug(LL_DEBUG1, "skipping CONDSTORE flag update for unknown message number %u\n",
963 header_msn);
964 continue;
965 }
966
967 imap_hcache_put(mdata, imap_msn_get(&mdata->msn, header_msn - 1));
968 }
969
970 if (rc != IMAP_RES_OK)
971 goto fail;
972
973 /* The IMAP flag setting as part of cmd_parse_fetch() ends up
974 * flipping these on. */
975 mdata->check_status &= ~IMAP_FLAGS_PENDING;
976 m->changed = false;
977
978 /* VANISHED handling: we need to empty out the messages */
979 if (mdata->reopen & IMAP_EXPUNGE_PENDING)
980 {
982 imap_expunge_mailbox(m, false);
983
984 imap_hcache_open(adata, mdata, false);
985 mdata->reopen &= ~IMAP_EXPUNGE_PENDING;
986 }
987
988 /* undo expunge count updates.
989 * mview_update() will do this at the end of the header fetch. */
990 m->vcount = 0;
991 m->msg_tagged = 0;
992 m->msg_deleted = 0;
993 m->msg_new = 0;
994 m->msg_unread = 0;
995 m->msg_flagged = 0;
996 m->changed = false;
997
998 rc = 0;
999fail:
1000 progress_free(&progress);
1001 return rc;
1002}
#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:301
void imap_hcache_close(struct ImapMboxData *mdata)
Close the header cache.
Definition: util.c:342
void imap_expunge_mailbox(struct Mailbox *m, bool resort)
Purge messages from the server.
Definition: imap.c:671
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 1012 of file message.c.

1013{
1014 ASSERT(m);
1016 struct ImapMboxData *mdata = imap_mdata_get(m);
1017 if (!adata || (adata->mailbox != m))
1018 return -1;
1019
1020 const size_t max_msn = imap_msn_highest(&mdata->msn);
1021
1022 unsigned int msn;
1023 unsigned int uid;
1024 struct Email *e = NULL;
1025 struct Email *uidh = NULL;
1026
1027 for (int i = 0; i < m->msg_count; i++)
1028 {
1029 e = m->emails[i];
1030 const struct ImapEmailData *edata = imap_edata_get(e);
1031 if (!edata)
1032 goto fail;
1033
1034 msn = imap_edata_get(e)->msn;
1035 uid = imap_edata_get(e)->uid;
1036
1037 if ((msn < 1) || (msn > max_msn) || imap_msn_get(&mdata->msn, msn - 1) != e)
1038 goto fail;
1039
1040 uidh = (struct Email *) mutt_hash_int_find(mdata->uid_hash, uid);
1041 if (uidh != e)
1042 goto fail;
1043 }
1044
1045 return 0;
1046
1047fail:
1048 imap_msn_free(&mdata->msn);
1049 mutt_hash_free(&mdata->uid_hash);
1053
1054 for (int i = 0; i < m->msg_count; i++)
1055 {
1056 if (m->emails[i] && m->emails[i]->edata)
1057 imap_edata_free(&m->emails[i]->edata);
1058 email_free(&m->emails[i]);
1059 }
1060 m->msg_count = 0;
1061 m->size = 0;
1062 hcache_delete_raw(mdata->hcache, "MODSEQ", 6);
1064 imap_hcache_close(mdata);
1065
1066 if (m->verbose)
1067 {
1068 /* L10N: After opening an IMAP mailbox using QRESYNC, Mutt performs a quick
1069 sanity check. If that fails, Mutt reopens the mailbox using a normal
1070 download. */
1071 mutt_error(_("QRESYNC failed. Reopening mailbox."));
1072 }
1073 return -1;
1074}
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:752
int imap_hcache_clear_uid_seqset(struct ImapMboxData *mdata)
Delete a UID Sequence Set from the header cache.
Definition: util.c:438
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 1089 of file message.c.

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

1341{
1342 unsigned int maxuid = 0;
1343 int rc = -1;
1344 bool evalhc = false;
1345
1346#ifdef USE_HCACHE
1347 uint32_t uidvalidity = 0;
1348 unsigned int uid_next = 0;
1349 unsigned long long modseq = 0;
1350 bool has_condstore = false;
1351 bool has_qresync = false;
1352 bool eval_condstore = false;
1353 bool eval_qresync = false;
1354 char *uid_seqset = NULL;
1355 const unsigned int msn_begin_save = msn_begin;
1356#endif /* USE_HCACHE */
1357
1359 struct ImapMboxData *mdata = imap_mdata_get(m);
1360 if (!adata || (adata->mailbox != m))
1361 return -1;
1362
1363#ifdef USE_HCACHE
1364retry:
1365#endif /* USE_HCACHE */
1366
1367 /* make sure context has room to hold the mailbox */
1368 mx_alloc_memory(m, msn_end);
1369 imap_msn_reserve(&mdata->msn, msn_end);
1370 imap_alloc_uid_hash(adata, msn_end);
1371
1373 mdata->new_mail_count = 0;
1374
1375#ifdef USE_HCACHE
1376 imap_hcache_open(adata, mdata, true);
1377
1378 if (mdata->hcache && initial_download)
1379 {
1380 hcache_fetch_raw_obj(mdata->hcache, "UIDVALIDITY", 11, &uidvalidity);
1381 hcache_fetch_raw_obj(mdata->hcache, "UIDNEXT", 7, &uid_next);
1382 if (mdata->modseq)
1383 {
1384 const bool c_imap_condstore = cs_subset_bool(NeoMutt->sub, "imap_condstore");
1385 if ((adata->capabilities & IMAP_CAP_CONDSTORE) && c_imap_condstore)
1386 has_condstore = true;
1387
1388 /* If IMAP_CAP_QRESYNC and ImapQResync then NeoMutt sends ENABLE QRESYNC.
1389 * If we receive an ENABLED response back, then adata->qresync is set. */
1390 if (adata->qresync)
1391 has_qresync = true;
1392 }
1393
1394 if (uidvalidity && uid_next && (uidvalidity == mdata->uidvalidity))
1395 {
1396 evalhc = true;
1397 if (hcache_fetch_raw_obj(mdata->hcache, "MODSEQ", 6, &modseq))
1398 {
1399 if (has_qresync)
1400 {
1401 uid_seqset = imap_hcache_get_uid_seqset(mdata);
1402 if (uid_seqset)
1403 eval_qresync = true;
1404 }
1405
1406 if (!eval_qresync && has_condstore)
1407 eval_condstore = true;
1408 }
1409 }
1410 }
1411 if (evalhc)
1412 {
1413 if (eval_qresync)
1414 {
1415 if (read_headers_qresync_eval_cache(adata, uid_seqset) < 0)
1416 goto bail;
1417 }
1418 else
1419 {
1420 if (read_headers_normal_eval_cache(adata, msn_end, uid_next, has_condstore || has_qresync,
1421 eval_condstore) < 0)
1422 goto bail;
1423 }
1424
1425 if ((eval_condstore || eval_qresync) && (modseq != mdata->modseq))
1426 {
1428 modseq, eval_qresync) < 0)
1429 {
1430 goto bail;
1431 }
1432 }
1433
1434 /* Look for the first empty MSN and start there */
1435 while (msn_begin <= msn_end)
1436 {
1437 if (!imap_msn_get(&mdata->msn, msn_begin - 1))
1438 break;
1439 msn_begin++;
1440 }
1441 }
1442#endif /* USE_HCACHE */
1443
1444 if (read_headers_fetch_new(m, msn_begin, msn_end, evalhc, &maxuid, initial_download) < 0)
1445 goto bail;
1446
1447#ifdef USE_HCACHE
1448 if (eval_qresync && initial_download)
1449 {
1450 if (imap_verify_qresync(m) != 0)
1451 {
1452 eval_qresync = false;
1453 eval_condstore = false;
1454 evalhc = false;
1455 modseq = 0;
1456 maxuid = 0;
1457 FREE(&uid_seqset);
1458 uidvalidity = 0;
1459 uid_next = 0;
1460 msn_begin = msn_begin_save;
1461
1462 goto retry;
1463 }
1464 }
1465#endif /* USE_HCACHE */
1466
1467 if (maxuid && (mdata->uid_next < maxuid + 1))
1468 mdata->uid_next = maxuid + 1;
1469
1470#ifdef USE_HCACHE
1471 hcache_store_raw(mdata->hcache, "UIDVALIDITY", 11, &mdata->uidvalidity,
1472 sizeof(mdata->uidvalidity));
1473 if (maxuid && (mdata->uid_next < maxuid + 1))
1474 {
1475 mutt_debug(LL_DEBUG2, "Overriding UIDNEXT: %u -> %u\n", mdata->uid_next, maxuid + 1);
1476 mdata->uid_next = maxuid + 1;
1477 }
1478 if (mdata->uid_next > 1)
1479 {
1480 hcache_store_raw(mdata->hcache, "UIDNEXT", 7, &mdata->uid_next, sizeof(mdata->uid_next));
1481 }
1482
1483 /* We currently only sync CONDSTORE and QRESYNC on the initial download.
1484 * To do it more often, we'll need to deal with flag updates combined with
1485 * unsync'ed local flag changes. We'll also need to properly sync flags to
1486 * the header cache on close. I'm not sure it's worth the added complexity. */
1487 if (initial_download)
1488 {
1489 if (has_condstore || has_qresync)
1490 {
1491 hcache_store_raw(mdata->hcache, "MODSEQ", 6, &mdata->modseq, sizeof(mdata->modseq));
1492 }
1493 else
1494 {
1495 hcache_delete_raw(mdata->hcache, "MODSEQ", 6);
1496 }
1497
1498 if (has_qresync)
1500 else
1502 }
1503#endif /* USE_HCACHE */
1504
1505 /* TODO: it's not clear to me why we are calling mx_alloc_memory yet again. */
1507
1508 mdata->reopen |= IMAP_REOPEN_ALLOW;
1509
1510 rc = msn_end;
1511
1512bail:
1513#ifdef USE_HCACHE
1515 FREE(&uid_seqset);
1516#endif /* USE_HCACHE */
1517
1518 return rc;
1519}
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:724
#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:1012
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:913
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:1089
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:682
static int read_headers_qresync_eval_cache(struct ImapAccountData *adata, char *uid_seqset)
Retrieve data from the header cache.
Definition: message.c:827
static void imap_alloc_uid_hash(struct ImapAccountData *adata, unsigned int msn_count)
Create a Hash Table for the UIDs.
Definition: message.c:526
int imap_hcache_store_uid_seqset(struct ImapMboxData *mdata)
Store a UID Sequence Set in the header cache.
Definition: util.c:417
#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:452
#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 1528 of file message.c.

1529{
1530 if (!m || !msg)
1531 return -1;
1532
1533 FILE *fp = NULL;
1534 char buf[2048] = { 0 };
1535 struct Buffer *internaldate = NULL;
1536 struct Buffer *imap_flags = NULL;
1537 size_t len;
1538 struct Progress *progress = NULL;
1539 size_t sent;
1540 int c, last;
1541 int rc;
1542
1544 struct ImapMboxData *mdata = imap_mdata_get(m);
1545
1546 fp = mutt_file_fopen(msg->path, "r");
1547 if (!fp)
1548 {
1549 mutt_perror("%s", msg->path);
1550 goto fail;
1551 }
1552
1553 /* currently we set the \Seen flag on all messages, but probably we
1554 * should scan the message Status header for flag info. Since we're
1555 * already rereading the whole file for length it isn't any more
1556 * expensive (it'd be nice if we had the file size passed in already
1557 * by the code that writes the file, but that's a lot of changes.
1558 * Ideally we'd have an Email structure with flag info here... */
1559 for (last = EOF, len = 0; (c = fgetc(fp)) != EOF; last = c)
1560 {
1561 if ((c == '\n') && (last != '\r'))
1562 len++;
1563
1564 len++;
1565 }
1566 rewind(fp);
1567
1568 if (m->verbose)
1569 {
1570 progress = progress_new(MUTT_PROGRESS_NET, len);
1571 progress_set_message(progress, _("Uploading message..."));
1572 }
1573
1574 internaldate = buf_pool_get();
1575 mutt_date_make_imap(internaldate, msg->received);
1576
1577 imap_flags = buf_pool_get();
1578
1579 if (msg->flags.read)
1580 buf_addstr(imap_flags, " \\Seen");
1581 if (msg->flags.replied)
1582 buf_addstr(imap_flags, " \\Answered");
1583 if (msg->flags.flagged)
1584 buf_addstr(imap_flags, " \\Flagged");
1585 if (msg->flags.draft)
1586 buf_addstr(imap_flags, " \\Draft");
1587
1588 snprintf(buf, sizeof(buf), "APPEND %s (%s) \"%s\" {%lu}", mdata->munge_name,
1589 imap_flags->data + 1, buf_string(internaldate), (unsigned long) len);
1590 buf_pool_release(&internaldate);
1591
1592 imap_cmd_start(adata, buf);
1593
1594 do
1595 {
1596 rc = imap_cmd_step(adata);
1597 } while (rc == IMAP_RES_CONTINUE);
1598
1599 if (rc != IMAP_RES_RESPOND)
1600 goto cmd_step_fail;
1601
1602 for (last = EOF, sent = len = 0; (c = fgetc(fp)) != EOF; last = c)
1603 {
1604 if ((c == '\n') && (last != '\r'))
1605 buf[len++] = '\r';
1606
1607 buf[len++] = c;
1608
1609 if (len > sizeof(buf) - 3)
1610 {
1611 sent += len;
1612 if (flush_buffer(buf, &len, adata->conn) < 0)
1613 goto fail;
1614 progress_update(progress, sent, -1);
1615 }
1616 }
1617
1618 if (len > 0)
1619 if (flush_buffer(buf, &len, adata->conn) < 0)
1620 goto fail;
1621
1622 if (mutt_socket_send(adata->conn, "\r\n") < 0)
1623 goto fail;
1624 mutt_file_fclose(&fp);
1625
1626 do
1627 {
1628 rc = imap_cmd_step(adata);
1629 } while (rc == IMAP_RES_CONTINUE);
1630
1631 if (rc != IMAP_RES_OK)
1632 goto cmd_step_fail;
1633
1634 progress_free(&progress);
1635 buf_pool_release(&imap_flags);
1636 return 0;
1637
1638cmd_step_fail:
1639 mutt_debug(LL_DEBUG1, "command failed: %s\n", adata->buf);
1640 if (rc != IMAP_RES_BAD)
1641 {
1642 char *pc = imap_next_word(adata->buf); /* skip sequence number or token */
1643 pc = imap_next_word(pc); /* skip response code */
1644 if (*pc != '\0')
1645 mutt_error("%s", pc);
1646 }
1647
1648fail:
1649 mutt_file_fclose(&fp);
1650 progress_free(&progress);
1651 buf_pool_release(&imap_flags);
1652 return -1;
1653}
#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:486
#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:811
@ 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 * path
path to temp file
Definition: message.h:36
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
struct Message::@0 flags
Flags for the Message.
+ 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 1661 of file message.c.

1662{
1663 struct Email **ep = NULL;
1664 ARRAY_FOREACH(ep, ea)
1665 {
1666 struct Email *e = *ep;
1667 struct ImapEmailData *edata = imap_edata_get(e);
1668
1669 ARRAY_ADD(uida, edata->uid);
1670 }
1671 ARRAY_SORT(uida, imap_sort_uid, NULL);
1672
1673 return ARRAY_SIZE(uida);
1674}
#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 1686 of file message.c.

1688{
1689 if (!m || !ea || ARRAY_EMPTY(ea) || !dest)
1690 return -1;
1691
1692 char buf[PATH_MAX] = { 0 };
1693 char mbox[PATH_MAX] = { 0 };
1694 char mmbox[PATH_MAX] = { 0 };
1695 char prompt[PATH_MAX + 64];
1696 int rc;
1697 struct ConnAccount cac = { { 0 } };
1698 enum QuadOption err_continue = MUTT_NO;
1699 int triedcreate = 0;
1700 struct Email *e_cur = *ARRAY_GET(ea, 0);
1701 bool single = (ARRAY_SIZE(ea) == 1);
1703
1704 if (single && e_cur->attach_del)
1705 {
1706 mutt_debug(LL_DEBUG3, "#1 Message contains attachments to be deleted\n");
1707 return 1;
1708 }
1709
1710 if (imap_parse_path(dest, &cac, buf, sizeof(buf)))
1711 {
1712 mutt_debug(LL_DEBUG1, "bad destination %s\n", dest);
1713 return -1;
1714 }
1715
1716 /* check that the save-to folder is in the same account */
1717 if (!imap_account_match(&adata->conn->account, &cac))
1718 {
1719 mutt_debug(LL_DEBUG3, "%s not same server as %s\n", dest, mailbox_path(m));
1720 return 1;
1721 }
1722
1723 imap_fix_path_with_delim(adata->delim, buf, mbox, sizeof(mbox));
1724 if (*mbox == '\0')
1725 mutt_str_copy(mbox, "INBOX", sizeof(mbox));
1726 imap_munge_mbox_name(adata->unicode, mmbox, sizeof(mmbox), mbox);
1727
1728 /* loop in case of TRYCREATE */
1729 struct Buffer *cmd = buf_pool_get();
1730 struct Buffer *sync_cmd = buf_pool_get();
1731 do
1732 {
1733 buf_reset(sync_cmd);
1734 buf_reset(cmd);
1735
1736 if (single)
1737 {
1738 mutt_message(_("Copying message %d to %s..."), e_cur->index + 1, mbox);
1739 buf_add_printf(cmd, "UID COPY %u %s", imap_edata_get(e_cur)->uid, mmbox);
1740
1741 if (e_cur->active && e_cur->changed)
1742 {
1743 rc = imap_sync_message_for_copy(m, e_cur, sync_cmd, &err_continue);
1744 if (rc < 0)
1745 {
1746 mutt_debug(LL_DEBUG1, "#2 could not sync\n");
1747 goto out;
1748 }
1749 }
1750 rc = imap_exec(adata, buf_string(cmd), IMAP_CMD_QUEUE);
1751 if (rc != IMAP_EXEC_SUCCESS)
1752 {
1753 mutt_debug(LL_DEBUG1, "#2 could not queue copy\n");
1754 goto out;
1755 }
1756 }
1757 else /* copy tagged messages */
1758 {
1759 /* if any messages have attachments to delete, fall through to FETCH
1760 * and APPEND. TODO: Copy what we can with COPY, fall through for the
1761 * remainder. */
1762 struct Email **ep = NULL;
1763 ARRAY_FOREACH(ep, ea)
1764 {
1765 struct Email *e = *ep;
1766 if (e->attach_del)
1767 {
1768 mutt_debug(LL_DEBUG3, "#2 Message contains attachments to be deleted\n");
1769 rc = 1;
1770 goto out;
1771 }
1772
1773 if (e->active && e->changed)
1774 {
1775 rc = imap_sync_message_for_copy(m, e, sync_cmd, &err_continue);
1776 if (rc < 0)
1777 {
1778 mutt_debug(LL_DEBUG1, "#1 could not sync\n");
1779 goto out;
1780 }
1781 }
1782 }
1783
1784 struct UidArray uida = ARRAY_HEAD_INITIALIZER;
1785 emails_to_uid_array(ea, &uida);
1786 rc = imap_exec_msg_set(adata, "UID COPY", mmbox, &uida);
1787 ARRAY_FREE(&uida);
1788
1789 if (rc == 0)
1790 {
1791 mutt_debug(LL_DEBUG1, "No messages tagged\n");
1792 rc = -1;
1793 goto out;
1794 }
1795 else if (rc < 0)
1796 {
1797 mutt_debug(LL_DEBUG1, "#1 could not queue copy\n");
1798 goto out;
1799 }
1800 else
1801 {
1802 mutt_message(ngettext("Copying %d message to %s...", "Copying %d messages to %s...", rc),
1803 rc, mbox);
1804 }
1805 }
1806
1807 /* let's get it on */
1808 rc = imap_exec(adata, NULL, IMAP_CMD_NO_FLAGS);
1809 if (rc == IMAP_EXEC_ERROR)
1810 {
1811 if (triedcreate)
1812 {
1813 mutt_debug(LL_DEBUG1, "Already tried to create mailbox %s\n", mbox);
1814 break;
1815 }
1816 /* bail out if command failed for reasons other than nonexistent target */
1817 if (!mutt_istr_startswith(imap_get_qualifier(adata->buf), "[TRYCREATE]"))
1818 break;
1819 mutt_debug(LL_DEBUG3, "server suggests TRYCREATE\n");
1820 snprintf(prompt, sizeof(prompt), _("Create %s?"), mbox);
1821 const bool c_confirm_create = cs_subset_bool(NeoMutt->sub, "confirm_create");
1822 if (c_confirm_create &&
1823 (query_yesorno_help(prompt, MUTT_YES, NeoMutt->sub, "confirm_create") != MUTT_YES))
1824 {
1826 goto out;
1827 }
1828 if (imap_create_mailbox(adata, mbox) < 0)
1829 break;
1830 triedcreate = 1;
1831 }
1832 } while (rc == IMAP_EXEC_ERROR);
1833
1834 if (rc != 0)
1835 {
1836 imap_error("imap_copy_messages", adata->buf);
1837 goto out;
1838 }
1839
1840 /* cleanup */
1841 if (save_opt == SAVE_MOVE)
1842 {
1843 struct Email **ep = NULL;
1844 ARRAY_FOREACH(ep, ea)
1845 {
1846 struct Email *e = *ep;
1847 mutt_set_flag(m, e, MUTT_DELETE, true, true);
1848 mutt_set_flag(m, e, MUTT_PURGE, true, true);
1849 }
1850 }
1851
1852 rc = 0;
1853
1854out:
1855 buf_pool_release(&cmd);
1856 buf_pool_release(&sync_cmd);
1857
1858 return (rc < 0) ? -1 : rc;
1859}
#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:223
@ SAVE_MOVE
Move message to another mailbox, removing the original.
Definition: external.h:53
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:1302
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:476
static int emails_to_uid_array(struct EmailArray *ea, struct UidArray *uida)
Extract IMAP UIDs from Emails.
Definition: message.c:1661
#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:712
bool imap_account_match(const struct ConnAccount *a1, const struct ConnAccount *a2)
Compare two Accounts.
Definition: util.c:1092
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:959
#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:806
int imap_create_mailbox(struct ImapAccountData *adata, const char *mailbox)
Create a new mailbox.
Definition: imap.c:435
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:928
@ 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:580
@ 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)
Definition: mutt_logging.c:74
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:354
Login details for a remote server.
Definition: connaccount.h:53
bool attach_del
Has an attachment marked for deletion.
Definition: email.h:99
+ 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 1868 of file message.c.

1869{
1871 struct ImapMboxData *mdata = imap_mdata_get(m);
1872
1873 if (!e || !adata || (adata->mailbox != m))
1874 return -1;
1875
1876 mdata->bcache = imap_bcache_open(m);
1877 char id[64] = { 0 };
1878 snprintf(id, sizeof(id), "%u-%u", mdata->uidvalidity, imap_edata_get(e)->uid);
1879 return mutt_bcache_del(mdata->bcache, id);
1880}
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 1887 of file message.c.

1888{
1890 struct ImapMboxData *mdata = imap_mdata_get(m);
1891
1892 if (!adata || (adata->mailbox != m))
1893 return -1;
1894
1895 mdata->bcache = imap_bcache_open(m);
1897
1898 return 0;
1899}
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:168
+ 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 1919 of file message.c.

1920{
1922 if (!adata || (adata->mailbox != m))
1923 return NULL;
1924
1925 struct ImapHeader newh = { 0 };
1926 struct ImapEmailData old_edata = { 0 };
1927 int local_changes = e->changed;
1928
1929 struct ImapEmailData *edata = e->edata;
1930 newh.edata = edata;
1931
1932 mutt_debug(LL_DEBUG2, "parsing FLAGS\n");
1933 s = msg_parse_flags(&newh, s);
1934 if (!s)
1935 return NULL;
1936
1937 /* Update tags system */
1938 /* We take a copy of the tags so we can split the string */
1939 char *tags_copy = mutt_str_dup(edata->flags_remote);
1940 driver_tags_replace(&e->tags, tags_copy);
1941 FREE(&tags_copy);
1942
1943 /* YAUH (yet another ugly hack): temporarily set context to
1944 * read-write even if it's read-only, so *server* updates of
1945 * flags can be processed by mutt_set_flag. mailbox->changed must
1946 * be restored afterwards */
1947 bool readonly = m->readonly;
1948 m->readonly = false;
1949
1950 /* This is redundant with the following two checks. Removing:
1951 * mutt_set_flag (m, e, MUTT_NEW, !(edata->read || edata->old), true); */
1952 set_changed_flag(m, e, local_changes, server_changes, MUTT_OLD, old_edata.old,
1953 edata->old, e->old);
1954 set_changed_flag(m, e, local_changes, server_changes, MUTT_READ,
1955 old_edata.read, edata->read, e->read);
1956 set_changed_flag(m, e, local_changes, server_changes, MUTT_DELETE,
1957 old_edata.deleted, edata->deleted, e->deleted);
1958 set_changed_flag(m, e, local_changes, server_changes, MUTT_FLAG,
1959 old_edata.flagged, edata->flagged, e->flagged);
1960 set_changed_flag(m, e, local_changes, server_changes, MUTT_REPLIED,
1961 old_edata.replied, edata->replied, e->replied);
1962
1963 /* this message is now definitively *not* changed (mutt_set_flag
1964 * marks things changed as a side-effect) */
1965 if (local_changes == 0)
1966 e->changed = false;
1967 m->changed &= !readonly;
1968 m->readonly = readonly;
1969
1970 return s;
1971}
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:642
@ 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: