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"
27 static const std::string CONFIG_PREFIX = "C";
29 struct _notmuch_config_list {
30 notmuch_database_t *notmuch;
31 Xapian::TermIterator iterator;
36 struct _notmuch_config_values {
40 void *children; /* talloc_context */
43 struct _notmuch_config_pairs {
44 notmuch_string_map_iterator_t *iter;
47 static const char *_notmuch_config_key_to_string (notmuch_config_key_t key);
50 _notmuch_config_list_destroy (notmuch_config_list_t *list)
52 /* invoke destructor w/o deallocating memory */
53 list->iterator.~TermIterator();
58 notmuch_database_set_config (notmuch_database_t *notmuch,
62 notmuch_status_t status;
64 status = _notmuch_database_ensure_writable (notmuch);
68 if (! notmuch->config) {
69 if ((status = _notmuch_config_load_from_database (notmuch)))
74 notmuch->writable_xapian_db->set_metadata (CONFIG_PREFIX + key, value);
75 } catch (const Xapian::Error &error) {
76 status = NOTMUCH_STATUS_XAPIAN_EXCEPTION;
77 notmuch->exception_reported = true;
78 _notmuch_database_log (notmuch, "Error: A Xapian exception occurred setting metadata: %s\n",
79 error.get_msg ().c_str ());
85 _notmuch_string_map_set (notmuch->config, key, value);
87 return NOTMUCH_STATUS_SUCCESS;
90 static notmuch_status_t
91 _metadata_value (notmuch_database_t *notmuch,
95 notmuch_status_t status = NOTMUCH_STATUS_SUCCESS;
98 value = notmuch->xapian_db->get_metadata (CONFIG_PREFIX + key);
99 } catch (const Xapian::Error &error) {
100 status = NOTMUCH_STATUS_XAPIAN_EXCEPTION;
101 notmuch->exception_reported = true;
102 _notmuch_database_log (notmuch, "Error: A Xapian exception occurred getting metadata: %s\n",
103 error.get_msg ().c_str ());
109 notmuch_database_get_config (notmuch_database_t *notmuch,
113 const char *stored_val;
114 notmuch_status_t status;
116 if (! notmuch->config) {
117 if ((status = _notmuch_config_load_from_database (notmuch)))
122 return NOTMUCH_STATUS_NULL_POINTER;
124 stored_val = _notmuch_string_map_get (notmuch->config, key);
126 /* XXX in principle this API should be fixed so empty string
127 * is distinguished from not found */
128 *value = strdup ("");
130 *value = strdup (stored_val);
133 return NOTMUCH_STATUS_SUCCESS;
137 notmuch_database_get_config_list (notmuch_database_t *notmuch,
139 notmuch_config_list_t **out)
141 notmuch_config_list_t *list = NULL;
142 notmuch_status_t status = NOTMUCH_STATUS_SUCCESS;
144 list = talloc (notmuch, notmuch_config_list_t);
146 status = NOTMUCH_STATUS_OUT_OF_MEMORY;
150 list->notmuch = notmuch;
151 list->current_key = NULL;
152 list->current_val = NULL;
156 new(&(list->iterator)) Xapian::TermIterator (notmuch->xapian_db->metadata_keys_begin
157 (CONFIG_PREFIX + (prefix ? prefix : "")));
158 talloc_set_destructor (list, _notmuch_config_list_destroy);
160 } catch (const Xapian::Error &error) {
161 _notmuch_database_log (notmuch,
162 "A Xapian exception occurred getting metadata iterator: %s.\n",
163 error.get_msg ().c_str ());
164 notmuch->exception_reported = true;
165 status = NOTMUCH_STATUS_XAPIAN_EXCEPTION;
174 if (status != NOTMUCH_STATUS_XAPIAN_EXCEPTION)
175 _notmuch_config_list_destroy (list);
178 talloc_set_destructor (list, _notmuch_config_list_destroy);
185 notmuch_config_list_valid (notmuch_config_list_t *metadata)
187 if (metadata->iterator == metadata->notmuch->xapian_db->metadata_keys_end ())
194 _key_from_iterator (notmuch_config_list_t *list)
196 return talloc_strdup (list, (*list->iterator).c_str () + CONFIG_PREFIX.length ());
200 notmuch_config_list_key (notmuch_config_list_t *list)
202 if (list->current_key)
203 talloc_free (list->current_key);
205 list->current_key = _key_from_iterator (list);
207 return list->current_key;
211 notmuch_config_list_value (notmuch_config_list_t *list)
214 notmuch_status_t status;
215 char *key = _key_from_iterator (list);
217 /* TODO: better error reporting?? */
218 status = _metadata_value (list->notmuch, key, strval);
222 if (list->current_val)
223 talloc_free (list->current_val);
225 list->current_val = talloc_strdup (list, strval.c_str ());
227 return list->current_val;
231 notmuch_config_list_move_to_next (notmuch_config_list_t *list)
237 notmuch_config_list_destroy (notmuch_config_list_t *list)
243 _notmuch_config_load_from_database (notmuch_database_t *notmuch)
245 notmuch_status_t status = NOTMUCH_STATUS_SUCCESS;
246 notmuch_config_list_t *list;
248 if (notmuch->config == NULL)
249 notmuch->config = _notmuch_string_map_create (notmuch);
251 if (unlikely (notmuch->config == NULL))
252 return NOTMUCH_STATUS_OUT_OF_MEMORY;
254 status = notmuch_database_get_config_list (notmuch, "", &list);
258 for (; notmuch_config_list_valid (list); notmuch_config_list_move_to_next (list)) {
259 _notmuch_string_map_append (notmuch->config,
260 notmuch_config_list_key (list),
261 notmuch_config_list_value (list));
267 notmuch_config_values_t *
268 notmuch_config_get_values (notmuch_database_t *notmuch, notmuch_config_key_t key)
270 const char *key_str = _notmuch_config_key_to_string (key);
275 return notmuch_config_get_values_string (notmuch, key_str);
278 notmuch_config_values_t *
279 notmuch_config_get_values_string (notmuch_database_t *notmuch, const char *key_str)
281 notmuch_config_values_t *values = NULL;
284 values = talloc (notmuch, notmuch_config_values_t);
285 if (unlikely (! values))
288 values->children = talloc_new (values);
290 values->string = _notmuch_string_map_get (notmuch->config, key_str);
291 if (! values->string)
294 values->iterator = strsplit_len (values->string, ';', &(values->tok_len));
300 talloc_free (values);
307 notmuch_config_values_valid (notmuch_config_values_t *values)
312 return (values->iterator != NULL);
316 notmuch_config_values_get (notmuch_config_values_t *values)
318 return talloc_strndup (values, values->iterator, values->tok_len);
322 notmuch_config_values_start (notmuch_config_values_t *values)
326 if (values->children) {
327 talloc_free (values->children);
330 values->children = talloc_new (values);
332 values->iterator = strsplit_len (values->string, ';', &(values->tok_len));
336 notmuch_config_values_move_to_next (notmuch_config_values_t *values)
338 values->iterator += values->tok_len;
339 values->iterator = strsplit_len (values->iterator, ';', &(values->tok_len));
343 notmuch_config_values_destroy (notmuch_config_values_t *values)
345 talloc_free (values);
348 notmuch_config_pairs_t *
349 notmuch_config_get_pairs (notmuch_database_t *notmuch,
352 notmuch_config_pairs_t *pairs = talloc (notmuch, notmuch_config_pairs_t);
354 pairs->iter = _notmuch_string_map_iterator_create (notmuch->config, prefix, false);
359 notmuch_config_pairs_valid (notmuch_config_pairs_t *pairs)
361 return _notmuch_string_map_iterator_valid (pairs->iter);
365 notmuch_config_pairs_move_to_next (notmuch_config_pairs_t *pairs)
367 _notmuch_string_map_iterator_move_to_next (pairs->iter);
371 notmuch_config_pairs_key (notmuch_config_pairs_t *pairs)
373 return _notmuch_string_map_iterator_key (pairs->iter);
377 notmuch_config_pairs_value (notmuch_config_pairs_t *pairs)
379 return _notmuch_string_map_iterator_value (pairs->iter);
383 notmuch_config_pairs_destroy (notmuch_config_pairs_t *pairs)
385 _notmuch_string_map_iterator_destroy (pairs->iter);
390 _notmuch_config_load_from_file (notmuch_database_t *notmuch,
393 notmuch_status_t status = NOTMUCH_STATUS_SUCCESS;
394 gchar **groups = NULL, **keys, *val;
396 if (notmuch->config == NULL)
397 notmuch->config = _notmuch_string_map_create (notmuch);
399 if (unlikely (notmuch->config == NULL)) {
400 status = NOTMUCH_STATUS_OUT_OF_MEMORY;
404 groups = g_key_file_get_groups (file, NULL);
405 for (gchar **grp = groups; *grp; grp++) {
406 keys = g_key_file_get_keys (file, *grp, NULL, NULL);
407 for (gchar **keys_p = keys; *keys_p; keys_p++) {
408 char *absolute_key = talloc_asprintf (notmuch, "%s.%s", *grp, *keys_p);
409 val = g_key_file_get_value (file, *grp, *keys_p, NULL);
411 status = NOTMUCH_STATUS_FILE_ERROR;
414 _notmuch_string_map_set (notmuch->config, absolute_key, val);
416 talloc_free (absolute_key);
431 notmuch_config_get_bool (notmuch_database_t *notmuch, notmuch_config_key_t key, notmuch_bool_t *val)
433 const char *key_string, *val_string;
435 key_string = _notmuch_config_key_to_string (key);
437 return NOTMUCH_STATUS_ILLEGAL_ARGUMENT;
440 val_string = _notmuch_string_map_get (notmuch->config, key_string);
443 return NOTMUCH_STATUS_SUCCESS;
446 if (strcase_equal (val_string, "false") || strcase_equal (val_string, "no"))
448 else if (strcase_equal (val_string, "true") || strcase_equal (val_string, "yes"))
451 return NOTMUCH_STATUS_ILLEGAL_ARGUMENT;
453 return NOTMUCH_STATUS_SUCCESS;
457 _get_name_from_passwd_file (void *ctx)
461 struct passwd passwd, *ignored;
465 pw_buf_size = sysconf (_SC_GETPW_R_SIZE_MAX);
466 if (pw_buf_size == -1) pw_buf_size = 64;
467 pw_buf = (char *) talloc_size (ctx, pw_buf_size);
469 while ((e = getpwuid_r (getuid (), &passwd, pw_buf,
470 pw_buf_size, &ignored)) == ERANGE) {
471 pw_buf_size = pw_buf_size * 2;
472 pw_buf = (char *) talloc_zero_size (ctx, pw_buf_size);
476 char *comma = strchr (passwd.pw_gecos, ',');
478 name = talloc_strndup (ctx, passwd.pw_gecos,
479 comma - passwd.pw_gecos);
481 name = talloc_strdup (ctx, passwd.pw_gecos);
483 name = talloc_strdup (ctx, "");
486 talloc_free (pw_buf);
492 _notmuch_config_key_to_string (notmuch_config_key_t key)
495 case NOTMUCH_CONFIG_DATABASE_PATH:
496 return "database.path";
497 case NOTMUCH_CONFIG_MAIL_ROOT:
498 return "database.mail_root";
499 case NOTMUCH_CONFIG_HOOK_DIR:
500 return "database.hook_dir";
501 case NOTMUCH_CONFIG_BACKUP_DIR:
502 return "database.backup_dir";
503 case NOTMUCH_CONFIG_EXCLUDE_TAGS:
504 return "search.exclude_tags";
505 case NOTMUCH_CONFIG_NEW_TAGS:
507 case NOTMUCH_CONFIG_NEW_IGNORE:
509 case NOTMUCH_CONFIG_SYNC_MAILDIR_FLAGS:
510 return "maildir.synchronize_flags";
511 case NOTMUCH_CONFIG_PRIMARY_EMAIL:
512 return "user.primary_email";
513 case NOTMUCH_CONFIG_OTHER_EMAIL:
514 return "user.other_email";
515 case NOTMUCH_CONFIG_USER_NAME:
523 _notmuch_config_default (notmuch_database_t *notmuch, notmuch_config_key_t key)
529 case NOTMUCH_CONFIG_DATABASE_PATH:
530 path = getenv ("MAILDIR");
532 path = talloc_strdup (notmuch, path);
534 path = talloc_asprintf (notmuch, "%s/mail",
537 case NOTMUCH_CONFIG_MAIL_ROOT:
538 /* by default, mail root is the same as database path */
539 return notmuch_database_get_path (notmuch);
540 case NOTMUCH_CONFIG_EXCLUDE_TAGS:
542 case NOTMUCH_CONFIG_NEW_TAGS:
543 return "inbox;unread";
544 case NOTMUCH_CONFIG_SYNC_MAILDIR_FLAGS:
546 case NOTMUCH_CONFIG_USER_NAME:
547 name = getenv ("NAME");
549 name = talloc_strdup (notmuch, name);
551 name = _get_name_from_passwd_file (notmuch);
555 case NOTMUCH_CONFIG_HOOK_DIR:
556 case NOTMUCH_CONFIG_BACKUP_DIR:
557 case NOTMUCH_CONFIG_NEW_IGNORE:
558 case NOTMUCH_CONFIG_PRIMARY_EMAIL:
559 case NOTMUCH_CONFIG_OTHER_EMAIL:
562 case NOTMUCH_CONFIG_LAST:
563 INTERNAL_ERROR ("illegal key enum %d", key);
568 _notmuch_config_load_defaults (notmuch_database_t *notmuch)
570 notmuch_config_key_t key;
572 for (key = NOTMUCH_CONFIG_FIRST;
573 key < NOTMUCH_CONFIG_LAST;
574 key = notmuch_config_key_t (key + 1)) {
575 const char *val = notmuch_config_get (notmuch, key);
576 const char *key_string = _notmuch_config_key_to_string (key);
578 val = _notmuch_string_map_get (notmuch->config, key_string);
580 _notmuch_string_map_set (notmuch->config, key_string, _notmuch_config_default (notmuch,
584 return NOTMUCH_STATUS_SUCCESS;
588 notmuch_config_get (notmuch_database_t *notmuch, notmuch_config_key_t key)
591 return _notmuch_string_map_get (notmuch->config, _notmuch_config_key_to_string (key));
595 notmuch_config_set (notmuch_database_t *notmuch, notmuch_config_key_t key, const char *val)
598 return notmuch_database_set_config (notmuch, _notmuch_config_key_to_string (key), val);
602 _notmuch_config_cache (notmuch_database_t *notmuch, notmuch_config_key_t key, const char *val)
604 if (notmuch->config == NULL)
605 notmuch->config = _notmuch_string_map_create (notmuch);
607 _notmuch_string_map_set (notmuch->config, _notmuch_config_key_to_string (key), val);