NeoMutt  2024-03-23-147-g885fbc
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
message.c File Reference

Manage IMAP messages. More...

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

83{
86
87 if (!adata || (adata->mailbox != m))
88 return NULL;
89
90 if (mdata->bcache)
91 return mdata->bcache;
92
93 struct Buffer *mailbox = buf_pool_get();
94 imap_cachepath(adata->delim, mdata->name, mailbox);
95
96 struct BodyCache *bc = mutt_bcache_open(&adata->conn->account, buf_string(mailbox));
97 buf_pool_release(&mailbox);
98
99 return bc;
100}
struct BodyCache * mutt_bcache_open(struct ConnAccount *account, const char *mailbox)
Open an Email-Body Cache.
Definition: bcache.c:143
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 109 of file message.c.

110{
112 struct ImapMboxData *mdata = imap_mdata_get(m);
113
114 if (!e || !adata || (adata->mailbox != m))
115 return NULL;
116
117 mdata->bcache = imap_bcache_open(m);
118 char id[64] = { 0 };
119 snprintf(id, sizeof(id), "%u-%u", mdata->uidvalidity, imap_edata_get(e)->uid);
120 return mutt_bcache_get(mdata->bcache, id);
121}
FILE * mutt_bcache_get(struct BodyCache *bcache, const char *id)
Open a file in the Body Cache.
Definition: bcache.c:182
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:82
+ 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 130 of file message.c.

131{
133 struct ImapMboxData *mdata = imap_mdata_get(m);
134
135 if (!e || !adata || (adata->mailbox != m))
136 return NULL;
137
138 mdata->bcache = imap_bcache_open(m);
139 char id[64] = { 0 };
140 snprintf(id, sizeof(id), "%u-%u", mdata->uidvalidity, imap_edata_get(e)->uid);
141 return mutt_bcache_put(mdata->bcache, id);
142}
FILE * mutt_bcache_put(struct BodyCache *bcache, const char *id)
Create a file in the Body Cache.
Definition: bcache.c:209
+ 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 151 of file message.c.

152{
154 struct ImapMboxData *mdata = imap_mdata_get(m);
155
156 if (!e || !adata || (adata->mailbox != m))
157 return -1;
158
159 mdata->bcache = imap_bcache_open(m);
160 char id[64] = { 0 };
161 snprintf(id, sizeof(id), "%u-%u", mdata->uidvalidity, imap_edata_get(e)->uid);
162
163 return mutt_bcache_commit(mdata->bcache, id);
164}
int mutt_bcache_commit(struct BodyCache *bcache, const char *id)
Move a temporary file into the Body Cache.
Definition: bcache.c:249
+ 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 193 of file message.c.

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

311{
312 if (!s)
313 return -1;
314
315 char tmp[128] = { 0 };
316 char *ptmp = NULL;
317 size_t plen = 0;
318
319 while (*s)
320 {
321 SKIPWS(s);
322
323 if (mutt_istr_startswith(s, "FLAGS"))
324 {
325 s = msg_parse_flags(h, s);
326 if (!s)
327 return -1;
328 }
329 else if ((plen = mutt_istr_startswith(s, "UID")))
330 {
331 s += plen;
332 SKIPWS(s);
333 if (!mutt_str_atoui(s, &h->edata->uid))
334 return -1;
335
336 s = imap_next_word(s);
337 }
338 else if ((plen = mutt_istr_startswith(s, "INTERNALDATE")))
339 {
340 s += plen;
341 SKIPWS(s);
342 if (*s != '\"')
343 {
344 mutt_debug(LL_DEBUG1, "bogus INTERNALDATE entry: %s\n", s);
345 return -1;
346 }
347 s++;
348 ptmp = tmp;
349 while (*s && (*s != '\"') && (ptmp != (tmp + sizeof(tmp) - 1)))
350 *ptmp++ = *s++;
351 if (*s != '\"')
352 return -1;
353 s++; /* skip past the trailing " */
354 *ptmp = '\0';
356 }
357 else if ((plen = mutt_istr_startswith(s, "RFC822.SIZE")))
358 {
359 s += plen;
360 SKIPWS(s);
361 ptmp = tmp;
362 while (isdigit((unsigned char) *s) && (ptmp != (tmp + sizeof(tmp) - 1)))
363 *ptmp++ = *s++;
364 *ptmp = '\0';
365 if (!mutt_str_atol(tmp, &h->content_length))
366 return -1;
367 }
368 else if (mutt_istr_startswith(s, "BODY") || mutt_istr_startswith(s, "RFC822.HEADER"))
369 {
370 /* handle above, in msg_fetch_header */
371 return -2;
372 }
373 else if ((plen = mutt_istr_startswith(s, "MODSEQ")))
374 {
375 s += plen;
376 SKIPWS(s);
377 if (*s != '(')
378 {
379 mutt_debug(LL_DEBUG1, "bogus MODSEQ response: %s\n", s);
380 return -1;
381 }
382 s++;
383 while (*s && (*s != ')'))
384 s++;
385 if (*s == ')')
386 {
387 s++;
388 }
389 else
390 {
391 mutt_debug(LL_DEBUG1, "Unterminated MODSEQ response: %s\n", s);
392 return -1;
393 }
394 }
395 else if (*s == ')')
396 {
397 s++; /* end of request */
398 }
399 else if (*s)
400 {
401 /* got something i don't understand */
402 imap_error("msg_parse_fetch", s);
403 return -1;
404 }
405 }
406
407 return 0;
408}
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:193
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:853
unsigned int uid
32-bit Message UID
Definition: edata.h:45
time_t received
Definition: message.h:37
long content_length
Definition: message.h:38
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ msg_fetch_header()

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

Import IMAP FETCH response into an ImapHeader.

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

Expects string beginning with * n FETCH.

Definition at line 422 of file message.c.

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

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

505{
506 bool abort = false;
507
509 /* L10N: This prompt is made if the user hits Ctrl-C when opening an IMAP mailbox */
510 if (query_yesorno(_("Abort download and close mailbox?"), MUTT_YES) == MUTT_YES)
511 {
512 abort = true;
514 }
515 SigInt = false;
516
517 return abort;
518}
void mutt_flushinp(void)
Empty all the keyboard buffers.
Definition: get.c:57
void imap_close_connection(struct ImapAccountData *adata)
Close an IMAP connection.
Definition: imap.c:850
#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:327
volatile sig_atomic_t SigInt
true after SIGINT is received
Definition: signal.c:63
+ 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 528 of file message.c.

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

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

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

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

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

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

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

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

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

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