xref: /netbsd-src/crypto/external/bsd/libsaslc/dist/src/parser.c (revision 4817a0b0b8fe9612e8ebe21a9bf2d97b95038a97)
1 /* $Id: parser.c,v 1.1.1.1 2010/11/27 21:23:59 agc Exp $ */
2 
3 /* Copyright (c) 2010 The NetBSD Foundation, Inc.
4  * All rights reserved.
5  *
6  * This code is derived from software contributed to The NetBSD Foundation
7  * by Mateusz Kocielski.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. All advertising materials mentioning features or use of this software
18  *    must display the following acknowledgement:
19  *  	  This product includes software developed by the NetBSD
20  *  	  Foundation, Inc. and its contributors.
21  * 4. Neither the name of The NetBSD Foundation nor the names of its
22  *    contributors may be used to endorse or promote products derived
23  *    from this software without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
26  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
27  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28  * PURPOSE ARE DISCLAIMED.	IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
29  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35  * POSSIBILITY OF SUCH DAMAGE.
36  */
37 
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <ctype.h>
42 #include <limits.h>
43 #include <err.h>
44 #include <saslc.h>
45 #include "saslc_private.h"
46 #include "dict.h"
47 #include "parser.h"
48 
49 /* local headers */
50 
51 /* token types */
52 enum {
53 	TOKEN_OPTION,	/* option */
54 	TOKEN_STRING,	/* quoted string */
55 	TOKEN_NUM,	/* number */
56 	TOKEN_UNKNOWN	/* unknown */
57 };
58 
59 /** token structure */
60 typedef struct saslc__token_t {
61 	int type; /**< token type */
62 	char *val; /**< token value */
63 	struct saslc__token_t *next; /**< next token */
64 } saslc__token_t;
65 
66 
67 static void saslc__destroy_tokens(saslc__token_t *);
68 static saslc__token_t *saslc__get_token(char **);
69 static saslc__token_t *saslc__get_tokens(char *);
70 static int saslc__parse_line(char *, saslc__dict_t *);
71 static int saslc__parse_file(saslc_t *, char *, saslc__dict_t *);
72 
73 /**
74  * @brief destroys tokens list and freeing resouces.
75  * @param token token
76  */
77 
78 static void
79 saslc__destroy_tokens(saslc__token_t *token)
80 {
81 	saslc__token_t *next;
82 
83 	while(token != NULL) {
84 		next = token->next;
85 		free(token);
86 		token = next;
87 	}
88 
89 	return;
90 }
91 
92 /**
93  * @brief gets token from string c and updates pointer position.
94  * @param c pointer to string
95  * @return token on success, NULL on failure (e.g. at end of string). Note that
96  * c is updated to point on next token.
97  */
98 
99 static saslc__token_t *
100 saslc__get_token(char **c)
101 {
102 	saslc__token_t *token;
103 
104 	/* omit spaces and tabs */
105 	while (**c == ' ' || **c == '\t')
106 		(*c)++;
107 
108 	if (**c == '\0')
109 		return NULL;
110 
111 	if ((token = calloc(1, sizeof(saslc__token_t))) == NULL)
112 		return NULL;
113 
114 	token->val = *c;
115 	token->type = TOKEN_UNKNOWN;
116 
117 	/* try to recognize type of the token by prefix */
118 	if (**c == '\"')
119 		token->type = TOKEN_STRING;
120 
121 	if (isdigit((unsigned char)**c))
122 		token->type = TOKEN_NUM;
123 
124 	if (isalpha((unsigned char)**c))
125 		token->type = TOKEN_OPTION;
126 
127 	/* move to the next unchecked character */
128 	(*c)++;
129 
130 	switch(token->type)
131 	{
132 	case TOKEN_NUM:
133 		while (isdigit((unsigned char)**c))
134 			(*c)++;
135 		break;
136 	case TOKEN_OPTION:
137 		while (isalnum((unsigned char)**c))
138 			(*c)++;
139 		break;
140 	case TOKEN_STRING:
141 		while (**c != '\0' && **c != '\"')
142 			(*c)++;
143 		if (**c == '\"') {
144 			(*c)++;
145 			break;
146 		}
147 	/*FALLTHROUGH*/
148 	default:
149 		/* UNKNOWN TOKEN */
150 		free(token);
151 		return NULL;
152 	}
153 
154 	if (isspace((unsigned char)**c)) {
155 		**c = '\0';
156 		(*c)++;
157 	}
158 
159 	return token;
160 }
161 
162 /**
163  * @brief tokenizes line.
164  * @param line input line
165  * @return list of tokens, NULL on error
166  */
167 
168 static saslc__token_t *
169 saslc__get_tokens(char *line)
170 {
171 	saslc__token_t *act, *prev;
172 	saslc__token_t *head;
173 	char *c;
174 
175 	c = line;
176 
177 	for (head = NULL, prev = NULL, act = saslc__get_token(&c);
178 	    act != NULL;) {
179 		/* set head */
180 		if (head == NULL)
181 			head = act;
182 
183 		/* remember previous node */
184 		if (prev != NULL)
185 			prev->next = act;
186 
187 		/* get next token */
188 		prev = act;
189 		act = saslc__get_token(&c);
190 
191 		/* an error occured */
192 		if (act == NULL && *c != '\0') {
193 			/* free list */
194 		    	saslc__destroy_tokens(head);
195 			return NULL;
196 		}
197 	}
198 
199 	return head;
200 }
201 
202 /**
203  * @brief parses line and store result in dict.
204  * @param line input line
205  * @param dict dictionary in which parsed options will be stored
206  * @return 0 on success, -1 on failure.
207  */
208 
209 static int
210 saslc__parse_line(char *line, saslc__dict_t *dict)
211 {
212 	char *opt, *val, len;
213 	int rv = -1;
214 	saslc__token_t *token, *head;
215 
216 	token = saslc__get_tokens(line);
217 
218 	/* line can't be parsed */
219 	if (token == NULL)
220 		return -1;
221 
222 	head = token; /* keep pointer to head (to free memory at the end) */
223 
224 	while (token != NULL) {
225 		if (token->type != TOKEN_OPTION)
226 		goto out;
227 		/* get option */
228 		opt = token->val;
229 		token = token->next;
230 		/* check if value is specified */
231 		if (token == NULL)
232 			goto out;
233 		val = token->val;
234 		if (token->type == TOKEN_STRING) {
235 			/* striping " */
236 			val++;
237 			len = strlen(val);
238 			if (len == 0)
239 				goto out;
240 			val[len-1] = '\0';
241 		}
242 		/* check if value has got proper type */
243 		if (token->type != TOKEN_STRING && token->type !=
244 		    TOKEN_NUM)
245 			goto out;
246 		/* insert (option, value) into dictionary */
247 		if (saslc__dict_insert(dict, opt, val) < 0)
248 			goto out;
249 		token = token->next; /* parse next token */
250 	}
251 	rv = 0;
252 
253 out:
254 	saslc__destroy_tokens(head);
255 
256 	return rv;
257 }
258 
259 /**
260  * @brief parses file and store result in dict
261  * @param ctx saslc context
262  * @param path path to the file
263  * @param dict dictionary in which parsed options will be stored
264  * @return 0 on success, -1 on failure.
265  */
266 
267 static int
268 saslc__parse_file(saslc_t *ctx, char *path, saslc__dict_t *dict)
269 {
270 	char input[LINE_MAX], *c;
271 	FILE *fd;
272 	int rv = 0;
273 
274 	fd = fopen(path, "r");
275 
276 	if (fd == NULL) {
277 		/* XXX */
278 		saslc__error_set(ERR(ctx), ERROR_PARSE, "can't open file");
279 		return 0;
280 	}
281 
282 	while (fgets(input, sizeof(input), fd) != NULL) {
283 		/* strip newline char */
284 		c = strchr(input, '\n');
285 		if (c != NULL)
286 			*c = '\0';
287 
288 		if (feof(fd) > 0)
289 			break;
290 
291 		/* parse line */
292 		if (saslc__parse_line(input, dict) < 0) {
293 			/* XXX */
294 			saslc__error_set(ERR(ctx), ERROR_PARSE,
295 			    "can't parse file");
296 			rv = -1;
297 			break;
298 		}
299 	}
300 
301 	fclose(fd);
302 	return rv;
303 }
304 
305 /**
306  * @brief parse configuration files. By default function reads
307  * files from /etc/saslc.d/saslc/ directory if appname is not setup. Otherwise
308  * function uses /etc/saslc.d/[appname]/ directory. /etc/saslc.d/ is default
309  * directory which stores configuration for all applications, but can be
310  * overwritten by SASLC_CONFIG variable in environment.
311  * @param ctx saslc context
312  * @return 0 on success, -1 on failure.
313  */
314 
315 int
316 saslc__parser_config(saslc_t *ctx)
317 {
318 	char path[FILENAME_MAX];
319 	const char *config_path;
320 	const char *appname;
321 	saslc__mech_list_node_t *mech_node;
322 
323 
324 	if ((config_path = getenv(SASLC__ENV_PATH)) == NULL)
325 		config_path = SASLC__CONFIG_PATH;
326 
327 	if ((appname = ctx->appname) == NULL)
328 		appname = SASLC__DEFAULT_APPNAME;
329 
330 	/* parse general */
331 	snprintf(path, sizeof(path), "%s/%s/%s%s", config_path,
332 	    appname,SASLC__CONFIG_MAIN_FILE, SASLC__CONFIG_SUFFIX);
333 	if (saslc__parse_file(ctx, path, ctx->prop) < 0)
334 		return -1;
335 
336 	/* parse mechs */
337 	for (mech_node = ctx->mechanisms->lh_first; mech_node != NULL;
338 	    mech_node = mech_node->nodes.le_next) {
339 		snprintf(path, sizeof(path), "%s/%s/%s/%s%s",
340 		    config_path, appname, SASLC__CONFIG_MECH_DIRECTORY,
341 		    mech_node->mech->name, SASLC__CONFIG_SUFFIX);
342 		if (saslc__parse_file(ctx, path, mech_node->prop) < 0)
343 			return -1;
344 	}
345 
346 	return 0;
347 }
348