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