NeoMutt  2021-10-29-43-g6b8931
Teaching an old dog new tricks
DOXYGEN
message.c File Reference

Manage IMAP messages. More...

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

Go to the source code of this file.

Functions

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

Detailed Description

Manage IMAP messages.

Authors
  • Brandon Long
  • Brendan Cully
  • Richard Russon

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

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

Definition in file message.c.

Function Documentation

◆ msg_cache_open()

static struct BodyCache* msg_cache_open ( struct Mailbox m)
static

Open a message cache.

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

Definition at line 79 of file message.c.

80 {
82  struct ImapMboxData *mdata = imap_mdata_get(m);
83 
84  if (!adata || (adata->mailbox != m))
85  return NULL;
86 
87  if (mdata->bcache)
88  return mdata->bcache;
89 
90  struct Buffer *mailbox = mutt_buffer_pool_get();
91  imap_cachepath(adata->delim, mdata->name, mailbox);
92 
93  struct BodyCache *bc =
94  mutt_bcache_open(&adata->conn->account, mutt_buffer_string(mailbox));
95  mutt_buffer_pool_release(&mailbox);
96 
97  return bc;
98 }
struct BodyCache * mutt_bcache_open(struct ConnAccount *account, const char *mailbox)
Open an Email-Body Cache.
Definition: bcache.c:143
static const char * mutt_buffer_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:77
struct ImapAccountData * imap_adata_get(struct Mailbox *m)
Get the Account data for this mailbox.
Definition: adata.c:90
struct ImapMboxData * imap_mdata_get(struct Mailbox *m)
Get the Mailbox data for this mailbox.
Definition: mdata.c:59
void imap_cachepath(char delim, const char *mailbox, struct Buffer *dest)
Generate a cache path for a mailbox.
Definition: util.c:719
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:136
+ 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 107 of file message.c.

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

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

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

192 {
193  struct ImapEmailData *edata = h->edata;
194 
195  /* sanity-check string */
196  size_t plen = mutt_istr_startswith(s, "FLAGS");
197  if (plen == 0)
198  {
199  mutt_debug(LL_DEBUG1, "not a FLAGS response: %s\n", s);
200  return NULL;
201  }
202  s += plen;
203  SKIPWS(s);
204  if (*s != '(')
205  {
206  mutt_debug(LL_DEBUG1, "bogus FLAGS response: %s\n", s);
207  return NULL;
208  }
209  s++;
210 
211  FREE(&edata->flags_system);
212  FREE(&edata->flags_remote);
213 
214  edata->deleted = false;
215  edata->flagged = false;
216  edata->replied = false;
217  edata->read = false;
218  edata->old = false;
219 
220  /* start parsing */
221  while (*s && (*s != ')'))
222  {
223  if ((plen = mutt_istr_startswith(s, "\\deleted")))
224  {
225  s += plen;
226  edata->deleted = true;
227  }
228  else if ((plen = mutt_istr_startswith(s, "\\flagged")))
229  {
230  s += plen;
231  edata->flagged = true;
232  }
233  else if ((plen = mutt_istr_startswith(s, "\\answered")))
234  {
235  s += plen;
236  edata->replied = true;
237  }
238  else if ((plen = mutt_istr_startswith(s, "\\seen")))
239  {
240  s += plen;
241  edata->read = true;
242  }
243  else if ((plen = mutt_istr_startswith(s, "\\recent")))
244  {
245  s += plen;
246  }
247  else if ((plen = mutt_istr_startswith(s, "old")))
248  {
249  s += plen;
250  edata->old = cs_subset_bool(NeoMutt->sub, "mark_old");
251  }
252  else
253  {
254  char ctmp;
255  char *flag_word = s;
256  bool is_system_keyword = mutt_istr_startswith(s, "\\");
257 
258  while (*s && !IS_SPACE(*s) && (*s != ')'))
259  s++;
260 
261  ctmp = *s;
262  *s = '\0';
263 
264  /* store other system flags as well (mainly \\Draft) */
265  if (is_system_keyword)
266  mutt_str_append_item(&edata->flags_system, flag_word, ' ');
267  /* store custom flags as well */
268  else
269  mutt_str_append_item(&edata->flags_remote, flag_word, ' ');
270 
271  *s = ctmp;
272  }
273  SKIPWS(s);
274  }
275 
276  /* wrap up, or note bad flags response */
277  if (*s == ')')
278  s++;
279  else
280  {
281  mutt_debug(LL_DEBUG1, "Unterminated FLAGS response: %s\n", s);
282  return NULL;
283  }
284 
285  return s;
286 }
#define mutt_debug(LEVEL,...)
Definition: logging.h:84
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
@ LL_DEBUG1
Log at debug level 1.
Definition: logging.h:40
#define FREE(x)
Definition: memory.h:40
void mutt_str_append_item(char **str, const char *item, char sep)
Add string to another separated by sep.
Definition: string.c:277
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition: string.c:170
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];
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';
341  h->received = mutt_date_parse_imap(tmp);
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") ||
355  mutt_istr_startswith(s, "RFC822.HEADER"))
356  {
357  /* handle above, in msg_fetch_header */
358  return -2;
359  }
360  else if ((plen = mutt_istr_startswith(s, "MODSEQ")))
361  {
362  s += plen;
363  SKIPWS(s);
364  if (*s != '(')
365  {
366  mutt_debug(LL_DEBUG1, "bogus MODSEQ response: %s\n", s);
367  return -1;
368  }
369  s++;
370  while (*s && (*s != ')'))
371  s++;
372  if (*s == ')')
373  s++;
374  else
375  {
376  mutt_debug(LL_DEBUG1, "Unterminated MODSEQ response: %s\n", s);
377  return -1;
378  }
379  }
380  else if (*s == ')')
381  s++; /* end of request */
382  else if (*s)
383  {
384  /* got something i don't understand */
385  imap_error("msg_parse_fetch", s);
386  return -1;
387  }
388  }
389 
390  return 0;
391 }
const char * mutt_str_atol(const char *str, long *dst)
Convert ASCII string to a long.
Definition: atoi.c:135
const char * mutt_str_atoui(const char *str, unsigned int *dst)
Convert ASCII string to an unsigned integer.
Definition: atoi.c:202
time_t mutt_date_parse_imap(const char *s)
Parse date of the form: DD-MMM-YYYY HH:MM:SS +ZZzz.
Definition: date.c:600
static char * msg_parse_flags(struct ImapHeader *h, char *s)
Read a FLAGS token into an ImapHeader.
Definition: message.c:191
char * imap_next_word(char *s)
Find where the next IMAP word begins.
Definition: util.c:792
void imap_error(const char *where, const char *msg)
Show an error and abort.
Definition: util.c:665
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 405 of file message.c.

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

Definition at line 469 of file message.c.

470 {
471  buf[*len] = '\0';
472  int rc = mutt_socket_write_n(conn, buf, *len);
473  *len = 0;
474  return rc;
475 }
#define mutt_socket_write_n(conn, buf, len)
Definition: mutt_socket.h:39
+ Here is the caller graph for this function:

◆ query_abort_header_download()

static bool query_abort_header_download ( struct ImapAccountData adata)
static

Ask the user whether to abort the download.

Parameters
adataImap Account data
Return values
trueAbort the download

If the user hits ctrl-c during an initial header download for a mailbox, prompt whether to completely abort the download and close the mailbox.

Definition at line 485 of file message.c.

