NeoMutt  2022-04-29-249-gaae397
Teaching an old dog new tricks
DOXYGEN
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 "commands.h"
#include "edata.h"
#include "mdata.h"
#include "msn.h"
#include "mutt_globals.h"
#include "mutt_logging.h"
#include "muttlib.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 BodyCachemsg_cache_open (struct Mailbox *m)
 Open a message cache. More...
 
static FILE * msg_cache_get (struct Mailbox *m, struct Email *e)
 Get the message cache entry for an email. More...
 
static FILE * msg_cache_put (struct Mailbox *m, struct Email *e)
 Put an email into the message cache. More...
 
static int msg_cache_commit (struct Mailbox *m, struct Email *e)
 Add to the message cache. More...
 
static int msg_cache_clean_cb (const char *id, struct BodyCache *bcache, void *data)
 Delete an entry from the message cache - Implements bcache_list_t -. More...
 
static char * msg_parse_flags (struct ImapHeader *h, char *s)
 Read a FLAGS token into an ImapHeader. More...
 
static int msg_parse_fetch (struct ImapHeader *h, char *s)
 Handle headers returned from header fetch. More...
 
static int msg_fetch_header (struct Mailbox *m, struct ImapHeader *ih, char *buf, FILE *fp)
 Import IMAP FETCH response into an ImapHeader. More...
 
static int flush_buffer (char *buf, size_t *len, struct Connection *conn)
 Write data to a connection. More...
 
static bool query_abort_header_download (struct ImapAccountData *adata)
 Ask the user whether to abort the download. More...
 
static void imap_alloc_uid_hash (struct ImapAccountData *adata, unsigned int msn_count)
 Create a Hash Table for the UIDs. More...
 
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. More...
 
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. More...
 
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. More...
 
static int read_headers_qresync_eval_cache (struct ImapAccountData *adata, char *uid_seqset)
 Retrieve data from the header cache. More...
 
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. More...
 
static int imap_verify_qresync (struct Mailbox *m)
 Check to see if QRESYNC got jumbled. More...
 
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. More...
 
int imap_read_headers (struct Mailbox *m, unsigned int msn_begin, unsigned int msn_end, bool initial_download)
 Read headers from the server. More...
 
int imap_append_message (struct Mailbox *m, struct Message *msg)
 Write an email back to the server. More...
 
int imap_copy_messages (struct Mailbox *m, struct EmailList *el, const char *dest, enum MessageSaveOpt save_opt)
 Server COPY messages to another folder. More...
 
int imap_cache_del (struct Mailbox *m, struct Email *e)
 Delete an email from the body cache. More...
 
int imap_cache_clean (struct Mailbox *m)
 Delete all the entries in the message cache. More...
 
char * imap_set_flags (struct Mailbox *m, struct Email *e, char *s, bool *server_changes)
 Fill the message header according to the server flags. More...
 
bool imap_msg_open (struct Mailbox *m, struct Message *msg, int msgno)
 Open an email message in a Mailbox - Implements MxOps::msg_open() -. More...
 
int imap_msg_commit (struct Mailbox *m, struct Message *msg)
 Save changes to an email - Implements MxOps::msg_commit() -. More...
 
int imap_msg_close (struct Mailbox *m, struct Message *msg)
 Close an email - Implements MxOps::msg_close() -. More...
 
int imap_msg_save_hcache (struct Mailbox *m, struct Email *e)
 Save message to the header cache - Implements MxOps::msg_save_hcache() -. More...
 

Detailed Description

Manage IMAP messages.

Authors
  • Brandon Long
  • Brendan Cully
  • Richard Russon

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

◆ msg_cache_open()

static struct BodyCache * msg_cache_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 78 of file message.c.

79{
82
83 if (!adata || (adata->mailbox != m))
84 return NULL;
85
86 if (mdata->bcache)
87 return mdata->bcache;
88
89 struct Buffer *mailbox = mutt_buffer_pool_get();
90 imap_cachepath(adata->delim, mdata->name, mailbox);
91
92 struct BodyCache *bc = mutt_bcache_open(&adata->conn->account,
93 mutt_buffer_string(mailbox));
95
96 return bc;
97}
struct BodyCache * mutt_bcache_open(struct ConnAccount *account, const char *mailbox)
Open an Email-Body Cache.
Definition: bcache.c:144
static const char * mutt_buffer_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:77
struct ImapAccountData * imap_adata_get(struct Mailbox *m)
Get the Account data for this mailbox.
Definition: adata.c:89
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:716
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
void * adata
Private data (for Mailbox backends)
Definition: account.h:43
Local cache of email bodies.
Definition: bcache.c:51
String manipulation buffer.
Definition: buffer.h:34
struct ConnAccount account
Account details: username, password, etc.
Definition: connection.h:50
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:39
char * name
Mailbox name.
Definition: mdata.h:40
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 106 of file message.c.

107{
109 struct ImapMboxData *mdata = imap_mdata_get(m);
110
111 if (!e || !adata || (adata->mailbox != m))
112 return NULL;
113
114 mdata->bcache = msg_cache_open(m);
115 char id[64] = { 0 };
116 snprintf(id, sizeof(id), "%u-%u", mdata->uidvalidity, imap_edata_get(e)->uid);
117 return mutt_bcache_get(mdata->bcache, id);
118}
FILE * mutt_bcache_get(struct BodyCache *bcache, const char *id)
Open a file in the Body Cache.
Definition: bcache.c:180
struct ImapEmailData * imap_edata_get(struct Email *e)
Get the private data for this Email.
Definition: edata.c:65
static struct BodyCache * msg_cache_open(struct Mailbox *m)
Open a message cache.
Definition: message.c:78
+ 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 127 of file message.c.

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

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

