NeoMutt  2022-04-29-145-g9b6a0e
Teaching an old dog new tricks
DOXYGEN
notmuch.c File Reference

Notmuch virtual mailbox type. More...

#include "config.h"
#include <errno.h>
#include <limits.h>
#include <notmuch.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "private.h"
#include "mutt/lib.h"
#include "config/lib.h"
#include "email/lib.h"
#include "core/lib.h"
#include "mutt.h"
#include "lib.h"
#include "enter/lib.h"
#include "hcache/lib.h"
#include "index/lib.h"
#include "maildir/lib.h"
#include "progress/lib.h"
#include "adata.h"
#include "command_parse.h"
#include "edata.h"
#include "maildir/edata.h"
#include "mdata.h"
#include "mutt_commands.h"
#include "mutt_globals.h"
#include "mutt_thread.h"
#include "mx.h"
#include "protos.h"
#include "query.h"
#include "tag.h"
#include <libintl.h>
+ Include dependency graph for notmuch.c:

Go to the source code of this file.

Functions

void nm_init (void)
 Setup feature commands. More...
 
static struct HeaderCachenm_hcache_open (struct Mailbox *m)
 Open a header cache. More...
 
static void nm_hcache_close (struct HeaderCache *h)
 Close the header cache. More...
 
static char * nm_get_default_url (void)
 Create a Mailbox with default Notmuch settings. More...
 
static struct NmMboxDatanm_get_default_data (void)
 Create a Mailbox with default Notmuch settings. More...
 
static int init_mailbox (struct Mailbox *m)
 Add Notmuch data to the Mailbox. More...
 
static char * email_get_id (struct Email *e)
 Get the unique Notmuch Id. More...
 
static char * email_get_fullpath (struct Email *e, char *buf, size_t buflen)
 Get the full path of an email. More...
 
static void query_window_reset (void)
 Restore vfolder's search window to its original position. More...
 
static bool windowed_query_from_query (const char *query, char *buf, size_t buflen)
 Transforms a vfolder search query into a windowed one. More...
 
static char * get_query_string (struct NmMboxData *mdata, bool window)
 Builds the notmuch vfolder search string. More...
 
static int get_limit (struct NmMboxData *mdata)
 Get the database limit. More...
 
static void apply_exclude_tags (notmuch_query_t *query)
 Exclude the configured tags. More...
 
static notmuch_query_t * get_query (struct Mailbox *m, bool writable)
 Create a new query. More...
 
static int update_email_tags (struct Email *e, notmuch_message_t *msg)
 Update the Email's tags from Notmuch. More...
 
static int update_message_path (struct Email *e, const char *path)
 Set the path for a message. More...
 
static char * get_folder_from_path (const char *path)
 Find an email's folder from its path. More...
 
static char * nm2mutt_message_id (const char *id)
 Converts notmuch message Id to neomutt message Id. More...
 
static int init_email (struct Email *e, const char *path, notmuch_message_t *msg)
 Set up an email's Notmuch data. More...
 
static const char * get_message_last_filename (notmuch_message_t *msg)
 Get a message's last filename. More...
 
static void progress_setup (struct Mailbox *m)
 Set up the Progress Bar. More...
 
static void nm_progress_update (struct Mailbox *m)
 Update the progress counter. More...
 
static struct Emailget_mutt_email (struct Mailbox *m, notmuch_message_t *msg)
 Get the Email of a Notmuch message. More...
 
static void append_message (struct HeaderCache *h, struct Mailbox *m, notmuch_message_t *msg, bool dedup)
 Associate a message. More...
 
static void append_replies (struct HeaderCache *h, struct Mailbox *m, notmuch_query_t *q, notmuch_message_t *top, bool dedup)
 Add all the replies to a given messages into the display. More...
 
static void append_thread (struct HeaderCache *h, struct Mailbox *m, notmuch_query_t *q, notmuch_thread_t *thread, bool dedup)
 Add each top level reply in the thread. More...
 
static notmuch_messages_t * get_messages (notmuch_query_t *query)
 Load messages for a query. More...
 
static bool read_mesgs_query (struct Mailbox *m, notmuch_query_t *q, bool dedup)
 Search for matching messages. More...
 
static notmuch_threads_t * get_threads (notmuch_query_t *query)
 Load threads for a query. More...
 
static bool read_threads_query (struct Mailbox *m, notmuch_query_t *q, bool dedup, int limit)
 Perform a query with threads. More...
 
static notmuch_message_t * get_nm_message (notmuch_database_t *db, struct Email *e)
 Find a Notmuch message. More...
 
static bool nm_message_has_tag (notmuch_message_t *msg, char *tag)
 Does a message have this tag? More...
 
static void sync_email_path_with_nm (struct Email *e, notmuch_message_t *msg)
 Synchronize Neomutt's Email path with notmuch. More...
 
static int update_tags (notmuch_message_t *msg, const char *tag_str)
 Update the tags on a message. More...
 
static int update_email_flags (struct Mailbox *m, struct Email *e, const char *tag_str)
 Update the Email's flags. More...
 
static int rename_maildir_filename (const char *old, char *buf, size_t buflen, struct Email *e)
 Rename a Maildir file. More...
 
static int remove_filename (struct Mailbox *m, const char *path)
 Delete a file. More...
 
static int rename_filename (struct Mailbox *m, const char *old_file, const char *new_file, struct Email *e)
 Rename the file. More...
 
static unsigned int count_query (notmuch_database_t *db, const char *qstr, int limit)
 Count the results of a query. More...
 
char * nm_email_get_folder (struct Email *e)
 Get the folder for a Email. More...
 
char * nm_email_get_folder_rel_db (struct Mailbox *m, struct Email *e)
 Get the folder for a Email from the same level as the notmuch database. More...
 
int nm_read_entire_thread (struct Mailbox *m, struct Email *e)
 Get the entire thread of an email. More...
 
char * nm_url_from_query (struct Mailbox *m, char *buf, size_t buflen)
 Turn a query into a URL. More...
 
bool nm_query_window_available (void)
 Are windowed queries enabled for use? More...
 
void nm_query_window_forward (void)
 Function to move the current search window forward in time. More...
 
void nm_query_window_backward (void)
 Function to move the current search window backward in time. More...
 
void nm_query_window_reset (void)
 Resets the vfolder window position to the present. More...
 
bool nm_message_is_still_queried (struct Mailbox *m, struct Email *e)
 Is a message still visible in the query? More...
 
int nm_update_filename (struct Mailbox *m, const char *old_file, const char *new_file, struct Email *e)
 Change the filename. More...
 
static enum MxStatus nm_mbox_check_stats (struct Mailbox *m, uint8_t flags)
 Check the Mailbox statistics - Implements MxOps::mbox_check_stats() -. More...
 
static struct Mailboxget_default_mailbox (void)
 Get Mailbox for notmuch without any parameters. More...
 
int nm_record_message (struct Mailbox *m, char *path, struct Email *e)
 Add a message to the Notmuch database. More...
 
int nm_get_all_tags (struct Mailbox *m, const char **tag_list, int *tag_count)
 Fill a list with all notmuch tags. More...
 
static bool nm_ac_owns_path (struct Account *a, const char *path)
 Check whether an Account owns a Mailbox path - Implements MxOps::ac_owns_path() -. More...
 
static bool nm_ac_add (struct Account *a, struct Mailbox *m)
 Add a Mailbox to an Account - Implements MxOps::ac_add() -. More...
 
static enum MxOpenReturns nm_mbox_open (struct Mailbox *m)
 Open a Mailbox - Implements MxOps::mbox_open() -. More...
 
static enum MxStatus nm_mbox_check (struct Mailbox *m)
 Check for new mail - Implements MxOps::mbox_check() -. More...
 
static enum MxStatus nm_mbox_sync (struct Mailbox *m)
 Save changes to the Mailbox - Implements MxOps::mbox_sync() -. More...
 
static enum MxStatus nm_mbox_close (struct Mailbox *m)
 Close a Mailbox - Implements MxOps::mbox_close() -. More...
 
static bool nm_msg_open (struct Mailbox *m, struct Message *msg, int msgno)
 Open an email message in a Mailbox - Implements MxOps::msg_open() -. More...
 
static int nm_msg_commit (struct Mailbox *m, struct Message *msg)
 Save changes to an email - Implements MxOps::msg_commit() -. More...
 
static int nm_msg_close (struct Mailbox *m, struct Message *msg)
 Close an email - Implements MxOps::msg_close() -. More...
 
static int nm_tags_edit (struct Mailbox *m, const char *tags, struct Buffer *buf)
 Prompt and validate new messages tags - Implements MxOps::tags_edit() -. More...
 
static int nm_tags_commit (struct Mailbox *m, struct Email *e, const char *buf)
 Save the tags to a message - Implements MxOps::tags_commit() -. More...
 
enum MailboxType nm_path_probe (const char *path, const struct stat *st)
 Is this a Notmuch Mailbox? - Implements MxOps::path_probe() -. More...
 
static int nm_path_canon (char *buf, size_t buflen)
 Canonicalise a Mailbox path - Implements MxOps::path_canon() -. More...
 
static int nm_path_pretty (char *buf, size_t buflen, const char *folder)
 Abbreviate a Mailbox path - Implements MxOps::path_pretty() -. More...
 
static int nm_path_parent (char *buf, size_t buflen)
 Find the parent of a Mailbox path - Implements MxOps::path_parent() -. More...
 

Variables

static const struct Command nm_commands []
 
const char NmUrlProtocol [] = "notmuch://"
 
const int NmUrlProtocolLen = sizeof(NmUrlProtocol) - 1
 
struct MxOps MxNotmuchOps
 Notmuch Mailbox - Implements MxOps -. More...
 

Detailed Description

Notmuch virtual mailbox type.

Authors
  • Karel Zak
  • Richard Russon
  • Kevin Velghe
  • Bernard 'Guyzmo' Pratz

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 notmuch.c.

Function Documentation

◆ nm_init()

void nm_init ( void  )

Setup feature commands.

Definition at line 99 of file notmuch.c.

100 {
102 }
#define COMMANDS_REGISTER(cmds)
Definition: mutt_commands.h:47
static const struct Command nm_commands[]
Definition: notmuch.c:86
+ Here is the caller graph for this function:

◆ nm_hcache_open()

static struct HeaderCache* nm_hcache_open ( struct Mailbox m)
static

Open a header cache.

Parameters
mMailbox
Return values
ptrHeader cache handle

Definition at line 109 of file notmuch.c.

110 {
111 #ifdef USE_HCACHE
112  const char *const c_header_cache = cs_subset_path(NeoMutt->sub, "header_cache");
113  return mutt_hcache_open(c_header_cache, mailbox_path(m), NULL);
114 #else
115  return NULL;
116 #endif
117 }
const char * cs_subset_path(const struct ConfigSubset *sub, const char *name)
Get a path config item by name.
Definition: helpers.c:194
struct HeaderCache * mutt_hcache_open(const char *path, const char *folder, hcache_namer_t namer)
Multiplexor for StoreOps::open.
Definition: hcache.c:332
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox's path string.
Definition: mailbox.h:211
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:

◆ nm_hcache_close()

static void nm_hcache_close ( struct HeaderCache h)
static