486 {
487  bool abort = false;
488 
489  mutt_flushinp();
490  /* L10N: This prompt is made if the user hits Ctrl-C when opening an IMAP mailbox */
491  if (mutt_yesorno(_("Abort download and close mailbox?"), MUTT_YES) == MUTT_YES)
492  {
493  abort = true;
494  imap_close_connection(adata);
495  }
496  SigInt = false;
497 
498  return abort;
499 }
void mutt_flushinp(void)
Empty all the keyboard buffers.
Definition: curs_lib.c:673
void imap_close_connection(struct ImapAccountData *adata)
Close an IMAP connection.
Definition: imap.c:853
#define _(a)
Definition: message.h:28
SIG_ATOMIC_VOLATILE_T SigInt
true after SIGINT is received
Definition: mutt_globals.h:73
@ 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:182
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_alloc_uid_hash()

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

Create a Hash Table for the UIDs.

Parameters
adataImap Account data
msn_countNumber of MSNs in use

This function is run after imap_imap_msn_reserve, so we skip the malicious msn_count size check.

Definition at line 509 of file message.c.

510 {
511  struct ImapMboxData *mdata = adata->mailbox->mdata;
512  if (!mdata->uid_hash)
513  mdata->uid_hash = mutt_hash_int_new(MAX(6 * msn_count / 5, 30), MUTT_HASH_NO_FLAGS);
514 }
struct HashTable * mutt_hash_int_new(size_t num_elems, HashFlags flags)
Create a new Hash Table (with integer keys)
Definition: hash.c:285
#define MUTT_HASH_NO_FLAGS
No flags are set.
Definition: hash.h:109
#define MAX(a, b)
Definition: memory.h:30
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_fetch_msn_seqset()

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

Generate a sequence set.

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

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

Definition at line 528 of file message.c.

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

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

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

806 {
807  int rc;
808  unsigned int uid = 0;
809 
810  mutt_debug(LL_DEBUG2, "Reading uid seqset from header cache\n");
811  struct Mailbox *m = adata->mailbox;
812  struct ImapMboxData *mdata = adata->mailbox->mdata;
813  unsigned int msn = 1;
814 
815  if (m->verbose)
816  mutt_message(_("Evaluating cache..."));
817 
818  struct SeqsetIterator *iter = mutt_seqset_iterator_new(uid_seqset);
819  if (!iter)
820  return -1;
821 
822  while ((rc = mutt_seqset_iterator_next(iter, &uid)) == 0)
823  {
824  /* The seqset may contain more headers than the fetch request, so
825  * we need to watch and reallocate the context and msn_index */
826  imap_msn_reserve(&mdata->msn, msn);
827 
828  struct Email *e = imap_hcache_get(mdata, uid);
829  if (e)
830  {
831  imap_msn_set(&mdata->msn, msn - 1, e);
832 
833  if (m->msg_count >= m->email_max)
834  mx_alloc_memory(m);
835 
836  struct ImapEmailData *edata = imap_edata_new();
837  e->edata = edata;
839 
840  e->index = uid;
841  e->active = true;
842  e->changed = false;
843  edata->read = e->read;
844  edata->old = e->old;
845  edata->deleted = e->deleted;
846  edata->flagged = e->flagged;
847  edata->replied = e->replied;
848 
849  edata->msn = msn;
850  edata->uid = uid;
851  mutt_hash_int_insert(mdata->uid_hash, uid, e);
852 
853  mailbox_size_add(m, e);
854  m->emails[m->msg_count++] = e;
855 
856  msn++;
857  }
858  }
859 
861 
862  return rc;
863 }
#define mutt_message(...)
Definition: logging.h:86
int mutt_seqset_iterator_next(struct SeqsetIterator *iter, unsigned int *next)
Get the next UID from a Sequence Set.
Definition: util.c:1102
void mutt_seqset_iterator_free(struct SeqsetIterator **ptr)
Free a Sequence Set Iterator.
Definition: util.c:1159
struct SeqsetIterator * mutt_seqset_iterator_new(const char *seqset)
Create a new Sequence Set Iterator.
Definition: util.c:1081
void imap_msn_reserve(struct MSN *msn, size_t num)
Create / reallocate the cache.
Definition: msn.c:40
void mx_alloc_memory(struct Mailbox *m)
Create storage for the emails.
Definition: mx.c:1217
int email_max
Number of pointers in emails.
Definition: mailbox.h:100
UID Sequence Set Iterator.
Definition: private.h:170
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ read_headers_condstore_qresync_updates()

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

Retrieve updates from the server.

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

CONDSTORE and QRESYNC use FETCH extensions to grab updates.

Definition at line 877 of file message.c.

880 {
881  struct Progress *progress = NULL;
882  char buf[1024];
883  unsigned int header_msn = 0;
884 
885  struct Mailbox *m = adata->mailbox;
886  struct ImapMboxData *mdata = imap_mdata_get(m);
887 
888  if (m->verbose)
889  {
890  /* L10N: Fetching IMAP flag changes, using the CONDSTORE extension */
891  progress = progress_new(_("Fetching flag updates..."), MUTT_PROGRESS_READ, msn_end);
892  }
893 
894  snprintf(buf, sizeof(buf), "UID FETCH 1:%u (FLAGS) (CHANGEDSINCE %llu%s)",
895  uid_next - 1, hc_modseq, eval_qresync ? " VANISHED" : "");
896 
897  imap_cmd_start(adata, buf);
898 
899  int rc = IMAP_RES_CONTINUE;
900  for (int msgno = 1; rc == IMAP_RES_CONTINUE; msgno++)
901  {
902  if (SigInt && query_abort_header_download(adata))
903  goto fail;
904 
905  if (m->verbose)
906  progress_update(progress, msgno, -1);
907 
908  /* cmd_parse_fetch will update the flags */
909  rc = imap_cmd_step(adata);
910  if (rc != IMAP_RES_CONTINUE)
911  break;
912 
913  /* so we just need to grab the header and persist it back into
914  * the header cache */
915  char *fetch_buf = adata->buf;
916  if (fetch_buf[0] != '*')
917  continue;
918 
919  fetch_buf = imap_next_word(fetch_buf);
920  if (!isdigit((unsigned char) *fetch_buf) || !mutt_str_atoui(fetch_buf, &header_msn))
921  continue;
922 
923  if ((header_msn < 1) || (header_msn > msn_end) ||
924  !imap_msn_get(&mdata->msn, header_msn - 1))
925  {
926  mutt_debug(LL_DEBUG1, "skipping CONDSTORE flag update for unknown message number %u\n",
927  header_msn);
928  continue;
929  }
930 
931  imap_hcache_put(mdata, imap_msn_get(&mdata->msn, header_msn - 1));
932  }
933 
934  if (rc != IMAP_RES_OK)
935  goto fail;
936 
937  /* The IMAP flag setting as part of cmd_parse_fetch() ends up
938  * flipping these on. */
939  mdata->check_status &= ~IMAP_FLAGS_PENDING;
940  m->changed = false;
941 
942  /* VANISHED handling: we need to empty out the messages */
943  if (mdata->reopen & IMAP_EXPUNGE_PENDING)
944  {
947 
948  imap_hcache_open(adata, mdata);
949  mdata->reopen &= ~IMAP_EXPUNGE_PENDING;
950  }
951 
952  /* undo expunge count updates.
953  * ctx_update() will do this at the end of the header fetch. */
954  m->vcount = 0;
955  m->msg_tagged = 0;
956  m->msg_deleted = 0;
957  m->msg_new = 0;
958  m->msg_unread = 0;
959  m->msg_flagged = 0;
960  m->changed = false;
961 
962  rc = 0;
963 fail:
964  progress_free(&progress);
965  return rc;
966 }
#define IMAP_EXPUNGE_PENDING
Messages on the server have been expunged.
Definition: private.h:68
#define IMAP_FLAGS_PENDING
Flags have changed on the server.
Definition: private.h:70
void imap_hcache_close(struct ImapMboxData *mdata)
Close the header cache.
Definition: util.c:340
void imap_hcache_open(struct ImapAccountData *adata, struct ImapMboxData *mdata)
Open a header cache.
Definition: util.c:297
void imap_expunge_mailbox(struct Mailbox *m)
Purge messages from the server.
Definition: imap.c:676
int vcount
The number of virtual messages.
Definition: mailbox.h:102
bool changed
Mailbox has been modified.
Definition: mailbox.h:114
int msg_new
Number of new messages.
Definition: mailbox.h:95
int msg_deleted
Number of deleted messages.
Definition: mailbox.h:96
int msg_flagged
Number of flagged messages.
Definition: mailbox.h:93
int msg_tagged
How many messages are tagged?
Definition: mailbox.h:97
int msg_unread
Number of unread messages.
Definition: mailbox.h:92
+ 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 976 of file message.c.

977 {
978  assert(m);
979  struct ImapAccountData *adata = imap_adata_get(m);
980  struct ImapMboxData *mdata = imap_mdata_get(m);
981  if (!adata || (adata->mailbox != m))
982  return -1;
983 
984  const size_t max_msn = imap_msn_highest(&mdata->msn);
985 
986  unsigned int msn;
987  unsigned int uid;
988  struct Email *e = NULL;
989  struct Email *uidh = NULL;
990 
991  for (int i = 0; i < m->msg_count; i++)
992  {
993  e = m->emails[i];
994  const struct ImapEmailData *edata = imap_edata_get(e);
995  if (!edata)
996  goto fail;
997 
998  msn = imap_edata_get(e)->msn;
999  uid = imap_edata_get(e)->uid;
1000 
1001  if ((msn < 1) || (msn > max_msn) || imap_msn_get(&mdata->msn, msn - 1) != e)
1002  goto fail;
1003 
1004  uidh = (struct Email *) mutt_hash_int_find(mdata->uid_hash, uid);
1005  if (uidh != e)
1006  goto fail;
1007  }
1008 
1009  return 0;
1010 
1011 fail:
1012  imap_msn_free(&mdata->msn);
1013  mutt_hash_free(&mdata->uid_hash);
1014 
1015  for (int i = 0; i < m->msg_count; i++)
1016  {
1017  if (m->emails[i] && m->emails[i]->edata)
1018  imap_edata_free(&m->emails[i]->edata);
1019  email_free(&m->emails[i]);
1020  }
1021  m->msg_count = 0;
1022  mutt_hcache_delete_record(mdata->hcache, "/MODSEQ", 7);
1024  imap_hcache_close(mdata);
1025 
1026  if (m->verbose)
1027  {
1028  /* L10N: After opening an IMAP mailbox using QRESYNC, Mutt performs a quick
1029  sanity check. If that fails, Mutt reopens the mailbox using a normal
1030  download. */
1031  mutt_error(_("QRESYNC failed. Reopening mailbox."));
1032  }
1033  return -1;
1034 }
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:639
int imap_hcache_clear_uid_seqset(struct ImapMboxData *mdata)
Delete a UID Sequence Set from the header cache.
Definition: util.c:438
void imap_msn_free(struct MSN *msn)
Free the cache.
Definition: msn.c:58
size_t imap_msn_highest(const struct MSN *msn)
Return the highest MSN in use.
Definition: msn.c:68
struct HeaderCache * hcache
Email header cache.
Definition: mdata.h:62
+ 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 1049 of file message.c.

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

◆ imap_read_headers()

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

Read headers from the server.

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

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

Definition at line 1298 of file message.c.

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

◆ imap_append_message()

int imap_append_message ( struct Mailbox m,
struct Message msg 
)

Write an email back to the server.

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

Definition at line 1510 of file message.c.

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

