1 /*
2 * Trivial smb.conf parsing code
3 * iniparser compatibility layer.
4 *
5 * Copyright Jeremy Allison <jra@samba.org> 2014
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, and the entire permission notice in its entirety,
12 * including the disclaimer of warranties.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. The name of the author may not be used to endorse or promote
17 * products derived from this software without specific prior
18 * written permission.
19 *
20 * ALTERNATIVELY, this product may be distributed under the terms of
21 * the GNU Public License Version 3 or later, in which case the provisions
22 * of the GPL are required INSTEAD OF the above restrictions. (This clause is
23 * necessary due to a potential bad interaction between the GPL and
24 * the restrictions contained in a BSD-style copyright.)
25 *
26 * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED
27 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
28 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
29 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
30 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
31 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
32 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
34 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
36 * OF THE POSSIBILITY OF SUCH DAMAGE.
37 */
38
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <stdbool.h>
42 #include <ctype.h>
43 #include <errno.h>
44 #include <string.h>
45 #include <stddef.h>
46 #include "tini.h"
47 #include "tiniparser.h"
48
49 struct tiniparser_entry {
50 struct tiniparser_entry *next_entry;
51 char *key;
52 char *value;
53 };
54
55 struct tiniparser_section {
56 struct tiniparser_section *next_section;
57 struct tiniparser_entry *entry_list;
58 char section_name[];
59 };
60
61 struct tiniparser_dictionary {
62 struct tiniparser_section *section_list;
63 };
64
65 /*
66 * Find a section from a given key.
67 * Also return start of subkey.
68 * Return NULL if section name can't be found,
69 * if no section name given, or no subkey given.
70 */
71
find_section(struct tiniparser_dictionary * d,const char * key,const char ** subkey)72 static struct tiniparser_section *find_section(struct tiniparser_dictionary *d,
73 const char *key,
74 const char **subkey)
75 {
76 struct tiniparser_section *curr_section;
77 const char *p;
78 size_t section_len;
79
80 if (key == NULL) {
81 return NULL;
82 }
83 p = strchr(key, ':');
84 if (p == NULL) {
85 /* No section. */
86 return NULL;
87 }
88
89 section_len = p - key;
90 /* Ensure we have at least one character of section name. */
91 if (section_len == 0) {
92 return NULL;
93 }
94 /* Ensure we have at least one character of subkey. */
95 if (p[1] == '\0') {
96 return NULL;
97 }
98
99 for (curr_section = d->section_list;
100 curr_section;
101 curr_section = curr_section->next_section) {
102 /*
103 * Check if the key section matches the
104 * section name *exactly* (with terminating
105 * null after section_len characters.
106 */
107 if ((strncasecmp(key, curr_section->section_name, section_len) == 0) &&
108 (curr_section->section_name[section_len] == '\0')) {
109 *subkey = p+1;
110 return curr_section;
111 }
112 }
113 return NULL;
114 }
115
find_entry(struct tiniparser_section * section,const char * key)116 static struct tiniparser_entry *find_entry(struct tiniparser_section *section,
117 const char *key)
118 {
119 struct tiniparser_entry *curr_entry;
120
121 for (curr_entry = section->entry_list;
122 curr_entry;
123 curr_entry = curr_entry->next_entry) {
124 if (strcasecmp(key,
125 curr_entry->key) == 0) {
126 return curr_entry;
127 }
128 }
129 return NULL;
130 }
131
tiniparser_getstring(struct tiniparser_dictionary * d,const char * key,const char * default_value)132 const char *tiniparser_getstring(struct tiniparser_dictionary *d,
133 const char *key,
134 const char *default_value)
135 {
136 struct tiniparser_section *section;
137 struct tiniparser_entry *entry;
138 const char *subkey;
139
140 section = find_section(d, key, &subkey);
141 if (section == NULL) {
142 return default_value;
143 }
144
145 entry = find_entry(section, subkey);
146 if (entry == NULL) {
147 return default_value;
148 }
149
150 return entry->value;
151 }
152
153
tiniparser_getboolean(struct tiniparser_dictionary * d,const char * key,bool default_value)154 bool tiniparser_getboolean(struct tiniparser_dictionary *d,
155 const char *key,
156 bool default_value)
157 {
158 const char *value = tiniparser_getstring(d, key, NULL);
159
160 if (value == NULL) {
161 return default_value;
162 }
163
164 switch(value[0]) {
165 case '1':
166 case 'T':
167 case 't':
168 case 'y':
169 case 'Y':
170 return true;
171 case '0':
172 case 'F':
173 case 'f':
174 case 'n':
175 case 'N':
176 return false;
177 default:
178 break;
179 }
180
181 return default_value;
182 }
183
tiniparser_getint(struct tiniparser_dictionary * d,const char * key,int default_value)184 int tiniparser_getint(struct tiniparser_dictionary *d,
185 const char *key,
186 int default_value)
187 {
188 const char *value = tiniparser_getstring(d, key, NULL);
189
190 if (value == NULL) {
191 return default_value;
192 }
193
194 return (int)strtol(value, NULL, 0);
195 }
196
value_parser(const char * key,const char * value,void * private_data)197 static bool value_parser(const char *key,
198 const char *value,
199 void *private_data)
200 {
201 struct tiniparser_dictionary *d =
202 (struct tiniparser_dictionary *)private_data;
203 struct tiniparser_section *section = d->section_list;
204 struct tiniparser_entry *entry = NULL;
205 size_t val_len;
206 size_t key_len;
207
208 if (section == NULL) {
209 return false;
210 }
211 if (key == NULL) {
212 return false;
213 }
214 if (value == NULL) {
215 return false;
216 }
217
218 key_len = strlen(key) + 1;
219 val_len = strlen(value) + 1;
220
221 entry = find_entry(section, key);
222 if (entry) {
223 /* Replace current value. */
224 char *new_val = malloc(val_len);
225 if (new_val == NULL) {
226 return false;
227 }
228 memcpy(new_val, value, val_len);
229 free(entry->value);
230 entry->value = new_val;
231 return true;
232 }
233
234 /* Create a new entry. */
235 entry = malloc(sizeof(struct tiniparser_entry));
236 if (entry == NULL) {
237 return false;
238 }
239 entry->key = malloc(key_len);
240 if (entry->key == NULL) {
241 free(entry);
242 return false;
243 }
244 memcpy(entry->key, key, key_len);
245
246 entry->value = malloc(val_len);
247 if (entry->value == NULL) {
248 free(entry->key);
249 free(entry);
250 return false;
251 }
252 memcpy(entry->value, value, val_len);
253
254 entry->next_entry = section->entry_list;
255 section->entry_list = entry;
256 return true;
257 }
258
section_parser(const char * section_name,void * private_data)259 static bool section_parser(const char *section_name,
260 void *private_data)
261 {
262 struct tiniparser_section **pp_section;
263 struct tiniparser_section *new_section;
264 struct tiniparser_dictionary *d =
265 (struct tiniparser_dictionary *)private_data;
266 size_t section_name_len;
267
268 if (section_name == NULL) {
269 return false;
270 }
271
272 /* Section names can't contain ':' */
273 if (strchr(section_name, ':') != NULL) {
274 return false;
275 }
276
277 /* Do we already have this section ? */
278 for (pp_section = &d->section_list;
279 *pp_section;
280 pp_section = &(*pp_section)->next_section) {
281 if (strcasecmp(section_name,
282 (*pp_section)->section_name) == 0) {
283 /*
284 * Move to the front of the list for
285 * value_parser() to find it.
286 */
287
288 /* First save current entry. */
289 struct tiniparser_section *curr_section = *pp_section;
290
291 /* Now unlink current entry from list. */
292 *pp_section = curr_section->next_section;
293
294 /* Make current entry next point to whole list. */
295 curr_section->next_section = d->section_list;
296
297 /* And replace list with current entry at start. */
298 d->section_list = curr_section;
299
300 return true;
301 }
302 }
303
304 section_name_len = strlen(section_name) + 1;
305
306 /* Create new section. */
307 new_section = malloc(
308 offsetof(struct tiniparser_section, section_name) +
309 section_name_len);
310 if (new_section == NULL) {
311 return false;
312 }
313
314 memcpy(new_section->section_name, section_name, section_name_len);
315
316 new_section->entry_list = NULL;
317
318 /* Add it to the head of the singly linked list. */
319 new_section->next_section = d->section_list;
320 d->section_list = new_section;
321 return true;
322 }
323
tiniparser_load_stream(FILE * fp)324 struct tiniparser_dictionary *tiniparser_load_stream(FILE *fp)
325 {
326 bool ret;
327 struct tiniparser_dictionary *d = NULL;
328
329 d = malloc(sizeof(struct tiniparser_dictionary));
330 if (d == NULL) {
331 return NULL;
332 }
333 d->section_list = NULL;
334
335 ret = tini_parse(fp,
336 false,
337 section_parser,
338 value_parser,
339 d);
340 if (ret == false) {
341 tiniparser_freedict(d);
342 d = NULL;
343 }
344 return d;
345 }
346
tiniparser_load(const char * filename)347 struct tiniparser_dictionary *tiniparser_load(const char *filename)
348 {
349 struct tiniparser_dictionary *d;
350 FILE *fp = fopen(filename, "r");
351
352 if (fp == NULL) {
353 return NULL;
354 }
355
356 d = tiniparser_load_stream(fp);
357
358 fclose(fp);
359
360 return d;
361 }
362
tiniparser_freedict(struct tiniparser_dictionary * d)363 void tiniparser_freedict(struct tiniparser_dictionary *d)
364 {
365 struct tiniparser_section *curr_section, *next_section;
366
367 if (d == NULL) {
368 return;
369 }
370
371 for (curr_section = d->section_list;
372 curr_section;
373 curr_section = next_section) {
374 struct tiniparser_entry *curr_entry, *next_entry;
375
376 next_section = curr_section->next_section;
377
378 for (curr_entry = curr_section->entry_list;
379 curr_entry;
380 curr_entry = next_entry) {
381 next_entry = curr_entry->next_entry;
382
383 free(curr_entry->key);
384 free(curr_entry->value);
385 free(curr_entry);
386 }
387 free(curr_section);
388 }
389 free(d);
390 }
391