]> granicus.if.org Git - neomutt/commitdiff
Notmuch: separate db functions
authorRichard Russon <rich@flatcap.org>
Fri, 19 Oct 2018 18:12:51 +0000 (19:12 +0100)
committerRichard Russon <rich@flatcap.org>
Thu, 25 Oct 2018 20:38:43 +0000 (21:38 +0100)
Makefile.autosetup
commands.c
curs_main.c
init.c
notmuch/mutt_notmuch.c
notmuch/mutt_notmuch.h
notmuch/nm_db.c [new file with mode: 0644]
notmuch/notmuch_private.h

index 4beb18d001a672fa21af3524c681ba79d5087a19..7460dd7e2930d151af8b84e5c192424b70ab7f64 100644 (file)
@@ -117,7 +117,7 @@ ALLOBJS+=   $(LIBMBOXOBJS)
 # libnotmuch
 @if USE_NOTMUCH
 LIBNOTMUCH=    libnotmuch.a
-LIBNOTMUCHOBJS=        notmuch/mutt_notmuch.o
+LIBNOTMUCHOBJS=        notmuch/mutt_notmuch.o notmuch/nm_db.o
 CLEANFILES+=   $(LIBNOTMUCH) $(LIBNOTMUCHOBJS)
 MUTTLIBS+=     $(LIBNOTMUCH)
 ALLOBJS+=      $(LIBNOTMUCHOBJS)
index adab8ebd8ba032d3ec51472e6aa808e5c6d17d16..728bab12b46c4cd25685b4614dcd375a6e733776 100644 (file)
@@ -1026,7 +1026,7 @@ int mutt_save_message(struct Email *e, bool delete, bool decode, bool decrypt)
 
 #ifdef USE_NOTMUCH
       if (Context->mailbox->magic == MUTT_NOTMUCH)
-        nm_longrun_init(Context->mailbox, true);
+        nm_db_longrun_init(Context->mailbox, true);
 #endif
       for (int i = 0; i < Context->mailbox->msg_count; i++)
       {
@@ -1051,7 +1051,7 @@ int mutt_save_message(struct Email *e, bool delete, bool decode, bool decrypt)
       }
 #ifdef USE_NOTMUCH
       if (Context->mailbox->magic == MUTT_NOTMUCH)
-        nm_longrun_done(Context->mailbox);
+        nm_db_longrun_done(Context->mailbox);
 #endif
       if (rc != 0)
       {
index 1b4571b56cf3d52699b5308728c2eb889c68df8d..6dfb6492717a22d3926c1c784c4a4f00391f2e91 100644 (file)
@@ -1240,7 +1240,7 @@ int mutt_index_menu(void)
 
 #ifdef USE_NOTMUCH
     if (Context)
-      nm_debug_check(Context->mailbox);
+      nm_db_debug_check(Context->mailbox);
 #endif
 
     switch (op)
@@ -1996,7 +1996,7 @@ int mutt_index_menu(void)
 
 #ifdef USE_NOTMUCH
           if (Context->mailbox->magic == MUTT_NOTMUCH)
-            nm_longrun_init(Context->mailbox, true);
+            nm_db_longrun_init(Context->mailbox, true);
 #endif
           for (px = 0, j = 0; j < Context->mailbox->msg_count; j++)
           {
@@ -2020,7 +2020,7 @@ int mutt_index_menu(void)
           }
 #ifdef USE_NOTMUCH
           if (Context->mailbox->magic == MUTT_NOTMUCH)
-            nm_longrun_done(Context->mailbox);
+            nm_db_longrun_done(Context->mailbox);
 #endif
           menu->redraw = REDRAW_STATUS | REDRAW_INDEX;
         }
@@ -3501,7 +3501,7 @@ int mutt_index_menu(void)
 
 #ifdef USE_NOTMUCH
     if (Context)
-      nm_debug_check(Context->mailbox);
+      nm_db_debug_check(Context->mailbox);
 #endif
 
     if (menu->menu == MENU_PAGER)
diff --git a/init.c b/init.c
index a5c335793194009f75122f2356f6125c3769e1c4..59d5e373991e4c31dfa0a9eac74d301dd5942c70 100644 (file)
--- a/init.c
+++ b/init.c
@@ -225,7 +225,7 @@ static int complete_all_nm_tags(const char *pt)
   memset(Matches, 0, MatchesListsize);
   memset(Completed, 0, sizeof(Completed));
 
-  nm_longrun_init(Context->mailbox, false);
+  nm_db_longrun_init(Context->mailbox, false);
 
   /* Work out how many tags there are. */
   if (nm_get_all_tags(Context->mailbox, NULL, &tag_count_1) || tag_count_1 == 0)
@@ -247,7 +247,7 @@ static int complete_all_nm_tags(const char *pt)
   {
     FREE(&nm_tags);
     nm_tags = NULL;
-    nm_longrun_done(Context->mailbox);
+    nm_db_longrun_done(Context->mailbox);
     return -1;
   }
 
@@ -261,7 +261,7 @@ static int complete_all_nm_tags(const char *pt)
   Matches[NumMatched++] = UserTyped;
 
 done:
-  nm_longrun_done(Context->mailbox);
+  nm_db_longrun_done(Context->mailbox);
   return 0;
 }
 #endif
index 16f734479aab6b069738b6b012cbf3f1390862f2..b91446567d41bc6c7d777dc7168d0805b1a7ca3d 100644 (file)
@@ -94,13 +94,27 @@ char *NmUnreadTag; ///< Config: (notmuch) Tag to use for unread messages
    (LIBNOTMUCH_MAJOR_VERSION == (major) &&                                        \
     LIBNOTMUCH_MINOR_VERSION == (minor) && LIBNOTMUCH_MICRO_VERSION >= (micro)))
 
-static enum NmQueryType string_to_query_type(const char *str);
+/**
+ * string_to_query_type - Lookup a query type
+ * @param str String to lookup
+ * @retval num Query type, e.g. #NM_QUERY_TYPE_MESGS
+ */
+static enum NmQueryType string_to_query_type(const char *str)
+{
+  if (mutt_str_strcmp(str, "threads") == 0)
+    return NM_QUERY_TYPE_THREADS;
+  else if (mutt_str_strcmp(str, "messages") == 0)
+    return NM_QUERY_TYPE_MESGS;
+
+  mutt_error(_("failed to parse notmuch query type: %s"), NONULL(str));
+  return NM_QUERY_TYPE_MESGS;
+}
 
 /**
  * nm_adata_free - Release and clear storage in an NmAccountData structure
  * @param ptr Nm Account data
  */
-static void nm_adata_free(void **ptr)
+void nm_adata_free(void **ptr)
 {
   if (!ptr || !*ptr)
     return;
@@ -123,7 +137,7 @@ static void nm_adata_free(void **ptr)
  * nm_adata_new - Allocate and initialise a new NmAccountData structure
  * @retval ptr New NmAccountData
  */
-static struct NmAccountData *nm_adata_new(void)
+struct NmAccountData *nm_adata_new(void)
 {
   struct NmAccountData *adata = mutt_mem_calloc(1, sizeof(struct NmAccountData));
 
@@ -136,7 +150,7 @@ static struct NmAccountData *nm_adata_new(void)
  * @retval ptr  Success
  * @retval NULL Failure, not a Notmuch mailbox
  */
-static struct NmAccountData *nm_adata_get(struct Mailbox *m)
+struct NmAccountData *nm_adata_get(struct Mailbox *m)
 {
   if (!m || (m->magic != MUTT_NOTMUCH))
     return NULL;
@@ -156,7 +170,7 @@ static struct NmAccountData *nm_adata_get(struct Mailbox *m)
  * the database.  This function will close the database, free the resources and
  * the struct itself.
  */
-static void nm_mdata_free(void **ptr)
+void nm_mdata_free(void **ptr)
 {
   if (!ptr || !*ptr)
     return;
@@ -179,7 +193,7 @@ static void nm_mdata_free(void **ptr)
  * A new NmMboxData struct is created, then the query is parsed and saved
  * within it.  This should be freed using nm_mdata_free().
  */
-static struct NmMboxData *nm_mdata_new(const char *uri)
+struct NmMboxData *nm_mdata_new(const char *uri)
 {
   if (!uri)
     return NULL;
@@ -206,7 +220,7 @@ static struct NmMboxData *nm_mdata_new(const char *uri)
  * @retval ptr  Success
  * @retval NULL Failure, not a Notmuch mailbox
  */
-static struct NmMboxData *nm_mdata_get(struct Mailbox *m)
+struct NmMboxData *nm_mdata_get(struct Mailbox *m)
 {
   if (!m || (m->magic != MUTT_NOTMUCH))
     return NULL;
@@ -221,7 +235,7 @@ static struct NmMboxData *nm_mdata_get(struct Mailbox *m)
  * Each email has an attached NmEmailData, which contains things like the tags
  * (labels).
  */
-static void nm_edata_free(void **ptr)
+void nm_edata_free(void **ptr)
 {
   if (!ptr || !*ptr)
     return;
@@ -238,27 +252,11 @@ static void nm_edata_free(void **ptr)
  * nm_edata_new - Create a new NmEmailData for an email
  * @retval ptr New NmEmailData struct
  */
-static struct NmEmailData *nm_edata_new(void)
+struct NmEmailData *nm_edata_new(void)
 {
   return mutt_mem_calloc(1, sizeof(struct NmEmailData));
 }
 
-/**
- * string_to_query_type - Lookup a query type
- * @param str String to lookup
- * @retval num Query type, e.g. #NM_QUERY_TYPE_MESGS
- */
-static enum NmQueryType string_to_query_type(const char *str)
-{
-  if (mutt_str_strcmp(str, "threads") == 0)
-    return NM_QUERY_TYPE_THREADS;
-  else if (mutt_str_strcmp(str, "messages") == 0)
-    return NM_QUERY_TYPE_MESGS;
-
-  mutt_error(_("failed to parse notmuch query type: %s"), NONULL(str));
-  return NM_QUERY_TYPE_MESGS;
-}
-
 /**
  * nm_get_default_data - Create a Mailbox with default Notmuch settings
  * @retval ptr  Mailbox with default Notmuch settings
@@ -545,236 +543,6 @@ static int get_limit(struct NmMboxData *mdata)
   return mdata ? mdata->db_limit : 0;
 }
 
-/**
- * get_db_filename - Get the filename of the Notmuch database
- * @param m Notmuch Mailbox data
- * @retval ptr Filename
- *
- * @note The return value is a pointer into the NmDefaultUri global variable.
- *       If that variable changes, the result will be invalid.
- *       It must not be freed.
- */
-static const char *get_db_filename(struct Mailbox *m)
-{
-  struct NmMboxData *mdata = nm_mdata_get(m);
-  if (!mdata)
-    return NULL;
-
-  char *db_filename = mdata->db_url.path ? mdata->db_url.path : NmDefaultUri;
-  if (!db_filename)
-    db_filename = Folder;
-  if (!db_filename)
-    return NULL;
-  if (strncmp(db_filename, "notmuch://", 10) == 0)
-    db_filename += 10;
-
-  mutt_debug(2, "nm: db filename '%s'\n", db_filename);
-  return db_filename;
-}
-
-/**
- * do_database_open - Open a Notmuch database
- * @param filename Database filename
- * @param writable Read/write?
- * @param verbose  Show errors on failure?
- * @retval ptr Notmuch database
- */
-static notmuch_database_t *do_database_open(const char *filename, bool writable, bool verbose)
-{
-  notmuch_database_t *db = NULL;
-  int ct = 0;
-  notmuch_status_t st = NOTMUCH_STATUS_SUCCESS;
-#if LIBNOTMUCH_CHECK_VERSION(4, 2, 0)
-  char *msg = NULL;
-#endif
-
-  mutt_debug(1, "nm: db open '%s' %s (timeout %d)\n", filename,
-             writable ? "[WRITE]" : "[READ]", NmOpenTimeout);
-
-  const notmuch_database_mode_t mode =
-      writable ? NOTMUCH_DATABASE_MODE_READ_WRITE : NOTMUCH_DATABASE_MODE_READ_ONLY;
-
-  const struct timespec wait = {
-    .tv_sec = 0, .tv_nsec = 500000000, /* Half a second */
-  };
-
-  do
-  {
-#if LIBNOTMUCH_CHECK_VERSION(4, 2, 0)
-    st = notmuch_database_open_verbose(filename, mode, &db, &msg);
-#elif defined(NOTMUCH_API_3)
-    st = notmuch_database_open(filename, mode, &db);
-#else
-    db = notmuch_database_open(filename, mode);
-#endif
-    if ((st == NOTMUCH_STATUS_FILE_ERROR) || db || !NmOpenTimeout || ((ct / 2) > NmOpenTimeout))
-      break;
-
-    if (verbose && ct && ((ct % 2) == 0))
-      mutt_error(_("Waiting for notmuch DB... (%d sec)"), ct / 2);
-    nanosleep(&wait, NULL);
-    ct++;
-  } while (true);
-
-  if (verbose)
-  {
-    if (!db)
-    {
-#if LIBNOTMUCH_CHECK_VERSION(4, 2, 0)
-      if (msg)
-      {
-        mutt_error(msg);
-        FREE(&msg);
-      }
-      else
-#endif
-      {
-        mutt_error(_("Cannot open notmuch database: %s: %s"), filename,
-                   st ? notmuch_status_to_string(st) : _("unknown reason"));
-      }
-    }
-    else if (ct > 1)
-    {
-      mutt_clear_error();
-    }
-  }
-  return db;
-}
-
-/**
- * get_db - Get the Notmuch database
- * @param m        Mailbox
- * @param writable Read/write?
- * @retval ptr Notmuch database
- */
-static notmuch_database_t *get_db(struct Mailbox *m, bool writable)
-{
-  if (!m || (m->magic != MUTT_NOTMUCH))
-    return NULL;
-  struct Account *a = m->account;
-  if (!a)
-    return NULL;
-  struct NmAccountData *adata = a->adata;
-  if (!adata)
-    return NULL;
-
-  const char *db_filename = get_db_filename(m);
-  if (db_filename)
-    adata->db = do_database_open(db_filename, writable, true);
-
-  return adata->db;
-}
-
-/**
- * release_db - Close the Notmuch database
- * @param m Mailbox
- * @retval  0 Success
- * @retval -1 Failure
- */
-static int release_db(struct Mailbox *m)
-{
-  struct NmAccountData *adata = nm_adata_get(m);
-  if (!adata || !adata->db)
-    return -1;
-
-  mutt_debug(1, "nm: db close\n");
-#ifdef NOTMUCH_API_3
-  notmuch_database_destroy(adata->db);
-#else
-  notmuch_database_close(adata->db);
-#endif
-  adata->db = NULL;
-  adata->longrun = false;
-  return 0;
-}
-
-/**
- * db_trans_begin - Start a Notmuch database transaction
- * @param m Mailbox
- * @retval <0 error
- * @retval 1  new transaction started
- * @retval 0  already within transaction
- */
-static int db_trans_begin(struct Mailbox *m)
-{
-  struct NmAccountData *adata = nm_adata_get(m);
-  if (!adata || !adata->db)
-    return -1;
-
-  if (adata->trans)
-    return 0;
-
-  mutt_debug(2, "nm: db trans start\n");
-  if (notmuch_database_begin_atomic(adata->db))
-    return -1;
-  adata->trans = true;
-  return 1;
-}
-
-/**
- * db_trans_end - End a database transaction
- * @param m Mailbox
- * @retval  0 Success
- * @retval -1 Failure
- */
-static int db_trans_end(struct Mailbox *m)
-{
-  struct NmAccountData *adata = nm_adata_get(m);
-  if (!adata || !adata->db)
-    return -1;
-
-  if (!adata->trans)
-    return 0;
-
-  mutt_debug(2, "nm: db trans end\n");
-  adata->trans = false;
-  if (notmuch_database_end_atomic(adata->db))
-    return -1;
-
-  return 0;
-}
-
-/**
- * is_longrun - Is Notmuch in the middle of a long-running transaction
- * @param m Notmuch Mailbox data
- * @retval true if it is
- */
-static bool is_longrun(struct Mailbox *m)
-{
-  struct NmAccountData *adata = nm_adata_get(m);
-  if (!adata)
-    return false;
-
-  return adata->longrun;
-}
-
-/**
- * get_database_mtime - Get the database modification time
- * @param[in]  m     Mailbox
- * @param[out] mtime Save the modification time
- * @retval  0 Success (result in mtime)
- * @retval -1 Error
- *
- * Get the "mtime" (modification time) of the database file.
- * This is the time of the last update.
- */
-static int get_database_mtime(struct Mailbox *m, time_t *mtime)
-{
-  if (!m || !mtime)
-    return -1;
-
-  char path[PATH_MAX];
-  snprintf(path, sizeof(path), "%s/.notmuch/xapian", get_db_filename(m));
-  mutt_debug(2, "nm: checking '%s' mtime\n", path);
-
-  struct stat st;
-  if (stat(path, &st))
-    return -1;
-
-  *mtime = st.st_mtime;
-  return 0;
-}
-
 /**
  * apply_exclude_tags - Exclude the configured tags
  * @param query Notmuch query
@@ -826,7 +594,7 @@ static notmuch_query_t *get_query(struct Mailbox *m, bool writable)
   if (!mdata)
     return NULL;
 
-  notmuch_database_t *db = get_db(m, writable);
+  notmuch_database_t *db = nm_db_get(m, writable);
   const char *str = get_query_string(mdata, true);
 
   if (!db || !str)
@@ -841,8 +609,8 @@ static notmuch_query_t *get_query(struct Mailbox *m, bool writable)
   mutt_debug(2, "nm: query successfully initialized (%s)\n", str);
   return q;
 err:
-  if (!is_longrun(m))
-    release_db(m);
+  if (!nm_db_is_longrun(m))
+    nm_db_release(m);
   return NULL;
 }
 
@@ -1594,7 +1362,7 @@ static int remove_filename(struct Mailbox *m, const char *path)
 
   mutt_debug(2, "nm: remove filename '%s'\n", path);
 
-  notmuch_database_t *db = get_db(m, true);
+  notmuch_database_t *db = nm_db_get(m, true);
   if (!db)
     return -1;
 
@@ -1603,7 +1371,7 @@ static int remove_filename(struct Mailbox *m, const char *path)
   if (st || !msg)
     return -1;
 
-  int trans = db_trans_begin(m);
+  int trans = nm_db_trans_begin(m);
   if (trans < 0)
     return -1;
 
@@ -1638,7 +1406,7 @@ static int remove_filename(struct Mailbox *m, const char *path)
 
   notmuch_message_destroy(msg);
   if (trans)
-    db_trans_end(m);
+    nm_db_trans_end(m);
   return 0;
 }
 
@@ -1658,7 +1426,7 @@ static int rename_filename(struct Mailbox *m, const char *old, const char *new,
   if (!mdata)
     return -1;
 
-  notmuch_database_t *db = get_db(m, true);
+  notmuch_database_t *db = nm_db_get(m, true);
   if (!db || !new || !old || (access(new, F_OK) != 0))
     return -1;
 
@@ -1668,7 +1436,7 @@ static int rename_filename(struct Mailbox *m, const char *old, const char *new,
   notmuch_message_t *msg = NULL;
 
   mutt_debug(1, "nm: rename filename, %s -> %s\n", old, new);
-  int trans = db_trans_begin(m);
+  int trans = nm_db_trans_begin(m);
   if (trans < 0)
     return -1;
 
@@ -1744,7 +1512,7 @@ done:
   if (msg)
     notmuch_message_destroy(msg);
   if (trans)
-    db_trans_end(m);
+    nm_db_trans_end(m);
   return rc;
 }
 
@@ -1789,51 +1557,6 @@ char *nm_email_get_folder(struct Email *e)
   return (e && e->edata) ? ((struct NmEmailData *) e->edata)->folder : NULL;
 }
 
-/**
- * nm_longrun_init - Start a long transaction
- * @param m        Mailbox
- * @param writable Read/write?
- */
-void nm_longrun_init(struct Mailbox *m, bool writable)
-{
-  struct NmAccountData *adata = nm_adata_get(m);
-
-  if (adata && get_db(m, writable))
-  {
-    adata->longrun = true;
-    mutt_debug(2, "nm: long run initialized\n");
-  }
-}
-
-/**
- * nm_longrun_done - Finish a long transaction
- * @param m Mailbox
- */
-void nm_longrun_done(struct Mailbox *m)
-{
-  struct NmAccountData *adata = nm_adata_get(m);
-
-  if (adata && (release_db(m) == 0))
-    mutt_debug(2, "nm: long run deinitialized\n");
-}
-
-/**
- * nm_debug_check - Check if the database is open
- * @param m Mailbox
- */
-void nm_debug_check(struct Mailbox *m)
-{
-  struct NmAccountData *adata = nm_adata_get(m);
-  if (!adata || !adata->db)
-    return;
-
-  if (adata->db)
-  {
-    mutt_debug(1, "nm: ERROR: db is open, closing\n");
-    release_db(m);
-  }
-}
-
 /**
  * nm_read_entire_thread - Get the entire thread of an email
  * @param ctx Mailbox
@@ -1852,7 +1575,7 @@ int nm_read_entire_thread(struct Context *ctx, struct Email *e)
   notmuch_message_t *msg = NULL;
   int rc = -1;
 
-  if (!(db = get_db(ctx->mailbox, false)) || !(msg = get_nm_message(db, e)))
+  if (!(db = nm_db_get(ctx->mailbox, false)) || !(msg = get_nm_message(db, e)))
     goto done;
 
   mutt_debug(1, "nm: reading entire-thread messages...[current count=%d]\n",
@@ -1884,8 +1607,8 @@ int nm_read_entire_thread(struct Context *ctx, struct Email *e)
 done:
   if (q)
     notmuch_query_destroy(q);
-  if (!is_longrun(ctx->mailbox))
-    release_db(ctx->mailbox);
+  if (!nm_db_is_longrun(ctx->mailbox))
+    nm_db_release(ctx->mailbox);
 
   if (ctx->mailbox->msg_count == mdata->oldmsgcount)
     mutt_message(_("No more messages in the thread"));
@@ -1956,13 +1679,13 @@ char *nm_uri_from_query(struct Mailbox *m, char *buf, size_t buflen)
     if (get_limit(mdata) != NmDbLimit)
     {
       added = snprintf(uri, sizeof(uri),
-                       "notmuch://%s?type=%s&limit=%d&query=", get_db_filename(m),
+                       "notmuch://%s?type=%s&limit=%d&query=", nm_db_get_filename(m),
                        query_type_to_string(mdata->query_type), get_limit(mdata));
     }
     else
     {
       added = snprintf(uri, sizeof(uri),
-                       "notmuch://%s?type=%s&query=", get_db_filename(m),
+                       "notmuch://%s?type=%s&query=", nm_db_get_filename(m),
                        query_type_to_string(mdata->query_type));
     }
   }
@@ -2088,7 +1811,7 @@ void nm_query_window_backward(void)
 bool nm_message_is_still_queried(struct Mailbox *m, struct Email *e)
 {
   struct NmMboxData *mdata = nm_mdata_get(m);
-  notmuch_database_t *db = get_db(m, false);
+  notmuch_database_t *db = nm_db_get(m, false);
   char *orig_str = get_query_string(mdata, true);
 
   if (!db || !orig_str)
@@ -2171,8 +1894,8 @@ int nm_update_filename(struct Mailbox *m, const char *old, const char *new, stru
 
   int rc = rename_filename(m, old, new, e);
 
-  if (!is_longrun(m))
-    release_db(m);
+  if (!nm_db_is_longrun(m))
+    nm_db_release(m);
   m->mtime.tv_sec = time(NULL);
   m->mtime.tv_nsec = 0;
   return rc;
@@ -2230,7 +1953,7 @@ int nm_nonctx_get_count(char *path, int *all, int *new)
 
   /* don't be verbose about connection, as we're called from
    * sidebar/mailbox very often */
-  db = do_database_open(db_filename, false, false);
+  db = nm_db_do_open(db_filename, false, false);
   if (!db)
     goto done;
 
@@ -2311,12 +2034,12 @@ int nm_record_message(struct Mailbox *m, char *path, struct Email *e)
 
   if (!path || !mdata || (access(path, F_OK) != 0))
     return 0;
-  db = get_db(m, true);
+  db = nm_db_get(m, true);
   if (!db)
     return -1;
 
   mutt_debug(1, "nm: record message: %s\n", path);
-  int trans = db_trans_begin(m);
+  int trans = nm_db_trans_begin(m);
   if (trans < 0)
     goto done;
 
@@ -2350,9 +2073,9 @@ done:
   if (msg)
     notmuch_message_destroy(msg);
   if (trans == 1)
-    db_trans_end(m);
-  if (!is_longrun(m))
-    release_db(m);
+    nm_db_trans_end(m);
+  if (!nm_db_is_longrun(m))
+    nm_db_release(m);
   return rc;
 }
 
@@ -2377,7 +2100,7 @@ int nm_get_all_tags(struct Mailbox *m, char **tag_list, int *tag_count)
   const char *tag = NULL;
   int rc = -1;
 
-  if (!(db = get_db(m, false)) || !(tags = notmuch_database_get_all_tags(db)))
+  if (!(db = nm_db_get(m, false)) || !(tags = notmuch_database_get_all_tags(db)))
     goto done;
 
   *tag_count = 0;
@@ -2401,8 +2124,8 @@ done:
   if (tags)
     notmuch_tags_destroy(tags);
 
-  if (!is_longrun(m))
-    release_db(m);
+  if (!nm_db_is_longrun(m))
+    nm_db_release(m);
 
   mutt_debug(1, "nm: get all tags done [rc=%d tag_count=%u]\n", rc, *tag_count);
   return rc;
@@ -2491,8 +2214,8 @@ static int nm_mbox_open(struct Context *ctx)
     notmuch_query_destroy(q);
   }
 
-  if (!is_longrun(ctx->mailbox))
-    release_db(ctx->mailbox);
+  if (!nm_db_is_longrun(ctx->mailbox))
+    nm_db_release(ctx->mailbox);
 
   ctx->mailbox->mtime.tv_sec = time(NULL);
   ctx->mailbox->mtime.tv_nsec = 0;
@@ -2519,7 +2242,7 @@ static int nm_mbox_check(struct Context *ctx, int *index_hint)
 {
   struct NmMboxData *mdata = nm_mdata_get(ctx->mailbox);
   time_t mtime = 0;
-  if (!mdata || (get_database_mtime(ctx->mailbox, &mtime) != 0))
+  if (!mdata || (nm_db_get_mtime(ctx->mailbox, &mtime) != 0))
     return -1;
 
   int new_flags = 0;
@@ -2619,8 +2342,8 @@ done:
   if (q)
     notmuch_query_destroy(q);
 
-  if (!is_longrun(ctx->mailbox))
-    release_db(ctx->mailbox);
+  if (!nm_db_is_longrun(ctx->mailbox))
+    nm_db_release(ctx->mailbox);
 
   ctx->mailbox->mtime.tv_sec = time(NULL);
   ctx->mailbox->mtime.tv_nsec = 0;
@@ -2710,8 +2433,8 @@ static int nm_mbox_sync(struct Context *ctx, int *index_hint)
   mutt_str_strfcpy(ctx->mailbox->path, uri, sizeof(ctx->mailbox->path));
   ctx->mailbox->magic = MUTT_NOTMUCH;
 
-  if (!is_longrun(ctx->mailbox))
-    release_db(ctx->mailbox);
+  if (!nm_db_is_longrun(ctx->mailbox))
+    nm_db_release(ctx->mailbox);
   if (changed)
   {
     ctx->mailbox->mtime.tv_sec = time(NULL);
@@ -2801,7 +2524,7 @@ static int nm_tags_commit(struct Context *ctx, struct Email *e, char *buf)
   notmuch_message_t *msg = NULL;
   int rc = -1;
 
-  if (!(db = get_db(ctx->mailbox, true)) || !(msg = get_nm_message(db, e)))
+  if (!(db = nm_db_get(ctx->mailbox, true)) || !(msg = get_nm_message(db, e)))
     goto done;
 
   mutt_debug(1, "nm: tags modify: '%s'\n", buf);
@@ -2814,8 +2537,8 @@ static int nm_tags_commit(struct Context *ctx, struct Email *e, char *buf)
   rc = 0;
   e->changed = true;
 done:
-  if (!is_longrun(ctx->mailbox))
-    release_db(ctx->mailbox);
+  if (!nm_db_is_longrun(ctx->mailbox))
+    nm_db_release(ctx->mailbox);
   if (e->changed)
   {
     ctx->mailbox->mtime.tv_sec = time(NULL);
index f3ed86975bda24e507adf9acc4a7a5f2aa5cf65d..f057afcb9844f6d102979943859086b8e178791e 100644 (file)
@@ -54,12 +54,12 @@ extern char *NmUnreadTag;
 
 extern struct MxOps mx_notmuch_ops;
 
-void  nm_debug_check             (struct Mailbox *m);
+void  nm_db_debug_check             (struct Mailbox *m);
 int   nm_description_to_path     (const char *desc, char *buf, size_t buflen);
 int   nm_get_all_tags            (struct Mailbox *m, char **tag_list, int *tag_count);
 char *nm_email_get_folder        (struct Email *e);
-void  nm_longrun_done            (struct Mailbox *m);
-void  nm_longrun_init            (struct Mailbox *m, bool writable);
+void  nm_db_longrun_done            (struct Mailbox *m);
+void  nm_db_longrun_init            (struct Mailbox *m, bool writable);
 bool  nm_message_is_still_queried(struct Mailbox *m, struct Email *e);
 int   nm_nonctx_get_count        (char *path, int *all, int *new);
 bool  nm_normalize_uri           (const char *uri, char *buf, size_t buflen);
diff --git a/notmuch/nm_db.c b/notmuch/nm_db.c
new file mode 100644 (file)
index 0000000..a283a43
--- /dev/null
@@ -0,0 +1,307 @@
+/**
+ * @file
+ * Notmuch database handling
+ *
+ * @authors
+ * Copyright (C) 2018 Richard Russon <rich@flatcap.org>
+ *
+ * @copyright
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * 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/>.
+ */
+
+#include "config.h"
+#include <notmuch.h>
+#include <string.h>
+#include <sys/stat.h>
+#include "notmuch_private.h"
+#include "email/lib.h"
+#include "account.h"
+#include "globals.h"
+#include "mutt_logging.h"
+#include "mutt_notmuch.h"
+
+/**
+ * nm_db_get_filename - Get the filename of the Notmuch database
+ * @param m Mailbox
+ * @retval ptr Filename
+ *
+ * @note The return value is a pointer into the NmDefaultUri global variable.
+ *       If that variable changes, the result will be invalid.
+ *       It must not be freed.
+ */
+const char *nm_db_get_filename(struct Mailbox *m)
+{
+  struct NmMboxData *mdata = nm_mdata_get(m);
+  if (!mdata)
+    return NULL;
+
+  char *db_filename = mdata->db_url.path ? mdata->db_url.path : NmDefaultUri;
+  if (!db_filename)
+    db_filename = Folder;
+  if (!db_filename)
+    return NULL;
+  if (strncmp(db_filename, "notmuch://", 10) == 0)
+    db_filename += 10;
+
+  mutt_debug(2, "nm: db filename '%s'\n", db_filename);
+  return db_filename;
+}
+
+/**
+ * nm_db_do_open - Open a Notmuch database
+ * @param filename Database filename
+ * @param writable Read/write?
+ * @param verbose  Show errors on failure?
+ * @retval ptr Notmuch database
+ */
+notmuch_database_t *nm_db_do_open(const char *filename, bool writable, bool verbose)
+{
+  notmuch_database_t *db = NULL;
+  int ct = 0;
+  notmuch_status_t st = NOTMUCH_STATUS_SUCCESS;
+#if LIBNOTMUCH_CHECK_VERSION(4, 2, 0)
+  char *msg = NULL;
+#endif
+
+  mutt_debug(1, "nm: db open '%s' %s (timeout %d)\n", filename,
+             writable ? "[WRITE]" : "[READ]", NmOpenTimeout);
+
+  const notmuch_database_mode_t mode =
+      writable ? NOTMUCH_DATABASE_MODE_READ_WRITE : NOTMUCH_DATABASE_MODE_READ_ONLY;
+
+  const struct timespec wait = {
+    .tv_sec = 0, .tv_nsec = 500000000, /* Half a second */
+  };
+
+  do
+  {
+#if LIBNOTMUCH_CHECK_VERSION(4, 2, 0)
+    st = notmuch_database_open_verbose(filename, mode, &db, &msg);
+#elif defined(NOTMUCH_API_3)
+    st = notmuch_database_open(filename, mode, &db);
+#else
+    db = notmuch_database_open(filename, mode);
+#endif
+    if ((st == NOTMUCH_STATUS_FILE_ERROR) || db || !NmOpenTimeout || ((ct / 2) > NmOpenTimeout))
+      break;
+
+    if (verbose && ct && ((ct % 2) == 0))
+      mutt_error(_("Waiting for notmuch DB... (%d sec)"), ct / 2);
+    nanosleep(&wait, NULL);
+    ct++;
+  } while (true);
+
+  if (verbose)
+  {
+    if (!db)
+    {
+#if LIBNOTMUCH_CHECK_VERSION(4, 2, 0)
+      if (msg)
+      {
+        mutt_error(msg);
+        FREE(&msg);
+      }
+      else
+#endif
+      {
+        mutt_error(_("Cannot open notmuch database: %s: %s"), filename,
+                   st ? notmuch_status_to_string(st) : _("unknown reason"));
+      }
+    }
+    else if (ct > 1)
+    {
+      mutt_clear_error();
+    }
+  }
+  return db;
+}
+
+/**
+ * nm_db_get - Get the Notmuch database
+ * @param m        Mailbox
+ * @param writable Read/write?
+ * @retval ptr Notmuch database
+ */
+notmuch_database_t *nm_db_get(struct Mailbox *m, bool writable)
+{
+  if (!m || (m->magic != MUTT_NOTMUCH))
+    return NULL;
+  struct Account *a = m->account;
+  if (!a)
+    return NULL;
+  struct NmAccountData *adata = a->adata;
+  if (!adata)
+    return NULL;
+
+  const char *db_filename = nm_db_get_filename(m);
+  if (db_filename)
+    adata->db = nm_db_do_open(db_filename, writable, true);
+
+  return adata->db;
+}
+
+/**
+ * nm_db_release - Close the Notmuch database
+ * @param m Mailbox
+ * @retval  0 Success
+ * @retval -1 Failure
+ */
+int nm_db_release(struct Mailbox *m)
+{
+  struct NmAccountData *adata = nm_adata_get(m);
+  if (!adata || !adata->db)
+    return -1;
+
+  mutt_debug(1, "nm: db close\n");
+#ifdef NOTMUCH_API_3
+  notmuch_database_destroy(adata->db);
+#else
+  notmuch_database_close(adata->db);
+#endif
+  adata->db = NULL;
+  adata->longrun = false;
+  return 0;
+}
+
+/**
+ * nm_db_trans_begin - Start a Notmuch database transaction
+ * @param m Mailbox
+ * @retval <0 error
+ * @retval 1  new transaction started
+ * @retval 0  already within transaction
+ */
+int nm_db_trans_begin(struct Mailbox *m)
+{
+  struct NmAccountData *adata = nm_adata_get(m);
+  if (!adata || !adata->db)
+    return -1;
+
+  if (adata->trans)
+    return 0;
+
+  mutt_debug(2, "nm: db trans start\n");
+  if (notmuch_database_begin_atomic(adata->db))
+    return -1;
+  adata->trans = true;
+  return 1;
+}
+
+/**
+ * nm_db_trans_end - End a database transaction
+ * @param m Mailbox
+ * @retval  0 Success
+ * @retval -1 Failure
+ */
+int nm_db_trans_end(struct Mailbox *m)
+{
+  struct NmAccountData *adata = nm_adata_get(m);
+  if (!adata || !adata->db)
+    return -1;
+
+  if (!adata->trans)
+    return 0;
+
+  mutt_debug(2, "nm: db trans end\n");
+  adata->trans = false;
+  if (notmuch_database_end_atomic(adata->db))
+    return -1;
+
+  return 0;
+}
+
+/**
+ * nm_db_get_mtime - Get the database modification time
+ * @param[in]  m     Mailbox
+ * @param[out] mtime Save the modification time
+ * @retval  0 Success (result in mtime)
+ * @retval -1 Error
+ *
+ * Get the "mtime" (modification time) of the database file.
+ * This is the time of the last update.
+ */
+int nm_db_get_mtime(struct Mailbox *m, time_t *mtime)
+{
+  if (!m || !mtime)
+    return -1;
+
+  char path[PATH_MAX];
+  snprintf(path, sizeof(path), "%s/.notmuch/xapian", nm_db_get_filename(m));
+  mutt_debug(2, "nm: checking '%s' mtime\n", path);
+
+  struct stat st;
+  if (stat(path, &st))
+    return -1;
+
+  *mtime = st.st_mtime;
+  return 0;
+}
+
+/**
+ * nm_db_is_longrun - Is Notmuch in the middle of a long-running transaction
+ * @param m Mailbox
+ * @retval true if it is
+ */
+bool nm_db_is_longrun(struct Mailbox *m)
+{
+  struct NmAccountData *adata = nm_adata_get(m);
+  if (!adata)
+    return false;
+
+  return adata->longrun;
+}
+
+/**
+ * nm_db_longrun_init - Start a long transaction
+ * @param m        Mailbox
+ * @param writable Read/write?
+ */
+void nm_db_longrun_init(struct Mailbox *m, bool writable)
+{
+  struct NmAccountData *adata = nm_adata_get(m);
+
+  if (adata && nm_db_get(m, writable))
+  {
+    adata->longrun = true;
+    mutt_debug(2, "nm: long run initialized\n");
+  }
+}
+
+/**
+ * nm_db_longrun_done - Finish a long transaction
+ * @param m Mailbox
+ */
+void nm_db_longrun_done(struct Mailbox *m)
+{
+  struct NmAccountData *adata = nm_adata_get(m);
+
+  if (adata && (nm_db_release(m) == 0))
+    mutt_debug(2, "nm: long run deinitialized\n");
+}
+
+/**
+ * nm_db_debug_check - Check if the database is open
+ * @param m Mailbox
+ */
+void nm_db_debug_check(struct Mailbox *m)
+{
+  struct NmAccountData *adata = nm_adata_get(m);
+  if (!adata || !adata->db)
+    return;
+
+  if (adata->db)
+  {
+    mutt_debug(1, "nm: ERROR: db is open, closing\n");
+    nm_db_release(m);
+  }
+}
index e0624995dcfe5e638863a8051604b666967e321c..43e16c688e9e0d87aed663ed3de9f3f968fecf26 100644 (file)
@@ -27,6 +27,8 @@
 #ifndef MUTT_NOTMUCH_NOTMUCH_PRIVATE_H
 #define MUTT_NOTMUCH_NOTMUCH_PRIVATE_H
 
+struct Mailbox;
+
 /**
  * struct NmAccountData - Account-specific Notmuch data - @extends Account
  */
@@ -79,4 +81,25 @@ struct NmEmailData
   enum MailboxType magic; /**< Type of Mailbox the Email is in */
 };
 
+void                nm_db_debug_check (struct Mailbox *m);
+notmuch_database_t *nm_db_do_open     (const char *filename, bool writable, bool verbose);
+const char *        nm_db_get_filename(struct Mailbox *m);
+int                 nm_db_get_mtime   (struct Mailbox *m, time_t *mtime);
+notmuch_database_t *nm_db_get         (struct Mailbox *m, bool writable);
+bool                nm_db_is_longrun  (struct Mailbox *m);
+void                nm_db_longrun_done(struct Mailbox *m);
+void                nm_db_longrun_init(struct Mailbox *m, bool writable);
+int                 nm_db_release     (struct Mailbox *m);
+int                 nm_db_trans_begin (struct Mailbox *m);
+int                 nm_db_trans_end   (struct Mailbox *m);
+
+void                  nm_adata_free(void **ptr);
+struct NmAccountData *nm_adata_get (struct Mailbox *m);
+struct NmAccountData *nm_adata_new (void);
+void                  nm_edata_free(void **ptr);
+struct NmEmailData *  nm_edata_new (void);
+void                  nm_mdata_free(void **ptr);
+struct NmMboxData *   nm_mdata_get (struct Mailbox *m);
+struct NmMboxData *   nm_mdata_new (const char *uri);
+
 #endif /* MUTT_NOTMUCH_NOTMUCH_PRIVATE_H */