From 6804b8a3f47894810d2ffa16b1429b0eb9f6c11b Mon Sep 17 00:00:00 2001 From: Ethan Glasser-Camp Date: Thu, 16 Feb 2012 17:01:55 +1900 Subject: [PATCH] [RFC PATCH 02/13] Add the concept of a mailstore in its absolute minimal sense --- 7b/5f91766ed469dc4d88322aed1f40821617eb89 | 1108 +++++++++++++++++++++ 1 file changed, 1108 insertions(+) create mode 100644 7b/5f91766ed469dc4d88322aed1f40821617eb89 diff --git a/7b/5f91766ed469dc4d88322aed1f40821617eb89 b/7b/5f91766ed469dc4d88322aed1f40821617eb89 new file mode 100644 index 000000000..efbefb05b --- /dev/null +++ b/7b/5f91766ed469dc4d88322aed1f40821617eb89 @@ -0,0 +1,1108 @@ +Return-Path: +X-Original-To: notmuch@notmuchmail.org +Delivered-To: notmuch@notmuchmail.org +Received: from localhost (localhost [127.0.0.1]) + by olra.theworths.org (Postfix) with ESMTP id 499BD429E4D + for ; Wed, 15 Feb 2012 14:05:21 -0800 (PST) +X-Virus-Scanned: Debian amavisd-new at olra.theworths.org +X-Spam-Flag: NO +X-Spam-Score: -0.54 +X-Spam-Level: +X-Spam-Status: No, score=-0.54 tagged_above=-999 required=5 + tests=[DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, + RCVD_IN_BL_SPAMCOP_NET=1.246, RCVD_IN_DNSWL_MED=-2.3, + RCVD_IN_SORBS_WEB=0.614] autolearn=disabled +Received: from olra.theworths.org ([127.0.0.1]) + by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024) + with ESMTP id l8jycoFcKWZ4 for ; + Wed, 15 Feb 2012 14:05:17 -0800 (PST) +Received: from cliffclavin.cs.rpi.edu (cliffclavin.cs.rpi.edu + [128.113.126.25]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) + (No client certificate requested) by olra.theworths.org (Postfix) with ESMTPS + id D6089431E62 for ; Wed, 15 Feb 2012 14:05:16 -0800 + (PST) +X-Hash: SCtCte|89dfd227c9662973caf8391b7d228905169bd57f|97dc102990e8867edb52cd10b7e2ee4b +X-Countries: Cameroon, United States +X-SMTP-From: accepted [195.24.209.20] [195.24.209.20] + (localhost) {Cameroon} +DKIM-Signature: v=1; a=rsa-sha1; c=relaxed/relaxed; d=cs.rpi.edu; h=from + :to:cc:subject:date:message-id:in-reply-to:references + :mime-version:content-type:content-transfer-encoding; s=default; + i=glasse@cs.rpi.edu; t=1329343514; x=1329948314; l=35856; bh=OT + sr0oOTFdwKst/jNUR1YAEfm8s=; b=dO/rdBc0sonb8IjwP6uYCMDQX5mU+46QaW + GDHTdq1M0LxCMl0Re2zWsOs975MGeb5e4IhxwJyQW83BRasSMVPX1wRbdUy0p45f + I2zFDTGORHlhbphVpOsz9nb/IJTLQkOCoKwjiVkBziFlFtUbd33OlJ5v+ky/+Uqs + 9D8kupyjo= +DomainKey-Signature: a=rsa-sha1; c=nofws; d=cs.rpi.edu; h=from:to:cc + :subject:date:message-id:in-reply-to:references:mime-version + :content-type:content-transfer-encoding; q=dns; s=default; b=DQm + VIkdWCj7voYXR1hMb3ZXdLVLbsPuBzB7PR8EGSkCSb2wlVOpH0TcxYqEW2syBAcY + LdudrqeN8Y4mqsk0+iJdgGAh+lm4oxWBR1n1R1eW8aUqULD7I6xeNyDtpQeJcrHn + On4Pmbrv/0f4VPPosFot7q/pf5zQywrZqlzA3RVY= +X-Spam-Info: -2.6; ALL_TRUSTED,AWL,BAYES_00,SARE_SUB_ENC_UTF8 +X-Spam-Scanned-By: cliffclavin.cs.rpi.edu using SpamAssassin 3.2.5 (hard limit + 15) +Authentication-Results: cliffclavin.cs.rpi.edu; + DKIM=neutral (none) header.from=glasse@cs.rpi.edu; + SPF=neutral (mfrom; + Mechanism '?all' matched) smtp.mail=glasse@cs.rpi.edu +X-Auth-Passed: cliffclavin.cs.rpi.edu:q1FM3qv2008742 Auth:glasse +X-Virus-Scanned-By: cliffclavin.cs.rpi.edu +Received: from localhost ([195.24.209.20]) (authenticated bits=0) + by cliffclavin.cs.rpi.edu (8.14.3/8.14.3) with ESMTP id q1FM3qv2008742 + (version=TLSv1/SSLv3 cipher=DHE-RSA-AES128-SHA bits=128 verify=NO); + Wed, 15 Feb 2012 17:04:06 -0500 (EST) + (envelope-from glasse@cs.rpi.edu) +From: Ethan Glasser-Camp +To: notmuch@notmuchmail.org +Subject: [RFC PATCH 02/13] Add the concept of a mailstore in its absolute minimal sense +Date: Wed, 15 Feb 2012 17:01:55 -0500 +Message-Id: <1329343326-16410-3-git-send-email-glasse@cs.rpi.edu> +X-Mailer: git-send-email 1.7.5.4 +In-Reply-To: <1329343326-16410-1-git-send-email-glasse@cs.rpi.edu> +References: <1329343326-16410-1-git-send-email-glasse@cs.rpi.edu> +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit +X-Scanned-By: MIMEDefang 2.67 on 128.113.126.25 +Cc: Ethan Glasser-Camp +X-BeenThere: notmuch@notmuchmail.org +X-Mailman-Version: 2.1.13 +Precedence: list +List-Id: "Use and development of the notmuch mail system." + +List-Unsubscribe: , + +List-Archive: +List-Post: +List-Help: +List-Subscribe: , + +X-List-Received-Date: Wed, 15 Feb 2012 22:05:21 -0000 + +From: Ethan Glasser-Camp + +This introduces (and uses) the mailstore parameter to the +notmuch_message_file_open API, and passes this through wherever it +will be needed. This requires touching a lot of places just to change +one API. We end up adding it to the notmuch_database_t struct because +it is needed for notmuch_database_upgrade. + +This doesn't touch the Python bindings, which require a certain amount +of effort. (Therefore, the Python tests will be broken until the next commit.) + +Signed-off-by: Ethan Glasser-Camp +--- + lib/Makefile.local | 1 + + lib/database-private.h | 1 + + lib/database.cc | 10 +++++--- + lib/mailstore.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++ + lib/message-file.c | 10 +++++--- + lib/message.cc | 11 +++++--- + lib/notmuch-private.h | 7 ++++- + lib/notmuch.h | 21 ++++++++++++++-- + lib/thread.cc | 24 +++++++++++-------- + notmuch-client.h | 6 +++++ + notmuch-config.c | 12 +++++++++ + notmuch-count.c | 3 +- + notmuch-dump.c | 3 +- + notmuch-new.c | 8 +++++- + notmuch-reply.c | 45 ++++++++++++++++++++++++------------ + notmuch-restore.c | 3 +- + notmuch-search.c | 3 +- + notmuch-show.c | 56 ++++++++++++++++++++++++++++++--------------- + notmuch-tag.c | 3 +- + test/symbol-test.cc | 3 +- + 20 files changed, 220 insertions(+), 69 deletions(-) + create mode 100644 lib/mailstore.c + +diff --git a/lib/Makefile.local b/lib/Makefile.local +index 54c4dea..461e5cd 100644 +--- a/lib/Makefile.local ++++ b/lib/Makefile.local +@@ -51,6 +51,7 @@ libnotmuch_c_srcs = \ + $(dir)/filenames.c \ + $(dir)/string-list.c \ + $(dir)/libsha1.c \ ++ $(dir)/mailstore.c \ + $(dir)/message-file.c \ + $(dir)/messages.c \ + $(dir)/sha1.c \ +diff --git a/lib/database-private.h b/lib/database-private.h +index 88532d5..1cb8c43 100644 +--- a/lib/database-private.h ++++ b/lib/database-private.h +@@ -39,6 +39,7 @@ + struct _notmuch_database { + notmuch_bool_t exception_reported; + ++ notmuch_mailstore_t *mailstore; + char *path; + + notmuch_bool_t needs_upgrade; +diff --git a/lib/database.cc b/lib/database.cc +index c928d02..e3c8095 100644 +--- a/lib/database.cc ++++ b/lib/database.cc +@@ -521,7 +521,7 @@ parse_references (void *ctx, + } + + notmuch_database_t * +-notmuch_database_create (const char *path) ++notmuch_database_create (notmuch_mailstore_t *mailstore, const char *path) + { + notmuch_database_t *notmuch = NULL; + char *notmuch_path = NULL; +@@ -556,7 +556,7 @@ notmuch_database_create (const char *path) + goto DONE; + } + +- notmuch = notmuch_database_open (path, ++ notmuch = notmuch_database_open (mailstore, path, + NOTMUCH_DATABASE_MODE_READ_WRITE); + notmuch_database_upgrade (notmuch, NULL, NULL); + +@@ -579,7 +579,8 @@ _notmuch_database_ensure_writable (notmuch_database_t *notmuch) + } + + notmuch_database_t * +-notmuch_database_open (const char *path, ++notmuch_database_open (notmuch_mailstore_t *mailstore, ++ const char *path, + notmuch_database_mode_t mode) + { + void *local = talloc_new (NULL); +@@ -619,6 +620,7 @@ notmuch_database_open (const char *path, + notmuch = talloc_zero (NULL, notmuch_database_t); + notmuch->exception_reported = FALSE; + notmuch->path = talloc_strdup (notmuch, path); ++ notmuch->mailstore = mailstore; + + if (notmuch->path[strlen (notmuch->path) - 1] == '/') + notmuch->path[strlen (notmuch->path) - 1] = '\0'; +@@ -1636,7 +1638,7 @@ notmuch_database_add_message (notmuch_database_t *notmuch, + if (ret) + return ret; + +- message_file = notmuch_message_file_open (filename); ++ message_file = notmuch_message_file_open (notmuch->mailstore, filename); + if (message_file == NULL) + return NOTMUCH_STATUS_FILE_ERROR; + +diff --git a/lib/mailstore.c b/lib/mailstore.c +new file mode 100644 +index 0000000..290da70 +--- /dev/null ++++ b/lib/mailstore.c +@@ -0,0 +1,59 @@ ++/* mailstore.c - mail storage backends ++ * ++ * Copyright © 2009 Carl Worth ++ * ++ * 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 3 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 ++ ++#include "notmuch-private.h" ++ ++typedef struct _notmuch_mailstore { ++ FILE *(*open) (struct _notmuch_mailstore *mailstore, const char *filename); ++} _notmuch_mailstore; ++ ++static FILE * ++_maildir_open_function (unused (notmuch_mailstore_t *mailstore), ++ const char *filename) ++{ ++ return fopen (filename, "r"); ++} ++ ++/* A mailstore is defined as: ++ * ++ * - A function used to "open" a mail message. This takes the ++ * "filename" for the file and should return a FILE *. ++ * ++ * - TODO: A way to scan for new messages? ++ * ++ * - TODO: A "constructor"? ++ */ ++_notmuch_mailstore ++notmuch_mailstore_maildir = { _maildir_open_function }; ++ ++_notmuch_mailstore * ++notmuch_mailstore_get_by_name (const char *name) ++{ ++ if (strcmp (name, "maildir") == 0) ++ return ¬much_mailstore_maildir; ++ ++ return NULL; ++} ++ ++FILE * ++notmuch_mailstore_open (notmuch_mailstore_t *mailstore, const char *filename) ++{ ++ return mailstore->open (mailstore, filename); ++} +diff --git a/lib/message-file.c b/lib/message-file.c +index 915aba8..61f4d04 100644 +--- a/lib/message-file.c ++++ b/lib/message-file.c +@@ -94,7 +94,8 @@ _notmuch_message_file_destructor (notmuch_message_file_t *message) + /* Create a new notmuch_message_file_t for 'filename' with 'ctx' as + * the talloc owner. */ + notmuch_message_file_t * +-_notmuch_message_file_open_ctx (void *ctx, const char *filename) ++_notmuch_message_file_open_ctx (void *ctx, notmuch_mailstore_t *mailstore, ++ const char *filename) + { + notmuch_message_file_t *message; + +@@ -104,7 +105,7 @@ _notmuch_message_file_open_ctx (void *ctx, const char *filename) + + talloc_set_destructor (message, _notmuch_message_file_destructor); + +- message->file = fopen (filename, "r"); ++ message->file = notmuch_mailstore_open (mailstore, filename); + if (message->file == NULL) + goto FAIL; + +@@ -126,9 +127,10 @@ _notmuch_message_file_open_ctx (void *ctx, const char *filename) + } + + notmuch_message_file_t * +-notmuch_message_file_open (const char *filename) ++notmuch_message_file_open (notmuch_mailstore_t *mailstore, ++ const char *filename) + { +- return _notmuch_message_file_open_ctx (NULL, filename); ++ return _notmuch_message_file_open_ctx (NULL, mailstore, filename); + } + + void +diff --git a/lib/message.cc b/lib/message.cc +index 0075425..762a18f 100644 +--- a/lib/message.cc ++++ b/lib/message.cc +@@ -395,7 +395,8 @@ notmuch_message_get_message_id (notmuch_message_t *message) + } + + static void +-_notmuch_message_ensure_message_file (notmuch_message_t *message) ++_notmuch_message_ensure_message_file (notmuch_mailstore_t *mailstore, ++ notmuch_message_t *message) + { + const char *filename; + +@@ -406,11 +407,13 @@ _notmuch_message_ensure_message_file (notmuch_message_t *message) + if (unlikely (filename == NULL)) + return; + +- message->message_file = _notmuch_message_file_open_ctx (message, filename); ++ message->message_file = _notmuch_message_file_open_ctx (message, mailstore, ++ filename); + } + + const char * +-notmuch_message_get_header (notmuch_message_t *message, const char *header) ++notmuch_message_get_header (notmuch_mailstore_t *mailstore, ++ notmuch_message_t *message, const char *header) + { + std::string value; + +@@ -427,7 +430,7 @@ notmuch_message_get_header (notmuch_message_t *message, const char *header) + return talloc_strdup (message, value.c_str ()); + + /* Otherwise fall back to parsing the file */ +- _notmuch_message_ensure_message_file (message); ++ _notmuch_message_ensure_message_file (mailstore, message); + if (message->message_file == NULL) + return NULL; + +diff --git a/lib/notmuch-private.h b/lib/notmuch-private.h +index 7bf153e..0f01437 100644 +--- a/lib/notmuch-private.h ++++ b/lib/notmuch-private.h +@@ -331,11 +331,14 @@ typedef struct _notmuch_message_file notmuch_message_file_t; + * Returns NULL if any error occurs. + */ + notmuch_message_file_t * +-notmuch_message_file_open (const char *filename); ++notmuch_message_file_open (notmuch_mailstore_t *mailstore, ++ const char *filename); + + /* Like notmuch_message_file_open but with 'ctx' as the talloc owner. */ + notmuch_message_file_t * +-_notmuch_message_file_open_ctx (void *ctx, const char *filename); ++_notmuch_message_file_open_ctx (void *ctx, ++ notmuch_mailstore_t *mailstore, ++ const char *filename); + + /* Close a notmuch message previously opened with notmuch_message_open. */ + void +diff --git a/lib/notmuch.h b/lib/notmuch.h +index 7929fe7..7ebe034 100644 +--- a/lib/notmuch.h ++++ b/lib/notmuch.h +@@ -123,6 +123,7 @@ typedef struct _notmuch_message notmuch_message_t; + typedef struct _notmuch_tags notmuch_tags_t; + typedef struct _notmuch_directory notmuch_directory_t; + typedef struct _notmuch_filenames notmuch_filenames_t; ++typedef struct _notmuch_mailstore notmuch_mailstore_t; + + /* Create a new, empty notmuch database located at 'path'. + * +@@ -144,7 +145,7 @@ typedef struct _notmuch_filenames notmuch_filenames_t; + * an error message on stderr). + */ + notmuch_database_t * +-notmuch_database_create (const char *path); ++notmuch_database_create (notmuch_mailstore_t *mailstore, const char *path); + + typedef enum { + NOTMUCH_DATABASE_MODE_READ_ONLY = 0, +@@ -172,7 +173,8 @@ typedef enum { + * an error message on stderr). + */ + notmuch_database_t * +-notmuch_database_open (const char *path, ++notmuch_database_open (notmuch_mailstore_t *mailstore, ++ const char *path, + notmuch_database_mode_t mode); + + /* Close the given notmuch database, freeing all associated +@@ -409,6 +411,18 @@ notmuch_database_find_message_by_filename (notmuch_database_t *notmuch, + notmuch_tags_t * + notmuch_database_get_all_tags (notmuch_database_t *db); + ++/* Return a mail storage backend based on the given name. ++ * ++ * Storage backends are required in order to manipulate message files. ++ */ ++notmuch_mailstore_t * ++notmuch_mailstore_get_by_name (const char *name); ++ ++/* Get an input stream for a filename. ++ */ ++FILE * ++notmuch_mailstore_open (notmuch_mailstore_t *mailstore, const char *filename); ++ + /* Create a new query for 'database'. + * + * Here, 'database' should be an open database, (see +@@ -929,7 +943,8 @@ notmuch_message_get_date (notmuch_message_t *message); + * header line matching 'header'. Returns NULL if any error occurs. + */ + const char * +-notmuch_message_get_header (notmuch_message_t *message, const char *header); ++notmuch_message_get_header (notmuch_mailstore_t *mailstore, ++ notmuch_message_t *message, const char *header); + + /* Get the tags for 'message', returning a notmuch_tags_t object which + * can be used to iterate over all tags. +diff --git a/lib/thread.cc b/lib/thread.cc +index 0435ee6..73edf83 100644 +--- a/lib/thread.cc ++++ b/lib/thread.cc +@@ -213,7 +213,8 @@ _thread_cleanup_author (notmuch_thread_t *thread, + * reference to it. + */ + static void +-_thread_add_message (notmuch_thread_t *thread, ++_thread_add_message (notmuch_mailstore_t *mailstore, ++ notmuch_thread_t *thread, + notmuch_message_t *message) + { + notmuch_tags_t *tags; +@@ -231,7 +232,7 @@ _thread_add_message (notmuch_thread_t *thread, + xstrdup (notmuch_message_get_message_id (message)), + message); + +- from = notmuch_message_get_header (message, "from"); ++ from = notmuch_message_get_header (mailstore, message, "from"); + if (from) + list = internet_address_list_parse_string (from); + +@@ -253,7 +254,7 @@ _thread_add_message (notmuch_thread_t *thread, + + if (! thread->subject) { + const char *subject; +- subject = notmuch_message_get_header (message, "subject"); ++ subject = notmuch_message_get_header (mailstore, message, "subject"); + thread->subject = talloc_strdup (thread, subject ? subject : ""); + } + +@@ -267,13 +268,14 @@ _thread_add_message (notmuch_thread_t *thread, + } + + static void +-_thread_set_subject_from_message (notmuch_thread_t *thread, ++_thread_set_subject_from_message (notmuch_mailstore_t *mailstore, ++ notmuch_thread_t *thread, + notmuch_message_t *message) + { + const char *subject; + const char *cleaned_subject; + +- subject = notmuch_message_get_header (message, "subject"); ++ subject = notmuch_message_get_header (mailstore, message, "subject"); + if (! subject) + return; + +@@ -300,7 +302,8 @@ _thread_set_subject_from_message (notmuch_thread_t *thread, + * oldest or newest matching subject is applied to the thread as a + * whole. */ + static void +-_thread_add_matched_message (notmuch_thread_t *thread, ++_thread_add_matched_message (notmuch_mailstore_t *mailstore, ++ notmuch_thread_t *thread, + notmuch_message_t *message, + notmuch_sort_t sort) + { +@@ -312,13 +315,13 @@ _thread_add_matched_message (notmuch_thread_t *thread, + if (date < thread->oldest || ! thread->matched_messages) { + thread->oldest = date; + if (sort == NOTMUCH_SORT_OLDEST_FIRST) +- _thread_set_subject_from_message (thread, message); ++ _thread_set_subject_from_message (mailstore, thread, message); + } + + if (date > thread->newest || ! thread->matched_messages) { + thread->newest = date; + if (sort != NOTMUCH_SORT_OLDEST_FIRST) +- _thread_set_subject_from_message (thread, message); ++ _thread_set_subject_from_message (mailstore, thread, message); + } + + thread->matched_messages++; +@@ -467,11 +470,12 @@ _notmuch_thread_create (void *ctx, + if (doc_id == seed_doc_id) + message = seed_message; + +- _thread_add_message (thread, message); ++ _thread_add_message (notmuch->mailstore, thread, message); + + if ( _notmuch_doc_id_set_contains (match_set, doc_id)) { + _notmuch_doc_id_set_remove (match_set, doc_id); +- _thread_add_matched_message (thread, message, sort); ++ _thread_add_matched_message (notmuch->mailstore, thread, ++ message, sort); + } + + _notmuch_message_close (message); +diff --git a/notmuch-client.h b/notmuch-client.h +index 4518cb0..c1c30a2 100644 +--- a/notmuch-client.h ++++ b/notmuch-client.h +@@ -68,14 +68,17 @@ struct notmuch_show_params; + typedef struct notmuch_show_format { + const char *message_set_start; + void (*part) (const void *ctx, ++ notmuch_mailstore_t *mailstore, + struct mime_node *node, int indent, + const struct notmuch_show_params *params); + const char *message_start; + void (*message) (const void *ctx, ++ notmuch_mailstore_t *mailstore, + notmuch_message_t *message, + int indent); + const char *header_start; + void (*header) (const void *ctx, ++ notmuch_mailstore_t *mailstore, + notmuch_message_t *message); + void (*header_message_part) (GMimeMessage *message); + const char *header_end; +@@ -226,6 +229,9 @@ void + notmuch_config_set_database_type (notmuch_config_t *config, + const char *database_type); + ++notmuch_mailstore_t * ++notmuch_config_get_mailstore (notmuch_config_t *config); ++ + const char * + notmuch_config_get_user_name (notmuch_config_t *config); + +diff --git a/notmuch-config.c b/notmuch-config.c +index b8bee69..f611b26 100644 +--- a/notmuch-config.c ++++ b/notmuch-config.c +@@ -584,6 +584,18 @@ notmuch_config_set_database_type (notmuch_config_t *config, + config->database_type = NULL; + } + ++notmuch_mailstore_t * ++notmuch_config_get_mailstore (notmuch_config_t *config) ++{ ++ /* This is just a stub since there's only one mailstore. ++ * ++ * When there are multiple mailstore types and "constructors" for ++ * them, this may have to be much more complicated. ++ */ ++ const char *type = notmuch_config_get_database_type (config); ++ return notmuch_mailstore_get_by_name (type); ++} ++ + const char * + notmuch_config_get_user_name (notmuch_config_t *config) + { +diff --git a/notmuch-count.c b/notmuch-count.c +index 63459fb..0c7beef 100644 +--- a/notmuch-count.c ++++ b/notmuch-count.c +@@ -57,7 +57,8 @@ notmuch_count_command (void *ctx, int argc, char *argv[]) + if (config == NULL) + return 1; + +- notmuch = notmuch_database_open (notmuch_config_get_database_path (config), ++ notmuch = notmuch_database_open (notmuch_config_get_mailstore (config), ++ notmuch_config_get_database_path (config), + NOTMUCH_DATABASE_MODE_READ_ONLY); + if (notmuch == NULL) + return 1; +diff --git a/notmuch-dump.c b/notmuch-dump.c +index a735875..f7729dd 100644 +--- a/notmuch-dump.c ++++ b/notmuch-dump.c +@@ -36,7 +36,8 @@ notmuch_dump_command (unused (void *ctx), int argc, char *argv[]) + if (config == NULL) + return 1; + +- notmuch = notmuch_database_open (notmuch_config_get_database_path (config), ++ notmuch = notmuch_database_open (notmuch_config_get_mailstore (config), ++ notmuch_config_get_database_path (config), + NOTMUCH_DATABASE_MODE_READ_ONLY); + if (notmuch == NULL) + return 1; +diff --git a/notmuch-new.c b/notmuch-new.c +index 8dbebb3..355dde4 100644 +--- a/notmuch-new.c ++++ b/notmuch-new.c +@@ -808,6 +808,7 @@ notmuch_new_command (void *ctx, int argc, char *argv[]) + { + notmuch_config_t *config; + notmuch_database_t *notmuch; ++ notmuch_mailstore_t *mailstore; + add_files_state_t add_files_state; + double elapsed; + struct timeval tv_now, tv_start; +@@ -843,6 +844,7 @@ notmuch_new_command (void *ctx, int argc, char *argv[]) + add_files_state.new_tags = notmuch_config_get_new_tags (config, &add_files_state.new_tags_length); + add_files_state.synchronize_flags = notmuch_config_get_maildir_synchronize_flags (config); + db_path = notmuch_config_get_database_path (config); ++ mailstore = notmuch_config_get_mailstore (config); + + if (run_hooks) { + ret = notmuch_run_hook (db_path, "pre-new"); +@@ -861,10 +863,12 @@ notmuch_new_command (void *ctx, int argc, char *argv[]) + return 1; + + printf ("Found %d total files (that's not much mail).\n", count); +- notmuch = notmuch_database_create (db_path); ++ notmuch = notmuch_database_create (mailstore, ++ db_path); + add_files_state.total_files = count; + } else { +- notmuch = notmuch_database_open (db_path, ++ notmuch = notmuch_database_open (mailstore, ++ db_path, + NOTMUCH_DATABASE_MODE_READ_WRITE); + if (notmuch == NULL) + return 1; +diff --git a/notmuch-reply.c b/notmuch-reply.c +index 6b244e6..cb1dd6e 100644 +--- a/notmuch-reply.c ++++ b/notmuch-reply.c +@@ -256,14 +256,15 @@ scan_address_string (const char *recipients, + * in either the 'To' or 'Cc' header of the message? + */ + static int +-reply_to_header_is_redundant (notmuch_message_t *message) ++reply_to_header_is_redundant (notmuch_mailstore_t *mailstore, ++ notmuch_message_t *message) + { + const char *reply_to, *to, *cc, *addr; + InternetAddressList *list; + InternetAddress *address; + InternetAddressMailbox *mailbox; + +- reply_to = notmuch_message_get_header (message, "reply-to"); ++ reply_to = notmuch_message_get_header (mailstore, message, "reply-to"); + if (reply_to == NULL || *reply_to == '\0') + return 0; + +@@ -279,8 +280,8 @@ reply_to_header_is_redundant (notmuch_message_t *message) + mailbox = INTERNET_ADDRESS_MAILBOX (address); + addr = internet_address_mailbox_get_addr (mailbox); + +- to = notmuch_message_get_header (message, "to"); +- cc = notmuch_message_get_header (message, "cc"); ++ to = notmuch_message_get_header (mailstore, message, "to"); ++ cc = notmuch_message_get_header (mailstore, message, "cc"); + + if ((to && strstr (to, addr) != 0) || + (cc && strstr (cc, addr) != 0)) +@@ -319,10 +320,13 @@ add_recipients_from_message (GMimeMessage *reply, + { "cc", NULL, GMIME_RECIPIENT_TYPE_CC }, + { "bcc", NULL, GMIME_RECIPIENT_TYPE_BCC } + }; ++ notmuch_mailstore_t *mailstore; + const char *from_addr = NULL; + unsigned int i; + unsigned int n = 0; + ++ mailstore = notmuch_config_get_mailstore (config); ++ + /* Some mailing lists munge the Reply-To header despite it being A Bad + * Thing, see http://www.unicom.com/pw/reply-to-harmful.html + * +@@ -334,7 +338,7 @@ add_recipients_from_message (GMimeMessage *reply, + * that the address in the Reply-To header will always appear in + * the reply. + */ +- if (reply_to_header_is_redundant (message)) { ++ if (reply_to_header_is_redundant (mailstore, message)) { + reply_to_map[0].header = "from"; + reply_to_map[0].fallback = NULL; + } +@@ -342,10 +346,10 @@ add_recipients_from_message (GMimeMessage *reply, + for (i = 0; i < ARRAY_SIZE (reply_to_map); i++) { + const char *recipients; + +- recipients = notmuch_message_get_header (message, ++ recipients = notmuch_message_get_header (mailstore, message, + reply_to_map[i].header); + if ((recipients == NULL || recipients[0] == '\0') && reply_to_map[i].fallback) +- recipients = notmuch_message_get_header (message, ++ recipients = notmuch_message_get_header (mailstore, message, + reply_to_map[i].fallback); + + n += scan_address_string (recipients, config, reply, +@@ -374,6 +378,7 @@ add_recipients_from_message (GMimeMessage *reply, + static const char * + guess_from_received_header (notmuch_config_t *config, notmuch_message_t *message) + { ++ notmuch_mailstore_t *mailstore; + const char *received,*primary,*by; + const char **other; + char *tohdr; +@@ -387,6 +392,7 @@ guess_from_received_header (notmuch_config_t *config, notmuch_message_t *message + + primary = notmuch_config_get_user_primary_email (config); + other = notmuch_config_get_user_other_email (config, &other_len); ++ mailstore = notmuch_config_get_mailstore (config); + + /* sadly, there is no standard way to find out to which email + * address a mail was delivered - what is in the headers depends +@@ -403,7 +409,8 @@ guess_from_received_header (notmuch_config_t *config, notmuch_message_t *message + * If none of these work, we give up and return NULL + */ + for (i = 0; i < sizeof(to_headers)/sizeof(*to_headers); i++) { +- tohdr = xstrdup(notmuch_message_get_header (message, to_headers[i])); ++ tohdr = xstrdup(notmuch_message_get_header (mailstore, ++ message, to_headers[i])); + if (tohdr && *tohdr) { + /* tohdr is potentialy a list of email addresses, so here we + * check if one of the email addresses is a substring of tohdr +@@ -428,7 +435,7 @@ guess_from_received_header (notmuch_config_t *config, notmuch_message_t *message + * The Received: header is special in our get_header function + * and is always concatenated. + */ +- received = notmuch_message_get_header (message, "received"); ++ received = notmuch_message_get_header (mailstore, message, "received"); + if (received == NULL) + return NULL; + +@@ -515,10 +522,12 @@ notmuch_reply_format_default(void *ctx, + GMimeMessage *reply; + notmuch_messages_t *messages; + notmuch_message_t *message; ++ notmuch_mailstore_t *mailstore; + const char *subject, *from_addr = NULL; + const char *in_reply_to, *orig_references, *references; + const notmuch_show_format_t *format = &format_reply; + ++ mailstore = notmuch_config_get_mailstore (config); + for (messages = notmuch_query_search_messages (query); + notmuch_messages_valid (messages); + notmuch_messages_move_to_next (messages)) +@@ -532,7 +541,7 @@ notmuch_reply_format_default(void *ctx, + return 1; + } + +- subject = notmuch_message_get_header (message, "subject"); ++ subject = notmuch_message_get_header (mailstore, message, "subject"); + if (subject) { + if (strncasecmp (subject, "Re:", 3)) + subject = talloc_asprintf (ctx, "Re: %s", subject); +@@ -560,7 +569,8 @@ notmuch_reply_format_default(void *ctx, + g_mime_object_set_header (GMIME_OBJECT (reply), + "In-Reply-To", in_reply_to); + +- orig_references = notmuch_message_get_header (message, "references"); ++ orig_references = notmuch_message_get_header (mailstore, ++ message, "references"); + references = talloc_asprintf (ctx, "%s%s%s", + orig_references ? orig_references : "", + orig_references ? " " : "", +@@ -574,8 +584,8 @@ notmuch_reply_format_default(void *ctx, + reply = NULL; + + printf ("On %s, %s wrote:\n", +- notmuch_message_get_header (message, "date"), +- notmuch_message_get_header (message, "from")); ++ notmuch_message_get_header (mailstore, message, "date"), ++ notmuch_message_get_header (mailstore, message, "from")); + + show_message_body (message, format, params); + +@@ -595,9 +605,12 @@ notmuch_reply_format_headers_only(void *ctx, + GMimeMessage *reply; + notmuch_messages_t *messages; + notmuch_message_t *message; ++ notmuch_mailstore_t *mailstore; + const char *in_reply_to, *orig_references, *references; + char *reply_headers; + ++ mailstore = notmuch_config_get_mailstore (config); ++ + for (messages = notmuch_query_search_messages (query); + notmuch_messages_valid (messages); + notmuch_messages_move_to_next (messages)) +@@ -618,7 +631,8 @@ notmuch_reply_format_headers_only(void *ctx, + "In-Reply-To", in_reply_to); + + +- orig_references = notmuch_message_get_header (message, "references"); ++ orig_references = notmuch_message_get_header (mailstore, message, ++ "references"); + + /* We print In-Reply-To followed by References because git format-patch treats them + * specially. Git does not interpret the other headers specially +@@ -720,7 +734,8 @@ notmuch_reply_command (void *ctx, int argc, char *argv[]) + return 1; + } + +- notmuch = notmuch_database_open (notmuch_config_get_database_path (config), ++ notmuch = notmuch_database_open (notmuch_config_get_mailstore (config), ++ notmuch_config_get_database_path (config), + NOTMUCH_DATABASE_MODE_READ_ONLY); + if (notmuch == NULL) + return 1; +diff --git a/notmuch-restore.c b/notmuch-restore.c +index 87d9772..b382b7b 100644 +--- a/notmuch-restore.c ++++ b/notmuch-restore.c +@@ -40,7 +40,8 @@ notmuch_restore_command (unused (void *ctx), int argc, char *argv[]) + if (config == NULL) + return 1; + +- notmuch = notmuch_database_open (notmuch_config_get_database_path (config), ++ notmuch = notmuch_database_open (notmuch_config_get_mailstore (config), ++ notmuch_config_get_database_path (config), + NOTMUCH_DATABASE_MODE_READ_WRITE); + if (notmuch == NULL) + return 1; +diff --git a/notmuch-search.c b/notmuch-search.c +index d504051..8ba3c48 100644 +--- a/notmuch-search.c ++++ b/notmuch-search.c +@@ -470,7 +470,8 @@ notmuch_search_command (void *ctx, int argc, char *argv[]) + if (config == NULL) + return 1; + +- notmuch = notmuch_database_open (notmuch_config_get_database_path (config), ++ notmuch = notmuch_database_open (notmuch_config_get_mailstore (config), ++ notmuch_config_get_database_path (config), + NOTMUCH_DATABASE_MODE_READ_ONLY); + if (notmuch == NULL) + return 1; +diff --git a/notmuch-show.c b/notmuch-show.c +index d930f94..81d4cf0 100644 +--- a/notmuch-show.c ++++ b/notmuch-show.c +@@ -24,7 +24,8 @@ static void + format_headers_message_part_text (GMimeMessage *message); + + static void +-format_part_text (const void *ctx, mime_node_t *node, ++format_part_text (const void *ctx, notmuch_mailstore_t *mailstore, ++ mime_node_t *node, + int indent, const notmuch_show_params_t *params); + + static const notmuch_show_format_t format_text = { +@@ -36,10 +37,12 @@ static const notmuch_show_format_t format_text = { + + static void + format_message_json (const void *ctx, ++ notmuch_mailstore_t *mailstore, + notmuch_message_t *message, + unused (int indent)); + static void + format_headers_json (const void *ctx, ++ notmuch_mailstore_t *mailstore, + notmuch_message_t *message); + + static void +@@ -83,6 +86,7 @@ static const notmuch_show_format_t format_json = { + + static void + format_message_mbox (const void *ctx, ++ notmuch_mailstore_t *mailstore, + notmuch_message_t *message, + unused (int indent)); + +@@ -149,14 +153,15 @@ _get_tags_as_string (const void *ctx, notmuch_message_t *message) + + /* Get a nice, single-line summary of message. */ + static const char * +-_get_one_line_summary (const void *ctx, notmuch_message_t *message) ++_get_one_line_summary (const void *ctx, notmuch_mailstore_t *mailstore, ++ notmuch_message_t *message) + { + const char *from; + time_t date; + const char *relative_date; + const char *tags; + +- from = notmuch_message_get_header (message, "from"); ++ from = notmuch_message_get_header (mailstore, message, "from"); + + date = notmuch_message_get_date (message); + relative_date = notmuch_time_relative_date (ctx, date); +@@ -168,7 +173,8 @@ _get_one_line_summary (const void *ctx, notmuch_message_t *message) + } + + static void +-format_message_json (const void *ctx, notmuch_message_t *message, unused (int indent)) ++format_message_json (const void *ctx, unused (notmuch_mailstore_t *mailstore), ++ notmuch_message_t *message, unused (int indent)) + { + notmuch_tags_t *tags; + int first = 1; +@@ -262,6 +268,7 @@ _is_from_line (const char *line) + */ + static void + format_message_mbox (const void *ctx, ++ notmuch_mailstore_t *mailstore, + notmuch_message_t *message, + unused (int indent)) + { +@@ -285,7 +292,7 @@ format_message_mbox (const void *ctx, + return; + } + +- from = notmuch_message_get_header (message, "from"); ++ from = notmuch_message_get_header (mailstore, message, "from"); + from = _extract_email_address (ctx, from); + + date = notmuch_message_get_date (message); +@@ -327,7 +334,7 @@ format_headers_message_part_text (GMimeMessage *message) + } + + static void +-format_headers_json (const void *ctx, notmuch_message_t *message) ++format_headers_json (const void *ctx, notmuch_mailstore_t *mailstore, notmuch_message_t *message) + { + const char *headers[] = { + "Subject", "From", "To", "Cc", "Bcc", "Date" +@@ -339,7 +346,7 @@ format_headers_json (const void *ctx, notmuch_message_t *message) + + for (i = 0; i < ARRAY_SIZE (headers); i++) { + name = headers[i]; +- value = notmuch_message_get_header (message, name); ++ value = notmuch_message_get_header (mailstore, message, name); + if (value) + { + if (!first_header) +@@ -719,7 +726,8 @@ format_part_content_raw (GMimeObject *part) + } + + static void +-format_part_text (const void *ctx, mime_node_t *node, ++format_part_text (const void *ctx, notmuch_mailstore_t *mailstore, ++ mime_node_t *node, + int indent, const notmuch_show_params_t *params) + { + /* The disposition and content-type metadata are associated with +@@ -768,7 +776,8 @@ format_part_text (const void *ctx, mime_node_t *node, + + printf ("\fheader{\n"); + if (node->envelope_file) +- printf ("%s\n", _get_one_line_summary (ctx, node->envelope_file)); ++ printf ("%s\n", _get_one_line_summary (ctx, mailstore, ++ node->envelope_file)); + printf ("Subject: %s\n", g_mime_message_get_subject (message)); + printf ("From: %s\n", g_mime_message_get_sender (message)); + recipients = g_mime_message_get_recipients (message, GMIME_RECIPIENT_TYPE_TO); +@@ -800,7 +809,8 @@ format_part_text (const void *ctx, mime_node_t *node, + } + + for (i = 0; i < node->nchildren; i++) +- format_part_text (ctx, mime_node_child (node, i), indent, params); ++ format_part_text (ctx, mailstore, mime_node_child (node, i), ++ indent, params); + + if (GMIME_IS_MESSAGE (node->part)) + printf ("\fbody}\n"); +@@ -811,6 +821,7 @@ format_part_text (const void *ctx, mime_node_t *node, + static void + show_message (void *ctx, + const notmuch_show_format_t *format, ++ notmuch_mailstore_t *mailstore, + notmuch_message_t *message, + int indent, + notmuch_show_params_t *params) +@@ -823,7 +834,7 @@ show_message (void *ctx, + &root) == NOTMUCH_STATUS_SUCCESS && + (part = mime_node_seek_dfs (root, (params->part < 0 ? + 0 : params->part)))) +- format->part (local, part, indent, params); ++ format->part (local, mailstore, part, indent, params); + talloc_free (local); + return; + } +@@ -831,11 +842,11 @@ show_message (void *ctx, + if (params->part <= 0) { + fputs (format->message_start, stdout); + if (format->message) +- format->message(ctx, message, indent); ++ format->message(ctx, mailstore, message, indent); + + fputs (format->header_start, stdout); + if (format->header) +- format->header(ctx, message); ++ format->header(ctx, mailstore, message); + fputs (format->header_end, stdout); + + fputs (format->body_start, stdout); +@@ -854,6 +865,7 @@ show_message (void *ctx, + static void + show_messages (void *ctx, + const notmuch_show_format_t *format, ++ notmuch_mailstore_t *mailstore, + notmuch_messages_t *messages, + int indent, + notmuch_show_params_t *params) +@@ -882,7 +894,7 @@ show_messages (void *ctx, + next_indent = indent; + + if (match || params->entire_thread) { +- show_message (ctx, format, message, indent, params); ++ show_message (ctx, format, mailstore, message, indent, params); + next_indent = indent + 1; + + fputs (format->message_set_sep, stdout); +@@ -890,6 +902,7 @@ show_messages (void *ctx, + + show_messages (ctx, + format, ++ mailstore, + notmuch_message_get_replies (message), + next_indent, + params); +@@ -905,6 +918,7 @@ show_messages (void *ctx, + /* Formatted output of single message */ + static int + do_show_single (void *ctx, ++ notmuch_mailstore_t *mailstore, + notmuch_query_t *query, + const notmuch_show_format_t *format, + notmuch_show_params_t *params) +@@ -966,7 +980,7 @@ do_show_single (void *ctx, + + } else { + +- show_message (ctx, format, message, 0, params); ++ show_message (ctx, format, mailstore, message, 0, params); + + } + +@@ -976,6 +990,7 @@ do_show_single (void *ctx, + /* Formatted output of threads */ + static int + do_show (void *ctx, ++ notmuch_mailstore_t *mailstore, + notmuch_query_t *query, + const notmuch_show_format_t *format, + notmuch_show_params_t *params) +@@ -1003,7 +1018,7 @@ do_show (void *ctx, + fputs (format->message_set_sep, stdout); + first_toplevel = 0; + +- show_messages (ctx, format, messages, 0, params); ++ show_messages (ctx, format, mailstore, messages, 0, params); + + notmuch_thread_destroy (thread); + +@@ -1027,6 +1042,7 @@ notmuch_show_command (void *ctx, unused (int argc), unused (char *argv[])) + { + notmuch_config_t *config; + notmuch_database_t *notmuch; ++ notmuch_mailstore_t *mailstore; + notmuch_query_t *query; + char *query_string; + int opt_index, ret; +@@ -1122,7 +1138,9 @@ notmuch_show_command (void *ctx, unused (int argc), unused (char *argv[])) + return 1; + } + +- notmuch = notmuch_database_open (notmuch_config_get_database_path (config), ++ mailstore = notmuch_config_get_mailstore (config); ++ notmuch = notmuch_database_open (mailstore, ++ notmuch_config_get_database_path (config), + NOTMUCH_DATABASE_MODE_READ_ONLY); + if (notmuch == NULL) + return 1; +@@ -1134,9 +1152,9 @@ notmuch_show_command (void *ctx, unused (int argc), unused (char *argv[])) + } + + if (params.part >= 0) +- ret = do_show_single (ctx, query, format, ¶ms); ++ ret = do_show_single (ctx, mailstore, query, format, ¶ms); + else +- ret = do_show (ctx, query, format, ¶ms); ++ ret = do_show (ctx, mailstore, query, format, ¶ms); + + notmuch_query_destroy (query); + notmuch_database_close (notmuch); +diff --git a/notmuch-tag.c b/notmuch-tag.c +index 36b9b09..5e8d74a 100644 +--- a/notmuch-tag.c ++++ b/notmuch-tag.c +@@ -187,7 +187,8 @@ notmuch_tag_command (void *ctx, int argc, char *argv[]) + if (config == NULL) + return 1; + +- notmuch = notmuch_database_open (notmuch_config_get_database_path (config), ++ notmuch = notmuch_database_open (notmuch_config_get_mailstore (config), ++ notmuch_config_get_database_path (config), + NOTMUCH_DATABASE_MODE_READ_WRITE); + if (notmuch == NULL) + return 1; +diff --git a/test/symbol-test.cc b/test/symbol-test.cc +index 1548ca4..36c6ddb 100644 +--- a/test/symbol-test.cc ++++ b/test/symbol-test.cc +@@ -4,7 +4,8 @@ + + + int main() { +- (void) notmuch_database_open("fakedb", NOTMUCH_DATABASE_MODE_READ_ONLY); ++ (void) notmuch_database_open (notmuch_mailstore_get_by_name ("maildir"), ++ "fakedb", NOTMUCH_DATABASE_MODE_READ_ONLY); + + try { + (void) new Xapian::WritableDatabase("./nonexistant", Xapian::DB_OPEN); +-- +1.7.5.4 + -- 2.26.2