Close the header cache.

Parameters
hHeader cache handle

Definition at line 123 of file notmuch.c.

124 {
125 #ifdef USE_HCACHE
127 #endif
128 }
void mutt_hcache_close(struct HeaderCache *hc)
Multiplexor for StoreOps::close.
Definition: hcache.c:432
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ nm_get_default_url()

static char* nm_get_default_url ( void  )
static

Create a Mailbox with default Notmuch settings.

Return values
ptrMailbox with default Notmuch settings
NULLError, it's impossible to create an NmMboxData

Definition at line 135 of file notmuch.c.

136 {
137  // path to DB + query + url "decoration"
138  size_t len = PATH_MAX + 1024 + 32;
139  char *url = mutt_mem_malloc(len);
140 
141  // Try to use `$nm_default_url` or `$folder`.
142  // If neither are set, it is impossible to create a Notmuch URL.
143  const char *const c_nm_default_url = cs_subset_string(NeoMutt->sub, "nm_default_url");
144  const char *const c_folder = cs_subset_string(NeoMutt->sub, "folder");
145  if (c_nm_default_url)
146  snprintf(url, len, "%s", c_nm_default_url);
147  else if (c_folder)
148  snprintf(url, len, "notmuch://%s", c_folder);
149  else
150  {
151  FREE(&url);
152  return NULL;
153  }
154 
155  return url;
156 }
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:317
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
#define FREE(x)
Definition: memory.h:43
#define PATH_MAX
Definition: mutt.h:40
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ nm_get_default_data()

static struct NmMboxData* nm_get_default_data ( void  )
static

Create a Mailbox with default Notmuch settings.

Return values
ptrMailbox with default Notmuch settings
NULLError, it's impossible to create an NmMboxData

Definition at line 163 of file notmuch.c.

164 {
165  // path to DB + query + url "decoration"
166  char *url = nm_get_default_url();
167  if (!url)
168  return NULL;
169 
170  struct NmMboxData *default_data = nm_mdata_new(url);
171  FREE(&url);
172 
173  return default_data;
174 }
struct NmMboxData * nm_mdata_new(const char *url)
Create a new NmMboxData object from a query.
Definition: mdata.c:69
static char * nm_get_default_url(void)
Create a Mailbox with default Notmuch settings.
Definition: notmuch.c:135
Notmuch-specific Mailbox data -.
Definition: mdata.h:34
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ init_mailbox()

static int init_mailbox ( struct Mailbox m)
static

Add Notmuch data to the Mailbox.

Parameters
mMailbox
Return values
0Success
-1Error Bad format

Create a new NmMboxData struct and add it Mailbox::data. Notmuch-specific data will be stored in this struct. This struct can be freed using nm_mdata_free().

Definition at line 186 of file notmuch.c.

187 {
188  if (!m || (m->type != MUTT_NOTMUCH))
189  return -1;
190 
191  if (m->mdata)
192  return 0;
193 
195  if (!m->mdata)
196  return -1;
197 
199  return 0;
200 }
@ MUTT_NOTMUCH
'Notmuch' (virtual) Mailbox type
Definition: mailbox.h:51
void nm_mdata_free(void **ptr)
Free the private Mailbox data - Implements Mailbox::mdata_free()
Definition: mdata.c:46
void(* mdata_free)(void **ptr)
Free the private data attached to the Mailbox.
Definition: mailbox.h:143
enum MailboxType type
Mailbox type.
Definition: mailbox.h:102
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:

◆ email_get_id()

static char* email_get_id ( struct Email e)
static

Get the unique Notmuch Id.

Parameters
eEmail
Return values
ptrID string
NULLError

Definition at line 208 of file notmuch.c.

209 {
210  struct NmEmailData *edata = nm_edata_get(e);
211  if (!edata)
212  return NULL;
213 
214  return edata->virtual_id;
215 }
struct NmEmailData * nm_edata_get(struct Email *e)
Get the Notmuch Email data.
Definition: edata.c:72
void * edata
Driver-specific data.
Definition: email.h:72
Notmuch-specific Email data -.
Definition: edata.h:34
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ email_get_fullpath()

static char* email_get_fullpath ( struct Email e,
char *  buf,
size_t  buflen 
)
static

Get the full path of an email.

Parameters
eEmail
bufBuffer for the path
buflenLength of the buffer
Return values
ptrPath string

Definition at line 224 of file notmuch.c.

225 {
226  snprintf(buf, buflen, "%s/%s", nm_email_get_folder(e), e->path);
227  return buf;
228 }
char * nm_email_get_folder(struct Email *e)
Get the folder for a Email.
Definition: notmuch.c:1449
char * path
Path of Email (for local Mailboxes)
Definition: email.h:68
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ query_window_reset()

static void query_window_reset ( void  )
static

Restore vfolder's search window to its original position.

After moving a vfolder search window backward and forward, calling this function will reset the search position to its original value, setting to 0 the user settable variable:

nm_query_window_current_position

Definition at line 239 of file notmuch.c.

240 {
241  mutt_debug(LL_DEBUG2, "entering\n");
242  cs_subset_str_native_set(NeoMutt->sub, "nm_query_window_current_position", 0, NULL);
243 }
#define mutt_debug(LEVEL,...)
Definition: logging.h:84
@ LL_DEBUG2
Log at debug level 2.
Definition: logging.h:41
int cs_subset_str_native_set(const struct ConfigSubset *sub, const char *name, intptr_t value, struct Buffer *err)
Natively set the value of a string config item.
Definition: subset.c:305
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ windowed_query_from_query()

static bool windowed_query_from_query ( const char *  query,
char *  buf,
size_t  buflen 
)
static

Transforms a vfolder search query into a windowed one.

Parameters
[in]queryvfolder search string
[out]bufallocated string buffer to receive the modified search query
[in]buflenallocated maximum size of the buf string buffer
Return values
trueTransformed search query is available as a string in buf
falseSearch query shall not be transformed

Creates a date: search term window from the following user settings:

  • nm_query_window_enable (only required for nm_query_window_duration = 0)
  • nm_query_window_duration
  • nm_query_window_timebase
  • nm_query_window_current_position

The window won't be applied:

  • If the duration of the search query is set to 0 this function will be disabled unless a user explicitly enables windowed queries.
  • If the timebase is invalid, it will show an error message and do nothing.

If there's no search registered in nm_query_window_current_search or this is a new search, it will reset the window and do the search.

Definition at line 269 of file notmuch.c.

270 {
271  mutt_debug(LL_DEBUG2, "nm: %s\n", query);
272 
273  const bool c_nm_query_window_enable = cs_subset_bool(NeoMutt->sub, "nm_query_window_enable");
274  const short c_nm_query_window_duration = cs_subset_number(NeoMutt->sub, "nm_query_window_duration");
275  const short c_nm_query_window_current_position = cs_subset_number(NeoMutt->sub, "nm_query_window_current_position");
276  const char *const c_nm_query_window_current_search = cs_subset_string(NeoMutt->sub, "nm_query_window_current_search");
277  const char *const c_nm_query_window_timebase = cs_subset_string(NeoMutt->sub, "nm_query_window_timebase");
278  const char *const c_nm_query_window_or_terms = cs_subset_string(NeoMutt->sub, "nm_query_window_or_terms");
279 
280  /* if the query has changed, reset the window position */
281  if (!c_nm_query_window_current_search ||
282  (strcmp(query, c_nm_query_window_current_search) != 0))
283  {
285  }
286 
288  buf, buflen, c_nm_query_window_enable, c_nm_query_window_duration,
289  c_nm_query_window_current_position, c_nm_query_window_current_search,
290  c_nm_query_window_timebase, c_nm_query_window_or_terms);
291 
292  switch (rc)
293  {
295  {
296  mutt_debug(LL_DEBUG2, "nm: %s -> %s\n", query, buf);
297  break;
298  }
300  {
302  return false;
303  }
305  {
306  mutt_message(
307  // L10N: The values 'hour', 'day', 'week', 'month', 'year' are literal.
308  // They should not be translated.
309  _("Invalid nm_query_window_timebase value (valid values are: hour, day, week, month, year)"));
310  mutt_debug(LL_DEBUG2, "Invalid nm_query_window_timebase value\n");
311  return false;
312  }
313  }
314 
315  return true;
316 }
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition: helpers.c:169
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
#define mutt_message(...)
Definition: logging.h:86
#define _(a)
Definition: message.h:28
static void query_window_reset(void)
Restore vfolder's search window to its original position.
Definition: notmuch.c:239
enum NmWindowQueryRc nm_windowed_query_from_query(char *buf, size_t buflen, const bool force_enable, const short duration, const short cur_pos, const char *cur_search, const char *timebase, const char *or_terms)
Windows buf with notmuch date: search term.
Definition: query.c:207
NmWindowQueryRc
Return codes for nm_windowed_query_from_query()
Definition: query.h:45
@ NM_WINDOW_QUERY_SUCCESS
Query was successful.
Definition: query.h:46
@ NM_WINDOW_QUERY_INVALID_DURATION
Invalid duration.
Definition: query.h:48
@ NM_WINDOW_QUERY_INVALID_TIMEBASE
Invalid timebase.
Definition: query.h:47
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ get_query_string()

static char* get_query_string ( struct NmMboxData mdata,
bool  window 
)
static

Builds the notmuch vfolder search string.

Parameters
mdataNotmuch Mailbox data
windowIf true enable application of the window on the search string
Return values
ptrString containing a notmuch search query
NULLNone can be generated

This function parses the internal representation of a search, and returns a search query string ready to be fed to the notmuch API, given the search is valid.

Note
The window parameter here is here to decide contextually whether we want to return a search query with window applied (for the actual search result in mailbox) or not (for the count in the sidebar). It is not aimed at enabling/disabling the feature.

Definition at line 334 of file notmuch.c.

