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

Manage IMAP messages. More...

#include "config.h"
#include <limits.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "private.h"
#include "mutt/lib.h"
#include "config/lib.h"
#include "email/lib.h"
#include "core/lib.h"
#include "conn/lib.h"
#include "gui/lib.h"
#include "mutt.h"
#include "message.h"
#include "lib.h"
#include "bcache/lib.h"
#include "key/lib.h"
#include "progress/lib.h"
#include "question/lib.h"
#include "adata.h"
#include "edata.h"
#include "external.h"
#include "mdata.h"
#include "msg_set.h"
#include "msn.h"
#include "mutt_logging.h"
#include "mx.h"
#include <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, struct Mailbox *m, unsigned int msn_count)
 Create a Hash Table for the UIDs.
 
static unsigned int imap_fetch_msn_seqset (struct Buffer *buf, struct ImapAccountData *adata, struct Mailbox *m, 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, struct Mailbox *m, 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, struct Mailbox *m, char *uid_seqset)
 Retrieve data from the header cache.
 
static int read_headers_condstore_qresync_updates (struct ImapAccountData *adata, struct Mailbox *m, 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
  • Thomas Klausner

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.

Definition in file message.c.

Function Documentation

◆ imap_bcache_open()

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

Open a message cache.

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

Definition at line 81 of file message.c.

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

◆ msg_cache_get()

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

Get the message cache entry for an email.

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

Definition at line 108 of file message.c.

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

◆ msg_cache_put()

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

Put an email into the message cache.

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

Definition at line 129 of file message.c.

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

◆ msg_cache_commit()

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

Add to the message cache.

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

Definition at line 150 of file message.c.

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

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

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

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

494{
495 buf[*len] = '\0';
496 int rc = mutt_socket_write_n(conn, buf, *len);
497 *len = 0;
498 return rc;
499}
#define mutt_socket_write_n(conn, buf, len)
Definition socket.h:58
+ 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 509 of file message.c.

510{
511 bool abort = false;
512
514 /* L10N: This prompt is made if the user hits Ctrl-C when opening an IMAP mailbox */
515 if (query_yesorno(_("Abort download and close mailbox?"), MUTT_YES) == MUTT_YES)
516 {
517 abort = true;
519 }
520 SigInt = false;
521
522 return abort;
523}
void mutt_flushinp(void)
MacroEvents moved to KeyModuleData UngetKeyEvents moved to KeyModuleData.
Definition get.c:81
void imap_close_connection(struct ImapAccountData *adata)
Close an IMAP connection.
Definition imap.c:1029
#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:329
volatile sig_atomic_t SigInt
true after SIGINT is received
Definition signal.c:68
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_alloc_uid_hash()

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

Create a Hash Table for the UIDs.

Parameters
adataImap Account data
mMailbox
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 534 of file message.c.

536{
537 struct ImapMboxData *mdata = imap_mdata_get(m);
538 if (mdata && !mdata->uid_hash)
539 mdata->uid_hash = mutt_hash_int_new(MAX(6 * msn_count / 5, 30), MUTT_HASH_NONE);
540}
struct HashTable * mutt_hash_int_new(size_t num_elems, HashFlags flags)
Create a new Hash Table (with integer keys)
Definition hash.c:287
@ MUTT_HASH_NONE
No flags are set.
Definition hash.h:115
#define MAX(a, b)
Return the maximum of two values.
Definition memory.h:38
+ 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,
struct Mailbox * m,
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]mMailbox
[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 560 of file message.c.

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

659{
660 /* If there are local_changes, we only want to note if the server
661 * flags have changed, so we can set a reopen flag in
662 * cmd_parse_fetch(). We don't want to count a local modification
663 * to the header flag as a "change". */
664 if ((old_hd_flag == new_hd_flag) && (local_changes != 0))
665 return;
666
667 if (new_hd_flag == h_flag)
668 return;
669
670 if (server_changes)
671 *server_changes = true;
672
673 /* Local changes have priority */
674 if (local_changes == 0)
675 mutt_set_flag(m, e, flag_name, new_hd_flag, true);
676}
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:54
+ 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,
struct Mailbox * m,
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
mMailbox
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 697 of file message.c.

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

Retrieve data from the header cache.

Parameters
adataImap Account data
mMailbox
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 847 of file message.c.

849{
850 int rc;
851 unsigned int uid = 0;
852
853 mutt_debug(LL_DEBUG2, "Reading uid seqset from header cache\n");
854 unsigned int msn = 1;
855 struct ImapMboxData *mdata = imap_mdata_get(m);
856 if (!mdata)
857 return -1;
858
859 if (m->verbose)
860 mutt_message(_("Evaluating cache..."));
861
862 struct SeqsetIterator *iter = mutt_seqset_iterator_new(uid_seqset);
863 if (!iter)
864 return -1;
865
866 while ((rc = mutt_seqset_iterator_next(iter, &uid)) == 0)
867 {
868 /* The seqset may contain more headers than the fetch request, so
869 * we need to watch and reallocate the context and msn_index */
870 imap_msn_reserve(&mdata->msn, msn);
871
872 struct Email *e = imap_hcache_get(mdata, uid);
873 if (e)
874 {
875 imap_msn_set(&mdata->msn, msn - 1, e);
876
878
880 e->edata = edata;
882
883 e->index = uid;
884 e->active = true;
885 e->changed = false;
886 edata->read = e->read;
887 edata->old = e->old;
888 edata->deleted = e->deleted;
889 edata->flagged = e->flagged;
890 edata->replied = e->replied;
891
892 edata->msn = msn;
893 edata->uid = uid;
895
896 mailbox_size_add(m, e);
897 m->emails[m->msg_count++] = e;
898
899 msn++;
900 }
901 else if (!uid)
902 {
903 /* A non-zero uid missing from the header cache is either the
904 * result of an expunged message (not recorded in the uid seqset)
905 * or a hole in the header cache.
906 *
907 * We have to assume it's an earlier expunge and compact the msn's
908 * in that case, because cmd_parse_vanished() won't find it in the
909 * uid_hash and decrement later msn's there.
910 *
911 * Thus we only increment the uid if the uid was 0: an actual
912 * stored "blank" in the uid seqset.
913 */
914 msn++;
915 }
916 }
917
919
920 return rc;
921}
#define mutt_message(...)
Definition logging2.h:93
struct SeqsetIterator * mutt_seqset_iterator_new(const char *seqset)
Create a new Sequence Set Iterator.
Definition util.c:1138
int mutt_seqset_iterator_next(struct SeqsetIterator *iter, unsigned int *next)
Get the next UID from a Sequence Set.
Definition util.c:1159
void mutt_seqset_iterator_free(struct SeqsetIterator **ptr)
Free a Sequence Set Iterator.
Definition util.c:1218
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:1208
UID Sequence Set Iterator.
Definition private.h:185
+ 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,
struct Mailbox * m,
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
mMailbox
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 936 of file message.c.

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

1039{
1040 ASSERT(m);
1042 struct ImapMboxData *mdata = imap_mdata_get(m);
1043 if (!adata || (adata->mailbox != m) || !mdata)
1044 return -1;
1045
1046 const size_t max_msn = imap_msn_highest(&mdata->msn);
1047
1048 unsigned int msn;
1049 unsigned int uid;
1050 struct Email *e = NULL;
1051 struct Email *uidh = NULL;
1052
1053 for (int i = 0; i < m->msg_count; i++)
1054 {
1055 e = m->emails[i];
1056 const struct ImapEmailData *edata = imap_edata_get(e);
1057 if (!edata)
1058 goto fail;
1059
1060 msn = imap_edata_get(e)->msn;
1061 uid = imap_edata_get(e)->uid;
1062
1063 if ((msn < 1) || (msn > max_msn) || imap_msn_get(&mdata->msn, msn - 1) != e)
1064 goto fail;
1065
1066 uidh = (struct Email *) mutt_hash_int_find(mdata->uid_hash, uid);
1067 if (uidh != e)
1068 goto fail;
1069 }
1070
1071 return 0;
1072
1073fail:
1074 imap_msn_free(&mdata->msn);
1075 mutt_hash_free(&mdata->uid_hash);
1079
1080 for (int i = 0; i < m->msg_count; i++)
1081 {
1082 if (m->emails[i] && m->emails[i]->edata)
1083 imap_edata_free(&m->emails[i]->edata);
1084 email_free(&m->emails[i]);
1085 }
1086 m->msg_count = 0;
1087 m->size = 0;
1088 hcache_delete_raw(mdata->hcache, "MODSEQ", 6);
1090 imap_hcache_close(mdata);
1091
1092 if (m->verbose)
1093 {
1094 /* L10N: After opening an IMAP mailbox using QRESYNC, Mutt performs a quick
1095 sanity check. If that fails, Mutt reopens the mailbox using a normal
1096 download. */
1097 mutt_error(_("QRESYNC failed. Reopening mailbox."));
1098 }
1099 return -1;
1100}
void email_free(struct Email **ptr)
Free an Email.
Definition email.c:46
#define mutt_error(...)
Definition logging2.h:94
void * mutt_hash_int_find(const struct HashTable *table, unsigned int intkey)
Find the HashElem data in a Hash Table element using a key.
Definition hash.c:394
void mutt_hash_free(struct HashTable **ptr)
Free a hash table.
Definition hash.c:459
int hcache_delete_raw(struct HeaderCache *hc, const char *key, size_t keylen)
Multiplexor for StoreOps::delete_record.
Definition hcache.c:765
int imap_hcache_clear_uid_seqset(struct ImapMboxData *mdata)
Delete a UID Sequence Set from the header cache.
Definition util.c:444
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:59
struct HeaderCache * hcache
Email header cache.
Definition mdata.h:64
struct HashTable * subj_hash
Hash Table: "Subject" -> Email.
Definition mailbox.h:126
struct HashTable * id_hash
Hash Table: "Message-ID" -> Email.
Definition mailbox.h:125
off_t size
Size of the Mailbox.
Definition mailbox.h:86
struct HashTable * label_hash
Hash Table: "X-Label" -> Email.
Definition mailbox.h:127
+ 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 1115 of file message.c.

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

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

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

◆ emails_to_uid_array()

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

Extract IMAP UIDs from Emails.

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

Definition at line 1691 of file message.c.

1692{
1693 struct Email **ep = NULL;
1694 ARRAY_FOREACH(ep, ea)
1695 {
1696 struct Email *e = *ep;
1697 struct ImapEmailData *edata = imap_edata_get(e);
1698
1699 ARRAY_ADD(uida, edata->uid);
1700 }
1701 ARRAY_SORT(uida, imap_sort_uid, NULL);
1702
1703 return ARRAY_SIZE(uida);
1704}
#define ARRAY_SORT(head, fn, sdata)
Sort an array.
Definition array.h:373
#define ARRAY_ADD(head, elem)
Add an element at the end of the array.
Definition array.h:157
#define ARRAY_FOREACH(elem, head)
Iterate over all elements of the array.
Definition array.h:223
#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:48
+ 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 1716 of file message.c.

1718{
1719 if (!m || !ea || ARRAY_EMPTY(ea) || !dest)
1720 return -1;
1721
1723 if (!adata)
1724 return -1;
1725
1726 char buf[PATH_MAX] = { 0 };
1727 char mbox[PATH_MAX] = { 0 };
1728 char mmbox[PATH_MAX] = { 0 };
1729 char prompt[PATH_MAX + 64];
1730 int rc;
1731 struct ConnAccount cac = { { 0 } };
1732 enum QuadOption err_continue = MUTT_NO;
1733 int triedcreate = 0;
1734 struct Email *e_cur = *ARRAY_GET(ea, 0);
1735 bool single = (ARRAY_SIZE(ea) == 1);
1736
1737 if (single && e_cur->attach_del)
1738 {
1739 mutt_debug(LL_DEBUG3, "#1 Message contains attachments to be deleted\n");
1740 return 1;
1741 }
1742
1743 if (imap_parse_path(dest, &cac, buf, sizeof(buf)) != 0)
1744 {
1745 mutt_debug(LL_DEBUG1, "bad destination %s\n", dest);
1746 return -1;
1747 }
1748
1749 /* check that the save-to folder is in the same account */
1750 if (!imap_account_match(&adata->conn->account, &cac))
1751 {
1752 mutt_debug(LL_DEBUG3, "%s not same server as %s\n", dest, mailbox_path(m));
1753 return 1;
1754 }
1755
1756 imap_fix_path_with_delim(adata->delim, buf, mbox, sizeof(mbox));
1757 if (*mbox == '\0')
1758 mutt_str_copy(mbox, "INBOX", sizeof(mbox));
1759 imap_munge_mbox_name(adata->unicode, mmbox, sizeof(mmbox), mbox);
1760
1761 /* loop in case of TRYCREATE */
1762 struct Buffer *cmd = buf_pool_get();
1763 struct Buffer *sync_cmd = buf_pool_get();
1764 do
1765 {
1766 buf_reset(sync_cmd);
1767 buf_reset(cmd);
1768
1769 if (single)
1770 {
1771 mutt_message(_("Copying message %d to %s..."), e_cur->index + 1, mbox);
1772 buf_add_printf(cmd, "UID COPY %u %s", imap_edata_get(e_cur)->uid, mmbox);
1773
1774 if (e_cur->active && e_cur->changed)
1775 {
1776 rc = imap_sync_message_for_copy(m, e_cur, sync_cmd, &err_continue);
1777 if (rc < 0)
1778 {
1779 mutt_debug(LL_DEBUG1, "#2 could not sync\n");
1780 goto out;
1781 }
1782 }
1783 rc = imap_exec(adata, buf_string(cmd), IMAP_CMD_QUEUE);
1784 if (rc != IMAP_EXEC_SUCCESS)
1785 {
1786 mutt_debug(LL_DEBUG1, "#2 could not queue copy\n");
1787 goto out;
1788 }
1789 }
1790 else /* copy tagged messages */
1791 {
1792 /* if any messages have attachments to delete, fall through to FETCH
1793 * and APPEND. TODO: Copy what we can with COPY, fall through for the
1794 * remainder. */
1795 struct Email **ep = NULL;
1796 ARRAY_FOREACH(ep, ea)
1797 {
1798 struct Email *e = *ep;
1799 if (e->attach_del)
1800 {
1801 mutt_debug(LL_DEBUG3, "#2 Message contains attachments to be deleted\n");
1802 rc = 1;
1803 goto out;
1804 }
1805
1806 if (e->active && e->changed)
1807 {
1808 rc = imap_sync_message_for_copy(m, e, sync_cmd, &err_continue);
1809 if (rc < 0)
1810 {
1811 mutt_debug(LL_DEBUG1, "#1 could not sync\n");
1812 goto out;
1813 }
1814 }
1815 }
1816
1817 struct UidArray uida = ARRAY_HEAD_INITIALIZER;
1818 emails_to_uid_array(ea, &uida);
1819 rc = imap_exec_msg_set(adata, "UID COPY", mmbox, &uida);
1820 ARRAY_FREE(&uida);
1821
1822 if (rc == 0)
1823 {
1824 mutt_debug(LL_DEBUG1, "No messages tagged\n");
1825 rc = -1;
1826 goto out;
1827 }
1828 else if (rc < 0)
1829 {
1830 mutt_debug(LL_DEBUG1, "#1 could not queue copy\n");
1831 goto out;
1832 }
1833 else
1834 {
1835 mutt_message(ngettext("Copying %d message to %s...", "Copying %d messages to %s...", rc),
1836 rc, mbox);
1837 }
1838 }
1839
1840 /* let's get it on */
1841 rc = imap_exec(adata, NULL, IMAP_CMD_NONE);
1842 if (rc == IMAP_EXEC_ERROR)
1843 {
1844 if (triedcreate)
1845 {
1846 mutt_debug(LL_DEBUG1, "Already tried to create mailbox %s\n", mbox);
1847 break;
1848 }
1849 /* bail out if command failed for reasons other than nonexistent target */
1850 if (!mutt_istr_startswith(imap_get_qualifier(adata->buf), "[TRYCREATE]"))
1851 break;
1852 mutt_debug(LL_DEBUG3, "server suggests TRYCREATE\n");
1853 snprintf(prompt, sizeof(prompt), _("Create %s?"), mbox);
1854 const bool c_confirm_create = cs_subset_bool(NeoMutt->sub, "confirm_create");
1855 if (c_confirm_create &&
1856 (query_yesorno_help(prompt, MUTT_YES, NeoMutt->sub, "confirm_create") != MUTT_YES))
1857 {
1859 goto out;
1860 }
1861 if (imap_create_mailbox(adata, mbox) < 0)
1862 break;
1863 triedcreate = 1;
1864 }
1865 } while (rc == IMAP_EXEC_ERROR);
1866
1867 if (rc != 0)
1868 {
1869 imap_error("imap_copy_messages", adata->buf);
1870 goto out;
1871 }
1872
1873 /* cleanup */
1874 if (save_opt == SAVE_MOVE)
1875 {
1876 struct Email **ep = NULL;
1877 ARRAY_FOREACH(ep, ea)
1878 {
1879 struct Email *e = *ep;
1880 mutt_set_flag(m, e, MUTT_DELETE, true, true);
1881 mutt_set_flag(m, e, MUTT_PURGE, true, true);
1882 }
1883 }
1884
1885 rc = 0;
1886
1887out:
1888 buf_pool_release(&cmd);
1889 buf_pool_release(&sync_cmd);
1890
1891 return (rc < 0) ? -1 : rc;
1892}
#define ARRAY_EMPTY(head)
Check if an array is empty.
Definition array.h:74
#define ARRAY_FREE(head)
Release all memory.
Definition array.h:209
#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:216
@ SAVE_MOVE
Move message to another mailbox, removing the original.
Definition external.h:54
int imap_exec(struct ImapAccountData *adata, const char *cmdstr, ImapCmdFlags flags)
Execute a command and wait for the response from the server.
Definition command.c:1420
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:482
static int emails_to_uid_array(struct EmailArray *ea, struct UidArray *uida)
Extract IMAP UIDs from Emails.
Definition message.c:1691
@ IMAP_EXEC_SUCCESS
Imap command executed or queued successfully.
Definition private.h:95
@ IMAP_EXEC_ERROR
Imap command failure.
Definition private.h:96
char * imap_fix_path_with_delim(char delim, const char *mailbox, char *path, size_t plen)
Fix up the imap path.
Definition util.c:718
bool imap_account_match(const struct ConnAccount *a1, const struct ConnAccount *a2)
Compare two Accounts.
Definition util.c:1106
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:971
@ IMAP_CMD_NONE
No flags are set.
Definition private.h:82
@ IMAP_CMD_QUEUE
Queue a command, do not execute.
Definition private.h:84
char * imap_get_qualifier(char *buf)
Get the qualifier from a tagged response.
Definition util.c:812
int imap_create_mailbox(struct ImapAccountData *adata, const char *mailbox)
Create a new mailbox.
Definition imap.c:542
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:1114
@ LL_DEBUG3
Log at debug level 3.
Definition logging2.h:47
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:127
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:586
@ MUTT_PURGE
Messages to be purged (bypass trash)
Definition mutt.h:96
@ MUTT_DELETE
Messages to be deleted.
Definition mutt.h:94
#define PATH_MAX
Definition mutt.h:49
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:357
Login details for a remote server.
Definition connaccount.h:59
bool attach_del
Has an attachment marked for deletion.
Definition email.h:99
bool unicode
If true, we can send UTF-8, and the server will use UTF8 rather than mUTF7.
Definition adata.h:64
+ 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 1901 of file message.c.

1902{
1904 struct ImapMboxData *mdata = imap_mdata_get(m);
1905
1906 if (!e || !adata || (adata->mailbox != m) || !mdata)
1907 return -1;
1908
1909 mdata->bcache = imap_bcache_open(m);
1910 char id[64] = { 0 };
1911 snprintf(id, sizeof(id), "%u-%u", mdata->uidvalidity, imap_edata_get(e)->uid);
1912 return mutt_bcache_del(mdata->bcache, id);
1913}
int mutt_bcache_del(struct BodyCache *bcache, const char *id)
Delete a file from the Body Cache.
Definition bcache.c:274
+ 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 1920 of file message.c.

1921{
1923 struct ImapMboxData *mdata = imap_mdata_get(m);
1924
1925 if (!adata || (adata->mailbox != m) || !mdata)
1926 return -1;
1927
1928 mdata->bcache = imap_bcache_open(m);
1930
1931 return 0;
1932}
int mutt_bcache_list(struct BodyCache *bcache, bcache_list_t want_id, void *data)
Find matching entries in the Body Cache.
Definition bcache.c:339
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 1952 of file message.c.

1953{
1955 if (!adata || (adata->mailbox != m))
1956 return NULL;
1957
1958 struct ImapHeader newh = { 0 };
1959 struct ImapEmailData old_edata = { 0 };
1960 int local_changes = e->changed;
1961
1962 struct ImapEmailData *edata = e->edata;
1963 newh.edata = edata;
1964
1965 mutt_debug(LL_DEBUG2, "parsing FLAGS\n");
1966 s = msg_parse_flags(&newh, s);
1967 if (!s)
1968 return NULL;
1969
1970 /* Update tags system */
1971 /* We take a copy of the tags so we can split the string */
1972 char *tags_copy = mutt_str_dup(edata->flags_remote);
1973 driver_tags_replace(&e->tags, tags_copy);
1974 FREE(&tags_copy);
1975
1976 /* YAUH (yet another ugly hack): temporarily set context to
1977 * read-write even if it's read-only, so *server* updates of
1978 * flags can be processed by mutt_set_flag. mailbox->changed must
1979 * be restored afterwards */
1980 bool readonly = m->readonly;
1981 m->readonly = false;
1982
1983 /* This is redundant with the following two checks. Removing:
1984 * mutt_set_flag (m, e, MUTT_NEW, !(edata->read || edata->old), true); */
1985 set_changed_flag(m, e, local_changes, server_changes, MUTT_OLD, old_edata.old,
1986 edata->old, e->old);
1987 set_changed_flag(m, e, local_changes, server_changes, MUTT_READ,
1988 old_edata.read, edata->read, e->read);
1989 set_changed_flag(m, e, local_changes, server_changes, MUTT_DELETE,
1990 old_edata.deleted, edata->deleted, e->deleted);
1991 set_changed_flag(m, e, local_changes, server_changes, MUTT_FLAG,
1992 old_edata.flagged, edata->flagged, e->flagged);
1993 set_changed_flag(m, e, local_changes, server_changes, MUTT_REPLIED,
1994 old_edata.replied, edata->replied, e->replied);
1995
1996 /* this message is now definitively *not* changed (mutt_set_flag
1997 * marks things changed as a side-effect) */
1998 if (local_changes == 0)
1999 e->changed = false;
2000 m->changed &= !readonly;
2001 m->readonly = readonly;
2002
2003 return s;
2004}
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:656
@ MUTT_READ
Messages that have been read.
Definition mutt.h:92
@ MUTT_OLD
Old messages.
Definition mutt.h:90
@ MUTT_FLAG
Flagged messages.
Definition mutt.h:98
@ MUTT_REPLIED
Messages that have been replied to.
Definition mutt.h:91
bool readonly
Don't allow changes to the mailbox.
Definition mailbox.h:118
+ Here is the call graph for this function:
+ Here is the caller graph for this function: