NeoMutt  2023-03-22-27-g3cb248
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 "edata.h"
#include "external.h"
#include "globals.h"
#include "mdata.h"
#include "msn.h"
#include "mutt_logging.h"
#include "mx.h"
#include "protos.h"
#include <libintl.h>
#include "hcache/lib.h"
+ Include dependency graph for message.c:

Go to the source code of this file.

Functions

static struct 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 77 of file message.c.

78{
81
82 if (!adata || (adata->mailbox != m))
83 return NULL;
84
85 if (mdata->bcache)
86 return mdata->bcache;
87
88 struct Buffer *mailbox = mutt_buffer_pool_get();
89 imap_cachepath(adata->delim, mdata->name, mailbox);
90
91 struct BodyCache *bc = mutt_bcache_open(&adata->conn->account,
92 mutt_buffer_string(mailbox));
94
95 return bc;
96}
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:78
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:710
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 105 of file message.c.

106{
108 struct ImapMboxData *mdata = imap_mdata_get(m);
109
110 if (!e || !adata || (adata->mailbox != m))
111 return NULL;
112
113 mdata->bcache = msg_cache_open(m);
114 char id[64] = { 0 };
115 snprintf(id, sizeof(id), "%u-%u", mdata->uidvalidity, imap_edata_get(e)->uid);
116 return mutt_bcache_get(mdata->bcache, id);
117}
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:77
+ 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 126 of file message.c.

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

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

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

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

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

473{
474 buf[*len] = '\0';
475 int rc = mutt_socket_write_n(conn, buf, *len);
476 *len = 0;
477 return rc;
478}
#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 488 of file message.c.

489{
490 bool abort = false;
491
493 /* L10N: This prompt is made if the user hits Ctrl-C when opening an IMAP mailbox */
494 if (mutt_yesorno(_("Abort download and close mailbox?"), MUTT_YES) == MUTT_YES)
495 {
496 abort = true;
498 }
499 SigInt = false;
500
501 return abort;
502}
void mutt_flushinp(void)
Empty all the keyboard buffers.
Definition: curs_lib.c:591
SIG_ATOMIC_VOLATILE_T SigInt
true after SIGINT is received
Definition: globals.c:58
void imap_close_connection(struct ImapAccountData *adata)
Close an IMAP connection.
Definition: imap.c:863
#define _(a)
Definition: message.h:28
@ 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 512 of file message.c.

513{
514 struct ImapMboxData *mdata = adata->mailbox->mdata;
515 if (!mdata->uid_hash)
516 mdata->uid_hash = mutt_hash_int_new(MAX(6 * msn_count / 5, 30), MUTT_HASH_NO_FLAGS);
517}
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:108
#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 536 of file message.c.

539{
540 struct ImapMboxData *mdata = adata->mailbox->mdata;
541 unsigned int max_headers_per_fetch = UINT_MAX;
542 bool first_chunk = true;
543 int state = 0; /* 1: single msn, 2: range of msn */
544 unsigned int msn;
545 unsigned int range_begin = 0;
546 unsigned int range_end = 0;
547 unsigned int msn_count = 0;
548
550 if (msn_end < msn_begin)
551 return 0;
552
553 const long c_imap_fetch_chunk_size = cs_subset_long(NeoMutt->sub, "imap_fetch_chunk_size");
554 if (c_imap_fetch_chunk_size > 0)
555 max_headers_per_fetch = c_imap_fetch_chunk_size;
556
557 if (!evalhc)
558 {
559 if (msn_end - msn_begin + 1 <= max_headers_per_fetch)
560 *fetch_msn_end = msn_end;
561 else
562 *fetch_msn_end = msn_begin + max_headers_per_fetch - 1;
563 mutt_buffer_printf(buf, "%u:%u", msn_begin, *fetch_msn_end);
564 return (*fetch_msn_end - msn_begin + 1);
565 }
566
567 for (msn = msn_begin; msn <= (msn_end + 1); msn++)
568 {
569 if (msn_count < max_headers_per_fetch && msn <= msn_end &&
570 !imap_msn_get(&mdata->msn, msn - 1))
571 {
572 msn_count++;
573
574 switch (state)
575 {
576 case 1: /* single: convert to a range */
577 state = 2;
578 /* fallthrough */
579 case 2: /* extend range ending */
580 range_end = msn;
581 break;
582 default:
583 state = 1;
584 range_begin = msn;
585 break;
586 }
587 }
588 else if (state)
589 {
590 if (first_chunk)
591 first_chunk = false;
592 else
593 mutt_buffer_addch(buf, ',');
594
595 if (state == 1)
596 mutt_buffer_add_printf(buf, "%u", range_begin);
597 else if (state == 2)
598 mutt_buffer_add_printf(buf, "%u:%u", range_begin, range_end);
599 state = 0;
600
601 if ((mutt_buffer_len(buf) > 500) || (msn_count >= max_headers_per_fetch))
602 break;
603 }
604 }
605
606 /* The loop index goes one past to terminate the range if needed. */
607 *fetch_msn_end = msn - 1;
608
609 return msn_count;
610}
size_t mutt_buffer_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition: buffer.c:409
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 627 of file message.c.

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

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

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

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

