1/*
2 * Copyright (c) 2000, Boris Popov
3 * All rights reserved.
4 *
5 * Portions Copyright (C) 2001 - 2010 Apple Inc. All rights reserved.
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, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 *    must display the following acknowledgement:
17 *    This product includes software developed by Boris Popov.
18 * 4. Neither the name of the author nor the names of any co-contributors
19 *    may be used to endorse or promote products derived from this software
20 *    without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 *
34 * $Id: rcfile.c,v 1.1.1.2 2001/07/06 22:38:43 conrad Exp $
35 */
36#include <err.h>
37#include <netsmb/smb_lib.h>
38
39#include "rcfile.h"
40#include "rcfile_priv.h"
41
42#define	SMB_CFG_FILE		"/etc/nsmb.conf"
43#define SMB_CFG_LOCAL_FILE	"/Library/Preferences/nsmb.conf"
44
45static void
46rc_key_free(struct rckey *p)
47{
48	if (p->rk_value)
49		free(p->rk_value);
50	if (p->rk_name)
51		free(p->rk_name);
52	if (p)
53		free(p);
54}
55
56static struct rckey *
57rc_sect_findkey(struct rcsection *rsp, const char *keyname)
58{
59	struct rckey *p;
60
61	SLIST_FOREACH(p, &rsp->rs_keys, rk_next)
62	if (strcmp(p->rk_name, keyname)==0)
63		return p;
64	return NULL;
65}
66
67static struct rckey *
68rc_sect_addkey(struct rcsection *rsp, const char *name, const char *value)
69{
70	struct rckey *p;
71
72	p = rc_sect_findkey(rsp, name);
73	if (p) {
74		free(p->rk_value);
75	} else {
76		p = malloc(sizeof(*p));
77		if (!p) return NULL;
78		SLIST_INSERT_HEAD(&rsp->rs_keys, p, rk_next);
79		p->rk_name = strdup(name);
80	}
81	p->rk_value = value ? strdup(value) : strdup("");
82	return p;
83}
84
85static struct rcsection *
86rc_findsect(struct rcfile *rcp, const char *sectname)
87{
88	struct rcsection *p;
89
90	SLIST_FOREACH(p, &rcp->rf_sect, rs_next)
91	if (strcmp(p->rs_name, sectname)==0)
92		return p;
93	return NULL;
94}
95
96static struct rcsection * rc_addsect(struct rcfile *rcp, const char *sectname)
97{
98	struct rcsection *p;
99
100	/* Not the best way to do this, but it improves the lookup speed */
101	if (strcmp(sectname, "global") == 0)
102		sectname = "default";	/* To us they are the same section */
103	p = rc_findsect(rcp, sectname);
104	if (p) return p;
105	p = malloc(sizeof(*p));
106	if (!p) return NULL;
107	p->rs_name = strdup(sectname);
108	SLIST_INIT(&p->rs_keys);
109	SLIST_INSERT_HEAD(&rcp->rf_sect, p, rs_next);
110	return p;
111}
112
113enum { stNewLine, stHeader, stSkipToEOL, stGetKey, stGoToGetValue, stGetValue};
114
115static void
116rc_parse(struct rcfile *rcp)
117{
118	FILE *f = rcp->rf_f;
119	int state = stNewLine, c;
120	struct rcsection *rsp = NULL;
121	struct rckey *rkp = NULL;
122	char buf[2048];
123	char *next = buf, *last = &buf[sizeof(buf)-1];
124
125	while ((c = getc (f)) != EOF) {
126		if (c == '\r')
127			continue;
128		if (state == stNewLine) {
129			next = buf;
130			if (isspace(c))
131				continue;	/* skip leading junk */
132			if (c == '[') {
133				state = stHeader;
134				rsp = NULL;
135				continue;
136			}
137			if (c == '#' || c == ';') {
138				state = stSkipToEOL;
139			} else {		/* something meaningfull */
140				state = stGetKey;
141			}
142		}
143		if (state == stSkipToEOL || next == last) {/* ignore long lines */
144			if (c == '\n'){
145				state = stNewLine;
146				next = buf;
147			}
148			continue;
149		}
150		if (state == stHeader) {
151			if (c == ']') {
152				*next = 0;
153				next = buf;
154				rsp = rc_addsect(rcp, buf);
155				state = stSkipToEOL;
156			} else
157				*next++ = c;
158			continue;
159		}
160		if (state == stGetKey) {
161			if (c == ' ' || c == '\t')/* side effect: 'key name='*/
162				continue;	  /* become 'keyname=' 	     */
163			if (c == '\n') {	/* silently ignore ... */
164				state = stNewLine;
165				continue;
166			}
167			if (c != '=') {
168				*next++ = c;
169				continue;
170			}
171			*next = 0;
172			if (rsp == NULL) {
173				fprintf(stderr, "Key '%s' defined before section\n", buf);
174				state = stSkipToEOL;
175				continue;
176			}
177			rkp = rc_sect_addkey(rsp, buf, NULL);
178			next = buf;
179			state = stGoToGetValue;
180			continue;
181		}
182		/* only stGetValue or stGoToGetValue left */
183		if ((state != stGetValue) && (state != stGoToGetValue)) {
184			fprintf(stderr, "Well, I can't parse file '%s'\n",rcp->rf_name);
185			state = stSkipToEOL;
186		}
187		/* Remove any spaces or tabs between equal sign and the value */
188		if ((state == stGoToGetValue) && ((c == ' ') || (c == '\t')))
189			continue;
190		else /* Now only the value left get it */
191			state = stGetValue;
192
193		if (c != '\n') {
194			*next++ = c;
195			continue;
196		}
197		*next = 0;
198		if (rkp->rk_value)
199			free(rkp->rk_value);
200		rkp->rk_value = strdup(buf);
201		state = stNewLine;
202		rkp = NULL;
203	} 	/* while */
204	if (c == EOF && state == stGetValue) {
205		*next = 0;
206		if (rkp->rk_value)
207			free(rkp->rk_value);
208		rkp->rk_value = strdup(buf);
209	}
210	return;
211}
212
213/*
214 * open rcfile and load its content, if already open - return previous handle
215 */
216static int
217rc_open(const char *filename, const char *mode, struct rcfile **rcfile)
218{
219	struct rcfile *rcp;
220	FILE *f;
221
222	f = fopen(filename, mode);
223	if (f == NULL)
224		return errno;
225	rcp = malloc(sizeof(struct rcfile));
226	if (rcp == NULL) {
227		fclose(f);
228		return ENOMEM;
229	}
230	bzero(rcp, sizeof(struct rcfile));
231	rcp->rf_name = strdup(filename);
232	rcp->rf_f = f;
233
234	rc_parse(rcp);
235	*rcfile = rcp;
236	return 0;
237}
238
239static int
240rc_merge(const char *filename, struct rcfile **rcfile)
241{
242	struct rcfile *rcp = *rcfile;
243	FILE *f, *t;
244
245	if (rcp == NULL) {
246		return rc_open(filename, "r", rcfile);
247	}
248	f = fopen (filename, "r");
249	if (f == NULL)
250		return errno;
251	t = rcp->rf_f;
252	rcp->rf_f = f;
253	rc_parse(rcp);
254	rcp->rf_f = t;
255	fclose(f);
256	return 0;
257}
258
259static int
260rc_freesect(struct rcfile *rcp, struct rcsection *rsp)
261{
262	struct rckey *p,*n;
263
264	SLIST_REMOVE(&rcp->rf_sect, rsp, rcsection, rs_next);
265	for(p = SLIST_FIRST(&rsp->rs_keys);p;) {
266		n = p;
267		p = SLIST_NEXT(p,rk_next);
268		rc_key_free(n);
269	}
270	free(rsp->rs_name);
271	free(rsp);
272	return 0;
273}
274
275int
276rc_close(struct rcfile *rcp)
277{
278	struct rcsection *p, *n;
279
280	fclose(rcp->rf_f);
281	for(p = SLIST_FIRST(&rcp->rf_sect); p;) {
282		n = p;
283		p = SLIST_NEXT(p,rs_next);
284		rc_freesect(rcp, n);
285	}
286	free(rcp->rf_name);
287	free(rcp);
288	return 0;
289}
290
291#if 0
292void
293rc_sect_delkey(struct rcsection *rsp, struct rckey *p)
294{
295
296	SLIST_REMOVE(&rsp->rs_keys, p, rckey, rk_next);
297	rc_key_free(p);
298	return;
299}
300#endif
301
302int
303rc_getstringptr(struct rcfile *rcp, const char *section, const char *key,
304	char **dest)
305{
306	struct rcsection *rsp;
307	struct rckey *rkp;
308
309	*dest = NULL;
310	rsp = rc_findsect(rcp, section);
311	if (!rsp) return ENOENT;
312	rkp = rc_sect_findkey(rsp,key);
313	if (!rkp) return ENOENT;
314	*dest = rkp->rk_value;
315	return 0;
316}
317
318int
319rc_getstring(struct rcfile *rcp, const char *section, const char *key,
320	size_t maxlen, char *dest)
321{
322	char *value;
323	int error;
324
325	error = rc_getstringptr(rcp, section, key, &value);
326	if (error)
327		return error;
328	if (strlen(value) >= maxlen) {
329		warnx("line too long for key '%s' in section '%s', max = %zu\n", key, section, maxlen);
330		return EINVAL;
331	}
332	strlcpy(dest, value, maxlen);
333	return 0;
334}
335
336int
337rc_getint(struct rcfile *rcp, const char *section, const char *key, int *value)
338{
339	struct rcsection *rsp;
340	struct rckey *rkp;
341
342	rsp = rc_findsect(rcp, section);
343	if (!rsp)
344		return ENOENT;
345	rkp = rc_sect_findkey(rsp, key);
346	if (!rkp)
347		return ENOENT;
348	errno = 0;
349	*value = (int)strtol(rkp->rk_value, NULL, 0);
350	if (errno) {
351		warnx("invalid int value '%s' for key '%s' in section '%s'\n", rkp->rk_value, key, section);
352		return errno;
353	}
354	return 0;
355}
356
357/*
358 * 1,yes,true
359 * 0,no,false
360 */
361int
362rc_getbool(struct rcfile *rcp, const char *section, const char *key, int *value)
363{
364	struct rcsection *rsp;
365	struct rckey *rkp;
366	char *p;
367
368	rsp = rc_findsect(rcp, section);
369	if (!rsp) return ENOENT;
370	rkp = rc_sect_findkey(rsp,key);
371	if (!rkp) return ENOENT;
372	p = rkp->rk_value;
373	while (*p && isspace(*p)) p++;
374	if (*p == '0' || strcasecmp(p,"no") == 0 || strcasecmp(p,"false") == 0) {
375		*value = 0;
376		return 0;
377	}
378	if (*p == '1' || strcasecmp(p,"yes") == 0 || strcasecmp(p,"true") == 0) {
379		*value = 1;
380		return 0;
381	}
382	fprintf(stderr, "invalid boolean value '%s' for key '%s' in section '%s' \n",p, key, section);
383	return EINVAL;
384}
385
386
387/*
388 * first read ~/.smbrc, next try to merge SMB_CFG_FILE -
389 */
390struct rcfile * smb_open_rcfile(int noUserPrefs)
391{
392	struct rcfile * smb_rc = NULL;
393	char *home = NULL;
394	char *fn;
395	int error;
396	int fnlen;
397
398	if (! noUserPrefs)
399		home = getenv("HOME");
400
401	if (home) {
402		fnlen = (int)(strlen(home) + strlen(SMB_CFG_LOCAL_FILE) + 1);
403		fn = malloc(fnlen);
404		snprintf(fn, fnlen, "%s%s", home, SMB_CFG_LOCAL_FILE);
405		error = rc_open(fn, "r", &smb_rc);
406		/* Used for debugging bad configuration files. */
407		if (error && (error != ENOENT) )
408			smb_log_info("%s: Can't open %s, syserr = %s",
409						 ASL_LEVEL_DEBUG, __FUNCTION__, fn, strerror(errno));
410		free(fn);
411	}
412	fn = (char *)SMB_CFG_FILE;
413	error = rc_merge(fn, &smb_rc);
414	/* Used for debugging bad configuration files. */
415	if (error && (error != ENOENT) )
416		smb_log_info("%s: Can't open %s, syserr = %s",
417					 ASL_LEVEL_DEBUG, __FUNCTION__, fn, strerror(errno));
418
419
420	return smb_rc;
421}
422