NeoMutt  2024-04-25-76-g20fe7b
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
message.c File Reference

Manage IMAP messages. More...

#include "config.h"
#include <ctype.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 81 of file message.c.

82{
85
86 if (!adata || (adata->mailbox != m))
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:148
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:60
void imap_cachepath(char delim, const char *mailbox, struct Buffer *dest)
Generate a cache path for a mailbox.
Definition: util.c:710
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:81
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition: pool.c:94
void * adata
Private data (for Mailbox backends)
Definition: account.h:42
Local cache of email bodies.
Definition: bcache.c:51
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))
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:187
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: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))
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:214
+ 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))
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:254
+ 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 192 of file message.c.

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

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

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

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

504{
505 bool abort = false;
506
508 /* L10N: This prompt is made if the user hits Ctrl-C when opening an IMAP mailbox */
509 if (query_yesorno(_("Abort download and close mailbox?"), MUTT_YES) == MUTT_YES)
510 {
511 abort = true;
513 }
514 SigInt = false;
515
516 return abort;
517}
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:849
#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:66
+ 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 527 of file message.c.

528{
529 struct ImapMboxData *mdata = adata->mailbox->mdata;
530 if (!mdata->uid_hash)
531 mdata->uid_hash = mutt_hash_int_new(MAX(6 * msn_count / 5, 30), MUTT_HASH_NO_FLAGS);
532}
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:109
#define MAX(a, b)
Definition: memory.h:31
+ 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 551 of file message.c.

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

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

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

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

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

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

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

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

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

1663{
1664 struct Email **ep = NULL;
1665 ARRAY_FOREACH(ep, ea)
1666 {
1667 struct Email *e = *ep;
1668 struct ImapEmailData *edata = imap_edata_get(e);
1669
1670 ARRAY_ADD(uida, edata->uid);
1671 }
1672 ARRAY_SORT(uida, imap_sort_uid, NULL);
1673
1674 return ARRAY_SIZE(uida);
1675}
#define ARRAY_SORT(head, fn, sdata)
Sort an array.
Definition: array.h:279
#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:212
#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:55
+ 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 1687 of file message.c.

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

1870{
1872 struct ImapMboxData *mdata = imap_mdata_get(m);
1873
1874 if (!e || !adata || (adata->mailbox != m))
1875 return -1;
1876
1877 mdata->bcache = imap_bcache_open(m);
1878 char id[64] = { 0 };
1879 snprintf(id, sizeof(id), "%u-%u", mdata->uidvalidity, imap_edata_get(e)->uid);
1880 return mutt_bcache_del(mdata->bcache, id);
1881}
int mutt_bcache_del(struct BodyCache *bcache, const char *id)
Delete a file from the Body Cache.
Definition: bcache.c:271
+ 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 1888 of file message.c.

1889{
1891 struct ImapMboxData *mdata = imap_mdata_get(m);
1892
1893 if (!adata || (adata->mailbox != m))
1894 return -1;
1895
1896 mdata->bcache = imap_bcache_open(m);
1898
1899 return 0;
1900}
int mutt_bcache_list(struct BodyCache *bcache, bcache_list_t want_id, void *data)
Find matching entries in the Body Cache.
Definition: bcache.c:336
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 1920 of file message.c.

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