]> git.notmuchmail.org Git - notmuch/blob - lib/config.cc
lib/open: add support for config profiles and default locations
[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 static int
35 _notmuch_config_list_destroy (notmuch_config_list_t *list)
36 {
37     /* invoke destructor w/o deallocating memory */
38     list->iterator.~TermIterator();
39     return 0;
40 }
41
42 notmuch_status_t
43 notmuch_database_set_config (notmuch_database_t *notmuch,
44                              const char *key,
45                              const char *value)
46 {
47     notmuch_status_t status;
48
49     status = _notmuch_database_ensure_writable (notmuch);
50     if (status)
51         return status;
52
53     if (! notmuch->config) {
54         if ((status = _notmuch_config_load_from_database (notmuch)))
55             return status;
56     }
57
58     try {
59         notmuch->writable_xapian_db->set_metadata (CONFIG_PREFIX + key, value);
60     } catch (const Xapian::Error &error) {
61         status = NOTMUCH_STATUS_XAPIAN_EXCEPTION;
62         notmuch->exception_reported = true;
63         _notmuch_database_log (notmuch, "Error: A Xapian exception occurred setting metadata: %s\n",
64                                error.get_msg ().c_str ());
65     }
66
67     if (status)
68         return status;
69
70     _notmuch_string_map_set (notmuch->config, key, value);
71
72     return NOTMUCH_STATUS_SUCCESS;
73 }
74
75 static notmuch_status_t
76 _metadata_value (notmuch_database_t *notmuch,
77                  const char *key,
78                  std::string &value)
79 {
80     notmuch_status_t status = NOTMUCH_STATUS_SUCCESS;
81
82     try {
83         value = notmuch->xapian_db->get_metadata (CONFIG_PREFIX + key);
84     } catch (const Xapian::Error &error) {
85         status = NOTMUCH_STATUS_XAPIAN_EXCEPTION;
86         notmuch->exception_reported = true;
87         _notmuch_database_log (notmuch, "Error: A Xapian exception occurred getting metadata: %s\n",
88                                error.get_msg ().c_str ());
89     }
90     return status;
91 }
92
93 notmuch_status_t
94 notmuch_database_get_config (notmuch_database_t *notmuch,
95                              const char *key,
96                              char **value)
97 {
98     const char* stored_val;
99     notmuch_status_t status;
100
101     if (! notmuch->config) {
102         if ((status = _notmuch_config_load_from_database (notmuch)))
103             return status;
104     }
105
106     if (! value)
107         return NOTMUCH_STATUS_NULL_POINTER;
108
109     stored_val = _notmuch_string_map_get (notmuch->config, key);
110     if (! stored_val) {
111         /* XXX in principle this API should be fixed so empty string
112          * is distinguished from not found */
113         *value = strdup("");
114     } else {
115         *value = strdup (stored_val);
116     }
117
118     return NOTMUCH_STATUS_SUCCESS;
119 }
120
121 notmuch_status_t
122 notmuch_database_get_config_list (notmuch_database_t *notmuch,
123                                   const char *prefix,
124                                   notmuch_config_list_t **out)
125 {
126     notmuch_config_list_t *list = NULL;
127     notmuch_status_t status = NOTMUCH_STATUS_SUCCESS;
128
129     list = talloc (notmuch, notmuch_config_list_t);
130     if (! list) {
131         status = NOTMUCH_STATUS_OUT_OF_MEMORY;
132         goto DONE;
133     }
134
135     list->notmuch = notmuch;
136     list->current_key = NULL;
137     list->current_val = NULL;
138
139     try {
140
141         new(&(list->iterator)) Xapian::TermIterator (notmuch->xapian_db->metadata_keys_begin
142                                                          (CONFIG_PREFIX + (prefix ? prefix : "")));
143         talloc_set_destructor (list, _notmuch_config_list_destroy);
144
145     } catch (const Xapian::Error &error) {
146         _notmuch_database_log (notmuch, "A Xapian exception occurred getting metadata iterator: %s.\n",
147                                error.get_msg ().c_str ());
148         notmuch->exception_reported = true;
149         status = NOTMUCH_STATUS_XAPIAN_EXCEPTION;
150     }
151
152     *out = list;
153
154   DONE:
155     if (status) {
156         if (list) {
157             talloc_free (list);
158             if (status != NOTMUCH_STATUS_XAPIAN_EXCEPTION)
159                 _notmuch_config_list_destroy (list);
160         }
161     }  else {
162         talloc_set_destructor (list, _notmuch_config_list_destroy);
163     }
164
165     return status;
166 }
167
168 notmuch_bool_t
169 notmuch_config_list_valid (notmuch_config_list_t *metadata)
170 {
171     if (metadata->iterator == metadata->notmuch->xapian_db->metadata_keys_end ())
172         return false;
173
174     return true;
175 }
176
177 static inline char * _key_from_iterator (notmuch_config_list_t *list) {
178     return talloc_strdup (list, (*list->iterator).c_str () + CONFIG_PREFIX.length ());
179 }
180
181 const char *
182 notmuch_config_list_key (notmuch_config_list_t *list)
183 {
184     if (list->current_key)
185         talloc_free (list->current_key);
186
187     list->current_key = _key_from_iterator (list);
188
189     return list->current_key;
190 }
191
192 const char *
193 notmuch_config_list_value (notmuch_config_list_t *list)
194 {
195     std::string strval;
196     notmuch_status_t status;
197     char *key = _key_from_iterator (list);
198
199     /* TODO: better error reporting?? */
200     status = _metadata_value (list->notmuch, key, strval);
201     if (status)
202         return NULL;
203
204     if (list->current_val)
205         talloc_free (list->current_val);
206
207     list->current_val = talloc_strdup (list, strval.c_str ());
208     talloc_free (key);
209     return list->current_val;
210 }
211
212 void
213 notmuch_config_list_move_to_next (notmuch_config_list_t *list)
214 {
215     list->iterator++;
216 }
217
218 void
219 notmuch_config_list_destroy (notmuch_config_list_t *list)
220 {
221     talloc_free (list);
222 }
223
224 notmuch_status_t
225 _notmuch_config_load_from_database (notmuch_database_t *notmuch)
226 {
227     notmuch_status_t status = NOTMUCH_STATUS_SUCCESS;
228     notmuch_config_list_t *list;
229
230     if (notmuch->config == NULL)
231         notmuch->config = _notmuch_string_map_create (notmuch);
232
233     if (unlikely(notmuch->config == NULL))
234         return NOTMUCH_STATUS_OUT_OF_MEMORY;
235
236     status = notmuch_database_get_config_list (notmuch, "", &list);
237     if (status)
238         return status;
239
240     for (; notmuch_config_list_valid (list); notmuch_config_list_move_to_next (list)) {
241         _notmuch_string_map_append (notmuch->config,
242                                     notmuch_config_list_key (list),
243                                     notmuch_config_list_value (list));
244     }
245
246     return status;
247 }
248
249 notmuch_status_t
250 _notmuch_config_load_from_file (notmuch_database_t *notmuch,
251                                 GKeyFile *file)
252 {
253     notmuch_status_t status = NOTMUCH_STATUS_SUCCESS;
254     gchar **groups, **keys, *val;
255
256     if (notmuch->config == NULL)
257         notmuch->config = _notmuch_string_map_create (notmuch);
258
259     if (unlikely(notmuch->config == NULL)) {
260         status = NOTMUCH_STATUS_OUT_OF_MEMORY;
261         goto DONE;
262     }
263
264     for (groups = g_key_file_get_groups (file, NULL); *groups; groups++) {
265         for (keys = g_key_file_get_keys (file, *groups, NULL, NULL); *keys; keys++) {
266             char *absolute_key = talloc_asprintf(notmuch, "%s.%s", *groups,  *keys);
267             val = g_key_file_get_value (file, *groups, *keys, NULL);
268             if (! val) {
269                 status = NOTMUCH_STATUS_FILE_ERROR;
270                 goto DONE;
271             }
272             _notmuch_string_map_set (notmuch->config, absolute_key, val);
273             talloc_free (absolute_key);
274             if (status)
275                 goto DONE;
276         }
277     }
278
279  DONE:
280     return status;
281 }