#include <netdb.h>
#include <assert.h>
+#include "unicode-util.h"
+
static const char toplevel_config_comment[] =
" .notmuch-config - Configuration file for the notmuch mail system\n"
"\n"
static const char crypto_config_comment[] =
" Cryptography related configuration\n"
"\n"
- " The following option is supported here:\n"
+ " The following old option is now ignored:\n"
"\n"
- "\tgpg_path\n"
- "\t\tbinary name or full path to invoke gpg.\n";
+ "\tgpgpath\n"
+ "\t\tThis option was used by older builds of notmuch to choose\n"
+ "\t\tthe version of gpg to use.\n"
+ "\t\tSetting $PATH is a better approach.\n";
struct _notmuch_config {
char *filename;
GKeyFile *key_file;
- notmuch_bool_t is_new;
+ bool is_new;
char *database_path;
char *crypto_gpg_path;
size_t new_tags_length;
const char **new_ignore;
size_t new_ignore_length;
- notmuch_bool_t maildir_synchronize_flags;
+ bool maildir_synchronize_flags;
const char **search_exclude_tags;
size_t search_exclude_tags_length;
};
return name;
}
-static notmuch_bool_t
-get_config_from_file (notmuch_config_t *config, notmuch_bool_t create_new)
+static bool
+get_config_from_file (notmuch_config_t *config, bool create_new)
{
#define BUF_SIZE 4096
char *config_str = NULL;
int config_bufsize = BUF_SIZE;
size_t len;
GError *error = NULL;
- notmuch_bool_t ret = FALSE;
+ bool ret = false;
FILE *fp = fopen(config->filename, "r");
if (fp == NULL) {
- /* If create_new is true, then the caller is prepared for a
- * default configuration file in the case of FILE NOT FOUND.
- */
- if (create_new) {
- config->is_new = TRUE;
- ret = TRUE;
- goto out;
- } else if (errno == ENOENT) {
- fprintf (stderr, "Configuration file %s not found.\n"
- "Try running 'notmuch setup' to create a configuration.\n",
- config->filename);
- goto out;
+ if (errno == ENOENT) {
+ /* If create_new is true, then the caller is prepared for a
+ * default configuration file in the case of FILE NOT FOUND.
+ */
+ if (create_new) {
+ config->is_new = true;
+ ret = true;
+ } else {
+ fprintf (stderr, "Configuration file %s not found.\n"
+ "Try running 'notmuch setup' to create a configuration.\n",
+ config->filename);
+ }
} else {
- fprintf (stderr, "Error opening config file '%s': %s\n"
- "Try running 'notmuch setup' to create a configuration.\n",
+ fprintf (stderr, "Error opening config file '%s': %s\n",
config->filename, strerror(errno));
- goto out;
}
+ goto out;
}
config_str = talloc_zero_array (config, char, config_bufsize);
if (g_key_file_load_from_data (config->key_file, config_str, config_len,
G_KEY_FILE_KEEP_COMMENTS, &error)) {
- ret = TRUE;
+ ret = true;
goto out;
}
notmuch_config_t *
notmuch_config_open (void *ctx,
const char *filename,
- notmuch_bool_t create_new)
+ notmuch_config_mode_t config_mode)
{
GError *error = NULL;
size_t tmp;
talloc_set_destructor (config, notmuch_config_destructor);
/* non-zero defaults */
- config->maildir_synchronize_flags = TRUE;
+ config->maildir_synchronize_flags = true;
if (filename) {
config->filename = talloc_strdup (config, filename);
config->key_file = g_key_file_new ();
- if (! get_config_from_file (config, create_new)) {
- talloc_free (config);
- return NULL;
+ if (config_mode & NOTMUCH_CONFIG_OPEN) {
+ bool create_new = (config_mode & NOTMUCH_CONFIG_CREATE) != 0;
+
+ if (! get_config_from_file (config, create_new)) {
+ talloc_free (config);
+ return NULL;
+ }
}
/* Whenever we know of configuration sections that don't appear in
g_key_file_get_boolean (config->key_file,
"maildir", "synchronize_flags", &error);
if (error) {
- notmuch_config_set_maildir_synchronize_flags (config, TRUE);
+ notmuch_config_set_maildir_synchronize_flags (config, true);
g_error_free (error);
}
- if (notmuch_config_get_crypto_gpg_path (config) == NULL) {
- notmuch_config_set_crypto_gpg_path (config, "gpg");
- }
-
/* Whenever we know of configuration sections that don't appear in
* the configuration file, we add some comments to help the user
* understand what can be done. */
return 0;
}
-notmuch_bool_t
+bool
notmuch_config_is_new (notmuch_config_t *config)
{
return config->is_new;
const char *
notmuch_config_get_database_path (notmuch_config_t *config)
{
- return _config_get (config, &config->database_path, "database", "path");
+ char *db_path = (char *)_config_get (config, &config->database_path, "database", "path");
+
+ if (db_path && *db_path != '/') {
+ /* If the path in the configuration file begins with any
+ * character other than /, presume that it is relative to
+ * $HOME and update as appropriate.
+ */
+ char *abs_path = talloc_asprintf (config, "%s/%s", getenv ("HOME"), db_path);
+ talloc_free (db_path);
+ db_path = config->database_path = abs_path;
+ }
+
+ return db_path;
}
void
&(config->search_exclude_tags));
}
-const char *
-notmuch_config_get_crypto_gpg_path (notmuch_config_t *config)
-{
- return _config_get (config, &config->crypto_gpg_path, "crypto", "gpg_path");
-}
-
-void
-notmuch_config_set_crypto_gpg_path (notmuch_config_t *config,
- const char *gpg_path)
-{
- _config_set (config, &config->crypto_gpg_path, "crypto", "gpg_path", gpg_path);
-}
-
/* Given a configuration item of the form <group>.<key> return the
* component group and key. If any error occurs, print a message on
return 0;
}
+/* These are more properly called Xapian fields, but the user facing
+ docs call them prefixes, so make the error message match */
+static bool
+validate_field_name (const char *str)
+{
+ const char *key;
+
+ if (! g_utf8_validate (str, -1, NULL)) {
+ fprintf (stderr, "Invalid utf8: %s\n", str);
+ return false;
+ }
+
+ key = g_utf8_strrchr (str, -1, '.');
+ if (! key ) {
+ INTERNAL_ERROR ("Impossible code path on input: %s\n", str);
+ }
+
+ key++;
+
+ if (! *key) {
+ fprintf (stderr, "Empty prefix name: %s\n", str);
+ return false;
+ }
+
+ if (! unicode_word_utf8 (key)) {
+ fprintf (stderr, "Non-word character in prefix name: %s\n", key);
+ return false;
+ }
+
+ if (key[0] >= 'a' && key[0] <= 'z') {
+ fprintf (stderr, "Prefix names starting with lower case letters are reserved: %s\n", key);
+ return false;
+ }
+
+ return true;
+}
+
#define BUILT_WITH_PREFIX "built_with."
-#define QUERY_PREFIX "query."
+
+typedef struct config_key {
+ const char *name;
+ bool in_db;
+ bool prefix;
+ bool (*validate)(const char *);
+} config_key_info_t;
+
+static struct config_key
+config_key_table[] = {
+ {"index.decrypt", true, false, NULL},
+ {"index.header.", true, true, validate_field_name},
+ {"query.", true, true, NULL},
+};
+
+static config_key_info_t *
+_config_key_info (const char *item)
+{
+ for (size_t i = 0; i < ARRAY_SIZE (config_key_table); i++) {
+ if (config_key_table[i].prefix &&
+ strncmp (item, config_key_table[i].name,
+ strlen(config_key_table[i].name)) == 0)
+ return config_key_table+i;
+ if (strcmp (item, config_key_table[i].name) == 0)
+ return config_key_table+i;
+ }
+ return NULL;
+}
+
+static bool
+_stored_in_db (const char *item)
+{
+ config_key_info_t *info;
+ info = _config_key_info (item);
+
+ return (info && info->in_db);
+}
static int
_print_db_config(notmuch_config_t *config, const char *name)
} else if (STRNCMP_LITERAL (item, BUILT_WITH_PREFIX) == 0) {
printf ("%s\n",
notmuch_built_with (item + strlen (BUILT_WITH_PREFIX)) ? "true" : "false");
- } else if (STRNCMP_LITERAL (item, QUERY_PREFIX) == 0) {
+ } else if (_stored_in_db (item)) {
return _print_db_config (config, item);
} else {
char **value;
notmuch_config_command_set (notmuch_config_t *config, char *item, int argc, char *argv[])
{
char *group, *key;
+ config_key_info_t *key_info;
if (STRNCMP_LITERAL (item, BUILT_WITH_PREFIX) == 0) {
fprintf (stderr, "Error: read only option: %s\n", item);
return 1;
}
- if (STRNCMP_LITERAL (item, QUERY_PREFIX) == 0) {
+ key_info = _config_key_info (item);
+ if (key_info && key_info->validate && (! key_info->validate (item)))
+ return 1;
+
+ if (key_info && key_info->in_db) {
return _set_db_config (config, item, argc, argv);
}
}
-notmuch_bool_t
+bool
notmuch_config_get_maildir_synchronize_flags (notmuch_config_t *config)
{
return config->maildir_synchronize_flags;
void
notmuch_config_set_maildir_synchronize_flags (notmuch_config_t *config,
- notmuch_bool_t synchronize_flags)
+ bool synchronize_flags)
{
g_key_file_set_boolean (config->key_file,
"maildir", "synchronize_flags", synchronize_flags);