WvStreams
|
00001 /* 00002 * Worldvisions Weaver Software: 00003 * Copyright (C) 1997-2002 Net Integration Technologies, Inc. 00004 * 00005 * Implementation of the WvConfigFile class. 00006 * 00007 * Created: Sept 12 1997 D. Coombs 00008 * 00009 */ 00010 #include "wvconf.h" 00011 #include "wvfile.h" 00012 #include "wvstringtable.h" 00013 #include <string.h> 00014 #include <sys/stat.h> 00015 00016 00017 void WvConf::setbool(void *userdata, 00018 WvStringParm sect, WvStringParm ent, 00019 WvStringParm oldval, WvStringParm newval) 00020 { 00021 if (!*(bool *)userdata) 00022 { 00023 WvLog log("Config Event", WvLog::Debug); 00024 if(sect == "Tunnel Vision" && ent == "Magic Password") 00025 log("Changed:[%s]%s\n",sect, ent); 00026 else 00027 log("Changed: [%s]%s = '%s' -> '%s'\n", sect, ent, oldval, newval); 00028 } 00029 00030 *(bool *)userdata = true; 00031 } 00032 00033 void WvConf::addname(void *userdata, 00034 WvStringParm sect, WvStringParm ent, 00035 WvStringParm oldval, WvStringParm newval) 00036 { 00037 (*(WvStringList *)userdata).append(new WvString(ent), true); 00038 } 00039 00040 00041 void WvConf::addfile(void *userdata, 00042 WvStringParm sect, WvStringParm ent, 00043 WvStringParm oldval, WvStringParm newval) 00044 { 00045 WvFile tmp(WvString("/home/%s/%s", ent, *(WvString *)userdata), 00046 O_WRONLY | O_CREAT | O_TRUNC, 0600); 00047 if(tmp.isok()) 00048 { 00049 if(!!newval) 00050 tmp.print("%s\n", newval); 00051 else 00052 tmp.print("%s\n", ent); 00053 } 00054 } 00055 00056 WvConf::WvConf(WvStringParm _filename, int _create_mode) 00057 : filename(_filename), log(filename), globalsection("") 00058 { 00059 create_mode = _create_mode; 00060 dirty = error = loaded_once = false; 00061 wvauthd = NULL; 00062 load_file(); 00063 } 00064 00065 00066 int WvConf::check_for_bool_string(const char *s) 00067 { 00068 if (strcasecmp(s, "off") == 0 00069 || strcasecmp(s, "false") == 0 00070 || strncasecmp(s, "no", 2) == 0) // also handles "none" 00071 return (0); 00072 00073 if (strcasecmp(s, "on") == 0 00074 || strcasecmp(s, "true") == 0 00075 || strcasecmp(s, "yes") == 0) 00076 return (1); 00077 00078 // not a special bool case, so just return the number 00079 return (atoi(s)); 00080 } 00081 00082 // parse the WvConf string "request"; pointers to the found section, 00083 // entry, and value fields are stored in *section, *entry, and *value 00084 // respectively, and request[] is modified. 00085 // 00086 // For example, the string: 00087 // [silly]billy=willy 00088 // is parsed into: 00089 // section="silly"; entry="billy"; value="willy"; 00090 // 00091 // Returns 0 on success, -1 if the command is missing the '[', -2 if the 00092 // string is missing a ']', or -3 if the section or entry is blank. If a 00093 // "value" is not found (ie. there is no equal sign outside the [] brackets) 00094 // this does not qualify as an error, but *value is set to NULL. 00095 // 00096 int WvConf::parse_wvconf_request(char *request, char *§ion, 00097 char *&entry, char *&value) 00098 { 00099 //printf("parsing %s\n", request); 00100 entry = value = NULL; 00101 00102 section = strchr(request, '['); 00103 if (!section) 00104 return -1; 00105 00106 section++; 00107 00108 entry = strchr(section, ']'); 00109 if (!entry) 00110 return -2; 00111 00112 *entry++ = 0; 00113 00114 value = strchr(entry, '='); 00115 if (value) 00116 { 00117 *value++ = 0; 00118 value = trim_string(value); 00119 } 00120 00121 //printf("section: %s\nentry: %s\n", section, entry); 00122 section = trim_string(section); 00123 entry = trim_string(entry); 00124 00125 if (!*section) 00126 return -3; 00127 00128 return 0; 00129 } 00130 00131 00132 // This "int" version of get is smart enough to interpret words like on/off, 00133 // true/false, and yes/no. 00134 int WvConf::getint(WvStringParm section, WvStringParm entry, int def_val) 00135 { 00136 WvString def_str(def_val); 00137 return check_for_bool_string(get(section, entry, def_str)); 00138 } 00139 00140 00141 // This "int" version of fuzzy_get is smart enough to interpret words like 00142 // on/off, true/false, and yes/no. 00143 int WvConf::fuzzy_getint(WvStringList §ion, WvStringList &entry, 00144 int def_val) 00145 { 00146 WvString def_str(def_val); 00147 return check_for_bool_string(fuzzy_get(section, entry, def_str)); 00148 } 00149 00150 00151 // This "int" version of fuzzy_get is smart enough to interpret words like 00152 // on/off, true/false, and yes/no. 00153 int WvConf::fuzzy_getint(WvStringList §ion, WvStringParm entry, 00154 int def_val) 00155 { 00156 WvString def_str(def_val); 00157 return check_for_bool_string(fuzzy_get(section, entry, def_str)); 00158 } 00159 00160 00161 void WvConf::setint(WvStringParm section, WvStringParm entry, int value) 00162 { 00163 WvString def_str(value); 00164 set(section, entry, def_str); 00165 } 00166 00167 00168 // only set the value if it isn't already in the config file 00169 void WvConf::maybesetint(WvStringParm section, WvStringParm entry, 00170 int value) 00171 { 00172 if (!get(section, entry, NULL)) 00173 setint(section, entry, value); 00174 } 00175 00176 00177 void WvConf::load_file(WvStringParm filename) 00178 { 00179 const char *p; 00180 char *from_file; 00181 WvConfigSection *sect = &globalsection; 00182 bool quick_mode = false; 00183 00184 WvFile file(filename, O_RDONLY); 00185 00186 #ifdef _WIN32 00187 //FIXME: Windows doesn't have a sticky bit so we can't use that to signal other processes that 00188 // the file is being written to. Just be careful :). 00189 #else 00190 // check the sticky bit and fail if set 00191 struct stat statbuf; 00192 if (file.isok() && fstat(file.getrfd(), &statbuf) == -1) 00193 { 00194 log(WvLog::Warning, "Can't stat config file %s\n", filename); 00195 file.close(); 00196 } 00197 00198 if (file.isok() && (statbuf.st_mode & S_ISVTX)) 00199 { 00200 file.close(); 00201 file.seterr(EAGAIN); 00202 } 00203 #endif 00204 00205 if (!file.isok()) 00206 { 00207 // Could not open for read. 00208 // ...actually, this warning is mainly just annoying. 00209 //log(loaded_once ? WvLog::Debug1 : WvLog::Warning, 00210 // "Can't read config file %s: %s\n", filename, file.errstr()); 00211 if (file.geterr() != ENOENT && !loaded_once) 00212 error = true; 00213 return; 00214 } 00215 00216 while ((from_file = trim_string(file.getline())) != NULL) 00217 { 00218 00219 if ((p = parse_section(from_file)) != NULL) 00220 { 00221 quick_mode = false; 00222 00223 // a new section? 00224 if (!p[0]) // blank name: global section 00225 sect = &globalsection; 00226 else 00227 { 00228 sect = (*this)[p]; 00229 if (!sect) 00230 { 00231 sect = new WvConfigSection(p); 00232 append(sect, true); 00233 quick_mode = true; 00234 } 00235 } 00236 } 00237 else 00238 { 00239 // it must be an element for the current section *sect. 00240 p = parse_value(from_file); 00241 if (!p) 00242 p = ""; // allow empty entries 00243 00244 from_file = trim_string(from_file); 00245 if (from_file[0]) // nonblank option name 00246 { 00247 if (quick_mode) 00248 sect->quick_set(from_file, p); 00249 else 00250 sect->set(from_file, p); 00251 } 00252 } 00253 } 00254 00255 run_all_callbacks(); 00256 00257 loaded_once = true; 00258 } 00259 00260 00261 WvConf::~WvConf() 00262 { 00263 // We don't really have to do anything here. sections's destructor 00264 // will go through and delete all its entries, so we should be fine. 00265 00266 flush(); 00267 } 00268 00269 00270 const char *WvConf::get(WvStringParm section, WvStringParm entry, 00271 const char *def_val) 00272 { 00273 WvStringTable cache(5); 00274 WvConfigSection *s; 00275 00276 for(s = (*this)[section]; 00277 s && !cache[s->name]; 00278 s = (*s)["Inherits"] ? (*this)[(*s)["Inherits"]->value] : NULL) 00279 { 00280 const char *ret = s->get(entry); 00281 if (ret) return ret; 00282 cache.add(&s->name, false); 00283 } 00284 00285 return globalsection.get(entry, def_val); 00286 } 00287 00288 00289 // Gets an entry, given a string in the form [section]entry=value. Returns 00290 // the value or NULL if not found. The parameter parse_error is set to the 00291 // return value of parse_wvconf_request. 00292 WvString WvConf::getraw(WvString wvconfstr, int &parse_error) 00293 { 00294 char *section, *entry, *value; 00295 parse_error = parse_wvconf_request(wvconfstr.edit(), 00296 section, entry, value); 00297 00298 if (parse_error) 00299 return WvString(); 00300 00301 return get(section, entry, value); 00302 } 00303 00304 00305 const char *WvConf::fuzzy_get(WvStringList §ions, WvStringList &entries, 00306 const char *def_val) 00307 { 00308 WvStringList::Iter i(sections), i2(entries); 00309 WvStringTable cache(5); 00310 WvConfigSection *s; 00311 00312 for (i.rewind(); i.next(); ) 00313 { 00314 for (i2.rewind(); i2.next();) 00315 { 00316 for(s = (*this)[*i]; 00317 s && !cache[s->name]; 00318 s = (*s)["Inherits"] ? (*this)[(*s)["Inherits"]->value] : NULL) 00319 { 00320 const char *ret = s->get(*i2); 00321 if (ret) return ret; 00322 cache.add(&s->name, false); 00323 } 00324 } 00325 } 00326 00327 return def_val; 00328 } 00329 00330 00331 const char *WvConf::fuzzy_get(WvStringList §ions, WvStringParm entry, 00332 const char *def_val) 00333 { 00334 WvStringList::Iter i(sections); 00335 WvStringTable cache(5); 00336 WvConfigSection *s; 00337 00338 for (i.rewind(); i.next(); ) 00339 { 00340 for(s = (*this)[*i]; 00341 s && !cache[s->name]; 00342 s = (*s)["Inherits"] ? (*this)[(*s)["Inherits"]->value] : NULL) 00343 { 00344 const char *ret = s->get(entry); 00345 if (ret) return ret; 00346 cache.add(&s->name, false); 00347 } 00348 } 00349 00350 return def_val; 00351 } 00352 00353 00354 void WvConf::set(WvStringParm section, WvStringParm entry, 00355 const char *value) 00356 { 00357 WvConfigSection *s = (*this)[section]; 00358 00359 // section does not exist yet 00360 if (!s) 00361 { 00362 if (!value || !value[0]) 00363 return; // no section, no entry, no problem! 00364 00365 s = new WvConfigSection(section); 00366 append(s, true); 00367 } 00368 00369 const char *oldval = s->get(entry, ""); 00370 if (!value) value = ""; 00371 if (strcmp(oldval, value)) // case sensitive 00372 { 00373 run_callbacks(section, entry, oldval, value); 00374 00375 /* fprintf(stderr, "cfg.set: set [%s]%s = %s\n", 00376 (const char *)section, (const char *)entry, 00377 (const char *)value ?: "!!!"); */ 00378 00379 s->set(entry, value); 00380 dirty = true; 00381 } 00382 } 00383 00384 00385 // Takes a string in the form [section]entry=value and sets it. Returns an 00386 // error code as defined in parse_wvconf_request. The value parameter is 00387 // also set to the value (useful in rcommand, when we display the value after 00388 // it has been set). 00389 void WvConf::setraw(WvString wvconfstr, const char *&xvalue, int &parse_error) 00390 { 00391 char *section, *entry, *value; 00392 parse_error = parse_wvconf_request(wvconfstr.edit(), 00393 section, entry, value); 00394 if (!parse_error) 00395 { 00396 set(section, entry, value); 00397 xvalue = get(section, entry, value); 00398 } 00399 else 00400 xvalue = NULL; 00401 } 00402 00403 00404 // only set the value if it isn't already in the config file 00405 void WvConf::maybeset(WvStringParm section, WvStringParm entry, 00406 const char *value) 00407 { 00408 if (value && !get(section, entry, NULL)) 00409 set(section, entry, value); 00410 } 00411 00412 00413 WvConfigSection *WvConf::operator[] (WvStringParm section) 00414 { 00415 Iter i(*this); 00416 00417 if (section) 00418 for (i.rewind(); i.next(); ) 00419 { 00420 if (strcasecmp(i().name, section) == 0) 00421 return &i(); 00422 } 00423 00424 return NULL; 00425 } 00426 00427 00428 void WvConf::delete_section(WvStringParm section) 00429 { 00430 WvConfigSection *s = (*this)[section]; 00431 if (s) 00432 { 00433 unlink(s); 00434 dirty = true; 00435 } 00436 } 00437 00438 00439 char *WvConf::parse_section(char *s) 00440 { 00441 char *q; 00442 00443 if (s[0] != '[') 00444 return (NULL); 00445 00446 q = strchr(s, ']'); 00447 if (!q || q[1]) 00448 return (NULL); 00449 00450 *q = 0; 00451 return trim_string(s + 1); 00452 } 00453 00454 00455 char *WvConf::parse_value(char *s) 00456 { 00457 char *q; 00458 00459 q = strchr(s, '='); 00460 if (q == NULL) 00461 return (NULL); 00462 00463 *q++ = 0; // 's' points to option name, 'q' points to value 00464 00465 return (trim_string(q)); 00466 } 00467 00468 00469 void WvConf::save(WvStringParm _filename) 00470 { 00471 if (error || !_filename) 00472 return; 00473 00474 WvFile fp(_filename, O_WRONLY|O_CREAT|O_TRUNC, create_mode); 00475 00476 if (!fp.isok()) 00477 { 00478 log(WvLog::Error, "Can't write to config file %s: %s\n", 00479 _filename, strerror(errno)); 00480 if (fp.geterr() != ENOENT) 00481 error = true; 00482 return; 00483 } 00484 00485 #ifdef _WIN32 00486 //FIXME: Windows doesn't have a sticky bit so we can't use that to signal other processes that 00487 // the file is being written to. Just be careful :). 00488 #else 00489 struct stat statbuf; 00490 if (fstat(fp.getwfd(), &statbuf) == -1) 00491 { 00492 log(WvLog::Error, "Can't stat config file %s: %s\n", 00493 _filename, strerror(errno)); 00494 error = true; 00495 return; 00496 } 00497 00498 fchmod(fp.getwfd(), (statbuf.st_mode & 07777) | S_ISVTX); 00499 #endif 00500 00501 globalsection.dump(fp); 00502 00503 Iter i(*this); 00504 for (i.rewind(); i.next();) 00505 { 00506 WvConfigSection & sect = *i; 00507 fp.print("\n[%s]\n", sect.name); 00508 sect.dump(fp); 00509 } 00510 00511 #ifdef _WIN32 00512 //FIXME: Windows doesn't have a sticky bit so we can't use that to signal other processes that 00513 // the file is being written to. Just be careful :). 00514 #else 00515 fchmod(fp.getwfd(), statbuf.st_mode & 07777); 00516 #endif 00517 } 00518 00519 00520 void WvConf::save() 00521 { 00522 save(filename); 00523 } 00524 00525 00526 // only save the config file if it's dirty 00527 void WvConf::flush() 00528 { 00529 if (!dirty || error) 00530 return; 00531 00532 // save under default filename 00533 save(filename); 00534 00535 dirty = false; 00536 } 00537 00538 00539 void WvConf::add_callback(WvConfCallback callback, void *userdata, 00540 WvStringParm section, WvStringParm entry, 00541 void *cookie) 00542 { 00543 callbacks.append(new WvConfCallbackInfo(callback, userdata, 00544 section, entry, cookie), true); 00545 } 00546 00547 00548 void WvConf::del_callback(WvStringParm section, WvStringParm entry, 00549 void *cookie) 00550 { 00551 WvConfCallbackInfoList::Iter i(callbacks); 00552 00553 for (i.rewind(); i.next(); ) 00554 { 00555 if (i->cookie == cookie && i->section == section && i->entry == entry) 00556 { 00557 i.unlink(); 00558 return; 00559 } 00560 } 00561 } 00562 00563 00564 void WvConf::run_callbacks(WvStringParm section, WvStringParm entry, 00565 WvStringParm oldvalue, WvStringParm newvalue) 00566 { 00567 WvConfCallbackInfoList::Iter i(callbacks); 00568 00569 for (i.rewind(); i.next(); ) 00570 { 00571 if (!i->section || !strcasecmp(i->section, section)) 00572 { 00573 if (!i->entry || !strcasecmp(i->entry, entry)) 00574 i->callback(i->userdata, section, entry, 00575 oldvalue, newvalue); 00576 } 00577 } 00578 } 00579 00580 00581 void WvConf::run_all_callbacks() 00582 { 00583 WvConfCallbackInfoList::Iter i(callbacks); 00584 00585 for (i.rewind(); i.next(); ) 00586 i->callback(i->userdata, "", "", "", ""); 00587 }