NeoMutt
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
message.c File Reference

Manage IMAP messages. More...

#include "config.h"
#include <assert.h>
#include <ctype.h>
#include <limits.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "private.h"
#include "mutt/lib.h"
#include "config/lib.h"
#include "email/lib.h"
#include "core/lib.h"
#include "conn/lib.h"
#include "gui/lib.h"
#include "mutt.h"
#include "message.h"
#include "lib.h"
#include "bcache/lib.h"
#include "progress/lib.h"
#include "question/lib.h"
#include "adata.h"
#include "edata.h"
#include "external.h"
#include "globals.h"
#include "mdata.h"
#include "msg_set.h"
#include "msn.h"
#include "mutt_logging.h"
#include "mx.h"
#include "protos.h"
#include <libintl.h>
#include "hcache/lib.h"
+ Include dependency graph for message.c:

Go to the source code of this file.

Functions

static struct BodyCacheimap_bcache_open (struct Mailbox *m)
 Open a message cache.
 
static FILE * msg_cache_get (struct Mailbox *m, struct Email *e)
 Get the message cache entry for an email.
 
static FILE * msg_cache_put (struct Mailbox *m, struct Email *e)
 Put an email into the message cache.
 
static int msg_cache_commit (struct Mailbox *m, struct Email *e)
 Add to the message cache.
 
static int msg_cache_clean_cb (const char *id, struct BodyCache *bcache, void *data)
 Delete an entry from the message cache - Implements bcache_list_t -.
 
static char * msg_parse_flags (struct ImapHeader *h, char *s)
 Read a FLAGS token into an ImapHeader.
 
static int msg_parse_fetch (struct ImapHeader *h, char *s)
 Handle headers returned from header fetch.
 
static int msg_fetch_header (struct Mailbox *m, struct ImapHeader *ih, char *buf, FILE *fp)
 Import IMAP FETCH response into an ImapHeader.
 
static int flush_buffer (char *buf, size_t *len, struct Connection *conn)
 Write data to a connection.
 
static bool query_abort_header_download (struct ImapAccountData *adata)
 Ask the user whether to abort the download.
 
static void imap_alloc_uid_hash (struct ImapAccountData *adata, unsigned int msn_count)
 Create a Hash Table for the UIDs.
 
static unsigned int imap_fetch_msn_seqset (struct Buffer *buf, struct ImapAccountData *adata, bool evalhc, unsigned int msn_begin, unsigned int msn_end, unsigned int *fetch_msn_end)
 Generate a sequence set.
 
static void set_changed_flag (struct Mailbox *m, struct Email *e, int local_changes, bool *server_changes, enum MessageType flag_name, bool old_hd_flag, bool new_hd_flag, bool h_flag)
 Have the flags of an email changed.
 
static int read_headers_normal_eval_cache (struct ImapAccountData *adata, unsigned int msn_end, unsigned int uid_next, bool store_flag_updates, bool eval_condstore)
 Retrieve data from the header cache.
 
static int read_headers_qresync_eval_cache (struct ImapAccountData *adata, char *uid_seqset)
 Retrieve data from the header cache.
 
static int read_headers_condstore_qresync_updates (struct ImapAccountData *adata, unsigned int msn_end, unsigned int uid_next, unsigned long long hc_modseq, bool eval_qresync)
 Retrieve updates from the server.
 
static int imap_verify_qresync (struct Mailbox *m)
 Check to see if QRESYNC got jumbled.
 
static int read_headers_fetch_new (struct Mailbox *m, unsigned int msn_begin, unsigned int msn_end, bool evalhc, unsigned int *maxuid, bool initial_download)
 Retrieve new messages from the server.
 
int imap_read_headers (struct Mailbox *m, unsigned int msn_begin, unsigned int msn_end, bool initial_download)
 Read headers from the server.
 
int imap_append_message (struct Mailbox *m, struct Message *msg)
 Write an email back to the server.
 
static int emails_to_uid_array (struct EmailArray *ea, struct UidArray *uida)
 Extract IMAP UIDs from Emails.
 
int imap_copy_messages (struct Mailbox *m, struct EmailArray *ea, const char *dest, enum MessageSaveOpt save_opt)
 Server COPY messages to another folder.
 
int imap_cache_del (struct Mailbox *m, struct Email *e)
 Delete an email from the body cache.
 
int imap_cache_clean (struct Mailbox *m)
 Delete all the entries in the message cache.
 
char * imap_set_flags (struct Mailbox *m, struct Email *e, char *s, bool *server_changes)
 Fill the message header according to the server flags.
 
bool imap_msg_open (struct Mailbox *m, struct Message *msg, struct Email *e)
 Open an email message in a Mailbox - Implements MxOps::msg_open() -.
 
int imap_msg_commit (struct Mailbox *m, struct Message *msg)
 Save changes to an email - Implements MxOps::msg_commit() -.
 
int imap_msg_close (struct Mailbox *m, struct Message *msg)
 Close an email - Implements MxOps::msg_close() -.
 
int imap_msg_save_hcache (struct Mailbox *m, struct Email *e)
 Save message to the header cache - Implements MxOps::msg_save_hcache() -.
 

Detailed Description

Manage IMAP messages.

Authors
  • Brandon Long
  • Brendan Cully
  • Richard Russon

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

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

Definition in file message.c.

Function Documentation

◆ imap_bcache_open()

static struct BodyCache * imap_bcache_open ( struct Mailbox m)
static

Open a message cache.

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

Definition at line 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 = buf_pool_get();
90 imap_cachepath(adata->delim, mdata->name, mailbox);
91
92 struct BodyCache *bc = mutt_bcache_open(&adata->conn->account, buf_string(mailbox));
93 buf_pool_release(&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:143
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:93
struct ImapAccountData * imap_adata_get(struct Mailbox *m)
Get the Account data for this mailbox.
Definition: adata.c:123
struct ImapMboxData * imap_mdata_get(struct Mailbox *m)
Get the Mailbox data for this mailbox.
Definition: mdata.c:60
void imap_cachepath(char delim, const char *mailbox, struct Buffer *dest)
Generate a cache path for a mailbox.
Definition: util.c:707
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:81
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition: pool.c:94
void * adata
Private data (for Mailbox backends)
Definition: account.h: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:40
char * name
Mailbox name.
Definition: mdata.h:41
void * mdata
Driver specific data.
Definition: mailbox.h:133
+ 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 = imap_bcache_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:182
struct ImapEmailData * imap_edata_get(struct Email *e)
Get the private data for this Email.
Definition: edata.c:66
static struct BodyCache * imap_bcache_open(struct Mailbox *m)
Open a message cache.
Definition: message.c: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 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 = imap_bcache_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:209
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ msg_cache_commit()

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

Add to the message cache.

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

Definition at line 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 = imap_bcache_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:249
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ msg_parse_flags()

static char * msg_parse_flags ( struct ImapHeader h,
char *  s 
)
static

Read a FLAGS token into an ImapHeader.

Parameters
hHeader to store flags
sCommand string containing flags
Return values
ptrThe end of flags string
NULLFailure

Definition at line 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 && !isspace(*s) && (*s != ')'))
257 s++;
258
259 ctmp = *s;
260 *s = '\0';
261
262 if (is_system_keyword)
263 {
264 /* store other system flags as well (mainly \\Draft) */
265 mutt_str_append_item(&edata->flags_system, flag_word, ' ');
266 }
267 else
268 {
269 /* store custom flags as well */
270 mutt_str_append_item(&edata->flags_remote, flag_word, ' ');
271 }
272
273 *s = ctmp;
274 }
275 SKIPWS(s);
276 }
277
278 /* wrap up, or note bad flags response */
279 if (*s == ')')
280 {
281 s++;
282 }
283 else
284 {
285 mutt_debug(LL_DEBUG1, "Unterminated FLAGS response: %s\n", s);
286 return NULL;
287 }
288
289 return s;
290}
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:48
#define mutt_debug(LEVEL,...)
Definition: logging2.h:89
@ LL_DEBUG1
Log at debug level 1.
Definition: logging2.h:43
#define FREE(x)
Definition: memory.h:45
void mutt_str_append_item(char **str, const char *item, char sep)
Add string to another separated by sep.
Definition: string.c:347
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition: string.c:240
#define SKIPWS(ch)
Definition: string2.h:45
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:41
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:45
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ msg_parse_fetch()

static int msg_parse_fetch ( struct ImapHeader h,
char *  s 
)
static

Handle headers returned from header fetch.

Parameters
hIMAP Header
sCommand string
Return values
0Success
-1String is corrupted
-2Fetch contains a body or header lines that still need to be parsed

Definition at line 300 of file message.c.

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

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

479{
480 buf[*len] = '\0';
481 int rc = mutt_socket_write_n(conn, buf, *len);
482 *len = 0;
483 return rc;
484}
#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 494 of file message.c.

495{
496 bool abort = false;
497
499 /* L10N: This prompt is made if the user hits Ctrl-C when opening an IMAP mailbox */
500 if (query_yesorno(_("Abort download and close mailbox?"), MUTT_YES) == MUTT_YES)
501 {
502 abort = true;
504 }
505 SigInt = false;
506
507 return abort;
508}
void mutt_flushinp(void)
Empty all the keyboard buffers.
Definition: get.c:59
SIG_ATOMIC_VOLATILE_T SigInt
true after SIGINT is received
Definition: globals.c:59
void imap_close_connection(struct ImapAccountData *adata)
Close an IMAP connection.
Definition: imap.c:841
#define _(a)
Definition: message.h:28
@ MUTT_YES
User answered 'Yes', or assume 'Yes'.
Definition: quad.h:39
enum QuadOption query_yesorno(const char *prompt, enum QuadOption def)
Ask the user a Yes/No question.
Definition: question.c:331
+ 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 518 of file message.c.

519{
520 struct ImapMboxData *mdata = adata->mailbox->mdata;
521 if (!mdata->uid_hash)
522 mdata->uid_hash = mutt_hash_int_new(MAX(6 * msn_count / 5, 30), MUTT_HASH_NO_FLAGS);
523}
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:110
#define MAX(a, b)
Definition: memory.h:31
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_fetch_msn_seqset()

static unsigned int imap_fetch_msn_seqset ( struct Buffer buf,
struct ImapAccountData adata,
bool  evalhc,
unsigned int  msn_begin,
unsigned int  msn_end,
unsigned int *  fetch_msn_end 
)
static

Generate a sequence set.

Parameters
[in]bufBuffer for the result
[in]adataImap Account data
[in]evalhcIf true, check the Header Cache
[in]msn_beginFirst Message Sequence Number
[in]msn_endLast Message Sequence Number
[out]fetch_msn_endHighest Message Sequence Number fetched
Return values
numMSN count

Generates a more complicated sequence set after using the header cache, in case there are missing MSNs in the middle.

This can happen if during a sync/close, messages are deleted from the cache, but the server doesn't get the updates (via a dropped network connection, or just plain refusing the updates).

Definition at line 542 of file message.c.

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

636{
637 /* If there are local_changes, we only want to note if the server
638 * flags have changed, so we can set a reopen flag in
639 * cmd_parse_fetch(). We don't want to count a local modification
640 * to the header flag as a "change". */
641 if ((old_hd_flag == new_hd_flag) && (local_changes != 0))
642 return;
643
644 if (new_hd_flag == h_flag)
645 return;
646
647 if (server_changes)
648 *server_changes = true;
649
650 /* Local changes have priority */
651 if (local_changes == 0)
652 mutt_set_flag(m, e, flag_name, new_hd_flag, true);
653}
void mutt_set_flag(struct Mailbox *m, struct Email *e, enum MessageType flag, bool bf, bool upd_mbox)
Set a flag on an email.
Definition: flags.c:54
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ read_headers_normal_eval_cache()

static int read_headers_normal_eval_cache ( struct ImapAccountData adata,
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 673 of file message.c.

676{
677 struct Progress *progress = NULL;
678 char buf[1024] = { 0 };
679 int rc = -1;
680
681 struct Mailbox *m = adata->mailbox;
682 struct ImapMboxData *mdata = imap_mdata_get(m);
683 int idx = m->msg_count;
684
685 if (m->verbose)
686 {
687 /* L10N: Comparing the cached data with the IMAP server's data */
688 progress = progress_new(_("Evaluating cache..."), MUTT_PROGRESS_READ, msn_end);
689 }
690
691 /* If we are using CONDSTORE's "FETCH CHANGEDSINCE", then we keep
692 * the flags in the header cache, and update them further below.
693 * Otherwise, we fetch the current state of the flags here. */
694 snprintf(buf, sizeof(buf), "UID FETCH 1:%u (UID%s)", uid_next - 1,
695 eval_condstore ? "" : " FLAGS");
696
697 imap_cmd_start(adata, buf);
698
700 int mfhrc = 0;
701 struct ImapHeader h = { 0 };
702 for (int msgno = 1; rc == IMAP_RES_CONTINUE; msgno++)
703 {
705 goto fail;
706
707 progress_update(progress, msgno, -1);
708
709 memset(&h, 0, sizeof(h));
710 h.edata = imap_edata_new();
711 do
712 {
713 rc = imap_cmd_step(adata);
714 if (rc != IMAP_RES_CONTINUE)
715 break;
716
717 mfhrc = msg_fetch_header(m, &h, adata->buf, NULL);
718 if (mfhrc < 0)
719 continue;
720
721 if (!h.edata->uid)
722 {
723 mutt_debug(LL_DEBUG2, "skipping hcache FETCH response for message number %d missing a UID\n",
724 h.edata->msn);
725 continue;
726 }
727
728 if ((h.edata->msn < 1) || (h.edata->msn > msn_end))
729 {
730 mutt_debug(LL_DEBUG1, "skipping hcache FETCH response for unknown message number %d\n",
731 h.edata->msn);
732 continue;
733 }
734
735 if (imap_msn_get(&mdata->msn, h.edata->msn - 1))
736 {
737 mutt_debug(LL_DEBUG2, "skipping hcache FETCH for duplicate message %d\n",
738 h.edata->msn);
739 continue;
740 }
741
742 struct Email *e = imap_hcache_get(mdata, h.edata->uid);
743 m->emails[idx] = e;
744 if (e)
745 {
746 imap_msn_set(&mdata->msn, h.edata->msn - 1, e);
747 mutt_hash_int_insert(mdata->uid_hash, h.edata->uid, e);
748
749 e->index = h.edata->uid;
750 /* messages which have not been expunged are ACTIVE (borrowed from mh
751 * folders) */
752 e->active = true;
753 e->changed = false;
754 if (eval_condstore)
755 {
756 h.edata->read = e->read;
757 h.edata->old = e->old;
758 h.edata->deleted = e->deleted;
759 h.edata->flagged = e->flagged;
760 h.edata->replied = e->replied;
761 }
762 else
763 {
764 e->read = h.edata->read;
765 e->old = h.edata->old;
766 e->deleted = h.edata->deleted;
767 e->flagged = h.edata->flagged;
768 e->replied = h.edata->replied;
769 }
770
771 /* mailbox->emails[msgno]->received is restored from hcache_fetch() */
772 e->edata = h.edata;
774
775 /* We take a copy of the tags so we can split the string */
776 char *tags_copy = mutt_str_dup(h.edata->flags_remote);
777 driver_tags_replace(&e->tags, tags_copy);
778 FREE(&tags_copy);
779
780 m->msg_count++;
781 mailbox_size_add(m, e);
782
783 /* If this is the first time we are fetching, we need to
784 * store the current state of flags back into the header cache */
785 if (!eval_condstore && store_flag_updates)
786 imap_hcache_put(mdata, e);
787
788 h.edata = NULL;
789 idx++;
790 }
791 } while (mfhrc == -1);
792
793 imap_edata_free((void **) &h.edata);
794
795 if ((mfhrc < -1) || ((rc != IMAP_RES_CONTINUE) && (rc != IMAP_RES_OK)))
796 goto fail;
797 }
798
799 rc = 0;
800fail:
801 progress_free(&progress);
802 return rc;
803}
struct HashElem * mutt_hash_int_insert(struct HashTable *table, unsigned int intkey, void *data)
Add a new element to the Hash Table (with integer keys)
Definition: hash.c:347
int imap_cmd_start(struct ImapAccountData *adata, const char *cmdstr)
Given an IMAP command, send it to the server.
Definition: command.c:1115
struct ImapEmailData * imap_edata_new(void)
Create a new ImapEmailData.
Definition: edata.c:56
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:412
static bool query_abort_header_download(struct ImapAccountData *adata)
Ask the user whether to abort the download.
Definition: message.c:494
#define IMAP_RES_OK
<tag> OK ...
Definition: private.h:55
int imap_hcache_put(struct ImapMboxData *mdata, struct Email *e)
Add an entry to the header cache.
Definition: util.c:377
struct Email * imap_hcache_get(struct ImapMboxData *mdata, unsigned int uid)
Get a header cache entry by its UID.
Definition: util.c:352
@ LL_DEBUG2
Log at debug level 2.
Definition: logging2.h:44
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:242
void imap_msn_set(struct MSNArray *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:251
@ 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:92
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:124
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
char * flags_remote
Definition: edata.h:48
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
IMAP-specific header.
Definition: message.h:33
unsigned int uid_next
Definition: mdata.h:52
struct HashTable * uid_hash
Hash Table: "uid" -> Email.
Definition: mdata.h:59
A mailbox.
Definition: mailbox.h:79
int msg_count
Total number of messages.
Definition: mailbox.h:88
struct Email ** emails
Array of Emails.
Definition: mailbox.h:96
bool verbose
Display status messages?
Definition: mailbox.h:116
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 817 of file message.c.

818{
819 int rc;
820 unsigned int uid = 0;
821
822 mutt_debug(LL_DEBUG2, "Reading uid seqset from header cache\n");
823 struct Mailbox *m = adata->mailbox;
824 struct ImapMboxData *mdata = adata->mailbox->mdata;
825 unsigned int msn = 1;
826
827 if (m->verbose)
828 mutt_message(_("Evaluating cache..."));
829
830 struct SeqsetIterator *iter = mutt_seqset_iterator_new(uid_seqset);
831 if (!iter)
832 return -1;
833
834 while ((rc = mutt_seqset_iterator_next(iter, &uid)) == 0)
835 {
836 /* The seqset may contain more headers than the fetch request, so
837 * we need to watch and reallocate the context and msn_index */
838 imap_msn_reserve(&mdata->msn, msn);
839
840 struct Email *e = imap_hcache_get(mdata, uid);
841 if (e)
842 {
843 imap_msn_set(&mdata->msn, msn - 1, e);
844
846
848 e->edata = edata;
850
851 e->index = uid;
852 e->active = true;
853 e->changed = false;
854 edata->read = e->read;
855 edata->old = e->old;
856 edata->deleted = e->deleted;
857 edata->flagged = e->flagged;
858 edata->replied = e->replied;
859
860 edata->msn = msn;
861 edata->uid = uid;
863
864 mailbox_size_add(m, e);
865 m->emails[m->msg_count++] = e;
866
867 msn++;
868 }
869 else if (!uid)
870 {
871 /* A non-zero uid missing from the header cache is either the
872 * result of an expunged message (not recorded in the uid seqset)
873 * or a hole in the header cache.
874 *
875 * We have to assume it's an earlier expunge and compact the msn's
876 * in that case, because cmd_parse_vanished() won't find it in the
877 * uid_hash and decrement later msn's there.
878 *
879 * Thus we only increment the uid if the uid was 0: an actual
880 * stored "blank" in the uid seqset.
881 */
882 msn++;
883 }
884 }
885
887
888 return rc;
889}
#define mutt_message(...)
Definition: logging2.h:91
struct SeqsetIterator * mutt_seqset_iterator_new(const char *seqset)
Create a new Sequence Set Iterator.
Definition: util.c:1083
int mutt_seqset_iterator_next(struct SeqsetIterator *iter, unsigned int *next)
Get the next UID from a Sequence Set.
Definition: util.c:1104
void mutt_seqset_iterator_free(struct SeqsetIterator **ptr)
Free a Sequence Set Iterator.
Definition: util.c:1163
void imap_msn_reserve(struct MSNArray *msn, size_t num)
Create / reallocate the cache.
Definition: msn.c:41
void mx_alloc_memory(struct Mailbox *m, int req_size)
Create storage for the emails.
Definition: mx.c:1232
UID Sequence Set Iterator.
Definition: private.h:169
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ read_headers_condstore_qresync_updates()

static int read_headers_condstore_qresync_updates ( struct ImapAccountData adata,
unsigned int  msn_end,
unsigned int  uid_next,
unsigned long long  hc_modseq,
bool  eval_qresync 
)
static

Retrieve updates from the server.

Parameters
adataImap Account data
msn_endLast Message Sequence number
uid_nextUID of next email
hc_modseqTimestamp of last Header Cache update
eval_qresyncIf true, use QRESYNC
Return values
0Success
-1Error

CONDSTORE and QRESYNC use FETCH extensions to grab updates.

Definition at line 903 of file message.c.

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

1002{
1003 assert(m);
1005 struct ImapMboxData *mdata = imap_mdata_get(m);
1006 if (!adata || (adata->mailbox != m))
1007 return -1;
1008
1009 const size_t max_msn = imap_msn_highest(&mdata->msn);
1010
1011 unsigned int msn;
1012 unsigned int uid;
1013 struct Email *e = NULL;
1014 struct Email *uidh = NULL;
1015
1016 for (int i = 0; i < m->msg_count; i++)
1017 {
1018 e = m->emails[i];
1019 const struct ImapEmailData *edata = imap_edata_get(e);
1020 if (!edata)
1021 goto fail;
1022
1023 msn = imap_edata_get(e)->msn;
1024 uid = imap_edata_get(e)->uid;
1025
1026 if ((msn < 1) || (msn > max_msn) || imap_msn_get(&mdata->msn, msn - 1) != e)
1027 goto fail;
1028
1029 uidh = (struct Email *) mutt_hash_int_find(mdata->uid_hash, uid);
1030 if (uidh != e)
1031 goto fail;
1032 }
1033
1034 return 0;
1035
1036fail:
1037 imap_msn_free(&mdata->msn);
1038 mutt_hash_free(&mdata->uid_hash);
1042
1043 for (int i = 0; i < m->msg_count; i++)
1044 {
1045 if (m->emails[i] && m->emails[i]->edata)
1046 imap_edata_free(&m->emails[i]->edata);
1047 email_free(&m->emails[i]);
1048 }
1049 m->msg_count = 0;
1050 m->size = 0;
1051 hcache_delete_record(mdata->hcache, "/MODSEQ", 7);
1053 imap_hcache_close(mdata);
1054
1055 if (m->verbose)
1056 {
1057 /* L10N: After opening an IMAP mailbox using QRESYNC, Mutt performs a quick
1058 sanity check. If that fails, Mutt reopens the mailbox using a normal
1059 download. */
1060 mutt_error(_("QRESYNC failed. Reopening mailbox."));
1061 }
1062 return -1;
1063}
void email_free(struct Email **ptr)
Free an Email.
Definition: email.c:44
#define mutt_error(...)
Definition: logging2.h:92
void * mutt_hash_int_find(const struct HashTable *table, unsigned int intkey)
Find the HashElem data in a Hash Table element using a key.
Definition: hash.c:392
void mutt_hash_free(struct HashTable **ptr)
Free a hash table.
Definition: hash.c:457
int hcache_delete_record(struct HeaderCache *hc, const char *key, size_t keylen)
Multiplexor for StoreOps::delete_record.
Definition: hcache.c:759
int imap_hcache_clear_uid_seqset(struct ImapMboxData *mdata)
Delete a UID Sequence Set from the header cache.
Definition: util.c:433
void imap_msn_free(struct MSNArray *msn)
Free the cache.
Definition: msn.c:59
size_t imap_msn_highest(const struct MSNArray *msn)
Return the highest MSN in use.
Definition: msn.c:69
struct HeaderCache * hcache
Email header cache.
Definition: mdata.h:63
struct HashTable * subj_hash
Hash Table: "subject" -> Email.
Definition: mailbox.h:125
struct HashTable * id_hash
Hash Table: "message-id" -> Email.
Definition: mailbox.h:124
off_t size
Size of the Mailbox.
Definition: mailbox.h:84
struct HashTable * label_hash
Hash Table: "x-labels" -> Email.
Definition: mailbox.h:126
+ 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 1078 of file message.c.

1081{
1082 int rc = -1;
1083 unsigned int fetch_msn_end = 0;
1084 struct Progress *progress = NULL;
1085 char *hdrreq = NULL;
1086 struct Buffer *tempfile = NULL;
1087 FILE *fp = NULL;
1088 struct ImapHeader h = { 0 };
1089 struct Buffer *buf = NULL;
1090 static const char *const want_headers = "DATE FROM SENDER SUBJECT TO CC MESSAGE-ID REFERENCES "
1091 "CONTENT-TYPE CONTENT-DESCRIPTION IN-REPLY-TO REPLY-TO "
1092 "LINES LIST-POST LIST-SUBSCRIBE LIST-UNSUBSCRIBE X-LABEL "
1093 "X-ORIGINAL-TO";
1094
1096 struct ImapMboxData *mdata = imap_mdata_get(m);
1097
1098 if (!adata || (adata->mailbox != m))
1099 return -1;
1100
1101 struct Buffer *hdr_list = buf_pool_get();
1102 buf_strcpy(hdr_list, want_headers);
1103 const char *const c_imap_headers = cs_subset_string(NeoMutt->sub, "imap_headers");
1104 if (c_imap_headers)
1105 {
1106 buf_addch(hdr_list, ' ');
1107 buf_addstr(hdr_list, c_imap_headers);
1108 }
1109#ifdef USE_AUTOCRYPT
1110 const bool c_autocrypt = cs_subset_bool(NeoMutt->sub, "autocrypt");
1111 if (c_autocrypt)
1112 {
1113 buf_addch(hdr_list, ' ');
1114 buf_addstr(hdr_list, "AUTOCRYPT");
1115 }
1116#endif
1117
1118 if (adata->capabilities & IMAP_CAP_IMAP4REV1)
1119 {
1120 mutt_str_asprintf(&hdrreq, "BODY.PEEK[HEADER.FIELDS (%s)]", buf_string(hdr_list));
1121 }
1122 else if (adata->capabilities & IMAP_CAP_IMAP4)
1123 {
1124 mutt_str_asprintf(&hdrreq, "RFC822.HEADER.LINES (%s)", buf_string(hdr_list));
1125 }
1126 else
1127 { /* Unable to fetch headers for lower versions */
1128 mutt_error(_("Unable to fetch headers from this IMAP server version"));
1129 goto bail;
1130 }
1131
1132 buf_pool_release(&hdr_list);
1133
1134 /* instead of downloading all headers and then parsing them, we parse them
1135 * as they come in. */
1136 tempfile = buf_pool_get();
1137 buf_mktemp(tempfile);
1138 fp = mutt_file_fopen(buf_string(tempfile), "w+");
1139 if (!fp)
1140 {
1141 mutt_error(_("Could not create temporary file %s"), buf_string(tempfile));
1142 goto bail;
1143 }
1144 unlink(buf_string(tempfile));
1145 buf_pool_release(&tempfile);
1146
1147 if (m->verbose)
1148 {
1149 progress = progress_new(_("Fetching message headers..."), MUTT_PROGRESS_READ, msn_end);
1150 }
1151
1152 buf = buf_pool_get();
1153
1154 /* NOTE:
1155 * The (fetch_msn_end < msn_end) used to be important to prevent
1156 * an infinite loop, in the event the server did not return all
1157 * the headers (due to a pending expunge, for example).
1158 *
1159 * I believe the new chunking imap_fetch_msn_seqset()
1160 * implementation and "msn_begin = fetch_msn_end + 1" assignment
1161 * at the end of the loop makes the comparison unneeded, but to be
1162 * cautious I'm keeping it.
1163 */
1165 while ((fetch_msn_end < msn_end) &&
1166 imap_fetch_msn_seqset(buf, adata, evalhc, msn_begin, msn_end, &fetch_msn_end))
1167 {
1168 char *cmd = NULL;
1169 mutt_str_asprintf(&cmd, "FETCH %s (UID FLAGS INTERNALDATE RFC822.SIZE %s)",
1170 buf_string(buf), hdrreq);
1171 imap_cmd_start(adata, cmd);
1172 FREE(&cmd);
1173
1174 int msgno = msn_begin;
1175
1176 while (true)
1177 {
1178 rewind(fp);
1179 memset(&h, 0, sizeof(h));
1180 h.edata = edata;
1181
1182 if (initial_download && SigInt && query_abort_header_download(adata))
1183 {
1184 goto bail;
1185 }
1186
1187 const int rc2 = imap_cmd_step(adata);
1188 if (rc2 != IMAP_RES_CONTINUE)
1189 {
1190 if (rc2 != IMAP_RES_OK)
1191 {
1192 goto bail;
1193 }
1194 break;
1195 }
1196
1197 switch (msg_fetch_header(m, &h, adata->buf, fp))
1198 {
1199 case 0:
1200 break;
1201 case -1:
1202 continue;
1203 case -2:
1204 goto bail;
1205 }
1206
1207 if (!ftello(fp))
1208 {
1209 mutt_debug(LL_DEBUG2, "ignoring fetch response with no body\n");
1210 continue;
1211 }
1212
1213 /* make sure we don't get remnants from older larger message headers */
1214 fputs("\n\n", fp);
1215
1216 if ((h.edata->msn < 1) || (h.edata->msn > fetch_msn_end))
1217 {
1218 mutt_debug(LL_DEBUG1, "skipping FETCH response for unknown message number %d\n",
1219 h.edata->msn);
1220 continue;
1221 }
1222
1223 /* May receive FLAGS updates in a separate untagged response */
1224 if (imap_msn_get(&mdata->msn, h.edata->msn - 1))
1225 {
1226 mutt_debug(LL_DEBUG2, "skipping FETCH response for duplicate message %d\n",
1227 h.edata->msn);
1228 continue;
1229 }
1230
1231 progress_update(progress, msgno++, -1);
1232
1233 struct Email *e = email_new();
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 mx_alloc_memory(m, msn_end);
1282 imap_msn_reserve(&mdata->msn, msn_end);
1283 mdata->reopen &= ~IMAP_NEWMAIL_PENDING;
1284 mdata->new_mail_count = 0;
1285 }
1286
1287 /* Note: RFC3501 section 7.4.1 and RFC7162 section 3.2.10.2 say we
1288 * must not get any EXPUNGE/VANISHED responses in the middle of a
1289 * FETCH, nor when no command is in progress (e.g. between the
1290 * chunked FETCH commands). We previously tried to be robust by
1291 * setting:
1292 * msn_begin = mdata->max_msn + 1;
1293 * but with chunking and header cache holes this
1294 * may not be correct. So here we must assume the msn values have
1295 * not been altered during or after the fetch. */
1296 msn_begin = fetch_msn_end + 1;
1297 }
1298
1299 rc = 0;
1300
1301bail:
1302 buf_pool_release(&hdr_list);
1303 buf_pool_release(&buf);
1304 buf_pool_release(&tempfile);
1305 mutt_file_fclose(&fp);
1306 FREE(&hdrreq);
1307 imap_edata_free((void **) &edata);
1308 progress_free(&progress);
1309
1310 return rc;
1311}
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:238
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:407
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:292
struct Email * email_new(void)
Create a new Email.
Definition: email.c:78
struct Envelope * mutt_rfc822_read_header(FILE *fp, struct Email *e, bool user_hdrs, bool weed)
Parses an RFC822 header.
Definition: parse.c:1170
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:636
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:78
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:542
#define IMAP_CAP_IMAP4
Server supports IMAP4.
Definition: private.h:121
#define IMAP_CAP_IMAP4REV1
Server supports IMAP4rev1.
Definition: private.h:122
#define IMAP_NEWMAIL_PENDING
New mail is waiting on the server.
Definition: private.h:67
int mutt_str_asprintf(char **strp, const char *fmt,...)
Definition: string.c:1022
#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:45
unsigned int new_mail_count
Set when EXISTS notifies of new mail.
Definition: mdata.h:47
#define buf_mktemp(buf)
Definition: tmp.h:33
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_read_headers()

int imap_read_headers ( struct Mailbox m,
unsigned int  msn_begin,
unsigned int  msn_end,
bool  initial_download 
)

Read headers from the server.

Parameters
mImap Selected Mailbox
msn_beginFirst Message Sequence Number
msn_endLast Message Sequence Number
initial_downloadtrue, if this is the first opening of the mailbox
Return values
numLast MSN
-1Failure

Changed to read many headers instead of just one. It will return the msn of the last message read. It will return a value other than msn_end if mail comes in while downloading headers (in theory).

Definition at line 1326 of file message.c.

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

◆ imap_append_message()

int imap_append_message ( struct Mailbox m,
struct Message msg 
)

Write an email back to the server.

Parameters
mMailbox
msgMessage to save
Return values
0Success
-1Failure

Definition at line 1516 of file message.c.

1517{
1518 if (!m || !msg)
1519 return -1;
1520
1521 FILE *fp = NULL;
1522 char buf[2048];
1523 char internaldate[IMAP_DATELEN] = { 0 };
1524 char imap_flags[128] = { 0 };
1525 size_t len;
1526 struct Progress *progress = NULL;
1527 size_t sent;
1528 int c, last;
1529 int rc;
1530
1532 struct ImapMboxData *mdata = imap_mdata_get(m);
1533
1534 fp = fopen(msg->path, "r");
1535 if (!fp)
1536 {
1537 mutt_perror("%s", msg->path);
1538 goto fail;
1539 }
1540
1541 /* currently we set the \Seen flag on all messages, but probably we
1542 * should scan the message Status header for flag info. Since we're
1543 * already rereading the whole file for length it isn't any more
1544 * expensive (it'd be nice if we had the file size passed in already
1545 * by the code that writes the file, but that's a lot of changes.
1546 * Ideally we'd have an Email structure with flag info here... */
1547 for (last = EOF, len = 0; (c = fgetc(fp)) != EOF; last = c)
1548 {
1549 if ((c == '\n') && (last != '\r'))
1550 len++;
1551
1552 len++;
1553 }
1554 rewind(fp);
1555
1556 if (m->verbose)
1557 progress = progress_new(_("Uploading message..."), MUTT_PROGRESS_NET, len);
1558
1559 mutt_date_make_imap(internaldate, sizeof(internaldate), msg->received);
1560
1561 imap_flags[0] = '\0';
1562 imap_flags[1] = '\0';
1563
1564 if (msg->flags.read)
1565 mutt_str_cat(imap_flags, sizeof(imap_flags), " \\Seen");
1566 if (msg->flags.replied)
1567 mutt_str_cat(imap_flags, sizeof(imap_flags), " \\Answered");
1568 if (msg->flags.flagged)
1569 mutt_str_cat(imap_flags, sizeof(imap_flags), " \\Flagged");
1570 if (msg->flags.draft)
1571 mutt_str_cat(imap_flags, sizeof(imap_flags), " \\Draft");
1572
1573 snprintf(buf, sizeof(buf), "APPEND %s (%s) \"%s\" {%lu}", mdata->munge_name,
1574 imap_flags + 1, internaldate, (unsigned long) len);
1575
1576 imap_cmd_start(adata, buf);
1577
1578 do
1579 {
1580 rc = imap_cmd_step(adata);
1581 } while (rc == IMAP_RES_CONTINUE);
1582
1583 if (rc != IMAP_RES_RESPOND)
1584 goto cmd_step_fail;
1585
1586 for (last = EOF, sent = len = 0; (c = fgetc(fp)) != EOF; last = c)
1587 {
1588 if ((c == '\n') && (last != '\r'))
1589 buf[len++] = '\r';
1590
1591 buf[len++] = c;
1592
1593 if (len > sizeof(buf) - 3)
1594 {
1595 sent += len;
1596 if (flush_buffer(buf, &len, adata->conn) < 0)
1597 goto fail;
1598 progress_update(progress, sent, -1);
1599 }
1600 }
1601
1602 if (len)
1603 if (flush_buffer(buf, &len, adata->conn) < 0)
1604 goto fail;
1605
1606 if (mutt_socket_send(adata->conn, "\r\n") < 0)
1607 goto fail;
1608 mutt_file_fclose(&fp);
1609
1610 do
1611 {
1612 rc = imap_cmd_step(adata);
1613 } while (rc == IMAP_RES_CONTINUE);
1614
1615 if (rc != IMAP_RES_OK)
1616 goto cmd_step_fail;
1617
1618 progress_free(&progress);
1619 return 0;
1620
1621cmd_step_fail:
1622 mutt_debug(LL_DEBUG1, "command failed: %s\n", adata->buf);
1623 if (rc != IMAP_RES_BAD)
1624 {
1625 char *pc = imap_next_word(adata->buf); /* skip sequence number or token */
1626 pc = imap_next_word(pc); /* skip response code */
1627 if (*pc != '\0')
1628 mutt_error("%s", pc);
1629 }
1630
1631fail:
1632 mutt_file_fclose(&fp);
1633 progress_free(&progress);
1634 return -1;
1635}
#define mutt_perror(...)
Definition: logging2.h:93
static int flush_buffer(char *buf, size_t *len, struct Connection *conn)
Write data to a connection.
Definition: message.c:478
#define IMAP_RES_RESPOND
+
Definition: private.h:57
#define IMAP_DATELEN
Definition: private.h:88
#define IMAP_RES_BAD
<tag> BAD ...
Definition: private.h:54
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:784
char * mutt_str_cat(char *buf, size_t buflen, const char *s)
Concatenate two strings.
Definition: string.c:266
@ 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: message.h:36
bool draft
Message has been read.
Definition: message.h:44
bool replied
Message has been replied to.
Definition: message.h:43
time_t received
Time at which this message was received.
Definition: message.h:46
bool flagged
Message is flagged.
Definition: message.h:42
bool read
Message has been read.
Definition: message.h:41
struct Message::@0 flags
Flags for the Message.
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ emails_to_uid_array()

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

Extract IMAP UIDs from Emails.

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

Definition at line 1643 of file message.c.

1644{
1645 struct Email **ep = NULL;
1646 ARRAY_FOREACH(ep, ea)
1647 {
1648 struct Email *e = *ep;
1649 struct ImapEmailData *edata = imap_edata_get(e);
1650
1651 ARRAY_ADD(uida, edata->uid);
1652 }
1653 ARRAY_SORT(uida, imap_sort_uid, NULL);
1654
1655 return ARRAY_SIZE(uida);
1656}
#define ARRAY_SORT(head, fn, sdata)
Sort an array.
Definition: array.h:278
#define ARRAY_ADD(head, elem)
Add an element at the end of the array.
Definition: array.h:155
#define ARRAY_FOREACH(elem, head)
Iterate over all elements of the array.
Definition: array.h:211
#define ARRAY_SIZE(head)
The number of elements stored.
Definition: array.h:86
int imap_sort_uid(const void *a, const void *b, void *sdata)
Compare two UIDs - Implements sort_t -.
Definition: msg_set.c:54
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_copy_messages()

int imap_copy_messages ( struct Mailbox m,
struct EmailArray *  ea,
const char *  dest,
enum MessageSaveOpt  save_opt 
)

Server COPY messages to another folder.

Parameters
mMailbox
eaArray of Emails to copy
destDestination folder
save_optCopy or move, e.g. SAVE_MOVE
Return values
-1Error
0Success
1Non-fatal error - try fetch/append

Definition at line 1668 of file message.c.

1670{
1671 if (!m || !ea || ARRAY_EMPTY(ea) || !dest)
1672 return -1;
1673
1674 struct Buffer cmd, sync_cmd;
1675 char buf[PATH_MAX] = { 0 };
1676 char mbox[PATH_MAX] = { 0 };
1677 char mmbox[PATH_MAX] = { 0 };
1678 char prompt[PATH_MAX + 64];
1679 int rc;
1680 struct ConnAccount cac = { { 0 } };
1681 enum QuadOption err_continue = MUTT_NO;
1682 int triedcreate = 0;
1683 struct Email *e_cur = *ARRAY_GET(ea, 0);
1684 bool single = (ARRAY_SIZE(ea) == 1);
1686
1687 if (single && e_cur->attach_del)
1688 {
1689 mutt_debug(LL_DEBUG3, "#1 Message contains attachments to be deleted\n");
1690 return 1;
1691 }
1692
1693 if (imap_parse_path(dest, &cac, buf, sizeof(buf)))
1694 {
1695 mutt_debug(LL_DEBUG1, "bad destination %s\n", dest);
1696 return -1;
1697 }
1698
1699 /* check that the save-to folder is in the same account */
1700 if (!imap_account_match(&adata->conn->account, &cac))
1701 {
1702 mutt_debug(LL_DEBUG3, "%s not same server as %s\n", dest, mailbox_path(m));
1703 return 1;
1704 }
1705
1706 imap_fix_path(adata->delim, buf, mbox, sizeof(mbox));
1707 if (*mbox == '\0')
1708 mutt_str_copy(mbox, "INBOX", sizeof(mbox));
1709 imap_munge_mbox_name(adata->unicode, mmbox, sizeof(mmbox), mbox);
1710
1711 /* loop in case of TRYCREATE */
1712 do
1713 {
1714 buf_init(&sync_cmd);
1715 buf_init(&cmd);
1716
1717 if (single)
1718 {
1719 mutt_message(_("Copying message %d to %s..."), e_cur->index + 1, mbox);
1720 buf_add_printf(&cmd, "UID COPY %u %s", imap_edata_get(e_cur)->uid, mmbox);
1721
1722 if (e_cur->active && e_cur->changed)
1723 {
1724 rc = imap_sync_message_for_copy(m, e_cur, &sync_cmd, &err_continue);
1725 if (rc < 0)
1726 {
1727 mutt_debug(LL_DEBUG1, "#2 could not sync\n");
1728 goto out;
1729 }
1730 }
1731 rc = imap_exec(adata, cmd.data, IMAP_CMD_QUEUE);
1732 if (rc != IMAP_EXEC_SUCCESS)
1733 {
1734 mutt_debug(LL_DEBUG1, "#2 could not queue copy\n");
1735 goto out;
1736 }
1737 }
1738 else /* copy tagged messages */
1739 {
1740 /* if any messages have attachments to delete, fall through to FETCH
1741 * and APPEND. TODO: Copy what we can with COPY, fall through for the
1742 * remainder. */
1743 struct Email **ep = NULL;
1744 ARRAY_FOREACH(ep, ea)
1745 {
1746 struct Email *e = *ep;
1747 if (e->attach_del)
1748 {
1749 mutt_debug(LL_DEBUG3, "#2 Message contains attachments to be deleted\n");
1750 return 1;
1751 }
1752
1753 if (e->active && e->changed)
1754 {
1755 rc = imap_sync_message_for_copy(m, e, &sync_cmd, &err_continue);
1756 if (rc < 0)
1757 {
1758 mutt_debug(LL_DEBUG1, "#1 could not sync\n");
1759 goto out;
1760 }
1761 }
1762 }
1763
1764 struct UidArray uida = ARRAY_HEAD_INITIALIZER;
1765 emails_to_uid_array(ea, &uida);
1766 rc = imap_exec_msg_set(adata, "UID COPY", mmbox, &uida);
1767 ARRAY_FREE(&uida);
1768
1769 if (rc == 0)
1770 {
1771 mutt_debug(LL_DEBUG1, "No messages tagged\n");
1772 rc = -1;
1773 goto out;
1774 }
1775 else if (rc < 0)
1776 {
1777 mutt_debug(LL_DEBUG1, "#1 could not queue copy\n");
1778 goto out;
1779 }
1780 else
1781 {
1782 mutt_message(ngettext("Copying %d message to %s...", "Copying %d messages to %s...", rc),
1783 rc, mbox);
1784 }
1785 }
1786
1787 /* let's get it on */
1788 rc = imap_exec(adata, NULL, IMAP_CMD_NO_FLAGS);
1789 if (rc == IMAP_EXEC_ERROR)
1790 {
1791 if (triedcreate)
1792 {
1793 mutt_debug(LL_DEBUG1, "Already tried to create mailbox %s\n", mbox);
1794 break;
1795 }
1796 /* bail out if command failed for reasons other than nonexistent target */
1797 if (!mutt_istr_startswith(imap_get_qualifier(adata->buf), "[TRYCREATE]"))
1798 break;
1799 mutt_debug(LL_DEBUG3, "server suggests TRYCREATE\n");
1800 snprintf(prompt, sizeof(prompt), _("Create %s?"), mbox);
1801 const bool c_confirm_create = cs_subset_bool(NeoMutt->sub, "confirm_create");
1802 if (c_confirm_create &&
1803 (query_yesorno_help(prompt, MUTT_YES, NeoMutt->sub, "confirm_create") != MUTT_YES))
1804 {
1806 goto out;
1807 }
1808 if (imap_create_mailbox(adata, mbox) < 0)
1809 break;
1810 triedcreate = 1;
1811 }
1812 } while (rc == IMAP_EXEC_ERROR);
1813
1814 if (rc != 0)
1815 {
1816 imap_error("imap_copy_messages", adata->buf);
1817 goto out;
1818 }
1819
1820 /* cleanup */
1821 if (save_opt == SAVE_MOVE)
1822 {
1823 struct Email **ep = NULL;
1824 ARRAY_FOREACH(ep, ea)
1825 {
1826 struct Email *e = *ep;
1827 mutt_set_flag(m, e, MUTT_DELETE, true, true);
1828 mutt_set_flag(m, e, MUTT_PURGE, true, true);
1829 }
1830 }
1831
1832 rc = 0;
1833
1834out:
1835 FREE(&cmd.data);
1836 FREE(&sync_cmd.data);
1837
1838 return (rc < 0) ? -1 : rc;
1839}
#define ARRAY_EMPTY(head)
Check if an array is empty.
Definition: array.h:73
#define ARRAY_FREE(head)
Release all memory.
Definition: array.h:203
#define ARRAY_GET(head, idx)
Return the element at index.
Definition: array.h:108
#define ARRAY_HEAD_INITIALIZER
Static initializer for arrays.
Definition: array.h:57
struct Buffer * buf_init(struct Buffer *buf)
Initialise a new Buffer.
Definition: buffer.c:55
@ 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:1304
int imap_parse_path(const char *path, struct ConnAccount *cac, char *mailbox, size_t mailboxlen)
Parse an IMAP mailbox name into ConnAccount, name.
Definition: util.c:471
static int emails_to_uid_array(struct EmailArray *ea, struct UidArray *uida)
Extract IMAP UIDs from Emails.
Definition: message.c:1643
#define IMAP_CMD_NO_FLAGS
No flags are set.
Definition: private.h:71
@ IMAP_EXEC_SUCCESS
Imap command executed or queued successfully.
Definition: private.h:82
@ IMAP_EXEC_ERROR
Imap command failure.
Definition: private.h:83
char * imap_fix_path(char delim, const char *mailbox, char *path, size_t plen)
Fix up the imap path.
Definition: util.c:676
bool imap_account_match(const struct ConnAccount *a1, const struct ConnAccount *a2)
Compare two Accounts.
Definition: util.c:1051
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:918
#define IMAP_CMD_QUEUE
Queue a command, do not execute.
Definition: private.h:73
char * imap_get_qualifier(char *buf)
Get the qualifier from a tagged response.
Definition: util.c:765
int imap_create_mailbox(struct ImapAccountData *adata, const char *mailbox)
Create a new mailbox.
Definition: imap.c:429
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:918
@ LL_DEBUG3
Log at debug level 3.
Definition: logging2.h:45
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox's path string.
Definition: mailbox.h:210
int imap_exec_msg_set(struct ImapAccountData *adata, const char *pre, const char *post, struct UidArray *uida)
Execute a command using a set of UIDs.
Definition: msg_set.c:132
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:653
@ MUTT_PURGE
Messages to be purged (bypass trash)
Definition: mutt.h:76
@ MUTT_DELETE
Messages to be deleted.
Definition: mutt.h:74
#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
enum QuadOption query_yesorno_help(const char *prompt, enum QuadOption def, struct ConfigSubset *sub, const char *name)
Ask the user a Yes/No question offering help.
Definition: question.c:346
Login details for a remote server.
Definition: connaccount.h:53
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 1848 of file message.c.

1849{
1851 struct ImapMboxData *mdata = imap_mdata_get(m);
1852
1853 if (!e || !adata || (adata->mailbox != m))
1854 return -1;
1855
1856 mdata->bcache = imap_bcache_open(m);
1857 char id[64] = { 0 };
1858 snprintf(id, sizeof(id), "%u-%u", mdata->uidvalidity, imap_edata_get(e)->uid);
1859 return mutt_bcache_del(mdata->bcache, id);
1860}
int mutt_bcache_del(struct BodyCache *bcache, const char *id)
Delete a file from the Body Cache.
Definition: bcache.c:266
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_cache_clean()

int imap_cache_clean ( struct Mailbox m)

Delete all the entries in the message cache.

Parameters
mSelectedImap Mailbox
Return values
0Always

Definition at line 1867 of file message.c.

1868{
1870 struct ImapMboxData *mdata = imap_mdata_get(m);
1871
1872 if (!adata || (adata->mailbox != m))
1873 return -1;
1874
1875 mdata->bcache = imap_bcache_open(m);
1877
1878 return 0;
1879}
int mutt_bcache_list(struct BodyCache *bcache, bcache_list_t want_id, void *data)
Find matching entries in the Body Cache.
Definition: bcache.c:331
static int 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 1899 of file message.c.

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