191{
192 struct ImapEmailData *edata = h->edata;
193
194 /* sanity-check string */
195 size_t plen = mutt_istr_startswith(s, "FLAGS");
196 if (plen == 0)
197 {
198 mutt_debug(LL_DEBUG1, "not a FLAGS response: %s\n", s);
199 return NULL;
200 }
201 s += plen;
202 SKIPWS(s);
203 if (*s != '(')
204 {
205 mutt_debug(LL_DEBUG1, "bogus FLAGS response: %s\n", s);
206 return NULL;
207 }
208 s++;
209
210 FREE(&edata->flags_system);
211 FREE(&edata->flags_remote);
212
213 edata->deleted = false;
214 edata->flagged = false;
215 edata->replied = false;
216 edata->read = false;
217 edata->old = false;
218
219 /* start parsing */
220 while (*s && (*s != ')'))
221 {
222 if ((plen = mutt_istr_startswith(s, "\\deleted")))
223 {
224 s += plen;
225 edata->deleted = true;
226 }
227 else if ((plen = mutt_istr_startswith(s, "\\flagged")))
228 {
229 s += plen;
230 edata->flagged = true;
231 }
232 else if ((plen = mutt_istr_startswith(s, "\\answered")))
233 {
234 s += plen;
235 edata->replied = true;
236 }
237 else if ((plen = mutt_istr_startswith(s, "\\seen")))
238 {
239 s += plen;
240 edata->read = true;
241 }
242 else if ((plen = mutt_istr_startswith(s, "\\recent")))
243 {
244 s += plen;
245 }
246 else if ((plen = mutt_istr_startswith(s, "old")))
247 {
248 s += plen;
249 edata->old = cs_subset_bool(NeoMutt->sub, "mark_old");
250 }
251 else
252 {
253 char ctmp;
254 char *flag_word = s;
255 bool is_system_keyword = mutt_istr_startswith(s, "\\");
256
257 while (*s && !IS_SPACE(*s) && (*s != ')'))
258 s++;
259
260 ctmp = *s;
261 *s = '\0';
262
263 /* store other system flags as well (mainly \\Draft) */
264 if (is_system_keyword)
265 mutt_str_append_item(&edata->flags_system, flag_word, ' ');
266 /* store custom flags as well */
267 else
268 mutt_str_append_item(&edata->flags_remote, flag_word, ' ');
269
270 *s = ctmp;
271 }
272 SKIPWS(s);
273 }
274
275 /* wrap up, or note bad flags response */
276 if (*s == ')')
277 s++;
278 else
279 {
280 mutt_debug(LL_DEBUG1, "Unterminated FLAGS response: %s\n", s);
281 return NULL;
282 }
283
284 return s;
285}
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
#define mutt_debug(LEVEL,...)
Definition: logging.h:84
@ LL_DEBUG1
Log at debug level 1.
Definition: logging.h:40
#define FREE(x)
Definition: memory.h:43
void mutt_str_append_item(char **str, const char *item, char sep)
Add string to another separated by sep.
Definition: string.c:346
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition: string.c:239
static size_t plen
Length of cached packet.
Definition: pgppacket.c:39
#define IS_SPACE(ch)
Definition: string2.h:38
#define SKIPWS(ch)
Definition: string2.h:46
void * edata
Driver-specific data.
Definition: email.h:72
IMAP-specific Email data -.
Definition: edata.h:34
struct ImapEmailData * edata
Definition: message.h:34
Container for Accounts, Notifications.
Definition: neomutt.h:37
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
+ 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 295 of file message.c.

296{
297 if (!s)
298 return -1;
299
300 char tmp[128] = { 0 };
301 char *ptmp = NULL;
302 size_t plen = 0;
303
304 while (*s)
305 {
306 SKIPWS(s);
307
308 if (mutt_istr_startswith(s, "FLAGS"))
309 {
310 s = msg_parse_flags(h, s);
311 if (!s)
312 return -1;
313 }
314 else if ((plen = mutt_istr_startswith(s, "UID")))
315 {
316 s += plen;
317 SKIPWS(s);
318 if (!mutt_str_atoui(s, &h->edata->uid))
319 return -1;
320
321 s = imap_next_word(s);
322 }
323 else if ((plen = mutt_istr_startswith(s, "INTERNALDATE")))
324 {
325 s += plen;
326 SKIPWS(s);
327 if (*s != '\"')
328 {
329 mutt_debug(LL_DEBUG1, "bogus INTERNALDATE entry: %s\n", s);
330 return -1;
331 }
332 s++;
333 ptmp = tmp;
334 while (*s && (*s != '\"') && (ptmp != (tmp + sizeof(tmp) - 1)))
335 *ptmp++ = *s++;
336 if (*s != '\"')
337 return -1;
338 s++; /* skip past the trailing " */
339 *ptmp = '\0';
341 }
342 else if ((plen = mutt_istr_startswith(s, "RFC822.SIZE")))
343 {
344 s += plen;
345 SKIPWS(s);
346 ptmp = tmp;
347 while (isdigit((unsigned char) *s) && (ptmp != (tmp + sizeof(tmp) - 1)))
348 *ptmp++ = *s++;
349 *ptmp = '\0';
350 if (!mutt_str_atol(tmp, &h->content_length))
351 return -1;
352 }
353 else if (mutt_istr_startswith(s, "BODY") || mutt_istr_startswith(s, "RFC822.HEADER"))
354 {
355 /* handle above, in msg_fetch_header */
356 return -2;
357 }
358 else if ((plen = mutt_istr_startswith(s, "MODSEQ")))
359 {
360 s += plen;
361 SKIPWS(s);
362 if (*s != '(')
363 {
364 mutt_debug(LL_DEBUG1, "bogus MODSEQ response: %s\n", s);
365 return -1;
366 }
367 s++;
368 while (*s && (*s != ')'))
369 s++;
370 if (*s == ')')
371 s++;
372 else
373 {
374 mutt_debug(LL_DEBUG1, "Unterminated MODSEQ response: %s\n", s);
375 return -1;
376 }
377 }
378 else if (*s == ')')
379 s++; /* end of request */
380 else if (*s)
381 {
382 /* got something i don't understand */
383 imap_error("msg_parse_fetch", s);
384 return -1;
385 }
386 }
387
388 return 0;
389}
const char * mutt_str_atol(const char *str, long *dst)
Convert ASCII string to a long.
Definition: atoi.c:135
const char * mutt_str_atoui(const char *str, unsigned int *dst)
Convert ASCII string to an unsigned integer.
Definition: atoi.c:202
time_t mutt_date_parse_imap(const char *s)
Parse date of the form: DD-MMM-YYYY HH:MM:SS +ZZzz.
Definition: date.c:601
static char * msg_parse_flags(struct ImapHeader *h, char *s)
Read a FLAGS token into an ImapHeader.
Definition: message.c:190
void imap_error(const char *where, const char *msg)
Show an error and abort.
Definition: util.c:663
char * imap_next_word(char *s)
Find where the next IMAP word begins.
Definition: util.c:789
unsigned int uid
32-bit Message UID
Definition: edata.h:44
time_t received
Definition: message.h:36
long content_length
Definition: message.h:37
+ 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 403 of file message.c.

404{
405 int rc = -1; /* default now is that string isn't FETCH response */
406
408
409 if (buf[0] != '*')
410 return rc;
411
412 /* skip to message number */
414 if (!mutt_str_atoui(buf, &ih->edata->msn))
415 return rc;
416
417 /* find FETCH tag */
419 if (!mutt_istr_startswith(buf, "FETCH"))
420 return rc;
421
422 rc = -2; /* we've got a FETCH response, for better or worse */
423 buf = strchr(buf, '(');
424 if (!buf)
425 return rc;
426 buf++;
427
428 /* FIXME: current implementation - call msg_parse_fetch - if it returns -2,
429 * read header lines and call it again. Silly. */
430 int parse_rc = msg_parse_fetch(ih, buf);
431 if (parse_rc == 0)
432 return 0;
433 if ((parse_rc != -2) || !fp)
434 return rc;
435
436 unsigned int bytes = 0;
437 if (imap_get_literal_count(buf, &bytes) == 0)
438 {
439 imap_read_literal(fp, adata, bytes, NULL);
440
441 /* we may have other fields of the FETCH _after_ the literal
442 * (eg Domino puts FLAGS here). Nothing wrong with that, either.
443 * This all has to go - we should accept literals and nonliterals
444 * interchangeably at any time. */
446 return rc;
447
448 if (msg_parse_fetch(ih, adata->buf) == -1)
449 return rc;
450 }
451
452 rc = 0; /* success */
453
454 /* subtract headers from message size - unfortunately only the subset of
455 * headers we've requested. */
456 ih->content_length -= bytes;
457
458 return rc;
459}
int imap_cmd_step(struct ImapAccountData *adata)
Reads server responses from an IMAP command.
Definition: command.c:1076
static int msg_parse_fetch(struct ImapHeader *h, char *s)
Handle headers returned from header fetch.
Definition: message.c:295
int imap_get_literal_count(const char *buf, unsigned int *bytes)
Write number of bytes in an IMAP literal into bytes.
Definition: util.c:745
#define IMAP_RES_CONTINUE
* ...
Definition: private.h:57
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:598
char * buf
Definition: adata.h:59
unsigned int msn
Message Sequence Number.
Definition: edata.h:45
+ 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 469 of file message.c.

470{
471 buf[*len] = '\0';
472 int rc = mutt_socket_write_n(conn, buf, *len);
473 *len = 0;
474 return rc;
475}
#define mutt_socket_write_n(conn, buf, len)
Definition: socket.h:61
+ 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 485 of file message.c.

486{
487 bool abort = false;
488
490 /* L10N: This prompt is made if the user hits Ctrl-C when opening an IMAP mailbox */
491 if (mutt_yesorno(_("Abort download and close mailbox?"), MUTT_YES) == MUTT_YES)
492 {
493 abort = true;
495 }
496 SigInt = false;
497
498 return abort;
499}
void mutt_flushinp(void)
Empty all the keyboard buffers.
Definition: curs_lib.c:592
void imap_close_connection(struct ImapAccountData *adata)
Close an IMAP connection.
Definition: imap.c:853
#define _(a)
Definition: message.h:28
SIG_ATOMIC_VOLATILE_T SigInt
true after SIGINT is received
Definition: mutt_globals.h:69
@ MUTT_YES
User answered 'Yes', or assume 'Yes'.
Definition: quad.h:39
enum QuadOption mutt_yesorno(const char *msg, enum QuadOption def)
Ask the user a Yes/No question.
Definition: question.c:194
+ 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 509 of file message.c.

510{
511 struct ImapMboxData *mdata = adata->mailbox->mdata;
512 if (!mdata->uid_hash)
513 mdata->uid_hash = mutt_hash_int_new(MAX(6 * msn_count / 5, 30), MUTT_HASH_NO_FLAGS);
514}
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:30
+ 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 533 of file message.c.

536{
537 struct ImapMboxData *mdata = adata->mailbox->mdata;
538 unsigned int max_headers_per_fetch = UINT_MAX;
539 bool first_chunk = true;
540 int state = 0; /* 1: single msn, 2: range of msn */
541 unsigned int msn;
542 unsigned int range_begin = 0;
543 unsigned int range_end = 0;
544 unsigned int msn_count = 0;
545
547 if (msn_end < msn_begin)
548 return 0;
549
550 const long c_imap_fetch_chunk_size = cs_subset_long(NeoMutt->sub, "imap_fetch_chunk_size");
551 if (c_imap_fetch_chunk_size > 0)
552 max_headers_per_fetch = c_imap_fetch_chunk_size;
553
554 if (!evalhc)
555 {
556 if (msn_end - msn_begin + 1 <= max_headers_per_fetch)
557 *fetch_msn_end = msn_end;
558 else
559 *fetch_msn_end = msn_begin + max_headers_per_fetch - 1;
560 mutt_buffer_printf(buf, "%u:%u", msn_begin, *fetch_msn_end);
561 return (*fetch_msn_end - msn_begin + 1);
562 }
563
564 for (msn = msn_begin; msn <= (msn_end + 1); msn++)
565 {
566 if (msn_count < max_headers_per_fetch && msn <= msn_end &&
567 !imap_msn_get(&mdata->msn, msn - 1))
568 {
569 msn_count++;
570
571 switch (state)
572 {
573 case 1: /* single: convert to a range */
574 state = 2;
575 /* fallthrough */
576 case 2: /* extend range ending */
577 range_end = msn;
578 break;
579 default:
580 state = 1;
581 range_begin = msn;
582 break;
583 }
584 }
585 else if (state)
586 {
587 if (first_chunk)
588 first_chunk = false;
589 else
590 mutt_buffer_addch(buf, ',');
591
592 if (state == 1)
593 mutt_buffer_add_printf(buf, "%u", range_begin);
594 else if (state == 2)
595 mutt_buffer_add_printf(buf, "%u:%u", range_begin, range_end);
596 state = 0;
597
598 if ((mutt_buffer_len(buf) > 500) || (msn_count >= max_headers_per_fetch))
599 break;
600 }
601 }
602
603 /* The loop index goes one past to terminate the range if needed. */
604 *fetch_msn_end = msn - 1;
605
606 return msn_count;
607}
size_t mutt_buffer_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition: buffer.c:371
size_t mutt_buffer_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:248
int mutt_buffer_add_printf(struct Buffer *buf, const char *fmt,...)
Format a string appending a Buffer.
Definition: buffer.c:211
int mutt_buffer_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:168
void mutt_buffer_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:85
long cs_subset_long(const struct ConfigSubset *sub, const char *name)
Get a long config item by name.
Definition: helpers.c:121
struct Email * imap_msn_get(const struct MSN *msn, size_t idx)
Return the Email associated with an msn.
Definition: msn.c:80
+ 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 624 of file message.c.

627{
628 /* If there are local_changes, we only want to note if the server
629 * flags have changed, so we can set a reopen flag in
630 * cmd_parse_fetch(). We don't want to count a local modification
631 * to the header flag as a "change". */
632 if ((old_hd_flag == new_hd_flag) && (local_changes != 0))
633 return;
634
635 if (new_hd_flag == h_flag)
636 return;
637
638 if (server_changes)
639 *server_changes = true;
640
641 /* Local changes have priority */
642 if (local_changes == 0)
643 mutt_set_flag(m, e, flag_name, new_hd_flag);
644}
#define mutt_set_flag(m, e, flag, bf)
Definition: protos.h:63
+ 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 664 of file message.c.

