unofficial mirror of notmuch@notmuchmail.org
 help / color / mirror / code / Atom feed
* [RFC] database: get and set mapping of dovecot compatible maildir keywords
@ 2014-09-27  8:17 David Bremner
  2014-09-27 19:43 ` Michal Sojka
  0 siblings, 1 reply; 8+ messages in thread
From: David Bremner @ 2014-09-27  8:17 UTC (permalink / raw)
  To: notmuch

A future modification of notmuch_message_tags_to_maildir_flags and
notmuch_message_maildir_flags_to_tags could allow 26 more tags to be
synced via maildir.
---

I'm not sure if this is worth pursuing or not, but I thought I'd toss
it out there.  On IRC the other day the topic of syncing IMAP keywords
to notmuch tags came up again, in particular the dovecot variant that
maps 26 user defined keywords to characters a-z on the end of the
maildir info.  One roadblock I saw at the time was the need for
configuration of mapping of letters to tags.  This patch is the result
of my realizing that at least that part is not hard (unlike the rabbit
hole we seemed to get into for e.g. log configuration).

Some downsides of this approach are fairly obvious

    - only 26 tags. It turns out I don't have that many non-nmbug tags
      that I really care about. YMMV, of course.

    - nonstandard. this won't roundtrip via offlineimap (unless
      offlineimap is modified).  Directly syncing the maildir or using
      some dovecot specific syncer would work.

Some upsides are:

    - Provides an IMAP bridge solution; compatible with dovecot; my
      impression is this is the most common imap solution among
      notmuch users.  
    - relatively simple implementation, just need to
      update the maildir name synching routines. Of course no
      implementation is as simple as possible

 lib/database.cc | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 lib/notmuch.h   | 19 ++++++++++++++++++
 2 files changed, 80 insertions(+)

diff --git a/lib/database.cc b/lib/database.cc
index a3a7cd3..5427f7c 100644
--- a/lib/database.cc
+++ b/lib/database.cc
@@ -1169,6 +1169,67 @@ notmuch_database_get_version (notmuch_database_t *notmuch)
     return version;
 }
 
+notmuch_status_t
+notmuch_database_get_maildir_keyword (notmuch_database_t *notmuch,
+				      int index, const char **tag)
+{
+    string tag_string;
+    const char *key;
+    const char *str;
+
+    if (!notmuch || !tag)
+	return NOTMUCH_STATUS_NULL_POINTER;
+
+    if (index < 0 || index > ('z' - 'a'))
+	return NOTMUCH_STATUS_UNSUPPORTED_OPERATION;
+
+    key = talloc_asprintf(notmuch, "maildir_keyword_%c", 'a' + index);
+    if (!key)
+	return NOTMUCH_STATUS_OUT_OF_MEMORY;
+
+    *tag = NULL;
+    tag_string = notmuch->xapian_db->get_metadata (key);
+    if (tag_string.empty ())
+	return NOTMUCH_STATUS_SUCCESS;
+
+    str = tag_string.c_str ();
+    if (str == NULL || *str == '\0')
+	return NOTMUCH_STATUS_SUCCESS;
+
+    *tag = str;
+
+    return NOTMUCH_STATUS_SUCCESS;
+}
+
+notmuch_status_t
+notmuch_database_set_maildir_keyword (notmuch_database_t *notmuch,
+				      int index, const char *tag)
+{
+    string tag_string;
+    const char *key;
+    notmuch_status_t ret;
+    Xapian::WritableDatabase *db;
+
+    if (!notmuch || !tag)
+	return NOTMUCH_STATUS_NULL_POINTER;
+
+    ret = _notmuch_database_ensure_writable (notmuch);
+    if (ret)
+	return ret;
+
+    if (index < 0 || index > ('z' - 'a'))
+	return NOTMUCH_STATUS_UNSUPPORTED_OPERATION;
+
+    key = talloc_asprintf(notmuch, "maildir_keyword_%c", 'a' + index);
+    if (!key)
+	return NOTMUCH_STATUS_OUT_OF_MEMORY;
+
+    db = static_cast <Xapian::WritableDatabase *> (notmuch->xapian_db);
+    db->set_metadata (key, tag);
+
+    return NOTMUCH_STATUS_SUCCESS;
+}
+
 notmuch_bool_t
 notmuch_database_needs_upgrade (notmuch_database_t *notmuch)
 {
diff --git a/lib/notmuch.h b/lib/notmuch.h
index fe2340b..5fec8b3 100644
--- a/lib/notmuch.h
+++ b/lib/notmuch.h
@@ -614,6 +614,25 @@ notmuch_tags_t *
 notmuch_database_get_all_tags (notmuch_database_t *db);
 
 /**
+ * Return the tag corresponding to a maildir keyword.
+ *
+ */
+notmuch_status_t
+notmuch_database_get_maildir_keyword(notmuch_database_t *db,
+				     int index, char **tag);
+
+/**
+ * Set the tag corresponding to a maildir keyword.
+ *
+ * Note that no messages have their tags modified by this call.
+ */
+
+notmuch_status_t
+notmuch_database_set_maildir_keyword(notmuch_database_t *db,
+				     int index, const char *tag);
+
+
+/**
  * Create a new query for 'database'.
  *
  * Here, 'database' should be an open database, (see
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [RFC] database: get and set mapping of dovecot compatible maildir keywords
  2014-09-27  8:17 [RFC] database: get and set mapping of dovecot compatible maildir keywords David Bremner
@ 2014-09-27 19:43 ` Michal Sojka
  2014-09-28  6:27   ` David Bremner
  2014-09-28 18:28   ` David Bremner
  0 siblings, 2 replies; 8+ messages in thread
From: Michal Sojka @ 2014-09-27 19:43 UTC (permalink / raw)
  To: David Bremner, notmuch

Hi David,

On Sat, Sep 27 2014, David Bremner wrote:
> A future modification of notmuch_message_tags_to_maildir_flags and
> notmuch_message_maildir_flags_to_tags could allow 26 more tags to be
> synced via maildir.
> ---
>
> I'm not sure if this is worth pursuing or not, but I thought I'd toss
> it out there.  On IRC the other day the topic of syncing IMAP keywords
> to notmuch tags came up again, in particular the dovecot variant that
> maps 26 user defined keywords to characters a-z on the end of the
> maildir info.  One roadblock I saw at the time was the need for
> configuration of mapping of letters to tags.  This patch is the result
> of my realizing that at least that part is not hard (unlike the rabbit
> hole we seemed to get into for e.g. log configuration).
>
> Some downsides of this approach are fairly obvious
>
>     - only 26 tags. It turns out I don't have that many non-nmbug tags
>       that I really care about. YMMV, of course.
>
>     - nonstandard. this won't roundtrip via offlineimap (unless
>       offlineimap is modified).  Directly syncing the maildir or using
>       some dovecot specific syncer would work.
>
> Some upsides are:
>
>     - Provides an IMAP bridge solution; compatible with dovecot; my
>       impression is this is the most common imap solution among
>       notmuch users.  
>     - relatively simple implementation, just need to
>       update the maildir name synching routines. Of course no
>       implementation is as simple as possible
>
>  lib/database.cc | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++

I'd prefer having the mapping stored in the config file rather than in
the database. This would allow me to synchronize the configuration
between multiple computers. Would there be some benefit of having the
mapping (also) in the database?

-Michal

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [RFC] database: get and set mapping of dovecot compatible maildir keywords
  2014-09-27 19:43 ` Michal Sojka
@ 2014-09-28  6:27   ` David Bremner
  2014-09-28  6:36     ` David Bremner
  2014-09-28 18:28   ` David Bremner
  1 sibling, 1 reply; 8+ messages in thread
From: David Bremner @ 2014-09-28  6:27 UTC (permalink / raw)
  To: Michal Sojka, notmuch

Michal Sojka <sojkam1@fel.cvut.cz> writes:


> I'd prefer having the mapping stored in the config file rather than in
> the database. This would allow me to synchronize the configuration
> between multiple computers. Would there be some benefit of having the
> mapping (also) in the database?

In retrospect, probably not that great a benefit, except I kindof
dislike the idea of putting the mess that is notmuch-config.c into the
library, so we'd likely have to deal with coming up with a clean config
file mechanism.

I agree that being able to sync plain text config probably trumps this.

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [RFC] database: get and set mapping of dovecot compatible maildir keywords
  2014-09-28  6:27   ` David Bremner
@ 2014-09-28  6:36     ` David Bremner
  0 siblings, 0 replies; 8+ messages in thread
From: David Bremner @ 2014-09-28  6:36 UTC (permalink / raw)
  To: Michal Sojka, notmuch

David Bremner <david@tethera.net> writes:

> Michal Sojka <sojkam1@fel.cvut.cz> writes:
>
>
>> I'd prefer having the mapping stored in the config file rather than in
>> the database. This would allow me to synchronize the configuration
>> between multiple computers. Would there be some benefit of having the
>> mapping (also) in the database?
>
> In retrospect, probably not that great a benefit, except I kindof
> dislike the idea of putting the mess that is notmuch-config.c into the
> library, so we'd likely have to deal with coming up with a clean config
> file mechanism.

One issue such a configuration mechanism would have to deal with that
the current one does not is concurrent access by multiple clients. This
is not impossible, but just another complication.

d

^ permalink raw reply	[flat|nested] 8+ messages in thread

* (no subject)
  2014-09-27 19:43 ` Michal Sojka
  2014-09-28  6:27   ` David Bremner
@ 2014-09-28 18:28   ` David Bremner
  2014-09-28 18:28     ` [WIP 1/3] util: move chomp_newline to string-util.h David Bremner
                       ` (2 more replies)
  1 sibling, 3 replies; 8+ messages in thread
From: David Bremner @ 2014-09-28 18:28 UTC (permalink / raw)
  To: notmuch

Here's one approach to keeping config information at the library
level.  IMHO, a key philosophical point is that this metadata is
associated with a database, not with the library.

Having every key map to a distinct file is arguably not as nice for
humans to edit, but it avoids certain concurrency complications;
e.g. glib can atomically write a keyfile (like we use for
.notmuch-config), but that means e.g. the result of two concurrent
updates to different keys is not a valid serialization.

It won't be very efficient for huge numbers of keys, but for keeping
some static metadata associated with a notmuch database, this should
work ok.

In addition to needing some polishing (documentation? what
documentation?), this isn't actually used anywhere in notmuch.

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [WIP 1/3] util: move chomp_newline to string-util.h
  2014-09-28 18:28   ` David Bremner
@ 2014-09-28 18:28     ` David Bremner
  2014-09-28 18:28     ` [WIP 2/3] cli/insert: move several file/directory manipulation routines into a new util file David Bremner
  2014-09-28 18:28     ` [WIP 3/3] lib: add configuration framework David Bremner
  2 siblings, 0 replies; 8+ messages in thread
From: David Bremner @ 2014-09-28 18:28 UTC (permalink / raw)
  To: notmuch

This allows it to be called from functions in the library, if needed.

Instead of making all of string-util available to everywhere
notmuch-client.h is included, just add an extra include to the three
places chomp_newline was needed (and string-util.h not yet included).
---
 notmuch-client.h   | 7 -------
 notmuch-count.c    | 1 +
 notmuch-setup.c    | 1 +
 test/hex-xcode.c   | 2 ++
 util/string-util.h | 7 +++++++
 5 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/notmuch-client.h b/notmuch-client.h
index e1efbe0..bd01005 100644
--- a/notmuch-client.h
+++ b/notmuch-client.h
@@ -111,13 +111,6 @@ typedef struct notmuch_show_params {
 #define STRNCMP_LITERAL(var, literal) \
     strncmp ((var), (literal), sizeof (literal) - 1)
 
-static inline void
-chomp_newline (char *str)
-{
-    if (str && str[strlen(str)-1] == '\n')
-	str[strlen(str)-1] = '\0';
-}
-
 /* Exit status code indicating the requested format version is too old
  * (support for that version has been dropped).  CLI code should use
  * notmuch_exit_if_unsupported_format rather than directly exiting
diff --git a/notmuch-count.c b/notmuch-count.c
index 6058f7c..c3f4b54 100644
--- a/notmuch-count.c
+++ b/notmuch-count.c
@@ -20,6 +20,7 @@
  */
 
 #include "notmuch-client.h"
+#include "string-util.h"
 
 enum {
     OUTPUT_THREADS,
diff --git a/notmuch-setup.c b/notmuch-setup.c
index 36a6171..eb91179 100644
--- a/notmuch-setup.c
+++ b/notmuch-setup.c
@@ -19,6 +19,7 @@
  */
 
 #include "notmuch-client.h"
+#include "string-util.h"
 
 static const char *
 make_path_absolute (void *ctx, const char *path)
diff --git a/test/hex-xcode.c b/test/hex-xcode.c
index 65d4956..20d0a68 100644
--- a/test/hex-xcode.c
+++ b/test/hex-xcode.c
@@ -9,6 +9,8 @@
 
 #include "notmuch-client.h"
 #include "hex-escape.h"
+#include "string-util.h"
+
 #include <assert.h>
 
 enum direction {
diff --git a/util/string-util.h b/util/string-util.h
index e409cb3..9337770 100644
--- a/util/string-util.h
+++ b/util/string-util.h
@@ -64,6 +64,13 @@ int
 parse_boolean_term (void *ctx, const char *str,
 		    char **prefix_out, char **term_out);
 
+static inline void
+chomp_newline (char *str)
+{
+    if (str && str[strlen(str)-1] == '\n')
+	str[strlen(str)-1] = '\0';
+}
+
 #ifdef __cplusplus
 }
 #endif
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [WIP 2/3] cli/insert: move several file/directory manipulation routines into a new util file.
  2014-09-28 18:28   ` David Bremner
  2014-09-28 18:28     ` [WIP 1/3] util: move chomp_newline to string-util.h David Bremner
@ 2014-09-28 18:28     ` David Bremner
  2014-09-28 18:28     ` [WIP 3/3] lib: add configuration framework David Bremner
  2 siblings, 0 replies; 8+ messages in thread
From: David Bremner @ 2014-09-28 18:28 UTC (permalink / raw)
  To: notmuch

This will promote code sharing of low level file operations.
---
 notmuch-insert.c    | 79 ++----------------------------------------
 util/Makefile.local |  1 +
 util/file-util.c    | 98 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 util/file-util.h    | 20 +++++++++++
 4 files changed, 121 insertions(+), 77 deletions(-)
 create mode 100644 util/file-util.c
 create mode 100644 util/file-util.h

diff --git a/notmuch-insert.c b/notmuch-insert.c
index 5ef6e66..48bdd28 100644
--- a/notmuch-insert.c
+++ b/notmuch-insert.c
@@ -23,6 +23,7 @@
 
 #include "notmuch-client.h"
 #include "tag-util.h"
+#include "file-util.h"
 
 #include <sys/types.h>
 #include <sys/stat.h>
@@ -63,26 +64,6 @@ safe_gethostname (char *hostname, size_t len)
     }
 }
 
-/* Call fsync() on a directory path. */
-static notmuch_bool_t
-sync_dir (const char *dir)
-{
-    int fd, r;
-
-    fd = open (dir, O_RDONLY);
-    if (fd == -1) {
-	fprintf (stderr, "Error: open %s: %s\n", dir, strerror (errno));
-	return FALSE;
-    }
-
-    r = fsync (fd);
-    if (r)
-	fprintf (stderr, "Error: fsync %s: %s\n", dir, strerror (errno));
-
-    close (fd);
-
-    return r == 0;
-}
 
 /*
  * Check the specified folder name does not contain a directory
@@ -92,65 +73,9 @@ sync_dir (const char *dir)
 static notmuch_bool_t
 is_valid_folder_name (const char *folder)
 {
-    const char *p = folder;
-
-    for (;;) {
-	if ((p[0] == '.') && (p[1] == '.') && (p[2] == '\0' || p[2] == '/'))
-	    return FALSE;
-	p = strchr (p, '/');
-	if (!p)
-	    return TRUE;
-	p++;
-    }
+    return ! has_double_dot_component (folder);
 }
 
-/*
- * Make the given directory and its parents as necessary, using the
- * given mode. Return TRUE on success, FALSE otherwise. Partial
- * results are not cleaned up on errors.
- */
-static notmuch_bool_t
-mkdir_recursive (const void *ctx, const char *path, int mode)
-{
-    struct stat st;
-    int r;
-    char *parent = NULL, *slash;
-
-    /* First check the common case: directory already exists. */
-    r = stat (path, &st);
-    if (r == 0) {
-        if (! S_ISDIR (st.st_mode)) {
-	    fprintf (stderr, "Error: '%s' is not a directory: %s\n",
-		     path, strerror (EEXIST));
-	    return FALSE;
-	}
-
-	return TRUE;
-    } else if (errno != ENOENT) {
-	fprintf (stderr, "Error: stat '%s': %s\n", path, strerror (errno));
-	return FALSE;
-    }
-
-    /* mkdir parents, if any */
-    slash = strrchr (path, '/');
-    if (slash && slash != path) {
-	parent = talloc_strndup (ctx, path, slash - path);
-	if (! parent) {
-	    fprintf (stderr, "Error: %s\n", strerror (ENOMEM));
-	    return FALSE;
-	}
-
-	if (! mkdir_recursive (ctx, parent, mode))
-	    return FALSE;
-    }
-
-    if (mkdir (path, mode)) {
-	fprintf (stderr, "Error: mkdir '%s': %s\n", path, strerror (errno));
-	return FALSE;
-    }
-
-    return parent ? sync_dir (parent) : TRUE;
-}
 
 /*
  * Create the given maildir folder, i.e. maildir and its
diff --git a/util/Makefile.local b/util/Makefile.local
index 905f237..8749cfb 100644
--- a/util/Makefile.local
+++ b/util/Makefile.local
@@ -5,6 +5,7 @@ extra_cflags += -I$(srcdir)/$(dir)
 
 libutil_c_srcs := $(dir)/xutil.c $(dir)/error_util.c $(dir)/hex-escape.c \
 		  $(dir)/string-util.c $(dir)/talloc-extra.c $(dir)/zlib-extra.c \
+		  $(dir)/file-util.c \
 		$(dir)/util.c
 
 libutil_modules := $(libutil_c_srcs:.c=.o)
diff --git a/util/file-util.c b/util/file-util.c
new file mode 100644
index 0000000..66c4485
--- /dev/null
+++ b/util/file-util.c
@@ -0,0 +1,98 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <talloc.h>
+#include <unistd.h>
+
+#include "file-util.h"
+
+/*
+ * Check whether specified path name contains a directory
+ * component "..". to prevent writes outside of the Maildir
+ */
+int
+has_double_dot_component (const char *path)
+{
+    const char *p = path;
+
+    for (;;) {
+	if ((p[0] == '.') && (p[1] == '.') && (p[2] == '\0' || p[2] == '/'))
+	    return TRUE;
+	p = strchr (p, '/');
+	if (!p)
+	    return FALSE;
+	p++;
+    }
+}
+
+/*
+ * Make the given directory and its parents as necessary, using the
+ * given mode. return TRUE on success, 0 otherwise. Partial
+ * results are not cleaned up on errors.
+ */
+int
+mkdir_recursive (const void *ctx, const char *path, int mode)
+{
+    struct stat st;
+    int r;
+    char *parent = NULL, *slash;
+
+    /* First check the common case: directory already exists. */
+    r = stat (path, &st);
+    if (r == 0) {
+        if (! S_ISDIR (st.st_mode)) {
+	    fprintf (stderr, "Error: '%s' is not a directory: %s\n",
+		     path, strerror (EEXIST));
+	    return FALSE;
+	}
+
+	return TRUE;
+    } else if (errno != ENOENT) {
+	fprintf (stderr, "Error: stat '%s': %s\n", path, strerror (errno));
+	return FALSE;
+    }
+
+    /* mkdir parents, if any */
+    slash = strrchr (path, '/');
+    if (slash && slash != path) {
+	parent = talloc_strndup (ctx, path, slash - path);
+	if (! parent) {
+	    fprintf (stderr, "Error: %s\n", strerror (ENOMEM));
+	    return FALSE;
+	}
+
+	if (! mkdir_recursive (ctx, parent, mode))
+	    return FALSE;
+    }
+
+    if (mkdir (path, mode)) {
+	fprintf (stderr, "Error: mkdir '%s': %s\n", path, strerror (errno));
+	return FALSE;
+    }
+
+    return parent ? sync_dir (parent) : TRUE;
+}
+
+/* Call fsync() on a directory path. */
+int
+sync_dir (const char *dir)
+{
+    int fd, r;
+
+    fd = open (dir, O_RDONLY);
+    if (fd == -1) {
+	fprintf (stderr, "Error: open %s: %s\n", dir, strerror (errno));
+	return FALSE;
+    }
+
+    r = fsync (fd);
+    if (r)
+	fprintf (stderr, "Error: fsync %s: %s\n", dir, strerror (errno));
+
+    close (fd);
+
+    return r == 0;
+}
diff --git a/util/file-util.h b/util/file-util.h
new file mode 100644
index 0000000..4f96957
--- /dev/null
+++ b/util/file-util.h
@@ -0,0 +1,20 @@
+#ifndef _FILE_UTIL_H
+#define _FILE_UTIL_H
+
+#ifndef FALSE 
+#define FALSE 0
+#endif
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+int
+has_double_dot_component (const char *path);
+
+int
+mkdir_recursive (const void *ctx, const char *path, int mode);
+
+int
+sync_dir (const char *path);
+
+#endif
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [WIP 3/3] lib: add configuration framework.
  2014-09-28 18:28   ` David Bremner
  2014-09-28 18:28     ` [WIP 1/3] util: move chomp_newline to string-util.h David Bremner
  2014-09-28 18:28     ` [WIP 2/3] cli/insert: move several file/directory manipulation routines into a new util file David Bremner
@ 2014-09-28 18:28     ` David Bremner
  2 siblings, 0 replies; 8+ messages in thread
From: David Bremner @ 2014-09-28 18:28 UTC (permalink / raw)
  To: notmuch

Allow clients to atomically get and set key value pairs.
---
 lib/Makefile.local      |   1 +
 lib/config.c            | 156 ++++++++++++++++++++++++++++++++++++++++++++++++
 lib/notmuch.h           |   7 +++
 test/Makefile.local     |   7 +++
 test/T560-lib-config.sh |  15 +++++
 test/config-test.c      |  28 +++++++++
 6 files changed, 214 insertions(+)
 create mode 100644 lib/config.c
 create mode 100755 test/T560-lib-config.sh
 create mode 100644 test/config-test.c

diff --git a/lib/Makefile.local b/lib/Makefile.local
index 4120390..7ca2b3b 100644
--- a/lib/Makefile.local
+++ b/lib/Makefile.local
@@ -54,6 +54,7 @@ lib := $(dir)
 
 libnotmuch_c_srcs =		\
 	$(notmuch_compat_srcs)	\
+	$(dir)/config.c		\
 	$(dir)/filenames.c	\
 	$(dir)/string-list.c	\
 	$(dir)/libsha1.c	\
diff --git a/lib/config.c b/lib/config.c
new file mode 100644
index 0000000..c3b8f39
--- /dev/null
+++ b/lib/config.c
@@ -0,0 +1,156 @@
+#include "notmuch-private.h"
+#include "string-util.h"
+#include "file-util.h"
+#include <talloc.h>
+
+static notmuch_status_t 
+compute_paths (void *ctx, const char *notmuch_path, const char *key,
+	       char **parent_out, char **dest_out) {
+
+    char *parent, *dest, *final_component, *last_slash;
+
+    parent = talloc_asprintf (ctx, "%s/config/%s", notmuch_path, key);
+    if (!parent)
+	return NOTMUCH_STATUS_OUT_OF_MEMORY;
+
+    last_slash = strrchr (parent, '/');
+    *last_slash = '\0';
+
+    final_component = talloc_strdup (ctx, last_slash + 1);
+    
+    dest = talloc_asprintf(ctx, "%s/_%s", parent, final_component);
+    if (!dest)
+	return NOTMUCH_STATUS_OUT_OF_MEMORY;
+
+    *parent_out = parent;
+    *dest_out = dest;
+	
+    return NOTMUCH_STATUS_SUCCESS;
+
+}
+
+notmuch_status_t
+notmuch_config_get (const char *notmuch_path, const char *key, const char **val){
+
+    char *line = NULL;
+    size_t line_size;
+    ssize_t line_len;
+    char *buf = NULL;
+    char *file_name, *parent;
+    notmuch_status_t status;
+    void *local = NULL;
+    FILE *file_ptr = NULL;
+
+    if (notmuch_path == NULL || key == NULL || val == NULL)
+	return NOTMUCH_STATUS_NULL_POINTER;
+
+    local = talloc_new (NULL);
+    
+    status = compute_paths (local, notmuch_path, key, &parent, &file_name);
+    if (status) 
+	goto DONE;
+
+    file_ptr = fopen (file_name, "r");
+    if (file_ptr == NULL) {
+	status = NOTMUCH_STATUS_FILE_ERROR;
+	goto DONE;
+    }
+
+    while ((line_len = getline (&line, &line_size, file_ptr)) != -1) {
+
+	if (buf)
+	    buf = talloc_asprintf (local, "%s%s", buf, line);
+	else 
+	    buf = talloc_strdup (local, line);
+	
+	if (buf == NULL) {
+	    status = NOTMUCH_STATUS_OUT_OF_MEMORY;
+	    goto DONE;
+	}
+
+    }
+
+
+    /* remove the last newline. Convenient for the single line case. */
+    chomp_newline (buf);
+
+    *val = buf;
+    status =  NOTMUCH_STATUS_SUCCESS;
+	
+ DONE:
+    if (line)
+	free (line);
+    
+    if (file_ptr)
+	fclose (file_ptr);
+
+    talloc_free (local);
+
+    return status;
+}
+
+notmuch_status_t
+notmuch_config_set (const char *notmuch_path, const char *key, const char *val){
+
+    char *parent, *path, *temp_path;
+    int out_fd = -1;
+    notmuch_status_t status = NOTMUCH_STATUS_SUCCESS;
+    void *local = NULL;
+    FILE *out_file;
+    
+    if (notmuch_path == NULL || key == NULL || val == NULL)
+	return NOTMUCH_STATUS_NULL_POINTER;
+
+    if (has_double_dot_component (key))
+	return NOTMUCH_STATUS_UNSUPPORTED_OPERATION;
+
+    local = talloc_new (NULL);
+
+    status = compute_paths (local, notmuch_path, key, &parent, &path);
+    if (status) 
+	goto DONE;
+
+    if (! mkdir_recursive (local, parent, 0700)) {
+	status = NOTMUCH_STATUS_FILE_ERROR;
+	goto DONE;
+    }
+
+    temp_path = talloc_asprintf (local, "%s/tmp.XXXXXX", parent);
+    if (temp_path == NULL) {
+	status = NOTMUCH_STATUS_OUT_OF_MEMORY;
+	goto DONE;
+    }
+
+    out_fd = mkstemp (temp_path);
+    if (out_fd == -1) {
+	status = NOTMUCH_STATUS_FILE_ERROR;
+	goto DONE;
+    }
+    
+    out_file = fdopen (out_fd, "w");
+    if (out_file == NULL) {
+	status = NOTMUCH_STATUS_FILE_ERROR;
+	goto DONE;
+    }
+
+    if (fputs (val, out_file) == EOF) {
+	status = NOTMUCH_STATUS_FILE_ERROR;
+	goto DONE;
+    }
+
+    if (fclose (out_file)) {
+	status = NOTMUCH_STATUS_FILE_ERROR;
+	goto DONE;
+    }
+
+    if (rename (temp_path, path) < 0) {
+	status = NOTMUCH_STATUS_FILE_ERROR;
+	goto DONE;
+    }
+	
+ DONE:
+
+    talloc_free(local);
+    
+    return status;
+}
diff --git a/lib/notmuch.h b/lib/notmuch.h
index fe2340b..9a5f9df 100644
--- a/lib/notmuch.h
+++ b/lib/notmuch.h
@@ -192,6 +192,13 @@ typedef struct _notmuch_directory notmuch_directory_t;
 typedef struct _notmuch_filenames notmuch_filenames_t;
 #endif /* __DOXYGEN__ */
 
+
+notmuch_status_t
+notmuch_config_get (const char *notmuch_path, const char *key, const char **val);
+
+notmuch_status_t
+notmuch_config_set (const char *notmuch_path, const char *key, const char *val);
+
 /**
  * Create a new, empty notmuch database located at 'path'.
  *
diff --git a/test/Makefile.local b/test/Makefile.local
index a2d58fc..8a203f0 100644
--- a/test/Makefile.local
+++ b/test/Makefile.local
@@ -23,6 +23,9 @@ random_corpus_deps =  $(dir)/random-corpus.o  $(dir)/database-test.o \
 			lib/libnotmuch.a util/libutil.a \
 			parse-time-string/libparse-time-string.a
 
+config_test_deps =  $(dir)/config-test.o  \
+			lib/libnotmuch.a util/libutil.a
+
 $(dir)/random-corpus: $(random_corpus_deps)
 	$(call quiet,CXX) $(CFLAGS_FINAL) $^ -o $@ $(CONFIGURE_LDFLAGS)
 
@@ -38,6 +41,9 @@ $(dir)/parse-time: $(dir)/parse-time.o parse-time-string/parse-time-string.o
 $(dir)/make-db-version: $(dir)/make-db-version.o
 	$(call quiet,CXX) $^ -o $@ $(XAPIAN_LDFLAGS)
 
+$(dir)/config-test: $(config_test_deps)
+	$(call quiet,CXX) $(CFLAGS_FINAL) $^ -o $@ $(CONFIGURE_LDFLAGS)
+
 .PHONY: test check
 
 test_main_srcs=$(dir)/arg-test.c \
@@ -47,6 +53,7 @@ test_main_srcs=$(dir)/arg-test.c \
 	      $(dir)/smtp-dummy.c \
 	      $(dir)/symbol-test.cc \
 	      $(dir)/make-db-version.cc \
+	      $(dir)/config-test.c \
 
 test_srcs=$(test_main_srcs) $(dir)/database-test.c
 
diff --git a/test/T560-lib-config.sh b/test/T560-lib-config.sh
new file mode 100755
index 0000000..ec8ddbe
--- /dev/null
+++ b/test/T560-lib-config.sh
@@ -0,0 +1,15 @@
+#!/usr/bin/env bash
+test_description="library config handling"
+
+. ./test-lib.sh
+
+test_begin_subtest "getting and setting"
+${TEST_DIRECTORY}/config-test ${MAIL_DIR}/.notmuch set a foo
+${TEST_DIRECTORY}/config-test ${MAIL_DIR}/.notmuch set a/b bar
+${TEST_DIRECTORY}/config-test ${MAIL_DIR}/.notmuch set b/a fub
+${TEST_DIRECTORY}/config-test ${MAIL_DIR}/.notmuch get a  >> OUTPUT
+${TEST_DIRECTORY}/config-test ${MAIL_DIR}/.notmuch get a/b  >> OUTPUT
+${TEST_DIRECTORY}/config-test ${MAIL_DIR}/.notmuch get b/a  >> OUTPUT
+test_expect_equal "$(cat OUTPUT)" "foobarfub"
+
+test_done
diff --git a/test/config-test.c b/test/config-test.c
new file mode 100644
index 0000000..d9a1116
--- /dev/null
+++ b/test/config-test.c
@@ -0,0 +1,28 @@
+#include <stdio.h>
+#include <string.h>
+
+#include "notmuch.h"
+
+int
+main (int argc, char **argv) {
+    const char *val;
+    notmuch_status_t status;
+
+    if (argc == 4 && strcmp (argv[2], "get") == 0) {
+	
+	status = notmuch_config_get (argv[1], argv[3], &val);
+	if (status) 
+	    return status;
+	fputs (val, stdout);
+	return 0;
+
+    } else  if (argc == 5 && strcmp (argv[2], "set") == 0) {
+	
+	status = notmuch_config_set (argv[1], argv[3], argv[4]);
+	if (status) 
+	    return status;
+	return 0;
+    }
+
+    return 1;
+}
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2014-09-28 18:29 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-09-27  8:17 [RFC] database: get and set mapping of dovecot compatible maildir keywords David Bremner
2014-09-27 19:43 ` Michal Sojka
2014-09-28  6:27   ` David Bremner
2014-09-28  6:36     ` David Bremner
2014-09-28 18:28   ` David Bremner
2014-09-28 18:28     ` [WIP 1/3] util: move chomp_newline to string-util.h David Bremner
2014-09-28 18:28     ` [WIP 2/3] cli/insert: move several file/directory manipulation routines into a new util file David Bremner
2014-09-28 18:28     ` [WIP 3/3] lib: add configuration framework David Bremner

Code repositories for project(s) associated with this public inbox

	https://yhetil.org/notmuch.git/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).