1 /*
2 INI LIBRARY
3
4 Module represents interface to the main INI object.
5
6 Copyright (C) Dmitri Pal <dpal@redhat.com> 2010
7
8 INI Library is free software: you can redistribute it and/or modify
9 it under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
12
13 INI Library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with INI Library. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include "config.h"
23 #include <sys/types.h>
24 #include <regex.h>
25 #include <errno.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <stdint.h>
29 #include <stdlib.h>
30 #include <stdarg.h>
31 /* For error text */
32 #include <libintl.h>
33 #define _(String) gettext (String)
34 #include "trace.h"
35 #include "collection.h"
36 #include "collection_tools.h"
37 #include "ini_configobj.h"
38 #include "ini_config_priv.h"
39 #include "ini_defines.h"
40 #include "ini_valueobj.h"
41 #include "ini_configobj.h"
42
43 /* Internal structure used during the merge operation */
44 struct merge_data {
45 struct collection_item *ci;
46 uint32_t flags;
47 int error;
48 int found;
49 };
50
51 /* Callback */
ini_cleanup_cb(const char * property,int property_len,int type,void * data,int length,void * custom_data)52 void ini_cleanup_cb(const char *property,
53 int property_len,
54 int type,
55 void *data,
56 int length,
57 void *custom_data)
58 {
59 struct value_obj *vo = NULL;
60
61 TRACE_FLOW_ENTRY();
62 TRACE_INFO_STRING("Cleaning ", property);
63
64 /* Banary items are the values */
65 if(type == COL_TYPE_BINARY) {
66 vo = *((struct value_obj **)(data));
67 value_destroy(vo);
68 }
69
70 TRACE_FLOW_EXIT();
71 }
72
73 /* Clean the search state */
ini_config_clean_state(struct ini_cfgobj * ini_config)74 void ini_config_clean_state(struct ini_cfgobj *ini_config)
75 {
76 TRACE_FLOW_ENTRY();
77
78 if (ini_config) {
79 if (ini_config->iterator) col_unbind_iterator(ini_config->iterator);
80 ini_config->iterator = NULL;
81 free(ini_config->section);
82 ini_config->section = NULL;
83 free(ini_config->name);
84 ini_config->name = NULL;
85 ini_config->section_len = 0;
86 ini_config->name_len = 0;
87 }
88
89 TRACE_FLOW_EXIT();
90 }
91
92
93
94 /* Traverse the collection and clean the object */
ini_config_destroy(struct ini_cfgobj * ini_config)95 void ini_config_destroy(struct ini_cfgobj *ini_config)
96 {
97 TRACE_FLOW_ENTRY();
98
99 ini_config_clean_state(ini_config);
100
101 if (ini_config) {
102 if (ini_config->cfg) {
103
104 col_destroy_collection_with_cb(ini_config->cfg,
105 ini_cleanup_cb,
106 NULL);
107 }
108 if (ini_config->last_comment) {
109 ini_comment_destroy(ini_config->last_comment);
110 }
111
112 if (ini_config->error_list) {
113 col_destroy_collection(ini_config->error_list);
114 }
115
116 free(ini_config);
117 }
118
119 TRACE_FLOW_EXIT();
120 }
121
122 /* Create a config object */
ini_config_create(struct ini_cfgobj ** ini_config)123 int ini_config_create(struct ini_cfgobj **ini_config)
124 {
125 int error = EOK;
126 struct ini_cfgobj *new_co = NULL;
127
128 TRACE_FLOW_ENTRY();
129
130 if (!ini_config) {
131 TRACE_ERROR_NUMBER("Invalid argument", EINVAL);
132 return EINVAL;
133 }
134
135 new_co = malloc(sizeof(struct ini_cfgobj));
136 if (!new_co) {
137 TRACE_ERROR_NUMBER("Failed to allocate memory", ENOMEM);
138 return ENOMEM;
139 }
140
141 new_co->cfg = NULL;
142 new_co->boundary = INI_WRAP_BOUNDARY;
143 new_co->last_comment = NULL;
144 new_co->section = NULL;
145 new_co->name = NULL;
146 new_co->section_len = 0;
147 new_co->name_len = 0;
148 new_co->iterator = NULL;
149 new_co->error_list = NULL;
150 new_co->count = 0;
151
152 /* Create a collection to hold configuration data */
153 error = col_create_collection(&(new_co->cfg),
154 INI_CONFIG_NAME,
155 COL_CLASS_INI_CONFIG);
156 if (error != EOK) {
157 TRACE_ERROR_NUMBER("Failed to create collection.", error);
158 ini_config_destroy(new_co);
159 return error;
160 }
161
162 /* Create error list collection */
163 error = col_create_collection(&(new_co->error_list),
164 INI_ERROR,
165 COL_CLASS_INI_PERROR);
166 if (error) {
167 TRACE_ERROR_NUMBER("Failed to create error list", error);
168 ini_config_destroy(new_co);
169 return error;
170 }
171
172 *ini_config = new_co;
173
174 TRACE_FLOW_EXIT();
175 return error;
176 }
177
178 /* Callback to set the boundary */
ini_boundary_cb(const char * property,int property_len,int type,void * data,int length,void * custom_data,int * dummy)179 static int ini_boundary_cb(const char *property,
180 int property_len,
181 int type,
182 void *data,
183 int length,
184 void *custom_data,
185 int *dummy)
186 {
187 int error = EOK;
188 struct value_obj *vo = NULL;
189 uint32_t boundary;
190
191 TRACE_FLOW_ENTRY();
192
193 boundary = *((uint32_t *)(custom_data));
194 /* Banary items are the values */
195 if(type == COL_TYPE_BINARY) {
196 vo = *((struct value_obj **)(data));
197 error = value_set_boundary(vo, boundary);
198 }
199
200 TRACE_FLOW_EXIT();
201 return error;
202 }
203
204 /* Set the folding boundary for multiline values.
205 * Use before serializing and saving to a file if the
206 * default boundary of 80 characters does not work for you.
207 */
ini_config_set_wrap(struct ini_cfgobj * ini_config,uint32_t boundary)208 int ini_config_set_wrap(struct ini_cfgobj *ini_config,
209 uint32_t boundary)
210 {
211 int error = EOK;
212
213 TRACE_FLOW_ENTRY();
214
215 if (!ini_config) {
216 TRACE_ERROR_NUMBER("Invalid argument", EINVAL);
217 return EINVAL;
218 }
219
220 ini_config->boundary = boundary;
221 error = col_traverse_collection(ini_config->cfg,
222 COL_TRAVERSE_DEFAULT,
223 ini_boundary_cb,
224 (void *)(&(ini_config->boundary)));
225 if (error) {
226 TRACE_ERROR_NUMBER("Failed to set wrapping boundary", error);
227 return error;
228 }
229
230
231 TRACE_FLOW_EXIT();
232 return error;
233 }
234
235 /* Configuration copy callback */
ini_copy_cb(struct collection_item * item,void * ext_data,int * skip)236 static int ini_copy_cb(struct collection_item *item,
237 void *ext_data,
238 int *skip)
239 {
240 int error = EOK;
241 struct value_obj *vo = NULL;
242 struct value_obj *new_vo = NULL;
243
244 TRACE_FLOW_ENTRY();
245
246 *skip = 0;
247
248 /* Binary items are the values */
249 if(col_get_item_type(item) == COL_TYPE_BINARY) {
250 vo = *((struct value_obj **)(col_get_item_data(item)));
251
252 error = value_copy(vo, &new_vo);
253 if (error) {
254 TRACE_ERROR_NUMBER("Failed to copy value", error);
255 return error;
256 }
257
258 error = col_modify_binary_item(item,
259 NULL,
260 &new_vo,
261 sizeof(struct value_obj *));
262 if (error) {
263 TRACE_ERROR_NUMBER("Failed to copy value", error);
264 value_destroy(new_vo);
265 return error;
266 }
267 }
268
269 TRACE_FLOW_EXIT();
270 return error;
271 }
272
273 /* Check flags for flag */
ini_flags_have(uint32_t flag,uint32_t flags)274 int ini_flags_have(uint32_t flag, uint32_t flags)
275 {
276 switch (flag) {
277 case INI_MS_MERGE:
278 case INI_MS_ERROR:
279 case INI_MS_OVERWRITE:
280 case INI_MS_PRESERVE:
281 return flag == (flags & INI_MS_MODE_MASK);
282 case INI_MS_DETECT:
283 return flag == (flags & INI_MS_DETECT);
284 default:
285 TRACE_ERROR_NUMBER("Unsupported flag", flag);
286 }
287 return 0;
288 }
289
290 /* Copy configuration */
ini_config_copy(struct ini_cfgobj * ini_config,struct ini_cfgobj ** ini_new)291 int ini_config_copy(struct ini_cfgobj *ini_config,
292 struct ini_cfgobj **ini_new)
293 {
294 int error = EOK;
295 struct ini_cfgobj *new_co = NULL;
296
297 TRACE_FLOW_ENTRY();
298
299 if ((!ini_config) ||
300 (!ini_new)) {
301 TRACE_ERROR_NUMBER("Invalid argument", EINVAL);
302 return EINVAL;
303 }
304
305 /* Create a new configuration object */
306 new_co = malloc(sizeof(struct ini_cfgobj));
307 if (!new_co) {
308 TRACE_ERROR_NUMBER("Failed to allocate memory", ENOMEM);
309 return ENOMEM;
310 }
311
312 new_co->cfg = NULL;
313 new_co->boundary = ini_config->boundary;
314 new_co->last_comment = NULL;
315 new_co->section = NULL;
316 new_co->name = NULL;
317 new_co->section_len = 0;
318 new_co->name_len = 0;
319 new_co->iterator = NULL;
320 new_co->error_list = NULL;
321 new_co->count = 0;
322
323 error = col_copy_collection_with_cb(&(new_co->cfg),
324 ini_config->cfg,
325 INI_CONFIG_NAME,
326 COL_COPY_NORMAL,
327 ini_copy_cb,
328 NULL);
329 if (error) {
330 TRACE_ERROR_NUMBER("Failed to copy collection", error);
331 ini_config_destroy(new_co);
332 return error;
333 }
334
335 if (ini_config->last_comment) {
336 error = ini_comment_copy(ini_config->last_comment,
337 &(new_co->last_comment));
338 if (error) {
339 TRACE_ERROR_NUMBER("Failed to copy comment", error);
340 ini_config_destroy(new_co);
341 return error;
342 }
343 }
344
345 *ini_new = new_co;
346
347 TRACE_FLOW_EXIT();
348 return error;
349 }
350
351
352 /* Callback to process merging of the sections */
merge_section_handler(const char * property,int property_len,int type,void * data,int length,void * custom_data,int * dummy)353 static int merge_section_handler(const char *property,
354 int property_len,
355 int type,
356 void *data,
357 int length,
358 void *custom_data,
359 int *dummy)
360 {
361 int error = EOK;
362 struct value_obj *vo = NULL;
363 struct value_obj *new_vo = NULL;
364 struct value_obj *vo_old = NULL;
365 struct merge_data *passed_data;
366 struct collection_item *acceptor = NULL;
367 struct collection_item *item = NULL;
368 unsigned insertmode;
369 uint32_t mergemode;
370 int suppress = 0;
371 int doinsert = 0;
372
373 TRACE_FLOW_ENTRY();
374
375 if ((type != COL_TYPE_BINARY) ||
376 ((type == COL_TYPE_BINARY) &&
377 (strncmp(property, INI_SECTION_KEY,
378 sizeof(INI_SECTION_KEY)) == 0))) {
379 /* Skip items we do not care about */
380 TRACE_FLOW_EXIT();
381 return EOK;
382 }
383
384 /* Get value */
385 vo = *((struct value_obj **)(data));
386
387 /* Copy it */
388 error = value_copy(vo, &new_vo);
389 if (error) {
390 TRACE_ERROR_NUMBER("Failed to copy value", error);
391 return error;
392 }
393
394 passed_data = (struct merge_data *)(custom_data);
395 acceptor = passed_data->ci;
396 mergemode = passed_data->flags & INI_MV2S_MASK;
397
398 switch (mergemode) {
399 case INI_MV2S_ERROR: insertmode = COL_INSERT_DUPERROR;
400 doinsert = 1;
401 break;
402 case INI_MV2S_PRESERVE: insertmode = COL_INSERT_DUPERROR;
403 doinsert = 1;
404 suppress = 1;
405 break;
406 case INI_MV2S_ALLOW: insertmode = COL_INSERT_NOCHECK;
407 doinsert = 1;
408 break;
409 case INI_MV2S_OVERWRITE: /* Special handling */
410 case INI_MV2S_DETECT:
411 default:
412 break;
413 }
414
415 /* Do not insert but search for dups first */
416 if (!doinsert) {
417 TRACE_INFO_STRING("Overwrite mode. Looking for:",
418 property);
419
420 error = col_get_item(acceptor,
421 property,
422 COL_TYPE_BINARY,
423 COL_TRAVERSE_DEFAULT,
424 &item);
425
426 if (error) {
427 TRACE_ERROR_NUMBER("Failed searching for dup", error);
428 value_destroy(new_vo);
429 return error;
430 }
431
432 /* Check if there is a dup */
433 if (item) {
434 /* Check if we are in the detect mode */
435 if (mergemode == INI_MV2S_DETECT) {
436 passed_data->error = EEXIST;
437 doinsert = 1;
438 insertmode = COL_INSERT_NOCHECK;
439 }
440 else {
441
442 /* We are in the OVERWRITE mode.
443 * Dup exists - update it.
444 */
445 vo_old = *((struct value_obj **)(col_get_item_data(item)));
446 error = col_modify_binary_item(item,
447 NULL,
448 &new_vo,
449 sizeof(struct value_obj *));
450 if (error) {
451 TRACE_ERROR_NUMBER("Failed updating the value", error);
452 value_destroy(new_vo);
453 return error;
454 }
455
456 /* If we failed to update it is better to leak then crash,
457 * so destroy original value only on the successful update.
458 */
459 value_destroy(vo_old);
460 }
461 }
462 else {
463 /* No dup found so we can insert with no check */
464 doinsert = 1;
465 insertmode = COL_INSERT_NOCHECK;
466 }
467 }
468
469 if (doinsert) {
470 /* Add value to collection */
471 error = col_insert_binary_property(acceptor,
472 NULL,
473 COL_DSP_END,
474 NULL,
475 0,
476 insertmode,
477 property,
478 &new_vo,
479 sizeof(struct value_obj *));
480 if (error) {
481 value_destroy(new_vo);
482
483 if ((suppress) && (error == EEXIST)) {
484 /* We are here is we do not allow dups
485 * but found one and need to ignore it.
486 */
487 TRACE_INFO_STRING("Preseved exisitng value",
488 property);
489 error = 0;
490 }
491 else {
492 /* Check if this is a critical error or not */
493 if ((mergemode == INI_MV2S_ERROR) && (error == EEXIST)) {
494 TRACE_ERROR_NUMBER("Failed to add value object to "
495 "the section in error mode ", error);
496 passed_data->error = EEXIST;
497 *dummy = 1;
498 }
499 else {
500 TRACE_ERROR_NUMBER("Failed to add value object"
501 " to the section", error);
502 return error;
503 }
504 }
505 }
506 }
507
508 TRACE_FLOW_EXIT();
509 return error;
510 }
511
512
513 /* Internal function to merge two configs */
merge_two_sections(struct collection_item * donor,struct collection_item * acceptor,uint32_t flags)514 static int merge_two_sections(struct collection_item *donor,
515 struct collection_item *acceptor,
516 uint32_t flags)
517 {
518 int error = EOK;
519 struct merge_data data;
520
521 TRACE_FLOW_ENTRY();
522
523 data.ci = acceptor;
524 data.flags = flags;
525 data.error = 0;
526 data.found = 0;
527
528 error = col_traverse_collection(donor,
529 COL_TRAVERSE_ONELEVEL,
530 merge_section_handler,
531 (void *)(&data));
532 if (error) {
533 TRACE_ERROR_NUMBER("Merge values failed", error);
534 return error;
535 }
536
537 TRACE_FLOW_EXIT();
538 return data.error;
539 }
540
541
542
543 /* Callback to process the accepting config */
acceptor_handler(const char * property,int property_len,int type,void * data,int length,void * custom_data,int * dummy)544 static int acceptor_handler(const char *property,
545 int property_len,
546 int type,
547 void *data,
548 int length,
549 void *custom_data,
550 int *dummy)
551 {
552 int error = EOK;
553 struct merge_data *passed_data;
554 struct collection_item *acceptor = NULL;
555 struct collection_item *donor = NULL;
556 uint32_t mergemode;
557
558 TRACE_FLOW_ENTRY();
559
560 /* This callback is called when the dup section is found */
561 passed_data = (struct merge_data *)(custom_data);
562 passed_data->found = 1;
563
564 donor = passed_data->ci;
565 acceptor = *((struct collection_item **)(data));
566
567 mergemode = passed_data->flags & INI_MS_MODE_MASK;
568
569 if (passed_data->flags & INI_MS_DETECT) {
570 TRACE_INFO_STRING("Detect mode", "");
571 passed_data->error = EEXIST;
572 }
573
574 switch (mergemode) {
575 case INI_MS_ERROR: /* Report error and return */
576 TRACE_INFO_STRING("Error ",
577 "duplicate section");
578 passed_data->error = EEXIST;
579 break;
580
581 case INI_MS_PRESERVE: /* Preserve what we have */
582 TRACE_INFO_STRING("Preserve mode", "");
583 break;
584
585 case INI_MS_OVERWRITE: /* Empty existing section */
586 TRACE_INFO_STRING("Ovewrite mode", "");
587 error = empty_section(acceptor);
588 if (error) {
589 TRACE_ERROR_NUMBER("Failed to "
590 "empty section",
591 error);
592 return error;
593 }
594 error = merge_two_sections(donor,
595 acceptor,
596 passed_data->flags);
597 if (error) {
598 TRACE_ERROR_NUMBER("Failed to merge "
599 "sections", error);
600 if (error == EEXIST) {
601 passed_data->error = error;
602 }
603 return error;
604 }
605 break;
606
607 case INI_MS_MERGE: /* Merge */
608 default: TRACE_INFO_STRING("Merge mode", "");
609 error = merge_two_sections(donor,
610 acceptor,
611 passed_data->flags);
612 if (error) {
613 if (error != EEXIST) {
614 TRACE_ERROR_NUMBER("Failed to merge "
615 "sections", error);
616 return error;
617 }
618
619 if (!(passed_data->flags & INI_MS_DETECT)) {
620 passed_data->error = error;
621 }
622
623 error = EOK;
624 }
625 break;
626 }
627
628 if (error == EOK) {
629 *dummy = 1;
630 }
631
632 TRACE_FLOW_EXIT();
633 return error;
634 }
635
636 /* Callback to process the donating config */
donor_handler(const char * property,int property_len,int type,void * data,int length,void * custom_data,int * dummy)637 static int donor_handler(const char *property,
638 int property_len,
639 int type,
640 void *data,
641 int length,
642 void *custom_data,
643 int *dummy)
644 {
645 int error = EOK;
646 struct merge_data *passed_data;
647 struct merge_data acceptor_data;
648 struct collection_item *new_ci = NULL;
649
650 TRACE_FLOW_ENTRY();
651
652 *dummy = 0;
653
654 /* Opaque data passed to callback is merge data */
655 passed_data = (struct merge_data *)(custom_data);
656
657 TRACE_INFO_STRING("Property: ", property);
658 TRACE_INFO_NUMBER("Type is: ", type);
659 TRACE_INFO_NUMBER("Flags: ", passed_data->flags);
660
661 /* All sections are subcollections */
662 if(type == COL_TYPE_COLLECTIONREF) {
663
664 /* Prepare data for the next callback */
665 acceptor_data.flags = passed_data->flags;
666 acceptor_data.ci = *((struct collection_item **)(data));
667 acceptor_data.error = 0;
668 acceptor_data.found = 0;
669
670 /* Try to find same section as the current one */
671 error = col_get_item_and_do(passed_data->ci,
672 property,
673 COL_TYPE_COLLECTIONREF,
674 COL_TRAVERSE_ONELEVEL,
675 acceptor_handler,
676 (void *)(&acceptor_data));
677 if (error) {
678 TRACE_ERROR_NUMBER("Critical error", error);
679 return error;
680 }
681
682 /* Was duplicate found ? */
683 if (acceptor_data.found) {
684 /* Check for logical error. It can be only EEXIST */
685 if (acceptor_data.error) {
686 /* Save error anyway */
687 passed_data->error = acceptor_data.error;
688 /* If it is section DETECT or MERGE+DETECT */
689 if (ini_flags_have(INI_MS_DETECT, passed_data->flags) ||
690 (!ini_flags_have(INI_MS_ERROR, passed_data->flags) &&
691 ((passed_data->flags & INI_MV2S_MASK) ==
692 INI_MV2S_DETECT))) {
693 TRACE_INFO_NUMBER("Non-critical error",
694 acceptor_data.error);
695 }
696 else {
697 /* In any other mode we need to stop */
698 TRACE_INFO_NUMBER("Merge error detected",
699 acceptor_data.error);
700 /* Force stop */
701 *dummy = 1;
702 }
703 }
704 }
705 else {
706 /* Not found? Then create a copy... */
707 error = col_copy_collection_with_cb(&new_ci,
708 acceptor_data.ci,
709 NULL,
710 COL_COPY_NORMAL,
711 ini_copy_cb,
712 NULL);
713 if (error) {
714 TRACE_ERROR_NUMBER("Failed to copy collection", error);
715 return error;
716 }
717
718 /* ... and embed into the existing collection */
719 error = col_add_collection_to_collection(passed_data->ci,
720 NULL,
721 NULL,
722 new_ci,
723 COL_ADD_MODE_EMBED);
724 if (error) {
725 TRACE_ERROR_NUMBER("Failed to copy collection", error);
726 col_destroy_collection(new_ci);
727 return error;
728 }
729 }
730 }
731
732 TRACE_FLOW_EXIT();
733 return EOK;
734 }
735
merge_comment(struct ini_cfgobj * donor,struct ini_cfgobj * acceptor)736 static int merge_comment(struct ini_cfgobj *donor,
737 struct ini_cfgobj *acceptor)
738 {
739 int error = EOK;
740
741 TRACE_FLOW_ENTRY();
742
743 if (donor->last_comment) {
744
745 if (acceptor->last_comment) {
746
747 error = ini_comment_add(donor->last_comment,
748 acceptor->last_comment);
749 if (error) {
750 TRACE_ERROR_NUMBER("Merge comment failed", error);
751 return error;
752 }
753
754 }
755 else {
756 error = ini_comment_copy(donor->last_comment,
757 &(acceptor->last_comment));
758 if (error) {
759 TRACE_ERROR_NUMBER("Copy comment failed", error);
760 return error;
761 }
762 }
763 }
764
765 TRACE_FLOW_EXIT();
766 return EOK;
767 }
768
769
770
771 /* Internal function to merge two configs */
merge_configs(struct ini_cfgobj * donor,struct ini_cfgobj * acceptor,uint32_t collision_flags)772 static int merge_configs(struct ini_cfgobj *donor,
773 struct ini_cfgobj *acceptor,
774 uint32_t collision_flags)
775 {
776 int error = EOK;
777 struct merge_data data;
778
779 TRACE_FLOW_ENTRY();
780
781 data.ci = acceptor->cfg;
782 data.flags = collision_flags;
783 data.error = 0;
784 data.found = 0;
785
786 /* Loop through the donor collection calling
787 * donor_handler callback for every section we find.
788 */
789 error = col_traverse_collection(donor->cfg,
790 COL_TRAVERSE_ONELEVEL,
791 donor_handler,
792 (void *)(&data));
793 if (error) {
794 TRACE_ERROR_NUMBER("Merge failed", error);
795 return error;
796 }
797
798 /* Check if we got error */
799 if ((data.error) &&
800 (ini_flags_have(INI_MS_ERROR, collision_flags) ||
801 ((collision_flags & INI_MV2S_MASK) == INI_MV2S_ERROR))) {
802 TRACE_ERROR_NUMBER("Got error in error mode", data.error);
803 return data.error;
804 }
805
806 /* If boundaries are different re-align the values */
807 if (acceptor->boundary != donor->boundary) {
808 error = ini_config_set_wrap(acceptor, acceptor->boundary);
809 if (error) {
810 TRACE_ERROR_NUMBER("Failed to re-align", error);
811 return error;
812 }
813 }
814
815 /* Merge last comment */
816 error = merge_comment(donor, acceptor);
817 if (error) {
818 TRACE_ERROR_NUMBER("Failed to merge comment", error);
819 return error;
820 }
821
822 /* Check if we got error */
823 if ((data.error) &&
824 (ini_flags_have(INI_MS_DETECT, collision_flags) ||
825 ((collision_flags & INI_MV2S_MASK) == INI_MV2S_DETECT))) {
826 TRACE_ERROR_NUMBER("Got error in error or detect mode", data.error);
827 error = data.error;
828 }
829
830 TRACE_FLOW_EXIT();
831 return error;
832 }
833
834 /* Check if collision flags are valid */
valid_collision_flags(uint32_t collision_flags)835 int valid_collision_flags(uint32_t collision_flags)
836 {
837 uint32_t flag;
838
839 TRACE_FLOW_ENTRY();
840
841 flag = collision_flags & INI_MV1S_MASK;
842 if ((flag != INI_MV1S_OVERWRITE) &&
843 (flag != INI_MV1S_ERROR) &&
844 (flag != INI_MV1S_PRESERVE) &&
845 (flag != INI_MV1S_ALLOW) &&
846 (flag != INI_MV1S_DETECT)) {
847 TRACE_ERROR_STRING("Invalid value collision flag","");
848 return 0;
849 }
850
851 flag = collision_flags & INI_MV2S_MASK;
852 if ((flag != INI_MV2S_OVERWRITE) &&
853 (flag != INI_MV2S_ERROR) &&
854 (flag != INI_MV2S_PRESERVE) &&
855 (flag != INI_MV2S_ALLOW) &&
856 (flag != INI_MV2S_DETECT)) {
857 TRACE_ERROR_STRING("Invalid value cross-section collision flag","");
858 return 0;
859 }
860
861 /* Any combination of DETECT and a MODE flag is valid. */
862 flag = collision_flags & INI_MS_MODE_MASK;
863 if ((flag != INI_MS_MERGE) &&
864 (flag != INI_MS_OVERWRITE) &&
865 (flag != INI_MS_ERROR) &&
866 (flag != INI_MS_PRESERVE)) {
867 TRACE_ERROR_STRING("Invalid section collision flag","");
868 return 0;
869 }
870
871 TRACE_FLOW_EXIT();
872 return 1;
873 }
874
875 /* Merge two configurations together creating a new one */
ini_config_merge(struct ini_cfgobj * first,struct ini_cfgobj * second,uint32_t collision_flags,struct ini_cfgobj ** result)876 int ini_config_merge(struct ini_cfgobj *first,
877 struct ini_cfgobj *second,
878 uint32_t collision_flags,
879 struct ini_cfgobj **result)
880 {
881 int error = EOK;
882 struct ini_cfgobj *new_co = NULL;
883
884 TRACE_FLOW_ENTRY();
885
886 /* Check input params */
887 if ((!first) ||
888 (!second) ||
889 (!result)) {
890 TRACE_ERROR_NUMBER("Invalid argument", EINVAL);
891 return EINVAL;
892 }
893
894 /* Check collision flags */
895 if (!valid_collision_flags(collision_flags)) {
896 TRACE_ERROR_NUMBER("Invalid flags.", EINVAL);
897 return EINVAL;
898 }
899
900 /* NOTE: We assume that the configuration we merge to
901 * is consistent regarding duplicate values.
902 * For example, if the duplicates are not allowed,
903 * the parsing function should have been instructed
904 * to not allow duplicates.
905 * If in future we decide to be explicite we would need
906 * to introduce a "compacting" function and call it here
907 * after we create a copy.
908 * For now it is treated as a corner case and thus not worth
909 * implementing.
910 */
911
912 /* Create a new config object */
913 error = ini_config_copy(first, &new_co);
914 if (error) {
915 TRACE_ERROR_NUMBER("Failed to copy configuration", error);
916 return error;
917 }
918
919 /* Merge configs */
920 error = merge_configs(second, new_co, collision_flags);
921 if (error) {
922 TRACE_ERROR_NUMBER("Failed to merge configuration", error);
923 if ((error == EEXIST) &&
924 ((ini_flags_have(INI_MS_DETECT, collision_flags) &&
925 ((collision_flags & INI_MV2S_MASK) != INI_MV2S_ERROR)) ||
926 (!ini_flags_have(INI_MS_ERROR, collision_flags) &&
927 ((collision_flags & INI_MV2S_MASK) == INI_MV2S_DETECT)))) {
928 TRACE_ERROR_NUMBER("Got error in detect mode", error);
929 /* Fall through! */
930 }
931 else {
932 /* Got an error in any other mode */
933 TRACE_ERROR_NUMBER("Got error in non detect mode", error);
934 ini_config_destroy(new_co);
935 return error;
936 }
937 }
938
939 *result = new_co;
940 TRACE_FLOW_EXIT();
941 return error;
942
943 }
944
945 /* How many errors do we have in the list ? */
ini_config_error_count(struct ini_cfgobj * cfg_ctx)946 unsigned ini_config_error_count(struct ini_cfgobj *cfg_ctx)
947 {
948 unsigned count = 0;
949
950 TRACE_FLOW_ENTRY();
951
952 count = cfg_ctx->count;
953
954 TRACE_FLOW_EXIT();
955 return count;
956
957 }
958
959 /* Free error strings */
ini_config_free_errors(char ** errors)960 void ini_config_free_errors(char **errors)
961 {
962 TRACE_FLOW_ENTRY();
963
964 col_free_property_list(errors);
965
966 TRACE_FLOW_EXIT();
967 }
968
969 /* Get the list of error strings */
ini_config_get_errors(struct ini_cfgobj * cfg_ctx,char *** errors)970 int ini_config_get_errors(struct ini_cfgobj *cfg_ctx,
971 char ***errors)
972 {
973 char **errlist = NULL;
974 struct collection_iterator *iterator = NULL;
975 int error;
976 struct collection_item *item = NULL;
977 struct ini_parse_error *pe;
978 unsigned int count = 0;
979 char *line;
980
981 TRACE_FLOW_ENTRY();
982
983 /* If we have something to print print it */
984 if ((!errors) || (!cfg_ctx)) {
985 TRACE_ERROR_NUMBER("Invalid parameter.", EINVAL);
986 return EINVAL;
987 }
988
989 errlist = calloc(cfg_ctx->count + 1, sizeof(char *));
990 if (!errlist) {
991 TRACE_ERROR_NUMBER("Failed to allocate memory for errors.", ENOMEM);
992 return ENOMEM;
993 }
994
995 /* Bind iterator */
996 error = col_bind_iterator(&iterator,
997 cfg_ctx->error_list,
998 COL_TRAVERSE_DEFAULT);
999 if (error) {
1000 TRACE_ERROR_NUMBER("Faile to bind iterator:", error);
1001 ini_config_free_errors(errlist);
1002 return error;
1003 }
1004
1005 while(1) {
1006 /* Loop through a collection */
1007 error = col_iterate_collection(iterator, &item);
1008 if (error) {
1009 TRACE_ERROR_NUMBER("Error iterating collection", error);
1010 col_unbind_iterator(iterator);
1011 ini_config_free_errors(errlist);
1012 return error;
1013 }
1014
1015 /* Are we done ? */
1016 if (item == NULL) break;
1017
1018 /* Process collection header */
1019 if (col_get_item_type(item) == COL_TYPE_COLLECTION) {
1020 continue;
1021 }
1022 else {
1023 /* Put error into provided format */
1024 pe = (struct ini_parse_error *)(col_get_item_data(item));
1025
1026 /* Would be nice to have asprintf function...
1027 * ...but for now we know that all the errors
1028 * are pretty short and will fir into the predefined
1029 * error length buffer.
1030 */
1031 line = malloc(MAX_ERROR_LINE + 1);
1032 if (!line) {
1033 TRACE_ERROR_NUMBER("Failed to get memory for error.", ENOMEM);
1034 col_unbind_iterator(iterator);
1035 ini_config_free_errors(errlist);
1036 return ENOMEM;
1037 }
1038
1039 snprintf(line, MAX_ERROR_LINE, LINE_FORMAT,
1040 col_get_item_property(item, NULL),
1041 pe->error,
1042 pe->line,
1043 ini_get_error_str(pe->error,
1044 INI_FAMILY_PARSING));
1045
1046 errlist[count] = line;
1047 count++;
1048 }
1049
1050 }
1051
1052 /* Do not forget to unbind iterator - otherwise there will be a leak */
1053 col_unbind_iterator(iterator);
1054
1055 *errors = errlist;
1056
1057 TRACE_FLOW_EXIT();
1058 return error;
1059 }
1060
ini_rules_read_from_file(const char * filename,struct ini_cfgobj ** _rules_obj)1061 int ini_rules_read_from_file(const char *filename,
1062 struct ini_cfgobj **_rules_obj)
1063 {
1064 int ret;
1065 struct ini_cfgfile *cfgfile = NULL;
1066
1067 if (_rules_obj == NULL) {
1068 return EINVAL;
1069 }
1070
1071 ret = ini_config_create(_rules_obj);
1072 if (ret != EOK) {
1073 return ret;
1074 }
1075
1076 ret = ini_config_file_open(filename, 0, &cfgfile);
1077 if (ret != EOK) {
1078 goto done;
1079 }
1080
1081 ret = ini_config_parse(cfgfile, 0, INI_MV1S_ALLOW, 0, *_rules_obj);
1082 if (ret != EOK) {
1083 goto done;
1084 }
1085
1086 done:
1087 if (ret != EOK) {
1088 ini_config_destroy(*_rules_obj);
1089 *_rules_obj = NULL;
1090 }
1091
1092 ini_config_file_destroy(cfgfile);
1093 return ret;
1094 }
1095
1096 /* This is used for testing only */
ini_dummy_noerror(const char * rule_name,struct ini_cfgobj * rules_obj,struct ini_cfgobj * config_obj,struct ini_errobj * errobj,void ** data)1097 static int ini_dummy_noerror(const char *rule_name,
1098 struct ini_cfgobj *rules_obj,
1099 struct ini_cfgobj *config_obj,
1100 struct ini_errobj *errobj,
1101 void **data)
1102 {
1103 return 0;
1104 }
1105
1106 /* This is used for testing only */
ini_dummy_error(const char * rule_name,struct ini_cfgobj * rules_obj,struct ini_cfgobj * config_obj,struct ini_errobj * errobj,void ** data)1107 static int ini_dummy_error(const char *rule_name,
1108 struct ini_cfgobj *rules_obj,
1109 struct ini_cfgobj *config_obj,
1110 struct ini_errobj *errobj,
1111 void **data)
1112 {
1113 return ini_errobj_add_msg(errobj, "Error");
1114 }
1115
is_allowed_section(const char * tested_section,char ** allowed_sections,size_t num_sec,regex_t * allowed_sections_re,size_t num_sec_re,int case_insensitive)1116 static int is_allowed_section(const char *tested_section,
1117 char **allowed_sections,
1118 size_t num_sec,
1119 regex_t *allowed_sections_re,
1120 size_t num_sec_re,
1121 int case_insensitive)
1122 {
1123 int ret;
1124 int i;
1125
1126 if (case_insensitive) {
1127 for (i = 0; i < num_sec; i++) {
1128 if (strcasecmp(tested_section, allowed_sections[i]) == 0) {
1129 return 1;
1130 }
1131 }
1132 } else { /* case sensitive */
1133 for (i = 0; i < num_sec; i++) {
1134 if (strcmp(tested_section, allowed_sections[i]) == 0) {
1135 return 1;
1136 }
1137 }
1138 }
1139
1140 for (i = 0; i < num_sec_re; i++) {
1141 ret = regexec(&allowed_sections_re[i], tested_section, 0, NULL, 0);
1142 if (ret == 0) {
1143 return 1;
1144 }
1145 }
1146
1147 return 0;
1148 }
1149
ini_allowed_sections(const char * rule_name,struct ini_cfgobj * rules_obj,struct ini_cfgobj * config_obj,struct ini_errobj * errobj,void ** data)1150 static int ini_allowed_sections(const char *rule_name,
1151 struct ini_cfgobj *rules_obj,
1152 struct ini_cfgobj *config_obj,
1153 struct ini_errobj *errobj,
1154 void **data)
1155 {
1156 struct value_obj *vo = NULL;
1157 int ret;
1158 char *regex_str = NULL;
1159 char **allowed_sections = NULL;
1160 char *insensitive_str;
1161 char **cfg_sections = NULL;
1162 int num_cfg_sections;
1163 char **attributes = NULL;
1164 int num_attributes;
1165 size_t num_sec = 0;
1166 size_t num_sec_re = 0;
1167 regex_t *allowed_sections_re = NULL;
1168 size_t buf_size;
1169 int reg_err;
1170 int is_allowed;
1171 int case_insensitive = 0;
1172 int regcomp_flags = REG_NOSUB;
1173 int i;
1174
1175 /* Get number of 'section' and 'section_re' attributes
1176 * in this rule */
1177 attributes = ini_get_attribute_list(rules_obj,
1178 rule_name,
1179 &num_attributes,
1180 NULL);
1181 if (attributes == NULL) {
1182 ret = ENOMEM;
1183 goto done;
1184 }
1185
1186 for (i = 0; i < num_attributes; i++) {
1187 if (strcmp("section", attributes[i]) == 0) {
1188 num_sec++;
1189 }
1190
1191 if (strcmp("section_re", attributes[i]) == 0) {
1192 num_sec_re++;
1193 }
1194 }
1195
1196 ini_free_attribute_list(attributes);
1197
1198 if (num_sec == 0 && num_sec_re == 0) {
1199 /* This rule is empty. */
1200 ret = ini_errobj_add_msg(errobj,
1201 "No allowed sections specified. "
1202 "Use 'section = default' to allow only "
1203 "default section");
1204 goto done;
1205 }
1206
1207 ret = ini_get_config_valueobj(rule_name,
1208 "case_insensitive",
1209 rules_obj,
1210 INI_GET_NEXT_VALUE,
1211 &vo);
1212 if (ret) {
1213 goto done;
1214 }
1215
1216 if (vo) {
1217 insensitive_str = ini_get_string_config_value(vo, &ret);
1218 if (ret) {
1219 goto done;
1220 }
1221
1222 if (strcasecmp(insensitive_str, "yes") == 0
1223 || strcasecmp(insensitive_str, "true") == 0
1224 || strcmp(insensitive_str, "1") == 0) {
1225 case_insensitive = 1;
1226 regcomp_flags |= REG_ICASE;
1227 }
1228
1229 free(insensitive_str);
1230 }
1231
1232 /* Create arrays for section_re regexes and section name
1233 * strings. */
1234 allowed_sections = calloc(num_sec + 1, sizeof(char *));
1235 if (allowed_sections == NULL) {
1236 ret = ENOMEM;
1237 goto done;
1238 }
1239
1240 allowed_sections_re = calloc(num_sec_re + 1, sizeof(regex_t));
1241 if (allowed_sections_re == NULL) {
1242 ret = ENOMEM;
1243 goto done;
1244 }
1245
1246 /* Get all allowed section names and store them to
1247 * allowed_sections array */
1248 for (i = 0; i < num_sec; i++) {
1249 ret = ini_get_config_valueobj(rule_name,
1250 "section",
1251 rules_obj,
1252 INI_GET_NEXT_VALUE,
1253 &vo);
1254 if (ret) {
1255 goto done;
1256 }
1257
1258 allowed_sections[i] = ini_get_string_config_value(vo, &ret);
1259 if (ret) {
1260 goto done;
1261 }
1262 }
1263
1264 /* Get all regular section_re regular expresions and
1265 * store them to allowed_sections_re array */
1266 for (i = 0; i < num_sec_re; i++) {
1267 ret = ini_get_config_valueobj(rule_name,
1268 "section_re",
1269 rules_obj,
1270 INI_GET_NEXT_VALUE,
1271 &vo);
1272 if (ret) {
1273 goto done;
1274 }
1275
1276 regex_str = ini_get_string_config_value(vo, &ret);
1277 if (ret) {
1278 goto done;
1279 }
1280
1281 reg_err = regcomp(&allowed_sections_re[i], regex_str, regcomp_flags);
1282 if (reg_err) {
1283 char *err_str;
1284
1285 buf_size = regerror(reg_err, &allowed_sections_re[i], NULL, 0);
1286 err_str = malloc(buf_size);
1287 if (err_str == NULL) {
1288 ret = ENOMEM;
1289 goto done;
1290 }
1291
1292 regerror(reg_err, &allowed_sections_re[i], err_str, buf_size);
1293 ret = ini_errobj_add_msg(errobj,
1294 "Validator failed to use regex [%s]:[%s]",
1295 regex_str, err_str);
1296 free(err_str);
1297 ret = ret ? ret : EINVAL;
1298 goto done;
1299 }
1300 free(regex_str);
1301 regex_str = NULL;
1302 }
1303
1304 /* Finally get list of all sections in configuration and
1305 * check if they are matched by some string in allowed_sections
1306 * or regex in allowed_sections_re */
1307 cfg_sections = ini_get_section_list(config_obj, &num_cfg_sections, &ret);
1308 if (ret != EOK) {
1309 goto done;
1310 }
1311
1312 for (i = 0; i < num_cfg_sections; i++) {
1313 is_allowed = is_allowed_section(cfg_sections[i],
1314 allowed_sections,
1315 num_sec,
1316 allowed_sections_re,
1317 num_sec_re,
1318 case_insensitive);
1319 if (!is_allowed) {
1320 ret = ini_errobj_add_msg(errobj,
1321 "Section [%s] is not allowed. "
1322 "Check for typos.",
1323 cfg_sections[i]);
1324 if (ret) {
1325 goto done;
1326 }
1327 }
1328 }
1329
1330 ret = EOK;
1331 done:
1332 if (allowed_sections != NULL) {
1333 for (i = 0; allowed_sections[i] != NULL; i++) {
1334 free(allowed_sections[i]);
1335 }
1336 free(allowed_sections);
1337 }
1338 if (allowed_sections_re != NULL) {
1339 for (i = 0; i < num_sec_re; i++) {
1340 regfree(&allowed_sections_re[i]);
1341 }
1342 free(allowed_sections_re);
1343 }
1344 ini_free_section_list(cfg_sections);
1345 free(regex_str);
1346
1347 return ret;
1348 }
1349
check_if_allowed(char * section,char * attr,char ** allowed,int num_allowed,struct ini_errobj * errobj)1350 static int check_if_allowed(char *section, char *attr, char **allowed,
1351 int num_allowed, struct ini_errobj *errobj)
1352 {
1353 int is_allowed = 0;
1354 int ret;
1355 int i;
1356
1357 for (i = 0; i < num_allowed; i++) {
1358 if (strcmp(attr, allowed[i]) == 0) {
1359 is_allowed = 1;
1360 break;
1361 }
1362 }
1363
1364 if (!is_allowed) {
1365 ret = ini_errobj_add_msg(errobj,
1366 "Attribute '%s' is not allowed in "
1367 "section '%s'. Check for typos.",
1368 attr, section);
1369 return ret;
1370 }
1371
1372 return 0;
1373 }
1374
ini_allowed_options(const char * rule_name,struct ini_cfgobj * rules_obj,struct ini_cfgobj * config_obj,struct ini_errobj * errobj,void ** data)1375 static int ini_allowed_options(const char *rule_name,
1376 struct ini_cfgobj *rules_obj,
1377 struct ini_cfgobj *config_obj,
1378 struct ini_errobj *errobj,
1379 void **data)
1380 {
1381 struct value_obj *vo = NULL;
1382 int ret;
1383 char *section_regex;
1384 int num_sections;
1385 char **sections = NULL;
1386 char **attributes = NULL;
1387 int num_attributes;
1388 int num_opts = 0;
1389 int i;
1390 int a;
1391 regex_t preg;
1392 size_t buf_size;
1393 char *err_str = NULL;
1394 int reg_err;
1395 char **allowed = NULL;
1396
1397 /* Get section regex */
1398 ret = ini_get_config_valueobj(rule_name,
1399 "section_re",
1400 rules_obj,
1401 INI_GET_FIRST_VALUE,
1402 &vo);
1403 if (ret != 0) {
1404 return ret;
1405 }
1406
1407 if (vo == NULL) {
1408 ret = ini_errobj_add_msg(errobj,
1409 "Validator misses 'section_re' parameter");
1410 if (ret) {
1411 return ret;
1412 }
1413 return EINVAL;
1414 }
1415
1416 section_regex = ini_get_string_config_value(vo, NULL);
1417 if (section_regex == NULL || section_regex[0] == '\0') {
1418 ret = ini_errobj_add_msg(errobj,
1419 "Validator misses 'section_re' parameter");
1420 if (ret) {
1421 return ret;
1422 }
1423
1424 free(section_regex);
1425 return EINVAL;
1426 }
1427
1428 /* compile the regular expression */
1429 reg_err = regcomp(&preg, section_regex, REG_NOSUB);
1430 if (reg_err) {
1431 buf_size = regerror(reg_err, &preg, NULL, 0);
1432 err_str = malloc(buf_size);
1433 if (err_str == NULL) {
1434 ret = ENOMEM;
1435 goto done;
1436 }
1437
1438 regerror(reg_err, &preg, err_str, buf_size);
1439 ret = ini_errobj_add_msg(errobj,
1440 "Cannot compile regular expression from "
1441 "option 'section_re'. Error: '%s'", err_str);
1442 ret = ret ? ret : EINVAL;
1443 goto done;
1444 }
1445
1446 /* Get all sections from config_obj */
1447 sections = ini_get_section_list(config_obj, &num_sections, &ret);
1448 if (ret != EOK) {
1449 goto done;
1450 }
1451
1452 /* Get number of 'option' attributes in this rule
1453 * and create an array long enough to store them all */
1454 attributes = ini_get_attribute_list(rules_obj,
1455 rule_name,
1456 &num_attributes,
1457 NULL);
1458 if (attributes == NULL) {
1459 ret = ENOMEM;
1460 goto done;
1461 }
1462
1463 for (i = 0; i < num_attributes; i++) {
1464 if (strcmp("option", attributes[i]) == 0) {
1465 num_opts++;
1466 }
1467 }
1468
1469 ini_free_attribute_list(attributes);
1470 attributes = NULL;
1471
1472 allowed = calloc(num_opts + 1, sizeof(char *));
1473 if (allowed == NULL) {
1474 ret = ENOMEM;
1475 goto done;
1476 }
1477
1478 for (i = 0; i < num_opts; i++) {
1479 ret = ini_get_config_valueobj(rule_name,
1480 "option",
1481 rules_obj,
1482 INI_GET_NEXT_VALUE,
1483 &vo);
1484 if (ret) {
1485 goto done;
1486 }
1487
1488 allowed[i] = ini_get_string_config_value(vo, &ret);
1489 if (ret) {
1490 goto done;
1491 }
1492 }
1493
1494 for (i = 0; i < num_sections; i++) {
1495 if (regexec(&preg, sections[i], 0, NULL, 0) == 0) {
1496 /* Regex matched section */
1497 /* Get options from this section */
1498 attributes = ini_get_attribute_list(config_obj,
1499 sections[i],
1500 &num_attributes,
1501 NULL);
1502 if (attributes == NULL) {
1503 ret = ENOMEM;
1504 goto done;
1505 }
1506
1507 for (a = 0; a < num_attributes; a++) {
1508 ret = check_if_allowed(sections[i], attributes[a], allowed,
1509 num_opts, errobj);
1510 if (ret != 0) {
1511 goto done;
1512 }
1513 }
1514 ini_free_attribute_list(attributes);
1515 attributes = NULL;
1516 }
1517 }
1518
1519 ret = 0;
1520 done:
1521 if (allowed != NULL) {
1522 for (i = 0; allowed[i] != NULL; i++) {
1523 free(allowed[i]);
1524 }
1525 free(allowed);
1526 }
1527 ini_free_section_list(sections);
1528 free(section_regex);
1529 ini_free_attribute_list(attributes);
1530 regfree(&preg);
1531 free(err_str);
1532 return ret;
1533 }
1534
1535 static ini_validator_func *
get_validator(char * validator_name,struct ini_validator ** validators)1536 get_validator(char *validator_name,
1537 struct ini_validator **validators)
1538 {
1539 struct ini_validator *ext_validator;
1540
1541 /* First we check all internal validators */
1542 if (strcmp(validator_name, "ini_dummy_noerror") == 0) {
1543 return ini_dummy_noerror;
1544 } else if (strcmp(validator_name, "ini_dummy_error") == 0) {
1545 return ini_dummy_error;
1546 } else if (strcmp(validator_name, "ini_allowed_options") == 0) {
1547 return ini_allowed_options;
1548 } else if (strcmp(validator_name, "ini_allowed_sections") == 0) {
1549 return ini_allowed_sections;
1550 }
1551
1552 if (validators == NULL) {
1553 return NULL;
1554 }
1555
1556 for (; *validators != NULL; ++validators) {
1557 ext_validator = *validators;
1558
1559 /* Skip invalid external validator. Name is required */
1560 if (ext_validator->name == NULL) {
1561 continue;
1562 }
1563 if (strcmp(validator_name, ext_validator->name) == 0) {
1564 return ext_validator->func;
1565 }
1566 }
1567
1568 return NULL;
1569 }
1570
ini_rules_check(struct ini_cfgobj * rules_obj,struct ini_cfgobj * config_obj,struct ini_validator ** extra_validators,struct ini_errobj * errobj)1571 int ini_rules_check(struct ini_cfgobj *rules_obj,
1572 struct ini_cfgobj *config_obj,
1573 struct ini_validator **extra_validators,
1574 struct ini_errobj *errobj)
1575 {
1576 char **sections;
1577 int ret;
1578 int num_sections;
1579 char *vname;
1580 ini_validator_func *vfunc;
1581 struct value_obj *vo = NULL;
1582 struct ini_errobj *localerr = NULL;
1583 int i;
1584
1585 /* Get all sections from the rules object */
1586 sections = ini_get_section_list(rules_obj, &num_sections, &ret);
1587 if (ret != EOK) {
1588 return ret;
1589 }
1590
1591 /* Now iterate through all the sections. If the section
1592 * name begins with a prefix "rule/", then it is a rule
1593 * name. */
1594 for (i = 0; i < num_sections; i++) {
1595 if (!strncmp(sections[i], "rule/", sizeof("rule/") - 1)) {
1596 ret = ini_get_config_valueobj(sections[i],
1597 "validator",
1598 rules_obj,
1599 INI_GET_FIRST_VALUE,
1600 &vo);
1601 if (ret != 0) {
1602 /* Failed to get value object. This should not
1603 * happen. */
1604 continue;
1605 }
1606
1607 if (vo == NULL) {
1608 ret = ini_errobj_add_msg(errobj,
1609 "Rule '%s' has no validator.",
1610 sections[i]);
1611 if (ret != EOK) {
1612 return ret;
1613 }
1614 /* Skip problematic rule */
1615 continue;
1616 }
1617
1618 vname = ini_get_string_config_value(vo, NULL);
1619 vfunc = get_validator(vname, extra_validators);
1620 if (vfunc == NULL) {
1621 ret = ini_errobj_add_msg(errobj,
1622 "Rule '%s' uses unknown "
1623 "validator '%s'.",
1624 sections[i], vname);
1625 if (ret != EOK) {
1626 goto done;
1627 }
1628 /* Skip problematic rule */
1629 free(vname);
1630 continue;
1631 }
1632 free(vname);
1633
1634 /* Do not pass global errobj to validators, they
1635 * could corrupt it. Create local one for each
1636 * validator. */
1637 ret = ini_errobj_create(&localerr);
1638 if (ret != EOK) {
1639 goto done;
1640 }
1641
1642 ret = vfunc(sections[i], rules_obj, config_obj, localerr, NULL);
1643 if (ret != 0) {
1644 /* Just report the error and continue normally,
1645 * maybe there are some errors in localerr */
1646 ret = ini_errobj_add_msg(errobj,
1647 "Rule '%s' returned error code '%d'",
1648 sections[i], ret);
1649 if (ret != EOK) {
1650 goto done;
1651 }
1652 }
1653
1654 /* Bad validator could destroy the localerr, check
1655 * for NULL */
1656 if (localerr == NULL) {
1657 continue;
1658 }
1659
1660 ini_errobj_reset(localerr);
1661 while (!ini_errobj_no_more_msgs(localerr)) {
1662 ret = ini_errobj_add_msg(errobj,
1663 "[%s]: %s",
1664 sections[i],
1665 ini_errobj_get_msg(localerr));
1666 if (ret != EOK) {
1667 goto done;
1668 }
1669 ini_errobj_next(localerr);
1670 }
1671
1672 ini_errobj_destroy(&localerr);
1673 }
1674 }
1675
1676 ret = EOK;
1677 done:
1678 ini_free_section_list(sections);
1679 ini_errobj_destroy(&localerr);
1680 return ret;
1681 }
1682
1683 /* This is just convenience function, so that
1684 * we manipulate with ini_rules_* functions. */
ini_rules_destroy(struct ini_cfgobj * rules)1685 void ini_rules_destroy(struct ini_cfgobj *rules)
1686 {
1687 ini_config_destroy(rules);
1688 }
1689
ini_errobj_create(struct ini_errobj ** _errobj)1690 int ini_errobj_create(struct ini_errobj **_errobj)
1691 {
1692 struct ini_errobj *new_errobj = NULL;
1693
1694 if (_errobj == NULL) {
1695 return EINVAL;
1696 }
1697
1698 new_errobj = calloc(1, sizeof(struct ini_errobj));
1699 if (new_errobj == NULL) {
1700 return ENOMEM;
1701 }
1702
1703 *_errobj = new_errobj;
1704 return EOK;
1705 }
1706
ini_errobj_destroy(struct ini_errobj ** errobj)1707 void ini_errobj_destroy(struct ini_errobj **errobj)
1708 {
1709 struct ini_errmsg *to_remove;
1710
1711 if (errobj == NULL || *errobj == NULL) {
1712 return;
1713 }
1714
1715 while ((*errobj)->first_msg) {
1716 to_remove = (*errobj)->first_msg;
1717 (*errobj)->first_msg = (*errobj)->first_msg->next;
1718 free(to_remove->str);
1719 free(to_remove);
1720 }
1721
1722 free(*errobj);
1723 *errobj = NULL;
1724 }
1725
ini_errobj_add_msg(struct ini_errobj * errobj,const char * format,...)1726 int ini_errobj_add_msg(struct ini_errobj *errobj, const char *format, ...)
1727 {
1728 int ret;
1729 va_list args;
1730 struct ini_errmsg *new;
1731
1732 new = calloc(1, sizeof(struct ini_errmsg));
1733 if (new == NULL) {
1734 return ENOMEM;
1735 }
1736
1737 va_start(args, format);
1738 ret = vasprintf(&new->str, format, args);
1739 va_end(args);
1740 if (ret == -1) {
1741 free(new);
1742 return ENOMEM;
1743 }
1744
1745 if (errobj->count == 0) {
1746 /* First addition to the list, all pointers are NULL */
1747 errobj->first_msg = new;
1748 errobj->last_msg = new;
1749 errobj->cur_msg = new;
1750 errobj->count++;
1751 } else {
1752 errobj->last_msg->next = new;
1753 errobj->last_msg = errobj->last_msg->next;
1754 errobj->count++;
1755 }
1756
1757 return EOK;
1758 }
1759
ini_errobj_reset(struct ini_errobj * errobj)1760 void ini_errobj_reset(struct ini_errobj *errobj)
1761 {
1762 errobj->cur_msg = errobj->first_msg;
1763 }
1764
ini_errobj_get_msg(struct ini_errobj * errobj)1765 const char *ini_errobj_get_msg(struct ini_errobj *errobj)
1766 {
1767 if (errobj->cur_msg != NULL) {
1768 return errobj->cur_msg->str;
1769 }
1770
1771 /* Should this be allowed? */
1772 return NULL;
1773 }
1774
ini_errobj_next(struct ini_errobj * errobj)1775 void ini_errobj_next(struct ini_errobj *errobj)
1776 {
1777 if (errobj->cur_msg != NULL) {
1778 errobj->cur_msg = errobj->cur_msg->next;
1779 }
1780
1781 /* If we can not move next, just return */
1782 return;
1783 }
1784
ini_errobj_no_more_msgs(struct ini_errobj * errobj)1785 int ini_errobj_no_more_msgs(struct ini_errobj *errobj)
1786 {
1787 return errobj->cur_msg == NULL;
1788 }
1789
ini_errobj_count(struct ini_errobj * errobj)1790 size_t ini_errobj_count(struct ini_errobj *errobj)
1791 {
1792 return errobj->count;
1793 }
1794