* 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/ .
+ * along with this program. If not, see https://www.gnu.org/licenses/ .
*
* Author: Carl Worth <cworth@cworth.org>
*/
#include <pwd.h>
#include <netdb.h>
+#include <assert.h>
static const char toplevel_config_comment[] =
" .notmuch-config - Configuration file for the notmuch mail system\n"
"\n"
- " For more information about notmuch, see http://notmuchmail.org";
+ " For more information about notmuch, see https://notmuchmail.org";
static const char database_config_comment[] =
" Database configuration\n"
" Notmuch will store its database within a sub-directory of the path\n"
" configured here named \".notmuch\".\n";
-static const char messages_config_comment[] =
- " Messages configuration\n"
+static const char new_config_comment[] =
+ " Configuration for \"notmuch new\"\n"
"\n"
- " The only value supported here is 'new_tags' which lists the tags that\n"
- " should be applied to new messages.\n";
+ " The following options are supported here:\n"
+ "\n"
+ "\ttags A list (separated by ';') of the tags that will be\n"
+ "\t added to all messages incorporated by \"notmuch new\".\n"
+ "\n"
+ "\tignore A list (separated by ';') of file and directory names\n"
+ "\t that will not be searched for messages by \"notmuch new\".\n"
+ "\n"
+ "\t NOTE: *Every* file/directory that goes by one of those\n"
+ "\t names will be ignored, independent of its depth/location\n"
+ "\t in the mail store.\n";
static const char user_config_comment[] =
" User configuration\n"
" recipient list of replies, and will set the From address based on the\n"
" address to which the original email was addressed.\n";
+static const char maildir_config_comment[] =
+ " Maildir compatibility configuration\n"
+ "\n"
+ " The following option is supported here:\n"
+ "\n"
+ "\tsynchronize_flags Valid values are true and false.\n"
+ "\n"
+ "\tIf true, then the following maildir flags (in message filenames)\n"
+ "\twill be synchronized with the corresponding notmuch tags:\n"
+ "\n"
+ "\t\tFlag Tag\n"
+ "\t\t---- -------\n"
+ "\t\tD draft\n"
+ "\t\tF flagged\n"
+ "\t\tP passed\n"
+ "\t\tR replied\n"
+ "\t\tS unread (added when 'S' flag is not present)\n"
+ "\n"
+ "\tThe \"notmuch new\" command will notice flag changes in filenames\n"
+ "\tand update tags, while the \"notmuch tag\" and \"notmuch restore\"\n"
+ "\tcommands will notice tag changes and update flags in filenames\n";
+
+static const char search_config_comment[] =
+ " Search configuration\n"
+ "\n"
+ " The following option is supported here:\n"
+ "\n"
+ "\texclude_tags\n"
+ "\t\tA ;-separated list of tags that will be excluded from\n"
+ "\t\tsearch results by default. Using an excluded tag in a\n"
+ "\t\tquery will override that exclusion.\n";
+
+static const char crypto_config_comment[] =
+ " Cryptography related configuration\n"
+ "\n"
+ " The following option is supported here:\n"
+ "\n"
+ "\tgpg_path\n"
+ "\t\tbinary name or full path to invoke gpg.\n";
+
struct _notmuch_config {
char *filename;
GKeyFile *key_file;
+ notmuch_bool_t is_new;
char *database_path;
+ char *crypto_gpg_path;
char *user_name;
char *user_primary_email;
- char **user_other_email;
+ const char **user_other_email;
size_t user_other_email_length;
const char **new_tags;
size_t new_tags_length;
+ const char **new_ignore;
+ size_t new_ignore_length;
+ notmuch_bool_t maildir_synchronize_flags;
+ const char **search_exclude_tags;
+ size_t search_exclude_tags_length;
};
static int
static char *
get_name_from_passwd_file (void *ctx)
{
- long pw_buf_size = sysconf(_SC_GETPW_R_SIZE_MAX);
- char *pw_buf = talloc_zero_size (ctx, pw_buf_size);
+ long pw_buf_size;
+ char *pw_buf;
struct passwd passwd, *ignored;
char *name;
int e;
+ pw_buf_size = sysconf(_SC_GETPW_R_SIZE_MAX);
if (pw_buf_size == -1) pw_buf_size = 64;
+ pw_buf = talloc_size (ctx, pw_buf_size);
while ((e = getpwuid_r (getuid (), &passwd, pw_buf,
pw_buf_size, &ignored)) == ERANGE) {
static char *
get_username_from_passwd_file (void *ctx)
{
- long pw_buf_size = sysconf(_SC_GETPW_R_SIZE_MAX);
- char *pw_buf = talloc_zero_size (ctx, pw_buf_size);
+ long pw_buf_size;
+ char *pw_buf;
struct passwd passwd, *ignored;
char *name;
int e;
+ pw_buf_size = sysconf(_SC_GETPW_R_SIZE_MAX);
if (pw_buf_size == -1) pw_buf_size = 64;
+ pw_buf = talloc_zero_size (ctx, pw_buf_size);
+
while ((e = getpwuid_r (getuid (), &passwd, pw_buf,
pw_buf_size, &ignored)) == ERANGE) {
pw_buf_size = pw_buf_size * 2;
return name;
}
+static notmuch_bool_t
+get_config_from_file (notmuch_config_t *config, notmuch_bool_t create_new)
+{
+ #define BUF_SIZE 4096
+ char *config_str = NULL;
+ int config_len = 0;
+ int config_bufsize = BUF_SIZE;
+ size_t len;
+ GError *error = NULL;
+ notmuch_bool_t ret = FALSE;
+
+ FILE *fp = fopen(config->filename, "r");
+ if (fp == NULL) {
+ 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",
+ config->filename, strerror(errno));
+ }
+ goto out;
+ }
+
+ config_str = talloc_zero_array (config, char, config_bufsize);
+ if (config_str == NULL) {
+ fprintf (stderr, "Error reading '%s': Out of memory\n", config->filename);
+ goto out;
+ }
+
+ while ((len = fread (config_str + config_len, 1,
+ config_bufsize - config_len, fp)) > 0) {
+ config_len += len;
+ if (config_len == config_bufsize) {
+ config_bufsize += BUF_SIZE;
+ config_str = talloc_realloc (config, config_str, char, config_bufsize);
+ if (config_str == NULL) {
+ fprintf (stderr, "Error reading '%s': Failed to reallocate memory\n",
+ config->filename);
+ goto out;
+ }
+ }
+ }
+
+ if (ferror (fp)) {
+ fprintf (stderr, "Error reading '%s': I/O error\n", config->filename);
+ goto out;
+ }
+
+ if (g_key_file_load_from_data (config->key_file, config_str, config_len,
+ G_KEY_FILE_KEEP_COMMENTS, &error)) {
+ ret = TRUE;
+ goto out;
+ }
+
+ fprintf (stderr, "Error parsing config file '%s': %s\n",
+ config->filename, error->message);
+
+ g_error_free (error);
+
+out:
+ if (fp)
+ fclose(fp);
+
+ if (config_str)
+ talloc_free(config_str);
+
+ return ret;
+}
+
/* Open the named notmuch configuration file. If the filename is NULL,
* the value of the environment variable $NOTMUCH_CONFIG will be used.
* If $NOTMUCH_CONFIG is unset, the default configuration file
* etc.), this function will print a message to stderr and return
* NULL.
*
- * Note: It is *not* an error if the specified configuration file does
- * not exist. In this case, a default configuration will be created
- * and returned. Subsequently calling notmuch_config_save will cause
- * the configuration to be written to the filename specified at the
- * time of notmuch_config_open.
+ * FILE NOT FOUND: When the specified configuration file (whether from
+ * 'filename' or the $NOTMUCH_CONFIG environment variable) does not
+ * exist, the behavior of this function depends on the 'is_new_ret'
+ * variable.
+ *
+ * If is_new_ret is NULL, then a "file not found" message will be
+ * printed to stderr and NULL will be returned.
+
+ * If is_new_ret is non-NULL then a default configuration will be
+ * returned and *is_new_ret will be set to 1 on return so that
+ * the caller can recognize this case.
*
- * The default configuration settings are determined as follows:
+ * These default configuration settings are determined as
+ * follows:
*
- * database_path: $HOME/mail
+ * database_path: $MAILDIR, otherwise $HOME/mail
*
- * user_name: From /etc/passwd
+ * user_name: $NAME variable if set, otherwise
+ * read from /etc/passwd
*
- * user_primary_mail: $EMAIL variable if set, otherwise
- * constructed from the username and
- * hostname of the current machine.
+ * user_primary_mail: $EMAIL variable if set, otherwise
+ * constructed from the username and
+ * hostname of the current machine.
*
- * user_other_email: Not set.
+ * user_other_email: Not set.
*
- * The default configuration also contains comments to guide the user
- * in editing the file directly.
+ * The default configuration also contains comments to guide the
+ * user in editing the file directly.
*/
notmuch_config_t *
notmuch_config_open (void *ctx,
const char *filename,
- notmuch_bool_t *is_new_ret)
+ notmuch_config_mode_t config_mode)
{
GError *error = NULL;
- int is_new = 0;
size_t tmp;
char *notmuch_config_env = NULL;
int file_had_database_group;
- int file_had_messages_group;
+ int file_had_new_group;
int file_had_user_group;
+ int file_had_maildir_group;
+ int file_had_search_group;
+ int file_had_crypto_group;
- if (is_new_ret)
- *is_new_ret = 0;
-
- notmuch_config_t *config = talloc (ctx, notmuch_config_t);
+ notmuch_config_t *config = talloc_zero (ctx, notmuch_config_t);
if (config == NULL) {
fprintf (stderr, "Out of memory.\n");
return NULL;
talloc_set_destructor (config, notmuch_config_destructor);
+ /* non-zero defaults */
+ config->maildir_synchronize_flags = TRUE;
+
if (filename) {
config->filename = talloc_strdup (config, filename);
} else if ((notmuch_config_env = getenv ("NOTMUCH_CONFIG"))) {
config->key_file = g_key_file_new ();
- config->database_path = NULL;
- config->user_name = NULL;
- config->user_primary_email = NULL;
- config->user_other_email = NULL;
- config->user_other_email_length = 0;
- config->new_tags = NULL;
- config->new_tags_length = 0;
-
- if (! g_key_file_load_from_file (config->key_file,
- config->filename,
- G_KEY_FILE_KEEP_COMMENTS,
- &error))
- {
- /* We are capable of dealing with a non-existent configuration
- * file, so be silent about that (unless the user had set a
- * non-default configuration file with the NOTMUCH_CONFIG
- * variable)
- */
- if (notmuch_config_env ||
- !(error->domain == G_FILE_ERROR &&
- error->code == G_FILE_ERROR_NOENT))
- {
- fprintf (stderr, "Error reading configuration file %s: %s\n",
- config->filename, error->message);
+ if (config_mode & NOTMUCH_CONFIG_OPEN) {
+ notmuch_bool_t create_new = (config_mode & NOTMUCH_CONFIG_CREATE) != 0;
+
+ if (! get_config_from_file (config, create_new)) {
talloc_free (config);
- g_error_free (error);
return NULL;
}
-
- g_error_free (error);
- is_new = 1;
}
/* Whenever we know of configuration sections that don't appear in
*/
file_had_database_group = g_key_file_has_group (config->key_file,
"database");
- file_had_messages_group = g_key_file_has_group (config->key_file,
- "messages");
+ file_had_new_group = g_key_file_has_group (config->key_file, "new");
file_had_user_group = g_key_file_has_group (config->key_file, "user");
-
+ file_had_maildir_group = g_key_file_has_group (config->key_file, "maildir");
+ file_had_search_group = g_key_file_has_group (config->key_file, "search");
+ file_had_crypto_group = g_key_file_has_group (config->key_file, "crypto");
if (notmuch_config_get_database_path (config) == NULL) {
- char *path = talloc_asprintf (config, "%s/mail",
- getenv ("HOME"));
+ char *path = getenv ("MAILDIR");
+ if (path)
+ path = talloc_strdup (config, path);
+ else
+ path = talloc_asprintf (config, "%s/mail",
+ getenv ("HOME"));
notmuch_config_set_database_path (config, path);
talloc_free (path);
}
if (notmuch_config_get_user_name (config) == NULL) {
- char *name = get_name_from_passwd_file (config);
+ char *name = getenv ("NAME");
+ if (name)
+ name = talloc_strdup (config, name);
+ else
+ name = get_name_from_passwd_file (config);
notmuch_config_set_user_name (config, name);
talloc_free (name);
}
notmuch_config_set_new_tags (config, tags, 2);
}
+ if (notmuch_config_get_new_ignore (config, &tmp) == NULL) {
+ notmuch_config_set_new_ignore (config, NULL, 0);
+ }
+
+ if (notmuch_config_get_search_exclude_tags (config, &tmp) == NULL) {
+ if (config->is_new) {
+ const char *tags[] = { "deleted", "spam" };
+ notmuch_config_set_search_exclude_tags (config, tags, 2);
+ } else {
+ notmuch_config_set_search_exclude_tags (config, NULL, 0);
+ }
+ }
+
+ error = NULL;
+ config->maildir_synchronize_flags =
+ g_key_file_get_boolean (config->key_file,
+ "maildir", "synchronize_flags", &error);
+ if (error) {
+ 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. */
- if (is_new)
- {
+ if (config->is_new)
g_key_file_set_comment (config->key_file, NULL, NULL,
toplevel_config_comment, NULL);
- }
if (! file_had_database_group)
- {
g_key_file_set_comment (config->key_file, "database", NULL,
database_config_comment, NULL);
- }
- if (! file_had_messages_group)
- {
- g_key_file_set_comment (config->key_file, "messages", NULL,
- messages_config_comment, NULL);
- }
+ if (! file_had_new_group)
+ g_key_file_set_comment (config->key_file, "new", NULL,
+ new_config_comment, NULL);
if (! file_had_user_group)
- {
g_key_file_set_comment (config->key_file, "user", NULL,
user_config_comment, NULL);
- }
- if (is_new_ret)
- *is_new_ret = is_new;
+ if (! file_had_maildir_group)
+ g_key_file_set_comment (config->key_file, "maildir", NULL,
+ maildir_config_comment, NULL);
+
+ if (! file_had_search_group)
+ g_key_file_set_comment (config->key_file, "search", NULL,
+ search_config_comment, NULL);
+
+ if (! file_had_crypto_group)
+ g_key_file_set_comment (config->key_file, "crypto", NULL,
+ crypto_config_comment, NULL);
return config;
}
notmuch_config_save (notmuch_config_t *config)
{
size_t length;
- char *data;
+ char *data, *filename;
GError *error = NULL;
data = g_key_file_to_data (config->key_file, &length, NULL);
return 1;
}
- if (! g_file_set_contents (config->filename, data, length, &error)) {
- fprintf (stderr, "Error saving configuration to %s: %s\n",
- config->filename, error->message);
+ /* Try not to overwrite symlinks. */
+ filename = canonicalize_file_name (config->filename);
+ if (! filename) {
+ if (errno == ENOENT) {
+ filename = strdup (config->filename);
+ if (! filename) {
+ fprintf (stderr, "Out of memory.\n");
+ g_free (data);
+ return 1;
+ }
+ } else {
+ fprintf (stderr, "Error canonicalizing %s: %s\n", config->filename,
+ strerror (errno));
+ g_free (data);
+ return 1;
+ }
+ }
+
+ if (! g_file_set_contents (filename, data, length, &error)) {
+ if (strcmp (filename, config->filename) != 0) {
+ fprintf (stderr, "Error saving configuration to %s (-> %s): %s\n",
+ config->filename, filename, error->message);
+ } else {
+ fprintf (stderr, "Error saving configuration to %s: %s\n",
+ filename, error->message);
+ }
g_error_free (error);
+ free (filename);
g_free (data);
return 1;
}
+ free (filename);
g_free (data);
return 0;
}
-const char *
-notmuch_config_get_database_path (notmuch_config_t *config)
+notmuch_bool_t
+notmuch_config_is_new (notmuch_config_t *config)
+{
+ return config->is_new;
+}
+
+static const char *
+_config_get (notmuch_config_t *config, char **field,
+ const char *group, const char *key)
+{
+ /* read from config file and cache value, if not cached already */
+ if (*field == NULL) {
+ char *value;
+ value = g_key_file_get_string (config->key_file, group, key, NULL);
+ if (value) {
+ *field = talloc_strdup (config, value);
+ free (value);
+ }
+ }
+ return *field;
+}
+
+static void
+_config_set (notmuch_config_t *config, char **field,
+ const char *group, const char *key, const char *value)
{
- char *path;
+ g_key_file_set_string (config->key_file, group, key, value);
- if (config->database_path == NULL) {
- path = g_key_file_get_string (config->key_file,
- "database", "path", NULL);
- if (path) {
- config->database_path = talloc_strdup (config, path);
- free (path);
+ /* drop the cached value */
+ talloc_free (*field);
+ *field = NULL;
+}
+
+static const char **
+_config_get_list (notmuch_config_t *config,
+ const char *section, const char *key,
+ const char ***outlist, size_t *list_length, size_t *ret_length)
+{
+ assert(outlist);
+
+ /* read from config file and cache value, if not cached already */
+ if (*outlist == NULL) {
+
+ char **inlist = g_key_file_get_string_list (config->key_file,
+ section, key, list_length, NULL);
+ if (inlist) {
+ unsigned int i;
+
+ *outlist = talloc_size (config, sizeof (char *) * (*list_length + 1));
+
+ for (i = 0; i < *list_length; i++)
+ (*outlist)[i] = talloc_strdup (*outlist, inlist[i]);
+
+ (*outlist)[i] = NULL;
+
+ g_strfreev (inlist);
}
}
- return config->database_path;
+ if (ret_length)
+ *ret_length = *list_length;
+
+ return *outlist;
+}
+
+static void
+_config_set_list (notmuch_config_t *config,
+ const char *group, const char *key,
+ const char *list[],
+ size_t length, const char ***config_var )
+{
+ g_key_file_set_string_list (config->key_file, group, key, list, length);
+
+ /* drop the cached value */
+ talloc_free (*config_var);
+ *config_var = NULL;
+}
+
+const char *
+notmuch_config_get_database_path (notmuch_config_t *config)
+{
+ return _config_get (config, &config->database_path, "database", "path");
}
void
notmuch_config_set_database_path (notmuch_config_t *config,
const char *database_path)
{
- g_key_file_set_string (config->key_file,
- "database", "path", database_path);
-
- talloc_free (config->database_path);
- config->database_path = NULL;
+ _config_set (config, &config->database_path, "database", "path", database_path);
}
const char *
notmuch_config_get_user_name (notmuch_config_t *config)
{
- char *name;
-
- if (config->user_name == NULL) {
- name = g_key_file_get_string (config->key_file,
- "user", "name", NULL);
- if (name) {
- config->user_name = talloc_strdup (config, name);
- free (name);
- }
- }
-
- return config->user_name;
+ return _config_get (config, &config->user_name, "user", "name");
}
void
notmuch_config_set_user_name (notmuch_config_t *config,
const char *user_name)
{
- g_key_file_set_string (config->key_file,
- "user", "name", user_name);
-
- talloc_free (config->user_name);
- config->user_name = NULL;
+ _config_set (config, &config->user_name, "user", "name", user_name);
}
const char *
notmuch_config_get_user_primary_email (notmuch_config_t *config)
{
- char *email;
-
- if (config->user_primary_email == NULL) {
- email = g_key_file_get_string (config->key_file,
- "user", "primary_email", NULL);
- if (email) {
- config->user_primary_email = talloc_strdup (config, email);
- free (email);
- }
- }
-
- return config->user_primary_email;
+ return _config_get (config, &config->user_primary_email, "user", "primary_email");
}
void
notmuch_config_set_user_primary_email (notmuch_config_t *config,
const char *primary_email)
{
- g_key_file_set_string (config->key_file,
- "user", "primary_email", primary_email);
+ _config_set (config, &config->user_primary_email, "user", "primary_email", primary_email);
+}
- talloc_free (config->user_primary_email);
- config->user_primary_email = NULL;
+const char **
+notmuch_config_get_user_other_email (notmuch_config_t *config, size_t *length)
+{
+ return _config_get_list (config, "user", "other_email",
+ &(config->user_other_email),
+ &(config->user_other_email_length), length);
}
-char **
-notmuch_config_get_user_other_email (notmuch_config_t *config,
- size_t *length)
+const char **
+notmuch_config_get_new_tags (notmuch_config_t *config, size_t *length)
{
- char **emails;
- size_t emails_length;
- unsigned int i;
+ return _config_get_list (config, "new", "tags",
+ &(config->new_tags),
+ &(config->new_tags_length), length);
+}
- if (config->user_other_email == NULL) {
- emails = g_key_file_get_string_list (config->key_file,
- "user", "other_email",
- &emails_length, NULL);
- if (emails) {
- config->user_other_email = talloc_size (config,
- sizeof (char *) *
- (emails_length + 1));
- for (i = 0; i < emails_length; i++)
- config->user_other_email[i] = talloc_strdup (config->user_other_email,
- emails[i]);
- config->user_other_email[i] = NULL;
+const char **
+notmuch_config_get_new_ignore (notmuch_config_t *config, size_t *length)
+{
+ return _config_get_list (config, "new", "ignore",
+ &(config->new_ignore),
+ &(config->new_ignore_length), length);
+}
- g_strfreev (emails);
+void
+notmuch_config_set_user_other_email (notmuch_config_t *config,
+ const char *list[],
+ size_t length)
+{
+ _config_set_list (config, "user", "other_email", list, length,
+ &(config->user_other_email));
+}
- config->user_other_email_length = emails_length;
+void
+notmuch_config_set_new_tags (notmuch_config_t *config,
+ const char *list[],
+ size_t length)
+{
+ _config_set_list (config, "new", "tags", list, length,
+ &(config->new_tags));
+}
+
+void
+notmuch_config_set_new_ignore (notmuch_config_t *config,
+ const char *list[],
+ size_t length)
+{
+ _config_set_list (config, "new", "ignore", list, length,
+ &(config->new_ignore));
+}
+
+const char **
+notmuch_config_get_search_exclude_tags (notmuch_config_t *config, size_t *length)
+{
+ return _config_get_list (config, "search", "exclude_tags",
+ &(config->search_exclude_tags),
+ &(config->search_exclude_tags_length), length);
+}
+
+void
+notmuch_config_set_search_exclude_tags (notmuch_config_t *config,
+ const char *list[],
+ size_t length)
+{
+ _config_set_list (config, "search", "exclude_tags", list, length,
+ &(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
+ * stderr and return 1. Otherwise, return 0.
+ *
+ * Note: This function modifies the original 'item' string.
+ */
+static int
+_item_split (char *item, char **group, char **key)
+{
+ char *period;
+
+ *group = item;
+
+ period = strchr (item, '.');
+ if (period == NULL || *(period+1) == '\0') {
+ fprintf (stderr,
+ "Invalid configuration name: %s\n"
+ "(Should be of the form <section>.<item>)\n", item);
+ return 1;
+ }
+
+ *period = '\0';
+ *key = period + 1;
+
+ return 0;
+}
+
+#define BUILT_WITH_PREFIX "built_with."
+#define QUERY_PREFIX "query."
+
+static int
+_print_db_config(notmuch_config_t *config, const char *name)
+{
+ notmuch_database_t *notmuch;
+ char *val;
+
+ if (notmuch_database_open (notmuch_config_get_database_path (config),
+ NOTMUCH_DATABASE_MODE_READ_ONLY, ¬much))
+ return EXIT_FAILURE;
+
+ /* XXX Handle UUID mismatch? */
+
+ if (print_status_database ("notmuch config", notmuch,
+ notmuch_database_get_config (notmuch, name, &val)))
+ return EXIT_FAILURE;
+
+ puts (val);
+
+ return EXIT_SUCCESS;
+}
+
+static int
+notmuch_config_command_get (notmuch_config_t *config, char *item)
+{
+ if (strcmp(item, "database.path") == 0) {
+ printf ("%s\n", notmuch_config_get_database_path (config));
+ } else if (strcmp(item, "user.name") == 0) {
+ printf ("%s\n", notmuch_config_get_user_name (config));
+ } else if (strcmp(item, "user.primary_email") == 0) {
+ printf ("%s\n", notmuch_config_get_user_primary_email (config));
+ } else if (strcmp(item, "user.other_email") == 0) {
+ const char **other_email;
+ size_t i, length;
+
+ other_email = notmuch_config_get_user_other_email (config, &length);
+ for (i = 0; i < length; i++)
+ printf ("%s\n", other_email[i]);
+ } else if (strcmp(item, "new.tags") == 0) {
+ const char **tags;
+ size_t i, length;
+
+ tags = notmuch_config_get_new_tags (config, &length);
+ for (i = 0; i < length; i++)
+ printf ("%s\n", tags[i]);
+ } 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) {
+ return _print_db_config (config, item);
+ } else {
+ char **value;
+ size_t i, length;
+ char *group, *key;
+
+ if (_item_split (item, &group, &key))
+ return 1;
+
+ value = g_key_file_get_string_list (config->key_file,
+ group, key,
+ &length, NULL);
+ if (value == NULL) {
+ fprintf (stderr, "Unknown configuration item: %s.%s\n",
+ group, key);
+ return 1;
}
+
+ for (i = 0; i < length; i++)
+ printf ("%s\n", value[i]);
+
+ g_strfreev (value);
+ }
+
+ return 0;
+}
+
+static int
+_set_db_config(notmuch_config_t *config, const char *key, int argc, char **argv)
+{
+ notmuch_database_t *notmuch;
+ const char *val = "";
+
+ if (argc > 1) {
+ /* XXX handle lists? */
+ fprintf (stderr, "notmuch config set: at most one value expected for %s\n", key);
+ return EXIT_FAILURE;
+ }
+
+ if (argc > 0) {
+ val = argv[0];
+ }
+
+ if (notmuch_database_open (notmuch_config_get_database_path (config),
+ NOTMUCH_DATABASE_MODE_READ_WRITE, ¬much))
+ return EXIT_FAILURE;
+
+ /* XXX Handle UUID mismatch? */
+
+ if (print_status_database ("notmuch config", notmuch,
+ notmuch_database_set_config (notmuch, key, val)))
+ return EXIT_FAILURE;
+
+ if (print_status_database ("notmuch config", notmuch,
+ notmuch_database_close (notmuch)))
+ return EXIT_FAILURE;
+
+ return EXIT_SUCCESS;
+}
+
+static int
+notmuch_config_command_set (notmuch_config_t *config, char *item, int argc, char *argv[])
+{
+ char *group, *key;
+
+ 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) {
+ return _set_db_config (config, item, argc, argv);
+ }
+
+ if (_item_split (item, &group, &key))
+ return 1;
+
+ /* With only the name of an item, we clear it from the
+ * configuration file.
+ *
+ * With a single value, we set it as a string.
+ *
+ * With multiple values, we set them as a string list.
+ */
+ switch (argc) {
+ case 0:
+ g_key_file_remove_key (config->key_file, group, key, NULL);
+ break;
+ case 1:
+ g_key_file_set_string (config->key_file, group, key, argv[0]);
+ break;
+ default:
+ g_key_file_set_string_list (config->key_file, group, key,
+ (const gchar **) argv, argc);
+ break;
}
- *length = config->user_other_email_length;
- return config->user_other_email;
+ return notmuch_config_save (config);
}
+static
void
-notmuch_config_set_user_other_email (notmuch_config_t *config,
- const char *other_email[],
- size_t length)
+_notmuch_config_list_built_with ()
+{
+ printf("%scompact=%s\n",
+ BUILT_WITH_PREFIX,
+ notmuch_built_with ("compact") ? "true" : "false");
+ printf("%sfield_processor=%s\n",
+ BUILT_WITH_PREFIX,
+ notmuch_built_with ("field_processor") ? "true" : "false");
+ printf("%sretry_lock=%s\n",
+ BUILT_WITH_PREFIX,
+ notmuch_built_with ("retry_lock") ? "true" : "false");
+}
+
+static int
+_list_db_config (notmuch_config_t *config)
{
- g_key_file_set_string_list (config->key_file,
- "user", "other_email",
- other_email, length);
+ notmuch_database_t *notmuch;
+ notmuch_config_list_t *list;
+
+ if (notmuch_database_open (notmuch_config_get_database_path (config),
+ NOTMUCH_DATABASE_MODE_READ_ONLY, ¬much))
+ return EXIT_FAILURE;
+
+ /* XXX Handle UUID mismatch? */
+
+
+ if (print_status_database ("notmuch config", notmuch,
+ notmuch_database_get_config_list (notmuch, "", &list)))
+ return EXIT_FAILURE;
+
+ for (; notmuch_config_list_valid (list); notmuch_config_list_move_to_next (list)) {
+ printf("%s=%s\n", notmuch_config_list_key (list), notmuch_config_list_value(list));
+ }
+ notmuch_config_list_destroy (list);
- talloc_free (config->user_other_email);
- config->user_other_email = NULL;
+ return EXIT_SUCCESS;
}
-const char **
-notmuch_config_get_new_tags (notmuch_config_t *config,
- size_t *length)
-{
- char **tags;
- size_t tags_length;
- unsigned int i;
-
- if (config->new_tags == NULL) {
- tags = g_key_file_get_string_list (config->key_file,
- "messages", "new_tags",
- &tags_length, NULL);
- if (tags) {
- config->new_tags = talloc_size (config,
- sizeof (char *) *
- (tags_length + 1));
- for (i = 0; i < tags_length; i++)
- config->new_tags[i] = talloc_strdup (config->new_tags,
- tags[i]);
- config->new_tags[i] = NULL;
-
- g_strfreev (tags);
-
- config->new_tags_length = tags_length;
+static int
+notmuch_config_command_list (notmuch_config_t *config)
+{
+ char **groups;
+ size_t g, groups_length;
+
+ groups = g_key_file_get_groups (config->key_file, &groups_length);
+ if (groups == NULL)
+ return 1;
+
+ for (g = 0; g < groups_length; g++) {
+ char **keys;
+ size_t k, keys_length;
+
+ keys = g_key_file_get_keys (config->key_file,
+ groups[g], &keys_length, NULL);
+ if (keys == NULL)
+ continue;
+
+ for (k = 0; k < keys_length; k++) {
+ char *value;
+
+ value = g_key_file_get_string (config->key_file,
+ groups[g], keys[k], NULL);
+ if (value != NULL) {
+ printf ("%s.%s=%s\n", groups[g], keys[k], value);
+ free (value);
+ }
}
+
+ g_strfreev (keys);
}
- *length = config->new_tags_length;
- return config->new_tags;
+ g_strfreev (groups);
+
+ _notmuch_config_list_built_with ();
+ return _list_db_config (config);
}
-void
-notmuch_config_set_new_tags (notmuch_config_t *config,
- const char *new_tags[],
- size_t length)
+int
+notmuch_config_command (notmuch_config_t *config, int argc, char *argv[])
{
- g_key_file_set_string_list (config->key_file,
- "messages", "new_tags",
- new_tags, length);
+ int ret;
+ int opt_index;
+
+ opt_index = notmuch_minimal_options ("config", argc, argv);
+ if (opt_index < 0)
+ return EXIT_FAILURE;
+
+ if (notmuch_requested_db_uuid)
+ fprintf (stderr, "Warning: ignoring --uuid=%s\n",
+ notmuch_requested_db_uuid);
- talloc_free (config->new_tags);
- config->new_tags = NULL;
+ /* skip at least subcommand argument */
+ argc-= opt_index;
+ argv+= opt_index;
+
+ if (argc < 1) {
+ fprintf (stderr, "Error: notmuch config requires at least one argument.\n");
+ return EXIT_FAILURE;
+ }
+
+ if (strcmp (argv[0], "get") == 0) {
+ if (argc != 2) {
+ fprintf (stderr, "Error: notmuch config get requires exactly "
+ "one argument.\n");
+ return EXIT_FAILURE;
+ }
+ ret = notmuch_config_command_get (config, argv[1]);
+ } else if (strcmp (argv[0], "set") == 0) {
+ if (argc < 2) {
+ fprintf (stderr, "Error: notmuch config set requires at least "
+ "one argument.\n");
+ return EXIT_FAILURE;
+ }
+ ret = notmuch_config_command_set (config, argv[1], argc - 2, argv + 2);
+ } else if (strcmp (argv[0], "list") == 0) {
+ ret = notmuch_config_command_list (config);
+ } else {
+ fprintf (stderr, "Unrecognized argument for notmuch config: %s\n",
+ argv[0]);
+ return EXIT_FAILURE;
+ }
+
+ return ret ? EXIT_FAILURE : EXIT_SUCCESS;
+
+}
+
+notmuch_bool_t
+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)
+{
+ g_key_file_set_boolean (config->key_file,
+ "maildir", "synchronize_flags", synchronize_flags);
+ config->maildir_synchronize_flags = synchronize_flags;
+}