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