1 /*
2  * prof_get.c --- routines that expose the public interfaces for
3  * 	querying items from the profile.
4  *
5  */
6 
7 #include "prof_int.h"
8 #include <stdio.h>
9 #include <string.h>
10 #ifdef HAVE_STDLIB_H
11 #include <stdlib.h>
12 #endif
13 #include <errno.h>
14 #include <limits.h>
15 
16 /*
17  * These functions --- init_list(), end_list(), and add_to_list() are
18  * internal functions used to build up a null-terminated char ** list
19  * of strings to be returned by functions like profile_get_values.
20  *
21  * The profile_string_list structure is used for internal booking
22  * purposes to build up the list, which is returned in *ret_list by
23  * the end_list() function.
24  *
25  * The publicly exported interface for freeing char** list is
26  * profile_free_list().
27  */
28 
29 struct profile_string_list {
30 	char	**list;
31 	int	num;
32 	int	max;
33 };
34 
35 /*
36  * Initialize the string list abstraction.
37  */
38 static errcode_t init_list(struct profile_string_list *list)
39 {
40 	list->num = 0;
41 	list->max = 10;
42 	list->list = malloc(list->max * sizeof(char *));
43 	if (list->list == 0)
44 		return ENOMEM;
45 	list->list[0] = 0;
46 	return 0;
47 }
48 
49 /*
50  * Free any memory left over in the string abstraction, returning the
51  * built up list in *ret_list if it is non-null.
52  */
53 static void end_list(struct profile_string_list *list, char ***ret_list)
54 {
55 	char	**cp;
56 
57 	if (list == 0)
58 		return;
59 
60 	if (ret_list) {
61 		*ret_list = list->list;
62 		return;
63 	} else {
64 		for (cp = list->list; *cp; cp++)
65 			free(*cp);
66 		free(list->list);
67 	}
68 	list->num = list->max = 0;
69 	list->list = 0;
70 }
71 
72 /*
73  * Add a string to the list.
74  */
75 static errcode_t add_to_list(struct profile_string_list *list, const char *str)
76 {
77 	char 	*newstr, **newlist;
78 	int	newmax;
79 
80 	if (list->num+1 >= list->max) {
81 		newmax = list->max + 10;
82 		newlist = realloc(list->list, newmax * sizeof(char *));
83 		if (newlist == 0)
84 			return ENOMEM;
85 		list->max = newmax;
86 		list->list = newlist;
87 	}
88 	newstr = malloc(strlen(str)+1);
89 	if (newstr == 0)
90 		return ENOMEM;
91 	strcpy(newstr, str);
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  */
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  */
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 errcode_t KRB5_CALLCONV
132 profile_get_values(profile_t profile, const char *const *names,
133 		   char ***ret_values)
134 {
135 	errcode_t		retval;
136 	void			*state;
137 	char			*value;
138 	struct profile_string_list values;
139 
140 	if ((retval = profile_node_iterator_create(profile, names,
141 						   PROFILE_ITER_RELATIONS_ONLY,
142 						   &state)))
143 		return retval;
144 
145 	if ((retval = init_list(&values)))
146 		return retval;
147 
148 	do {
149 		if ((retval = profile_node_iterator(&state, 0, 0, &value)))
150 			goto cleanup;
151 		if (value)
152 			add_to_list(&values, value);
153 	} while (state);
154 
155 	if (values.num == 0) {
156 		retval = PROF_NO_RELATION;
157 		goto cleanup;
158 	}
159 
160 	end_list(&values, ret_values);
161 	return 0;
162 
163 cleanup:
164 	end_list(&values, 0);
165 	return retval;
166 }
167 
168 /*
169  * This function only gets the first value from the file; it is a
170  * helper function for profile_get_string, profile_get_integer, etc.
171  */
172 errcode_t profile_get_value(profile_t profile, const char **names,
173 			    const char **ret_value)
174 {
175 	errcode_t		retval;
176 	void			*state;
177 	char			*value;
178 
179 	if ((retval = profile_node_iterator_create(profile, names,
180 						   PROFILE_ITER_RELATIONS_ONLY,
181 						   &state)))
182 		return retval;
183 
184 	if ((retval = profile_node_iterator(&state, 0, 0, &value)))
185 		goto cleanup;
186 
187 	if (value)
188 		*ret_value = value;
189 	else
190 		retval = PROF_NO_RELATION;
191 
192 cleanup:
193 	profile_node_iterator_free(&state);
194 	return retval;
195 }
196 
197 errcode_t KRB5_CALLCONV
198 profile_get_string(profile_t profile, const char *name, const char *subname,
199 		   const char *subsubname, const char *def_val,
200 		   char **ret_string)
201 {
202 	const char	*value;
203 	errcode_t	retval;
204 	const char	*names[4];
205 
206 	if (profile) {
207 		names[0] = name;
208 		names[1] = subname;
209 		names[2] = subsubname;
210 		names[3] = 0;
211 		retval = profile_get_value(profile, names, &value);
212 		if (retval == PROF_NO_SECTION || retval == PROF_NO_RELATION)
213 			value = def_val;
214 		else if (retval)
215 			return retval;
216 	} else
217 		value = def_val;
218 
219 	if (value) {
220 		*ret_string = malloc(strlen(value)+1);
221 		if (*ret_string == 0)
222 			return ENOMEM;
223 		strcpy(*ret_string, value);
224 	} else
225 		*ret_string = 0;
226 	return 0;
227 }
228 
229 errcode_t KRB5_CALLCONV
230 profile_get_integer(profile_t profile, const char *name, const char *subname,
231 		    const char *subsubname, int def_val, int *ret_int)
232 {
233 	const char	*value;
234 	errcode_t	retval;
235 	const char	*names[4];
236 	char            *end_value;
237 	long		ret_long;
238 
239 	*ret_int = def_val;
240 	if (profile == 0)
241 		return 0;
242 
243 	names[0] = name;
244 	names[1] = subname;
245 	names[2] = subsubname;
246 	names[3] = 0;
247 	retval = profile_get_value(profile, names, &value);
248 	if (retval == PROF_NO_SECTION || retval == PROF_NO_RELATION) {
249 		*ret_int = def_val;
250 		return 0;
251 	} else if (retval)
252 		return retval;
253 
254 	if (value[0] == 0)
255 	    /* Empty string is no good.  */
256 	    return PROF_BAD_INTEGER;
257 	errno = 0;
258 	ret_long = strtol (value, &end_value, 10);
259 
260 	/* Overflow or underflow.  */
261 	if ((ret_long == LONG_MIN || ret_long == LONG_MAX) && errno != 0)
262 	    return PROF_BAD_INTEGER;
263 	/* Value outside "int" range.  */
264 	if ((long) (int) ret_long != ret_long)
265 	    return PROF_BAD_INTEGER;
266 	/* Garbage in string.  */
267 	if (end_value != value + strlen (value))
268 	    return PROF_BAD_INTEGER;
269 
270 
271 	*ret_int = ret_long;
272 	return 0;
273 }
274 
275 static const char *const conf_yes[] = {
276     "y", "yes", "true", "t", "1", "on",
277     0,
278 };
279 
280 static const char *const conf_no[] = {
281     "n", "no", "false", "nil", "0", "off",
282     0,
283 };
284 
285 static errcode_t
286 profile_parse_boolean(const char *s, int *ret_boolean)
287 {
288     const char *const *p;
289 
290     if (ret_boolean == NULL)
291     	return PROF_EINVAL;
292 
293     for(p=conf_yes; *p; p++) {
294 		if (!strcasecmp(*p,s)) {
295 			*ret_boolean = 1;
296 	    	return 0;
297 		}
298     }
299 
300     for(p=conf_no; *p; p++) {
301 		if (!strcasecmp(*p,s)) {
302 			*ret_boolean = 0;
303 			return 0;
304 		}
305     }
306 
307 	return PROF_BAD_BOOLEAN;
308 }
309 
310 errcode_t KRB5_CALLCONV
311 profile_get_boolean(profile_t profile, const char *name, const char *subname,
312 		    const char *subsubname, int def_val, int *ret_boolean)
313 {
314 	const char	*value;
315 	errcode_t	retval;
316 	const char	*names[4];
317 
318 	if (profile == 0) {
319 		*ret_boolean = def_val;
320 		return 0;
321 	}
322 
323 	names[0] = name;
324 	names[1] = subname;
325 	names[2] = subsubname;
326 	names[3] = 0;
327 	retval = profile_get_value(profile, names, &value);
328 	if (retval == PROF_NO_SECTION || retval == PROF_NO_RELATION) {
329 		*ret_boolean = def_val;
330 		return 0;
331 	} else if (retval)
332 		return retval;
333 
334 	return profile_parse_boolean (value, ret_boolean);
335 }
336 
337 /*
338  * This function will return the list of the names of subections in the
339  * under the specified section name.
340  */
341 errcode_t KRB5_CALLCONV
342 profile_get_subsection_names(profile_t profile, const char **names,
343 			     char ***ret_names)
344 {
345 	errcode_t		retval;
346 	void			*state;
347 	char			*name;
348 	struct profile_string_list values;
349 
350 	if ((retval = profile_node_iterator_create(profile, names,
351 		   PROFILE_ITER_LIST_SECTION | PROFILE_ITER_SECTIONS_ONLY,
352 		   &state)))
353 		return retval;
354 
355 	if ((retval = init_list(&values)))
356 		return retval;
357 
358 	do {
359 		if ((retval = profile_node_iterator(&state, 0, &name, 0)))
360 			goto cleanup;
361 		if (name)
362 			add_to_list(&values, name);
363 	} while (state);
364 
365 	end_list(&values, ret_names);
366 	return 0;
367 
368 cleanup:
369 	end_list(&values, 0);
370 	return retval;
371 }
372 
373 /*
374  * This function will return the list of the names of relations in the
375  * under the specified section name.
376  */
377 errcode_t KRB5_CALLCONV
378 profile_get_relation_names(profile_t profile, const char **names,
379 			   char ***ret_names)
380 {
381 	errcode_t		retval;
382 	void			*state;
383 	char			*name;
384 	struct profile_string_list values;
385 
386 	if ((retval = profile_node_iterator_create(profile, names,
387 		   PROFILE_ITER_LIST_SECTION | PROFILE_ITER_RELATIONS_ONLY,
388 		   &state)))
389 		return retval;
390 
391 	if ((retval = init_list(&values)))
392 		return retval;
393 
394 	do {
395 		if ((retval = profile_node_iterator(&state, 0, &name, 0)))
396 			goto cleanup;
397 		if (name && !is_list_member(&values, name))
398 			add_to_list(&values, name);
399 	} while (state);
400 
401 	end_list(&values, ret_names);
402 	return 0;
403 
404 cleanup:
405 	end_list(&values, 0);
406 	return retval;
407 }
408 
409 errcode_t KRB5_CALLCONV
410 profile_iterator_create(profile_t profile, const char *const *names, int flags,
411 			void **ret_iter)
412 {
413 	return profile_node_iterator_create(profile, names, flags, ret_iter);
414 }
415 
416 void KRB5_CALLCONV
417 profile_iterator_free(void **iter_p)
418 {
419 	profile_node_iterator_free(iter_p);
420 }
421 
422 errcode_t KRB5_CALLCONV
423 profile_iterator(void **iter_p, char **ret_name, char **ret_value)
424 {
425 	char *name, *value;
426 	errcode_t	retval;
427 
428 	retval = profile_node_iterator(iter_p, 0, &name, &value);
429 	if (retval)
430 		return retval;
431 
432 	if (ret_name) {
433 		if (name) {
434 			*ret_name = malloc(strlen(name)+1);
435 			if (!*ret_name)
436 				return ENOMEM;
437 			strcpy(*ret_name, name);
438 		} else
439 			*ret_name = 0;
440 	}
441 	if (ret_value) {
442 		if (value) {
443 			*ret_value = malloc(strlen(value)+1);
444 			if (!*ret_value) {
445 				if (ret_name) {
446 					free(*ret_name);
447 					*ret_name = 0;
448 				}
449 				return ENOMEM;
450 			}
451 			strcpy(*ret_value, value);
452 		} else
453 			*ret_value = 0;
454 	}
455 	return 0;
456 }
457 
458 void KRB5_CALLCONV
459 profile_release_string(char *str)
460 {
461 	free(str);
462 }
463