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