667{
668 struct Progress *progress = NULL;
669 char buf[1024] = { 0 };
670 int rc = -1;
671
672 struct Mailbox *m = adata->mailbox;
673 struct ImapMboxData *mdata = imap_mdata_get(m);
674 int idx = m->msg_count;
675
676 if (m->verbose)
677 {
678 /* L10N: Comparing the cached data with the IMAP server's data */
679 progress = progress_new(_("Evaluating cache..."), MUTT_PROGRESS_READ, msn_end);
680 }
681
682 /* If we are using CONDSTORE's "FETCH CHANGEDSINCE", then we keep
683 * the flags in the header cache, and update them further below.
684 * Otherwise, we fetch the current state of the flags here. */
685 snprintf(buf, sizeof(buf), "UID FETCH 1:%u (UID%s)", uid_next - 1,
686 eval_condstore ? "" : " FLAGS");
687
688 imap_cmd_start(adata, buf);
689
691 int mfhrc = 0;
692 struct ImapHeader h;
693 for (int msgno = 1; rc == IMAP_RES_CONTINUE; msgno++)
694 {
696 goto fail;
697
698 if (m->verbose)
699 progress_update(progress, msgno, -1);
700
701 memset(&h, 0, sizeof(h));
702 h.edata = imap_edata_new();
703 do
704 {
705 rc = imap_cmd_step(adata);
706 if (rc != IMAP_RES_CONTINUE)
707 break;
708
709 mfhrc = msg_fetch_header(m, &h, adata->buf, NULL);
710 if (mfhrc < 0)
711 continue;
712
713 if (!h.edata->uid)
714 {
715 mutt_debug(LL_DEBUG2, "skipping hcache FETCH response for message number %d missing a UID\n",
716 h.edata->msn);
717 continue;
718 }
719
720 if ((h.edata->msn < 1) || (h.edata->msn > msn_end))
721 {
722 mutt_debug(LL_DEBUG1, "skipping hcache FETCH response for unknown message number %d\n",
723 h.edata->msn);
724 continue;
725 }
726
727 if (imap_msn_get(&mdata->msn, h.edata->msn - 1))
728 {
729 mutt_debug(LL_DEBUG2, "skipping hcache FETCH for duplicate message %d\n",
730 h.edata->msn);
731 continue;
732 }
733
734 struct Email *e = imap_hcache_get(mdata, h.edata->uid);
735 m->emails[idx] = e;
736 if (e)
737 {
738 imap_msn_set(&mdata->msn, h.edata->msn - 1, e);
739 mutt_hash_int_insert(mdata->uid_hash, h.edata->uid, e);
740
741 e->index = h.edata->uid;
742 /* messages which have not been expunged are ACTIVE (borrowed from mh
743 * folders) */
744 e->active = true;
745 e->changed = false;
746 if (eval_condstore)
747 {
748 h.edata->read = e->read;
749 h.edata->old = e->old;
750 h.edata->deleted = e->deleted;
751 h.edata->flagged = e->flagged;
752 h.edata->replied = e->replied;
753 }
754 else
755 {
756 e->read = h.edata->read;
757 e->old = h.edata->old;
758 e->deleted = h.edata->deleted;
759 e->flagged = h.edata->flagged;
760 e->replied = h.edata->replied;
761 }
762
763 /* mailbox->emails[msgno]->received is restored from mutt_hcache_restore */
764 e->edata = h.edata;
766
767 /* We take a copy of the tags so we can split the string */
768 char *tags_copy = mutt_str_dup(h.edata->flags_remote);
769 driver_tags_replace(&e->tags, tags_copy);
770 FREE(&tags_copy);
771
772 m->msg_count++;
773 mailbox_size_add(m, e);
774
775 /* If this is the first time we are fetching, we need to
776 * store the current state of flags back into the header cache */
777 if (!eval_condstore && store_flag_updates)
778 imap_hcache_put(mdata, e);
779
780 h.edata = NULL;
781 idx++;
782 }
783 } while (mfhrc == -1);
784
785 imap_edata_free((void **) &h.edata);
786
787 if ((mfhrc < -1) || ((rc != IMAP_RES_CONTINUE) && (rc != IMAP_RES_OK)))
788 goto fail;
789 }
790
791 rc = 0;
792fail:
793 progress_free(&progress);
794 return rc;
795}
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:1062
struct ImapEmailData * imap_edata_new(void)
Create a new ImapEmailData.
Definition: edata.c:55
void imap_edata_free(void **ptr)
Free the private Email data - Implements Email::edata_free()
Definition: edata.c:39
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:403
static bool query_abort_header_download(struct ImapAccountData *adata)
Ask the user whether to abort the download.
Definition: message.c:485
#define IMAP_RES_OK
<tag> OK ...
Definition: private.h:56
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: logging.h:41
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:237
void imap_msn_set(struct MSN *msn, size_t idx, struct Email *e)
Cache an Email into a given position.
Definition: msn.c:92
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:250
@ MUTT_PROGRESS_READ
Progress tracks elements, according to $read_inc
Definition: lib.h:49
void progress_free(struct Progress **ptr)
Free a Progress Bar.
Definition: progress.c:86
bool progress_update(struct Progress *progress, size_t pos, int percent)
Update the state of the progress bar.
Definition: progress.c:73
struct Progress * progress_new(const char *msg, enum ProgressType type, size_t size)
Create a new Progress Bar.
Definition: progress.c:118
The envelope/body of an email.
Definition: email.h:37
bool read
Email is read.
Definition: email.h:48
bool active
Message is not to be removed.
Definition: email.h:74
bool old
Email is seen, but unread.
Definition: email.h:47
void(* edata_free)(void **ptr)
Free the private data attached to the Email.
Definition: email.h:87
bool changed
Email has been edited.
Definition: email.h:75
bool flagged
Marked important?
Definition: email.h:45
bool replied
Email has been replied to.
Definition: email.h:49
struct TagList tags
For drivers that support server tagging.
Definition: email.h:70
bool deleted
Email is deleted.
Definition: email.h:76
int index
The absolute (unsorted) message number.
Definition: email.h:110
IMAP-specific header.
Definition: message.h:33
unsigned int uid_next
Definition: mdata.h:51
struct HashTable * uid_hash
Definition: mdata.h:58
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:114
bool driver_tags_replace(struct TagList *head, const char *tags)
Replace all tags.
Definition: tags.c:186
+ 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 809 of file message.c.