335 {
336  mutt_debug(LL_DEBUG2, "nm: %s\n", window ? "true" : "false");
337 
338  if (!mdata)
339  return NULL;
340  if (mdata->db_query && !window)
341  return mdata->db_query;
342 
343  const char *const c_nm_query_type = cs_subset_string(NeoMutt->sub, "nm_query_type");
344  mdata->query_type = nm_string_to_query_type(c_nm_query_type); /* user's default */
345 
346  struct UrlQuery *item = NULL;
347  STAILQ_FOREACH(item, &mdata->db_url->query_strings, entries)
348  {
349  if (!item->value || !item->name)
350  continue;
351 
352  if (strcmp(item->name, "limit") == 0)
353  {
354  if (!mutt_str_atoi_full(item->value, &mdata->db_limit))
355  {
356  mutt_error(_("failed to parse notmuch limit: %s"), item->value);
357  }
358  }
359  else if (strcmp(item->name, "type") == 0)
360  mdata->query_type = nm_string_to_query_type(item->value);
361  else if (strcmp(item->name, "query") == 0)
362  mutt_str_replace(&mdata->db_query, item->value);
363  }
364 
365  if (!mdata->db_query)
366  return NULL;
367 
368  if (window)
369  {
370  char buf[1024];
371  cs_subset_str_string_set(NeoMutt->sub, "nm_query_window_current_search",
372  mdata->db_query, NULL);
373 
374  /* if a date part is defined, do not apply windows (to avoid the risk of
375  * having a non-intersected date frame). A good improvement would be to
376  * accept if they intersect */
377  if (!strstr(mdata->db_query, "date:") &&
378  windowed_query_from_query(mdata->db_query, buf, sizeof(buf)))
379  {
380  mutt_str_replace(&mdata->db_query, buf);
381  }
382 
383  mutt_debug(LL_DEBUG2, "nm: query (windowed) '%s'\n", mdata->db_query);
384  }
385  else
386  mutt_debug(LL_DEBUG2, "nm: query '%s'\n", mdata->db_query);
387 
388  return mdata->db_query;
389 }
#define mutt_error(...)
Definition: logging.h:87
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:326
static bool windowed_query_from_query(const char *query, char *buf, size_t buflen)
Transforms a vfolder search query into a windowed one.
Definition: notmuch.c:269
enum NmQueryType nm_string_to_query_type(const char *str)
Lookup a query type.
Definition: query.c:110
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:352
struct Url * db_url
Parsed view url of the Notmuch database.
Definition: mdata.h:35
enum NmQueryType query_type
Messages or Threads.
Definition: mdata.h:38
int db_limit
Maximum number of results to return.
Definition: mdata.h:37
char * db_query
Previous query.
Definition: mdata.h:36
Parsed Query String.
Definition: url.h:58
char * name
Query name.
Definition: url.h:59
char * value
Query value.
Definition: url.h:60
struct UrlQueryList query_strings
List of query strings.
Definition: url.h:76
int cs_subset_str_string_set(const struct ConfigSubset *sub, const char *name, const char *value, struct Buffer *err)
Set a config item by string.
Definition: subset.c:408
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ get_limit()

static int get_limit ( struct NmMboxData mdata)
static

Get the database limit.

Parameters
mdataNotmuch Mailbox data
Return values
numCurrent limit

Definition at line 396 of file notmuch.c.

397 {
398  return mdata ? mdata->db_limit : 0;
399 }
+ Here is the caller graph for this function:

◆ apply_exclude_tags()

static void apply_exclude_tags ( notmuch_query_t *  query)
static

Exclude the configured tags.

Parameters
queryNotmuch query

Definition at line 405 of file notmuch.c.

406 {
407  const char *const c_nm_exclude_tags = cs_subset_string(NeoMutt->sub, "nm_exclude_tags");
408  if (!c_nm_exclude_tags || !query)
409  return;
410 
411  struct TagArray tags = nm_tag_str_to_tags(c_nm_exclude_tags);
412 
413  char **tag = NULL;
414  ARRAY_FOREACH(tag, &tags.tags)
415  {
416  mutt_debug(LL_DEBUG2, "nm: query exclude tag '%s'\n", *tag);
417  notmuch_query_add_tag_exclude(query, *tag);
418  }
419 
420  notmuch_query_set_omit_excluded(query, 1);
422 }
#define ARRAY_FOREACH(elem, head)
Iterate over all elements of the array.
Definition: array.h:211
Array of Notmuch tags.
Definition: tag.h:37
struct Tags tags
Tags.
Definition: tag.h:38
void nm_tag_array_free(struct TagArray *tags)
Free all memory of a TagArray.
Definition: tag.c:39
struct TagArray nm_tag_str_to_tags(const char *tag_str)
Converts a comma and/or space-delimited string of tags into an array.
Definition: tag.c:50
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ get_query()

static notmuch_query_t* get_query ( struct Mailbox m,
bool  writable 
)
static

Create a new query.

Parameters
mMailbox
writableShould the query be updateable?
Return values
ptrNotmuch query
NULLError

Definition at line 431 of file notmuch.c.

432 {
433  struct NmMboxData *mdata = nm_mdata_get(m);
434  if (!mdata)
435  return NULL;
436 
437  notmuch_database_t *db = nm_db_get(m, writable);
438  const char *str = get_query_string(mdata, true);
439 
440  if (!db || !str)
441  goto err;
442 
443  notmuch_query_t *q = notmuch_query_create(db, str);
444  if (!q)
445  goto err;
446 
448  notmuch_query_set_sort(q, NOTMUCH_SORT_NEWEST_FIRST);
449  mutt_debug(LL_DEBUG2, "nm: query successfully initialized (%s)\n", str);
450  return q;
451 err:
452  nm_db_release(m);
453  return NULL;
454 }
notmuch_database_t * nm_db_get(struct Mailbox *m, bool writable)
Get the Notmuch database.
Definition: db.c:169
int nm_db_release(struct Mailbox *m)
Close the Notmuch database.
Definition: db.c:193
struct NmMboxData * nm_mdata_get(struct Mailbox *m)
Get the Notmuch Mailbox data.
Definition: mdata.c:97
static void apply_exclude_tags(notmuch_query_t *query)
Exclude the configured tags.
Definition: notmuch.c:405
static char * get_query_string(struct NmMboxData *mdata, bool window)
Builds the notmuch vfolder search string.
Definition: notmuch.c:334
+ Here is the call graph for this function:

◆ update_email_tags()

static int update_email_tags ( struct Email e,
notmuch_message_t *  msg 
)
static

Update the Email's tags from Notmuch.

Parameters
eEmail
msgNotmuch message
Return values
0Success
1Tags unchanged

Definition at line 463 of file notmuch.c.

464 {
465  struct NmEmailData *edata = nm_edata_get(e);
466  char *new_tags = NULL;
467  char *old_tags = NULL;
468 
469  mutt_debug(LL_DEBUG2, "nm: tags update requested (%s)\n", edata->virtual_id);
470 
471  for (notmuch_tags_t *tags = notmuch_message_get_tags(msg);
472  tags && notmuch_tags_valid(tags); notmuch_tags_move_to_next(tags))
473  {
474  const char *t = notmuch_tags_get(tags);
475  if (!t || (*t == '\0'))
476  continue;
477 
478  mutt_str_append_item(&new_tags, t, ' ');
479  }
480 
481  old_tags = driver_tags_get(&e->tags);
482 
483  if (new_tags && old_tags && (strcmp(old_tags, new_tags) == 0))
484  {
485  FREE(&old_tags);
486  FREE(&new_tags);
487  mutt_debug(LL_DEBUG2, "nm: tags unchanged\n");
488  return 1;
489  }
490  FREE(&old_tags);
491 
492  /* new version */
493  driver_tags_replace(&e->tags, new_tags);
494  FREE(&new_tags);
495 
496  new_tags = driver_tags_get_transformed(&e->tags);
497  mutt_debug(LL_DEBUG2, "nm: new tags: '%s'\n", new_tags);
498  FREE(&new_tags);
499 
500  new_tags = driver_tags_get(&e->tags);
501  mutt_debug(LL_DEBUG2, "nm: new tag transforms: '%s'\n", new_tags);
502  FREE(&new_tags);
503 
504  return 0;
505 }
void mutt_str_append_item(char **str, const char *item, char sep)
Add string to another separated by sep.
Definition: string.c:346
struct TagList tags
For drivers that support server tagging.
Definition: email.h:70
bool driver_tags_replace(struct TagList *head, const char *tags)
Replace all tags.
Definition: tags.c:186
char * driver_tags_get_transformed(struct TagList *list)
Get transformed tags.
Definition: tags.c:133
char * driver_tags_get(struct TagList *list)
Get tags.
Definition: tags.c:145
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ update_message_path()

static int update_message_path ( struct Email e,
const char *  path 
)
static

Set the path for a message.

Parameters
eEmail
pathPath
Return values
0Success
1Failure

Definition at line 514 of file notmuch.c.

515 {
516  struct NmEmailData *edata = nm_edata_get(e);
517 
518  mutt_debug(LL_DEBUG2, "nm: path update requested path=%s, (%s)\n", path, edata->virtual_id);
519 
520  char *p = strrchr(path, '/');
521  if (p && ((p - path) > 3) &&
522  (mutt_strn_equal(p - 3, "cur", 3) || mutt_strn_equal(p - 3, "new", 3) ||
523  mutt_strn_equal(p - 3, "tmp", 3)))
524  {
525  edata->type = MUTT_MAILDIR;
526 
527  FREE(&e->path);
528  FREE(&edata->folder);
529 
530  p -= 3; /* skip subfolder (e.g. "new") */
531  if (cs_subset_bool(NeoMutt->sub, "mark_old"))
532  {
533  e->old = mutt_str_startswith(p, "cur");
534  }
535  e->path = mutt_str_dup(p);
536 
537  for (; (p > path) && (*(p - 1) == '/'); p--)
538  ; // do nothing
539 
540  edata->folder = mutt_strn_dup(path, p - path);
541 
542  mutt_debug(LL_DEBUG2, "nm: folder='%s', file='%s'\n", edata->folder, e->path);
543  return 0;
544  }
545 
546  return 1;
547 }
@ MUTT_MAILDIR
'Maildir' Mailbox type
Definition: mailbox.h:48
char * mutt_strn_dup(const char *begin, size_t len)
Duplicate a sub-string.
Definition: string.c:428
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:250
bool mutt_strn_equal(const char *a, const char *b, size_t num)
Check for equality of two strings (to a maximum), safely.
Definition: string.c:473
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:227
bool old
Email is seen, but unread.
Definition: email.h:47
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ get_folder_from_path()

static char* get_folder_from_path ( const char *  path)
static

Find an email's folder from its path.

Parameters
pathPath
Return values
ptrPath string
NULLError

Definition at line 555 of file notmuch.c.

556 {
557  char *p = strrchr(path, '/');
558 
559  if (p && ((p - path) > 3) &&
560  (mutt_strn_equal(p - 3, "cur", 3) || mutt_strn_equal(p - 3, "new", 3) ||
561  mutt_strn_equal(p - 3, "tmp", 3)))
562  {
563  p -= 3;
564  for (; (p > path) && (*(p - 1) == '/'); p--)
565  ; // do nothing
566 
567  return mutt_strn_dup(path, p - path);
568  }
569 
570  return NULL;
571 }
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ nm2mutt_message_id()

static char* nm2mutt_message_id ( const char *  id)
static

Converts notmuch message Id to neomutt message Id.

Parameters
idNotmuch ID to convert
Return values
ptrNeoMutt message ID

Caller must free the NeoMutt Message ID

Definition at line 580 of file notmuch.c.

581 {
582  size_t sz;
583  char *mid = NULL;
584 
585  if (!id)
586  return NULL;
587  sz = strlen(id) + 3;
588  mid = mutt_mem_malloc(sz);
589 
590  snprintf(mid, sz, "<%s>", id);
591  return mid;
592 }
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ init_email()

static int init_email ( struct Email e,
const char *  path,
notmuch_message_t *  msg 
)
static

Set up an email's Notmuch data.

Parameters
eEmail
pathPath to email
msgNotmuch message
Return values
0Success
-1Failure

Definition at line 602 of file notmuch.c.