1644 {
1645  if (!m || !el || !dest)
1646  return -1;
1647 
1648  struct Buffer cmd, sync_cmd;
1649  char buf[PATH_MAX];
1650  char mbox[PATH_MAX];
1651  char mmbox[PATH_MAX];
1652  char prompt[PATH_MAX + 64];
1653  int rc;
1654  struct ConnAccount cac = { { 0 } };
1655  enum QuadOption err_continue = MUTT_NO;
1656  int triedcreate = 0;
1657  struct EmailNode *en = STAILQ_FIRST(el);
1658  bool single = !STAILQ_NEXT(en, entries);
1659  struct ImapAccountData *adata = imap_adata_get(m);
1660 
1661  if (single && en->email->attach_del)
1662  {
1663  mutt_debug(LL_DEBUG3, "#1 Message contains attachments to be deleted\n");
1664  return 1;
1665  }
1666 
1667  if (imap_parse_path(dest, &cac, buf, sizeof(buf)))
1668  {
1669  mutt_debug(LL_DEBUG1, "bad destination %s\n", dest);
1670  return -1;
1671  }
1672 
1673  /* check that the save-to folder is in the same account */
1674  if (!imap_account_match(&adata->conn->account, &cac))
1675  {
1676  mutt_debug(LL_DEBUG3, "%s not same server as %s\n", dest, mailbox_path(m));
1677  return 1;
1678  }
1679 
1680  imap_fix_path(adata->delim, buf, mbox, sizeof(mbox));
1681  if (*mbox == '\0')
1682  mutt_str_copy(mbox, "INBOX", sizeof(mbox));
1683  imap_munge_mbox_name(adata->unicode, mmbox, sizeof(mmbox), mbox);
1684 
1685  /* loop in case of TRYCREATE */
1686  do
1687  {
1688  mutt_buffer_init(&sync_cmd);
1689  mutt_buffer_init(&cmd);
1690 
1691  if (!single) /* copy tagged messages */
1692  {
1693  /* if any messages have attachments to delete, fall through to FETCH
1694  * and APPEND. TODO: Copy what we can with COPY, fall through for the
1695  * remainder. */
1696  STAILQ_FOREACH(en, el, entries)
1697  {
1698  if (en->email->attach_del)
1699  {
1701  "#2 Message contains attachments to be deleted\n");
1702  return 1;
1703  }
1704 
1705  if (en->email->active && en->email->changed)
1706  {
1707  rc = imap_sync_message_for_copy(m, en->email, &sync_cmd, &err_continue);
1708  if (rc < 0)
1709  {
1710  mutt_debug(LL_DEBUG1, "#1 could not sync\n");
1711  goto out;
1712  }
1713  }
1714  }
1715 
1716  rc = imap_exec_msgset(m, "UID COPY", mmbox, MUTT_TAG, false, false);
1717  if (rc == 0)
1718  {
1719  mutt_debug(LL_DEBUG1, "No messages tagged\n");
1720  rc = -1;
1721  goto out;
1722  }
1723  else if (rc < 0)
1724  {
1725  mutt_debug(LL_DEBUG1, "#1 could not queue copy\n");
1726  goto out;
1727  }
1728  else
1729  {
1730  mutt_message(ngettext("Copying %d message to %s...", "Copying %d messages to %s...", rc),
1731  rc, mbox);
1732  }
1733  }
1734  else
1735  {
1736  mutt_message(_("Copying message %d to %s..."), en->email->index + 1, mbox);
1737  mutt_buffer_add_printf(&cmd, "UID COPY %u %s", imap_edata_get(en->email)->uid, mmbox);
1738 
1739  if (en->email->active && en->email->changed)
1740  {
1741  rc = imap_sync_message_for_copy(m, en->email, &sync_cmd, &err_continue);
1742  if (rc < 0)
1743  {
1744  mutt_debug(LL_DEBUG1, "#2 could not sync\n");
1745  goto out;
1746  }
1747  }
1748  rc = imap_exec(adata, cmd.data, IMAP_CMD_QUEUE);
1749  if (rc != IMAP_EXEC_SUCCESS)
1750  {
1751  mutt_debug(LL_DEBUG1, "#2 could not queue copy\n");
1752  goto out;
1753  }
1754  }
1755 
1756  /* let's get it on */
1757  rc = imap_exec(adata, NULL, IMAP_CMD_NO_FLAGS);
1758  if (rc == IMAP_EXEC_ERROR)
1759  {
1760  if (triedcreate)
1761  {
1762  mutt_debug(LL_DEBUG1, "Already tried to create mailbox %s\n", mbox);
1763  break;
1764  }
1765  /* bail out if command failed for reasons other than nonexistent target */
1766  if (!mutt_istr_startswith(imap_get_qualifier(adata->buf), "[TRYCREATE]"))
1767  break;
1768  mutt_debug(LL_DEBUG3, "server suggests TRYCREATE\n");
1769  snprintf(prompt, sizeof(prompt), _("Create %s?"), mbox);
1770  const bool c_confirm_create =
1771  cs_subset_bool(NeoMutt->sub, "confirm_create");
1772  if (c_confirm_create && (mutt_yesorno(prompt, MUTT_YES) != MUTT_YES))
1773  {
1774  mutt_clear_error();
1775  goto out;
1776  }
1777  if (imap_create_mailbox(adata, mbox) < 0)
1778  break;
1779  triedcreate = 1;
1780  }
1781  } while (rc == IMAP_EXEC_ERROR);
1782 
1783  if (rc != 0)
1784  {
1785  imap_error("imap_copy_messages", adata->buf);
1786  goto out;
1787  }
1788 
1789  /* cleanup */
1790  if (save_opt == SAVE_MOVE)
1791  {
1792  const bool c_delete_untag = cs_subset_bool(NeoMutt->sub, "delete_untag");
1793  STAILQ_FOREACH(en, el, entries)
1794  {
1795  mutt_set_flag(m, en->email, MUTT_DELETE, true);
1796  mutt_set_flag(m, en->email, MUTT_PURGE, true);
1797  if (c_delete_untag)
1798  mutt_set_flag(m, en->email, MUTT_TAG, false);
1799  }
1800  }
1801 
1802  rc = 0;
1803 
1804 out:
1805  FREE(&cmd.data);
1806  FREE(&sync_cmd.data);
1807 
1808  return (rc < 0) ? -1 : rc;
1809 }
struct Buffer * mutt_buffer_init(struct Buffer *buf)
Initialise a new Buffer.
Definition: buffer.c:46
@ SAVE_MOVE
Move message to another mailbox, removing the original.
Definition: commands.h:51
int imap_exec(struct ImapAccountData *adata, const char *cmdstr, ImapCmdFlags flags)
Execute a command and wait for the response from the server.
Definition: command.c:1255
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:483
char * imap_fix_path(char delim, const char *mailbox, char *path, size_t plen)
Fix up the imap path.
Definition: util.c:687
#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
bool imap_account_match(const struct ConnAccount *a1, const struct ConnAccount *a2)
Compare two Accounts.
Definition: util.c:1049
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:914
char * imap_get_qualifier(char *buf)
Get the qualifier from a tagged response.
Definition: util.c:775
#define IMAP_CMD_QUEUE
Queue a command, do not execute.
Definition: private.h:75
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:927
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:1004
int imap_create_mailbox(struct ImapAccountData *adata, char *mailbox)
Create a new mailbox.
Definition: imap.c:448
@ 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:215
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:560
@ MUTT_PURGE
Messages to be purged (bypass trash)
Definition: mutt.h:96
@ MUTT_TAG
Tagged messages.
Definition: mutt.h:99
@ MUTT_DELETE
Messages to be deleted.
Definition: mutt.h:94
#define PATH_MAX
Definition: mutt.h:40
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
Definition: mutt_logging.c:113
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:99
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_cache_del()

