application/config.c

changeset 6
09ac07345656
child 7
905ac52c910f
equal deleted inserted replaced
5:83263002816f 6:09ac07345656
1 /*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3 *
4 * Copyright 2024 Olaf Wintermann. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <sys/types.h>
33 #include <cx/hash_map.h>
34 #include <cx/utils.h>
35 #include <errno.h>
36 #include <libxml/tree.h>
37
38 #include "pwd.h"
39 #include "config.h"
40 #include "system.h"
41
42 #include <libidav/utils.h>
43 #include <libidav/config.h>
44
45 #define xstreq(a,b) xmlStrEqual(BAD_CAST a, BAD_CAST b)
46 #define xstrEQ(a,b) !xmlStrcasecmp(BAD_CAST a, BAD_CAST b)
47
48 #define print_error(lineno, ...) \
49 do {\
50 fprintf(stderr, "Error (config.xml line %u): ", lineno); \
51 fprintf(stderr, __VA_ARGS__); \
52 fprintf(stderr, "Abort.\n"); \
53 } while(0);
54 #define print_warning(lineno, ...) \
55 do {\
56 fprintf(stderr, "Warning (config.xml line %u): ", lineno); \
57 fprintf(stderr, __VA_ARGS__); \
58 } while(0);
59
60 #ifdef _WIN32
61 #define ENV_HOME getenv("USERPROFILE")
62 #else
63 #define ENV_HOME getenv("HOME")
64 #endif /* _WIN32 */
65
66 static CxMap* repos;
67 static CxMap* keys;
68
69 static DavConfig* davconfig;
70 static PwdStore* pstore;
71
72 static char* secretstore_unlock_cmd;
73 static char* secretstore_lock_cmd;
74
75 int check_config_dir(void) {
76 char* file = util_concat_path(ENV_HOME, ".dav");
77 int ret = 0;
78 if (util_mkdir(file, S_IRWXU)) {
79 if (errno != EEXIST) {
80 ret = 1;
81 }
82 }
83 free(file);
84 return ret;
85 }
86
87 static DavContext* context;
88
89 void create_default_config(char* file) {
90 xmlDoc* doc = xmlNewDoc(BAD_CAST "1.0");
91 xmlNode* root = xmlNewNode(NULL, BAD_CAST "configuration");
92 xmlDocSetRootElement(doc, root);
93 xmlSaveFormatFileEnc(file, doc, "UTF-8", 1);
94 xmlFreeDoc(doc);
95 }
96
97 char* config_file_path(char* name) {
98 char* davd = util_concat_path(ENV_HOME, ".dav");
99 if (!davd) {
100 return NULL;
101 }
102 char* path = util_concat_path(davd, name);
103 free(davd);
104 return path;
105 }
106
107 cxmutstr config_load_file(const char* path) {
108 FILE* file = sys_fopen(path, "r");
109 if (!file) {
110 return (cxmutstr) { NULL, 0 };
111 }
112
113 CxBuffer buf;
114 cxBufferInit(&buf, NULL, 1024, cxDefaultAllocator, CX_BUFFER_AUTO_EXTEND);
115 cx_stream_copy(file, &buf, (cx_read_func)fread, (cx_write_func)cxBufferWrite);
116 fclose(file);
117
118 return cx_mutstrn(buf.space, buf.size);
119 }
120
121 int load_config(DavContext* ctx) {
122 context = ctx;
123 // TODO: free the config somewhere
124 repos = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16);
125 keys = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16);
126
127 char* pwfile = util_concat_path(ENV_HOME, ".dav/secrets.crypt");
128 pstore = pwdstore_open(pwfile);
129 free(pwfile);
130
131 char* file = util_concat_path(ENV_HOME, ".dav/config.xml");
132
133 struct stat s;
134 if (stat(file, &s)) {
135 switch (errno) {
136 case ENOENT: {
137 return 0;
138 }
139 default: {
140 perror("Cannot load config.xml");
141 }
142 }
143 return 1;
144 }
145
146 cxmutstr config_content = config_load_file(file);
147 int config_error;
148 davconfig = dav_config_load(config_content, &config_error);
149 free(config_content.ptr);
150 free(file);
151
152 if (!davconfig) {
153 fprintf(stderr, "Cannot load config.xml\n");
154 return 1;
155 }
156
157 return dav_config_register_keys(davconfig, ctx, load_key_file);
158 }
159
160 DavConfig* get_config(void) {
161 return davconfig;
162 }
163
164 int store_config(void) {
165 if (check_config_dir()) {
166 return 1;
167 }
168
169 CxBuffer* buf = dav_config2buf(davconfig);
170 if (!buf) {
171 return 1;
172 }
173
174 char* file = util_concat_path(ENV_HOME, ".dav/config.xml");
175 FILE* cout = sys_fopen(file, "w");
176 if (!cout) {
177 cxBufferFree(buf);
178 return 1;
179 }
180
181 // should only fail if we run out of disk space or something like that
182 // in that case, the config file is only destroyed
183 // could only be prevented, if we write to a temp file first and than
184 // rename it
185 fwrite(buf->space, buf->size, 1, cout);
186
187 cxBufferFree(buf);
188 fclose(cout);
189
190 return 0;
191 }
192
193 void free_config(void) {
194 if (davconfig) {
195 dav_config_free(davconfig);
196 }
197 }
198
199 cxmutstr load_key_file(const char* filename) {
200 cxmutstr k;
201 k.ptr = NULL;
202 k.length = 0;
203
204 FILE* file = NULL;
205 if (filename[0] == '/') {
206 file = sys_fopen(filename, "r");
207 }
208 else {
209 char* path = util_concat_path(ENV_HOME, ".dav/");
210 char* p2 = util_concat_path(path, filename);
211 file = sys_fopen(p2, "r");
212 free(path);
213 free(p2);
214 }
215
216 if (!file) {
217 fprintf(stderr, "Error: cannot load keyfile %s\n", filename);
218 return k;
219 }
220
221 char* data = malloc(256);
222 size_t r = fread(data, 1, 256, file);
223 k.ptr = data;
224 k.length = r;
225
226 fclose(file);
227 return k;
228 }
229
230 static char* get_attr_content(xmlNode* node) {
231 // TODO: remove code duplication (util_xml_get_text)
232 while (node) {
233 if (node->type == XML_TEXT_NODE) {
234 return (char*)node->content;
235 }
236 node = node->next;
237 }
238 return NULL;
239 }
240
241 int load_namespace(const xmlNode* node) {
242 const char* prefix = NULL;
243 const char* uri = NULL;
244
245 xmlAttr* attr = node->properties;
246 while (attr) {
247 if (attr->type == XML_ATTRIBUTE_NODE) {
248 char* value = get_attr_content(attr->children);
249 if (!value) {
250 print_error(
251 node->line,
252 "missing value for attribute %s\n", (char*)attr->name);
253 return 1;
254 }
255 if (xstreq(attr->name, "prefix")) {
256 prefix = value;
257 }
258 else if (xstreq(attr->name, "uri")) {
259 uri = value;
260 }
261 else {
262 print_error(
263 node->line,
264 "unexpected attribute %s\n", (char*)attr->name);
265 return 1;
266 }
267 }
268 attr = attr->next;
269 }
270
271 if (!prefix) {
272 print_error(node->line, "missing prefix attribute\n");
273 return 1;
274 }
275 if (!uri) {
276 print_error(node->line, "missing uri attribute\n");
277 return 1;
278 }
279
280 if (dav_get_namespace(context, prefix)) {
281 print_error(node->line, "namespace prefix '%s' already used\n", prefix);
282 return 1;
283 }
284
285 return dav_add_namespace(context, prefix, uri);
286 }
287
288 int load_secretstore(const xmlNode* node) {
289 // currently only one secretstore is supported
290
291 if (!pstore) {
292 return 0;
293 }
294
295 node = node->children;
296 int error = 0;
297 while (node) {
298 if (node->type == XML_ELEMENT_NODE) {
299 char* value = util_xml_get_text(node);
300 if (value) {
301 if (xstreq(node->name, "unlock-command")) {
302 pstore->unlock_cmd = strdup(value);
303 }
304 else if (xstreq(node->name, "lock-command")) {
305 pstore->lock_cmd = strdup(value);
306 }
307 }
308 }
309 node = node->next;
310 }
311
312 return error;
313 }
314
315 PwdStore* get_pwdstore(void) {
316 return pstore;
317 }
318
319 int pwdstore_save(PwdStore* pwdstore) {
320 if (check_config_dir()) {
321 return 1;
322 }
323
324 char* pwfile = util_concat_path(ENV_HOME, ".dav/secrets.crypt");
325 int ret = pwdstore_store(pwdstore, pwfile);
326 free(pwfile);
327 return ret;
328 }

mercurial