]> git.notmuchmail.org Git - notmuch/blob - lib/config.cc
lib: run uncrustify
[notmuch] / lib / config.cc
1 /* config.cc - API for database metadata
2  *
3  * Copyright © 2016 David Bremner
4  *
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.
9  *
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.
14  *
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/ .
17  *
18  * Author: David Bremner <david@tethera.net>
19  */
20
21 #include "notmuch.h"
22 #include "notmuch-private.h"
23 #include "database-private.h"
24
25 static const std::string CONFIG_PREFIX = "C";
26
27 struct _notmuch_config_list {
28     notmuch_database_t *notmuch;
29     Xapian::TermIterator iterator;
30     char *current_key;
31     char *current_val;
32 };
33
34 struct _notmuch_config_values {
35     const char *iterator;
36     size_t tok_len;
37     const char *string;
38     void *children; /* talloc_context */
39 };
40
41 static const char *_notmuch_config_key_to_string (notmuch_config_key_t key);
42
43 static int
44 _notmuch_config_list_destroy (notmuch_config_list_t *list)
45 {
46     /* invoke destructor w/o deallocating memory */
47     list->iterator.~TermIterator();
48     return 0;
49 }
50
51 notmuch_status_t
52 notmuch_database_set_config (notmuch_database_t *notmuch,
53                              const char *key,
54                              const char *value)
55 {
56     notmuch_status_t status;
57
58     status = _notmuch_database_ensure_writable (notmuch);
59     if (status)
60         return status;
61
62     if (! notmuch->config) {
63         if ((status = _notmuch_config_load_from_database (notmuch)))
64             return status;
65     }
66
67     try {
68         notmuch->writable_xapian_db->set_metadata (CONFIG_PREFIX + key, value);
69     } catch (const Xapian::Error &error) {
70         status = NOTMUCH_STATUS_XAPIAN_EXCEPTION;
71         notmuch->exception_reported = true;
72         _notmuch_database_log (notmuch, "Error: A Xapian exception occurred setting metadata: %s\n",
73                                error.get_msg ().c_str ());
74     }
75
76     if (status)
77         return status;
78
79     _notmuch_string_map_set (notmuch->config, key, value);
80
81     return NOTMUCH_STATUS_SUCCESS;
82 }
83
84 static notmuch_status_t
85 _metadata_value (notmuch_database_t *notmuch,
86                  const char *key,
87                  std::string &value)
88 {
89     notmuch_status_t status = NOTMUCH_STATUS_SUCCESS;
90
91     try {
92         value = notmuch->xapian_db->get_metadata (CONFIG_PREFIX + key);
93     } catch (const Xapian::Error &error) {
94         status = NOTMUCH_STATUS_XAPIAN_EXCEPTION;
95         notmuch->exception_reported = true;
96         _notmuch_database_log (notmuch, "Error: A Xapian exception occurred getting metadata: %s\n",
97                                error.get_msg ().c_str ());
98     }
99     return status;
100 }
101
102 notmuch_status_t
103 notmuch_database_get_config (notmuch_database_t *notmuch,
104                              const char *key,
105                              char **value)
106 {
107     const char *stored_val;
108     notmuch_status_t status;
109
110     if (! notmuch->config) {
111         if ((status = _notmuch_config_load_from_database (notmuch)))
112             return status;
113     }
114
115     if (! value)
116         return NOTMUCH_STATUS_NULL_POINTER;
117
118     stored_val = _notmuch_string_map_get (notmuch->config, key);
119     if (! stored_val) {
120         /* XXX in principle this API should be fixed so empty string
121          * is distinguished from not found */
122         *value = strdup ("");
123     } else {
124         *value = strdup (stored_val);
125     }
126
127     return NOTMUCH_STATUS_SUCCESS;
128 }
129
130 notmuch_status_t
131 notmuch_database_get_config_list (notmuch_database_t *notmuch,
132                                   const char *prefix,
133                                   notmuch_config_list_t **out)
134 {
135     notmuch_config_list_t *list = NULL;
136     notmuch_status_t status = NOTMUCH_STATUS_SUCCESS;
137
138     list = talloc (notmuch, notmuch_config_list_t);
139     if (! list) {
140         status = NOTMUCH_STATUS_OUT_OF_MEMORY;
141         goto DONE;
142     }
143
144     list->notmuch = notmuch;
145     list->current_key = NULL;
146     list->current_val = NULL;
147
148     try {
149
150         new(&(list->iterator)) Xapian::TermIterator (notmuch->xapian_db->metadata_keys_begin
151                                                          (CONFIG_PREFIX + (prefix ? prefix : "")));
152         talloc_set_destructor (list, _notmuch_config_list_destroy);
153
154     } catch (const Xapian::Error &error) {
155         _notmuch_database_log (notmuch,
156                                "A Xapian exception occurred getting metadata iterator: %s.\n",
157                                error.get_msg ().c_str ());
158         notmuch->exception_reported = true;
159         status = NOTMUCH_STATUS_XAPIAN_EXCEPTION;
160     }
161
162     *out = list;
163
164   DONE:
165     if (status) {
166         if (list) {
167             talloc_free (list);
168             if (status != NOTMUCH_STATUS_XAPIAN_EXCEPTION)
169                 _notmuch_config_list_destroy (list);
170         }
171     } else {
172         talloc_set_destructor (list, _notmuch_config_list_destroy);
173     }
174
175     return status;
176 }
177
178 notmuch_bool_t
179 notmuch_config_list_valid (notmuch_config_list_t *metadata)
180 {
181     if (metadata->iterator == metadata->notmuch->xapian_db->metadata_keys_end ())
182         return false;
183
184     return true;
185 }
186
187 static inline char *
188 _key_from_iterator (notmuch_config_list_t *list)
189 {
190     return talloc_strdup (list, (*list->iterator).c_str () + CONFIG_PREFIX.length ());
191 }
192
193 const char *
194 notmuch_config_list_key (notmuch_config_list_t *list)
195 {
196     if (list->current_key)
197         talloc_free (list->current_key);
198
199     list->current_key = _key_from_iterator (list);
200
201     return list->current_key;
202 }
203
204 const char *
205 notmuch_config_list_value (notmuch_config_list_t *list)
206 {
207     std::string strval;
208     notmuch_status_t status;
209     char *key = _key_from_iterator (list);
210
211     /* TODO: better error reporting?? */
212     status = _metadata_value (list->notmuch, key, strval);
213     if (status)
214         return NULL;
215
216     if (list->current_val)
217         talloc_free (list->current_val);
218
219     list->current_val = talloc_strdup (list, strval.c_str ());
220     talloc_free (key);
221     return list->current_val;
222 }
223
224 void
225 notmuch_config_list_move_to_next (notmuch_config_list_t *list)
226 {
227     list->iterator++;
228 }
229
230 void
231 notmuch_config_list_destroy (notmuch_config_list_t *list)
232 {
233     talloc_free (list);
234 }
235
236 notmuch_status_t
237 _notmuch_config_load_from_database (notmuch_database_t *notmuch)
238 {
239     notmuch_status_t status = NOTMUCH_STATUS_SUCCESS;
240     notmuch_config_list_t *list;
241
242     if (notmuch->config == NULL)
243         notmuch->config = _notmuch_string_map_create (notmuch);
244
245     if (unlikely (notmuch->config == NULL))
246         return NOTMUCH_STATUS_OUT_OF_MEMORY;
247
248     status = notmuch_database_get_config_list (notmuch, "", &list);
249     if (status)
250         return status;
251
252     for (; notmuch_config_list_valid (list); notmuch_config_list_move_to_next (list)) {
253         _notmuch_string_map_append (notmuch->config,
254                                     notmuch_config_list_key (list),
255                                     notmuch_config_list_value (list));
256     }
257
258     return status;
259 }
260
261 notmuch_config_values_t *
262 notmuch_config_get_values (notmuch_database_t *notmuch, notmuch_config_key_t key)
263 {
264     notmuch_config_values_t *values = NULL;
265     bool ok = false;
266
267     const char *key_str = _notmuch_config_key_to_string (key);
268
269     if (! key_str)
270         goto DONE;
271
272     values = talloc (notmuch, notmuch_config_values_t);
273     if (unlikely (! values))
274         goto DONE;
275
276     values->children = talloc_new (values);
277
278     values->string = _notmuch_string_map_get (notmuch->config, key_str);
279     if (! values->string)
280         goto DONE;
281
282     values->iterator = strsplit_len (values->string, ';', &(values->tok_len));
283     ok = true;
284
285   DONE:
286     if (! ok) {
287         if (values)
288             talloc_free (values);
289         return NULL;
290     }
291     return values;
292 }
293
294 notmuch_bool_t
295 notmuch_config_values_valid (notmuch_config_values_t *values)
296 {
297     if (! values)
298         return false;
299
300     return (values->iterator != NULL);
301 }
302
303 const char *
304 notmuch_config_values_get (notmuch_config_values_t *values)
305 {
306     return talloc_strndup (values, values->iterator, values->tok_len);
307 }
308
309 void
310 notmuch_config_values_start (notmuch_config_values_t *values)
311 {
312     if (values == NULL)
313         return;
314     if (values->children) {
315         talloc_free (values->children);
316     }
317
318     values->children = talloc_new (values);
319
320     values->iterator = strsplit_len (values->string, ';', &(values->tok_len));
321 }
322
323 void
324 notmuch_config_values_move_to_next (notmuch_config_values_t *values)
325 {
326     values->iterator += values->tok_len;
327     values->iterator = strsplit_len (values->iterator, ';', &(values->tok_len));
328 }
329
330 void
331 notmuch_config_values_destroy (notmuch_config_values_t *values)
332 {
333     talloc_free (values);
334 }
335
336 notmuch_status_t
337 _notmuch_config_load_from_file (notmuch_database_t *notmuch,
338                                 GKeyFile *file)
339 {
340     notmuch_status_t status = NOTMUCH_STATUS_SUCCESS;
341     gchar **groups, **keys, *val;
342
343     if (notmuch->config == NULL)
344         notmuch->config = _notmuch_string_map_create (notmuch);
345
346     if (unlikely (notmuch->config == NULL)) {
347         status = NOTMUCH_STATUS_OUT_OF_MEMORY;
348         goto DONE;
349     }
350
351     for (groups = g_key_file_get_groups (file, NULL); *groups; groups++) {
352         for (keys = g_key_file_get_keys (file, *groups, NULL, NULL); *keys; keys++) {
353             char *absolute_key = talloc_asprintf (notmuch, "%s.%s", *groups,  *keys);
354             val = g_key_file_get_value (file, *groups, *keys, NULL);
355             if (! val) {
356                 status = NOTMUCH_STATUS_FILE_ERROR;
357                 goto DONE;
358             }
359             _notmuch_string_map_set (notmuch->config, absolute_key, val);
360             talloc_free (absolute_key);
361             if (status)
362                 goto DONE;
363         }
364     }
365
366   DONE:
367     return status;
368 }
369
370 notmuch_status_t
371 notmuch_config_get_bool (notmuch_database_t *notmuch, notmuch_config_key_t key, notmuch_bool_t *val)
372 {
373     const char *key_string, *val_string;
374
375     key_string = _notmuch_config_key_to_string (key);
376     if (! key_string) {
377         return NOTMUCH_STATUS_ILLEGAL_ARGUMENT;
378     }
379
380     val_string = _notmuch_string_map_get (notmuch->config, key_string);
381     if (! val_string) {
382         *val = FALSE;
383         return NOTMUCH_STATUS_SUCCESS;
384     }
385
386     if (strcase_equal (val_string, "false") || strcase_equal (val_string, "no"))
387         *val = FALSE;
388     else if (strcase_equal (val_string, "true") || strcase_equal (val_string, "yes"))
389         *val = TRUE;
390     else
391         return NOTMUCH_STATUS_ILLEGAL_ARGUMENT;
392
393     return NOTMUCH_STATUS_SUCCESS;
394 }
395
396 static const char *
397 _notmuch_config_key_to_string (notmuch_config_key_t key)
398 {
399     switch (key) {
400     case NOTMUCH_CONFIG_DATABASE_PATH:
401         return "database.path";
402     case NOTMUCH_CONFIG_HOOK_DIR:
403         return "database.hook_dir";
404     case NOTMUCH_CONFIG_EXCLUDE_TAGS:
405         return "search.exclude_tags";
406     case NOTMUCH_CONFIG_NEW_TAGS:
407         return "new.tags";
408     case NOTMUCH_CONFIG_NEW_IGNORE:
409         return "new.ignore";
410     case NOTMUCH_CONFIG_SYNC_MAILDIR_FLAGS:
411         return "maildir.synchronize_flags";
412     case NOTMUCH_CONFIG_PRIMARY_EMAIL:
413         return "user.primary_email";
414     case NOTMUCH_CONFIG_OTHER_EMAIL:
415         return "user.other_email";
416     case NOTMUCH_CONFIG_USER_NAME:
417         return "user.name";
418     default:
419         return NULL;
420     }
421 }
422
423 static const char *
424 _notmuch_config_default (void *ctx, notmuch_config_key_t key)
425 {
426     char *path;
427
428     switch (key) {
429     case NOTMUCH_CONFIG_DATABASE_PATH:
430         path = getenv ("MAILDIR");
431         if (path)
432             path = talloc_strdup (ctx, path);
433         else
434             path = talloc_asprintf (ctx, "%s/mail",
435                                     getenv ("HOME"));
436         return path;
437     case NOTMUCH_CONFIG_EXCLUDE_TAGS:
438         return "";
439     case NOTMUCH_CONFIG_NEW_TAGS:
440         return "inbox;unread";
441     case NOTMUCH_CONFIG_SYNC_MAILDIR_FLAGS:
442         return "true";
443     case NOTMUCH_CONFIG_HOOK_DIR:
444     case NOTMUCH_CONFIG_NEW_IGNORE:
445     case NOTMUCH_CONFIG_USER_NAME:
446     case NOTMUCH_CONFIG_PRIMARY_EMAIL:
447     case NOTMUCH_CONFIG_OTHER_EMAIL:
448         return NULL;
449     default:
450     case NOTMUCH_CONFIG_LAST:
451         INTERNAL_ERROR ("illegal key enum %d", key);
452     }
453 }
454
455 notmuch_status_t
456 _notmuch_config_load_defaults (notmuch_database_t *notmuch)
457 {
458     notmuch_config_key_t key;
459
460     for (key = NOTMUCH_CONFIG_FIRST;
461          key < NOTMUCH_CONFIG_LAST;
462          key = notmuch_config_key_t (key + 1)) {
463         const char *val = notmuch_config_get (notmuch, key);
464         const char *key_string = _notmuch_config_key_to_string (key);
465
466         val = _notmuch_string_map_get (notmuch->config, key_string);
467         if (! val) {
468             _notmuch_string_map_set (notmuch->config, key_string, _notmuch_config_default (notmuch,
469                                                                                            key));
470         }
471     }
472     return NOTMUCH_STATUS_SUCCESS;
473 }
474
475 const char *
476 notmuch_config_get (notmuch_database_t *notmuch, notmuch_config_key_t key)
477 {
478
479     return _notmuch_string_map_get (notmuch->config, _notmuch_config_key_to_string (key));
480 }
481
482 notmuch_status_t
483 notmuch_config_set (notmuch_database_t *notmuch, notmuch_config_key_t key, const char *val)
484 {
485
486     return notmuch_database_set_config (notmuch, _notmuch_config_key_to_string (key), val);
487 }
488
489 void
490 _notmuch_config_cache (notmuch_database_t *notmuch, notmuch_config_key_t key, const char *val)
491 {
492     _notmuch_string_map_set (notmuch->config, _notmuch_config_key_to_string (key), val);
493 }