1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3 * prof_get.c --- routines that expose the public interfaces for
4 * querying items from the profile.
5 *
6 */
7
8 #include "prof_int.h"
9 #include <stdio.h>
10 #include <string.h>
11 #ifdef HAVE_STDLIB_H
12 #include <stdlib.h>
13 #endif
14 #include <errno.h>
15 #include <limits.h>
16
17 /*
18 * These functions --- init_list(), end_list(), and add_to_list() are
19 * internal functions used to build up a null-terminated char ** list
20 * of strings to be returned by functions like profile_get_values.
21 *
22 * The profile_string_list structure is used for internal booking
23 * purposes to build up the list, which is returned in *ret_list by
24 * the end_list() function.
25 *
26 * The publicly exported interface for freeing char** list is
27 * profile_free_list().
28 */
29
30 struct profile_string_list {
31 char **list;
32 unsigned int num;
33 unsigned int max;
34 };
35
36 /*
37 * Initialize the string list abstraction.
38 */
init_list(struct profile_string_list * list)39 static errcode_t init_list(struct profile_string_list *list)
40 {
41 list->num = 0;
42 list->max = 10;
43 list->list = malloc(list->max * sizeof(char *));
44 if (list->list == 0)
45 return ENOMEM;
46 list->list[0] = 0;
47 return 0;
48 }
49
50 /*
51 * Free any memory left over in the string abstraction, returning the
52 * built up list in *ret_list if it is non-null.
53 */
end_list(struct profile_string_list * list,char *** ret_list)54 static void end_list(struct profile_string_list *list, char ***ret_list)
55 {
56 char **cp;
57
58 if (list == 0)
59 return;
60
61 if (ret_list) {
62 *ret_list = list->list;
63 return;
64 } else {
65 for (cp = list->list; cp && *cp; cp++)
66 free(*cp);
67 free(list->list);
68 }
69 list->num = list->max = 0;
70 list->list = 0;
71 }
72
73 /*
74 * Add a string to the list.
75 */
add_to_list(struct profile_string_list * list,const char * str)76 static errcode_t add_to_list(struct profile_string_list *list, const char *str)
77 {
78 char *newstr, **newlist;
79 unsigned int newmax;
80
81 if (list->num+1 >= list->max) {
82 newmax = list->max + 10;
83 newlist = realloc(list->list, newmax * sizeof(char *));
84 if (newlist == 0)
85 return ENOMEM;
86 list->max = newmax;
87 list->list = newlist;
88 }
89 newstr = strdup(str);
90 if (newstr == 0)
91 return ENOMEM;
92
93 list->list[list->num++] = newstr;
94 list->list[list->num] = 0;
95 return 0;
96 }
97
98 /*
99 * Return TRUE if the string is already a member of the list.
100 */
is_list_member(struct profile_string_list * list,const char * str)101 static int is_list_member(struct profile_string_list *list, const char *str)
102 {
103 char **cpp;
104
105 if (!list->list)
106 return 0;
107
108 for (cpp = list->list; *cpp; cpp++) {
109 if (!strcmp(*cpp, str))
110 return 1;
111 }
112 return 0;
113 }
114
115 /*
116 * This function frees a null-terminated list as returned by
117 * profile_get_values.
118 */
profile_free_list(char ** list)119 void KRB5_CALLCONV profile_free_list(char **list)
120 {
121 char **cp;
122
123 if (list == 0)
124 return;
125
126 for (cp = list; *cp; cp++)
127 free(*cp);
128 free(list);
129 }
130
131 /* Look up a relation in a vtable profile. */
132 static errcode_t
get_values_vt(profile_t profile,const char * const * names,char *** ret_values)133 get_values_vt(profile_t profile, const char *const *names, char ***ret_values)
134 {
135 errcode_t retval;
136 char **vtvalues, **val;
137 struct profile_string_list values;
138
139 retval = profile->vt->get_values(profile->cbdata, names, &vtvalues);
140 if (retval)
141 return retval;
142
143 /* Copy the result into memory we can free. */
144 retval = init_list(&values);
145 if (retval == 0) {
146 for (val = vtvalues; *val; val++)
147 add_to_list(&values, *val);
148 end_list(&values, ret_values);
149 }
150
151 profile->vt->free_values(profile->cbdata, vtvalues);
152 return retval;
153 }
154
155 errcode_t KRB5_CALLCONV
profile_get_values(profile_t profile,const char * const * names,char *** ret_values)156 profile_get_values(profile_t profile, const char *const *names,
157 char ***ret_values)
158 {
159 errcode_t retval;
160 void *state = NULL;
161 char *value;
162 struct profile_string_list values;
163
164 *ret_values = NULL;
165 if (!profile)
166 return PROF_NO_PROFILE;
167 if (profile->vt)
168 return get_values_vt(profile, names, ret_values);
169
170 if ((retval = profile_node_iterator_create(profile, names,
171 PROFILE_ITER_RELATIONS_ONLY,
172 &state)))
173 return retval;
174
175 retval = init_list(&values);
176 if (retval)
177 goto cleanup;
178
179 do {
180 if ((retval = profile_node_iterator(&state, 0, 0, &value)))
181 goto cleanup;
182 if (value)
183 add_to_list(&values, value);
184 } while (state);
185
186 if (values.num == 0) {
187 retval = PROF_NO_RELATION;
188 goto cleanup;
189 }
190
191 cleanup:
192 end_list(&values, retval ? NULL : ret_values);
193 profile_node_iterator_free(&state);
194 return retval;
195 }
196
197 /* Look up a relation in a vtable profile and return the first value in the
198 * result. */
199 static errcode_t
get_value_vt(profile_t profile,const char * const * names,char ** ret_value)200 get_value_vt(profile_t profile, const char *const *names, char **ret_value)
201 {
202 errcode_t retval;
203 char **vtvalues;
204
205 retval = profile->vt->get_values(profile->cbdata, names, &vtvalues);
206 if (retval)
207 return retval;
208 *ret_value = strdup(*vtvalues);
209 if (*ret_value == NULL)
210 retval = ENOMEM;
211 profile->vt->free_values(profile->cbdata, vtvalues);
212 return retval;
213 }
214
215 /*
216 * This function only gets the first value from the file; it is a
217 * helper function for profile_get_string, profile_get_integer, etc.
218 */
profile_get_value(profile_t profile,const char ** names,char ** ret_value)219 errcode_t profile_get_value(profile_t profile, const char **names,
220 char **ret_value)
221 {
222 errcode_t retval;
223 void *state;
224 char *value;
225
226 *ret_value = NULL;
227 if (!profile)
228 return PROF_NO_PROFILE;
229 if (profile->vt)
230 return get_value_vt(profile, names, ret_value);
231
232 retval = profile_iterator_create(profile, names,
233 PROFILE_ITER_RELATIONS_ONLY, &state);
234 if (retval)
235 return retval;
236
237 retval = profile_iterator(&state, NULL, &value);
238 if (retval)
239 goto cleanup;
240
241 if (value)
242 *ret_value = value;
243 else
244 retval = PROF_NO_RELATION;
245
246 cleanup:
247 profile_iterator_free(&state);
248 return retval;
249 }
250
251 errcode_t KRB5_CALLCONV
profile_get_string(profile_t profile,const char * name,const char * subname,const char * subsubname,const char * def_val,char ** ret_string)252 profile_get_string(profile_t profile, const char *name, const char *subname,
253 const char *subsubname, const char *def_val,
254 char **ret_string)
255 {
256 char *value;
257 errcode_t retval;
258 const char *names[4];
259
260 if (profile) {
261 names[0] = name;
262 names[1] = subname;
263 names[2] = subsubname;
264 names[3] = 0;
265 retval = profile_get_value(profile, names, &value);
266 if (retval == 0) {
267 *ret_string = value;
268 return 0;
269 } else if (retval != PROF_NO_SECTION && retval != PROF_NO_RELATION)
270 return retval;
271 }
272
273 if (def_val) {
274 *ret_string = strdup(def_val);
275 if (*ret_string == NULL)
276 return ENOMEM;
277 } else
278 *ret_string = NULL;
279 return 0;
280 }
281
282 static errcode_t
parse_int(const char * value,int * ret_int)283 parse_int(const char *value, int *ret_int)
284 {
285 char *end_value;
286 long ret_long;
287
288 if (value[0] == 0)
289 /* Empty string is no good. */
290 return PROF_BAD_INTEGER;
291 errno = 0;
292 ret_long = strtol(value, &end_value, 10);
293
294 /* Overflow or underflow. */
295 if ((ret_long == LONG_MIN || ret_long == LONG_MAX) && errno != 0)
296 return PROF_BAD_INTEGER;
297 /* Value outside "int" range. */
298 if ((long) (int) ret_long != ret_long)
299 return PROF_BAD_INTEGER;
300 /* Garbage in string. */
301 if (end_value != value + strlen (value))
302 return PROF_BAD_INTEGER;
303
304 *ret_int = ret_long;
305 return 0;
306 }
307
308 errcode_t KRB5_CALLCONV
profile_get_integer(profile_t profile,const char * name,const char * subname,const char * subsubname,int def_val,int * ret_int)309 profile_get_integer(profile_t profile, const char *name, const char *subname,
310 const char *subsubname, int def_val, int *ret_int)
311 {
312 char *value;
313 errcode_t retval;
314 const char *names[4];
315
316 *ret_int = def_val;
317 if (profile == 0)
318 return 0;
319
320 names[0] = name;
321 names[1] = subname;
322 names[2] = subsubname;
323 names[3] = 0;
324 retval = profile_get_value(profile, names, &value);
325 if (retval == PROF_NO_SECTION || retval == PROF_NO_RELATION) {
326 *ret_int = def_val;
327 return 0;
328 } else if (retval)
329 return retval;
330
331 retval = parse_int(value, ret_int);
332 free(value);
333 return retval;
334 }
335
336 static const char *const conf_yes[] = {
337 "y", "yes", "true", "t", "1", "on",
338 0,
339 };
340
341 static const char *const conf_no[] = {
342 "n", "no", "false", "nil", "0", "off",
343 0,
344 };
345
346 static errcode_t
profile_parse_boolean(const char * s,int * ret_boolean)347 profile_parse_boolean(const char *s, int *ret_boolean)
348 {
349 const char *const *p;
350
351 if (ret_boolean == NULL)
352 return PROF_EINVAL;
353
354 for(p=conf_yes; *p; p++) {
355 if (!strcasecmp(*p,s)) {
356 *ret_boolean = 1;
357 return 0;
358 }
359 }
360
361 for(p=conf_no; *p; p++) {
362 if (!strcasecmp(*p,s)) {
363 *ret_boolean = 0;
364 return 0;
365 }
366 }
367
368 return PROF_BAD_BOOLEAN;
369 }
370
371 errcode_t KRB5_CALLCONV
profile_get_boolean(profile_t profile,const char * name,const char * subname,const char * subsubname,int def_val,int * ret_boolean)372 profile_get_boolean(profile_t profile, const char *name, const char *subname,
373 const char *subsubname, int def_val, int *ret_boolean)
374 {
375 char *value;
376 errcode_t retval;
377 const char *names[4];
378
379 if (profile == 0) {
380 *ret_boolean = def_val;
381 return 0;
382 }
383
384 names[0] = name;
385 names[1] = subname;
386 names[2] = subsubname;
387 names[3] = 0;
388 retval = profile_get_value(profile, names, &value);
389 if (retval == PROF_NO_SECTION || retval == PROF_NO_RELATION) {
390 *ret_boolean = def_val;
391 return 0;
392 } else if (retval)
393 return retval;
394
395 retval = profile_parse_boolean(value, ret_boolean);
396 free(value);
397 return retval;
398 }
399
400 /*
401 * This function will return the list of the names of subections in the
402 * under the specified section name.
403 */
404 errcode_t KRB5_CALLCONV
profile_get_subsection_names(profile_t profile,const char ** names,char *** ret_names)405 profile_get_subsection_names(profile_t profile, const char **names,
406 char ***ret_names)
407 {
408 errcode_t retval;
409 void *state;
410 char *name;
411 struct profile_string_list values;
412
413 if ((retval = profile_iterator_create(profile, names,
414 PROFILE_ITER_LIST_SECTION |
415 PROFILE_ITER_SECTIONS_ONLY,
416 &state)))
417 return retval;
418
419 if ((retval = init_list(&values)))
420 return retval;
421
422 do {
423 if ((retval = profile_iterator(&state, &name, NULL)))
424 goto cleanup;
425 if (name)
426 add_to_list(&values, name);
427 free(name);
428 } while (state);
429
430 end_list(&values, ret_names);
431 return 0;
432
433 cleanup:
434 end_list(&values, 0);
435 return retval;
436 }
437
438 /*
439 * This function will return the list of the names of relations in the
440 * under the specified section name.
441 */
442 errcode_t KRB5_CALLCONV
profile_get_relation_names(profile_t profile,const char ** names,char *** ret_names)443 profile_get_relation_names(profile_t profile, const char **names,
444 char ***ret_names)
445 {
446 errcode_t retval;
447 void *state;
448 char *name;
449 struct profile_string_list values;
450
451 if ((retval = profile_iterator_create(profile, names,
452 PROFILE_ITER_LIST_SECTION |
453 PROFILE_ITER_RELATIONS_ONLY,
454 &state)))
455 return retval;
456
457 if ((retval = init_list(&values)))
458 return retval;
459
460 do {
461 if ((retval = profile_iterator(&state, &name, NULL)))
462 goto cleanup;
463 if (name && !is_list_member(&values, name))
464 add_to_list(&values, name);
465 free(name);
466 } while (state);
467
468 end_list(&values, ret_names);
469 return 0;
470
471 cleanup:
472 end_list(&values, 0);
473 return retval;
474 }
475
476 struct profile_iterator {
477 prf_magic_t magic;
478 profile_t profile;
479 void *idata;
480 };
481
482 errcode_t KRB5_CALLCONV
profile_iterator_create(profile_t profile,const char * const * names,int flags,void ** ret_iter)483 profile_iterator_create(profile_t profile, const char *const *names, int flags,
484 void **ret_iter)
485 {
486 struct profile_iterator *iter;
487 errcode_t retval;
488
489 *ret_iter = NULL;
490 if (!profile)
491 return PROF_NO_PROFILE;
492
493 iter = malloc(sizeof(*iter));
494 if (iter == NULL)
495 return ENOMEM;
496 iter->magic = PROF_MAGIC_ITERATOR;
497 iter->profile = profile;
498
499 /* Create the underlying iterator representation using the vtable or the
500 * built-in node iterator. */
501 if (profile->vt) {
502 if (!profile->vt->iterator_create)
503 retval = PROF_UNSUPPORTED;
504 else
505 retval = profile->vt->iterator_create(profile->cbdata, names,
506 flags, &iter->idata);
507 } else {
508 retval = profile_node_iterator_create(profile, names, flags,
509 &iter->idata);
510 }
511 if (retval) {
512 free(iter);
513 return retval;
514 }
515
516 *ret_iter = iter;
517 return 0;
518 }
519
520 void KRB5_CALLCONV
profile_iterator_free(void ** iter_p)521 profile_iterator_free(void **iter_p)
522 {
523 struct profile_iterator *iter;
524 profile_t profile;
525
526 if (!iter_p)
527 return;
528 iter = *iter_p;
529 if (!iter || iter->magic != PROF_MAGIC_ITERATOR)
530 return;
531 profile = iter->profile;
532 if (profile->vt)
533 profile->vt->iterator_free(profile->cbdata, iter->idata);
534 else
535 profile_node_iterator_free(&iter->idata);
536 free(iter);
537 *iter_p = NULL;
538 }
539
540 /* Make copies of name and value into *ret_name and *ret_value. Handle null
541 * values of any argument. */
542 static errcode_t
set_results(const char * name,const char * value,char ** ret_name,char ** ret_value)543 set_results(const char *name, const char *value, char **ret_name,
544 char **ret_value)
545 {
546 char *name_copy = NULL, *value_copy = NULL;
547
548 if (ret_name && name) {
549 name_copy = strdup(name);
550 if (name_copy == NULL)
551 goto oom;
552 }
553 if (ret_value && value) {
554 value_copy = strdup(value);
555 if (value_copy == NULL)
556 goto oom;
557 }
558 if (ret_name)
559 *ret_name = name_copy;
560 if (ret_value)
561 *ret_value = value_copy;
562 return 0;
563 oom:
564 free(name_copy);
565 free(value_copy);
566 return ENOMEM;
567 }
568
569 errcode_t KRB5_CALLCONV
profile_iterator(void ** iter_p,char ** ret_name,char ** ret_value)570 profile_iterator(void **iter_p, char **ret_name, char **ret_value)
571 {
572 char *name, *value;
573 errcode_t retval;
574 struct profile_iterator *iter = *iter_p;
575 profile_t profile;
576
577 if (ret_name)
578 *ret_name = NULL;
579 if (ret_value)
580 *ret_value = NULL;
581 if (iter == NULL || iter->magic != PROF_MAGIC_ITERATOR)
582 return PROF_MAGIC_ITERATOR;
583 profile = iter->profile;
584
585 if (profile->vt) {
586 retval = profile->vt->iterator(profile->cbdata, iter->idata, &name,
587 &value);
588 if (retval)
589 return retval;
590 if (name == NULL) {
591 profile->vt->iterator_free(profile->cbdata, iter->idata);
592 free(iter);
593 *iter_p = NULL;
594 }
595 retval = set_results(name, value, ret_name, ret_value);
596 if (name)
597 profile->vt->free_string(profile->cbdata, name);
598 if (value)
599 profile->vt->free_string(profile->cbdata, value);
600 return retval;
601 }
602
603 retval = profile_node_iterator(&iter->idata, 0, &name, &value);
604 if (iter->idata == NULL) {
605 free(iter);
606 *iter_p = NULL;
607 }
608 if (retval)
609 return retval;
610 return set_results(name, value, ret_name, ret_value);
611 }
612
613 void KRB5_CALLCONV
profile_release_string(char * str)614 profile_release_string(char *str)
615 {
616 free(str);
617 }
618