int imap_cache_del ( struct Mailbox m,
struct Email e 
)

Delete an email from the body cache.

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

Definition at line 1818 of file message.c.

1819 {
1820  struct ImapAccountData *adata = imap_adata_get(m);
1821  struct ImapMboxData *mdata = imap_mdata_get(m);
1822 
1823  if (!e || !adata || (adata->mailbox != m))
1824  return -1;
1825 
1826  mdata->bcache = msg_cache_open(m);
1827  char id[64];
1828  snprintf(id, sizeof(id), "%u-%u", mdata->uidvalidity, imap_edata_get(e)->uid);
1829  return mutt_bcache_del(mdata->bcache, id);
1830 }
int mutt_bcache_del(struct BodyCache *bcache, const char *id)
Delete a file from the Body Cache.
Definition: bcache.c:264
+ 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 1837 of file message.c.

1838 {
1839  struct ImapAccountData *adata = imap_adata_get(m);
1840  struct ImapMboxData *mdata = imap_mdata_get(m);
1841 
1842  if (!adata || (adata->mailbox != m))
1843  return -1;
1844 
1845  mdata->bcache = msg_cache_open(m);
1847 
1848  return 0;
1849 }
int mutt_bcache_list(struct BodyCache *bcache, bcache_list_t want_id, void *data)
Find matching entries in the Body Cache.
Definition: bcache.c:329
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:168
+ Here is the call 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 1869 of file message.c.

