NeoMutt  2021-02-05-89-gabe350
Teaching an old dog new tricks
DOXYGEN
notmuch.c
Go to the documentation of this file.
1 
44 #include "config.h"
45 #include <ctype.h>
46 #include <errno.h>
47 #include <limits.h>
48 #include <notmuch.h>
49 #include <stdbool.h>
50 #include <stdio.h>
51 #include <string.h>
52 #include <time.h>
53 #include <unistd.h>
54 #include "private.h"
55 #include "mutt/lib.h"
56 #include "config/lib.h"
57 #include "email/lib.h"
58 #include "core/lib.h"
59 #include "gui/lib.h"
60 #include "mutt.h"
61 #include "lib.h"
62 #include "hcache/lib.h"
63 #include "index/lib.h"
64 #include "maildir/lib.h"
65 #include "adata.h"
66 #include "command_parse.h"
67 #include "edata.h"
68 #include "mdata.h"
69 #include "mutt_commands.h"
70 #include "mutt_globals.h"
71 #include "mutt_thread.h"
72 #include "mx.h"
73 #include "progress.h"
74 #include "protos.h"
75 
76 struct stat;
77 
78 static const struct Command nm_commands[] = {
79  // clang-format off
80  { "unvirtual-mailboxes", parse_unmailboxes, 0 },
81  { "virtual-mailboxes", parse_mailboxes, MUTT_NAMED },
82  // clang-format on
83 };
84 
85 const char NmUrlProtocol[] = "notmuch://";
86 const int NmUrlProtocolLen = sizeof(NmUrlProtocol) - 1;
87 
91 void nm_init(void)
92 {
94 }
95 
101 static struct HeaderCache *nm_hcache_open(struct Mailbox *m)
102 {
103 #ifdef USE_HCACHE
104  return mutt_hcache_open(C_HeaderCache, mailbox_path(m), NULL);
105 #else
106  return NULL;
107 #endif
108 }
109 
114 static void nm_hcache_close(struct HeaderCache *h)
115 {
116 #ifdef USE_HCACHE
118 #endif
119 }
120 
126 enum NmQueryType string_to_query_type(const char *str)
127 {
128  if (mutt_str_equal(str, "threads"))
129  return NM_QUERY_TYPE_THREADS;
130  if (mutt_str_equal(str, "messages"))
131  return NM_QUERY_TYPE_MESGS;
132 
133  mutt_error(_("failed to parse notmuch query type: %s"), NONULL(str));
134  return NM_QUERY_TYPE_MESGS;
135 }
136 
142 static char *nm_get_default_url(void)
143 {
144  // path to DB + query + url "decoration"
145  size_t len = PATH_MAX + 1024 + 32;
146  char *url = mutt_mem_malloc(len);
147 
148  // Try to use `$nm_default_url` or `$folder`.
149  // If neither are set, it is impossible to create a Notmuch URL.
150  if (C_NmDefaultUrl)
151  snprintf(url, len, "%s", C_NmDefaultUrl);
152  else if (C_Folder)
153  snprintf(url, len, "notmuch://%s", C_Folder);
154  else
155  {
156  FREE(url);
157  return NULL;
158  }
159 
160  return url;
161 }
162 
168 static struct NmMboxData *nm_get_default_data(void)
169 {
170  // path to DB + query + url "decoration"
171  char *url = nm_get_default_url();
172  if (!url)
173  return NULL;
174 
175  struct NmMboxData *default_data = nm_mdata_new(url);
176  FREE(&url);
177 
178  return default_data;
179 }
180 
191 static int init_mailbox(struct Mailbox *m)
192 {
193  if (!m || (m->type != MUTT_NOTMUCH))
194  return -1;
195 
196  if (m->mdata)
197  return 0;
198 
200  if (!m->mdata)
201  return -1;
202 
204  return 0;
205 }
206 
213 static char *email_get_id(struct Email *e)
214 {
215  struct NmEmailData *edata = nm_edata_get(e);
216  if (!edata)
217  return NULL;
218 
219  return edata->virtual_id;
220 }
221 
229 static char *email_get_fullpath(struct Email *e, char *buf, size_t buflen)
230 {
231  snprintf(buf, buflen, "%s/%s", nm_email_get_folder(e), e->path);
232  return buf;
233 }
234 
242 static const char *query_type_to_string(enum NmQueryType query_type)
243 {
244  if (query_type == NM_QUERY_TYPE_THREADS)
245  return "threads";
246  return "messages";
247 }
248 
259 static bool query_window_check_timebase(const char *timebase)
260 {
261  if ((strcmp(timebase, "hour") == 0) || (strcmp(timebase, "day") == 0) ||
262  (strcmp(timebase, "week") == 0) || (strcmp(timebase, "month") == 0) ||
263  (strcmp(timebase, "year") == 0))
264  {
265  return true;
266  }
267  return false;
268 }
269 
279 static void query_window_reset(void)
280 {
281  mutt_debug(LL_DEBUG2, "entering\n");
282  cs_subset_str_native_set(NeoMutt->sub, "nm_query_window_current_position", 0, NULL);
283 }
284 
323 static bool windowed_query_from_query(const char *query, char *buf, size_t buflen)
324 {
325  mutt_debug(LL_DEBUG2, "nm: %s\n", query);
326 
329 
330  /* if the duration is a non positive integer, disable the window */
331  if (C_NmQueryWindowDuration <= 0)
332  {
334  return false;
335  }
336 
337  /* if the query has changed, reset the window position */
338  if (!C_NmQueryWindowCurrentSearch || (strcmp(query, C_NmQueryWindowCurrentSearch) != 0))
340 
342  {
343  mutt_message(_("Invalid nm_query_window_timebase value (valid values are: "
344  "hour, day, week, month or year)"));
345  mutt_debug(LL_DEBUG2, "Invalid nm_query_window_timebase value\n");
346  return false;
347  }
348 
349  if (end == 0)
350  {
351  // Open-ended date allows mail from the future.
352  // This may occur is the sender's time settings are off.
353  snprintf(buf, buflen, "date:%d%s.. and %s", beg, C_NmQueryWindowTimebase,
355  }
356  else
357  {
358  snprintf(buf, buflen, "date:%d%s..%d%s and %s", beg, C_NmQueryWindowTimebase,
360  }
361 
362  mutt_debug(LL_DEBUG2, "nm: %s -> %s\n", query, buf);
363 
364  return true;
365 }
366 
383 static char *get_query_string(struct NmMboxData *mdata, bool window)
384 {
385  mutt_debug(LL_DEBUG2, "nm: %s\n", window ? "true" : "false");
386 
387  if (!mdata)
388  return NULL;
389  if (mdata->db_query && !window)
390  return mdata->db_query;
391 
392  mdata->query_type = string_to_query_type(C_NmQueryType); /* user's default */
393 
394  struct UrlQuery *item = NULL;
395  STAILQ_FOREACH(item, &mdata->db_url->query_strings, entries)
396  {
397  if (!item->value || !item->name)
398  continue;
399 
400  if (strcmp(item->name, "limit") == 0)
401  {
402  if (mutt_str_atoi(item->value, &mdata->db_limit))
403  mutt_error(_("failed to parse notmuch limit: %s"), item->value);
404  }
405  else if (strcmp(item->name, "type") == 0)
406  mdata->query_type = string_to_query_type(item->value);
407  else if (strcmp(item->name, "query") == 0)
408  mdata->db_query = mutt_str_dup(item->value);
409  }
410 
411  if (!mdata->db_query)
412  return NULL;
413 
414  if (window)
415  {
416  char buf[1024];
418 
419  /* if a date part is defined, do not apply windows (to avoid the risk of
420  * having a non-intersected date frame). A good improvement would be to
421  * accept if they intersect */
422  if (!strstr(mdata->db_query, "date:") &&
423  windowed_query_from_query(mdata->db_query, buf, sizeof(buf)))
424  {
425  mdata->db_query = mutt_str_dup(buf);
426  }
427 
428  mutt_debug(LL_DEBUG2, "nm: query (windowed) '%s'\n", mdata->db_query);
429  }
430  else
431  mutt_debug(LL_DEBUG2, "nm: query '%s'\n", mdata->db_query);
432 
433  return mdata->db_query;
434 }
435 
441 static int get_limit(struct NmMboxData *mdata)
442 {
443  return mdata ? mdata->db_limit : 0;
444 }
445 
450 static void apply_exclude_tags(notmuch_query_t *query)
451 {
452  if (!C_NmExcludeTags || !query)
453  return;
454 
455  char *end = NULL, *tag = NULL;
456 
457  char *buf = mutt_str_dup(C_NmExcludeTags);
458 
459  for (char *p = buf; p && (p[0] != '\0'); p++)
460  {
461  if (!tag && isspace(*p))
462  continue;
463  if (!tag)
464  tag = p; /* begin of the tag */
465  if ((p[0] == ',') || (p[0] == ' '))
466  end = p; /* terminate the tag */
467  else if (p[1] == '\0')
468  end = p + 1; /* end of optstr */
469  if (!tag || !end)
470  continue;
471  if (tag >= end)
472  break;
473  *end = '\0';
474 
475  mutt_debug(LL_DEBUG2, "nm: query exclude tag '%s'\n", tag);
476  notmuch_query_add_tag_exclude(query, tag);
477  end = NULL;
478  tag = NULL;
479  }
480  notmuch_query_set_omit_excluded(query, 1);
481  FREE(&buf);
482 }
483 
491 static notmuch_query_t *get_query(struct Mailbox *m, bool writable)
492 {
493  struct NmMboxData *mdata = nm_mdata_get(m);
494  if (!mdata)
495  return NULL;
496 
497  notmuch_database_t *db = nm_db_get(m, writable);
498  const char *str = get_query_string(mdata, true);
499 
500  if (!db || !str)
501  goto err;
502 
503  notmuch_query_t *q = notmuch_query_create(db, str);
504  if (!q)
505  goto err;
506 
508  notmuch_query_set_sort(q, NOTMUCH_SORT_NEWEST_FIRST);
509  mutt_debug(LL_DEBUG2, "nm: query successfully initialized (%s)\n", str);
510  return q;
511 err:
512  nm_db_release(m);
513  return NULL;
514 }
515 
523 static int update_email_tags(struct Email *e, notmuch_message_t *msg)
524 {
525  struct NmEmailData *edata = nm_edata_get(e);
526  char *new_tags = NULL;
527  char *old_tags = NULL;
528 
529  mutt_debug(LL_DEBUG2, "nm: tags update requested (%s)\n", edata->virtual_id);
530 
531  for (notmuch_tags_t *tags = notmuch_message_get_tags(msg);
532  tags && notmuch_tags_valid(tags); notmuch_tags_move_to_next(tags))
533  {
534  const char *t = notmuch_tags_get(tags);
535  if (!t || (*t == '\0'))
536  continue;
537 
538  mutt_str_append_item(&new_tags, t, ' ');
539  }
540 
541  old_tags = driver_tags_get(&e->tags);
542 
543  if (new_tags && old_tags && (strcmp(old_tags, new_tags) == 0))
544  {
545  FREE(&old_tags);
546  FREE(&new_tags);
547  mutt_debug(LL_DEBUG2, "nm: tags unchanged\n");
548  return 1;
549  }
550 
551  /* new version */
552  driver_tags_replace(&e->tags, new_tags);
553  FREE(&new_tags);
554 
555  new_tags = driver_tags_get_transformed(&e->tags);
556  mutt_debug(LL_DEBUG2, "nm: new tags: '%s'\n", new_tags);
557  FREE(&new_tags);
558 
559  new_tags = driver_tags_get(&e->tags);
560  mutt_debug(LL_DEBUG2, "nm: new tag transforms: '%s'\n", new_tags);
561  FREE(&new_tags);
562 
563  return 0;
564 }
565 
573 static int update_message_path(struct Email *e, const char *path)
574 {
575  struct NmEmailData *edata = nm_edata_get(e);
576 
577  mutt_debug(LL_DEBUG2, "nm: path update requested path=%s, (%s)\n", path, edata->virtual_id);
578 
579  char *p = strrchr(path, '/');
580  if (p && ((p - path) > 3) &&
581  (mutt_strn_equal(p - 3, "cur", 3) || mutt_strn_equal(p - 3, "new", 3) ||
582  mutt_strn_equal(p - 3, "tmp", 3)))
583  {
584  edata->type = MUTT_MAILDIR;
585 
586  FREE(&e->path);
587  FREE(&edata->folder);
588 
589  p -= 3; /* skip subfolder (e.g. "new") */
590  e->path = mutt_str_dup(p);
591 
592  for (; (p > path) && (*(p - 1) == '/'); p--)
593  ; // do nothing
594 
595  edata->folder = mutt_strn_dup(path, p - path);
596 
597  mutt_debug(LL_DEBUG2, "nm: folder='%s', file='%s'\n", edata->folder, e->path);
598  return 0;
599  }
600 
601  return 1;
602 }
603 
610 static char *get_folder_from_path(const char *path)
611 {
612  char *p = strrchr(path, '/');
613 
614  if (p && ((p - path) > 3) &&
615  (mutt_strn_equal(p - 3, "cur", 3) || mutt_strn_equal(p - 3, "new", 3) ||
616  mutt_strn_equal(p - 3, "tmp", 3)))
617  {
618  p -= 3;
619  for (; (p > path) && (*(p - 1) == '/'); p--)
620  ; // do nothing
621 
622  return mutt_strn_dup(path, p - path);
623  }
624 
625  return NULL;
626 }
627 
635 static char *nm2mutt_message_id(const char *id)
636 {
637  size_t sz;
638  char *mid = NULL;
639 
640  if (!id)
641  return NULL;
642  sz = strlen(id) + 3;
643  mid = mutt_mem_malloc(sz);
644 
645  snprintf(mid, sz, "<%s>", id);
646  return mid;
647 }
648 
657 static int init_email(struct Email *e, const char *path, notmuch_message_t *msg)
658 {
659  if (nm_edata_get(e))
660  return 0;
661 
662  struct NmEmailData *edata = nm_edata_new();
663  e->nm_edata = edata;
664 
665  /* Notmuch ensures that message Id exists (if not notmuch Notmuch will
666  * generate an ID), so it's more safe than use neomutt Email->env->id */
667  const char *id = notmuch_message_get_message_id(msg);
668  edata->virtual_id = mutt_str_dup(id);
669 
670  mutt_debug(LL_DEBUG2, "nm: [e=%p, edata=%p] (%s)\n", (void *) e, (void *) edata, id);
671 
672  char *nm_msg_id = nm2mutt_message_id(id);
673  if (!e->env->message_id)
674  {
675  e->env->message_id = nm_msg_id;
676  }
677  else if (!mutt_str_equal(e->env->message_id, nm_msg_id))
678  {
679  FREE(&e->env->message_id);
680  e->env->message_id = nm_msg_id;
681  }
682  else
683  {
684  FREE(&nm_msg_id);
685  }
686 
687  if (update_message_path(e, path) != 0)
688  return -1;
689 
690  update_email_tags(e, msg);
691 
692  return 0;
693 }
694 
701 static const char *get_message_last_filename(notmuch_message_t *msg)
702 {
703  const char *name = NULL;
704 
705  for (notmuch_filenames_t *ls = notmuch_message_get_filenames(msg);
706  ls && notmuch_filenames_valid(ls); notmuch_filenames_move_to_next(ls))
707  {
708  name = notmuch_filenames_get(ls);
709  }
710 
711  return name;
712 }
713 
718 static void progress_reset(struct Mailbox *m)
719 {
720  if (!m->verbose)
721  return;
722 
723  struct NmMboxData *mdata = nm_mdata_get(m);
724  if (!mdata)
725  return;
726 
727  memset(&mdata->progress, 0, sizeof(mdata->progress));
728  mdata->oldmsgcount = m->msg_count;
729  mdata->ignmsgcount = 0;
730  mdata->noprogress = false;
731  mdata->progress_ready = false;
732 }
733 
739 static void progress_update(struct Mailbox *m, notmuch_query_t *q)
740 {
741  struct NmMboxData *mdata = nm_mdata_get(m);
742 
743  if (!m->verbose || !mdata || mdata->noprogress)
744  return;
745 
746  if (!mdata->progress_ready && q)
747  {
748  // The total mail count is in oldmsgcount, so use that instead of recounting.
749  mutt_progress_init(&mdata->progress, _("Reading messages..."),
750  MUTT_PROGRESS_READ, mdata->oldmsgcount);
751  mdata->progress_ready = true;
752  }
753 
754  if (mdata->progress_ready)
755  {
756  mutt_progress_update(&mdata->progress, m->msg_count + mdata->ignmsgcount, -1);
757  }
758 }
759 
767 static struct Email *get_mutt_email(struct Mailbox *m, notmuch_message_t *msg)
768 {
769  if (!m || !msg)
770  return NULL;
771 
772  const char *id = notmuch_message_get_message_id(msg);
773  if (!id)
774  return NULL;
775 
776  mutt_debug(LL_DEBUG2, "nm: neomutt email, id='%s'\n", id);
777 
778  if (!m->id_hash)
779  {
780  mutt_debug(LL_DEBUG2, "nm: init hash\n");
781  m->id_hash = mutt_make_id_hash(m);
782  if (!m->id_hash)
783  return NULL;
784  }
785 
786  char *mid = nm2mutt_message_id(id);
787  mutt_debug(LL_DEBUG2, "nm: neomutt id='%s'\n", mid);
788 
789  struct Email *e = mutt_hash_find(m->id_hash, mid);
790  FREE(&mid);
791  return e;
792 }
793 
802 static void append_message(struct HeaderCache *h, struct Mailbox *m,
803  notmuch_query_t *q, notmuch_message_t *msg, bool dedup)
804 {
805  struct NmMboxData *mdata = nm_mdata_get(m);
806  if (!mdata)
807  return;
808 
809  char *newpath = NULL;
810  struct Email *e = NULL;
811 
812  /* deduplicate */
813  if (dedup && get_mutt_email(m, msg))
814  {
815  mdata->ignmsgcount++;
816  progress_update(m, q);
817  mutt_debug(LL_DEBUG2, "nm: ignore id=%s, already in the m\n",
818  notmuch_message_get_message_id(msg));
819  return;
820  }
821 
822  const char *path = get_message_last_filename(msg);
823  if (!path)
824  return;
825 
826  mutt_debug(LL_DEBUG2, "nm: appending message, i=%d, id=%s, path=%s\n",
827  m->msg_count, notmuch_message_get_message_id(msg), path);
828 
829  if (m->msg_count >= m->email_max)
830  {
831  mutt_debug(LL_DEBUG2, "nm: allocate mx memory\n");
832  mx_alloc_memory(m);
833  }
834 
835 #ifdef USE_HCACHE
837  if (!e)
838 #endif
839  {
840  if (access(path, F_OK) == 0)
841  e = maildir_parse_message(MUTT_MAILDIR, path, false, NULL);
842  else
843  {
844  /* maybe moved try find it... */
845  char *folder = get_folder_from_path(path);
846 
847  if (folder)
848  {
849  FILE *fp = maildir_open_find_message(folder, path, &newpath);
850  if (fp)
851  {
852  e = maildir_parse_stream(MUTT_MAILDIR, fp, newpath, false, NULL);
853  mutt_file_fclose(&fp);
854 
855  mutt_debug(LL_DEBUG1, "nm: not up-to-date: %s -> %s\n", path, newpath);
856  }
857  }
858  FREE(&folder);
859  }
860 
861  if (!e)
862  {
863  mutt_debug(LL_DEBUG1, "nm: failed to parse message: %s\n", path);
864  goto done;
865  }
866 
867 #ifdef USE_HCACHE
868  mutt_hcache_store(h, newpath ? newpath : path,
869  mutt_str_len(newpath ? newpath : path), e, 0);
870 #endif
871  }
872 
873  if (init_email(e, newpath ? newpath : path, msg) != 0)
874  {
875  email_free(&e);
876  mutt_debug(LL_DEBUG1, "nm: failed to append email!\n");
877  goto done;
878  }
879 
880  e->active = true;
881  e->index = m->msg_count;
882  mailbox_size_add(m, e);
883  m->emails[m->msg_count] = e;
884  m->msg_count++;
885 
886  if (newpath)
887  {
888  /* remember that file has been moved -- nm_mbox_sync() will update the DB */
889  struct NmEmailData *edata = nm_edata_get(e);
890  if (edata)
891  {
892  mutt_debug(LL_DEBUG1, "nm: remember obsolete path: %s\n", path);
893  edata->oldpath = mutt_str_dup(path);
894  }
895  }
896  progress_update(m, q);
897 done:
898  FREE(&newpath);
899 }
900 
911 static void append_replies(struct HeaderCache *h, struct Mailbox *m,
912  notmuch_query_t *q, notmuch_message_t *top, bool dedup)
913 {
914  notmuch_messages_t *msgs = NULL;
915 
916  for (msgs = notmuch_message_get_replies(top); notmuch_messages_valid(msgs);
917  notmuch_messages_move_to_next(msgs))
918  {
919  notmuch_message_t *nm = notmuch_messages_get(msgs);
920  append_message(h, m, q, nm, dedup);
921  /* recurse through all the replies to this message too */
922  append_replies(h, m, q, nm, dedup);
923  notmuch_message_destroy(nm);
924  }
925 }
926 
938 static void append_thread(struct HeaderCache *h, struct Mailbox *m,
939  notmuch_query_t *q, notmuch_thread_t *thread, bool dedup)
940 {
941  notmuch_messages_t *msgs = NULL;
942 
943  for (msgs = notmuch_thread_get_toplevel_messages(thread);
944  notmuch_messages_valid(msgs); notmuch_messages_move_to_next(msgs))
945  {
946  notmuch_message_t *nm = notmuch_messages_get(msgs);
947  append_message(h, m, q, nm, dedup);
948  append_replies(h, m, q, nm, dedup);
949  notmuch_message_destroy(nm);
950  }
951 }
952 
962 static notmuch_messages_t *get_messages(notmuch_query_t *query)
963 {
964  if (!query)
965  return NULL;
966 
967  notmuch_messages_t *msgs = NULL;
968 
969 #if LIBNOTMUCH_CHECK_VERSION(5, 0, 0)
970  if (notmuch_query_search_messages(query, &msgs) != NOTMUCH_STATUS_SUCCESS)
971  return NULL;
972 #elif LIBNOTMUCH_CHECK_VERSION(4, 3, 0)
973  if (notmuch_query_search_messages_st(query, &msgs) != NOTMUCH_STATUS_SUCCESS)
974  return NULL;
975 #else
976  msgs = notmuch_query_search_messages(query);
977 #endif
978 
979  return msgs;
980 }
981 
990 static bool read_mesgs_query(struct Mailbox *m, notmuch_query_t *q, bool dedup)
991 {
992  struct NmMboxData *mdata = nm_mdata_get(m);
993  if (!mdata)
994  return false;
995 
996  int limit = get_limit(mdata);
997 
998  notmuch_messages_t *msgs = get_messages(q);
999 
1000  if (!msgs)
1001  return false;
1002 
1003  struct HeaderCache *h = nm_hcache_open(m);
1004 
1005  for (; notmuch_messages_valid(msgs) && ((limit == 0) || (m->msg_count < limit));
1006  notmuch_messages_move_to_next(msgs))
1007  {
1008  if (SigInt == 1)
1009  {
1010  nm_hcache_close(h);
1011  SigInt = 0;
1012  return false;
1013  }
1014  notmuch_message_t *nm = notmuch_messages_get(msgs);
1015  append_message(h, m, q, nm, dedup);
1016  notmuch_message_destroy(nm);
1017  }
1018 
1019  nm_hcache_close(h);
1020  return true;
1021 }
1022 
1032 static notmuch_threads_t *get_threads(notmuch_query_t *query)
1033 {
1034  if (!query)
1035  return NULL;
1036 
1037  notmuch_threads_t *threads = NULL;
1038 #if LIBNOTMUCH_CHECK_VERSION(5, 0, 0)
1039  if (notmuch_query_search_threads(query, &threads) != NOTMUCH_STATUS_SUCCESS)
1040  return false;
1041 #elif LIBNOTMUCH_CHECK_VERSION(4, 3, 0)
1042  if (notmuch_query_search_threads_st(query, &threads) != NOTMUCH_STATUS_SUCCESS)
1043  return false;
1044 #else
1045  threads = notmuch_query_search_threads(query);
1046 #endif
1047 
1048  return threads;
1049 }
1050 
1060 static bool read_threads_query(struct Mailbox *m, notmuch_query_t *q, bool dedup, int limit)
1061 {
1062  struct NmMboxData *mdata = nm_mdata_get(m);
1063  if (!mdata)
1064  return false;
1065 
1066  notmuch_threads_t *threads = get_threads(q);
1067  if (!threads)
1068  return false;
1069 
1070  struct HeaderCache *h = nm_hcache_open(m);
1071 
1072  for (; notmuch_threads_valid(threads) && ((limit == 0) || (m->msg_count < limit));
1073  notmuch_threads_move_to_next(threads))
1074  {
1075  if (SigInt == 1)
1076  {
1077  nm_hcache_close(h);
1078  SigInt = 0;
1079  return false;
1080  }
1081  notmuch_thread_t *thread = notmuch_threads_get(threads);
1082  append_thread(h, m, q, thread, dedup);
1083  notmuch_thread_destroy(thread);
1084  }
1085 
1086  nm_hcache_close(h);
1087  return true;
1088 }
1089 
1096 static notmuch_message_t *get_nm_message(notmuch_database_t *db, struct Email *e)
1097 {
1098  notmuch_message_t *msg = NULL;
1099  char *id = email_get_id(e);
1100 
1101  mutt_debug(LL_DEBUG2, "nm: find message (%s)\n", id);
1102 
1103  if (id && db)
1104  notmuch_database_find_message(db, id, &msg);
1105 
1106  return msg;
1107 }
1108 
1115 static bool nm_message_has_tag(notmuch_message_t *msg, char *tag)
1116 {
1117  const char *possible_match_tag = NULL;
1118  notmuch_tags_t *tags = NULL;
1119 
1120  for (tags = notmuch_message_get_tags(msg); notmuch_tags_valid(tags);
1121  notmuch_tags_move_to_next(tags))
1122  {
1123  possible_match_tag = notmuch_tags_get(tags);
1124  if (mutt_str_equal(possible_match_tag, tag))
1125  {
1126  return true;
1127  }
1128  }
1129  return false;
1130 }
1131 
1137 static void sync_email_path_with_nm(struct Email *e, notmuch_message_t *msg)
1138 {
1139  const char *new_file = get_message_last_filename(msg);
1140  char old_file[PATH_MAX];
1141  email_get_fullpath(e, old_file, sizeof(old_file));
1142 
1143  if (!mutt_str_equal(old_file, new_file))
1144  update_message_path(e, new_file);
1145 }
1146 
1154 static int update_tags(notmuch_message_t *msg, const char *tags)
1155 {
1156  char *buf = mutt_str_dup(tags);
1157  if (!buf)
1158  return -1;
1159 
1160  notmuch_message_freeze(msg);
1161 
1162  char *tag = NULL, *end = NULL;
1163  for (char *p = buf; p && *p; p++)
1164  {
1165  if (!tag && isspace(*p))
1166  continue;
1167  if (!tag)
1168  tag = p; /* begin of the tag */
1169  if ((p[0] == ',') || (p[0] == ' '))
1170  end = p; /* terminate the tag */
1171  else if (p[1] == '\0')
1172  end = p + 1; /* end of optstr */
1173  if (!tag || !end)
1174  continue;
1175  if (tag >= end)
1176  break;
1177 
1178  end[0] = '\0';
1179 
1180  if (tag[0] == '-')
1181  {
1182  mutt_debug(LL_DEBUG1, "nm: remove tag: '%s'\n", tag + 1);
1183  notmuch_message_remove_tag(msg, tag + 1);
1184  }
1185  else if (tag[0] == '!')
1186  {
1187  mutt_debug(LL_DEBUG1, "nm: toggle tag: '%s'\n", tag + 1);
1188  if (nm_message_has_tag(msg, tag + 1))
1189  {
1190  notmuch_message_remove_tag(msg, tag + 1);
1191  }
1192  else
1193  {
1194  notmuch_message_add_tag(msg, tag + 1);
1195  }
1196  }
1197  else
1198  {
1199  mutt_debug(LL_DEBUG1, "nm: add tag: '%s'\n", (tag[0] == '+') ? tag + 1 : tag);
1200  notmuch_message_add_tag(msg, (tag[0] == '+') ? tag + 1 : tag);
1201  }
1202  end = NULL;
1203  tag = NULL;
1204  }
1205 
1206  notmuch_message_thaw(msg);
1207  FREE(&buf);
1208  return 0;
1209 }
1210 
1223 static int update_email_flags(struct Mailbox *m, struct Email *e, const char *tags)
1224 {
1225  char *buf = mutt_str_dup(tags);
1226  if (!buf)
1227  return -1;
1228 
1229  char *tag = NULL, *end = NULL;
1230  for (char *p = buf; p && *p; p++)
1231  {
1232  if (!tag && isspace(*p))
1233  continue;
1234  if (!tag)
1235  tag = p; /* begin of the tag */
1236  if ((p[0] == ',') || (p[0] == ' '))
1237  end = p; /* terminate the tag */
1238  else if (p[1] == '\0')
1239  end = p + 1; /* end of optstr */
1240  if (!tag || !end)
1241  continue;
1242  if (tag >= end)
1243  break;
1244 
1245  end[0] = '\0';
1246 
1247  if (tag[0] == '-')
1248  {
1249  tag++;
1250  if (strcmp(tag, C_NmUnreadTag) == 0)
1251  mutt_set_flag(m, e, MUTT_READ, true);
1252  else if (strcmp(tag, C_NmRepliedTag) == 0)
1253  mutt_set_flag(m, e, MUTT_REPLIED, false);
1254  else if (strcmp(tag, C_NmFlaggedTag) == 0)
1255  mutt_set_flag(m, e, MUTT_FLAG, false);
1256  }
1257  else
1258  {
1259  tag = (tag[0] == '+') ? tag + 1 : tag;
1260  if (strcmp(tag, C_NmUnreadTag) == 0)
1261  mutt_set_flag(m, e, MUTT_READ, false);
1262  else if (strcmp(tag, C_NmRepliedTag) == 0)
1263  mutt_set_flag(m, e, MUTT_REPLIED, true);
1264  else if (strcmp(tag, C_NmFlaggedTag) == 0)
1265  mutt_set_flag(m, e, MUTT_FLAG, true);
1266  }
1267  end = NULL;
1268  tag = NULL;
1269  }
1270 
1271  FREE(&buf);
1272  return 0;
1273 }
1274 
1285 static int rename_maildir_filename(const char *old, char *buf, size_t buflen, struct Email *e)
1286 {
1287  char filename[PATH_MAX];
1288  char suffix[PATH_MAX];
1289  char folder[PATH_MAX];
1290 
1291  mutt_str_copy(folder, old, sizeof(folder));
1292  char *p = strrchr(folder, '/');
1293  if (p)
1294  {
1295  *p = '\0';
1296  p++;
1297  }
1298  else
1299  p = folder;
1300 
1301  mutt_str_copy(filename, p, sizeof(filename));
1302 
1303  /* remove (new,cur,...) from folder path */
1304  p = strrchr(folder, '/');
1305  if (p)
1306  *p = '\0';
1307 
1308  /* remove old flags from filename */
1309  p = strchr(filename, ':');
1310  if (p)
1311  *p = '\0';
1312 
1313  /* compose new flags */
1314  maildir_gen_flags(suffix, sizeof(suffix), e);
1315 
1316  snprintf(buf, buflen, "%s/%s/%s%s", folder,
1317  (e->read || e->old) ? "cur" : "new", filename, suffix);
1318 
1319  if (strcmp(old, buf) == 0)
1320  return 1;
1321 
1322  if (rename(old, buf) != 0)
1323  {
1324  mutt_debug(LL_DEBUG1, "nm: rename(2) failed %s -> %s\n", old, buf);
1325  return -1;
1326  }
1327 
1328  return 0;
1329 }
1330 
1338 static int remove_filename(struct Mailbox *m, const char *path)
1339 {
1340  struct NmMboxData *mdata = nm_mdata_get(m);
1341  if (!mdata)
1342  return -1;
1343 
1344  mutt_debug(LL_DEBUG2, "nm: remove filename '%s'\n", path);
1345 
1346  notmuch_database_t *db = nm_db_get(m, true);
1347  if (!db)
1348  return -1;
1349 
1350  notmuch_message_t *msg = NULL;
1351  notmuch_status_t st = notmuch_database_find_message_by_filename(db, path, &msg);
1352  if (st || !msg)
1353  return -1;
1354 
1355  int trans = nm_db_trans_begin(m);
1356  if (trans < 0)
1357  return -1;
1358 
1359  /* note that unlink() is probably unnecessary here, it's already removed
1360  * by mh_sync_mailbox_message(), but for sure... */
1361  notmuch_filenames_t *ls = NULL;
1362  st = notmuch_database_remove_message(db, path);
1363  switch (st)
1364  {
1365  case NOTMUCH_STATUS_SUCCESS:
1366  mutt_debug(LL_DEBUG2, "nm: remove success, call unlink\n");
1367  unlink(path);
1368  break;
1369  case NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID:
1370  mutt_debug(LL_DEBUG2, "nm: remove success (duplicate), call unlink\n");
1371  unlink(path);
1372  for (ls = notmuch_message_get_filenames(msg);
1373  ls && notmuch_filenames_valid(ls); notmuch_filenames_move_to_next(ls))
1374  {
1375  path = notmuch_filenames_get(ls);
1376 
1377  mutt_debug(LL_DEBUG2, "nm: remove duplicate: '%s'\n", path);
1378  unlink(path);
1379  notmuch_database_remove_message(db, path);
1380  }
1381  break;
1382  default:
1383  mutt_debug(LL_DEBUG1, "nm: failed to remove '%s' [st=%d]\n", path, (int) st);
1384  break;
1385  }
1386 
1387  notmuch_message_destroy(msg);
1388  if (trans)
1389  nm_db_trans_end(m);
1390  return 0;
1391 }
1392 
1402 static int rename_filename(struct Mailbox *m, const char *old_file,
1403  const char *new_file, struct Email *e)
1404 {
1405  struct NmMboxData *mdata = nm_mdata_get(m);
1406  if (!mdata)
1407  return -1;
1408 
1409  notmuch_database_t *db = nm_db_get(m, true);
1410  if (!db || !new_file || !old_file || (access(new_file, F_OK) != 0))
1411  return -1;
1412 
1413  int rc = -1;
1414  notmuch_status_t st;
1415  notmuch_filenames_t *ls = NULL;
1416  notmuch_message_t *msg = NULL;
1417 
1418  mutt_debug(LL_DEBUG1, "nm: rename filename, %s -> %s\n", old_file, new_file);
1419  int trans = nm_db_trans_begin(m);
1420  if (trans < 0)
1421  return -1;
1422 
1423  mutt_debug(LL_DEBUG2, "nm: rename: add '%s'\n", new_file);
1424 #ifdef HAVE_NOTMUCH_DATABASE_INDEX_FILE
1425  st = notmuch_database_index_file(db, new_file, NULL, &msg);
1426 #else
1427  st = notmuch_database_add_message(db, new_file, &msg);
1428 #endif
1429 
1430  if ((st != NOTMUCH_STATUS_SUCCESS) && (st != NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID))
1431  {
1432  mutt_debug(LL_DEBUG1, "nm: failed to add '%s' [st=%d]\n", new_file, (int) st);
1433  goto done;
1434  }
1435 
1436  mutt_debug(LL_DEBUG2, "nm: rename: rem '%s'\n", old_file);
1437  st = notmuch_database_remove_message(db, old_file);
1438  switch (st)
1439  {
1440  case NOTMUCH_STATUS_SUCCESS:
1441  break;
1442  case NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID:
1443  mutt_debug(LL_DEBUG2, "nm: rename: syncing duplicate filename\n");
1444  notmuch_message_destroy(msg);
1445  msg = NULL;
1446  notmuch_database_find_message_by_filename(db, new_file, &msg);
1447 
1448  for (ls = notmuch_message_get_filenames(msg);
1449  msg && ls && notmuch_filenames_valid(ls); notmuch_filenames_move_to_next(ls))
1450  {
1451  const char *path = notmuch_filenames_get(ls);
1452  char newpath[PATH_MAX];
1453 
1454  if (strcmp(new_file, path) == 0)
1455  continue;
1456 
1457  mutt_debug(LL_DEBUG2, "nm: rename: syncing duplicate: %s\n", path);
1458 
1459  if (rename_maildir_filename(path, newpath, sizeof(newpath), e) == 0)
1460  {
1461  mutt_debug(LL_DEBUG2, "nm: rename dup %s -> %s\n", path, newpath);
1462  notmuch_database_remove_message(db, path);
1463 #ifdef HAVE_NOTMUCH_DATABASE_INDEX_FILE
1464  notmuch_database_index_file(db, newpath, NULL, NULL);
1465 #else
1466  notmuch_database_add_message(db, newpath, NULL);
1467 #endif
1468  }
1469  }
1470  notmuch_message_destroy(msg);
1471  msg = NULL;
1472  notmuch_database_find_message_by_filename(db, new_file, &msg);
1473  st = NOTMUCH_STATUS_SUCCESS;
1474  break;
1475  default:
1476  mutt_debug(LL_DEBUG1, "nm: failed to remove '%s' [st=%d]\n", old_file, (int) st);
1477  break;
1478  }
1479 
1480  if ((st == NOTMUCH_STATUS_SUCCESS) && e && msg)
1481  {
1482  notmuch_message_maildir_flags_to_tags(msg);
1483  update_email_tags(e, msg);
1484 
1485  char *tags = driver_tags_get(&e->tags);
1486  update_tags(msg, tags);
1487  FREE(&tags);
1488  }
1489 
1490  rc = 0;
1491 done:
1492  if (msg)
1493  notmuch_message_destroy(msg);
1494  if (trans)
1495  nm_db_trans_end(m);
1496  return rc;
1497 }
1498 
1506 static unsigned int count_query(notmuch_database_t *db, const char *qstr, int limit)
1507 {
1508  notmuch_query_t *q = notmuch_query_create(db, qstr);
1509  if (!q)
1510  return 0;
1511 
1512  unsigned int res = 0;
1513 
1514  apply_exclude_tags(q);
1515 #if LIBNOTMUCH_CHECK_VERSION(5, 0, 0)
1516  if (notmuch_query_count_messages(q, &res) != NOTMUCH_STATUS_SUCCESS)
1517  res = 0; /* may not be defined on error */
1518 #elif LIBNOTMUCH_CHECK_VERSION(4, 3, 0)
1519  if (notmuch_query_count_messages_st(q, &res) != NOTMUCH_STATUS_SUCCESS)
1520  res = 0; /* may not be defined on error */
1521 #else
1522  res = notmuch_query_count_messages(q);
1523 #endif
1524  notmuch_query_destroy(q);
1525  mutt_debug(LL_DEBUG1, "nm: count '%s', result=%d\n", qstr, res);
1526 
1527  if ((limit > 0) && (res > limit))
1528  res = limit;
1529 
1530  return res;
1531 }
1532 
1539 char *nm_email_get_folder(struct Email *e)
1540 {
1541  struct NmEmailData *edata = nm_edata_get(e);
1542  if (!edata)
1543  return NULL;
1544 
1545  return edata->folder;
1546 }
1547 
1558 char *nm_email_get_folder_rel_db(struct Mailbox *m, struct Email *e)
1559 {
1560  char *full_folder = nm_email_get_folder(e);
1561  if (!full_folder)
1562  return NULL;
1563 
1564  const char *db_path = nm_db_get_filename(m);
1565  if (!db_path)
1566  return NULL;
1567 
1568  return full_folder + strlen(db_path);
1569 }
1570 
1578 int nm_read_entire_thread(struct Mailbox *m, struct Email *e)
1579 {
1580  if (!m)
1581  return -1;
1582 
1583  struct NmMboxData *mdata = nm_mdata_get(m);
1584  if (!mdata)
1585  return -1;
1586 
1587  notmuch_query_t *q = NULL;
1588  notmuch_database_t *db = NULL;
1589  notmuch_message_t *msg = NULL;
1590  int rc = -1;
1591 
1592  if (!(db = nm_db_get(m, false)) || !(msg = get_nm_message(db, e)))
1593  goto done;
1594 
1595  mutt_debug(LL_DEBUG1, "nm: reading entire-thread messages...[current count=%d]\n",
1596  m->msg_count);
1597 
1598  progress_reset(m);
1599  const char *id = notmuch_message_get_thread_id(msg);
1600  if (!id)
1601  goto done;
1602 
1603  char *qstr = NULL;
1604  mutt_str_append_item(&qstr, "thread:", '\0');
1605  mutt_str_append_item(&qstr, id, '\0');
1606 
1607  q = notmuch_query_create(db, qstr);
1608  FREE(&qstr);
1609  if (!q)
1610  goto done;
1611  apply_exclude_tags(q);
1612  notmuch_query_set_sort(q, NOTMUCH_SORT_NEWEST_FIRST);
1613 
1614  read_threads_query(m, q, true, 0);
1615  m->mtime.tv_sec = mutt_date_epoch();
1616  m->mtime.tv_nsec = 0;
1617  rc = 0;
1618 
1619  if (m->msg_count > mdata->oldmsgcount)
1621 done:
1622  if (q)
1623  notmuch_query_destroy(q);
1624 
1625  nm_db_release(m);
1626 
1627  if (m->msg_count == mdata->oldmsgcount)
1628  mutt_message(_("No more messages in the thread"));
1629 
1630  mdata->oldmsgcount = 0;
1631  mutt_debug(LL_DEBUG1, "nm: reading entire-thread messages... done [rc=%d, count=%d]\n",
1632  rc, m->msg_count);
1633  return rc;
1634 }
1635 
1647 {
1648  if (!buf)
1649  return;
1650 
1651  // The six variations of how type= could appear.
1652  const char *variants[6] = { "&type=threads", "&type=messages",
1653  "type=threads&", "type=messages&",
1654  "type=threads", "type=messages" };
1655 
1656  int variants_size = mutt_array_size(variants);
1657  for (int i = 0; i < variants_size; i++)
1658  {
1659  if (strcasestr(buf, variants[i]) != NULL)
1660  {
1661  // variants[] is setup such that type can be determined via modulo 2.
1662  mdata->query_type = ((i % 2) == 0) ? NM_QUERY_TYPE_THREADS : NM_QUERY_TYPE_MESGS;
1663 
1664  mutt_istr_remall(buf, variants[i]);
1665  }
1666  }
1667 }
1668 
1677 char *nm_url_from_query(struct Mailbox *m, char *buf, size_t buflen)
1678 {
1679  mutt_debug(LL_DEBUG2, "(%s)\n", buf);
1680  struct NmMboxData *mdata = nm_mdata_get(m);
1681  char url[PATH_MAX + 1024 + 32]; /* path to DB + query + URL "decoration" */
1682  int added;
1683  bool using_default_data = false;
1684 
1685  // No existing data. Try to get a default NmMboxData.
1686  if (!mdata)
1687  {
1689 
1690  // Failed to get default data.
1691  if (!mdata)
1692  return NULL;
1693 
1694  using_default_data = true;
1695  }
1696 
1698 
1699  if (get_limit(mdata) == C_NmDbLimit)
1700  {
1701  added = snprintf(url, sizeof(url), "%s%s?type=%s&query=", NmUrlProtocol,
1702  nm_db_get_filename(m), query_type_to_string(mdata->query_type));
1703  }
1704  else
1705  {
1706  added = snprintf(url, sizeof(url), "%s%s?type=%s&limit=%d&query=", NmUrlProtocol,
1707  nm_db_get_filename(m),
1708  query_type_to_string(mdata->query_type), get_limit(mdata));
1709  }
1710 
1711  if (added >= sizeof(url))
1712  {
1713  // snprintf output was truncated, so can't create URL
1714  return NULL;
1715  }
1716 
1717  url_pct_encode(&url[added], sizeof(url) - added, buf);
1718 
1719  mutt_str_copy(buf, url, buflen);
1720  buf[buflen - 1] = '\0';
1721 
1722  if (using_default_data)
1723  nm_mdata_free((void **) &mdata);
1724 
1725  mutt_debug(LL_DEBUG1, "nm: url from query '%s'\n", buf);
1726  return buf;
1727 }
1728 
1739 {
1742 
1744 }
1745 
1755 {
1758 }
1759 
1766 bool nm_message_is_still_queried(struct Mailbox *m, struct Email *e)
1767 {
1768  struct NmMboxData *mdata = nm_mdata_get(m);
1769  notmuch_database_t *db = nm_db_get(m, false);
1770  char *orig_str = get_query_string(mdata, true);
1771 
1772  if (!db || !orig_str)
1773  return false;
1774 
1775  char *new_str = NULL;
1776  bool rc = false;
1777  if (mutt_str_asprintf(&new_str, "id:%s and (%s)", email_get_id(e), orig_str) < 0)
1778  return false;
1779 
1780  mutt_debug(LL_DEBUG2, "nm: checking if message is still queried: %s\n", new_str);
1781 
1782  notmuch_query_t *q = notmuch_query_create(db, new_str);
1783 
1784  switch (mdata->query_type)
1785  {
1786  case NM_QUERY_TYPE_MESGS:
1787  {
1788  notmuch_messages_t *messages = get_messages(q);
1789 
1790  if (!messages)
1791  return false;
1792 
1793  rc = notmuch_messages_valid(messages);
1794  notmuch_messages_destroy(messages);
1795  break;
1796  }
1797  case NM_QUERY_TYPE_THREADS:
1798  {
1799  notmuch_threads_t *threads = get_threads(q);
1800 
1801  if (!threads)
1802  return false;
1803 
1804  rc = notmuch_threads_valid(threads);
1805  notmuch_threads_destroy(threads);
1806  break;
1807  }
1808  }
1809 
1810  notmuch_query_destroy(q);
1811 
1812  mutt_debug(LL_DEBUG2, "nm: checking if message is still queried: %s = %s\n",
1813  new_str, rc ? "true" : "false");
1814 
1815  return rc;
1816 }
1817 
1827 int nm_update_filename(struct Mailbox *m, const char *old_file,
1828  const char *new_file, struct Email *e)
1829 {
1830  char buf[PATH_MAX];
1831  struct NmMboxData *mdata = nm_mdata_get(m);
1832  if (!mdata || !new_file)
1833  return -1;
1834 
1835  if (!old_file && nm_edata_get(e))
1836  {
1837  email_get_fullpath(e, buf, sizeof(buf));
1838  old_file = buf;
1839  }
1840 
1841  int rc = rename_filename(m, old_file, new_file, e);
1842 
1843  nm_db_release(m);
1844  m->mtime.tv_sec = mutt_date_epoch();
1845  m->mtime.tv_nsec = 0;
1846  return rc;
1847 }
1848 
1852 static enum MxStatus nm_mbox_check_stats(struct Mailbox *m, uint8_t flags)
1853 {
1854  struct UrlQuery *item = NULL;
1855  struct Url *url = NULL;
1856  char *db_filename = NULL, *db_query = NULL;
1857  notmuch_database_t *db = NULL;
1858  enum MxStatus rc = MX_STATUS_ERROR;
1859  int limit = C_NmDbLimit;
1860  mutt_debug(LL_DEBUG1, "nm: count\n");
1861 
1862  url = url_parse(mailbox_path(m));
1863  if (!url)
1864  {
1865  mutt_error(_("failed to parse notmuch url: %s"), mailbox_path(m));
1866  goto done;
1867  }
1868 
1869  STAILQ_FOREACH(item, &url->query_strings, entries)
1870  {
1871  if (item->value && (strcmp(item->name, "query") == 0))
1872  db_query = item->value;
1873  else if (item->value && (strcmp(item->name, "limit") == 0))
1874  {
1875  // Try to parse the limit
1876  if (mutt_str_atoi(item->value, &limit) != 0)
1877  {
1878  mutt_error(_("failed to parse limit: %s"), item->value);
1879  goto done;
1880  }
1881  }
1882  }
1883 
1884  if (!db_query)
1885  goto done;
1886 
1887  db_filename = url->path;
1888  if (!db_filename)
1889  {
1890  if (C_NmDefaultUrl)
1891  {
1893  db_filename = C_NmDefaultUrl + NmUrlProtocolLen;
1894  else
1895  db_filename = C_NmDefaultUrl;
1896  }
1897  else if (C_Folder)
1898  db_filename = C_Folder;
1899  }
1900 
1901  /* don't be verbose about connection, as we're called from
1902  * sidebar/mailbox very often */
1903  db = nm_db_do_open(db_filename, false, false);
1904  if (!db)
1905  goto done;
1906 
1907  /* all emails */
1908  m->msg_count = count_query(db, db_query, limit);
1909  while (m->email_max < m->msg_count)
1910  mx_alloc_memory(m);
1911 
1912  // holder variable for extending query to unread/flagged
1913  char *qstr = NULL;
1914 
1915  // unread messages
1916  mutt_str_asprintf(&qstr, "( %s ) tag:%s", db_query, C_NmUnreadTag);
1917  m->msg_unread = count_query(db, qstr, limit);
1918  FREE(&qstr);
1919 
1920  // flagged messages
1921  mutt_str_asprintf(&qstr, "( %s ) tag:%s", db_query, C_NmFlaggedTag);
1922  m->msg_flagged = count_query(db, qstr, limit);
1923  FREE(&qstr);
1924 
1925  rc = (m->msg_new > 0) ? MX_STATUS_NEW_MAIL : MX_STATUS_OK;
1926 done:
1927  if (db)
1928  {
1929  nm_db_free(db);
1930  mutt_debug(LL_DEBUG1, "nm: count close DB\n");
1931  }
1932  url_free(&url);
1933 
1934  mutt_debug(LL_DEBUG1, "nm: count done [rc=%d]\n", rc);
1935  return rc;
1936 }
1937 
1942 static struct Mailbox *get_default_mailbox(void)
1943 {
1944  // Create a new notmuch mailbox from scratch and add plumbing for DB access.
1945  char *default_url = nm_get_default_url();
1946  struct Mailbox *m = mx_path_resolve(default_url);
1947 
1948  FREE(&default_url);
1949 
1950  // These are no-ops for an initialized mailbox.
1951  init_mailbox(m);
1952  mx_mbox_ac_link(m);
1953 
1954  return m;
1955 }
1956 
1965 int nm_record_message(struct Mailbox *m, char *path, struct Email *e)
1966 {
1967  notmuch_database_t *db = NULL;
1968  notmuch_status_t st;
1969  notmuch_message_t *msg = NULL;
1970  int rc = -1;
1971 
1972  struct NmMboxData *mdata = nm_mdata_get(m);
1973 
1974  // If no notmuch data, fall back to the default mailbox.
1975  //
1976  // IMPORTANT: DO NOT FREE THIS MAILBOX. Two reasons:
1977  // 1) If user has default mailbox in config, we'll be removing it. That's not
1978  // good program behavior!
1979  // 2) If not in user's config, keep mailbox around for future nm_record calls.
1980  // It saves NeoMutt from allocating/deallocating repeatedly.
1981  if (!mdata)
1982  {
1983  mutt_debug(LL_DEBUG1, "nm: non-nm mailbox. trying the default nm mailbox.");
1984  m = get_default_mailbox();
1985  mdata = nm_mdata_get(m);
1986  }
1987 
1988  if (!path || !mdata || (access(path, F_OK) != 0))
1989  return 0;
1990  db = nm_db_get(m, true);
1991  if (!db)
1992  return -1;
1993 
1994  mutt_debug(LL_DEBUG1, "nm: record message: %s\n", path);
1995  int trans = nm_db_trans_begin(m);
1996  if (trans < 0)
1997  goto done;
1998 
1999 #ifdef HAVE_NOTMUCH_DATABASE_INDEX_FILE
2000  st = notmuch_database_index_file(db, path, NULL, &msg);
2001 #else
2002  st = notmuch_database_add_message(db, path, &msg);
2003 #endif
2004 
2005  if ((st != NOTMUCH_STATUS_SUCCESS) && (st != NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID))
2006  {
2007  mutt_debug(LL_DEBUG1, "nm: failed to add '%s' [st=%d]\n", path, (int) st);
2008  goto done;
2009  }
2010 
2011  if ((st == NOTMUCH_STATUS_SUCCESS) && msg)
2012  {
2013  notmuch_message_maildir_flags_to_tags(msg);
2014  if (e)
2015  {
2016  char *tags = driver_tags_get(&e->tags);
2017  update_tags(msg, tags);
2018  FREE(&tags);
2019  }
2020  if (C_NmRecordTags)
2022  }
2023 
2024  rc = 0;
2025 done:
2026  if (msg)
2027  notmuch_message_destroy(msg);
2028  if (trans == 1)
2029  nm_db_trans_end(m);
2030 
2031  nm_db_release(m);
2032 
2033  return rc;
2034 }
2035 
2046 int nm_get_all_tags(struct Mailbox *m, char **tag_list, int *tag_count)
2047 {
2048  struct NmMboxData *mdata = nm_mdata_get(m);
2049  if (!mdata)
2050  return -1;
2051 
2052  notmuch_database_t *db = NULL;
2053  notmuch_tags_t *tags = NULL;
2054  const char *tag = NULL;
2055  int rc = -1;
2056 
2057  if (!(db = nm_db_get(m, false)) || !(tags = notmuch_database_get_all_tags(db)))
2058  goto done;
2059 
2060  *tag_count = 0;
2061  mutt_debug(LL_DEBUG1, "nm: get all tags\n");
2062 
2063  while (notmuch_tags_valid(tags))
2064  {
2065  tag = notmuch_tags_get(tags);
2066  /* Skip empty string */
2067  if (*tag)
2068  {
2069  if (tag_list)
2070  tag_list[*tag_count] = mutt_str_dup(tag);
2071  (*tag_count)++;
2072  }
2073  notmuch_tags_move_to_next(tags);
2074  }
2075 
2076  rc = 0;
2077 done:
2078  if (tags)
2079  notmuch_tags_destroy(tags);
2080 
2081  nm_db_release(m);
2082 
2083  mutt_debug(LL_DEBUG1, "nm: get all tags done [rc=%d tag_count=%u]\n", rc, *tag_count);
2084  return rc;
2085 }
2086 
2090 static bool nm_ac_owns_path(struct Account *a, const char *path)
2091 {
2092  return true;
2093 }
2094 
2098 static bool nm_ac_add(struct Account *a, struct Mailbox *m)
2099 {
2100  if (a->adata)
2101  return true;
2102 
2103  struct NmAccountData *adata = nm_adata_new();
2104  a->adata = adata;
2106 
2107  return true;
2108 }
2109 
2113 static enum MxOpenReturns nm_mbox_open(struct Mailbox *m)
2114 {
2115  if (init_mailbox(m) != 0)
2116  return MX_OPEN_ERROR;
2117 
2118  struct NmMboxData *mdata = nm_mdata_get(m);
2119  if (!mdata)
2120  return MX_OPEN_ERROR;
2121 
2122  mutt_debug(LL_DEBUG1, "nm: reading messages...[current count=%d]\n", m->msg_count);
2123 
2124  progress_reset(m);
2125 
2126  enum MxOpenReturns rc = MX_OPEN_ERROR;
2127 
2128  notmuch_query_t *q = get_query(m, false);
2129  if (q)
2130  {
2131  rc = MX_OPEN_OK;
2132  switch (mdata->query_type)
2133  {
2134  case NM_QUERY_TYPE_MESGS:
2135  if (!read_mesgs_query(m, q, false))
2136  rc = MX_OPEN_ABORT;
2137  break;
2138  case NM_QUERY_TYPE_THREADS:
2139  if (!read_threads_query(m, q, false, get_limit(mdata)))
2140  rc = MX_OPEN_ABORT;
2141  break;
2142  }
2143  notmuch_query_destroy(q);
2144  }
2145 
2146  nm_db_release(m);
2147 
2148  m->mtime.tv_sec = mutt_date_epoch();
2149  m->mtime.tv_nsec = 0;
2150 
2151  mdata->oldmsgcount = 0;
2152 
2153  mutt_debug(LL_DEBUG1, "nm: reading messages... done [rc=%d, count=%d]\n", rc, m->msg_count);
2154  return rc;
2155 }
2156 
2162 static enum MxStatus nm_mbox_check(struct Mailbox *m)
2163 {
2164  struct NmMboxData *mdata = nm_mdata_get(m);
2165  time_t mtime = 0;
2166  if (!mdata || (nm_db_get_mtime(m, &mtime) != 0))
2167  return MX_STATUS_ERROR;
2168 
2169  int new_flags = 0;
2170  bool occult = false;
2171 
2172  if (m->mtime.tv_sec >= mtime)
2173  {
2174  mutt_debug(LL_DEBUG2, "nm: check unnecessary (db=%lu mailbox=%lu)\n", mtime,
2175  m->mtime.tv_sec);
2176  return MX_STATUS_OK;
2177  }
2178 
2179  mutt_debug(LL_DEBUG1, "nm: checking (db=%lu mailbox=%lu)\n", mtime, m->mtime.tv_sec);
2180 
2181  notmuch_query_t *q = get_query(m, false);
2182  if (!q)
2183  goto done;
2184 
2185  mutt_debug(LL_DEBUG1, "nm: start checking (count=%d)\n", m->msg_count);
2186  mdata->oldmsgcount = m->msg_count;
2187  mdata->noprogress = true;
2188 
2189  for (int i = 0; i < m->msg_count; i++)
2190  {
2191  struct Email *e = m->emails[i];
2192  if (!e)
2193  break;
2194 
2195  e->active = false;
2196  }
2197 
2198  int limit = get_limit(mdata);
2199 
2200  notmuch_messages_t *msgs = get_messages(q);
2201 
2202  // TODO: Analyze impact of removing this version guard.
2203 #if LIBNOTMUCH_CHECK_VERSION(5, 0, 0)
2204  if (!msgs)
2205  return MX_STATUS_OK;
2206 #elif LIBNOTMUCH_CHECK_VERSION(4, 3, 0)
2207  if (!msgs)
2208  goto done;
2209 #endif
2210 
2211  struct HeaderCache *h = nm_hcache_open(m);
2212 
2213  for (int i = 0; notmuch_messages_valid(msgs) && ((limit == 0) || (i < limit));
2214  notmuch_messages_move_to_next(msgs), i++)
2215  {
2216  notmuch_message_t *msg = notmuch_messages_get(msgs);
2217  struct Email *e = get_mutt_email(m, msg);
2218 
2219  if (!e)
2220  {
2221  /* new email */
2222  append_message(h, m, NULL, msg, false);
2223  notmuch_message_destroy(msg);
2224  continue;
2225  }
2226 
2227  /* message already exists, merge flags */
2228  e->active = true;
2229 
2230  /* Check to see if the message has moved to a different subdirectory.
2231  * If so, update the associated filename. */
2232  const char *new_file = get_message_last_filename(msg);
2233  char old_file[PATH_MAX];
2234  email_get_fullpath(e, old_file, sizeof(old_file));
2235 
2236  if (!mutt_str_equal(old_file, new_file))
2237  update_message_path(e, new_file);
2238 
2239  if (!e->changed)
2240  {
2241  /* if the user hasn't modified the flags on this message, update the
2242  * flags we just detected. */
2243  struct Email e_tmp = { 0 };
2244  e_tmp.edata = maildir_edata_new();
2245  maildir_parse_flags(&e_tmp, new_file);
2246  maildir_update_flags(m, e, &e_tmp);
2247  maildir_edata_free(&e_tmp.edata);
2248  }
2249 
2250  if (update_email_tags(e, msg) == 0)
2251  new_flags++;
2252 
2253  notmuch_message_destroy(msg);
2254  }
2255 
2256  nm_hcache_close(h);
2257 
2258  for (int i = 0; i < m->msg_count; i++)
2259  {
2260  struct Email *e = m->emails[i];
2261  if (!e)
2262  break;
2263 
2264  if (!e->active)
2265  {
2266  occult = true;
2267  break;
2268  }
2269  }
2270 
2271  if (m->msg_count > mdata->oldmsgcount)
2273 done:
2274  if (q)
2275  notmuch_query_destroy(q);
2276 
2277  nm_db_release(m);
2278 
2279  m->mtime.tv_sec = mutt_date_epoch();
2280  m->mtime.tv_nsec = 0;
2281 
2282  mutt_debug(LL_DEBUG1, "nm: ... check done [count=%d, new_flags=%d, occult=%d]\n",
2283  m->msg_count, new_flags, occult);
2284 
2285  if (occult)
2286  return MX_STATUS_REOPENED;
2287  if (m->msg_count > mdata->oldmsgcount)
2288  return MX_STATUS_NEW_MAIL;
2289  if (new_flags)
2290  return MX_STATUS_FLAGS;
2291  return MX_STATUS_OK;
2292 }
2293 
2297 static enum MxStatus nm_mbox_sync(struct Mailbox *m)
2298 {
2299  struct NmMboxData *mdata = nm_mdata_get(m);
2300  if (!mdata)
2301  return MX_STATUS_ERROR;
2302 
2303  enum MxStatus rc = MX_STATUS_OK;
2304  struct Progress progress;
2305  char *url = mutt_str_dup(mailbox_path(m));
2306  bool changed = false;
2307 
2308  mutt_debug(LL_DEBUG1, "nm: sync start\n");
2309 
2310  if (m->verbose)
2311  {
2312  /* all is in this function so we don't use data->progress here */
2313  char msg[PATH_MAX];
2314  snprintf(msg, sizeof(msg), _("Writing %s..."), mailbox_path(m));
2316  }
2317 
2318  struct HeaderCache *h = nm_hcache_open(m);
2319 
2320  int mh_sync_errors = 0;
2321  for (int i = 0; i < m->msg_count; i++)
2322  {
2323  char old_file[PATH_MAX], new_file[PATH_MAX];
2324  struct Email *e = m->emails[i];
2325  if (!e)
2326  break;
2327 
2328  struct NmEmailData *edata = nm_edata_get(e);
2329 
2330  if (m->verbose)
2331  mutt_progress_update(&progress, i, -1);
2332 
2333  *old_file = '\0';
2334  *new_file = '\0';
2335 
2336  if (edata->oldpath)
2337  {
2338  mutt_str_copy(old_file, edata->oldpath, sizeof(old_file));
2339  old_file[sizeof(old_file) - 1] = '\0';
2340  mutt_debug(LL_DEBUG2, "nm: fixing obsolete path '%s'\n", old_file);
2341  }
2342  else
2343  email_get_fullpath(e, old_file, sizeof(old_file));
2344 
2345  mutt_buffer_strcpy(&m->pathbuf, edata->folder);
2346  m->type = edata->type;
2347 
2348  bool ok = maildir_sync_mailbox_message(m, i, h);
2349  if (!ok)
2350  {
2351  // Syncing file failed, query notmuch for new filepath.
2352  notmuch_database_t *db = nm_db_get(m, true);
2353  if (db)
2354  {
2355  notmuch_message_t *msg = get_nm_message(db, e);
2356 
2357  sync_email_path_with_nm(e, msg);
2358 
2359  ok = maildir_sync_mailbox_message(m, i, h);
2360  }
2361  nm_db_release(m);
2362  }
2363 
2364  mutt_buffer_strcpy(&m->pathbuf, url);
2365  m->type = MUTT_NOTMUCH;
2366 
2367  if (!ok)
2368  {
2369  mh_sync_errors += 1;
2370  rc = MX_STATUS_ERROR;
2371  continue;
2372  }
2373 
2374  if (!e->deleted)
2375  email_get_fullpath(e, new_file, sizeof(new_file));
2376 
2377  if (e->deleted || (strcmp(old_file, new_file) != 0))
2378  {
2379  if (e->deleted && (remove_filename(m, old_file) == 0))
2380  changed = true;
2381  else if (*new_file && *old_file && (rename_filename(m, old_file, new_file, e) == 0))
2382  changed = true;
2383  }
2384 
2385  FREE(&edata->oldpath);
2386  }
2387 
2388  if (mh_sync_errors > 0)
2389  {
2390  mutt_error(
2391  ngettext(
2392  "Unable to sync %d message due to external mailbox modification",
2393  "Unable to sync %d messages due to external mailbox modification", mh_sync_errors),
2394  mh_sync_errors);
2395  }
2396 
2397  mutt_buffer_strcpy(&m->pathbuf, url);
2398  m->type = MUTT_NOTMUCH;
2399 
2400  nm_db_release(m);
2401 
2402  if (changed)
2403  {
2404  m->mtime.tv_sec = mutt_date_epoch();
2405  m->mtime.tv_nsec = 0;
2406  }
2407 
2408  nm_hcache_close(h);
2409 
2410  FREE(&url);
2411  mutt_debug(LL_DEBUG1, "nm: .... sync done [rc=%d]\n", rc);
2412  return rc;
2413 }
2414 
2420 static enum MxStatus nm_mbox_close(struct Mailbox *m)
2421 {
2422  return MX_STATUS_OK;
2423 }
2424 
2428 static bool nm_msg_open(struct Mailbox *m, struct Message *msg, int msgno)
2429 {
2430  struct Email *e = m->emails[msgno];
2431  if (!e)
2432  return false;
2433 
2434  char path[PATH_MAX];
2435  char *folder = nm_email_get_folder(e);
2436 
2437  snprintf(path, sizeof(path), "%s/%s", folder, e->path);
2438 
2439  msg->fp = fopen(path, "r");
2440  if (!msg->fp && (errno == ENOENT) && ((m->type == MUTT_MAILDIR) || (m->type == MUTT_NOTMUCH)))
2441  {
2442  msg->fp = maildir_open_find_message(folder, e->path, NULL);
2443  }
2444 
2445  return msg->fp != NULL;
2446 }
2447 
2452 static int nm_msg_commit(struct Mailbox *m, struct Message *msg)
2453 {
2454  mutt_error(_("Can't write to virtual folder"));
2455  return -1;
2456 }
2457 
2461 static int nm_msg_close(struct Mailbox *m, struct Message *msg)
2462 {
2463  mutt_file_fclose(&(msg->fp));
2464  return 0;
2465 }
2466 
2470 static int nm_tags_edit(struct Mailbox *m, const char *tags, char *buf, size_t buflen)
2471 {
2472  *buf = '\0';
2473  if (mutt_get_field("Add/remove labels: ", buf, buflen, MUTT_NM_TAG, false, NULL, NULL) != 0)
2474  {
2475  return -1;
2476  }
2477  return 1;
2478 }
2479 
2483 static int nm_tags_commit(struct Mailbox *m, struct Email *e, char *buf)
2484 {
2485  if (*buf == '\0')
2486  return 0; /* no tag change, so nothing to do */
2487 
2488  struct NmMboxData *mdata = nm_mdata_get(m);
2489  if (!mdata)
2490  return -1;
2491 
2492  notmuch_database_t *db = NULL;
2493  notmuch_message_t *msg = NULL;
2494  int rc = -1;
2495 
2496  if (!(db = nm_db_get(m, true)) || !(msg = get_nm_message(db, e)))
2497  goto done;
2498 
2499  mutt_debug(LL_DEBUG1, "nm: tags modify: '%s'\n", buf);
2500 
2501  update_tags(msg, buf);
2502  update_email_flags(m, e, buf);
2503  update_email_tags(e, msg);
2504  mutt_set_header_color(m, e);
2505 
2506  rc = 0;
2507  e->changed = true;
2508 done:
2509  nm_db_release(m);
2510  if (e->changed)
2511  {
2512  m->mtime.tv_sec = mutt_date_epoch();
2513  m->mtime.tv_nsec = 0;
2514  }
2515  mutt_debug(LL_DEBUG1, "nm: tags modify done [rc=%d]\n", rc);
2516  return rc;
2517 }
2518 
2522 enum MailboxType nm_path_probe(const char *path, const struct stat *st)
2523 {
2524  if (!mutt_istr_startswith(path, NmUrlProtocol))
2525  return MUTT_UNKNOWN;
2526 
2527  return MUTT_NOTMUCH;
2528 }
2529 
2533 static int nm_path_canon(char *buf, size_t buflen)
2534 {
2535  return 0;
2536 }
2537 
2541 static int nm_path_pretty(char *buf, size_t buflen, const char *folder)
2542 {
2543  /* Succeed, but don't do anything, for now */
2544  return 0;
2545 }
2546 
2550 static int nm_path_parent(char *buf, size_t buflen)
2551 {
2552  /* Succeed, but don't do anything, for now */
2553  return 0;
2554 }
2555 
2556 // clang-format off
2561  .type = MUTT_NOTMUCH,
2562  .name = "notmuch",
2563  .is_local = false,
2564  .ac_owns_path = nm_ac_owns_path,
2565  .ac_add = nm_ac_add,
2566  .mbox_open = nm_mbox_open,
2567  .mbox_open_append = NULL,
2568  .mbox_check = nm_mbox_check,
2569  .mbox_check_stats = nm_mbox_check_stats,
2570  .mbox_sync = nm_mbox_sync,
2571  .mbox_close = nm_mbox_close,
2572  .msg_open = nm_msg_open,
2573  .msg_open_new = maildir_msg_open_new,
2574  .msg_commit = nm_msg_commit,
2575  .msg_close = nm_msg_close,
2576  .msg_padding_size = NULL,
2577  .msg_save_hcache = NULL,
2578  .tags_edit = nm_tags_edit,
2579  .tags_commit = nm_tags_commit,
2580  .path_probe = nm_path_probe,
2581  .path_canon = nm_path_canon,
2582  .path_pretty = nm_path_pretty,
2583  .path_parent = nm_path_parent,
2584  .path_is_empty = NULL,
2585 };
2586 // clang-format on
remove_filename
static int remove_filename(struct Mailbox *m, const char *path)
Delete a file.
Definition: notmuch.c:1338
mutt_str_append_item
void mutt_str_append_item(char **str, const char *item, char sep)
Add string to another separated by sep.
Definition: string.c:466
Mailbox::mdata_free
void(* mdata_free)(void **ptr)
Free the private data attached to the Mailbox.
Definition: mailbox.h:142
update_tags
static int update_tags(notmuch_message_t *msg, const char *tags)
Update the tags on a message.
Definition: notmuch.c:1154
maildir_sync_mailbox_message
bool maildir_sync_mailbox_message(struct Mailbox *m, int msgno, struct HeaderCache *hc)
Save changes to the mailbox.
Definition: maildir.c:928
C_NmQueryWindowCurrentPosition
int C_NmQueryWindowCurrentPosition
Config: (notmuch) Position of current search window.
Definition: config.c:43
Email::msgno
int msgno
Number displayed to the user.
Definition: email.h:87
nm_db_get_mtime
int nm_db_get_mtime(struct Mailbox *m, time_t *mtime)
Get the database modification time.
Definition: db.c:255
UrlQuery::name
char * name
Query name.
Definition: url.h:58
nm_db_trans_begin
int nm_db_trans_begin(struct Mailbox *m)
Start a Notmuch database transaction.
Definition: db.c:206
mutt_hcache_open
struct HeaderCache * mutt_hcache_open(const char *path, const char *folder, hcache_namer_t namer)
Multiplexor for StoreOps::open.
Definition: hcache.c:322
SigInt
WHERE SIG_ATOMIC_VOLATILE_T SigInt
true after SIGINT is received
Definition: mutt_globals.h:74
lib.h
MUTT_FLAG
@ MUTT_FLAG
Flagged messages.
Definition: mutt.h:102
lib.h
get_query
static notmuch_query_t * get_query(struct Mailbox *m, bool writable)
Create a new query.
Definition: notmuch.c:491
maildir_parse_stream
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:864
maildir_gen_flags
void maildir_gen_flags(char *dest, size_t destlen, struct Email *e)
Generate the Maildir flags for an email.
Definition: maildir.c:178
MxStatus
MxStatus
Return values from mx_mbox_check(), mx_mbox_sync(), and mx_mbox_close()
Definition: mx.h:71
MxOps::type
enum MailboxType type
Mailbox type, e.g. MUTT_IMAP.
Definition: mx.h:118
_
#define _(a)
Definition: message.h:28
NONULL
#define NONULL(x)
Definition: string2.h:37
Mailbox
A mailbox.
Definition: mailbox.h:81
Mailbox::emails
struct Email ** emails
Array of Emails.
Definition: mailbox.h:99
mutt_strn_equal
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:593
NT_MAILBOX_INVALID
@ NT_MAILBOX_INVALID
Email list was changed.
Definition: mailbox.h:173
private.h
Email::thread
struct MuttThread * thread
Thread of Emails.
Definition: email.h:95
get_default_mailbox
static struct Mailbox * get_default_mailbox(void)
Get Mailbox for notmuch without any parameters.
Definition: notmuch.c:1942
mailbox_size_add
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:200
nm_db_get_filename
const char * nm_db_get_filename(struct Mailbox *m)
Get the filename of the Notmuch database.
Definition: db.c:55
driver_tags_replace
bool driver_tags_replace(struct TagList *head, char *tags)
Replace all tags.
Definition: tags.c:185
mutt_file_fclose
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:153
nm_path_parent
static int nm_path_parent(char *buf, size_t buflen)
Find the parent of a Mailbox path - Implements MxOps::path_parent()
Definition: notmuch.c:2550
nm_ac_owns_path
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()
Definition: notmuch.c:2090
nm_ac_add
static bool nm_ac_add(struct Account *a, struct Mailbox *m)
Add a Mailbox to an Account - Implements MxOps::ac_add()
Definition: notmuch.c:2098
string_to_query_type
enum NmQueryType string_to_query_type(const char *str)
Lookup a query type.
Definition: notmuch.c:126
mutt_istr_remall
int mutt_istr_remall(char *str, const char *target)
Remove all occurrences of substring, ignoring case.
Definition: string.c:1037
C_NmUnreadTag
char * C_NmUnreadTag
Config: (notmuch) Tag to use for unread messages.
Definition: config.c:49
MxOpenReturns
MxOpenReturns
Return values for mbox_open()
Definition: mx.h:84
C_NmRepliedTag
char * C_NmRepliedTag
Config: (notmuch) Tag to use for replied messages.
Definition: config.c:48
MUTT_PROGRESS_WRITE
@ MUTT_PROGRESS_WRITE
Progress tracks elements, according to $write_inc
Definition: progress.h:43
MxOps
The Mailbox API.
Definition: mx.h:116
update_message_path
static int update_message_path(struct Email *e, const char *path)
Set the path for a message.
Definition: notmuch.c:573
lib.h
mutt_str_dup
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:370
mutt_globals.h
LL_DEBUG1
@ LL_DEBUG1
Log at debug level 1.
Definition: logging.h:40
Mailbox::flags
uint8_t flags
e.g. MB_NORMAL
Definition: mailbox.h:134
windowed_query_from_query
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:323
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.
Definition: notmuch.c:1558
FREE
#define FREE(x)
Definition: memory.h:40
MX_OPEN_OK
@ MX_OPEN_OK
Open succeeded.
Definition: mx.h:86
get_message_last_filename
static const char * get_message_last_filename(notmuch_message_t *msg)
Get a message's last filename.
Definition: notmuch.c:701
get_threads
static notmuch_threads_t * get_threads(notmuch_query_t *query)
load threads for a query
Definition: notmuch.c:1032
Email::path
char * path
Path of Email (for local Mailboxes)
Definition: email.h:92
UrlQuery
Parsed Query String.
Definition: url.h:56
maildir_msg_open_new
bool maildir_msg_open_new(struct Mailbox *m, struct Message *msg, const struct Email *e)
Open a new message in a Mailbox - Implements MxOps::msg_open_new()
Definition: maildir.c:1458
nm_db_do_open
notmuch_database_t * nm_db_do_open(const char *filename, bool writable, bool verbose)
Open a Notmuch database.
Definition: db.c:85
PATH_MAX
#define PATH_MAX
Definition: mutt.h:44
nm_query_window_backward
void nm_query_window_backward(void)
Function to move the current search window backward in time.
Definition: notmuch.c:1754
nm_mbox_sync
static enum MxStatus nm_mbox_sync(struct Mailbox *m)
Save changes to the Mailbox - Implements MxOps::mbox_sync()
Definition: notmuch.c:2297
C_NmQueryWindowCurrentSearch
char * C_NmQueryWindowCurrentSearch
Config: (notmuch) Current search parameters.
Definition: config.c:44
nm_mdata_get
struct NmMboxData * nm_mdata_get(struct Mailbox *m)
Get the Notmuch Mailbox data.
Definition: mdata.c:89
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?
Definition: notmuch.c:1766
query_type_to_string
static const char * query_type_to_string(enum NmQueryType query_type)
Turn a query type into a string.
Definition: notmuch.c:242
get_limit
static int get_limit(struct NmMboxData *mdata)
Get the database limit.
Definition: notmuch.c:441
url_parse
struct Url * url_parse(const char *src)
Fill in Url.
Definition: url.c:234
mx_path_resolve
struct Mailbox * mx_path_resolve(const char *path)
Get a Mailbox for a path.
Definition: mx.c:1681
mdata.h
mutt_array_size
#define mutt_array_size(x)
Definition: memory.h:33
mutt_str_atoi
int mutt_str_atoi(const char *str, int *dst)
Convert ASCII string to an integer.
Definition: string.c:252
C_NmQueryType
char * C_NmQueryType
Config: (notmuch) Default query type: 'threads' or 'messages'.
Definition: config.c:42
STAILQ_FOREACH
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:349
nm_read_entire_thread
int nm_read_entire_thread(struct Mailbox *m, struct Email *e)
Get the entire thread of an email.
Definition: notmuch.c:1578
cs_subset_str_native_set
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:292
nm_record_message
int nm_record_message(struct Mailbox *m, char *path, struct Email *e)
Add a message to the Notmuch database.
Definition: notmuch.c:1965
C_NmExcludeTags
char * C_NmExcludeTags
Config: (notmuch) Exclude messages with these tags.
Definition: config.c:39
MX_STATUS_REOPENED
@ MX_STATUS_REOPENED
Mailbox was reopened.
Definition: mx.h:77
query_window_check_timebase
static bool query_window_check_timebase(const char *timebase)
Checks if a given timebase string is valid.
Definition: notmuch.c:259
MUTT_NAMED
#define MUTT_NAMED
Definition: mutt_commands.h:74
parse_mailboxes
enum CommandResult parse_mailboxes(struct Buffer *buf, struct Buffer *s, intptr_t data, struct Buffer *err)
Parse the 'mailboxes' command - Implements Command::parse()
Definition: command_parse.c:913
Account::adata_free
void(* adata_free)(void **ptr)
Free the private data attached to the Account.
Definition: account.h:49
timespec::tv_nsec
long tv_nsec
Definition: file.h:52
MUTT_READ
@ MUTT_READ
Messages that have been read.
Definition: mutt.h:96
NmMboxData::oldmsgcount
int oldmsgcount
Definition: mdata.h:43
email_get_id
static char * email_get_id(struct Email *e)
Get the unique Notmuch Id.
Definition: notmuch.c:213
mutt_hash_find
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:354
nm_parse_type_from_query
void nm_parse_type_from_query(struct NmMboxData *mdata, char *buf)
Parse a query type out of a query.
Definition: notmuch.c:1646
C_NmQueryWindowDuration
int C_NmQueryWindowDuration
Config: (notmuch) Time duration of the current search window.
Definition: config.c:45
url_pct_encode
void url_pct_encode(char *buf, size_t buflen, const char *src)
Percent-encode a string.
Definition: url.c:151
parse_unmailboxes
enum CommandResult parse_unmailboxes(struct Buffer *buf, struct Buffer *s, intptr_t data, struct Buffer *err)
Parse the 'unmailboxes' command - Implements Command::parse()
Definition: command_parse.c:1970
mutt_str_equal
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:871
driver_tags_get_transformed
char * driver_tags_get_transformed(struct TagList *list)
Get transformed tags.
Definition: tags.c:132
update_email_flags
static int update_email_flags(struct Mailbox *m, struct Email *e, const char *tags)
Update the Email's flags.
Definition: notmuch.c:1223
MX_STATUS_NEW_MAIL
@ MX_STATUS_NEW_MAIL
New mail received in Mailbox.
Definition: mx.h:75
lib.h
nm_msg_close
static int nm_msg_close(struct Mailbox *m, struct Message *msg)
Close an email - Implements MxOps::msg_close()
Definition: notmuch.c:2461
init_email
static int init_email(struct Email *e, const char *path, notmuch_message_t *msg)
Set up an email's Notmuch data.
Definition: notmuch.c:657
Email::old
bool old
Email is seen, but unread.
Definition: email.h:50
command_parse.h
protos.h
mutt_progress_init
void mutt_progress_init(struct Progress *progress, const char *msg, enum ProgressType type, size_t size)
Set up a progress bar.
Definition: progress.c:153
Email::active
bool active
Message is not to be removed.
Definition: email.h:59
Command
A user-callable command.
Definition: mutt_commands.h:45
nm_get_all_tags
int nm_get_all_tags(struct Mailbox *m, char **tag_list, int *tag_count)
Fill a list with all notmuch tags.
Definition: notmuch.c:2046
mx_alloc_memory
void mx_alloc_memory(struct Mailbox *m)
Create storage for the emails.
Definition: mx.c:1230
rename_filename
static int rename_filename(struct Mailbox *m, const char *old_file, const char *new_file, struct Email *e)
Rename the file.
Definition: notmuch.c:1402
timespec::tv_sec
time_t tv_sec
Definition: file.h:51
get_query_string
static char * get_query_string(struct NmMboxData *mdata, bool window)
builds the notmuch vfolder search string
Definition: notmuch.c:383
maildir_update_flags
bool maildir_update_flags(struct Mailbox *m, struct Email *e_old, struct Email *e_new)
Update the mailbox flags.
Definition: shared.c:124
Mailbox::type
enum MailboxType type
Mailbox type.
Definition: mailbox.h:105
Mailbox::mdata
void * mdata
Driver specific data.
Definition: mailbox.h:136
get_nm_message
static notmuch_message_t * get_nm_message(notmuch_database_t *db, struct Email *e)
Find a Notmuch message.
Definition: notmuch.c:1096
Url::query_strings
struct UrlQueryList query_strings
List of query strings.
Definition: url.h:75
HeaderCache::folder
char * folder
Definition: lib.h:87
lib.h
Account
A group of associated Mailboxes.
Definition: account.h:36
C_Folder
WHERE char * C_Folder
Config: Base folder for a set of mailboxes.
Definition: mutt_globals.h:96
Progress
A progress bar.
Definition: progress.h:50
nm_email_get_folder
char * nm_email_get_folder(struct Email *e)
Get the folder for a Email.
Definition: notmuch.c:1539
Url
A parsed URL proto://user:password@host:port/path?a=1&b=2
Definition: url.h:67
lib.h
url_free
void url_free(struct Url **ptr)
Free the contents of a URL.
Definition: url.c:123
Mailbox::msg_count
int msg_count
Total number of messages.
Definition: mailbox.h:91
C_NmRecordTags
char * C_NmRecordTags
Config: (notmuch) Tags to apply to the 'record' mailbox (sent mail)
Definition: config.c:47
Mailbox::mtime
struct timespec mtime
Time Mailbox was last changed.
Definition: mailbox.h:107
MUTT_UNKNOWN
@ MUTT_UNKNOWN
Mailbox wasn't recognised.
Definition: mailbox.h:47
nm_hcache_close
static void nm_hcache_close(struct HeaderCache *h)
Close the header cache.
Definition: notmuch.c:114
adata.h
C_NmFlaggedTag
char * C_NmFlaggedTag
Config: (notmuch) Tag to use for flagged messages.
Definition: config.c:40
C_NmQueryWindowTimebase
char * C_NmQueryWindowTimebase
Config: (notmuch) Units for the time duration.
Definition: config.c:46
init_mailbox
static int init_mailbox(struct Mailbox *m)
Add Notmuch data to the Mailbox.
Definition: notmuch.c:191
NmAccountData
Notmuch-specific Account data -.
Definition: adata.h:34
MUTT_NOTMUCH
@ MUTT_NOTMUCH
'Notmuch' (virtual) Mailbox type
Definition: mailbox.h:54
MUTT_PROGRESS_READ
@ MUTT_PROGRESS_READ
Progress tracks elements, according to $read_inc
Definition: progress.h:42
C_NmDbLimit
int C_NmDbLimit
Config: (notmuch) Default limit for Notmuch queries.
Definition: config.c:37
HCacheEntry::email
struct Email * email
Retrieved email.
Definition: lib.h:100
nm_init
void nm_init(void)
Setup feature commands.
Definition: notmuch.c:91
NmMboxData::db_url
struct Url * db_url
Parsed view url of the Notmuch database.
Definition: mdata.h:37
NM_QUERY_TYPE_THREADS
@ NM_QUERY_TYPE_THREADS
Whole threads.
Definition: private.h:53
NmMboxData::db_query
char * db_query
Previous query.
Definition: mdata.h:38
mutt_debug
#define mutt_debug(LEVEL,...)
Definition: logging.h:81
Mailbox::email_max
int email_max
Number of pointers in emails.
Definition: mailbox.h:100
get_messages
static notmuch_messages_t * get_messages(notmuch_query_t *query)
load messages for a query
Definition: notmuch.c:962
MUTT_REPLIED
@ MUTT_REPLIED
Messages that have been replied to.
Definition: mutt.h:95
nm_url_from_query
char * nm_url_from_query(struct Mailbox *m, char *buf, size_t buflen)
Turn a query into a URL.
Definition: notmuch.c:1677
nm_db_trans_end
int nm_db_trans_end(struct Mailbox *m)
End a database transaction.
Definition: db.c:228
mutt_set_header_color
void mutt_set_header_color(struct Mailbox *m, struct Email *e)
Select a colour for a message.
Definition: index.c:4091
Mailbox::msg_flagged
int msg_flagged
Number of flagged messages.
Definition: mailbox.h:93
Message::fp
FILE * fp
pointer to the message data
Definition: mx.h:96
MX_STATUS_ERROR
@ MX_STATUS_ERROR
An error occurred.
Definition: mx.h:73
mutt_str_len
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:631
Mailbox::verbose
bool verbose
Display status messages?
Definition: mailbox.h:118
nm_message_has_tag
static bool nm_message_has_tag(notmuch_message_t *msg, char *tag)
Does a message have this tag?
Definition: notmuch.c:1115
sync_email_path_with_nm
static void sync_email_path_with_nm(struct Email *e, notmuch_message_t *msg)
Synchronize Neomutt's Email path with notmuch.
Definition: notmuch.c:1137
nm_db_release
int nm_db_release(struct Mailbox *m)
Close the Notmuch database.
Definition: db.c:173
mutt_date_epoch
time_t mutt_date_epoch(void)
Return the number of seconds since the Unix epoch.
Definition: date.c:416
mutt_str_replace
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:446
nm_tags_commit
static int nm_tags_commit(struct Mailbox *m, struct Email *e, char *buf)
Save the tags to a message - Implements MxOps::tags_commit()
Definition: notmuch.c:2483
nm_edata_new
struct NmEmailData * nm_edata_new(void)
Create a new NmEmailData for an email.
Definition: edata.c:60
get_mutt_email
static struct Email * get_mutt_email(struct Mailbox *m, notmuch_message_t *msg)
Get the Email of a Notmuch message.
Definition: notmuch.c:767
nm_db_free
void nm_db_free(notmuch_database_t *db)
decoupled way to close a Notmuch database
Definition: db.c:190
lib.h
NmUrlProtocol
const char NmUrlProtocol[]
Definition: notmuch.c:85
mutt_hcache_store
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:527
Email::deleted
bool deleted
Email is deleted.
Definition: email.h:45
nm_query_window_forward
void nm_query_window_forward(void)
Function to move the current search window forward in time.
Definition: notmuch.c:1738
count_query
static unsigned int count_query(notmuch_database_t *db, const char *qstr, int limit)
Count the results of a query.
Definition: notmuch.c:1506
maildir_open_find_message
FILE * maildir_open_find_message(const char *folder, const char *msg, char **newname)
Find a new.
Definition: maildir.c:974
nm_commands
static const struct Command nm_commands[]
Definition: notmuch.c:78
nm_path_pretty
static int nm_path_pretty(char *buf, size_t buflen, const char *folder)
Abbreviate a Mailbox path - Implements MxOps::path_pretty()
Definition: notmuch.c:2541
append_thread
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:938
HeaderCache
header cache structure
Definition: lib.h:85
nm_tags_edit
static int nm_tags_edit(struct Mailbox *m, const char *tags, char *buf, size_t buflen)
Prompt and validate new messages tags - Implements MxOps::tags_edit()
Definition: notmuch.c:2470
nm_db_get
notmuch_database_t * nm_db_get(struct Mailbox *m, bool writable)
Get the Notmuch database.
Definition: db.c:149
mutt_thread.h
nm_mdata_free
void nm_mdata_free(void **ptr)
Free the private Mailbox data - Implements Mailbox::mdata_free()
Definition: mdata.c:41
nm2mutt_message_id
static char * nm2mutt_message_id(const char *id)
converts notmuch message Id to neomutt message Id
Definition: notmuch.c:635
nm_edata_get
struct NmEmailData * nm_edata_get(struct Email *e)
Get the Notmuch Email data.
Definition: edata.c:71
nm_hcache_open
static struct HeaderCache * nm_hcache_open(struct Mailbox *m)
Open a header cache.
Definition: notmuch.c:101
Progress::msg
char msg[1024]
Definition: progress.h:52
MUTT_NM_TAG
#define MUTT_NM_TAG
Notmuch tag +/- mode.
Definition: mutt.h:67
nm_get_default_url
static char * nm_get_default_url(void)
Create a Mailbox with default Notmuch settings.
Definition: notmuch.c:142
mutt_make_id_hash
struct HashTable * mutt_make_id_hash(struct Mailbox *m)
Create a Hash Table for message-ids.
Definition: mutt_thread.c:1518
Email::edata
void * edata
Driver-specific data.
Definition: email.h:111
mutt_mem_malloc
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
driver_tags_get
char * driver_tags_get(struct TagList *list)
Get tags.
Definition: tags.c:144
Email::tags
struct TagList tags
For drivers that support server tagging.
Definition: email.h:109
UrlQuery::value
char * value
Query value.
Definition: url.h:59
nm_path_canon
static int nm_path_canon(char *buf, size_t buflen)
Canonicalise a Mailbox path - Implements MxOps::path_canon()
Definition: notmuch.c:2533
query_window_reset
static void query_window_reset(void)
Restore vfolder's search window to its original position.
Definition: notmuch.c:279
mutt_strn_dup
char * mutt_strn_dup(const char *begin, size_t len)
Duplicate a sub-string.
Definition: string.c:548
progress.h
maildir_parse_message
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:908
NeoMutt
Container for Accounts, Notifications.
Definition: neomutt.h:36
mutt.h
nm_msg_commit
static int nm_msg_commit(struct Mailbox *m, struct Message *msg)
Save changes to an email - Implements MxOps::msg_commit()
Definition: notmuch.c:2452
apply_exclude_tags
static void apply_exclude_tags(notmuch_query_t *query)
Exclude the configured tags.
Definition: notmuch.c:450
mutt_istr_startswith
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition: string.c:172
NM_QUERY_TYPE_MESGS
@ NM_QUERY_TYPE_MESGS
Default: Messages only.
Definition: private.h:52
C_HeaderCache
char * C_HeaderCache
Config: (hcache) Directory/file for the header cache database.
Definition: config.c:40
mailbox_changed
void mailbox_changed(struct Mailbox *m, enum NotifyMailbox action)
Notify observers of a change to a Mailbox.
Definition: mailbox.c:186
Account::adata
void * adata
Private data (for Mailbox backends)
Definition: account.h:43
MX_OPEN_ABORT
@ MX_OPEN_ABORT
Open was aborted.
Definition: mx.h:88
MailboxType
MailboxType
Supported mailbox formats.
Definition: mailbox.h:43
maildir_parse_flags
void maildir_parse_flags(struct Email *e, const char *path)
Parse Maildir file flags.
Definition: maildir.c:796
rename_maildir_filename
static int rename_maildir_filename(const char *old, char *buf, size_t buflen, struct Email *e)
Rename a Maildir file.
Definition: notmuch.c:1285
Email::index
int index
The absolute (unsorted) message number.
Definition: email.h:86
NmUrlProtocolLen
const int NmUrlProtocolLen
Definition: notmuch.c:86
C_NmDefaultUrl
char * C_NmDefaultUrl
Config: (notmuch) Path to the Notmuch database.
Definition: config.c:38
NeoMutt::sub
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
NmMboxData
Notmuch-specific Mailbox data -.
Definition: mdata.h:35
email_free
void email_free(struct Email **ptr)
Free an Email.
Definition: email.c:43
mutt_progress_update
void mutt_progress_update(struct Progress *progress, size_t pos, int percent)
Update the state of the progress bar.
Definition: progress.c:212
Mailbox::msg_unread
int msg_unread
Number of unread messages.
Definition: mailbox.h:92
mutt_get_field
int mutt_get_field(const char *field, char *buf, size_t buflen, CompletionFlags complete, bool multiple, char ***files, int *numfiles)
Ask the user for a string.
Definition: curs_lib.c:311
MUTT_MAILDIR
@ MUTT_MAILDIR
'Maildir' Mailbox type
Definition: mailbox.h:51
Url::path
char * path
Path.
Definition: url.h:74
mutt_commands.h
MX_STATUS_FLAGS
@ MX_STATUS_FLAGS
Nondestructive flags change (IMAP)
Definition: mx.h:78
mailbox_path
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox's path string.
Definition: mailbox.h:206
get_folder_from_path
static char * get_folder_from_path(const char *path)
Find an email's folder from its path.
Definition: notmuch.c:610
mx.h
MxNotmuchOps
struct MxOps MxNotmuchOps
Notmuch Mailbox - Implements MxOps.
Definition: notmuch.c:2560
nm_mdata_new
struct NmMboxData * nm_mdata_new(const char *url)
Create a new NmMboxData object from a query.
Definition: mdata.c:63
NmEmailData
Notmuch-specific Email data -.
Definition: edata.h:33
nm_get_default_data
static struct NmMboxData * nm_get_default_data(void)
Create a Mailbox with default Notmuch settings.
Definition: notmuch.c:168
nm_mbox_close
static enum MxStatus nm_mbox_close(struct Mailbox *m)
Close a Mailbox - Implements MxOps::mbox_close()
Definition: notmuch.c:2420
NmQueryType
NmQueryType
Notmuch Query Types.
Definition: private.h:50
nm_update_filename
int nm_update_filename(struct Mailbox *m, const char *old_file, const char *new_file, struct Email *e)
Change the filename.
Definition: notmuch.c:1827
mutt_hcache_close
void mutt_hcache_close(struct HeaderCache *hc)
Multiplexor for StoreOps::close.
Definition: hcache.c:419
Email
The envelope/body of an email.
Definition: email.h:37
lib.h
nm_mbox_check
static enum MxStatus nm_mbox_check(struct Mailbox *m)
Check for new mail - Implements MxOps::mbox_check()
Definition: notmuch.c:2162
mutt_message
#define mutt_message(...)
Definition: logging.h:83
mutt_set_flag
#define mutt_set_flag(m, e, flag, bf)
Definition: protos.h:67
NmMboxData::query_type
enum NmQueryType query_type
Messages or Threads.
Definition: mdata.h:40
nm_path_probe
enum MailboxType nm_path_probe(const char *path, const struct stat *st)
Is this a Notmuch Mailbox? - Implements MxOps::path_probe()
Definition: notmuch.c:2522
Mailbox::msg_new
int msg_new
Number of new messages.
Definition: mailbox.h:95
NmMboxData::db_limit
int db_limit
Maximum number of results to return.
Definition: mdata.h:39
read_mesgs_query
static bool read_mesgs_query(struct Mailbox *m, notmuch_query_t *q, bool dedup)
Search for matching messages.
Definition: notmuch.c:990
mutt_str_asprintf
int mutt_str_asprintf(char **strp, const char *fmt,...)
Definition: string.c:1095
MX_OPEN_ERROR
@ MX_OPEN_ERROR
Open failed with an error.
Definition: mx.h:87
nm_adata_new
struct NmAccountData * nm_adata_new(void)
Allocate and initialise a new NmAccountData structure.
Definition: adata.c:56
read_threads_query
static bool read_threads_query(struct Mailbox *m, notmuch_query_t *q, bool dedup, int limit)
Perform a query with threads.
Definition: notmuch.c:1060
append_replies
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:911
nm_mbox_check_stats
static enum MxStatus nm_mbox_check_stats(struct Mailbox *m, uint8_t flags)
Check the Mailbox statistics - Implements MxOps::mbox_check_stats()
Definition: notmuch.c:1852
mx_mbox_ac_link
bool mx_mbox_ac_link(struct Mailbox *m)
Link a Mailbox to an existing or new Account.
Definition: mx.c:272
progress_update
static void progress_update(struct Mailbox *m, notmuch_query_t *q)
Update the progress counter.
Definition: notmuch.c:739
Email::read
bool read
Email is read.
Definition: email.h:51
NmMboxData::ignmsgcount
int ignmsgcount
Ignored messages.
Definition: mdata.h:44
edata.h
COMMANDS_REGISTER
#define COMMANDS_REGISTER(cmds)
Definition: mutt_commands.h:77
mutt_buffer_strcpy
size_t mutt_buffer_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:312
Mailbox::id_hash
struct HashTable * id_hash
Hash Table by msg id.
Definition: mailbox.h:127
maildir_edata_free
void maildir_edata_free(void **ptr)
Free the private Email data - Implements Email::edata_free()
Definition: edata.c:38
email_get_fullpath
static char * email_get_fullpath(struct Email *e, char *buf, size_t buflen)
Get the full path of an email.
Definition: notmuch.c:229
append_message
static void append_message(struct HeaderCache *h, struct Mailbox *m, notmuch_query_t *q, notmuch_message_t *msg, bool dedup)
Associate a message.
Definition: notmuch.c:802
LL_DEBUG2
@ LL_DEBUG2
Log at debug level 2.
Definition: logging.h:41
maildir_edata_new
struct MaildirEmailData * maildir_edata_new(void)
Create a new MaildirEmailData object.
Definition: edata.c:53
progress_reset
static void progress_reset(struct Mailbox *m)
Reset the progress counter.
Definition: notmuch.c:718
update_email_tags
static int update_email_tags(struct Email *e, notmuch_message_t *msg)
Update the Email's tags from Notmuch.
Definition: notmuch.c:523
nm_msg_open
static bool nm_msg_open(struct Mailbox *m, struct Message *msg, int msgno)
Open an email message in a Mailbox - Implements MxOps::msg_open()
Definition: notmuch.c:2428
Mailbox::pathbuf
struct Buffer pathbuf
Definition: mailbox.h:83
Email::changed
bool changed
Email has been edited.
Definition: email.h:48
Message
A local copy of an email.
Definition: mx.h:94
Email::nm_edata
void * nm_edata
Notmuch private data.
Definition: email.h:106
nm_mbox_open
static enum MxOpenReturns nm_mbox_open(struct Mailbox *m)
Open a Mailbox - Implements MxOps::mbox_open()
Definition: notmuch.c:2113
MX_STATUS_OK
@ MX_STATUS_OK
No changes.
Definition: mx.h:74
nm_adata_free
void nm_adata_free(void **ptr)
Free the private Account data - Implements Account::adata_free()
Definition: adata.c:37
mutt_error
#define mutt_error(...)
Definition: logging.h:84
mutt_hcache_fetch
struct HCacheEntry mutt_hcache_fetch(struct HeaderCache *hc, const char *key, size_t keylen, uint32_t uidvalidity)
Multiplexor for StoreOps::fetch.
Definition: hcache.c:438
mutt_str_copy
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:716