603 {
604  if (nm_edata_get(e))
605  return 0;
606 
607  struct NmEmailData *edata = nm_edata_new();
608  e->nm_edata = edata;
609 
610  /* Notmuch ensures that message Id exists (if not notmuch Notmuch will
611  * generate an ID), so it's more safe than use neomutt Email->env->id */
612  const char *id = notmuch_message_get_message_id(msg);
613  edata->virtual_id = mutt_str_dup(id);
614 
615  mutt_debug(LL_DEBUG2, "nm: [e=%p, edata=%p] (%s)\n", (void *) e, (void *) edata, id);
616 
617  char *nm_msg_id = nm2mutt_message_id(id);
618  if (!e->env->message_id)
619  {
620  e->env->message_id = nm_msg_id;
621  }
622  else if (!mutt_str_equal(e->env->message_id, nm_msg_id))
623  {
624  FREE(&e->env->message_id);
625  e->env->message_id = nm_msg_id;
626  }
627  else
628  {
629  FREE(&nm_msg_id);
630  }
631 
632  if (update_message_path(e, path) != 0)
633  return -1;
634 
635  update_email_tags(e, msg);
636 
637  return 0;
638 }
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:784
struct NmEmailData * nm_edata_new(void)
Create a new NmEmailData for an email.
Definition: edata.c:61
static char * nm2mutt_message_id(const char *id)
Converts notmuch message Id to neomutt message Id.
Definition: notmuch.c:580
static int update_message_path(struct Email *e, const char *path)
Set the path for a message.
Definition: notmuch.c:514
static int update_email_tags(struct Email *e, notmuch_message_t *msg)
Update the Email's tags from Notmuch.
Definition: notmuch.c:463
void * nm_edata
Notmuch private data.
Definition: email.h:93
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ get_message_last_filename()

static const char* get_message_last_filename ( notmuch_message_t *  msg)
static

Get a message's last filename.

Parameters
msgNotmuch message
Return values
ptrFilename
NULLError

Definition at line 646 of file notmuch.c.

647 {
648  const char *name = NULL;
649 
650  for (notmuch_filenames_t *ls = notmuch_message_get_filenames(msg);
651  ls && notmuch_filenames_valid(ls); notmuch_filenames_move_to_next(ls))
652  {
653  name = notmuch_filenames_get(ls);
654  }
655 
656  return name;
657 }
+ Here is the caller graph for this function:

◆ progress_setup()

static void progress_setup ( struct Mailbox m)
static

Set up the Progress Bar.

Parameters
mMailbox

Definition at line 663 of file notmuch.c.

664 {
665  if (!m->verbose)
666  return;
667 
668  struct NmMboxData *mdata = nm_mdata_get(m);
669  if (!mdata)
670  return;
671 
672  mdata->oldmsgcount = m->msg_count;
673  mdata->ignmsgcount = 0;
674  mdata->progress = progress_new(_("Reading messages..."), MUTT_PROGRESS_READ,
675  mdata->oldmsgcount);
676 }
@ MUTT_PROGRESS_READ
Progress tracks elements, according to $read_inc
Definition: lib.h:49
struct Progress * progress_new(const char *msg, enum ProgressType type, size_t size)
Create a new Progress Bar.
Definition: progress.c:118
int msg_count
Total number of messages.
Definition: mailbox.h:88
bool verbose
Display status messages?
Definition: mailbox.h:115
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ nm_progress_update()

static void nm_progress_update ( struct Mailbox m)
static

Update the progress counter.

Parameters
mMailbox

Definition at line 682 of file notmuch.c.

683 {
684  struct NmMboxData *mdata = nm_mdata_get(m);
685 
686  if (!m->verbose || !mdata || !mdata->progress)
687  return;
688 
689  progress_update(mdata->progress, m->msg_count + mdata->ignmsgcount, -1);
690 }
bool progress_update(struct Progress *progress, size_t pos, int percent)
Update the state of the progress bar.
Definition: progress.c:73
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ get_mutt_email()

static struct Email* get_mutt_email ( struct Mailbox m,
notmuch_message_t *  msg 
)
static

Get the Email of a Notmuch message.

Parameters
mMailbox
msgNotmuch message
Return values
ptrEmail
NULLError

Definition at line 699 of file notmuch.c.

700 {
701  if (!m || !msg)
702  return NULL;
703 
704  const char *id = notmuch_message_get_message_id(msg);
705  if (!id)
706  return NULL;
707 
708  mutt_debug(LL_DEBUG2, "nm: neomutt email, id='%s'\n", id);
709 
710  if (!m->id_hash)
711  {
712  mutt_debug(LL_DEBUG2, "nm: init hash\n");
713  m->id_hash = mutt_make_id_hash(m);
714  if (!m->id_hash)
715  return NULL;
716  }
717 
718  char *mid = nm2mutt_message_id(id);
719  mutt_debug(LL_DEBUG2, "nm: neomutt id='%s'\n", mid);
720 
721  struct Email *e = mutt_hash_find(m->id_hash, mid);
722  FREE(&mid);
723  return e;
724 }
void * mutt_hash_find(const struct HashTable *table, const char *strkey)
Find the HashElem data in a Hash Table element using a key.
Definition: hash.c:362
struct HashTable * mutt_make_id_hash(struct Mailbox *m)
Create a Hash Table for message-ids.
Definition: mutt_thread.c:1651
The envelope/body of an email.
Definition: email.h:37
struct HashTable * id_hash
Hash Table by msg id.
Definition: mailbox.h:124
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ append_message()

static void append_message ( struct HeaderCache h,
struct Mailbox m,
notmuch_message_t *  msg,
bool  dedup 
)
static

Associate a message.

Parameters
hHeader cache handle
mMailbox
msgNotmuch message
dedupDe-duplicate results

Definition at line 733 of file notmuch.c.

735 {
736  struct NmMboxData *mdata = nm_mdata_get(m);
737  if (!mdata)
738  return;
739 
740  char *newpath = NULL;
741  struct Email *e = NULL;
742 
743  /* deduplicate */
744  if (dedup && get_mutt_email(m, msg))
745  {
746  mdata->ignmsgcount++;
748  mutt_debug(LL_DEBUG2, "nm: ignore id=%s, already in the m\n",
749  notmuch_message_get_message_id(msg));
750  return;
751  }
752 
753  const char *path = get_message_last_filename(msg);
754  if (!path)
755  return;
756 
757  mutt_debug(LL_DEBUG2, "nm: appending message, i=%d, id=%s, path=%s\n",
758  m->msg_count, notmuch_message_get_message_id(msg), path);
759 
760  if (m->msg_count >= m->email_max)
761  {
762  mutt_debug(LL_DEBUG2, "nm: allocate mx memory\n");
763  mx_alloc_memory(m);
764  }
765 
766 #ifdef USE_HCACHE
768  if (!e)
769 #endif
770  {
771  if (access(path, F_OK) == 0)
772  {
773  /* We pass is_old=false as argument here, but e->old will be updated later
774  * by update_message_path() (called by init_email() below). */
775  e = maildir_parse_message(MUTT_MAILDIR, path, false, NULL);
776  }
777  else
778  {
779  /* maybe moved try find it... */
780  char *folder = get_folder_from_path(path);
781 
782  if (folder)
783  {
784  FILE *fp = maildir_open_find_message(folder, path, &newpath);
785  if (fp)
786  {
787  e = maildir_parse_stream(MUTT_MAILDIR, fp, newpath, false, NULL);
788  mutt_file_fclose(&fp);
789 
790  mutt_debug(LL_DEBUG1, "nm: not up-to-date: %s -> %s\n", path, newpath);
791  }
792  }
793  FREE(&folder);
794  }
795 
796  if (!e)
797  {
798  mutt_debug(LL_DEBUG1, "nm: failed to parse message: %s\n", path);
799  goto done;
800  }
801 
802 #ifdef USE_HCACHE
803  mutt_hcache_store(h, newpath ? newpath : path,
804  mutt_str_len(newpath ? newpath : path), e, 0);
805 #endif
806  }
807 
808  if (init_email(e, newpath ? newpath : path, msg) != 0)
809  {
810  email_free(&e);
811  mutt_debug(LL_DEBUG1, "nm: failed to append email!\n");
812  goto done;
813  }
814 
815  e->active = true;
816  e->index = m->msg_count;
817  mailbox_size_add(m, e);
818  m->emails[m->msg_count] = e;
819  m->msg_count++;
820 
821  if (newpath)
822  {
823  /* remember that file has been moved -- nm_mbox_sync() will update the DB */
824  struct NmEmailData *edata = nm_edata_get(e);
825  if (edata)
826  {
827  mutt_debug(LL_DEBUG1, "nm: remember obsolete path: %s\n", path);
828  edata->oldpath = mutt_str_dup(path);
829  }
830  }
832 done:
833  FREE(&newpath);
834 }
void email_free(struct Email **ptr)
Free an Email.
Definition: email.c:44
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:152
int mutt_hcache_store(struct HeaderCache *hc, const char *key, size_t keylen, struct Email *e, uint32_t uidvalidity)
Multiplexor for StoreOps::store.
Definition: hcache.c:548
struct HCacheEntry mutt_hcache_fetch(struct HeaderCache *hc, const char *key, size_t keylen, uint32_t uidvalidity)
Multiplexor for StoreOps::fetch.
Definition: hcache.c:456
@ LL_DEBUG1
Log at debug level 1.
Definition: logging.h:40
void mailbox_size_add(struct Mailbox *m, const struct Email *e)
Add an email's size to the total size of a Mailbox.
Definition: mailbox.c:237
struct Email * maildir_parse_stream(enum MailboxType type, FILE *fp, const char *fname, bool is_old, struct Email *e)
Parse a Maildir message.
Definition: maildir.c:881
FILE * maildir_open_find_message(const char *folder, const char *msg, char **newname)
Find a new.
Definition: maildir.c:995
struct Email * maildir_parse_message(enum MailboxType type, const char *fname, bool is_old, struct Email *e)
Actually parse a maildir message.
Definition: maildir.c:928
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:544
void mx_alloc_memory(struct Mailbox *m)
Create storage for the emails.
Definition: mx.c:1219
static const char * get_message_last_filename(notmuch_message_t *msg)
Get a message's last filename.
Definition: notmuch.c:646
static char * get_folder_from_path(const char *path)
Find an email's folder from its path.
Definition: notmuch.c:555
static int init_email(struct Email *e, const char *path, notmuch_message_t *msg)
Set up an email's Notmuch data.
Definition: notmuch.c:602
static void nm_progress_update(struct Mailbox *m)
Update the progress counter.
Definition: notmuch.c:682
static struct Email * get_mutt_email(struct Mailbox *m, notmuch_message_t *msg)
Get the Email of a Notmuch message.
Definition: notmuch.c:699
bool active
Message is not to be removed.
Definition: email.h:74
int index
The absolute (unsorted) message number.
Definition: email.h:110
struct Email * email
Retrieved email.
Definition: lib.h:101
int email_max
Number of pointers in emails.
Definition: mailbox.h:97
struct Email ** emails
Array of Emails.
Definition: mailbox.h:96
int ignmsgcount
Ignored messages.
Definition: mdata.h:42
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ append_replies()

static void append_replies ( struct HeaderCache h,
struct Mailbox m,
notmuch_query_t *  q,
notmuch_message_t *  top,
bool  dedup 
)
static

Add all the replies to a given messages into the display.

Parameters
hHeader cache handle
mMailbox
qNotmuch query
topNotmuch message
dedupDe-duplicate the results

Careful, this calls itself recursively to make sure we get everything.

Definition at line 846 of file notmuch.c.

848 {
849  notmuch_messages_t *msgs = NULL;
850 
851  for (msgs = notmuch_message_get_replies(top); notmuch_messages_valid(msgs);
852  notmuch_messages_move_to_next(msgs))
853  {
854  notmuch_message_t *nm = notmuch_messages_get(msgs);
855  append_message(h, m, nm, dedup);
856  /* recurse through all the replies to this message too */
857  append_replies(h, m, q, nm, dedup);
858  notmuch_message_destroy(nm);
859  }
860 }
static void append_replies(struct HeaderCache *h, struct Mailbox *m, notmuch_query_t *q, notmuch_message_t *top, bool dedup)
Add all the replies to a given messages into the display.
Definition: notmuch.c:846
static void append_message(struct HeaderCache *h, struct Mailbox *m, notmuch_message_t *msg, bool dedup)
Associate a message.
Definition: notmuch.c:733
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ append_thread()

static void append_thread ( struct HeaderCache h,
struct Mailbox m,
notmuch_query_t *  q,
notmuch_thread_t *  thread,
bool  dedup 
)
static

Add each top level reply in the thread.

Parameters
hHeader cache handle
mMailbox
qNotmuch query
threadNotmuch thread
dedupDe-duplicate the results

add each top level reply in the thread, and then add each reply to the top level replies

Definition at line 873 of file notmuch.c.

875 {
876  notmuch_messages_t *msgs = NULL;
877 
878  for (msgs = notmuch_thread_get_toplevel_messages(thread);
879  notmuch_messages_valid(msgs); notmuch_messages_move_to_next(msgs))
880  {
881  notmuch_message_t *nm = notmuch_messages_get(msgs);
882  append_message(h, m, nm, dedup);
883  append_replies(h, m, q, nm, dedup);
884  notmuch_message_destroy(nm);
885  }
886 }
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ get_messages()

static notmuch_messages_t* get_messages ( notmuch_query_t *  query)
static

Load messages for a query.

Parameters
queryNotmuch query
Return values
ptrMessages matching query
NULLError occurred

This helper method is to be the single point for retrieving messages. It handles version specific calls, which will make maintenance easier.

Definition at line 897 of file notmuch.c.

898 {
899  if (!query)
900  return NULL;
901 
902  notmuch_messages_t *msgs = NULL;
903 
904 #if LIBNOTMUCH_CHECK_VERSION(5, 0, 0)
905  if (notmuch_query_search_messages(query, &msgs) != NOTMUCH_STATUS_SUCCESS)
906  return NULL;
907 #elif LIBNOTMUCH_CHECK_VERSION(4, 3, 0)
908  if (notmuch_query_search_messages_st(query, &msgs) != NOTMUCH_STATUS_SUCCESS)
909  return NULL;
910 #else
911  msgs = notmuch_query_search_messages(query);
912 #endif
913 
914  return msgs;
915 }
+ Here is the caller graph for this function:

◆ read_mesgs_query()

static bool read_mesgs_query ( struct Mailbox m,
notmuch_query_t *  q,
bool  dedup 
)
static

Search for matching messages.

Parameters
mMailbox
qNotmuch query
dedupDe-duplicate the results
Return values
trueSuccess
falseFailure

Definition at line 925 of file notmuch.c.

926 {
927  struct NmMboxData *mdata = nm_mdata_get(m);
928  if (!mdata)
929  return false;
930 
931  int limit = get_limit(mdata);
932 
933  notmuch_messages_t *msgs = get_messages(q);
934 
935  if (!msgs)
936  return false;
937 
938  struct HeaderCache *h = nm_hcache_open(m);
939 
940  for (; notmuch_messages_valid(msgs) && ((limit == 0) || (m->msg_count < limit));
941  notmuch_messages_move_to_next(msgs))
942  {
943  if (SigInt)
944  {
945  nm_hcache_close(h);
946  SigInt = false;
947  return false;
948  }
949  notmuch_message_t *nm = notmuch_messages_get(msgs);
950  append_message(h, m, nm, dedup);
951  notmuch_message_destroy(nm);
952  }
953 
954  nm_hcache_close(h);
955  return true;
956 }
SIG_ATOMIC_VOLATILE_T SigInt
true after SIGINT is received
Definition: mutt_globals.h:69
static struct HeaderCache * nm_hcache_open(struct Mailbox *m)
Open a header cache.
Definition: notmuch.c:109
static void nm_hcache_close(struct HeaderCache *h)
Close the header cache.
Definition: notmuch.c:123
static int get_limit(struct NmMboxData *mdata)
Get the database limit.
Definition: notmuch.c:396
static notmuch_messages_t * get_messages(notmuch_query_t *query)
Load messages for a query.
Definition: notmuch.c:897
Header cache structure.
Definition: lib.h:87
+ Here is the call graph for this function:

◆ get_threads()

static notmuch_threads_t* get_threads ( notmuch_query_t *  query)
static

Load threads for a query.

Parameters
queryNotmuch query
Return values
ptrThreads matching query
NULLError occurred

This helper method is to be the single point for retrieving messages. It handles version specific calls, which will make maintenance easier.

Definition at line 967 of file notmuch.c.

968 {
969  if (!query)
970  return NULL;
971 
972  notmuch_threads_t *threads = NULL;
973 #if LIBNOTMUCH_CHECK_VERSION(5, 0, 0)
974  if (notmuch_query_search_threads(query, &threads) != NOTMUCH_STATUS_SUCCESS)
975  return false;
976 #elif LIBNOTMUCH_CHECK_VERSION(4, 3, 0)
977  if (notmuch_query_search_threads_st(query, &threads) != NOTMUCH_STATUS_SUCCESS)
978  return false;
979 #else
980  threads = notmuch_query_search_threads(query);
981 #endif
982 
983  return threads;
984 }
+ Here is the caller graph for this function:

◆ read_threads_query()

static bool read_threads_query ( struct Mailbox m,
notmuch_query_t *  q,
bool  dedup,
int  limit 
)
static

Perform a query with threads.

Parameters
mMailbox
qQuery type
dedupShould the results be de-duped?
limitMaximum number of results
Return values
trueSuccess
falseFailure

Definition at line 995 of file notmuch.c.

996 {
997  struct NmMboxData *mdata = nm_mdata_get(m);
998  if (!mdata)
999  return false;
1000 
1001  notmuch_threads_t *threads = get_threads(q);
1002  if (!threads)
1003  return false;
1004 
1005  struct HeaderCache *h = nm_hcache_open(m);
1006 
1007  for (; notmuch_threads_valid(threads) && ((limit == 0) || (m->msg_count < limit));
1008  notmuch_threads_move_to_next(threads))
1009  {
1010  if (SigInt)
1011  {
1012  nm_hcache_close(h);
1013  SigInt = false;
1014  return false;
1015  }
1016  notmuch_thread_t *thread = notmuch_threads_get(threads);
1017  append_thread(h, m, q, thread, dedup);
1018  notmuch_thread_destroy(thread);
1019  }
1020 
1021  nm_hcache_close(h);
1022  return true;
1023 }
static void append_thread(struct HeaderCache *h, struct Mailbox *m, notmuch_query_t *q, notmuch_thread_t *thread, bool dedup)
Add each top level reply in the thread.
Definition: notmuch.c:873
static notmuch_threads_t * get_threads(notmuch_query_t *query)
Load threads for a query.
Definition: notmuch.c:967
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ get_nm_message()

static notmuch_message_t* get_nm_message ( notmuch_database_t *  db,
struct Email e 
)
static

Find a Notmuch message.

Parameters
dbNotmuch database
eEmail
Return values
ptrHandle to the Notmuch message

Definition at line 1031 of file notmuch.c.

1032 {
1033  notmuch_message_t *msg = NULL;
1034  char *id = email_get_id(e);
1035 
1036  mutt_debug(LL_DEBUG2, "nm: find message (%s)\n", id);
1037 
1038  if (id && db)
1039  notmuch_database_find_message(db, id, &msg);
1040 
1041  return msg;
1042 }
static char * email_get_id(struct Email *e)
Get the unique Notmuch Id.
Definition: notmuch.c:208
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ nm_message_has_tag()

static bool nm_message_has_tag ( notmuch_message_t *  msg,
char *  tag 
)
static

Does a message have this tag?

Parameters
msgNotmuch message
tagTag
Return values
trueIt does

Definition at line 1050 of file notmuch.c.

1051 {
1052  const char *possible_match_tag = NULL;
1053  notmuch_tags_t *tags = NULL;
1054 
1055  for (tags = notmuch_message_get_tags(msg); notmuch_tags_valid(tags);
1056  notmuch_tags_move_to_next(tags))
1057  {
1058  possible_match_tag = notmuch_tags_get(tags);
1059  if (mutt_str_equal(possible_match_tag, tag))
1060  {
1061  return true;
1062  }
1063  }
1064  return false;
1065 }
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ sync_email_path_with_nm()

static void sync_email_path_with_nm ( struct Email e,
notmuch_message_t *  msg 
)
static

Synchronize Neomutt's Email path with notmuch.

Parameters
eEmail in Neomutt
msgEmail from notmuch

Definition at line 1072 of file notmuch.c.

1073 {
1074  const char *new_file = get_message_last_filename(msg);
1075  char old_file[PATH_MAX];
1076  email_get_fullpath(e, old_file, sizeof(old_file));
1077 
1078  if (!mutt_str_equal(old_file, new_file))
1079  update_message_path(e, new_file);
1080 }
static char * email_get_fullpath(struct Email *e, char *buf, size_t buflen)
Get the full path of an email.
Definition: notmuch.c:224
+ Here is the call graph for this function:

◆ update_tags()

static int update_tags ( notmuch_message_t *  msg,
const char *  tag_str 
)
static

Update the tags on a message.

Parameters
msgNotmuch message
tag_strString of tags (space separated)
Return values
0Success
-1Failure

Definition at line 1089 of file notmuch.c.

1090 {
1091  if (!tag_str)
1092  return -1;
1093 
1094  notmuch_message_freeze(msg);
1095 
1097  char **tag_elem = NULL;
1098  ARRAY_FOREACH(tag_elem, &tags.tags)
1099  {
1100  char *tag = *tag_elem;
1101 
1102  if (tag[0] == '-')
1103  {
1104  mutt_debug(LL_DEBUG1, "nm: remove tag: '%s'\n", tag + 1);
1105  notmuch_message_remove_tag(msg, tag + 1);
1106  }
1107  else if (tag[0] == '!')
1108  {
1109  mutt_debug(LL_DEBUG1, "nm: toggle tag: '%s'\n", tag + 1);
1110  if (nm_message_has_tag(msg, tag + 1))
1111  {
1112  notmuch_message_remove_tag(msg, tag + 1);
1113  }
1114  else
1115  {
1116  notmuch_message_add_tag(msg, tag + 1);
1117  }
1118  }
1119  else
1120  {
1121  mutt_debug(LL_DEBUG1, "nm: add tag: '%s'\n", (tag[0] == '+') ? tag + 1 : tag);
1122  notmuch_message_add_tag(msg, (tag[0] == '+') ? tag + 1 : tag);
1123  }
1124  }
1125 
1126  notmuch_message_thaw(msg);
1128 
1129  return 0;
1130 }
static bool nm_message_has_tag(notmuch_message_t *msg, char *tag)
Does a message have this tag?
Definition: notmuch.c:1050
char * tag_str
Source string.
Definition: tag.h:39
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ update_email_flags()

static int update_email_flags ( struct Mailbox m,
struct Email e,
const char *  tag_str 
)
static

Update the Email's flags.

Parameters
mMailbox
eEmail
tag_strString of tags (space separated)
Return values
0Success
-1Failure

TODO: join update_email_tags and update_email_flags, which are given an array of tags.

Definition at line 1143 of file notmuch.c.

1144 {
1145  if (!tag_str)
1146  return -1;
1147 
1148  const char *const c_nm_unread_tag = cs_subset_string(NeoMutt->sub, "nm_unread_tag");
1149  const char *const c_nm_replied_tag = cs_subset_string(NeoMutt->sub, "nm_replied_tag");
1150  const char *const c_nm_flagged_tag = cs_subset_string(NeoMutt->sub, "nm_flagged_tag");
1151 
1153  char **tag_elem = NULL;
1154  ARRAY_FOREACH(tag_elem, &tags.tags)
1155  {
1156  char *tag = *tag_elem;
1157 
1158  if (tag[0] == '-')
1159  {
1160  tag++;
1161  if (strcmp(tag, c_nm_unread_tag) == 0)
1162  mutt_set_flag(m, e, MUTT_READ, true);
1163  else if (strcmp(tag, c_nm_replied_tag) == 0)
1164  mutt_set_flag(m, e, MUTT_REPLIED, false);
1165  else if (strcmp(tag, c_nm_flagged_tag) == 0)
1166  mutt_set_flag(m, e, MUTT_FLAG, false);
1167  }
1168  else
1169  {
1170  tag = (tag[0] == '+') ? tag + 1 : tag;
1171  if (strcmp(tag, c_nm_unread_tag) == 0)
1172  mutt_set_flag(m, e, MUTT_READ, false);
1173  else if (strcmp(tag, c_nm_replied_tag) == 0)
1174  mutt_set_flag(m, e, MUTT_REPLIED, true);
1175  else if (strcmp(tag, c_nm_flagged_tag) == 0)
1176  mutt_set_flag(m, e, MUTT_FLAG, true);
1177  }
1178  }
1179 
1181 
1182  return 0;
1183 }
@ MUTT_READ
Messages that have been read.
Definition: mutt.h:93
@ MUTT_FLAG
Flagged messages.
Definition: mutt.h:99
@ MUTT_REPLIED
Messages that have been replied to.
Definition: mutt.h:92
#define mutt_set_flag(m, e, flag, bf)
Definition: protos.h:64
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ rename_maildir_filename()

static int rename_maildir_filename ( const char *  old,
char *  buf,
size_t  buflen,
struct Email e 
)
static

Rename a Maildir file.

Parameters
oldOld path
bufBuffer for new path
buflenLength of buffer
eEmail
Return values
0Success, renamed
1Success, no change
-1Failure

Definition at line 1195 of file notmuch.c.

1196 {
1197  char filename[PATH_MAX];
1198  char suffix[PATH_MAX];
1199  char folder[PATH_MAX];
1200 
1201  mutt_str_copy(folder, old, sizeof(folder));
1202  char *p = strrchr(folder, '/');
1203  if (p)
1204  {
1205  *p = '\0';
1206  p++;
1207  }
1208  else
1209  p = folder;
1210 
1211  mutt_str_copy(filename, p, sizeof(filename));
1212 
1213  /* remove (new,cur,...) from folder path */
1214  p = strrchr(folder, '/');
1215  if (p)
1216  *p = '\0';
1217 
1218  /* remove old flags from filename */
1219  p = strchr(filename, ':');
1220  if (p)
1221  *p = '\0';
1222 
1223  /* compose new flags */
1224  maildir_gen_flags(suffix, sizeof(suffix), e);
1225 
1226  snprintf(buf, buflen, "%s/%s/%s%s", folder,
1227  (e->read || e->old) ? "cur" : "new", filename, suffix);
1228 
1229  if (strcmp(old, buf) == 0)
1230  return 1;
1231 
1232  if (rename(old, buf) != 0)
1233  {
1234  mutt_debug(LL_DEBUG1, "nm: rename(2) failed %s -> %s\n", old, buf);
1235  return -1;
1236  }
1237 
1238  return 0;
1239 }
void maildir_gen_flags(char *dest, size_t destlen, struct Email *e)
Generate the Maildir flags for an email.
Definition: maildir.c:186
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:629
bool read
Email is read.
Definition: email.h:48
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ remove_filename()

static int remove_filename ( struct Mailbox m,
const char *  path 
)
static

Delete a file.

Parameters
mMailbox
pathPath of file
Return values
0Success
-1Failure

Definition at line 1248 of file notmuch.c.

1249 {
1250  struct NmMboxData *mdata = nm_mdata_get(m);
1251  if (!mdata)
1252  return -1;
1253 
1254  mutt_debug(LL_DEBUG2, "nm: remove filename '%s'\n", path);
1255 
1256  notmuch_database_t *db = nm_db_get(m, true);
1257  if (!db)
1258  return -1;
1259 
1260  notmuch_message_t *msg = NULL;
1261  notmuch_status_t st = notmuch_database_find_message_by_filename(db, path, &msg);
1262  if (st || !msg)
1263  return -1;
1264 
1265  int trans = nm_db_trans_begin(m);
1266  if (trans < 0)
1267  return -1;
1268 
1269  /* note that unlink() is probably unnecessary here, it's already removed
1270  * by mh_sync_mailbox_message(), but for sure... */
1271  notmuch_filenames_t *ls = NULL;
1272  st = notmuch_database_remove_message(db, path);
1273  switch (st)
1274  {
1275  case NOTMUCH_STATUS_SUCCESS:
1276  mutt_debug(LL_DEBUG2, "nm: remove success, call unlink\n");
1277  unlink(path);
1278  break;
1279  case NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID:
1280  mutt_debug(LL_DEBUG2, "nm: remove success (duplicate), call unlink\n");
1281  unlink(path);
1282  for (ls = notmuch_message_get_filenames(msg);
1283  ls && notmuch_filenames_valid(ls); notmuch_filenames_move_to_next(ls))
1284  {
1285  path = notmuch_filenames_get(ls);
1286 
1287  mutt_debug(LL_DEBUG2, "nm: remove duplicate: '%s'\n", path);
1288  unlink(path);
1289  notmuch_database_remove_message(db, path);
1290  }
1291  break;
1292  default:
1293  mutt_debug(LL_DEBUG1, "nm: failed to remove '%s' [st=%d]\n", path, (int) st);
1294  break;
1295  }
1296 
1297  notmuch_message_destroy(msg);
1298  if (trans)
1299  nm_db_trans_end(m);
1300  return 0;
1301 }
int nm_db_trans_begin(struct Mailbox *m)
Start a Notmuch database transaction.
Definition: db.c:226
int nm_db_trans_end(struct Mailbox *m)
End a database transaction.
Definition: db.c:248
+ Here is the call graph for this function:

◆ rename_filename()

static int rename_filename ( struct Mailbox m,
const char *  old_file,
const char *  new_file,
struct Email e 
)
static

Rename the file.

Parameters
mNotmuch Mailbox data
old_fileOld filename
new_fileNew filename
eEmail
Return values
0Success
-1Failure

Definition at line 1312 of file notmuch.c.

1314 {
1315  struct NmMboxData *mdata = nm_mdata_get(m);
1316  if (!mdata)
1317  return -1;
1318 
1319  notmuch_database_t *db = nm_db_get(m, true);
1320  if (!db || !new_file || !old_file || (access(new_file, F_OK) != 0))
1321  return -1;
1322 
1323  int rc = -1;
1324  notmuch_status_t st;
1325  notmuch_filenames_t *ls = NULL;
1326  notmuch_message_t *msg = NULL;
1327 
1328  mutt_debug(LL_DEBUG1, "nm: rename filename, %s -> %s\n", old_file, new_file);
1329  int trans = nm_db_trans_begin(m);
1330  if (trans < 0)
1331  return -1;
1332 
1333  mutt_debug(LL_DEBUG2, "nm: rename: add '%s'\n", new_file);
1334 #if LIBNOTMUCH_CHECK_VERSION(5, 1, 0)
1335  st = notmuch_database_index_file(db, new_file, NULL, &msg);
1336 #else
1337  st = notmuch_database_add_message(db, new_file, &msg);
1338 #endif
1339 
1340  if ((st != NOTMUCH_STATUS_SUCCESS) && (st != NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID))
1341  {
1342  mutt_debug(LL_DEBUG1, "nm: failed to add '%s' [st=%d]\n", new_file, (int) st);
1343  goto done;
1344  }
1345 
1346  mutt_debug(LL_DEBUG2, "nm: rename: rem '%s'\n", old_file);
1347  st = notmuch_database_remove_message(db, old_file);
1348  switch (st)
1349  {
1350  case NOTMUCH_STATUS_SUCCESS:
1351  break;
1352  case NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID:
1353  mutt_debug(LL_DEBUG2, "nm: rename: syncing duplicate filename\n");
1354  notmuch_message_destroy(msg);
1355  msg = NULL;
1356  notmuch_database_find_message_by_filename(db, new_file, &msg);
1357 
1358  for (ls = notmuch_message_get_filenames(msg);
1359  msg && ls && notmuch_filenames_valid(ls); notmuch_filenames_move_to_next(ls))
1360  {
1361  const char *path = notmuch_filenames_get(ls);
1362  char newpath[PATH_MAX];
1363 
1364  if (strcmp(new_file, path) == 0)
1365  continue;
1366 
1367  mutt_debug(LL_DEBUG2, "nm: rename: syncing duplicate: %s\n", path);
1368 
1369  if (rename_maildir_filename(path, newpath, sizeof(newpath), e) == 0)
1370  {
1371  mutt_debug(LL_DEBUG2, "nm: rename dup %s -> %s\n", path, newpath);
1372  notmuch_database_remove_message(db, path);
1373 #if LIBNOTMUCH_CHECK_VERSION(5, 1, 0)
1374  notmuch_database_index_file(db, newpath, NULL, NULL);
1375 #else
1376  notmuch_database_add_message(db, newpath, NULL);
1377 #endif
1378  }
1379  }
1380  notmuch_message_destroy(msg);
1381  msg = NULL;
1382  notmuch_database_find_message_by_filename(db, new_file, &msg);
1383  st = NOTMUCH_STATUS_SUCCESS;
1384  break;
1385  default:
1386  mutt_debug(LL_DEBUG1, "nm: failed to remove '%s' [st=%d]\n", old_file, (int) st);
1387  break;
1388  }
1389 
1390  if ((st == NOTMUCH_STATUS_SUCCESS) && e && msg)
1391  {
1392  notmuch_message_maildir_flags_to_tags(msg);
1393  update_email_tags(e, msg);
1394 
1395  char *tags = driver_tags_get(&e->tags);
1396  update_tags(msg, tags);
1397  FREE(&tags);
1398  }
1399 
1400  rc = 0;
1401 done:
1402  if (msg)
1403  notmuch_message_destroy(msg);
1404  if (trans)
1405  nm_db_trans_end(m);
1406  return rc;
1407 }
static int rename_maildir_filename(const char *old, char *buf, size_t buflen, struct Email *e)
Rename a Maildir file.
Definition: notmuch.c:1195
static int update_tags(notmuch_message_t *msg, const char *tag_str)
Update the tags on a message.
Definition: notmuch.c:1089
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ count_query()