1870 {
1871  struct ImapAccountData *adata = imap_adata_get(m);
1872  if (!adata || (adata->mailbox != m))
1873  return NULL;
1874 
1875  struct ImapHeader newh = { 0 };
1876  struct ImapEmailData old_edata = { 0 };
1877  int local_changes = e->changed;
1878 
1879  struct ImapEmailData *edata = e->edata;
1880  newh.edata = edata;
1881 
1882  mutt_debug(LL_DEBUG2, "parsing FLAGS\n");
1883  s = msg_parse_flags(&newh, s);
1884  if (!s)
1885  return NULL;
1886 
1887  /* Update tags system */
1888  /* We take a copy of the tags so we can split the string */
1889  char *tags_copy = mutt_str_dup(edata->flags_remote);
1890  driver_tags_replace(&e->tags, tags_copy);
1891  FREE(&tags_copy);
1892 
1893  /* YAUH (yet another ugly hack): temporarily set context to
1894  * read-write even if it's read-only, so *server* updates of
1895  * flags can be processed by mutt_set_flag. mailbox->changed must
1896  * be restored afterwards */
1897  bool readonly = m->readonly;
1898  m->readonly = false;
1899 
1900  /* This is redundant with the following two checks. Removing:
1901  * mutt_set_flag (m, e, MUTT_NEW, !(edata->read || edata->old)); */
1902  set_changed_flag(m, e, local_changes, server_changes, MUTT_OLD, old_edata.old,
1903  edata->old, e->old);
1904  set_changed_flag(m, e, local_changes, server_changes, MUTT_READ,
1905  old_edata.read, edata->read, e->read);
1906  set_changed_flag(m, e, local_changes, server_changes, MUTT_DELETE,
1907  old_edata.deleted, edata->deleted, e->deleted);
1908  set_changed_flag(m, e, local_changes, server_changes, MUTT_FLAG,
1909  old_edata.flagged, edata->flagged, e->flagged);
1910  set_changed_flag(m, e, local_changes, server_changes, MUTT_REPLIED,
1911  old_edata.replied, edata->replied, e->replied);
1912 
1913  /* this message is now definitively *not* changed (mutt_set_flag
1914  * marks things changed as a side-effect) */
1915  if (local_changes == 0)
1916  e->changed = false;
1917  m->changed &= !readonly;
1918  m->readonly = readonly;
1919 
1920  return s;
1921 }
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:620
@ MUTT_READ
Messages that have been read.
Definition: mutt.h:92
@ MUTT_OLD
Old messages.
Definition: mutt.h:90
@ MUTT_FLAG
Flagged messages.
Definition: mutt.h:98
@ MUTT_REPLIED
Messages that have been replied to.
Definition: mutt.h:91
bool 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:119
+ Here is the call graph for this function:
+ Here is the caller graph for this function: