1 /* config.cc - API for database metadata
3 * Copyright © 2016 David Bremner
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see https://www.gnu.org/licenses/ .
18 * Author: David Bremner <david@tethera.net>
22 #include "notmuch-private.h"
23 #include "database-private.h"
25 static const std::string CONFIG_PREFIX = "C";
27 struct _notmuch_config_list {
28 notmuch_database_t *notmuch;
29 Xapian::TermIterator iterator;
34 struct _notmuch_config_values {
38 void *children; /* talloc_context */
41 struct _notmuch_config_pairs {
42 notmuch_string_map_iterator_t *iter;
45 static const char *_notmuch_config_key_to_string (notmuch_config_key_t key);
48 _notmuch_config_list_destroy (notmuch_config_list_t *list)
50 /* invoke destructor w/o deallocating memory */
51 list->iterator.~TermIterator();
56 notmuch_database_set_config (notmuch_database_t *notmuch,
60 notmuch_status_t status;
62 status = _notmuch_database_ensure_writable (notmuch);
66 if (! notmuch->config) {
67 if ((status = _notmuch_config_load_from_database (notmuch)))
72 notmuch->writable_xapian_db->set_metadata (CONFIG_PREFIX + key, value);
73 } catch (const Xapian::Error &error) {
74 status = NOTMUCH_STATUS_XAPIAN_EXCEPTION;
75 notmuch->exception_reported = true;
76 _notmuch_database_log (notmuch, "Error: A Xapian exception occurred setting metadata: %s\n",
77 error.get_msg ().c_str ());
83 _notmuch_string_map_set (notmuch->config, key, value);
85 return NOTMUCH_STATUS_SUCCESS;
88 static notmuch_status_t
89 _metadata_value (notmuch_database_t *notmuch,
93 notmuch_status_t status = NOTMUCH_STATUS_SUCCESS;
96 value = notmuch->xapian_db->get_metadata (CONFIG_PREFIX + key);
97 } catch (const Xapian::Error &error) {
98 status = NOTMUCH_STATUS_XAPIAN_EXCEPTION;
99 notmuch->exception_reported = true;
100 _notmuch_database_log (notmuch, "Error: A Xapian exception occurred getting metadata: %s\n",
101 error.get_msg ().c_str ());
107 notmuch_database_get_config (notmuch_database_t *notmuch,
111 const char *stored_val;
112 notmuch_status_t status;
114 if (! notmuch->config) {
115 if ((status = _notmuch_config_load_from_database (notmuch)))
120 return NOTMUCH_STATUS_NULL_POINTER;
122 stored_val = _notmuch_string_map_get (notmuch->config, key);
124 /* XXX in principle this API should be fixed so empty string
125 * is distinguished from not found */
126 *value = strdup ("");
128 *value = strdup (stored_val);
131 return NOTMUCH_STATUS_SUCCESS;
135 notmuch_database_get_config_list (notmuch_database_t *notmuch,
137 notmuch_config_list_t **out)
139 notmuch_config_list_t *list = NULL;
140 notmuch_status_t status = NOTMUCH_STATUS_SUCCESS;
142 list = talloc (notmuch, notmuch_config_list_t);
144 status = NOTMUCH_STATUS_OUT_OF_MEMORY;
148 list->notmuch = notmuch;
149 list->current_key = NULL;
150 list->current_val = NULL;
154 new(&(list->iterator)) Xapian::TermIterator (notmuch->xapian_db->metadata_keys_begin
155 (CONFIG_PREFIX + (prefix ? prefix : "")));
156 talloc_set_destructor (list, _notmuch_config_list_destroy);
158 } catch (const Xapian::Error &error) {
159 _notmuch_database_log (notmuch,
160 "A Xapian exception occurred getting metadata iterator: %s.\n",
161 error.get_msg ().c_str ());
162 notmuch->exception_reported = true;
163 status = NOTMUCH_STATUS_XAPIAN_EXCEPTION;
172 if (status != NOTMUCH_STATUS_XAPIAN_EXCEPTION)
173 _notmuch_config_list_destroy (list);
176 talloc_set_destructor (list, _notmuch_config_list_destroy);
183 notmuch_config_list_valid (notmuch_config_list_t *metadata)
185 if (metadata->iterator == metadata->notmuch->xapian_db->metadata_keys_end ())
192 _key_from_iterator (notmuch_config_list_t *list)
194 return talloc_strdup (list, (*list->iterator).c_str () + CONFIG_PREFIX.length ());
198 notmuch_config_list_key (notmuch_config_list_t *list)
200 if (list->current_key)
201 talloc_free (list->current_key);
203 list->current_key = _key_from_iterator (list);
205 return list->current_key;
209 notmuch_config_list_value (notmuch_config_list_t *list)
212 notmuch_status_t status;
213 char *key = _key_from_iterator (list);
215 /* TODO: better error reporting?? */
216 status = _metadata_value (list->notmuch, key, strval);
220 if (list->current_val)
221 talloc_free (list->current_val);
223 list->current_val = talloc_strdup (list, strval.c_str ());
225 return list->current_val;
229 notmuch_config_list_move_to_next (notmuch_config_list_t *list)
235 notmuch_config_list_destroy (notmuch_config_list_t *list)
241 _notmuch_config_load_from_database (notmuch_database_t *notmuch)
243 notmuch_status_t status = NOTMUCH_STATUS_SUCCESS;
244 notmuch_config_list_t *list;
246 if (notmuch->config == NULL)
247 notmuch->config = _notmuch_string_map_create (notmuch);
249 if (unlikely (notmuch->config == NULL))
250 return NOTMUCH_STATUS_OUT_OF_MEMORY;
252 status = notmuch_database_get_config_list (notmuch, "", &list);
256 for (; notmuch_config_list_valid (list); notmuch_config_list_move_to_next (list)) {
257 _notmuch_string_map_append (notmuch->config,
258 notmuch_config_list_key (list),
259 notmuch_config_list_value (list));
265 notmuch_config_values_t *
266 notmuch_config_get_values (notmuch_database_t *notmuch, notmuch_config_key_t key)
268 const char *key_str = _notmuch_config_key_to_string (key);
273 return notmuch_config_get_values_string (notmuch, key_str);
276 notmuch_config_values_t *
277 notmuch_config_get_values_string (notmuch_database_t *notmuch, const char *key_str)
279 notmuch_config_values_t *values = NULL;
282 values = talloc (notmuch, notmuch_config_values_t);
283 if (unlikely (! values))
286 values->children = talloc_new (values);
288 values->string = _notmuch_string_map_get (notmuch->config, key_str);
289 if (! values->string)
292 values->iterator = strsplit_len (values->string, ';', &(values->tok_len));
298 talloc_free (values);
305 notmuch_config_values_valid (notmuch_config_values_t *values)
310 return (values->iterator != NULL);
314 notmuch_config_values_get (notmuch_config_values_t *values)
316 return talloc_strndup (values, values->iterator, values->tok_len);
320 notmuch_config_values_start (notmuch_config_values_t *values)
324 if (values->children) {
325 talloc_free (values->children);
328 values->children = talloc_new (values);
330 values->iterator = strsplit_len (values->string, ';', &(values->tok_len));
334 notmuch_config_values_move_to_next (notmuch_config_values_t *values)
336 values->iterator += values->tok_len;
337 values->iterator = strsplit_len (values->iterator, ';', &(values->tok_len));
341 notmuch_config_values_destroy (notmuch_config_values_t *values)
343 talloc_free (values);
346 notmuch_config_pairs_t *
347 notmuch_config_get_pairs (notmuch_database_t *notmuch,
350 notmuch_config_pairs_t *pairs = talloc (notmuch, notmuch_config_pairs_t);
352 pairs->iter = _notmuch_string_map_iterator_create (notmuch->config, prefix, false);
357 notmuch_config_pairs_valid (notmuch_config_pairs_t *pairs)
359 return _notmuch_string_map_iterator_valid (pairs->iter);
363 notmuch_config_pairs_move_to_next (notmuch_config_pairs_t *pairs)
365 _notmuch_string_map_iterator_move_to_next (pairs->iter);
369 notmuch_config_pairs_key (notmuch_config_pairs_t *pairs)
371 return _notmuch_string_map_iterator_key (pairs->iter);
375 notmuch_config_pairs_value (notmuch_config_pairs_t *pairs)
377 return _notmuch_string_map_iterator_value (pairs->iter);
381 notmuch_config_pairs_destroy (notmuch_config_pairs_t *pairs)
383 _notmuch_string_map_iterator_destroy (pairs->iter);
388 _notmuch_config_load_from_file (notmuch_database_t *notmuch,
391 notmuch_status_t status = NOTMUCH_STATUS_SUCCESS;
392 gchar **groups = NULL, **keys, *val;
394 if (notmuch->config == NULL)
395 notmuch->config = _notmuch_string_map_create (notmuch);
397 if (unlikely (notmuch->config == NULL)) {
398 status = NOTMUCH_STATUS_OUT_OF_MEMORY;
402 groups = g_key_file_get_groups (file, NULL);
403 for (gchar **grp = groups; *grp; grp++) {
404 keys = g_key_file_get_keys (file, *grp, NULL, NULL);
405 for (gchar **keys_p = keys; *keys_p; keys_p++) {
406 char *absolute_key = talloc_asprintf (notmuch, "%s.%s", *grp, *keys_p);
407 val = g_key_file_get_value (file, *grp, *keys_p, NULL);
409 status = NOTMUCH_STATUS_FILE_ERROR;
412 _notmuch_string_map_set (notmuch->config, absolute_key, val);
414 talloc_free (absolute_key);
429 notmuch_config_get_bool (notmuch_database_t *notmuch, notmuch_config_key_t key, notmuch_bool_t *val)
431 const char *key_string, *val_string;
433 key_string = _notmuch_config_key_to_string (key);
435 return NOTMUCH_STATUS_ILLEGAL_ARGUMENT;
438 val_string = _notmuch_string_map_get (notmuch->config, key_string);
441 return NOTMUCH_STATUS_SUCCESS;
444 if (strcase_equal (val_string, "false") || strcase_equal (val_string, "no"))
446 else if (strcase_equal (val_string, "true") || strcase_equal (val_string, "yes"))
449 return NOTMUCH_STATUS_ILLEGAL_ARGUMENT;
451 return NOTMUCH_STATUS_SUCCESS;
455 _notmuch_config_key_to_string (notmuch_config_key_t key)
458 case NOTMUCH_CONFIG_DATABASE_PATH:
459 return "database.path";
460 case NOTMUCH_CONFIG_MAIL_ROOT:
461 return "database.mail_root";
462 case NOTMUCH_CONFIG_HOOK_DIR:
463 return "database.hook_dir";
464 case NOTMUCH_CONFIG_BACKUP_DIR:
465 return "database.backup_dir";
466 case NOTMUCH_CONFIG_EXCLUDE_TAGS:
467 return "search.exclude_tags";
468 case NOTMUCH_CONFIG_NEW_TAGS:
470 case NOTMUCH_CONFIG_NEW_IGNORE:
472 case NOTMUCH_CONFIG_SYNC_MAILDIR_FLAGS:
473 return "maildir.synchronize_flags";
474 case NOTMUCH_CONFIG_PRIMARY_EMAIL:
475 return "user.primary_email";
476 case NOTMUCH_CONFIG_OTHER_EMAIL:
477 return "user.other_email";
478 case NOTMUCH_CONFIG_USER_NAME:
486 _notmuch_config_default (notmuch_database_t *notmuch, notmuch_config_key_t key)
491 case NOTMUCH_CONFIG_DATABASE_PATH:
492 path = getenv ("MAILDIR");
494 path = talloc_strdup (notmuch, path);
496 path = talloc_asprintf (notmuch, "%s/mail",
499 case NOTMUCH_CONFIG_MAIL_ROOT:
500 /* by default, mail root is the same as database path */
501 return notmuch_database_get_path (notmuch);
502 case NOTMUCH_CONFIG_EXCLUDE_TAGS:
504 case NOTMUCH_CONFIG_NEW_TAGS:
505 return "inbox;unread";
506 case NOTMUCH_CONFIG_SYNC_MAILDIR_FLAGS:
508 case NOTMUCH_CONFIG_HOOK_DIR:
509 case NOTMUCH_CONFIG_BACKUP_DIR:
510 case NOTMUCH_CONFIG_NEW_IGNORE:
511 case NOTMUCH_CONFIG_USER_NAME:
512 case NOTMUCH_CONFIG_PRIMARY_EMAIL:
513 case NOTMUCH_CONFIG_OTHER_EMAIL:
516 case NOTMUCH_CONFIG_LAST:
517 INTERNAL_ERROR ("illegal key enum %d", key);
522 _notmuch_config_load_defaults (notmuch_database_t *notmuch)
524 notmuch_config_key_t key;
526 for (key = NOTMUCH_CONFIG_FIRST;
527 key < NOTMUCH_CONFIG_LAST;
528 key = notmuch_config_key_t (key + 1)) {
529 const char *val = notmuch_config_get (notmuch, key);
530 const char *key_string = _notmuch_config_key_to_string (key);
532 val = _notmuch_string_map_get (notmuch->config, key_string);
534 _notmuch_string_map_set (notmuch->config, key_string, _notmuch_config_default (notmuch,
538 return NOTMUCH_STATUS_SUCCESS;
542 notmuch_config_get (notmuch_database_t *notmuch, notmuch_config_key_t key)
545 return _notmuch_string_map_get (notmuch->config, _notmuch_config_key_to_string (key));
549 notmuch_config_set (notmuch_database_t *notmuch, notmuch_config_key_t key, const char *val)
552 return notmuch_database_set_config (notmuch, _notmuch_config_key_to_string (key), val);
556 _notmuch_config_cache (notmuch_database_t *notmuch, notmuch_config_key_t key, const char *val)
558 if (notmuch->config == NULL)
559 notmuch->config = _notmuch_string_map_create (notmuch);
561 _notmuch_string_map_set (notmuch->config, _notmuch_config_key_to_string (key), val);