s4:torture: Adapt KDC canon test to Heimdal upstream changes
[samba.git] / source4 / heimdal / lib / base / config_file.c
1 /*
2  * Copyright (c) 1997 - 2004 Kungliga Tekniska Högskolan
3  * (Royal Institute of Technology, Stockholm, Sweden).
4  * All rights reserved.
5  *
6  * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * 3. Neither the name of the Institute nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35
36 #include "baselocl.h"
37 #include <assert.h>
38 #include <ctype.h>
39 #include <parse_time.h>
40
41 #if defined(HAVE_FRAMEWORK_COREFOUNDATION)
42 #include <CoreFoundation/CoreFoundation.h>
43 #endif
44
45 /* Gaah! I want a portable funopen */
46 struct fileptr {
47     heim_context context;
48     const char *s;
49     FILE *f;
50 };
51
52 static char *
53 config_fgets(char *str, size_t len, struct fileptr *ptr)
54 {
55     /* XXX this is not correct, in that they don't do the same if the
56        line is longer than len */
57     if(ptr->f != NULL)
58         return fgets(str, len, ptr->f);
59     else {
60         /* this is almost strsep_copy */
61         const char *p;
62         ssize_t l;
63         if(*ptr->s == '\0')
64             return NULL;
65         p = ptr->s + strcspn(ptr->s, "\n");
66         if(*p == '\n')
67             p++;
68         l = min(len, (size_t)(p - ptr->s));
69         if(len > 0) {
70             memcpy(str, ptr->s, l);
71             str[l] = '\0';
72         }
73         ptr->s = p;
74         return str;
75     }
76 }
77
78 static heim_error_code parse_section(char *p, heim_config_section **s,
79                                      heim_config_section **res,
80                                      const char **err_message);
81 static heim_error_code parse_binding(struct fileptr *f, unsigned *lineno, char *p,
82                                      heim_config_binding **b,
83                                      heim_config_binding **parent,
84                                      const char **err_message);
85 static heim_error_code parse_list(struct fileptr *f, unsigned *lineno,
86                                   heim_config_binding **parent,
87                                   const char **err_message);
88
89 heim_config_section *
90 heim_config_get_entry(heim_config_section **parent, const char *name, int type)
91 {
92     heim_config_section **q;
93
94     for (q = parent; *q != NULL; q = &(*q)->next)
95         if (type == heim_config_list &&
96             (unsigned)type == (*q)->type &&
97             strcmp(name, (*q)->name) == 0)
98             return *q;
99     *q = calloc(1, sizeof(**q));
100     if (*q == NULL)
101         return NULL;
102     (*q)->name = strdup(name);
103     (*q)->type = type;
104     if ((*q)->name == NULL) {
105         free(*q);
106         *q = NULL;
107         return NULL;
108     }
109     return *q;
110 }
111
112 /*
113  * Parse a section:
114  *
115  * [section]
116  *      foo = bar
117  *      b = {
118  *              a
119  *          }
120  * ...
121  *
122  * starting at the line in `p', storing the resulting structure in
123  * `s' and hooking it into `parent'.
124  * Store the error message in `err_message'.
125  */
126
127 static heim_error_code
128 parse_section(char *p, heim_config_section **s, heim_config_section **parent,
129               const char **err_message)
130 {
131     char *p1;
132     heim_config_section *tmp;
133
134     p1 = strchr (p + 1, ']');
135     if (p1 == NULL) {
136         *err_message = "missing ]";
137         return HEIM_ERR_CONFIG_BADFORMAT;
138     }
139     *p1 = '\0';
140     tmp = heim_config_get_entry(parent, p + 1, heim_config_list);
141     if(tmp == NULL) {
142         *err_message = "out of memory";
143         return HEIM_ERR_CONFIG_BADFORMAT;
144     }
145     *s = tmp;
146     return 0;
147 }
148
149 /*
150  * Parse a brace-enclosed list from `f', hooking in the structure at
151  * `parent'.
152  * Store the error message in `err_message'.
153  */
154
155 static heim_error_code
156 parse_list(struct fileptr *f, unsigned *lineno, heim_config_binding **parent,
157            const char **err_message)
158 {
159     char buf[2048];
160     heim_error_code ret;
161     heim_config_binding *b = NULL;
162     unsigned beg_lineno = *lineno;
163
164     while(config_fgets(buf, sizeof(buf), f) != NULL) {
165         char *p;
166
167         ++*lineno;
168         buf[strcspn(buf, "\r\n")] = '\0';
169         p = buf;
170         while(isspace((unsigned char)*p))
171             ++p;
172         if (*p == '#' || *p == ';' || *p == '\0')
173             continue;
174         while(isspace((unsigned char)*p))
175             ++p;
176         if (*p == '}')
177             return 0;
178         if (*p == '\0')
179             continue;
180         ret = parse_binding (f, lineno, p, &b, parent, err_message);
181         if (ret)
182             return ret;
183     }
184     *lineno = beg_lineno;
185     *err_message = "unclosed {";
186     return HEIM_ERR_CONFIG_BADFORMAT;
187 }
188
189 /*
190  *
191  */
192
193 static heim_error_code
194 parse_binding(struct fileptr *f, unsigned *lineno, char *p,
195               heim_config_binding **b, heim_config_binding **parent,
196               const char **err_message)
197 {
198     heim_config_binding *tmp;
199     char *p1, *p2;
200     heim_error_code ret = 0;
201
202     p1 = p;
203     while (*p && *p != '=' && !isspace((unsigned char)*p))
204         ++p;
205     if (*p == '\0') {
206         *err_message = "missing =";
207         return HEIM_ERR_CONFIG_BADFORMAT;
208     }
209     p2 = p;
210     while (isspace((unsigned char)*p))
211         ++p;
212     if (*p != '=') {
213         *err_message = "missing =";
214         return HEIM_ERR_CONFIG_BADFORMAT;
215     }
216     ++p;
217     while(isspace((unsigned char)*p))
218         ++p;
219     *p2 = '\0';
220     if (*p == '{') {
221         tmp = heim_config_get_entry(parent, p1, heim_config_list);
222         if (tmp == NULL) {
223             *err_message = "out of memory";
224             return HEIM_ERR_CONFIG_BADFORMAT;
225         }
226         ret = parse_list (f, lineno, &tmp->u.list, err_message);
227     } else {
228         tmp = heim_config_get_entry(parent, p1, heim_config_string);
229         if (tmp == NULL) {
230             *err_message = "out of memory";
231             return HEIM_ERR_CONFIG_BADFORMAT;
232         }
233         p1 = p;
234         p = p1 + strlen(p1);
235         while(p > p1 && isspace((unsigned char)*(p-1)))
236             --p;
237         *p = '\0';
238         tmp->u.string = strdup(p1);
239     }
240     *b = tmp;
241     return ret;
242 }
243
244 #if defined(HAVE_FRAMEWORK_COREFOUNDATION)
245
246 #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
247 #define HAVE_CFPROPERTYLISTCREATEWITHSTREAM 1
248 #endif
249
250 static char *
251 cfstring2cstring(CFStringRef string)
252 {
253     CFIndex len;
254     char *str;
255
256     str = (char *) CFStringGetCStringPtr(string, kCFStringEncodingUTF8);
257     if (str)
258         return strdup(str);
259
260     len = CFStringGetLength(string);
261     len = 1 + CFStringGetMaximumSizeForEncoding(len, kCFStringEncodingUTF8);
262     str = malloc(len);
263     if (str == NULL)
264         return NULL;
265
266     if (!CFStringGetCString (string, str, len, kCFStringEncodingUTF8)) {
267         free (str);
268         return NULL;
269     }
270     return str;
271 }
272
273 static void
274 convert_content(const void *key, const void *value, void *context)
275 {
276     heim_config_section *tmp, **parent = context;
277     char *k;
278
279     if (CFGetTypeID(key) != CFStringGetTypeID())
280         return;
281
282     k = cfstring2cstring(key);
283     if (k == NULL)
284         return;
285
286     if (CFGetTypeID(value) == CFStringGetTypeID()) {
287         tmp = heim_config_get_entry(parent, k, heim_config_string);
288         tmp->u.string = cfstring2cstring(value);
289     } else if (CFGetTypeID(value) == CFDictionaryGetTypeID()) {
290         tmp = heim_config_get_entry(parent, k, heim_config_list);
291         CFDictionaryApplyFunction(value, convert_content, &tmp->u.list);
292     } else {
293         /* log */
294     }
295     free(k);
296 }
297
298 static heim_error_code
299 parse_plist_config(heim_context context, const char *path, heim_config_section **parent)
300 {
301     CFReadStreamRef s;
302     CFDictionaryRef d;
303     CFURLRef url;
304
305     url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (UInt8 *)path, strlen(path), 0);
306     if (url == NULL) {
307         heim_clear_error_message(context);
308         return ENOMEM;
309     }
310
311     s = CFReadStreamCreateWithFile(kCFAllocatorDefault, url);
312     CFRelease(url);
313     if (s == NULL) {
314         heim_clear_error_message(context);
315         return ENOMEM;
316     }
317
318     if (!CFReadStreamOpen(s)) {
319         CFRelease(s);
320         heim_clear_error_message(context);
321         return ENOENT;
322     }
323
324 #ifdef HAVE_CFPROPERTYLISTCREATEWITHSTREAM
325     d = (CFDictionaryRef)CFPropertyListCreateWithStream(NULL, s, 0, kCFPropertyListImmutable, NULL, NULL);
326 #else
327     d = (CFDictionaryRef)CFPropertyListCreateFromStream(NULL, s, 0, kCFPropertyListImmutable, NULL, NULL);
328 #endif
329     CFRelease(s);
330     if (d == NULL) {
331         heim_clear_error_message(context);
332         return ENOENT;
333     }
334
335     CFDictionaryApplyFunction(d, convert_content, parent);
336     CFRelease(d);
337
338     return 0;
339 }
340
341 #endif
342
343 static int
344 is_absolute_path(const char *path)
345 {
346     /*
347      * An absolute path is one that refers to an explicit object
348      * without ambiguity.
349      */
350 #ifdef WIN32
351     size_t len = strlen(path);
352
353     /* UNC path is by definition absolute */
354     if (len > 2
355          && ISPATHSEP(path[0])
356          && ISPATHSEP(path[1]))
357         return 1;
358
359     /* A drive letter path might be absolute */
360     if (len > 3
361          && isalpha(path[0])
362          && path[1] == ':'
363          && ISPATHSEP(path[2]))
364         return 1;
365
366     /*
367      * if no drive letter but first char is a path
368      * separator then the drive letter must be obtained
369      * from the including file.
370      */
371 #else
372     /* UNIX is easy, first char '/' is absolute */
373     if (ISPATHSEP(path[0]))
374         return 1;
375 #endif
376     return 0;
377 }
378
379 /*
380  * Parse the config file `fname', generating the structures into `res'
381  * returning error messages in `err_message'
382  */
383
384 static heim_error_code
385 heim_config_parse_debug(struct fileptr *f,
386                         heim_config_section **res,
387                         unsigned *lineno,
388                         const char **err_message)
389 {
390     heim_config_section *s = NULL;
391     heim_config_binding *b = NULL;
392     char buf[2048];
393     heim_error_code ret;
394
395     *lineno = 0;
396     *err_message = "";
397
398     while (config_fgets(buf, sizeof(buf), f) != NULL) {
399         char *p;
400
401         ++*lineno;
402         buf[strcspn(buf, "\r\n")] = '\0';
403         p = buf;
404         while(isspace((unsigned char)*p))
405             ++p;
406         if (*p == '#' || *p == ';')
407             continue;
408         if (*p == '[') {
409             ret = parse_section(p, &s, res, err_message);
410             if (ret)
411                 return ret;
412             b = NULL;
413         } else if (*p == '}') {
414             *err_message = "unmatched }";
415             return 2048;
416         } else if (strncmp(p, "include", sizeof("include") - 1) == 0 &&
417             isspace(p[sizeof("include") - 1])) {
418             p += sizeof("include");
419             while (isspace(*p))
420                 p++;
421             if (!is_absolute_path(p)) {
422                 heim_set_error_message(f->context, HEIM_ERR_CONFIG_BADFORMAT,
423                                        "Configuration include path must be "
424                                        "absolute");
425                 return HEIM_ERR_CONFIG_BADFORMAT;
426             }
427             ret = heim_config_parse_file_multi(f->context, p, res);
428             if (ret)
429                 return ret;
430         } else if (strncmp(p, "includedir", sizeof("includedir") - 1) == 0 &&
431             isspace(p[sizeof("includedir") - 1])) {
432             p += sizeof("includedir");
433             while (isspace(*p))
434                 p++;
435             if (!is_absolute_path(p)) {
436                 heim_set_error_message(f->context, HEIM_ERR_CONFIG_BADFORMAT,
437                                        "Configuration includedir path must be "
438                                        "absolute");
439                 return HEIM_ERR_CONFIG_BADFORMAT;
440             }
441             ret = heim_config_parse_dir_multi(f->context, p, res);
442             if (ret)
443                 return ret;
444         } else if(*p != '\0') {
445             if (s == NULL) {
446                 *err_message = "binding before section";
447                 return 2048;
448             }
449             ret = parse_binding(f, lineno, p, &b, &s->u.list, err_message);
450             if (ret)
451                 return ret;
452         }
453     }
454     return 0;
455 }
456
457 static int
458 is_plist_file(const char *fname)
459 {
460     size_t len = strlen(fname);
461     char suffix[] = ".plist";
462     if (len < sizeof(suffix))
463         return 0;
464     if (strcasecmp(&fname[len - (sizeof(suffix) - 1)], suffix) != 0)
465         return 0;
466     return 1;
467 }
468
469 /**
470  * Parse configuration files in the given directory and add the result
471  * into res.  Only files whose names consist only of alphanumeric
472  * characters, hyphen, and underscore, will be parsed, though files
473  * ending in ".conf" will also be parsed.
474  *
475  * This interface can be used to parse several configuration directories
476  * into one resulting heim_config_section by calling it repeatably.
477  *
478  * @param context a Kerberos 5 context.
479  * @param dname a directory name to a Kerberos configuration file
480  * @param res the returned result, must be free with heim_free_config_files().
481  * @return Return an error code or 0, see heim_get_error_message().
482  *
483  * @ingroup heim_support
484  */
485
486 heim_error_code
487 heim_config_parse_dir_multi(heim_context context,
488                             const char *dname,
489                             heim_config_section **res)
490 {
491     struct dirent *entry;
492     heim_error_code ret;
493     DIR *d;
494
495     if ((d = opendir(dname)) == NULL)
496         return errno;
497
498     while ((entry = readdir(d)) != NULL) {
499         char *p = entry->d_name;
500         char *path;
501         int is_valid = 1;
502
503         while (*p) {
504             /*
505              * Here be dragons.  The call to heim_config_parse_file_multi()
506              * below expands path tokens.  Because of the limitations here
507              * on file naming, we can't have path tokens in the file name,
508              * so we're safe.  Anyone changing this if condition here should
509              * be aware.
510              */
511             if (!isalnum(*p) && *p != '_' && *p != '-' &&
512                 strcmp(p, ".conf") != 0) {
513                 is_valid = 0;
514                 break;
515             }
516             p++;
517         }
518         if (!is_valid)
519             continue;
520
521         if (asprintf(&path, "%s/%s", dname, entry->d_name) == -1 ||
522             path == NULL) {
523             (void) closedir(d);
524             return heim_enomem(context);
525         }
526         ret = heim_config_parse_file_multi(context, path, res);
527         free(path);
528         if (ret == ENOMEM) {
529             (void) closedir(d);
530             return ENOMEM;
531         }
532         /* Ignore malformed config files so we don't lock out admins, etc... */
533     }
534     (void) closedir(d);
535     return 0;
536 }
537
538 /**
539  * Parse a configuration file and add the result into res. This
540  * interface can be used to parse several configuration files into one
541  * resulting heim_config_section by calling it repeatably.
542  *
543  * @param context a Kerberos 5 context.
544  * @param fname a file name to a Kerberos configuration file
545  * @param res the returned result, must be free with heim_free_config_files().
546  * @return Return an error code or 0, see heim_get_error_message().
547  *
548  * @ingroup heim_support
549  */
550
551 HEIMDAL_THREAD_LOCAL int config_include_depth = 0;
552
553 heim_error_code
554 heim_config_parse_file_multi(heim_context context,
555                              const char *fname,
556                              heim_config_section **res)
557 {
558     const char *str;
559     char *newfname = NULL;
560     unsigned lineno = 0;
561     heim_error_code ret = 0;
562     struct fileptr f;
563     struct stat st;
564
565     if (config_include_depth > 5) {
566         heim_warnx(context, "Maximum config file include depth reached; "
567                    "not including %s", fname);
568         return 0;
569     }
570     config_include_depth++;
571
572     /**
573      * If the fname starts with "~/" parse configuration file in the
574      * current users home directory. The behavior can be disabled and
575      * enabled by calling heim_set_home_dir_access().
576      */
577     if (ISTILDE(fname[0]) && ISPATHSEP(fname[1])) {
578         if (!heim_context_get_homedir_access(context)) {
579             heim_set_error_message(context, EPERM,
580                                    "Access to home directory not allowed");
581             ret = EPERM;
582             goto out;
583         }
584         if (asprintf(&newfname, "%%{USERCONFIG}%s", &fname[1]) < 0 ||
585             newfname == NULL) {
586             ret = heim_enomem(context);
587             goto out;
588         }
589         fname = newfname;
590     }
591
592     if (is_plist_file(fname)) {
593 #if defined(HAVE_FRAMEWORK_COREFOUNDATION)
594         ret = parse_plist_config(context, fname, res);
595         if (ret) {
596             heim_set_error_message(context, ret,
597                                    "Failed to parse plist %s", fname);
598             goto out;
599         }
600 #else
601         heim_set_error_message(context, ENOENT,
602                                "no support for plist configuration files");
603         ret = ENOENT;
604         goto out;
605 #endif
606     } else {
607         char *exp_fname = NULL;
608
609         /*
610          * Note that heim_config_parse_dir_multi() doesn't want tokens
611          * expanded here, but it happens to limit the names of files to
612          * include such that there can be no tokens to expand.  Don't
613          * add token expansion for tokens using _, say.
614          */
615         ret = heim_expand_path_tokens(context, fname, 1, &exp_fname, NULL);
616         if (ret)
617             goto out;
618         free(newfname);
619         fname = newfname = exp_fname;
620
621         f.context = context;
622         f.f = fopen(fname, "r");
623         f.s = NULL;
624         if (f.f == NULL || fstat(fileno(f.f), &st) == -1) {
625             if (f.f != NULL)
626                 (void) fclose(f.f);
627             ret = errno;
628             heim_set_error_message(context, ret, "open or stat %s: %s",
629                                    fname, strerror(ret));
630             goto out;
631         }
632
633         if (!S_ISREG(st.st_mode)) {
634             (void) fclose(f.f);
635             heim_set_error_message(context, EISDIR, "not a regular file %s: %s",
636                                    fname, strerror(EISDIR));
637             ret = EISDIR;
638             goto out;
639         }
640
641         ret = heim_config_parse_debug(&f, res, &lineno, &str);
642         fclose(f.f);
643         if (ret) {
644             if (ret != HEIM_ERR_CONFIG_BADFORMAT)
645                 ret = HEIM_ERR_CONFIG_BADFORMAT;
646             heim_set_error_message(context, ret, "%s:%u: %s",
647                                    fname, lineno, str);
648             goto out;
649         }
650     }
651
652   out:
653     config_include_depth--;
654     if (ret == HEIM_ERR_CONFIG_BADFORMAT || (ret && config_include_depth > 0)) {
655         heim_warn(context, ret, "Ignoring");
656         if (config_include_depth > 0)
657             ret = 0;
658     }
659     free(newfname);
660     return ret;
661 }
662
663 heim_error_code
664 heim_config_parse_file(heim_context context,
665                        const char *fname,
666                        heim_config_section **res)
667 {
668     *res = NULL;
669     return heim_config_parse_file_multi(context, fname, res);
670 }
671
672 static void
673 free_binding(heim_context context, heim_config_binding *b)
674 {
675     heim_config_binding *next_b;
676
677     while (b) {
678         free (b->name);
679         assert(b->type == heim_config_string || b->type == heim_config_list);
680         if (b->type == heim_config_string)
681             free (b->u.string);
682         else
683             free_binding (context, b->u.list);
684         next_b = b->next;
685         free (b);
686         b = next_b;
687     }
688 }
689
690 /**
691  * Free configuration file section, the result of
692  * heim_config_parse_file() and heim_config_parse_file_multi().
693  *
694  * @param context A Kerberos 5 context
695  * @param s the configuration section to free
696  *
697  * @return returns 0 on successes, otherwise an error code, see
698  *          heim_get_error_message()
699  *
700  * @ingroup heim_support
701  */
702
703 heim_error_code
704 heim_config_file_free(heim_context context, heim_config_section *s)
705 {
706     free_binding (context, s);
707     return 0;
708 }
709
710 #ifndef HEIMDAL_SMALLER
711
712 heim_error_code
713 heim_config_copy(heim_context context,
714                  heim_config_section *c,
715                  heim_config_section **head)
716 {
717     heim_config_binding *d, *previous = NULL;
718
719     *head = NULL;
720
721     while (c) {
722         d = calloc(1, sizeof(*d));
723
724         if (*head == NULL)
725             *head = d;
726
727         d->name = strdup(c->name);
728         d->type = c->type;
729         assert(d->type == heim_config_string || d->type == heim_config_list);
730         if (d->type == heim_config_string)
731             d->u.string = strdup(c->u.string);
732         else
733             heim_config_copy (context, c->u.list, &d->u.list);
734         if (previous)
735             previous->next = d;
736
737         previous = d;
738         c = c->next;
739     }
740     return 0;
741 }
742
743 #endif /* HEIMDAL_SMALLER */
744
745 const void *
746 heim_config_get_next(heim_context context,
747                      const heim_config_section *c,
748                      const heim_config_binding **pointer,
749                      int type,
750                      ...)
751 {
752     const char *ret;
753     va_list args;
754
755     va_start(args, type);
756     ret = heim_config_vget_next(context, c, pointer, type, args);
757     va_end(args);
758     return ret;
759 }
760
761 static const void *
762 vget_next(heim_context context,
763           const heim_config_binding *b,
764           const heim_config_binding **pointer,
765           int type,
766           const char *name,
767           va_list args)
768 {
769     const char *p = va_arg(args, const char *);
770
771     while (b != NULL) {
772         if (strcmp(b->name, name) == 0) {
773             if (b->type == (unsigned)type && p == NULL) {
774                 *pointer = b;
775                 return b->u.generic;
776             } else if (b->type == heim_config_list && p != NULL) {
777                 return vget_next(context, b->u.list, pointer, type, p, args);
778             }
779         }
780         b = b->next;
781     }
782     return NULL;
783 }
784
785 const void *
786 heim_config_vget_next(heim_context context,
787                       const heim_config_section *c,
788                       const heim_config_binding **pointer,
789                       int type,
790                       va_list args)
791 {
792     const heim_config_binding *b;
793     const char *p;
794
795     if (c == NULL)
796         return NULL;
797
798     if (*pointer == NULL) {
799         /* first time here, walk down the tree looking for the right
800            section */
801         p = va_arg(args, const char *);
802         if (p == NULL)
803             return NULL;
804         return vget_next(context, c, pointer, type, p, args);
805     }
806
807     /* we were called again, so just look for more entries with the
808        same name and type */
809     for (b = (*pointer)->next; b != NULL; b = b->next) {
810         if(strcmp(b->name, (*pointer)->name) == 0 && b->type == (unsigned)type) {
811             *pointer = b;
812             return b->u.generic;
813         }
814     }
815     return NULL;
816 }
817
818 const void *
819 heim_config_get(heim_context context,
820                 const heim_config_section *c,
821                 int type,
822                 ...)
823 {
824     const void *ret;
825     va_list args;
826
827     va_start(args, type);
828     ret = heim_config_vget(context, c, type, args);
829     va_end(args);
830     return ret;
831 }
832
833
834 const void *
835 heim_config_vget(heim_context context,
836                  const heim_config_section *c,
837                  int type,
838                  va_list args)
839 {
840     const heim_config_binding *foo = NULL;
841
842     return heim_config_vget_next(context, c, &foo, type, args);
843 }
844
845 /**
846  * Get a list of configuration binding list for more processing
847  *
848  * @param context A Kerberos 5 context.
849  * @param c a configuration section, or NULL to use the section from context
850  * @param ... a list of names, terminated with NULL.
851  *
852  * @return NULL if configuration list is not found, a list otherwise
853  *
854  * @ingroup heim_support
855  */
856
857 const heim_config_binding *
858 heim_config_get_list(heim_context context,
859                      const heim_config_section *c,
860                      ...)
861 {
862     const heim_config_binding *ret;
863     va_list args;
864
865     va_start(args, c);
866     ret = heim_config_vget_list(context, c, args);
867     va_end(args);
868     return ret;
869 }
870
871 /**
872  * Get a list of configuration binding list for more processing
873  *
874  * @param context A Kerberos 5 context.
875  * @param c a configuration section, or NULL to use the section from context
876  * @param args a va_list of arguments
877  *
878  * @return NULL if configuration list is not found, a list otherwise
879  *
880  * @ingroup heim_support
881  */
882
883 const heim_config_binding *
884 heim_config_vget_list(heim_context context,
885                       const heim_config_section *c,
886                       va_list args)
887 {
888     return heim_config_vget(context, c, heim_config_list, args);
889 }
890
891 /**
892  * Returns a "const char *" to a string in the configuration database.
893  * The string may not be valid after a reload of the configuration
894  * database so a caller should make a local copy if it needs to keep
895  * the string.
896  *
897  * @param context A Kerberos 5 context.
898  * @param c a configuration section, or NULL to use the section from context
899  * @param ... a list of names, terminated with NULL.
900  *
901  * @return NULL if configuration string not found, a string otherwise
902  *
903  * @ingroup heim_support
904  */
905
906 const char *
907 heim_config_get_string(heim_context context,
908                        const heim_config_section *c,
909                        ...)
910 {
911     const char *ret;
912     va_list args;
913
914     va_start(args, c);
915     ret = heim_config_vget_string(context, c, args);
916     va_end(args);
917     return ret;
918 }
919
920 /**
921  * Like heim_config_get_string(), but uses a va_list instead of ...
922  *
923  * @param context A Kerberos 5 context.
924  * @param c a configuration section, or NULL to use the section from context
925  * @param args a va_list of arguments
926  *
927  * @return NULL if configuration string not found, a string otherwise
928  *
929  * @ingroup heim_support
930  */
931
932 const char *
933 heim_config_vget_string(heim_context context,
934                         const heim_config_section *c,
935                         va_list args)
936 {
937     return heim_config_vget(context, c, heim_config_string, args);
938 }
939
940 /**
941  * Like heim_config_vget_string(), but instead of returning NULL,
942  * instead return a default value.
943  *
944  * @param context A Kerberos 5 context.
945  * @param c a configuration section, or NULL to use the section from context
946  * @param def_value the default value to return if no configuration
947  *        found in the database.
948  * @param args a va_list of arguments
949  *
950  * @return a configuration string
951  *
952  * @ingroup heim_support
953  */
954
955 const char *
956 heim_config_vget_string_default(heim_context context,
957                                 const heim_config_section *c,
958                                 const char *def_value,
959                                 va_list args)
960 {
961     const char *ret;
962
963     ret = heim_config_vget_string(context, c, args);
964     if (ret == NULL)
965         ret = def_value;
966     return ret;
967 }
968
969 /**
970  * Like heim_config_get_string(), but instead of returning NULL,
971  * instead return a default value.
972  *
973  * @param context A Kerberos 5 context.
974  * @param c a configuration section, or NULL to use the section from context
975  * @param def_value the default value to return if no configuration
976  *        found in the database.
977  * @param ... a list of names, terminated with NULL.
978  *
979  * @return a configuration string
980  *
981  * @ingroup heim_support
982  */
983
984 const char *
985 heim_config_get_string_default(heim_context context,
986                                const heim_config_section *c,
987                                const char *def_value,
988                                ...)
989 {
990     const char *ret;
991     va_list args;
992
993     va_start(args, def_value);
994     ret = heim_config_vget_string_default (context, c, def_value, args);
995     va_end(args);
996     return ret;
997 }
998
999 static char *
1000 next_component_string(char * begin, const char * delims, char **state)
1001 {
1002     char * end;
1003
1004     if (begin == NULL)
1005         begin = *state;
1006
1007     if (*begin == '\0')
1008         return NULL;
1009
1010     end = begin;
1011     while (*end == '"') {
1012         char * t = strchr(end + 1, '"');
1013
1014         if (t)
1015             end = ++t;
1016         else
1017             end += strlen(end);
1018     }
1019
1020     if (*end != '\0') {
1021         size_t pos;
1022
1023         pos = strcspn(end, delims);
1024         end = end + pos;
1025     }
1026
1027     if (*end != '\0') {
1028         *end = '\0';
1029         *state = end + 1;
1030         if (*begin == '"' && *(end - 1) == '"' && begin + 1 < end) {
1031             begin++; *(end - 1) = '\0';
1032         }
1033         return begin;
1034     }
1035
1036     *state = end;
1037     if (*begin == '"' && *(end - 1) == '"' && begin + 1 < end) {
1038         begin++; *(end - 1) = '\0';
1039     }
1040     return begin;
1041 }
1042
1043 /**
1044  * Get a list of configuration strings, free the result with
1045  * heim_config_free_strings().
1046  *
1047  * @param context A Kerberos 5 context.
1048  * @param c a configuration section, or NULL to use the section from context
1049  * @param args a va_list of arguments
1050  *
1051  * @return TRUE or FALSE
1052  *
1053  * @ingroup heim_support
1054  */
1055
1056 char **
1057 heim_config_vget_strings(heim_context context,
1058                          const heim_config_section *c,
1059                          va_list args)
1060 {
1061     char **strings = NULL;
1062     size_t nstr = 0;
1063     const heim_config_binding *b = NULL;
1064     const char *p;
1065
1066     while((p = heim_config_vget_next(context, c, &b,
1067                                      heim_config_string, args))) {
1068         char *tmp = strdup(p);
1069         char *pos = NULL;
1070         char *s;
1071         if(tmp == NULL)
1072             goto cleanup;
1073         s = next_component_string(tmp, " \t", &pos);
1074         while(s){
1075             char **tmp2 = realloc(strings, (nstr + 1) * sizeof(*strings));
1076             if(tmp2 == NULL) {
1077                 free(tmp);
1078                 goto cleanup;
1079             }
1080             strings = tmp2;
1081             strings[nstr] = strdup(s);
1082             nstr++;
1083             if(strings[nstr-1] == NULL) {
1084                 free(tmp);
1085                 goto cleanup;
1086             }
1087             s = next_component_string(NULL, " \t", &pos);
1088         }
1089         free(tmp);
1090     }
1091     if(nstr){
1092         char **tmp = realloc(strings, (nstr + 1) * sizeof(*strings));
1093         if(tmp == NULL)
1094             goto cleanup;
1095         strings = tmp;
1096         strings[nstr] = NULL;
1097     }
1098     return strings;
1099 cleanup:
1100     while(nstr--)
1101         free(strings[nstr]);
1102     free(strings);
1103     return NULL;
1104
1105 }
1106
1107 /**
1108  * Get a list of configuration strings, free the result with
1109  * heim_config_free_strings().
1110  *
1111  * @param context A Kerberos 5 context.
1112  * @param c a configuration section, or NULL to use the section from context
1113  * @param ... a list of names, terminated with NULL.
1114  *
1115  * @return TRUE or FALSE
1116  *
1117  * @ingroup heim_support
1118  */
1119
1120 char **
1121 heim_config_get_strings(heim_context context,
1122                         const heim_config_section *c,
1123                         ...)
1124 {
1125     va_list ap;
1126     char **ret;
1127     va_start(ap, c);
1128     ret = heim_config_vget_strings(context, c, ap);
1129     va_end(ap);
1130     return ret;
1131 }
1132
1133 /**
1134  * Free the resulting strings from heim_config-get_strings() and
1135  * heim_config_vget_strings().
1136  *
1137  * @param strings strings to free
1138  *
1139  * @ingroup heim_support
1140  */
1141
1142 void
1143 heim_config_free_strings(char **strings)
1144 {
1145     char **s = strings;
1146
1147     while (s && *s) {
1148         free(*s);
1149         s++;
1150     }
1151     free(strings);
1152 }
1153
1154 /**
1155  * Like heim_config_get_bool_default() but with a va_list list of
1156  * configuration selection.
1157  *
1158  * Configuration value to a boolean value, where yes/true and any
1159  * non-zero number means TRUE and other value is FALSE.
1160  *
1161  * @param context A Kerberos 5 context.
1162  * @param c a configuration section, or NULL to use the section from context
1163  * @param def_value the default value to return if no configuration
1164  *        found in the database.
1165  * @param args a va_list of arguments
1166  *
1167  * @return TRUE or FALSE
1168  *
1169  * @ingroup heim_support
1170  */
1171
1172 int
1173 heim_config_vget_bool_default(heim_context context,
1174                               const heim_config_section *c,
1175                               int def_value,
1176                               va_list args)
1177 {
1178     const char *str;
1179     str = heim_config_vget_string(context, c, args);
1180     if (str == NULL)
1181         return def_value;
1182     return !!(strcasecmp(str, "yes") == 0 ||
1183               strcasecmp(str, "true") == 0 ||
1184               atoi(str));
1185 }
1186
1187 /**
1188  * heim_config_get_bool() will convert the configuration
1189  * option value to a boolean value, where yes/true and any non-zero
1190  * number means TRUE and other value is FALSE.
1191  *
1192  * @param context A Kerberos 5 context.
1193  * @param c a configuration section, or NULL to use the section from context
1194  * @param args a va_list of arguments
1195  *
1196  * @return TRUE or FALSE
1197  *
1198  * @ingroup heim_support
1199  */
1200
1201 int
1202 heim_config_vget_bool(heim_context context,
1203                       const heim_config_section *c,
1204                       va_list args)
1205 {
1206     return heim_config_vget_bool_default(context, c, 0, args);
1207 }
1208
1209 /**
1210  * heim_config_get_bool_default() will convert the configuration
1211  * option value to a boolean value, where yes/true and any non-zero
1212  * number means TRUE and other value is FALSE.
1213  *
1214  * @param context A Kerberos 5 context.
1215  * @param c a configuration section, or NULL to use the section from context
1216  * @param def_value the default value to return if no configuration
1217  *        found in the database.
1218  * @param ... a list of names, terminated with NULL.
1219  *
1220  * @return TRUE or FALSE
1221  *
1222  * @ingroup heim_support
1223  */
1224
1225 int
1226 heim_config_get_bool_default(heim_context context,
1227                              const heim_config_section *c,
1228                              int def_value,
1229                              ...)
1230 {
1231     va_list ap;
1232     int ret;
1233
1234     va_start(ap, def_value);
1235     ret = heim_config_vget_bool_default(context, c, def_value, ap);
1236     va_end(ap);
1237     return ret;
1238 }
1239
1240 /**
1241  * Like heim_config_get_bool() but with a va_list list of
1242  * configuration selection.
1243  *
1244  * Configuration value to a boolean value, where yes/true and any
1245  * non-zero number means TRUE and other value is FALSE.
1246  *
1247  * @param context A Kerberos 5 context.
1248  * @param c a configuration section, or NULL to use the section from context
1249  * @param ... a list of names, terminated with NULL.
1250  *
1251  * @return TRUE or FALSE
1252  *
1253  * @ingroup heim_support
1254  */
1255
1256 int
1257 heim_config_get_bool(heim_context context,
1258                      const heim_config_section *c,
1259                      ...)
1260 {
1261     va_list ap;
1262     int ret;
1263     va_start(ap, c);
1264     ret = heim_config_vget_bool (context, c, ap);
1265     va_end(ap);
1266     return ret;
1267 }
1268
1269 /**
1270  * Get the time from the configuration file using a relative time.
1271  *
1272  * Like heim_config_get_time_default() but with a va_list list of
1273  * configuration selection.
1274  *
1275  * @param context A Kerberos 5 context.
1276  * @param c a configuration section, or NULL to use the section from context
1277  * @param def_value the default value to return if no configuration
1278  *        found in the database.
1279  * @param args a va_list of arguments
1280  *
1281  * @return parsed the time (or def_value on parse error)
1282  *
1283  * @ingroup heim_support
1284  */
1285
1286 time_t
1287 heim_config_vget_time_default(heim_context context,
1288                               const heim_config_section *c,
1289                               int def_value,
1290                               va_list args)
1291 {
1292     const char *str;
1293     time_t t = -1;
1294
1295     if ((str = heim_config_vget_string(context, c, args)))
1296         t = parse_time(str, "s");
1297     return t != -1 ? t : def_value;
1298 }
1299
1300 /**
1301  * Get the time from the configuration file using a relative time, for example: 1h30s
1302  *
1303  * @param context A Kerberos 5 context.
1304  * @param c a configuration section, or NULL to use the section from context
1305  * @param args a va_list of arguments
1306  *
1307  * @return parsed the time or -1 on error
1308  *
1309  * @ingroup heim_support
1310  */
1311
1312 time_t
1313 heim_config_vget_time(heim_context context,
1314                       const heim_config_section *c,
1315                       va_list args)
1316 {
1317     return heim_config_vget_time_default(context, c, -1, args);
1318 }
1319
1320 /**
1321  * Get the time from the configuration file using a relative time, for example: 1h30s
1322  *
1323  * @param context A Kerberos 5 context.
1324  * @param c a configuration section, or NULL to use the section from context
1325  * @param def_value the default value to return if no configuration
1326  *        found in the database.
1327  * @param ... a list of names, terminated with NULL.
1328  *
1329  * @return parsed the time (or def_value on parse error)
1330  *
1331  * @ingroup heim_support
1332  */
1333
1334 time_t
1335 heim_config_get_time_default(heim_context context,
1336                              const heim_config_section *c,
1337                              int def_value,
1338                              ...)
1339 {
1340     va_list ap;
1341     time_t ret;
1342
1343     va_start(ap, def_value);
1344     ret = heim_config_vget_time_default(context, c, def_value, ap);
1345     va_end(ap);
1346     return ret;
1347 }
1348
1349 /**
1350  * Get the time from the configuration file using a relative time, for example: 1h30s
1351  *
1352  * @param context A Kerberos 5 context.
1353  * @param c a configuration section, or NULL to use the section from context
1354  * @param ... a list of names, terminated with NULL.
1355  *
1356  * @return parsed the time or -1 on error
1357  *
1358  * @ingroup heim_support
1359  */
1360
1361 time_t
1362 heim_config_get_time(heim_context context,
1363                      const heim_config_section *c,
1364                      ...)
1365 {
1366     va_list ap;
1367     int ret;
1368     va_start(ap, c);
1369     ret = heim_config_vget_time(context, c, ap);
1370     va_end(ap);
1371     return ret;
1372 }
1373
1374
1375 int
1376 heim_config_vget_int_default(heim_context context,
1377                              const heim_config_section *c,
1378                              int def_value,
1379                              va_list args)
1380 {
1381     const char *str;
1382     str = heim_config_vget_string (context, c, args);
1383     if(str == NULL)
1384         return def_value;
1385     else {
1386         char *endptr;
1387         long l;
1388         l = strtol(str, &endptr, 0);
1389         if (endptr == str)
1390             return def_value;
1391         else
1392             return l;
1393     }
1394 }
1395
1396 int
1397 heim_config_vget_int(heim_context context,
1398                      const heim_config_section *c,
1399                      va_list args)
1400 {
1401     return heim_config_vget_int_default(context, c, -1, args);
1402 }
1403
1404 int
1405 heim_config_get_int_default(heim_context context,
1406                             const heim_config_section *c,
1407                             int def_value,
1408                             ...)
1409 {
1410     va_list ap;
1411     int ret;
1412
1413     va_start(ap, def_value);
1414     ret = heim_config_vget_int_default(context, c, def_value, ap);
1415     va_end(ap);
1416     return ret;
1417 }
1418
1419 int
1420 heim_config_get_int(heim_context context,
1421                     const heim_config_section *c,
1422                     ...)
1423 {
1424     va_list ap;
1425     int ret;
1426     va_start(ap, c);
1427     ret = heim_config_vget_int (context, c, ap);
1428     va_end(ap);
1429     return ret;
1430 }
1431
1432 #ifndef HEIMDAL_SMALLER
1433 heim_error_code
1434 heim_config_parse_string_multi(heim_context context,
1435                                const char *string,
1436                                heim_config_section **res)
1437 {
1438     const char *str;
1439     unsigned lineno = 0;
1440     heim_error_code ret;
1441     struct fileptr f;
1442
1443     f.context = context;
1444     f.f = NULL;
1445     f.s = string;
1446
1447     ret = heim_config_parse_debug(&f, res, &lineno, &str);
1448     if (ret) {
1449         if (ret != HEIM_ERR_CONFIG_BADFORMAT) {
1450             ret = HEIM_ERR_CONFIG_BADFORMAT;
1451             heim_set_error_message(context, ret, "%s:%u: %s",
1452                                    "<constant>", lineno, str);
1453         }
1454         return ret;
1455     }
1456     return 0;
1457 }
1458 #endif