999{
1000 assert(m);
1002 struct ImapMboxData *mdata = imap_mdata_get(m);
1003 if (!adata || (adata->mailbox != m))
1004 return -1;
1005
1006 const size_t max_msn = imap_msn_highest(&mdata->msn);
1007
1008 unsigned int msn;
1009 unsigned int uid;
1010 struct Email *e = NULL;
1011 struct Email *uidh = NULL;
1012
1013 for (int i = 0; i < m->msg_count; i++)
1014 {
1015 e = m->emails[i];
1016 const struct ImapEmailData *edata = imap_edata_get(e);
1017 if (!edata)
1018 goto fail;
1019
1020 msn = imap_edata_get(e)->msn;
1021 uid = imap_edata_get(e)->uid;
1022
1023 if ((msn < 1) || (msn > max_msn) || imap_msn_get(&mdata->msn, msn - 1) != e)
1024 goto fail;
1025
1026 uidh = (struct Email *) mutt_hash_int_find(mdata->uid_hash, uid);
1027 if (uidh != e)
1028 goto fail;
1029 }
1030
1031 return 0;
1032
1033fail:
1034 imap_msn_free(&mdata->msn);
1035 mutt_hash_free(&mdata->uid_hash);
1039
1040 for (int i = 0; i < m->msg_count; i++)
1041 {
1042 if (m->emails[i] && m->emails[i]->edata)
1043 imap_edata_free(&m->emails[i]->edata);
1044 email_free(&m->emails[i]);
1045 }
1046 m->msg_count = 0;
1047 m->size = 0;
1048 mutt_hcache_delete_record(mdata->hcache, "/MODSEQ", 7);
1050 imap_hcache_close(mdata);
1051
1052 if (m->verbose)
1053 {
1054 /* L10N: After opening an IMAP mailbox using QRESYNC, Mutt performs a quick
1055 sanity check. If that fails, Mutt reopens the mailbox using a normal
1056 download. */
1057 mutt_error(_("QRESYNC failed. Reopening mailbox."));
1058 }
1059 return -1;
1060}
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:689
int imap_hcache_clear_uid_seqset(struct ImapMboxData *mdata)
Delete a UID Sequence Set from the header cache.
Definition: util.c:436
void imap_msn_free(struct 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 1075 of file message.c.

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

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

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

1662{
1663 if (!m || !el || !dest)
1664 return -1;
1665
1666 struct Buffer cmd, sync_cmd;
1667 char buf[PATH_MAX] = { 0 };
1668 char mbox[PATH_MAX] = { 0 };
1669 char mmbox[PATH_MAX] = { 0 };
1670 char prompt[PATH_MAX + 64];
1671 int rc;
1672 struct ConnAccount cac = { { 0 } };
1673 enum QuadOption err_continue = MUTT_NO;
1674 int triedcreate = 0;
1675 struct EmailNode *en = STAILQ_FIRST(el);
1676 bool single = !STAILQ_NEXT(en, entries);
1678
1679 if (single && en->email->attach_del)
1680 {
1681 mutt_debug(LL_DEBUG3, "#1 Message contains attachments to be deleted\n");
1682 return 1;
1683 }
1684
1685 if (imap_parse_path(dest, &cac, buf, sizeof(buf)))
1686 {
1687 mutt_debug(LL_DEBUG1, "bad destination %s\n", dest);
1688 return -1;
1689 }
1690
1691 /* check that the save-to folder is in the same account */
1692 if (!imap_account_match(&adata->conn->account, &cac))
1693 {
1694 mutt_debug(LL_DEBUG3, "%s not same server as %s\n", dest, mailbox_path(m));
1695 return 1;
1696 }
1697
1698 imap_fix_path(adata->delim, buf, mbox, sizeof(mbox));
1699 if (*mbox == '\0')
1700 mutt_str_copy(mbox, "INBOX", sizeof(mbox));
1701 imap_munge_mbox_name(adata->unicode, mmbox, sizeof(mmbox), mbox);
1702
1703 /* loop in case of TRYCREATE */
1704 do
1705 {
1706 mutt_buffer_init(&sync_cmd);
1707 mutt_buffer_init(&cmd);
1708
1709 if (!single) /* copy tagged messages */
1710 {
1711 /* if any messages have attachments to delete, fall through to FETCH
1712 * and APPEND. TODO: Copy what we can with COPY, fall through for the
1713 * remainder. */
1714 STAILQ_FOREACH(en, el, entries)
1715 {
1716 if (en->email->attach_del)
1717 {
1718 mutt_debug(LL_DEBUG3, "#2 Message contains attachments to be deleted\n");
1719 return 1;
1720 }
1721
1722 if (en->email->active && en->email->changed)
1723 {
1724 rc = imap_sync_message_for_copy(m, en->email, &sync_cmd, &err_continue);
1725 if (rc < 0)
1726 {
1727 mutt_debug(LL_DEBUG1, "#1 could not sync\n");
1728 goto out;
1729 }
1730 }
1731 }
1732
1733 rc = imap_exec_msgset(m, "UID COPY", mmbox, MUTT_TAG, false, false);
1734 if (rc == 0)
1735 {
1736 mutt_debug(LL_DEBUG1, "No messages tagged\n");
1737 rc = -1;
1738 goto out;
1739 }
1740 else if (rc < 0)
1741 {
1742 mutt_debug(LL_DEBUG1, "#1 could not queue copy\n");
1743 goto out;
1744 }
1745 else
1746 {
1747 mutt_message(ngettext("Copying %d message to %s...", "Copying %d messages to %s...", rc),
1748 rc, mbox);
1749 }
1750 }
1751 else
1752 {
1753 mutt_message(_("Copying message %d to %s..."), en->email->index + 1, mbox);
1754 mutt_buffer_add_printf(&cmd, "UID COPY %u %s", imap_edata_get(en->email)->uid, mmbox);
1755
1756 if (en->email->active && en->email->changed)
1757 {
1758 rc = imap_sync_message_for_copy(m, en->email, &sync_cmd, &err_continue);
1759 if (rc < 0)
1760 {
1761 mutt_debug(LL_DEBUG1, "#2 could not sync\n");
1762 goto out;
1763 }
1764 }
1765 rc = imap_exec(adata, cmd.data, IMAP_CMD_QUEUE);
1766 if (rc != IMAP_EXEC_SUCCESS)
1767 {
1768 mutt_debug(LL_DEBUG1, "#2 could not queue copy\n");
1769 goto out;
1770 }
1771 }
1772
1773 /* let's get it on */
1774 rc = imap_exec(adata, NULL, IMAP_CMD_NO_FLAGS);
1775 if (rc == IMAP_EXEC_ERROR)
1776 {
1777 if (triedcreate)
1778 {
1779 mutt_debug(LL_DEBUG1, "Already tried to create mailbox %s\n", mbox);
1780 break;
1781 }
1782 /* bail out if command failed for reasons other than nonexistent target */
1783 if (!mutt_istr_startswith(imap_get_qualifier(adata->buf), "[TRYCREATE]"))
1784 break;
1785 mutt_debug(LL_DEBUG3, "server suggests TRYCREATE\n");
1786 snprintf(prompt, sizeof(prompt), _("Create %s?"), mbox);
1787 const bool c_confirm_create = cs_subset_bool(NeoMutt->sub, "confirm_create");
1788 if (c_confirm_create && (mutt_yesorno(prompt, MUTT_YES) != MUTT_YES))
1789 {
1791 goto out;
1792 }
1793 if (imap_create_mailbox(adata, mbox) < 0)
1794 break;
1795 triedcreate = 1;
1796 }
1797 } while (rc == IMAP_EXEC_ERROR);
1798
1799 if (rc != 0)
1800 {
1801 imap_error("imap_copy_messages", adata->buf);
1802 goto out;
1803 }
1804
1805 /* cleanup */
1806 if (save_opt == SAVE_MOVE)
1807 {
1808 const bool c_delete_untag = cs_subset_bool(NeoMutt->sub, "delete_untag");
1809 STAILQ_FOREACH(en, el, entries)
1810 {
1811 mutt_set_flag(m, en->email, MUTT_DELETE, true);
1812 mutt_set_flag(m, en->email, MUTT_PURGE, true);
1813 if (c_delete_untag)
1814 mutt_set_flag(m, en->email, MUTT_TAG, false);
1815 }
1816 }
1817
1818 rc = 0;
1819
1820out:
1821 FREE(&cmd.data);
1822 FREE(&sync_cmd.data);
1823
1824 return (rc < 0) ? -1 : rc;
1825}
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: external.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:1260
int imap_parse_path(const char *path, struct ConnAccount *cac, char *mailbox, size_t mailboxlen)
Parse an IMAP mailbox name into ConnAccount, name.
Definition: util.c:474
#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:679
bool imap_account_match(const struct ConnAccount *a1, const struct ConnAccount *a2)
Compare two Accounts.
Definition: util.c:1040
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:907
#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:768
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:940
int imap_create_mailbox(struct ImapAccountData *adata, const char *mailbox)
Create a new mailbox.
Definition: imap.c:453
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:1017
@ 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:209
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:85
@ MUTT_TAG
Tagged messages.
Definition: mutt.h:88
@ MUTT_DELETE
Messages to be deleted.
Definition: mutt.h:83
#define PATH_MAX
Definition: mutt.h:41
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
Definition: mutt_logging.c:73
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:131
struct Email * email
Email in the list.
Definition: email.h:132
bool attach_del
Has an attachment marked for deletion.
Definition: email.h:98
+ 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 1834 of file message.c.

1835{
1837 struct ImapMboxData *mdata = imap_mdata_get(m);
1838
1839 if (!e || !adata || (adata->mailbox != m))
1840 return -1;
1841
1842 mdata->bcache = msg_cache_open(m);
1843 char id[64] = { 0 };
1844 snprintf(id, sizeof(id), "%u-%u", mdata->uidvalidity, imap_edata_get(e)->uid);
1845 return mutt_bcache_del(mdata->bcache, id);
1846}
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 1853 of file message.c.

1854{
1856 struct ImapMboxData *mdata = imap_mdata_get(m);
1857
1858 if (!adata || (adata->mailbox != m))
1859 return -1;
1860
1861 mdata->bcache = msg_cache_open(m);
1863
1864 return 0;
1865}
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:166
+ 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 1885 of file message.c.

1886{
1888 if (!adata || (adata->mailbox != m))
1889 return NULL;
1890
1891 struct ImapHeader newh = { 0 };
1892 struct ImapEmailData old_edata = { 0 };
1893 int local_changes = e->changed;
1894
1895 struct ImapEmailData *edata = e->edata;
1896 newh.edata = edata;
1897
1898 mutt_debug(LL_DEBUG2, "parsing FLAGS\n");
1899 s = msg_parse_flags(&newh, s);
1900 if (!s)
1901 return NULL;
1902
1903 /* Update tags system */
1904 /* We take a copy of the tags so we can split the string */
1905 char *tags_copy = mutt_str_dup(edata->flags_remote);
1906 driver_tags_replace(&e->tags, tags_copy);
1907 FREE(&tags_copy);
1908
1909 /* YAUH (yet another ugly hack): temporarily set context to
1910 * read-write even if it's read-only, so *server* updates of
1911 * flags can be processed by mutt_set_flag. mailbox->changed must
1912 * be restored afterwards */
1913 bool readonly = m->readonly;
1914 m->readonly = false;
1915
1916 /* This is redundant with the following two checks. Removing:
1917 * mutt_set_flag (m, e, MUTT_NEW, !(edata->read || edata->old)); */
1918 set_changed_flag(m, e, local_changes, server_changes, MUTT_OLD, old_edata.old,
1919 edata->old, e->old);
1920 set_changed_flag(m, e, local_changes, server_changes, MUTT_READ,
1921 old_edata.read, edata->read, e->read);
1922 set_changed_flag(m, e, local_changes, server_changes, MUTT_DELETE,
1923 old_edata.deleted, edata->deleted, e->deleted);
1924 set_changed_flag(m, e, local_changes, server_changes, MUTT_FLAG,
1925 old_edata.flagged, edata->flagged, e->flagged);
1926 set_changed_flag(m, e, local_changes, server_changes, MUTT_REPLIED,
1927 old_edata.replied, edata->replied, e->replied);
1928
1929 /* this message is now definitively *not* changed (mutt_set_flag
1930 * marks things changed as a side-effect) */
1931 if (local_changes == 0)
1932 e->changed = false;
1933 m->changed &= !readonly;
1934 m->readonly = readonly;
1935
1936 return s;
1937}
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:627
@ MUTT_READ
Messages that have been read.
Definition: mutt.h:81
@ MUTT_OLD
Old messages.
Definition: mutt.h:79
@ MUTT_FLAG
Flagged messages.
Definition: mutt.h:87
@ MUTT_REPLIED
Messages that have been replied to.
Definition: mutt.h:80
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: