--- /dev/null
+Return-Path: <glasse@cs.rpi.edu>\r
+X-Original-To: notmuch@notmuchmail.org\r
+Delivered-To: notmuch@notmuchmail.org\r
+Received: from localhost (localhost [127.0.0.1])\r
+ by olra.theworths.org (Postfix) with ESMTP id B8CC8429E4B\r
+ for <notmuch@notmuchmail.org>; Wed, 15 Feb 2012 14:14:24 -0800 (PST)\r
+X-Virus-Scanned: Debian amavisd-new at olra.theworths.org\r
+X-Spam-Flag: NO\r
+X-Spam-Score: -0.54\r
+X-Spam-Level: \r
+X-Spam-Status: No, score=-0.54 tagged_above=-999 required=5\r
+ tests=[DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1,\r
+ RCVD_IN_BL_SPAMCOP_NET=1.246, RCVD_IN_DNSWL_MED=-2.3,\r
+ RCVD_IN_SORBS_WEB=0.614] autolearn=disabled\r
+Received: from olra.theworths.org ([127.0.0.1])\r
+ by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024)\r
+ with ESMTP id QVD6xA9JVspE for <notmuch@notmuchmail.org>;\r
+ Wed, 15 Feb 2012 14:14:21 -0800 (PST)\r
+Received: from cliffclavin.cs.rpi.edu (cliffclavin.cs.rpi.edu\r
+ [128.113.126.25]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits))\r
+ (No client certificate requested) by olra.theworths.org (Postfix) with ESMTPS\r
+ id 08E39429E48 for <notmuch@notmuchmail.org>; Wed, 15 Feb 2012 14:14:20 -0800\r
+ (PST)\r
+X-Hash:\r
+ S|9d0a70cf09b4d94fc531de6b69290ddde7b9d232|4287d7537ed9cdc3a91eb958ef996dd2\r
+X-Countries: Cameroon, United States\r
+X-SMTP-From: accepted <glasse@cs.rpi.edu> [195.24.209.20] [195.24.209.20]\r
+ (localhost) {Cameroon}\r
+DKIM-Signature: v=1; a=rsa-sha1; c=relaxed/relaxed; d=cs.rpi.edu; h=from\r
+ :to:cc:subject:date:message-id:in-reply-to:references; s=\r
+ default; i=glasse@cs.rpi.edu; t=1329344058; x=1329948858; l=17033;\r
+ bh=e8Cxd0KCuBnxMSP/RNsbHyyPFZI=; b=hb6rfQltD4z2WLFV6LX0NO\r
+ ahG6qF8SAQ0WRadpVixFA/hhRwgFptsAJblnZUXzJ8Ad9HWZt71J9AOBy/C1J7U4\r
+ yFzSjq3iI35Gjq7LRRg5PHNXwusjQf+0XWtkfEXPhxmePQhYSNXYX7m4TvKM6Ub2\r
+ aqTwemkujepZYbc4Tezak=\r
+DomainKey-Signature: a=rsa-sha1; c=nofws; d=cs.rpi.edu; h=from:to:cc\r
+ :subject:date:message-id:in-reply-to:references; q=dns; s=\r
+ default; b=j2zcLiGUqE7Nw5LOENLSOWn7AMC2wp5Wm2D6AJ/eE+KJ3WaJdkbwy\r
+ 8Jjbm9usaKc4hRy2jLXWRIIeMhVY3jP+02xjPROevwuTTArJZV187ODtmdgkIRlk\r
+ xxPjBs41j0TGIcYQb/UAwCxBZjrsUuhKr0SVm4dhqxAD8Be/ro9xRI=\r
+X-Spam-Info: -2.7; ALL_TRUSTED,AWL,BAYES_00\r
+X-Spam-Scanned-By: cliffclavin.cs.rpi.edu using SpamAssassin 3.2.5 (hard limit\r
+ 15)\r
+Authentication-Results: cliffclavin.cs.rpi.edu;\r
+ DKIM=neutral (none) header.from=glasse@cs.rpi.edu;\r
+ SPF=neutral (mfrom;\r
+ Mechanism '?all' matched) smtp.mail=glasse@cs.rpi.edu\r
+X-Auth-Passed: cliffclavin.cs.rpi.edu:q1FMDOMs008891 Auth:glasse\r
+X-Virus-Scanned-By: cliffclavin.cs.rpi.edu\r
+Received: from localhost ([195.24.209.20]) (authenticated bits=0)\r
+ by cliffclavin.cs.rpi.edu (8.14.3/8.14.3) with ESMTP id q1FMDOMs008891\r
+ (version=TLSv1/SSLv3 cipher=DHE-RSA-AES128-SHA bits=128 verify=NO);\r
+ Wed, 15 Feb 2012 17:13:36 -0500 (EST)\r
+ (envelope-from glasse@cs.rpi.edu)\r
+From: Ethan Glasser-Camp <glasse@cs.rpi.edu>\r
+To: notmuch@notmuchmail.org\r
+Subject: [RFC PATCH 13/13] First crack at a CouchDB mailstore\r
+Date: Wed, 15 Feb 2012 17:02:06 -0500\r
+Message-Id: <1329343326-16410-14-git-send-email-glasse@cs.rpi.edu>\r
+X-Mailer: git-send-email 1.7.5.4\r
+In-Reply-To: <1329343326-16410-1-git-send-email-glasse@cs.rpi.edu>\r
+References: <1329343326-16410-1-git-send-email-glasse@cs.rpi.edu>\r
+X-Scanned-By: MIMEDefang 2.67 on 128.113.126.25\r
+Cc: Ethan Glasser-Camp <ethan@betacantrips.com>\r
+X-BeenThere: notmuch@notmuchmail.org\r
+X-Mailman-Version: 2.1.13\r
+Precedence: list\r
+List-Id: "Use and development of the notmuch mail system."\r
+ <notmuch.notmuchmail.org>\r
+List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,\r
+ <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
+List-Archive: <http://notmuchmail.org/pipermail/notmuch>\r
+List-Post: <mailto:notmuch@notmuchmail.org>\r
+List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
+List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,\r
+ <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
+X-List-Received-Date: Wed, 15 Feb 2012 22:14:24 -0000\r
+\r
+From: Ethan Glasser-Camp <ethan@betacantrips.com>\r
+\r
+This introduces new parameters to notmuch-config to store the CouchDB\r
+URL and the "name" of the database.\r
+\r
+Signed-off-by: Ethan Glasser-Camp <ethan@betacantrips.com>\r
+---\r
+ Makefile.local | 3 +\r
+ lib/mailstore.c | 109 ++++++++++++++++++++++++++++++++\r
+ notmuch-client.h | 14 ++++\r
+ notmuch-config.c | 91 +++++++++++++++++++++++++-\r
+ notmuch-new.c | 184 ++++++++++++++++++++++++++++++++++++++++++++++++++++++\r
+ 5 files changed, 397 insertions(+), 4 deletions(-)\r
+\r
+diff --git a/Makefile.local b/Makefile.local\r
+index 1131dea..a105e58 100644\r
+--- a/Makefile.local\r
++++ b/Makefile.local\r
+@@ -27,6 +27,9 @@ endif\r
+ \r
+ UPSTREAM_TAG=$(subst ~,_,$(VERSION))\r
+ DEB_TAG=debian/$(UPSTREAM_TAG)-1\r
++# FIXME: Where should this really go?\r
++LDFLAGS += $(shell pkg-config --libs couchdb-glib-1.0 libsoup-2.4)\r
++extra_cflags += $(shell pkg-config --cflags couchdb-glib-1.0 libsoup-2.4)\r
+ \r
+ RELEASE_HOST=notmuchmail.org\r
+ RELEASE_DIR=/srv/notmuchmail.org/www/releases\r
+diff --git a/lib/mailstore.c b/lib/mailstore.c\r
+index 51c2710..4d7cc79 100644\r
+--- a/lib/mailstore.c\r
++++ b/lib/mailstore.c\r
+@@ -18,6 +18,10 @@\r
+ \r
+ #include <stdio.h>\r
+ #include <stdarg.h>\r
++#include <couchdb-session.h>\r
++#include <couchdb-database.h>\r
++#include <couchdb-document.h>\r
++#include <glib.h>\r
+ \r
+ #include "notmuch-private.h"\r
+ \r
+@@ -58,6 +62,101 @@ _maildir_rename_function (unused (notmuch_mailstore_t *mailstore),\r
+ return rename (old_filename, new_filename);\r
+ }\r
+ \r
++struct _couchdb_data {\r
++ char *db_path;\r
++ CouchdbDatabase *database;\r
++ GHashTable *files_to_documents;\r
++};\r
++\r
++/* CouchDB mailstore */\r
++static notmuch_status_t\r
++_couchdb_constructor (void **data, va_list ap)\r
++{\r
++ CouchdbSession *session = NULL;\r
++ CouchdbDatabase *database = NULL;\r
++ GError *error = NULL;\r
++ char *uri = NULL;\r
++ char *db_name = NULL;\r
++ struct _couchdb_data *my_data = NULL;\r
++\r
++ uri = va_arg (ap, char*);\r
++ session = couchdb_session_new (uri);\r
++\r
++ db_name = va_arg (ap, char*);\r
++ database = couchdb_session_get_database (session, db_name, &error);\r
++ if (database == NULL) {\r
++ fprintf (stderr, "Couldn't access database %s: %s\n", db_name,\r
++ error->message);\r
++ return NOTMUCH_STATUS_FILE_ERROR;\r
++ }\r
++\r
++ my_data = talloc_size (NULL, sizeof (struct _couchdb_data));\r
++ my_data->database = database;\r
++ my_data->db_path = va_arg (ap, char*);\r
++ my_data->files_to_documents = g_hash_table_new (NULL, NULL);\r
++ (*data) = (void*)my_data;\r
++\r
++ return NOTMUCH_STATUS_SUCCESS;\r
++}\r
++\r
++static FILE *\r
++_couchdb_open_function (notmuch_mailstore_t *mailstore,\r
++ const char *filename)\r
++{\r
++ CouchdbDatabase *database = NULL;\r
++ CouchdbDocument *document = NULL;\r
++ GError *error = NULL;\r
++ const char *text = NULL;\r
++ const char *relative = NULL;\r
++ struct _couchdb_data *data = (struct _couchdb_data *)mailstore->data;\r
++ FILE *ret = NULL;\r
++ database = data->database;\r
++ /* message assumes all files should be contained within db_path.\r
++ * This isn't true for us, so remove the db_path.\r
++ * I'd like to use _notmuch_database_relative_path but I don't have\r
++ * a notmuch_database_t*.\r
++ */\r
++ relative = filename;\r
++ if (strncmp (filename, data->db_path, strlen (data->db_path)) == 0) {\r
++ relative = filename + strlen (data->db_path);\r
++ while (*relative == '/' && *(relative+1) == '/')\r
++ relative++;\r
++ }\r
++\r
++ document = couchdb_database_get_document (database, relative, &error);\r
++ if (document == NULL)\r
++ /* file doesn't exist. Maybe it got deleted? */\r
++ return NULL;\r
++\r
++ text = couchdb_document_get_string_field (document, "text");\r
++ /* FIXME: null bytes in the mail file? */\r
++ ret = fmemopen ((char *)text, strlen(text), "r");\r
++ g_hash_table_insert (data->files_to_documents, ret, document);\r
++ return ret;\r
++}\r
++\r
++static int\r
++_couchdb_close_function (notmuch_mailstore_t *mailstore, FILE *file)\r
++{\r
++ struct _couchdb_data *data = (struct _couchdb_data *)mailstore->data;\r
++ GHashTable *hash = data->files_to_documents;\r
++ CouchdbDocument *document;\r
++ document = g_hash_table_lookup (hash, file);\r
++ g_object_unref (document);\r
++ fclose (file); /* just to be polite ;) */\r
++ g_hash_table_remove (hash, file);\r
++ return 0;\r
++}\r
++\r
++static int\r
++_couchdb_rename_function (unused (notmuch_mailstore_t *mailstore),\r
++ unused (const char *old_filename),\r
++ unused (const char *new_filename))\r
++{\r
++ /* Pass for now. */\r
++ return 0;\r
++}\r
++\r
+ /* A mailstore is defined as:\r
+ *\r
+ * - A function used to "open" a mail message. This takes the\r
+@@ -80,12 +179,22 @@ notmuch_mailstore_maildir = { _maildir_constructor,\r
+ _maildir_rename_function,\r
+ NULL };\r
+ \r
++_notmuch_mailstore\r
++notmuch_mailstore_couchdb = { _couchdb_constructor,\r
++ _couchdb_open_function, _couchdb_close_function,\r
++ _couchdb_rename_function,\r
++ NULL};\r
++\r
++\r
+ _notmuch_mailstore *\r
+ notmuch_mailstore_get_by_name (const char *name)\r
+ {\r
+ if (strcmp (name, "maildir") == 0)\r
+ return ¬much_mailstore_maildir;\r
+ \r
++ if (strcmp (name, "couchdb") == 0)\r
++ return ¬much_mailstore_couchdb;\r
++\r
+ return NULL;\r
+ }\r
+ \r
+diff --git a/notmuch-client.h b/notmuch-client.h\r
+index 405aad7..12dc868 100644\r
+--- a/notmuch-client.h\r
++++ b/notmuch-client.h\r
+@@ -230,6 +230,20 @@ void\r
+ notmuch_config_set_database_type (notmuch_config_t *config,\r
+ const char *database_type);\r
+ \r
++const char *\r
++notmuch_config_get_database_uri (notmuch_config_t *config);\r
++\r
++void\r
++notmuch_config_set_database_uri (notmuch_config_t *config,\r
++ const char *database_uri);\r
++\r
++const char *\r
++notmuch_config_get_database_name (notmuch_config_t *config);\r
++\r
++void\r
++notmuch_config_set_database_name (notmuch_config_t *config,\r
++ const char *database_name);\r
++\r
+ notmuch_mailstore_t *\r
+ notmuch_config_get_mailstore (notmuch_config_t *config);\r
+ \r
+diff --git a/notmuch-config.c b/notmuch-config.c\r
+index 99f872d..6090150 100644\r
+--- a/notmuch-config.c\r
++++ b/notmuch-config.c\r
+@@ -37,8 +37,8 @@ static const char database_config_comment[] =\r
+ "\n"\r
+ " The following options are supported here:\n"\r
+ "\n"\r
+- "\ttype The type of mail backend. The only currently supported\n"\r
+- "\t value is \"maildir\".\n"\r
++ "\ttype The type of mail backend. The currently supported\n"\r
++ "\t values are \"maildir\" and \"couchdb\".\n"\r
+ "\tpath For the maildir backend, the top-level maildir directory.\n"\r
+ "\t For all backends, the location where notmuch should store its\n"\r
+ "\t database. Notmuch will store its database within a sub-directory\n"\r
+@@ -49,7 +49,14 @@ static const char database_config_comment[] =\r
+ " This backend reads mail from a directory tree where files are\n"\r
+ " individual email messages.\n"\r
+ " The only configuration option is 'path' which should be the top-level\n"\r
+- " directory.\n";\r
++ " directory.\n"\r
++ " CouchDB backend\n"\r
++ "\n"\r
++ " This backend reads mail from a CouchDB database via HTTP.\n"\r
++ " For more details on the setup of such a database, please see the help\n"\r
++ " files.\n"\r
++ " The configuration options are 'uri' and 'name', which specify the URI\n"\r
++ " of the CouchDB instance and the database name of the mail store.\n";\r
+ \r
+ static const char new_config_comment[] =\r
+ " Configuration for \"notmuch new\"\n"\r
+@@ -113,6 +120,8 @@ struct _notmuch_config {\r
+ \r
+ char *database_path;\r
+ char *database_type;\r
++ char *database_uri;\r
++ char *database_name;\r
+ char *user_name;\r
+ char *user_primary_email;\r
+ const char **user_other_email;\r
+@@ -273,6 +282,8 @@ notmuch_config_open (void *ctx,\r
+ \r
+ config->database_path = NULL;\r
+ config->database_type = NULL;\r
++ config->database_uri = NULL;\r
++ config->database_name = NULL;\r
+ config->user_name = NULL;\r
+ config->user_primary_email = NULL;\r
+ config->user_other_email = NULL;\r
+@@ -339,6 +350,12 @@ notmuch_config_open (void *ctx,\r
+ notmuch_config_set_database_type (config, "maildir");\r
+ }\r
+ \r
++ if (notmuch_config_get_database_uri (config) == NULL)\r
++ notmuch_config_set_database_uri (config, "");\r
++\r
++ if (notmuch_config_get_database_name (config) == NULL)\r
++ notmuch_config_set_database_name (config, "");\r
++\r
+ if (notmuch_config_get_user_name (config) == NULL) {\r
+ char *name = get_name_from_passwd_file (config);\r
+ notmuch_config_set_user_name (config, name);\r
+@@ -584,6 +601,62 @@ notmuch_config_set_database_type (notmuch_config_t *config,\r
+ config->database_type = NULL;\r
+ }\r
+ \r
++const char *\r
++notmuch_config_get_database_uri (notmuch_config_t *config)\r
++{\r
++ char *uri;\r
++\r
++ if (config->database_uri == NULL) {\r
++ uri = g_key_file_get_string (config->key_file,\r
++ "database", "uri", NULL);\r
++ if (uri) {\r
++ config->database_uri = talloc_strdup (config, uri);\r
++ free (uri);\r
++ }\r
++ }\r
++\r
++ return config->database_uri;\r
++}\r
++\r
++void\r
++notmuch_config_set_database_uri (notmuch_config_t *config,\r
++ const char *database_uri)\r
++{\r
++ g_key_file_set_string (config->key_file,\r
++ "database", "uri", database_uri);\r
++\r
++ talloc_free (config->database_uri);\r
++ config->database_uri = NULL;\r
++}\r
++\r
++const char *\r
++notmuch_config_get_database_name (notmuch_config_t *config)\r
++{\r
++ char *name;\r
++\r
++ if (config->database_name == NULL) {\r
++ name = g_key_file_get_string (config->key_file,\r
++ "database", "name", NULL);\r
++ if (name) {\r
++ config->database_name = talloc_strdup (config, name);\r
++ free (name);\r
++ }\r
++ }\r
++\r
++ return config->database_name;\r
++}\r
++\r
++void\r
++notmuch_config_set_database_name (notmuch_config_t *config,\r
++ const char *database_name)\r
++{\r
++ g_key_file_set_string (config->key_file,\r
++ "database", "name", database_name);\r
++\r
++ talloc_free (config->database_name);\r
++ config->database_name = NULL;\r
++}\r
++\r
+ notmuch_mailstore_t *\r
+ notmuch_config_get_mailstore (notmuch_config_t *config)\r
+ {\r
+@@ -595,7 +668,17 @@ notmuch_config_get_mailstore (notmuch_config_t *config)\r
+ notmuch_status_t status;\r
+ const char *type = notmuch_config_get_database_type (config);\r
+ notmuch_mailstore_t *mailstore = notmuch_mailstore_get_by_name (type);\r
+- status = notmuch_mailstore_construct (mailstore);\r
++ if (strcmp (type, "maildir") == 0)\r
++ status = notmuch_mailstore_construct (mailstore);\r
++ else if (strcmp (type, "couchdb") == 0)\r
++ status = notmuch_mailstore_construct (mailstore,\r
++ notmuch_config_get_database_uri (config),\r
++ notmuch_config_get_database_name (config),\r
++ notmuch_config_get_database_path (config));\r
++ else\r
++ /* Doomed, doomed, doomed */\r
++ status = NOTMUCH_STATUS_FILE_ERROR;\r
++\r
+ if (status != NOTMUCH_STATUS_SUCCESS) {\r
+ /* abort messily? */\r
+ }\r
+diff --git a/notmuch-new.c b/notmuch-new.c\r
+index d30fba1..3c1acb2 100644\r
+--- a/notmuch-new.c\r
++++ b/notmuch-new.c\r
+@@ -21,6 +21,11 @@\r
+ #include "notmuch-client.h"\r
+ \r
+ #include <unistd.h>\r
++#include <libsoup/soup-method.h>\r
++#include <couchdb-session.h>\r
++#include <couchdb-database.h>\r
++#include <couchdb-document.h>\r
++#include <json-glib/json-glib.h>\r
+ \r
+ typedef struct _filename_node {\r
+ char *filename;\r
+@@ -297,6 +302,182 @@ _add_message (add_files_state_t *state, notmuch_database_t *notmuch,\r
+ return ret;\r
+ }\r
+ \r
++/* Send an unsupported message to a couchdb instance.\r
++ *\r
++ * This function is "supposed" to be "part" of the "public API",\r
++ * but it isn't declared in couchdb-glib's header files. See:\r
++ * https://bugs.launchpad.net/couchdb-glib/+bug/927847\r
++ */\r
++gboolean\r
++couchdb_session_send_message (CouchdbSession *session, const char *method, const char *url, const char *body, JsonParser *output, GError **error);\r
++\r
++/* Process a JSON "change" object and either add or delete the "file".\r
++ *\r
++ * This is based on code from couchdb-glib, which is why it's a weird\r
++ * melange of glib style and notmuch style.\r
++ *\r
++ * As with Maildir, we assume that message objects never change.\r
++ */\r
++static void\r
++couchdb_process_change (add_files_state_t *state,\r
++ notmuch_database_t *notmuch,\r
++ CouchdbDatabase *database,\r
++ JsonNode *node)\r
++{\r
++ JsonObject *this_change;\r
++ const gchar *id;\r
++ CouchdbDocument *document;\r
++ GError *error = NULL;\r
++\r
++ if (json_node_get_node_type (node) != JSON_NODE_OBJECT)\r
++ return;\r
++\r
++ this_change = json_node_get_object (node);\r
++ if (!json_object_has_member (this_change, "id"))\r
++ return;\r
++\r
++ id = json_object_get_string_member (this_change, "id");\r
++\r
++ /* We need to try retrieving the document, to check if it's removed or not */\r
++ document = couchdb_database_get_document (database, id, &error);\r
++ if (document) {\r
++ /* We got a document, dump it into Notmuch */\r
++ _report_before_adding_file (state, id);\r
++ _add_message (state, notmuch, id);\r
++ _report_added_file (state);\r
++ g_object_unref (G_OBJECT (document));\r
++ }\r
++ else {\r
++ if (error != NULL) {\r
++ g_warning ("Error retrieving document '%s': %s", id, error->message);\r
++ g_error_free (error);\r
++ } else {\r
++ /* The document is no longer in the DB, notify */\r
++ id = talloc_strdup (state->removed_files, id);\r
++ _filename_list_add (state->removed_files, id);\r
++ }\r
++ }\r
++}\r
++\r
++/* Fetch a batch of database updates from couch's "changes" feed.\r
++ *\r
++ * This is essentially a copied and modified version of code from\r
++ * couchdb-glib. There's code to "watch a feed of changes", but I just\r
++ * want to do it once and synchronously.\r
++ */\r
++static notmuch_status_t\r
++couchdb_add_messages_batch (add_files_state_t *state,\r
++ notmuch_database_t *notmuch,\r
++ CouchdbDatabase *database,\r
++ guint32 *last_seq, int limit)\r
++{\r
++ char *url;\r
++ JsonParser *parser;\r
++ GError *error = NULL;\r
++\r
++ url = g_strdup_printf ("%s/%s/_changes?since=%d&limit=%d",\r
++ couchdb_session_get_uri (couchdb_database_get_session (database)),\r
++ couchdb_database_get_name (database),\r
++ *last_seq, limit);\r
++ parser = json_parser_new ();\r
++\r
++ if (couchdb_session_send_message (couchdb_database_get_session (database),\r
++ SOUP_METHOD_GET, url, NULL, parser,\r
++ &error)) {\r
++ JsonNode *root_node;\r
++\r
++ root_node = json_parser_get_root (parser);\r
++ if (json_node_get_node_type (root_node) == JSON_NODE_OBJECT) {\r
++ JsonObject *root_object;\r
++ JsonArray *results;\r
++\r
++ root_object = json_node_get_object (root_node);\r
++ results = json_object_get_array_member (root_object, "results");\r
++ if (results) {\r
++ GList *json_elements, *sl;\r
++\r
++ json_elements = json_array_get_elements (results);\r
++ for (sl = json_elements; !interrupted && sl != NULL; sl = sl->next)\r
++ couchdb_process_change (state, notmuch, database,\r
++ (JsonNode *) sl->data);\r
++ g_list_free (json_elements);\r
++ }\r
++\r
++ if (json_object_has_member (root_object, "last_seq"))\r
++ *last_seq = json_object_get_int_member (root_object, "last_seq");\r
++ }\r
++ }\r
++\r
++ /* Free memory */\r
++ g_object_unref (G_OBJECT (parser));\r
++ g_free (url);\r
++\r
++ return NOTMUCH_STATUS_SUCCESS;\r
++}\r
++\r
++/* Couchdb add_files function.\r
++ *\r
++ * Use the Couchdb _changes API to just ask what files have been added or deleted.\r
++ *\r
++ * We use a dummy "/" directory to store the last change we got from couch.\r
++ */\r
++static notmuch_status_t\r
++couchdb_add_files (notmuch_database_t *notmuch,\r
++ notmuch_config_t *config,\r
++ add_files_state_t *state)\r
++{\r
++ CouchdbSession *session;\r
++ CouchdbDatabase *database;\r
++ GError *error;\r
++ notmuch_directory_t *directory;\r
++ notmuch_status_t status;\r
++ time_t db_mtime;\r
++ guint32 last_seq = 0;\r
++ guint32 old_last_seq = 0;\r
++ const char *db_name;\r
++ const char *uri;\r
++\r
++ /* These are probably abstraction-breaking hacks. Life is tough. */\r
++ uri = notmuch_config_get_database_uri (config);\r
++ db_name = notmuch_config_get_database_name (config);\r
++\r
++ /* FIXME: is this necessary? I think probably not? */\r
++ /*\r
++ db_name = talloc_strdup (config, db_name);\r
++ uri = talloc_strdup (config, uri);\r
++ */\r
++\r
++ session = couchdb_session_new (uri);\r
++ database = couchdb_session_get_database (session, db_name, &error);\r
++ if (database == NULL) {\r
++ fprintf (stderr, "Error: couldn't access couchdb database %s, %s: %s",\r
++ uri, db_name, error->message);\r
++ return NOTMUCH_STATUS_FILE_ERROR;\r
++ }\r
++\r
++ /* Store a dummy directory at / that just contains last_seq as its mtime. */\r
++ directory = notmuch_database_get_directory (notmuch, "/");\r
++ db_mtime = notmuch_directory_get_mtime (directory);\r
++ last_seq = (int)db_mtime;\r
++\r
++ /* Grab updates in sets of 100 just to be safe with memory. */\r
++ do {\r
++ if (interrupted)\r
++ break;\r
++ old_last_seq = last_seq;\r
++ status = couchdb_add_messages_batch (state, notmuch, database,\r
++ &last_seq, 100);\r
++ if (status != NOTMUCH_STATUS_SUCCESS) {\r
++ return status;\r
++ }\r
++ } while (last_seq == old_last_seq + 100);\r
++\r
++ notmuch_directory_set_mtime (directory, last_seq);\r
++ g_object_unref (database);\r
++ g_object_unref (session);\r
++ notmuch_directory_destroy (directory);\r
++ return NOTMUCH_STATUS_SUCCESS;\r
++}\r
+ \r
+ /* Examine 'path' recursively as follows:\r
+ *\r
+@@ -678,6 +859,9 @@ add_files (notmuch_database_t *notmuch, notmuch_config_t *config,\r
+ if (strcmp (notmuch_config_get_database_type (config), "maildir") == 0)\r
+ return maildir_add_files (notmuch, path, state);\r
+ \r
++ else if (strcmp (notmuch_config_get_database_type (config), "couchdb") == 0)\r
++ return couchdb_add_files (notmuch, config, state);\r
++\r
+ /* Default case */\r
+ fprintf (stderr, "Could not add files for mailstore %s: unknown mailstore\n",\r
+ notmuch_config_get_database_type (config));\r
+-- \r
+1.7.5.4\r
+\r