ucimap: fix boolean interpretation, add range checks for int values (thx, henning)
[project/uci.git] / ucimap.c
1 /*
2  * ucimap - library for mapping uci sections into data structures
3  * Copyright (C) 2008 Felix Fietkau <nbd@openwrt.org>
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 version 2
7  * as published by the Free Software Foundation
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  */
14 #include <strings.h>
15 #include <stdbool.h>
16 #include <string.h>
17 #include <stdlib.h>
18 #include <unistd.h>
19 #include <limits.h>
20 #include "ucimap.h"
21
22 struct uci_alloc {
23         enum ucimap_type type;
24         union {
25                 void **ptr;
26         } data;
27 };
28
29 struct uci_fixup {
30         struct list_head list;
31         struct uci_sectionmap *sm;
32         const char *name;
33         enum ucimap_type type;
34         union ucimap_data *data;
35 };
36
37 #define ucimap_foreach_option(_sm, _o) \
38         if (!(_sm)->options_size) \
39                 (_sm)->options_size = sizeof(struct uci_optmap); \
40         for (_o = &(_sm)->options[0]; \
41                  ((char *)(_o)) < ((char *) &(_sm)->options[0] + \
42                         (_sm)->options_size * (_sm)->n_options); \
43                  _o = (struct uci_optmap *) ((char *)(_o) + \
44                         (_sm)->options_size))
45
46
47 static inline bool
48 ucimap_is_alloc(enum ucimap_type type)
49 {
50         switch(type & UCIMAP_SUBTYPE) {
51         case UCIMAP_STRING:
52                 return true;
53         default:
54                 return false;
55         }
56 }
57
58 static inline bool
59 ucimap_is_fixup(enum ucimap_type type)
60 {
61         switch(type & UCIMAP_SUBTYPE) {
62         case UCIMAP_SECTION:
63                 return true;
64         default:
65                 return false;
66         }
67 }
68
69 static inline bool
70 ucimap_is_simple(enum ucimap_type type)
71 {
72         return ((type & UCIMAP_TYPE) == UCIMAP_SIMPLE);
73 }
74
75 static inline bool
76 ucimap_is_list(enum ucimap_type type)
77 {
78         return ((type & UCIMAP_TYPE) == UCIMAP_LIST);
79 }
80
81 static inline bool
82 ucimap_is_custom(enum ucimap_type type)
83 {
84         return ((type & UCIMAP_SUBTYPE) == UCIMAP_CUSTOM);
85 }
86
87 static inline void *
88 ucimap_section_ptr(struct ucimap_section_data *sd)
89 {
90         return ((char *) sd - sd->sm->smap_offset);
91 }
92
93 static inline union ucimap_data *
94 ucimap_get_data(struct ucimap_section_data *sd, struct uci_optmap *om)
95 {
96         void *data;
97
98         data = (char *) ucimap_section_ptr(sd) + om->offset;
99         return data;
100 }
101
102 int
103 ucimap_init(struct uci_map *map)
104 {
105         INIT_LIST_HEAD(&map->sdata);
106         INIT_LIST_HEAD(&map->fixup);
107         return 0;
108 }
109
110 static void
111 ucimap_free_item(struct uci_alloc *a)
112 {
113         switch(a->type & UCIMAP_TYPE) {
114         case UCIMAP_SIMPLE:
115         case UCIMAP_LIST:
116                 free(a->data.ptr);
117                 break;
118         }
119 }
120
121 static void
122 ucimap_add_alloc(struct ucimap_section_data *sd, void *ptr)
123 {
124         struct uci_alloc *a = &sd->allocmap[sd->allocmap_len++];
125         a->type = UCIMAP_SIMPLE;
126         a->data.ptr = ptr;
127 }
128
129 static void
130 ucimap_free_section(struct uci_map *map, struct ucimap_section_data *sd)
131 {
132         void *section;
133         int i;
134
135         section = ucimap_section_ptr(sd);
136         if (!list_empty(&sd->list))
137                 list_del(&sd->list);
138
139         if (sd->sm->free)
140                 sd->sm->free(map, section);
141
142         for (i = 0; i < sd->allocmap_len; i++) {
143                 ucimap_free_item(&sd->allocmap[i]);
144         }
145
146         free(sd->allocmap);
147         free(sd);
148 }
149
150 void
151 ucimap_cleanup(struct uci_map *map)
152 {
153         struct list_head *ptr, *tmp;
154
155         list_for_each_safe(ptr, tmp, &map->sdata) {
156                 struct ucimap_section_data *sd = list_entry(ptr, struct ucimap_section_data, list);
157                 ucimap_free_section(map, sd);
158         }
159 }
160
161 static void
162 ucimap_add_fixup(struct uci_map *map, union ucimap_data *data, struct uci_optmap *om, const char *str)
163 {
164         struct uci_fixup *f;
165
166         f = malloc(sizeof(struct uci_fixup));
167         if (!f)
168                 return;
169
170         INIT_LIST_HEAD(&f->list);
171         f->sm = om->data.sm;
172         f->name = str;
173         f->type = om->type;
174         f->data = data;
175         list_add(&f->list, &map->fixup);
176 }
177
178 static void
179 ucimap_add_value(union ucimap_data *data, struct uci_optmap *om, struct ucimap_section_data *sd, const char *str)
180 {
181         union ucimap_data tdata = *data;
182         char *eptr = NULL;
183         long lval;
184         char *s;
185         int val;
186
187         if (ucimap_is_list(om->type) && !ucimap_is_fixup(om->type))
188                 data = &data->list->item[data->list->n_items++];
189
190         switch(om->type & UCIMAP_SUBTYPE) {
191         case UCIMAP_STRING:
192                 if ((om->data.s.maxlen > 0) &&
193                         (strlen(str) > om->data.s.maxlen))
194                         return;
195
196                 s = strdup(str);
197                 tdata.s = s;
198                 ucimap_add_alloc(sd, s);
199                 break;
200         case UCIMAP_BOOL:
201                 if (!strcmp(str, "on"))
202                         val = true;
203                 else if (!strcmp(str, "1"))
204                         val = true;
205                 else if (!strcmp(str, "enabled"))
206                         val = true;
207                 else if (!strcmp(str, "off"))
208                         val = false;
209                 else if (!strcmp(str, "0"))
210                         val = false;
211                 else if (!strcmp(str, "disabled"))
212                         val = false;
213                 else
214                         return;
215
216                 tdata.b = val;
217                 break;
218         case UCIMAP_INT:
219                 lval = strtol(str, &eptr, om->data.i.base);
220                 if (lval < INT_MIN || lval > INT_MAX)
221                         return;
222
223                 if (!eptr || *eptr == '\0')
224                         tdata.i = (int) lval;
225                 else
226                         return;
227                 break;
228         case UCIMAP_SECTION:
229                 ucimap_add_fixup(sd->map, data, om, str);
230                 return;
231         case UCIMAP_CUSTOM:
232                 tdata.s = (char *) data;
233                 break;
234         }
235         if (om->parse) {
236                 if (om->parse(ucimap_section_ptr(sd), om, &tdata, str) < 0)
237                         return;
238         }
239         if (ucimap_is_custom(om->type))
240                 return;
241         memcpy(data, &tdata, sizeof(union ucimap_data));
242 }
243
244
245 static int
246 ucimap_parse_options(struct uci_map *map, struct uci_sectionmap *sm, struct ucimap_section_data *sd, struct uci_section *s)
247 {
248         struct uci_element *e, *l;
249         struct uci_option *o;
250         union ucimap_data *data;
251
252         uci_foreach_element(&s->options, e) {
253                 struct uci_optmap *om = NULL, *tmp;
254
255                 ucimap_foreach_option(sm, tmp) {
256                         if (strcmp(e->name, tmp->name) == 0) {
257                                 om = tmp;
258                                 break;
259                         }
260                 }
261                 if (!om)
262                         continue;
263
264                 data = ucimap_get_data(sd, om);
265                 o = uci_to_option(e);
266                 if ((o->type == UCI_TYPE_STRING) && ucimap_is_simple(om->type)) {
267                         ucimap_add_value(data, om, sd, o->v.string);
268                 } else if ((o->type == UCI_TYPE_LIST) && ucimap_is_list(om->type)) {
269                         uci_foreach_element(&o->v.list, l) {
270                                 ucimap_add_value(data, om, sd, l->name);
271                         }
272                 }
273         }
274
275         return 0;
276 }
277
278
279 static int
280 ucimap_parse_section(struct uci_map *map, struct uci_sectionmap *sm, struct uci_section *s)
281 {
282         struct ucimap_section_data *sd = NULL;
283         struct uci_optmap *om;
284         char *section_name;
285         void *section;
286         int n_alloc = 2;
287         int err;
288
289         if (sm->alloc) {
290                 sd = sm->alloc(map, sm, s);
291                 memset(sd, 0, sizeof(struct ucimap_section_data));
292         } else {
293                 sd = malloc(sm->alloc_len);
294                 memset(sd, 0, sm->alloc_len);
295         }
296
297         if (!sd)
298                 return UCI_ERR_MEM;
299
300         INIT_LIST_HEAD(&sd->list);
301         sd->map = map;
302         sd->sm = sm;
303
304         ucimap_foreach_option(sm, om) {
305                 if (ucimap_is_list(om->type)) {
306                         union ucimap_data *data;
307                         struct uci_element *e;
308                         int n_elements = 0;
309                         int size;
310
311                         data = ucimap_get_data(sd, om);
312                         uci_foreach_element(&s->options, e) {
313                                 struct uci_option *o = uci_to_option(e);
314                                 struct uci_element *tmp;
315
316                                 if (strcmp(e->name, om->name) != 0)
317                                         continue;
318
319                                 uci_foreach_element(&o->v.list, tmp) {
320                                         n_elements++;
321                                 }
322                                 break;
323                         }
324                         n_alloc += n_elements + 1;
325                         size = sizeof(struct ucimap_list) +
326                                 n_elements * sizeof(union ucimap_data);
327                         data->list = malloc(size);
328                         memset(data->list, 0, size);
329                 } else if (ucimap_is_alloc(om->type)) {
330                         n_alloc++;
331                 }
332         }
333
334         sd->allocmap = malloc(n_alloc * sizeof(struct uci_alloc));
335         if (!sd->allocmap)
336                 goto error_mem;
337
338         section_name = strdup(s->e.name);
339         if (!section_name)
340                 goto error_mem;
341
342         sd->section_name = section_name;
343
344         sd->cmap = malloc(BITFIELD_SIZE(sm->n_options));
345         if (!sd->cmap)
346                 goto error_mem;
347
348         memset(sd->cmap, 0, BITFIELD_SIZE(sm->n_options));
349         ucimap_add_alloc(sd, (void *)section_name);
350         ucimap_add_alloc(sd, (void *)sd->cmap);
351         ucimap_foreach_option(sm, om) {
352                 if (!ucimap_is_list(om->type))
353                         continue;
354
355                 ucimap_add_alloc(sd, ucimap_get_data(sd, om)->list);
356         }
357
358         section = ucimap_section_ptr(sd);
359         err = sm->init(map, section, s);
360         if (err)
361                 goto error;
362
363         list_add(&sd->list, &map->sdata);
364         err = ucimap_parse_options(map, sm, sd, s);
365         if (err)
366                 goto error;
367
368         return 0;
369
370 error_mem:
371         if (sd->allocmap)
372                 free(sd->allocmap);
373         free(sd);
374         return UCI_ERR_MEM;
375
376 error:
377         ucimap_free_section(map, sd);
378         return err;
379 }
380
381 static int
382 ucimap_fill_ptr(struct uci_ptr *ptr, struct uci_section *s, const char *option)
383 {
384         struct uci_package *p = s->package;
385
386         memset(ptr, 0, sizeof(struct uci_ptr));
387
388         ptr->package = p->e.name;
389         ptr->p = p;
390
391         ptr->section = s->e.name;
392         ptr->s = s;
393
394         ptr->option = option;
395         return uci_lookup_ptr(p->ctx, ptr, NULL, false);
396 }
397
398 void
399 ucimap_set_changed(struct ucimap_section_data *sd, void *field)
400 {
401         void *section = ucimap_section_ptr(sd);
402         struct uci_sectionmap *sm = sd->sm;
403         struct uci_optmap *om;
404         int ofs = (char *)field - (char *)section;
405         int i = 0;
406
407         ucimap_foreach_option(sm, om) {
408                 if (om->offset == ofs) {
409                         SET_BIT(sd->cmap, i);
410                         break;
411                 }
412                 i++;
413         }
414 }
415
416 int
417 ucimap_store_section(struct uci_map *map, struct uci_package *p, struct ucimap_section_data *sd)
418 {
419         struct uci_sectionmap *sm = sd->sm;
420         struct uci_section *s = NULL;
421         struct uci_optmap *om;
422         struct uci_element *e;
423         struct uci_ptr ptr;
424         int i = 0;
425         int ret;
426
427         uci_foreach_element(&p->sections, e) {
428                 if (!strcmp(e->name, sd->section_name)) {
429                         s = uci_to_section(e);
430                         break;
431                 }
432         }
433         if (!s)
434                 return UCI_ERR_NOTFOUND;
435
436         ucimap_foreach_option(sm, om) {
437                 union ucimap_data *data;
438                 static char buf[32];
439                 char *str = NULL;
440
441                 i++;
442                 if (ucimap_is_list(om->type))
443                         continue;
444
445                 data = ucimap_get_data(sd, om);
446                 if (!TEST_BIT(sd->cmap, i - 1))
447                         continue;
448
449                 ucimap_fill_ptr(&ptr, s, om->name);
450                 switch(om->type & UCIMAP_SUBTYPE) {
451                 case UCIMAP_STRING:
452                         str = data->s;
453                         break;
454                 case UCIMAP_INT:
455                         sprintf(buf, "%d", data->i);
456                         str = buf;
457                         break;
458                 case UCIMAP_BOOL:
459                         sprintf(buf, "%d", !!data->b);
460                         str = buf;
461                         break;
462                 case UCIMAP_CUSTOM:
463                         break;
464                 default:
465                         continue;
466                 }
467                 if (om->format) {
468                         union ucimap_data tdata, *data;
469
470                         data = ucimap_get_data(sd, om);
471                         if (ucimap_is_custom(om->type)) {
472                                 tdata.s = (char *)data;
473                                 data = &tdata;
474                         }
475
476                         if (om->format(ucimap_section_ptr(sd), om, data, &str) < 0)
477                                 continue;
478                 }
479                 if (!str)
480                         continue;
481                 ptr.value = str;
482
483                 ret = uci_set(s->package->ctx, &ptr);
484                 if (ret)
485                         return ret;
486
487                 CLR_BIT(sd->cmap, i - 1);
488         }
489
490         return 0;
491 }
492
493 void *
494 ucimap_find_section(struct uci_map *map, struct uci_fixup *f)
495 {
496         struct ucimap_section_data *sd;
497         struct list_head *p;
498
499         list_for_each(p, &map->sdata) {
500                 sd = list_entry(p, struct ucimap_section_data, list);
501                 if (sd->sm != f->sm)
502                         continue;
503                 if (strcmp(f->name, sd->section_name) != 0)
504                         continue;
505                 return ucimap_section_ptr(sd);
506         }
507         return NULL;
508 }
509
510 void
511 ucimap_parse(struct uci_map *map, struct uci_package *pkg)
512 {
513         struct uci_element *e;
514         struct list_head *p, *tmp;
515         int i;
516
517         INIT_LIST_HEAD(&map->fixup);
518         uci_foreach_element(&pkg->sections, e) {
519                 struct uci_section *s = uci_to_section(e);
520
521                 for (i = 0; i < map->n_sections; i++) {
522                         if (strcmp(s->type, map->sections[i]->type) != 0)
523                                 continue;
524                         ucimap_parse_section(map, map->sections[i], s);
525                 }
526         }
527         list_for_each_safe(p, tmp, &map->fixup) {
528                 struct uci_fixup *f = list_entry(p, struct uci_fixup, list);
529                 void *ptr = ucimap_find_section(map, f);
530                 struct ucimap_list *list;
531
532                 if (!ptr)
533                         continue;
534
535                 switch(f->type & UCIMAP_TYPE) {
536                 case UCIMAP_SIMPLE:
537                         f->data->section = ptr;
538                         break;
539                 case UCIMAP_LIST:
540                         list = f->data->list;
541                         list->item[list->n_items++].section = ptr;
542                         break;
543                 }
544                 free(f);
545         }
546         list_for_each_safe(p, tmp, &map->sdata) {
547                 struct ucimap_section_data *sd = list_entry(p, struct ucimap_section_data, list);
548                 void *section;
549
550                 if (sd->done)
551                         continue;
552
553                 section = ucimap_section_ptr(sd);
554                 if (sd->sm->add(map, section) != 0)
555                         ucimap_free_section(map, sd);
556         }
557 }