static unsigned int count_query ( notmuch_database_t *  db,
const char *  qstr,
int  limit 
)
static

Count the results of a query.

Parameters
dbNotmuch database
qstrQuery to execute
limitMaximum number of results
Return values
numNumber of results

Definition at line 1416 of file notmuch.c.

1417 {
1418  notmuch_query_t *q = notmuch_query_create(db, qstr);
1419  if (!q)
1420  return 0;
1421 
1422  unsigned int res = 0;
1423 
1424  apply_exclude_tags(q);
1425 #if LIBNOTMUCH_CHECK_VERSION(5, 0, 0)
1426  if (notmuch_query_count_messages(q, &res) != NOTMUCH_STATUS_SUCCESS)
1427  res = 0; /* may not be defined on error */
1428 #elif LIBNOTMUCH_CHECK_VERSION(4, 3, 0)
1429  if (notmuch_query_count_messages_st(q, &res) != NOTMUCH_STATUS_SUCCESS)
1430  res = 0; /* may not be defined on error */
1431 #else
1432  res = notmuch_query_count_messages(q);
1433 #endif
1434  notmuch_query_destroy(q);
1435  mutt_debug(LL_DEBUG1, "nm: count '%s', result=%d\n", qstr, res);
1436 
1437  if ((limit > 0) && (res > limit))
1438  res = limit;
1439 
1440  return res;
1441 }
+ Here is the call graph for this function:

◆ nm_email_get_folder()

char* nm_email_get_folder ( struct Email e)

Get the folder for a Email.

Parameters
eEmail
Return values
ptrFolder containing email
NULLError

Definition at line 1449 of file notmuch.c.

1450 {
1451  struct NmEmailData *edata = nm_edata_get(e);
1452  if (!edata)
1453  return NULL;
1454 
1455  return edata->folder;
1456 }
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ nm_email_get_folder_rel_db()

char* nm_email_get_folder_rel_db ( struct Mailbox m,
struct Email e 
)

Get the folder for a Email from the same level as the notmuch database.

Parameters
mMailbox containing Email
eEmail
Return values
ptrFolder containing email from the same level as the notmuch db
NULLError

Instead of returning a path like /var/mail/account/Inbox, this returns account/Inbox. If wanting the full path, use nm_email_get_folder().

Definition at line 1468 of file notmuch.c.

1469 {
1470  char *full_folder = nm_email_get_folder(e);
1471  if (!full_folder)
1472  return NULL;
1473 
1474  const char *db_path = nm_db_get_filename(m);
1475  if (!db_path)
1476  return NULL;
1477 
1478  return full_folder + strlen(db_path);
1479 }
const char * nm_db_get_filename(struct Mailbox *m)
Get the filename of the Notmuch database.
Definition: db.c:54
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ nm_read_entire_thread()

int nm_read_entire_thread ( struct Mailbox m,
struct Email e 
)

Get the entire thread of an email.

Parameters
mMailbox
eEmail
Return values
0Success
-1Failure

Definition at line 1488 of file notmuch.c.

1489 {
1490  if (!m)
1491  return -1;
1492 
1493  struct NmMboxData *mdata = nm_mdata_get(m);
1494  if (!mdata)
1495  return -1;
1496 
1497  notmuch_query_t *q = NULL;
1498  notmuch_database_t *db = NULL;
1499  notmuch_message_t *msg = NULL;
1500  int rc = -1;
1501 
1502  if (!(db = nm_db_get(m, false)) || !(msg = get_nm_message(db, e)))
1503  goto done;
1504 
1505  mutt_debug(LL_DEBUG1, "nm: reading entire-thread messages...[current count=%d]\n",
1506  m->msg_count);
1507 
1508  progress_setup(m);
1509  const char *id = notmuch_message_get_thread_id(msg);
1510  if (!id)
1511  goto done;
1512 
1513  char *qstr = NULL;
1514  mutt_str_append_item(&qstr, "thread:", '\0');
1515  mutt_str_append_item(&qstr, id, '\0');
1516 
1517  q = notmuch_query_create(db, qstr);
1518  FREE(&qstr);
1519  if (!q)
1520  goto done;
1521  apply_exclude_tags(q);
1522  notmuch_query_set_sort(q, NOTMUCH_SORT_NEWEST_FIRST);
1523 
1524  read_threads_query(m, q, true, 0);
1525  m->mtime.tv_sec = mutt_date_epoch();
1526  m->mtime.tv_nsec = 0;
1527  rc = 0;
1528 
1529  if (m->msg_count > mdata->oldmsgcount)
1531 done:
1532  if (q)
1533  notmuch_query_destroy(q);
1534 
1535  nm_db_release(m);
1536 
1537  if (m->msg_count == mdata->oldmsgcount)
1538  mutt_message(_("No more messages in the thread"));
1539 
1540  mdata->oldmsgcount = 0;
1541  mutt_debug(LL_DEBUG1, "nm: reading entire-thread messages... done [rc=%d, count=%d]\n",
1542  rc, m->msg_count);
1543  progress_free(&mdata->progress);
1544  return rc;
1545 }
time_t mutt_date_epoch(void)
Return the number of seconds since the Unix epoch.
Definition: date.c:427
void mailbox_changed(struct Mailbox *m, enum NotifyMailbox action)
Notify observers of a change to a Mailbox.
Definition: mailbox.c:222
@ NT_MAILBOX_INVALID
Email list was changed.
Definition: mailbox.h:177
static notmuch_message_t * get_nm_message(notmuch_database_t *db, struct Email *e)
Find a Notmuch message.
Definition: notmuch.c:1031
static bool read_threads_query(struct Mailbox *m, notmuch_query_t *q, bool dedup, int limit)
Perform a query with threads.
Definition: notmuch.c:995
static void progress_setup(struct Mailbox *m)
Set up the Progress Bar.
Definition: notmuch.c:663
void progress_free(struct Progress **ptr)
Free a Progress Bar.
Definition: progress.c:86
struct timespec mtime
Time Mailbox was last changed.
Definition: mailbox.h:104
long tv_nsec
Number of nanosecond, on top.
Definition: file.h:51
time_t tv_sec
Number of seconds since the epoch.
Definition: file.h:50
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ nm_url_from_query()

char* nm_url_from_query ( struct Mailbox m,
char *  buf,
size_t  buflen 
)

Turn a query into a URL.

Parameters
mMailbox
bufBuffer for URL
buflenLength of buffer
Return values
ptrQuery as a URL
NULLError

Definition at line 1555 of file notmuch.c.