810{
811 int rc;
812 unsigned int uid = 0;
813
814 mutt_debug(LL_DEBUG2, "Reading uid seqset from header cache\n");
815 struct Mailbox *m = adata->mailbox;
816 struct ImapMboxData *mdata = adata->mailbox->mdata;
817 unsigned int msn = 1;
818
819 if (m->verbose)
820 mutt_message(_("Evaluating cache..."));
821
822 struct SeqsetIterator *iter = mutt_seqset_iterator_new(uid_seqset);
823 if (!iter)
824 return -1;
825
826 while ((rc = mutt_seqset_iterator_next(iter, &uid)) == 0)
827 {
828 /* The seqset may contain more headers than the fetch request, so
829 * we need to watch and reallocate the context and msn_index */
830 imap_msn_reserve(&mdata->msn, msn);
831
832 struct Email *e = imap_hcache_get(mdata, uid);
833 if (e)
834 {
835 imap_msn_set(&mdata->msn, msn - 1, e);
836
837 if (m->msg_count >= m->email_max)
839
841 e->edata = edata;
843
844 e->index = uid;
845 e->active = true;
846 e->changed = false;
847 edata->read = e->read;
848 edata->old = e->old;
849 edata->deleted = e->deleted;
850 edata->flagged = e->flagged;
851 edata->replied = e->replied;
852
853 edata->msn = msn;
854 edata->uid = uid;
856
857 mailbox_size_add(m, e);
858 m->emails[m->msg_count++] = e;
859
860 msn++;
861 }
862 /* A non-zero uid missing from the header cache is either the
863 * result of an expunged message (not recorded in the uid seqset)
864 * or a hole in the header cache.
865 *
866 * We have to assume it's an earlier expunge and compact the msn's
867 * in that case, because cmd_parse_vanished() won't find it in the
868 * uid_hash and decrement later msn's there.
869 *
870 * Thus we only increment the uid if the uid was 0: an actual
871 * stored "blank" in the uid seqset.
872 */
873 else if (!uid)
874 {
875 msn++;
876 }
877 }
878
880
881 return rc;
882}
#define mutt_message(...)
Definition: logging.h:86
struct SeqsetIterator * mutt_seqset_iterator_new(const char *seqset)
Create a new Sequence Set Iterator.
Definition: util.c:1076
int mutt_seqset_iterator_next(struct SeqsetIterator *iter, unsigned int *next)
Get the next UID from a Sequence Set.
Definition: util.c:1097
void mutt_seqset_iterator_free(struct SeqsetIterator **ptr)
Free a Sequence Set Iterator.
Definition: util.c:1154
void imap_msn_reserve(struct MSN *msn, size_t num)
Create / reallocate the cache.
Definition: msn.c:41
void mx_alloc_memory(struct Mailbox *m)
Create storage for the emails.
Definition: mx.c:1219
int email_max
Number of pointers in emails.
Definition: mailbox.h:97
UID Sequence Set Iterator.
Definition: private.h:170
+ 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 896 of file message.c.

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

996{
997 assert(m);
999 struct ImapMboxData *mdata = imap_mdata_get(m);
1000 if (!adata || (adata->mailbox != m))
1001 return -1;
1002
1003 const size_t max_msn = imap_msn_highest(&mdata->msn);
1004
1005 unsigned int msn;
1006 unsigned int uid;
1007 struct Email *e = NULL;
1008 struct Email *uidh = NULL;
1009
1010 for (int i = 0; i < m->msg_count; i++)
1011 {
1012 e = m->emails[i];
1013 const struct ImapEmailData *edata = imap_edata_get(e);
1014 if (!edata)
1015 goto fail;
1016
1017 msn = imap_edata_get(e)->msn;
1018 uid = imap_edata_get(e)->uid;
1019
1020 if ((msn < 1) || (msn > max_msn) || imap_msn_get(&mdata->msn, msn - 1) != e)
1021 goto fail;
1022
1023 uidh = (struct Email *) mutt_hash_int_find(mdata->uid_hash, uid);
1024 if (uidh != e)
1025 goto fail;
1026 }
1027
1028 return 0;
1029
1030fail:
1031 imap_msn_free(&mdata->msn);
1032 mutt_hash_free(&mdata->uid_hash);
1036
1037 for (int i = 0; i < m->msg_count; i++)
1038 {
1039 if (m->emails[i] && m->emails[i]->edata)
1040 imap_edata_free(&m->emails[i]->edata);
1041 email_free(&m->emails[i]);
1042 }
1043 m->msg_count = 0;
1044 m->size = 0;
1045 mutt_hcache_delete_record(mdata->hcache, "/MODSEQ", 7);
1047 imap_hcache_close(mdata);
1048
1049 if (m->verbose)
1050 {
1051 /* L10N: After opening an IMAP mailbox using QRESYNC, Mutt performs a quick
1052 sanity check. If that fails, Mutt reopens the mailbox using a normal
1053 download. */
1054 mutt_error(_("QRESYNC failed. Reopening mailbox."));
1055 }
1056 return -1;
1057}
void email_free(struct Email **ptr)
Free an Email.
Definition: email.c:44
#define mutt_error(...)
Definition: logging.h:87
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 mutt_hcache_delete_record(struct HeaderCache *hc, const char *key, size_t keylen)
Multiplexor for StoreOps::delete_record.
Definition: hcache.c:641
int imap_hcache_clear_uid_seqset(struct ImapMboxData *mdata)
Delete a UID Sequence Set from the header cache.
Definition: util.c:437
void imap_msn_free(struct MSN *msn)
Free the cache.
Definition: msn.c:59
size_t imap_msn_highest(const struct MSN *msn)
Return the highest MSN in use.
Definition: msn.c:69
struct HeaderCache * hcache
Email header cache.
Definition: mdata.h:62
struct HashTable * subj_hash
Hash Table by subject.
Definition: mailbox.h:124
struct HashTable * id_hash
Hash Table by msg id.
Definition: mailbox.h:123
off_t size
Size of the Mailbox.
Definition: mailbox.h:84
struct HashTable * label_hash
Hash Table for x-labels.
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 1072 of file message.c.

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

1329{
1330 int oldmsgcount;
1331 unsigned int maxuid = 0;
1332 int retval = -1;
1333 bool evalhc = false;
1334
1335#ifdef USE_HCACHE
1336 void *uidvalidity = NULL;
1337 void *puid_next = NULL;
1338 unsigned int uid_next = 0;
1339 bool has_condstore = false;
1340 bool has_qresync = false;
1341 bool eval_condstore = false;
1342 bool eval_qresync = false;
1343 unsigned long long hc_modseq = 0;
1344 char *uid_seqset = NULL;
1345 const unsigned int msn_begin_save = msn_begin;
1346#endif /* USE_HCACHE */
1347
1349 struct ImapMboxData *mdata = imap_mdata_get(m);
1350 if (!adata || (adata->mailbox != m))
1351 return -1;
1352
1353#ifdef USE_HCACHE
1354retry:
1355#endif /* USE_HCACHE */
1356
1357 /* make sure context has room to hold the mailbox */
1358 while (msn_end > m->email_max)
1359 mx_alloc_memory(m);
1360 imap_msn_reserve(&mdata->msn, msn_end);
1361 imap_alloc_uid_hash(adata, msn_end);
1362
1363 oldmsgcount = m->msg_count;
1365 mdata->new_mail_count = 0;
1366
1367#ifdef USE_HCACHE
1368 imap_hcache_open(adata, mdata);
1369
1370 if (mdata->hcache && initial_download)
1371 {
1372 size_t dlen = 0;
1373 uidvalidity = mutt_hcache_fetch_raw(mdata->hcache, "/UIDVALIDITY", 12, &dlen);
1374 if (uidvalidity && (dlen < sizeof(uint32_t)))
1375 {
1377 uidvalidity = NULL;
1378 }
1379 puid_next = mutt_hcache_fetch_raw(mdata->hcache, "/UIDNEXT", 8, &dlen);
1380 if (puid_next)
1381 {
1382 if (dlen >= sizeof(unsigned int))
1383 {
1384 uid_next = *(unsigned int *) puid_next;
1385 }
1386 mutt_hcache_free_raw(mdata->hcache, &puid_next);
1387 }
1388
1389 if (mdata->modseq)
1390 {
1391 const bool c_imap_condstore = cs_subset_bool(NeoMutt->sub, "imap_condstore");
1392 if ((adata->capabilities & IMAP_CAP_CONDSTORE) && c_imap_condstore)
1393 has_condstore = true;
1394
1395 /* If IMAP_CAP_QRESYNC and ImapQResync then NeoMutt sends ENABLE QRESYNC.
1396 * If we receive an ENABLED response back, then adata->qresync is set. */
1397 if (adata->qresync)
1398 has_qresync = true;
1399 }
1400
1401 if (uidvalidity && uid_next && (*(uint32_t *) uidvalidity == mdata->uidvalidity))
1402 {
1403 size_t dlen2 = 0;
1404 evalhc = true;
1405 const unsigned long long *pmodseq = mutt_hcache_fetch_raw(mdata->hcache,
1406 "/MODSEQ", 7, &dlen2);
1407 if (pmodseq)
1408 {
1409 if (dlen2 >= sizeof(unsigned long long))
1410 {
1411 hc_modseq = *pmodseq;
1412 }
1413 mutt_hcache_free_raw(mdata->hcache, (void **) &pmodseq);
1414 }
1415 if (hc_modseq)
1416 {
1417 if (has_qresync)
1418 {
1419 uid_seqset = imap_hcache_get_uid_seqset(mdata);
1420 if (uid_seqset)
1421 eval_qresync = true;
1422 }
1423
1424 if (!eval_qresync && has_condstore)
1425 eval_condstore = true;
1426 }
1427 }
1429 }
1430 if (evalhc)
1431 {
1432 if (eval_qresync)
1433 {
1434 if (read_headers_qresync_eval_cache(adata, uid_seqset) < 0)
1435 goto bail;
1436 }
1437 else
1438 {
1439 if (read_headers_normal_eval_cache(adata, msn_end, uid_next, has_condstore || has_qresync,
1440 eval_condstore) < 0)
1441 goto bail;
1442 }
1443
1444 if ((eval_condstore || eval_qresync) && (hc_modseq != mdata->modseq))
1445 {
1447 hc_modseq, eval_qresync) < 0)
1448 {
1449 goto bail;
1450 }
1451 }
1452
1453 /* Look for the first empty MSN and start there */
1454 while (msn_begin <= msn_end)
1455 {
1456 if (!imap_msn_get(&mdata->msn, msn_begin - 1))
1457 break;
1458 msn_begin++;
1459 }
1460 }
1461#endif /* USE_HCACHE */
1462
1463 if (read_headers_fetch_new(m, msn_begin, msn_end, evalhc, &maxuid, initial_download) < 0)
1464 goto bail;
1465
1466#ifdef USE_HCACHE
1467 if (eval_qresync && initial_download)
1468 {
1469 if (imap_verify_qresync(m) != 0)
1470 {
1471 eval_qresync = false;
1472 eval_condstore = false;
1473 evalhc = false;
1474 hc_modseq = 0;
1475 maxuid = 0;
1476 FREE(&uid_seqset);
1477 uidvalidity = NULL;
1478 uid_next = 0;
1479 msn_begin = msn_begin_save;
1480
1481 goto retry;
1482 }
1483 }
1484#endif /* USE_HCACHE */
1485
1486 if (maxuid && (mdata->uid_next < maxuid + 1))
1487 mdata->uid_next = maxuid + 1;
1488
1489#ifdef USE_HCACHE
1490 mutt_hcache_store_raw(mdata->hcache, "/UIDVALIDITY", 12, &mdata->uidvalidity,
1491 sizeof(mdata->uidvalidity));
1492 if (maxuid && (mdata->uid_next < maxuid + 1))
1493 {
1494 mutt_debug(LL_DEBUG2, "Overriding UIDNEXT: %u -> %u\n", mdata->uid_next, maxuid + 1);
1495 mdata->uid_next = maxuid + 1;
1496 }
1497 if (mdata->uid_next > 1)
1498 {
1499 mutt_hcache_store_raw(mdata->hcache, "/UIDNEXT", 8, &mdata->uid_next,
1500 sizeof(mdata->uid_next));
1501 }
1502
1503 /* We currently only sync CONDSTORE and QRESYNC on the initial download.
1504 * To do it more often, we'll need to deal with flag updates combined with
1505 * unsync'ed local flag changes. We'll also need to properly sync flags to
1506 * the header cache on close. I'm not sure it's worth the added complexity. */
1507 if (initial_download)
1508 {
1509 if (has_condstore || has_qresync)
1510 {
1511 mutt_hcache_store_raw(mdata->hcache, "/MODSEQ", 7, &mdata->modseq,
1512 sizeof(mdata->modseq));
1513 }
1514 else
1515 mutt_hcache_delete_record(mdata->hcache, "/MODSEQ", 7);
1516
1517 if (has_qresync)
1519 else
1521 }
1522#endif /* USE_HCACHE */
1523
1524 if (m->msg_count > oldmsgcount)
1525 {
1526 /* TODO: it's not clear to me why we are calling mx_alloc_memory
1527 * yet again. */
1528 mx_alloc_memory(m);
1529 }
1530
1531 mdata->reopen |= IMAP_REOPEN_ALLOW;
1532
1533 retval = msn_end;
1534
1535bail:
1536#ifdef USE_HCACHE
1538 FREE(&uid_seqset);
1539#endif /* USE_HCACHE */
1540
1541 return retval;
1542}
void * mutt_hcache_fetch_raw(struct HeaderCache *hc, const char *key, size_t keylen, size_t *dlen)
Fetch a message's header from the cache.
Definition: hcache.c:529
int mutt_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:620
void mutt_hcache_free_raw(struct HeaderCache *hc, void **data)
Multiplexor for StoreOps::free.
Definition: hcache.c:548
static int imap_verify_qresync(struct Mailbox *m)
Check to see if QRESYNC got jumbled.
Definition: message.c:995
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:896
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:1072
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:664
static int read_headers_qresync_eval_cache(struct ImapAccountData *adata, char *uid_seqset)
Retrieve data from the header cache.
Definition: message.c:809
static void imap_alloc_uid_hash(struct ImapAccountData *adata, unsigned int msn_count)
Create a Hash Table for the UIDs.
Definition: message.c:509
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:66
char * imap_hcache_get_uid_seqset(struct ImapMboxData *mdata)
Get a UID Sequence Set from the header cache.
Definition: util.c:451
#define IMAP_CAP_CONDSTORE
RFC7162.
Definition: private.h:138
bool qresync
true, if QRESYNC is successfully ENABLE'd
Definition: adata.h:63
uint32_t uidvalidity
Definition: mdata.h:50
+ 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 1551 of file message.c.

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

Server COPY messages to another folder.

Parameters
mMailbox
elList 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 1683 of file message.c.

1685{
1686 if (!m || !el || !dest)
1687 return -1;
1688
1689 struct Buffer cmd, sync_cmd;
1690 char buf[PATH_MAX] = { 0 };
1691 char mbox[PATH_MAX] = { 0 };
1692 char mmbox[PATH_MAX] = { 0 };
1693 char prompt[PATH_MAX + 64];
1694 int rc;
1695 struct ConnAccount cac = { { 0 } };
1696 enum QuadOption err_continue = MUTT_NO;
1697 int triedcreate = 0;
1698 struct EmailNode *en = STAILQ_FIRST(el);
1699 bool single = !STAILQ_NEXT(en, entries);
1701
1702 if (single && en->email->attach_del)
1703 {
1704 mutt_debug(LL_DEBUG3, "#1 Message contains attachments to be deleted\n");
1705 return 1;
1706 }
1707
1708 if (imap_parse_path(dest, &cac, buf, sizeof(buf)))
1709 {
1710 mutt_debug(LL_DEBUG1, "bad destination %s\n", dest);
1711 return -1;
1712 }
1713
1714 /* check that the save-to folder is in the same account */
1715 if (!imap_account_match(&adata->conn->account, &cac))
1716 {
1717 mutt_debug(LL_DEBUG3, "%s not same server as %s\n", dest, mailbox_path(m));
1718 return 1;
1719 }
1720
1721 imap_fix_path(adata->delim, buf, mbox, sizeof(mbox));
1722 if (*mbox == '\0')
1723 mutt_str_copy(mbox, "INBOX", sizeof(mbox));
1724 imap_munge_mbox_name(adata->unicode, mmbox, sizeof(mmbox), mbox);
1725
1726 /* loop in case of TRYCREATE */
1727 do
1728 {
1729 mutt_buffer_init(&sync_cmd);
1730 mutt_buffer_init(&cmd);
1731
1732 if (!single) /* copy tagged messages */
1733 {
1734 /* if any messages have attachments to delete, fall through to FETCH
1735 * and APPEND. TODO: Copy what we can with COPY, fall through for the
1736 * remainder. */
1737 STAILQ_FOREACH(en, el, entries)
1738 {
1739 if (en->email->attach_del)
1740 {
1741 mutt_debug(LL_DEBUG3, "#2 Message contains attachments to be deleted\n");
1742 return 1;
1743 }
1744
1745 if (en->email->active && en->email->changed)
1746 {
1747 rc = imap_sync_message_for_copy(m, en->email, &sync_cmd, &err_continue);
1748 if (rc < 0)
1749 {
1750 mutt_debug(LL_DEBUG1, "#1 could not sync\n");
1751 goto out;
1752 }
1753 }
1754 }
1755
1756 rc = imap_exec_msgset(m, "UID COPY", mmbox, MUTT_TAG, false, false);
1757 if (rc == 0)
1758 {
1759 mutt_debug(LL_DEBUG1, "No messages tagged\n");
1760 rc = -1;
1761 goto out;
1762 }
1763 else if (rc < 0)
1764 {
1765 mutt_debug(LL_DEBUG1, "#1 could not queue copy\n");
1766 goto out;
1767 }
1768 else
1769 {
1770 mutt_message(ngettext("Copying %d message to %s...", "Copying %d messages to %s...", rc),
1771 rc, mbox);
1772 }
1773 }
1774 else
1775 {
1776 mutt_message(_("Copying message %d to %s..."), en->email->index + 1, mbox);
1777 mutt_buffer_add_printf(&cmd, "UID COPY %u %s", imap_edata_get(en->email)->uid, mmbox);
1778
1779 if (en->email->active && en->email->changed)
1780 {
1781 rc = imap_sync_message_for_copy(m, en->email, &sync_cmd, &err_continue);
1782 if (rc < 0)
1783 {
1784 mutt_debug(LL_DEBUG1, "#2 could not sync\n");
1785 goto out;
1786 }
1787 }
1788 rc = imap_exec(adata, cmd.data, IMAP_CMD_QUEUE);
1789 if (rc != IMAP_EXEC_SUCCESS)
1790 {
1791 mutt_debug(LL_DEBUG1, "#2 could not queue copy\n");
1792 goto out;
1793 }
1794 }
1795
1796 /* let's get it on */
1797 rc = imap_exec(adata, NULL, IMAP_CMD_NO_FLAGS);
1798 if (rc == IMAP_EXEC_ERROR)
1799 {
1800 if (triedcreate)
1801 {
1802 mutt_debug(LL_DEBUG1, "Already tried to create mailbox %s\n", mbox);
1803 break;
1804 }
1805 /* bail out if command failed for reasons other than nonexistent target */
1806 if (!mutt_istr_startswith(imap_get_qualifier(adata->buf), "[TRYCREATE]"))
1807 break;
1808 mutt_debug(LL_DEBUG3, "server suggests TRYCREATE\n");
1809 snprintf(prompt, sizeof(prompt), _("Create %s?"), mbox);
1810 const bool c_confirm_create = cs_subset_bool(NeoMutt->sub, "confirm_create");
1811 if (c_confirm_create && (mutt_yesorno(prompt, MUTT_YES) != MUTT_YES))
1812 {
1814 goto out;
1815 }
1816 if (imap_create_mailbox(adata, mbox) < 0)
1817 break;
1818 triedcreate = 1;
1819 }
1820 } while (rc == IMAP_EXEC_ERROR);
1821
1822 if (rc != 0)
1823 {
1824 imap_error("imap_copy_messages", adata->buf);
1825 goto out;
1826 }
1827
1828 /* cleanup */
1829 if (save_opt == SAVE_MOVE)
1830 {
1831 const bool c_delete_untag = cs_subset_bool(NeoMutt->sub, "delete_untag");
1832 STAILQ_FOREACH(en, el, entries)
1833 {
1834 mutt_set_flag(m, en->email, MUTT_DELETE, true);
1835 mutt_set_flag(m, en->email, MUTT_PURGE, true);
1836 if (c_delete_untag)
1837 mutt_set_flag(m, en->email, MUTT_TAG, false);
1838 }
1839 }
1840
1841 rc = 0;
1842
1843out:
1844 FREE(&cmd.data);
1845 FREE(&sync_cmd.data);
1846
1847 return (rc < 0) ? -1 : rc;
1848}
struct Buffer * mutt_buffer_init(struct Buffer *buf)
Initialise a new Buffer.
Definition: buffer.c:52
@ SAVE_MOVE
Move message to another mailbox, removing the original.
Definition: commands.h:51
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:1247
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
#define IMAP_CMD_NO_FLAGS
No flags are set.
Definition: private.h:73
@ IMAP_EXEC_SUCCESS
Imap command executed or queued successfully.
Definition: private.h:84
@ IMAP_EXEC_ERROR
Imap command failure.
Definition: private.h:85
char * imap_fix_path(char delim, const char *mailbox, char *path, size_t plen)
Fix up the imap path.
Definition: util.c:685
bool imap_account_match(const struct ConnAccount *a1, const struct ConnAccount *a2)
Compare two Accounts.
Definition: util.c:1044
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:911
#define IMAP_CMD_QUEUE
Queue a command, do not execute.
Definition: private.h:75
char * imap_get_qualifier(char *buf)
Get the qualifier from a tagged response.
Definition: util.c:772
int imap_exec_msgset(struct Mailbox *m, const char *pre, const char *post, enum MessageType flag, bool changed, bool invert)
Prepare commands for all messages matching conditions.
Definition: imap.c:930
int imap_create_mailbox(struct ImapAccountData *adata, const char *mailbox)
Create a new mailbox.
Definition: imap.c:447
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:1007
@ LL_DEBUG3
Log at debug level 3.
Definition: logging.h:42
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox's path string.
Definition: mailbox.h:210
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:652
@ MUTT_PURGE
Messages to be purged (bypass trash)
Definition: mutt.h:97
@ MUTT_TAG
Tagged messages.
Definition: mutt.h:100
@ MUTT_DELETE
Messages to be deleted.
Definition: mutt.h:95
#define PATH_MAX
Definition: mutt.h:40
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
#define STAILQ_FIRST(head)
Definition: queue.h:350
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:352
#define STAILQ_NEXT(elm, field)
Definition: queue.h:400
Login details for a remote server.
Definition: connaccount.h:53
List of Emails.
Definition: email.h:132
struct Email * email
Email in the list.
Definition: email.h:133
bool attach_del
Has an attachment marked for deletion.
Definition: email.h:99
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_cache_del()

int imap_cache_del ( struct Mailbox m,
struct Email e 
)

Delete an email from the body cache.

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

Definition at line 1857 of file message.c.

1858{
1860 struct ImapMboxData *mdata = imap_mdata_get(m);
1861
1862 if (!e || !adata || (adata->mailbox != m))
1863 return -1;
1864
1865 mdata->bcache = msg_cache_open(m);
1866 char id[64] = { 0 };
1867 snprintf(id, sizeof(id), "%u-%u", mdata->uidvalidity, imap_edata_get(e)->uid);
1868 return mutt_bcache_del(mdata->bcache, id);
1869}
int mutt_bcache_del(struct BodyCache *bcache, const char *id)
Delete a file from the Body Cache.
Definition: bcache.c:265
+ 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 1876 of file message.c.

1877{
1879 struct ImapMboxData *mdata = imap_mdata_get(m);
1880
1881 if (!adata || (adata->mailbox != m))
1882 return -1;
1883
1884 mdata->bcache = msg_cache_open(m);
1886
1887 return 0;
1888}
int mutt_bcache_list(struct BodyCache *bcache, bcache_list_t want_id, void *data)
Find matching entries in the Body Cache.
Definition: bcache.c:330
static int msg_cache_clean_cb(const char *id, struct BodyCache *bcache, void *data)
Delete an entry from the message cache - Implements bcache_list_t -.
Definition: message.c:167
+ 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 1908 of file message.c.

1909{
1911 if (!adata || (adata->mailbox != m))
1912 return NULL;
1913
1914 struct ImapHeader newh = { 0 };
1915 struct ImapEmailData old_edata = { 0 };
1916 int local_changes = e->changed;
1917
1918 struct ImapEmailData *edata = e->edata;
1919 newh.edata = edata;
1920
1921 mutt_debug(LL_DEBUG2, "parsing FLAGS\n");
1922 s = msg_parse_flags(&newh, s);
1923 if (!s)
1924 return NULL;
1925
1926 /* Update tags system */
1927 /* We take a copy of the tags so we can split the string */
1928 char *tags_copy = mutt_str_dup(edata->flags_remote);
1929 driver_tags_replace(&e->tags, tags_copy);
1930 FREE(&tags_copy);
1931
1932 /* YAUH (yet another ugly hack): temporarily set context to
1933 * read-write even if it's read-only, so *server* updates of
1934 * flags can be processed by mutt_set_flag. mailbox->changed must
1935 * be restored afterwards */
1936 bool readonly = m->readonly;
1937 m->readonly = false;
1938
1939 /* This is redundant with the following two checks. Removing:
1940 * mutt_set_flag (m, e, MUTT_NEW, !(edata->read || edata->old)); */
1941 set_changed_flag(m, e, local_changes, server_changes, MUTT_OLD, old_edata.old,
1942 edata->old, e->old);
1943 set_changed_flag(m, e, local_changes, server_changes, MUTT_READ,
1944 old_edata.read, edata->read, e->read);
1945 set_changed_flag(m, e, local_changes, server_changes, MUTT_DELETE,
1946 old_edata.deleted, edata->deleted, e->deleted);
1947 set_changed_flag(m, e, local_changes, server_changes, MUTT_FLAG,
1948 old_edata.flagged, edata->flagged, e->flagged);
1949 set_changed_flag(m, e, local_changes, server_changes, MUTT_REPLIED,
1950 old_edata.replied, edata->replied, e->replied);
1951
1952 /* this message is now definitively *not* changed (mutt_set_flag
1953 * marks things changed as a side-effect) */
1954 if (local_changes == 0)
1955 e->changed = false;
1956 m->changed &= !readonly;
1957 m->readonly = readonly;
1958
1959 return s;
1960}
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:624
@ MUTT_READ
Messages that have been read.
Definition: mutt.h:93
@ MUTT_OLD
Old messages.
Definition: mutt.h:91
@ MUTT_FLAG
Flagged messages.
Definition: mutt.h:99
@ MUTT_REPLIED
Messages that have been replied to.
Definition: mutt.h:92
bool deleted
Email has been deleted.
Definition: edata.h:38
bool old
Email has been seen.
Definition: edata.h:37
bool read
Email has been read.
Definition: edata.h:36
bool flagged
Email has been flagged.
Definition: edata.h:39
bool replied
Email has been replied to.
Definition: edata.h:40
bool readonly
Don't allow changes to the mailbox.
Definition: mailbox.h:115
+ Here is the call graph for this function:
+ Here is the caller graph for this function: