WvStreams
wvconf.cc
1/*
2 * Worldvisions Weaver Software:
3 * Copyright (C) 1997-2002 Net Integration Technologies, Inc.
4 *
5 * Implementation of the WvConfigFile class.
6 *
7 * Created: Sept 12 1997 D. Coombs
8 *
9 */
10#include "wvconf.h"
11#include "wvfile.h"
12#include "wvstringtable.h"
13#include <string.h>
14#include <sys/stat.h>
15
16
17void WvConf::setbool(void *userdata,
18 WvStringParm sect, WvStringParm ent,
19 WvStringParm oldval, WvStringParm newval)
20{
21 if (!*(bool *)userdata)
22 {
23 WvLog log("Config Event", WvLog::Debug);
24 if(sect == "Tunnel Vision" && ent == "Magic Password")
25 log("Changed:[%s]%s\n",sect, ent);
26 else
27 log("Changed: [%s]%s = '%s' -> '%s'\n", sect, ent, oldval, newval);
28 }
29
30 *(bool *)userdata = true;
31}
32
33void WvConf::addname(void *userdata,
34 WvStringParm sect, WvStringParm ent,
35 WvStringParm oldval, WvStringParm newval)
36{
37 (*(WvStringList *)userdata).append(new WvString(ent), true);
38}
39
40
41void WvConf::addfile(void *userdata,
42 WvStringParm sect, WvStringParm ent,
43 WvStringParm oldval, WvStringParm newval)
44{
45 WvFile tmp(WvString("/home/%s/%s", ent, *(WvString *)userdata),
46 O_WRONLY | O_CREAT | O_TRUNC, 0600);
47 if(tmp.isok())
48 {
49 if(!!newval)
50 tmp.print("%s\n", newval);
51 else
52 tmp.print("%s\n", ent);
53 }
54}
55
56WvConf::WvConf(WvStringParm _filename, int _create_mode)
57 : filename(_filename), log(filename), globalsection("")
58{
59 create_mode = _create_mode;
60 dirty = error = loaded_once = false;
61 wvauthd = NULL;
62 load_file();
63}
64
65
66int WvConf::check_for_bool_string(const char *s)
67{
68 if (strcasecmp(s, "off") == 0
69 || strcasecmp(s, "false") == 0
70 || strncasecmp(s, "no", 2) == 0) // also handles "none"
71 return (0);
72
73 if (strcasecmp(s, "on") == 0
74 || strcasecmp(s, "true") == 0
75 || strcasecmp(s, "yes") == 0)
76 return (1);
77
78 // not a special bool case, so just return the number
79 return (atoi(s));
80}
81
82// parse the WvConf string "request"; pointers to the found section,
83// entry, and value fields are stored in *section, *entry, and *value
84// respectively, and request[] is modified.
85//
86// For example, the string:
87// [silly]billy=willy
88// is parsed into:
89// section="silly"; entry="billy"; value="willy";
90//
91// Returns 0 on success, -1 if the command is missing the '[', -2 if the
92// string is missing a ']', or -3 if the section or entry is blank. If a
93// "value" is not found (ie. there is no equal sign outside the [] brackets)
94// this does not qualify as an error, but *value is set to NULL.
95//
96int WvConf::parse_wvconf_request(char *request, char *&section,
97 char *&entry, char *&value)
98{
99 //printf("parsing %s\n", request);
100 entry = value = NULL;
101
102 section = strchr(request, '[');
103 if (!section)
104 return -1;
105
106 section++;
107
108 entry = strchr(section, ']');
109 if (!entry)
110 return -2;
111
112 *entry++ = 0;
113
114 value = strchr(entry, '=');
115 if (value)
116 {
117 *value++ = 0;
118 value = trim_string(value);
119 }
120
121 //printf("section: %s\nentry: %s\n", section, entry);
122 section = trim_string(section);
123 entry = trim_string(entry);
124
125 if (!*section)
126 return -3;
127
128 return 0;
129}
130
131
132// This "int" version of get is smart enough to interpret words like on/off,
133// true/false, and yes/no.
134int WvConf::getint(WvStringParm section, WvStringParm entry, int def_val)
135{
136 WvString def_str(def_val);
137 return check_for_bool_string(get(section, entry, def_str));
138}
139
140
141// This "int" version of fuzzy_get is smart enough to interpret words like
142// on/off, true/false, and yes/no.
143int WvConf::fuzzy_getint(WvStringList &section, WvStringList &entry,
144 int def_val)
145{
146 WvString def_str(def_val);
147 return check_for_bool_string(fuzzy_get(section, entry, def_str));
148}
149
150
151// This "int" version of fuzzy_get is smart enough to interpret words like
152// on/off, true/false, and yes/no.
153int WvConf::fuzzy_getint(WvStringList &section, WvStringParm entry,
154 int def_val)
155{
156 WvString def_str(def_val);
157 return check_for_bool_string(fuzzy_get(section, entry, def_str));
158}
159
160
161void WvConf::setint(WvStringParm section, WvStringParm entry, int value)
162{
163 WvString def_str(value);
164 set(section, entry, def_str);
165}
166
167
168// only set the value if it isn't already in the config file
169void WvConf::maybesetint(WvStringParm section, WvStringParm entry,
170 int value)
171{
172 if (!get(section, entry, NULL))
173 setint(section, entry, value);
174}
175
176
177void WvConf::load_file(WvStringParm filename)
178{
179 const char *p;
180 char *from_file;
181 WvConfigSection *sect = &globalsection;
182 bool quick_mode = false;
183
184 WvFile file(filename, O_RDONLY);
185
186 #ifdef _WIN32
187 //FIXME: Windows doesn't have a sticky bit so we can't use that to signal other processes that
188 // the file is being written to. Just be careful :).
189 #else
190 // check the sticky bit and fail if set
191 struct stat statbuf;
192 if (file.isok() && fstat(file.getrfd(), &statbuf) == -1)
193 {
194 log(WvLog::Warning, "Can't stat config file %s\n", filename);
195 file.close();
196 }
197
198 if (file.isok() && (statbuf.st_mode & S_ISVTX))
199 {
200 file.close();
201 file.seterr(EAGAIN);
202 }
203 #endif
204
205 if (!file.isok())
206 {
207 // Could not open for read.
208 // ...actually, this warning is mainly just annoying.
209 //log(loaded_once ? WvLog::Debug1 : WvLog::Warning,
210 // "Can't read config file %s: %s\n", filename, file.errstr());
211 if (file.geterr() != ENOENT && !loaded_once)
212 error = true;
213 return;
214 }
215
216 while ((from_file = trim_string(file.getline())) != NULL)
217 {
218
219 if ((p = parse_section(from_file)) != NULL)
220 {
221 quick_mode = false;
222
223 // a new section?
224 if (!p[0]) // blank name: global section
225 sect = &globalsection;
226 else
227 {
228 sect = (*this)[p];
229 if (!sect)
230 {
231 sect = new WvConfigSection(p);
232 append(sect, true);
233 quick_mode = true;
234 }
235 }
236 }
237 else
238 {
239 // it must be an element for the current section *sect.
240 p = parse_value(from_file);
241 if (!p)
242 p = ""; // allow empty entries
243
244 from_file = trim_string(from_file);
245 if (from_file[0]) // nonblank option name
246 {
247 if (quick_mode)
248 sect->quick_set(from_file, p);
249 else
250 sect->set(from_file, p);
251 }
252 }
253 }
254
255 run_all_callbacks();
256
257 loaded_once = true;
258}
259
260
261WvConf::~WvConf()
262{
263 // We don't really have to do anything here. sections's destructor
264 // will go through and delete all its entries, so we should be fine.
265
266 flush();
267}
268
269
270const char *WvConf::get(WvStringParm section, WvStringParm entry,
271 const char *def_val)
272{
273 WvStringTable cache(5);
275
276 for(s = (*this)[section];
277 s && !cache[s->name];
278 s = (*s)["Inherits"] ? (*this)[(*s)["Inherits"]->value] : NULL)
279 {
280 const char *ret = s->get(entry);
281 if (ret) return ret;
282 cache.add(&s->name, false);
283 }
284
285 return globalsection.get(entry, def_val);
286}
287
288
289// Gets an entry, given a string in the form [section]entry=value. Returns
290// the value or NULL if not found. The parameter parse_error is set to the
291// return value of parse_wvconf_request.
292WvString WvConf::getraw(WvString wvconfstr, int &parse_error)
293{
294 char *section, *entry, *value;
295 parse_error = parse_wvconf_request(wvconfstr.edit(),
296 section, entry, value);
297
298 if (parse_error)
299 return WvString();
300
301 return get(section, entry, value);
302}
303
304
305const char *WvConf::fuzzy_get(WvStringList &sections, WvStringList &entries,
306 const char *def_val)
307{
308 WvStringList::Iter i(sections), i2(entries);
309 WvStringTable cache(5);
311
312 for (i.rewind(); i.next(); )
313 {
314 for (i2.rewind(); i2.next();)
315 {
316 for(s = (*this)[*i];
317 s && !cache[s->name];
318 s = (*s)["Inherits"] ? (*this)[(*s)["Inherits"]->value] : NULL)
319 {
320 const char *ret = s->get(*i2);
321 if (ret) return ret;
322 cache.add(&s->name, false);
323 }
324 }
325 }
326
327 return def_val;
328}
329
330
331const char *WvConf::fuzzy_get(WvStringList &sections, WvStringParm entry,
332 const char *def_val)
333{
334 WvStringList::Iter i(sections);
335 WvStringTable cache(5);
337
338 for (i.rewind(); i.next(); )
339 {
340 for(s = (*this)[*i];
341 s && !cache[s->name];
342 s = (*s)["Inherits"] ? (*this)[(*s)["Inherits"]->value] : NULL)
343 {
344 const char *ret = s->get(entry);
345 if (ret) return ret;
346 cache.add(&s->name, false);
347 }
348 }
349
350 return def_val;
351}
352
353
354void WvConf::set(WvStringParm section, WvStringParm entry,
355 const char *value)
356{
357 WvConfigSection *s = (*this)[section];
358
359 // section does not exist yet
360 if (!s)
361 {
362 if (!value || !value[0])
363 return; // no section, no entry, no problem!
364
365 s = new WvConfigSection(section);
366 append(s, true);
367 }
368
369 const char *oldval = s->get(entry, "");
370 if (!value) value = "";
371 if (strcmp(oldval, value)) // case sensitive
372 {
373 run_callbacks(section, entry, oldval, value);
374
375 /* fprintf(stderr, "cfg.set: set [%s]%s = %s\n",
376 (const char *)section, (const char *)entry,
377 (const char *)value ?: "!!!"); */
378
379 s->set(entry, value);
380 dirty = true;
381 }
382}
383
384
385// Takes a string in the form [section]entry=value and sets it. Returns an
386// error code as defined in parse_wvconf_request. The value parameter is
387// also set to the value (useful in rcommand, when we display the value after
388// it has been set).
389void WvConf::setraw(WvString wvconfstr, const char *&xvalue, int &parse_error)
390{
391 char *section, *entry, *value;
392 parse_error = parse_wvconf_request(wvconfstr.edit(),
393 section, entry, value);
394 if (!parse_error)
395 {
396 set(section, entry, value);
397 xvalue = get(section, entry, value);
398 }
399 else
400 xvalue = NULL;
401}
402
403
404// only set the value if it isn't already in the config file
405void WvConf::maybeset(WvStringParm section, WvStringParm entry,
406 const char *value)
407{
408 if (value && !get(section, entry, NULL))
409 set(section, entry, value);
410}
411
412
413WvConfigSection *WvConf::operator[] (WvStringParm section)
414{
415 Iter i(*this);
416
417 if (section)
418 for (i.rewind(); i.next(); )
419 {
420 if (strcasecmp(i().name, section) == 0)
421 return &i();
422 }
423
424 return NULL;
425}
426
427
428void WvConf::delete_section(WvStringParm section)
429{
430 WvConfigSection *s = (*this)[section];
431 if (s)
432 {
433 unlink(s);
434 dirty = true;
435 }
436}
437
438
439char *WvConf::parse_section(char *s)
440{
441 char *q;
442
443 if (s[0] != '[')
444 return (NULL);
445
446 q = strchr(s, ']');
447 if (!q || q[1])
448 return (NULL);
449
450 *q = 0;
451 return trim_string(s + 1);
452}
453
454
455char *WvConf::parse_value(char *s)
456{
457 char *q;
458
459 q = strchr(s, '=');
460 if (q == NULL)
461 return (NULL);
462
463 *q++ = 0; // 's' points to option name, 'q' points to value
464
465 return (trim_string(q));
466}
467
468
469void WvConf::save(WvStringParm _filename)
470{
471 if (error || !_filename)
472 return;
473
474 WvFile fp(_filename, O_WRONLY|O_CREAT|O_TRUNC, create_mode);
475
476 if (!fp.isok())
477 {
478 log(WvLog::Error, "Can't write to config file %s: %s\n",
479 _filename, strerror(errno));
480 if (fp.geterr() != ENOENT)
481 error = true;
482 return;
483 }
484
485 #ifdef _WIN32
486 //FIXME: Windows doesn't have a sticky bit so we can't use that to signal other processes that
487 // the file is being written to. Just be careful :).
488 #else
489 struct stat statbuf;
490 if (fstat(fp.getwfd(), &statbuf) == -1)
491 {
492 log(WvLog::Error, "Can't stat config file %s: %s\n",
493 _filename, strerror(errno));
494 error = true;
495 return;
496 }
497
498 fchmod(fp.getwfd(), (statbuf.st_mode & 07777) | S_ISVTX);
499 #endif
500
501 globalsection.dump(fp);
502
503 Iter i(*this);
504 for (i.rewind(); i.next();)
505 {
506 WvConfigSection & sect = *i;
507 fp.print("\n[%s]\n", sect.name);
508 sect.dump(fp);
509 }
510
511 #ifdef _WIN32
512 //FIXME: Windows doesn't have a sticky bit so we can't use that to signal other processes that
513 // the file is being written to. Just be careful :).
514 #else
515 fchmod(fp.getwfd(), statbuf.st_mode & 07777);
516 #endif
517}
518
519
520void WvConf::save()
521{
522 save(filename);
523}
524
525
526// only save the config file if it's dirty
527void WvConf::flush()
528{
529 if (!dirty || error)
530 return;
531
532 // save under default filename
533 save(filename);
534
535 dirty = false;
536}
537
538
539void WvConf::add_callback(WvConfCallback callback, void *userdata,
540 WvStringParm section, WvStringParm entry,
541 void *cookie)
542{
543 callbacks.append(new WvConfCallbackInfo(callback, userdata,
544 section, entry, cookie), true);
545}
546
547
548void WvConf::del_callback(WvStringParm section, WvStringParm entry,
549 void *cookie)
550{
551 WvConfCallbackInfoList::Iter i(callbacks);
552
553 for (i.rewind(); i.next(); )
554 {
555 if (i->cookie == cookie && i->section == section && i->entry == entry)
556 {
557 i.unlink();
558 return;
559 }
560 }
561}
562
563
564void WvConf::run_callbacks(WvStringParm section, WvStringParm entry,
565 WvStringParm oldvalue, WvStringParm newvalue)
566{
567 WvConfCallbackInfoList::Iter i(callbacks);
568
569 for (i.rewind(); i.next(); )
570 {
571 if (!i->section || !strcasecmp(i->section, section))
572 {
573 if (!i->entry || !strcasecmp(i->entry, entry))
574 i->callback(i->userdata, section, entry,
575 oldvalue, newvalue);
576 }
577 }
578}
579
580
581void WvConf::run_all_callbacks()
582{
583 WvConfCallbackInfoList::Iter i(callbacks);
584
585 for (i.rewind(); i.next(); )
586 i->callback(i->userdata, "", "", "", "");
587}
A WvFastString acts exactly like a WvString, but can take (const char *) strings without needing to a...
Definition wvstring.h:94
WvFile implements a stream connected to a file or Unix device.
Definition wvfile.h:29
A WvLog stream accepts log messages from applications and forwards them to all registered WvLogRcv's.
Definition wvlog.h:57
This is a WvList of WvStrings, and is a really handy way to parse strings.
WvString is an implementation of a simple and efficient printable-string class.
Definition wvstring.h:330
char * edit()
make the string editable, and return a non-const (char*)
Definition wvstring.h:397
Interface * get(IObject *aObj)
XPLC equivalent to dynamic_cast.
Definition utils.h:184
char * trim_string(char *string)
Trims whitespace from the beginning and end of the character string, including carriage return / line...
Definition strutils.cc:59