1556 {
1557  mutt_debug(LL_DEBUG2, "(%s)\n", buf);
1558  struct NmMboxData *mdata = nm_mdata_get(m);
1559  char url[PATH_MAX + 1024 + 32]; /* path to DB + query + URL "decoration" */
1560  int added;
1561  bool using_default_data = false;
1562 
1563  // No existing data. Try to get a default NmMboxData.
1564  if (!mdata)
1565  {
1567 
1568  // Failed to get default data.
1569  if (!mdata)
1570  return NULL;
1571 
1572  using_default_data = true;
1573  }
1574 
1575  enum NmQueryType c_nm_query_type = nm_string_to_query_type(
1576  cs_subset_string(NeoMutt->sub, "nm_query_type"));
1577  mdata->query_type = nm_parse_type_from_query(buf, c_nm_query_type);
1578 
1579  const short c_nm_db_limit = cs_subset_number(NeoMutt->sub, "nm_db_limit");
1580  if (get_limit(mdata) == c_nm_db_limit)
1581  {
1582  added = snprintf(url, sizeof(url), "%s%s?type=%s&query=", NmUrlProtocol,
1584  }
1585  else
1586  {
1587  added = snprintf(url, sizeof(url), "%s%s?type=%s&limit=%d&query=", NmUrlProtocol,
1588  nm_db_get_filename(m),
1589  nm_query_type_to_string(mdata->query_type), get_limit(mdata));
1590  }
1591 
1592  if (added >= sizeof(url))
1593  {
1594  // snprintf output was truncated, so can't create URL
1595  return NULL;
1596  }
1597 
1598  url_pct_encode(&url[added], sizeof(url) - added, buf);
1599 
1600  mutt_str_copy(buf, url, buflen);
1601  buf[buflen - 1] = '\0';
1602 
1603  if (using_default_data)
1604  nm_mdata_free((void **) &mdata);
1605 
1606  mutt_debug(LL_DEBUG1, "nm: url from query '%s'\n", buf);
1607  return buf;
1608 }
static struct NmMboxData * nm_get_default_data(void)
Create a Mailbox with default Notmuch settings.
Definition: notmuch.c:163
const char NmUrlProtocol[]
Definition: notmuch.c:93
const char * nm_query_type_to_string(enum NmQueryType query_type)
Turn a query type into a string.
Definition: query.c:96
enum NmQueryType nm_parse_type_from_query(char *buf, enum NmQueryType fallback)
Parse a query type out of a query.
Definition: query.c:49
NmQueryType
Notmuch Query Types.
Definition: query.h:35
void url_pct_encode(char *buf, size_t buflen, const char *src)
Percent-encode a string.
Definition: url.c:151
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ nm_query_window_available()

bool nm_query_window_available ( void  )

Are windowed queries enabled for use?

Return values
trueWindowed queries in use

Definition at line 1614 of file notmuch.c.

1615 {
1616  const short c_nm_query_window_duration = cs_subset_number(NeoMutt->sub, "nm_query_window_duration");
1617  const bool c_nm_query_window_enable = cs_subset_bool(NeoMutt->sub, "nm_query_window_enable");
1618 
1619  return c_nm_query_window_enable || (c_nm_query_window_duration > 0);
1620 }
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ nm_query_window_forward()

void nm_query_window_forward ( void  )

Function to move the current search window forward in time.

Updates nm_query_window_current_position by decrementing it by 1, or does nothing if the current window already is set to 0.

The lower the value of nm_query_window_current_position is, the more recent the result will be.

Definition at line 1631 of file notmuch.c.

1632 {
1633  const short c_nm_query_window_current_position = cs_subset_number(NeoMutt->sub, "nm_query_window_current_position");
1634  if (c_nm_query_window_current_position != 0)
1635  {
1636  cs_subset_str_native_set(NeoMutt->sub, "nm_query_window_current_position",
1637  c_nm_query_window_current_position - 1, NULL);
1638  }
1639 
1640  mutt_debug(LL_DEBUG2, "(%d)\n", c_nm_query_window_current_position - 1);
1641 }
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ nm_query_window_backward()

void nm_query_window_backward ( void  )

Function to move the current search window backward in time.

Updates nm_query_window_current_position by incrementing it by 1

The higher the value of nm_query_window_current_position is, the less recent the result will be.

Definition at line 1651 of file notmuch.c.

1652 {
1653  const short c_nm_query_window_current_position = cs_subset_number(NeoMutt->sub, "nm_query_window_current_position");
1654  cs_subset_str_native_set(NeoMutt->sub, "nm_query_window_current_position",
1655  c_nm_query_window_current_position + 1, NULL);
1656  mutt_debug(LL_DEBUG2, "(%d)\n", c_nm_query_window_current_position + 1);
1657 }
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ nm_query_window_reset()

void nm_query_window_reset ( void  )

Resets the vfolder window position to the present.

Definition at line 1662 of file notmuch.c.

1663 {
1664  cs_subset_str_native_set(NeoMutt->sub, "nm_query_window_current_position", 0, NULL);
1665  mutt_debug(LL_DEBUG2, "Reset nm_query_window_current_position to 0\n");
1666 }
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ nm_message_is_still_queried()

bool nm_message_is_still_queried ( struct Mailbox m,
struct Email e 
)

Is a message still visible in the query?

Parameters
mMailbox
eEmail
Return values
trueMessage is still in query

Definition at line 1674 of file notmuch.c.

1675 {
1676  struct NmMboxData *mdata = nm_mdata_get(m);
1677  notmuch_database_t *db = nm_db_get(m, false);
1678  char *orig_str = get_query_string(mdata, true);
1679 
1680  if (!db || !orig_str)
1681  return false;
1682 
1683  char *new_str = NULL;
1684  bool rc = false;
1685  if (mutt_str_asprintf(&new_str, "id:%s and (%s)", email_get_id(e), orig_str) < 0)
1686  return false;
1687 
1688  mutt_debug(LL_DEBUG2, "nm: checking if message is still queried: %s\n", new_str);
1689 
1690  notmuch_query_t *q = notmuch_query_create(db, new_str);
1691 
1692  switch (mdata->query_type)
1693  {
1694  case NM_QUERY_TYPE_UNKNOWN: // UNKNOWN should never occur, but MESGS is default
1695  case NM_QUERY_TYPE_MESGS:
1696  {
1697  notmuch_messages_t *messages = get_messages(q);
1698 
1699  if (!messages)
1700  return false;
1701 
1702  rc = notmuch_messages_valid(messages);
1703  notmuch_messages_destroy(messages);
1704  break;
1705  }
1706  case NM_QUERY_TYPE_THREADS:
1707  {
1708  notmuch_threads_t *threads = get_threads(q);
1709 
1710  if (!threads)
1711  return false;
1712 
1713  rc = notmuch_threads_valid(threads);
1714  notmuch_threads_destroy(threads);
1715  break;
1716  }
1717  }
1718 
1719  notmuch_query_destroy(q);
1720 
1721  mutt_debug(LL_DEBUG2, "nm: checking if message is still queried: %s = %s\n",
1722  new_str, rc ? "true" : "false");
1723 
1724  return rc;
1725 }
int mutt_str_asprintf(char **strp, const char *fmt,...)
Definition: string.c:1008
@ NM_QUERY_TYPE_UNKNOWN
Unknown query type. Error in notmuch query.
Definition: query.h:38
@ NM_QUERY_TYPE_THREADS
Whole threads.
Definition: query.h:37
@ NM_QUERY_TYPE_MESGS
Default: Messages only.
Definition: query.h:36
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ nm_update_filename()

int nm_update_filename ( struct Mailbox m,
const char *  old_file,
const char *  new_file,
struct Email e 
)

Change the filename.

Parameters
mMailbox
old_fileOld filename
new_fileNew filename
eEmail
Return values
0Success
-1Failure

Definition at line 1736 of file notmuch.c.

1738 {
1739  char buf[PATH_MAX];
1740  struct NmMboxData *mdata = nm_mdata_get(m);
1741  if (!mdata || !new_file)
1742  return -1;
1743 
1744  if (!old_file && nm_edata_get(e))
1745  {
1746  email_get_fullpath(e, buf, sizeof(buf));
1747  old_file = buf;
1748  }
1749 
1750  int rc = rename_filename(m, old_file, new_file, e);
1751 
1752  nm_db_release(m);
1753  m->mtime.tv_sec = mutt_date_epoch();
1754  m->mtime.tv_nsec = 0;
1755  return rc;
1756 }
static int rename_filename(struct Mailbox *m, const char *old_file, const char *new_file, struct Email *e)
Rename the file.
Definition: notmuch.c:1312
+ Here is the caller graph for this function:

◆ get_default_mailbox()

static struct Mailbox* get_default_mailbox ( void  )
static

Get Mailbox for notmuch without any parameters.

Return values
ptrMailbox pointer

Definition at line 1845 of file notmuch.c.

1846 {
1847  // Create a new notmuch mailbox from scratch and add plumbing for DB access.
1848  char *default_url = nm_get_default_url();
1849  struct Mailbox *m = mx_path_resolve(default_url);
1850 
1851  FREE(&default_url);
1852 
1853  // These are no-ops for an initialized mailbox.
1854  init_mailbox(m);
1855  mx_mbox_ac_link(m);
1856 
1857  return m;
1858 }
struct Mailbox * mx_path_resolve(const char *path)
Get a Mailbox for a path.
Definition: mx.c:1677
bool mx_mbox_ac_link(struct Mailbox *m)
Link a Mailbox to an existing or new Account.
Definition: mx.c:267
static int init_mailbox(struct Mailbox *m)
Add Notmuch data to the Mailbox.
Definition: notmuch.c:186
A mailbox.
Definition: mailbox.h:79
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ nm_record_message()

int nm_record_message ( struct Mailbox m,
char *  path,
struct Email e 
)

Add a message to the Notmuch database.

Parameters
mMailbox
pathPath of the email
eEmail
Return values
0Success
-1Failure

Definition at line 1868 of file notmuch.c.

1869 {
1870  notmuch_database_t *db = NULL;
1871  notmuch_status_t st;
1872  notmuch_message_t *msg = NULL;
1873  int rc = -1;
1874 
1875  struct NmMboxData *mdata = nm_mdata_get(m);
1876 
1877  // If no notmuch data, fall back to the default mailbox.
1878  //
1879  // IMPORTANT: DO NOT FREE THIS MAILBOX. Two reasons:
1880  // 1) If user has default mailbox in config, we'll be removing it. That's not
1881  // good program behavior!
1882  // 2) If not in user's config, keep mailbox around for future nm_record calls.
1883  // It saves NeoMutt from allocating/deallocating repeatedly.
1884  if (!mdata)
1885  {
1886  mutt_debug(LL_DEBUG1, "nm: non-nm mailbox. trying the default nm mailbox.");
1887  m = get_default_mailbox();
1888  mdata = nm_mdata_get(m);
1889  }
1890 
1891  if (!path || !mdata || (access(path, F_OK) != 0))
1892  return 0;
1893  db = nm_db_get(m, true);
1894  if (!db)
1895  return -1;
1896 
1897  mutt_debug(LL_DEBUG1, "nm: record message: %s\n", path);
1898  int trans = nm_db_trans_begin(m);
1899  if (trans < 0)
1900  goto done;
1901 
1902 #if LIBNOTMUCH_CHECK_VERSION(5, 1, 0)
1903  st = notmuch_database_index_file(db, path, NULL, &msg);
1904 #else
1905  st = notmuch_database_add_message(db, path, &msg);
1906 #endif
1907 
1908  if ((st != NOTMUCH_STATUS_SUCCESS) && (st != NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID))
1909  {
1910  mutt_debug(LL_DEBUG1, "nm: failed to add '%s' [st=%d]\n", path, (int) st);
1911  goto done;
1912  }
1913 
1914  if ((st == NOTMUCH_STATUS_SUCCESS) && msg)
1915  {
1916  notmuch_message_maildir_flags_to_tags(msg);
1917  if (e)
1918  {
1919  char *tags = driver_tags_get(&e->tags);
1920  update_tags(msg, tags);
1921  FREE(&tags);
1922  }
1923  const char *const c_nm_record_tags = cs_subset_string(NeoMutt->sub, "nm_record_tags");
1924  if (c_nm_record_tags)
1925  update_tags(msg, c_nm_record_tags);
1926  }
1927 
1928  rc = 0;
1929 done:
1930  if (msg)
1931  notmuch_message_destroy(msg);
1932  if (trans == 1)
1933  nm_db_trans_end(m);
1934 
1935  nm_db_release(m);
1936 
1937  return rc;
1938 }
static struct Mailbox * get_default_mailbox(void)
Get Mailbox for notmuch without any parameters.
Definition: notmuch.c:1845
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ nm_get_all_tags()

int nm_get_all_tags ( struct Mailbox m,
const char **  tag_list,
int *  tag_count 
)

Fill a list with all notmuch tags.

Parameters
[in]mMailbox
[out]tag_listList of tags
[out]tag_countNumber of tags
Return values
0Success
-1Failure

If tag_list is NULL, just count the tags.

Definition at line 1950 of file notmuch.c.

1951 {
1952  struct NmMboxData *mdata = nm_mdata_get(m);
1953  if (!mdata)
1954  return -1;
1955 
1956  notmuch_database_t *db = NULL;
1957  notmuch_tags_t *tags = NULL;
1958  const char *tag = NULL;
1959  int rc = -1;
1960 
1961  if (!(db = nm_db_get(m, false)) || !(tags = notmuch_database_get_all_tags(db)))
1962  goto done;
1963 
1964  *tag_count = 0;
1965  mutt_debug(LL_DEBUG1, "nm: get all tags\n");
1966 
1967  while (notmuch_tags_valid(tags))
1968  {
1969  tag = notmuch_tags_get(tags);
1970  /* Skip empty string */
1971  if (*tag)
1972  {
1973  if (tag_list)
1974  tag_list[*tag_count] = mutt_str_dup(tag);
1975  (*tag_count)++;
1976  }
1977  notmuch_tags_move_to_next(tags);
1978  }
1979 
1980  rc = 0;
1981 done:
1982  if (tags)
1983  notmuch_tags_destroy(tags);
1984 
1985  nm_db_release(m);
1986 
1987  mutt_debug(LL_DEBUG1, "nm: get all tags done [rc=%d tag_count=%u]\n", rc, *tag_count);
1988  return rc;
1989 }
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

Variable Documentation

◆ nm_commands

const struct Command nm_commands[]
static
Initial value:
= {
{ "unvirtual-mailboxes", parse_unmailboxes, 0 },
{ "virtual-mailboxes", parse_mailboxes, MUTT_NAMED },
}
enum CommandResult parse_unmailboxes(struct Buffer *buf, struct Buffer *s, intptr_t data, struct Buffer *err)
Parse the 'unmailboxes' command - Implements Command::parse() -.
enum CommandResult parse_mailboxes(struct Buffer *buf, struct Buffer *s, intptr_t data, struct Buffer *err)
Parse the 'mailboxes' command - Implements Command::parse() -.
#define MUTT_NAMED
Definition: mutt_commands.h:44

Definition at line 1 of file notmuch.c.

◆ NmUrlProtocol

const char NmUrlProtocol[] = "notmuch://"

Definition at line 93 of file notmuch.c.

◆ NmUrlProtocolLen

const int NmUrlProtocolLen = sizeof(NmUrlProtocol) - 1

Definition at line 94 of file notmuch.c.