1 /*
2 BAREOS® - Backup Archiving REcovery Open Sourced
3
4 Copyright (C) 2000-2012 Free Software Foundation Europe e.V.
5 Copyright (C) 2011-2012 Planets Communications B.V.
6 Copyright (C) 2013-2020 Bareos GmbH & Co. KG
7
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version three of the GNU Affero General Public
10 License as published by the Free Software Foundation and included
11 in the file LICENSE.
12
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Affero General Public License for more details.
17
18 You should have received a copy of the GNU Affero General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 02110-1301, USA.
22 */
23 /*
24 * Master Configuration routines.
25 *
26 * This file contains the common parts of the BAREOS configuration routines.
27 *
28 * Note, the configuration file parser consists of four parts
29 *
30 * 1. The generic lexical scanner in lib/lex.c and lib/lex.h
31 *
32 * 2. The generic config scanner in lib/parse_conf.c and lib/parse_conf.h.
33 * These files contain the parser code, some utility routines,
34 *
35 * 3. The generic resource functions in lib/res.c
36 * Which form the common store routines (name, int, string, time,
37 * int64, size, ...).
38 *
39 * 4. The daemon specific file, which contains the Resource definitions
40 * as well as any specific store routines for the resource records.
41 *
42 * N.B. This is a two pass parser, so if you malloc() a string in a "store"
43 * routine, you must ensure to do it during only one of the two passes, or to
44 * free it between.
45 *
46 * Also, note that the resource record is malloced and saved in SaveResource()
47 * during pass 1. Anything that you want saved after pass two (e.g. resource
48 * pointers) must explicitly be done in SaveResource. Take a look at the Job
49 * resource in src/dird/dird_conf.c to see how it is done.
50 *
51 * Kern Sibbald, January MM
52 */
53
54 #include "include/bareos.h"
55 #include "include/jcr.h"
56 #include "lib/address_conf.h"
57 #include "lib/edit.h"
58 #include "lib/parse_conf.h"
59 #include "lib/parse_conf_state_machine.h"
60 #include "lib/qualified_resource_name_type_converter.h"
61 #include "lib/bstringlist.h"
62 #include "lib/ascii_control_characters.h"
63 #include "lib/messages_resource.h"
64 #include "lib/resource_item.h"
65 #include "lib/berrno.h"
66 #include "lib/util.h"
67
68 #include <algorithm>
69
70 #if defined(HAVE_WIN32)
71 # include "shlobj.h"
72 #else
73 # define MAX_PATH 1024
74 #endif
75
PrintMessage(void * sock,const char * fmt,...)76 bool PrintMessage(void* sock, const char* fmt, ...)
77 {
78 va_list arg_ptr;
79
80 va_start(arg_ptr, fmt);
81 vfprintf(stdout, fmt, arg_ptr);
82 va_end(arg_ptr);
83
84 return true;
85 }
86
ConfigurationParser()87 ConfigurationParser::ConfigurationParser()
88 : scan_error_(nullptr)
89 , scan_warning_(nullptr)
90 , init_res_(nullptr)
91 , store_res_(nullptr)
92 , print_res_(nullptr)
93 , err_type_(0)
94 , omit_defaults_(false)
95 , r_first_(0)
96 , r_last_(0)
97 , r_own_(0)
98 , own_resource_(nullptr)
99 , resources_(0)
100 , res_head_(nullptr)
101 , SaveResourceCb_(nullptr)
102 , DumpResourceCb_(nullptr)
103 , FreeResourceCb_(nullptr)
104 , use_config_include_dir_(false)
105 , ParseConfigReadyCb_(nullptr)
106 , parser_first_run_(true)
107 {
108 return;
109 }
110
ConfigurationParser(const char * cf,LEX_ERROR_HANDLER * ScanError,LEX_WARNING_HANDLER * scan_warning,INIT_RES_HANDLER * init_res,STORE_RES_HANDLER * StoreRes,PRINT_RES_HANDLER * print_res,int32_t err_type,int32_t r_first,int32_t r_last,ResourceTable * resources,BareosResource ** res_head,const char * config_default_filename,const char * config_include_dir,void (* ParseConfigBeforeCb)(ConfigurationParser &),void (* ParseConfigReadyCb)(ConfigurationParser &),SaveResourceCb_t SaveResourceCb,DumpResourceCb_t DumpResourceCb,FreeResourceCb_t FreeResourceCb)111 ConfigurationParser::ConfigurationParser(
112 const char* cf,
113 LEX_ERROR_HANDLER* ScanError,
114 LEX_WARNING_HANDLER* scan_warning,
115 INIT_RES_HANDLER* init_res,
116 STORE_RES_HANDLER* StoreRes,
117 PRINT_RES_HANDLER* print_res,
118 int32_t err_type,
119 int32_t r_first,
120 int32_t r_last,
121 ResourceTable* resources,
122 BareosResource** res_head,
123 const char* config_default_filename,
124 const char* config_include_dir,
125 void (*ParseConfigBeforeCb)(ConfigurationParser&),
126 void (*ParseConfigReadyCb)(ConfigurationParser&),
127 SaveResourceCb_t SaveResourceCb,
128 DumpResourceCb_t DumpResourceCb,
129 FreeResourceCb_t FreeResourceCb)
130 : ConfigurationParser()
131 {
132 cf_ = cf == nullptr ? "" : cf;
133 use_config_include_dir_ = false;
134 config_include_naming_format_ = "%s/%s/%s.conf";
135 scan_error_ = ScanError;
136 scan_warning_ = scan_warning;
137 init_res_ = init_res;
138 store_res_ = StoreRes;
139 print_res_ = print_res;
140 err_type_ = err_type;
141 r_first_ = r_first;
142 r_last_ = r_last;
143 resources_ = resources;
144 res_head_ = res_head;
145 config_default_filename_
146 = config_default_filename == nullptr ? "" : config_default_filename;
147 config_include_dir_ = config_include_dir == nullptr ? "" : config_include_dir;
148 ParseConfigBeforeCb_ = ParseConfigBeforeCb;
149 ParseConfigReadyCb_ = ParseConfigReadyCb;
150 ASSERT(SaveResourceCb);
151 ASSERT(DumpResourceCb);
152 ASSERT(FreeResourceCb);
153 SaveResourceCb_ = SaveResourceCb;
154 DumpResourceCb_ = DumpResourceCb;
155 FreeResourceCb_ = FreeResourceCb;
156 }
157
~ConfigurationParser()158 ConfigurationParser::~ConfigurationParser()
159 {
160 if (res_head_) {
161 for (int i = r_first_; i <= r_last_; i++) {
162 if (res_head_[i - r_first_]) {
163 FreeResourceCb_(res_head_[i - r_first_], i);
164 }
165 res_head_[i - r_first_] = nullptr;
166 }
167 }
168 }
169
InitializeQualifiedResourceNameTypeConverter(const std::map<int,std::string> & map)170 void ConfigurationParser::InitializeQualifiedResourceNameTypeConverter(
171 const std::map<int, std::string>& map)
172 {
173 qualified_resource_name_type_converter_.reset(
174 new QualifiedResourceNameTypeConverter(map));
175 }
176
CreateOwnQualifiedNameForNetworkDump() const177 std::string ConfigurationParser::CreateOwnQualifiedNameForNetworkDump() const
178 {
179 std::string qualified_name;
180
181 if (own_resource_ && qualified_resource_name_type_converter_) {
182 if (qualified_resource_name_type_converter_->ResourceToString(
183 own_resource_->resource_name_, own_resource_->rcode_,
184 "::", qualified_name)) {
185 return qualified_name;
186 }
187 }
188 return qualified_name;
189 }
190
ParseConfig()191 bool ConfigurationParser::ParseConfig()
192 {
193 int errstat;
194 PoolMem config_path;
195
196 if (ParseConfigBeforeCb_) ParseConfigBeforeCb_(*this);
197
198 if (parser_first_run_ && (errstat = RwlInit(&res_lock_)) != 0) {
199 BErrNo be;
200 Jmsg1(nullptr, M_ABORT, 0,
201 _("Unable to initialize resource lock. ERR=%s\n"),
202 be.bstrerror(errstat));
203 }
204 parser_first_run_ = false;
205
206 if (!FindConfigPath(config_path)) {
207 Jmsg0(nullptr, M_ERROR_TERM, 0, _("Failed to find config filename.\n"));
208 }
209 used_config_path_ = config_path.c_str();
210 Dmsg1(100, "config file = %s\n", used_config_path_.c_str());
211 bool success = ParseConfigFile(config_path.c_str(), nullptr, scan_error_,
212 scan_warning_);
213 if (success && ParseConfigReadyCb_) { ParseConfigReadyCb_(*this); }
214 return success;
215 }
216
lex_error(const char * cf,LEX_ERROR_HANDLER * ScanError,LEX_WARNING_HANDLER * scan_warning) const217 void ConfigurationParser::lex_error(const char* cf,
218 LEX_ERROR_HANDLER* ScanError,
219 LEX_WARNING_HANDLER* scan_warning) const
220 {
221 /*
222 * We must create a lex packet to print the error
223 */
224 LEX* lexical_parser_ = (LEX*)malloc(sizeof(LEX));
225 memset(lexical_parser_, 0, sizeof(LEX));
226
227 if (ScanError) {
228 lexical_parser_->ScanError = ScanError;
229 } else {
230 LexSetDefaultErrorHandler(lexical_parser_);
231 }
232
233 if (scan_warning) {
234 lexical_parser_->scan_warning = scan_warning;
235 } else {
236 LexSetDefaultWarningHandler(lexical_parser_);
237 }
238
239 LexSetErrorHandlerErrorType(lexical_parser_, err_type_);
240 BErrNo be;
241 scan_err2(lexical_parser_, _("Cannot open config file \"%s\": %s\n"), cf,
242 be.bstrerror());
243 free(lexical_parser_);
244 }
245
ParseConfigFile(const char * config_file_name,void * caller_ctx,LEX_ERROR_HANDLER * scan_error,LEX_WARNING_HANDLER * scan_warning)246 bool ConfigurationParser::ParseConfigFile(const char* config_file_name,
247 void* caller_ctx,
248 LEX_ERROR_HANDLER* scan_error,
249 LEX_WARNING_HANDLER* scan_warning)
250 {
251 ConfigParserStateMachine state_machine(config_file_name, caller_ctx,
252 scan_error, scan_warning, *this);
253
254 Dmsg1(900, "Enter ParseConfigFile(%s)\n", config_file_name);
255
256 do {
257 if (!state_machine.InitParserPass()) { return false; }
258
259 if (!state_machine.ParseAllTokens()) {
260 scan_err0(state_machine.lexical_parser_, _("ParseAllTokens failed."));
261 return false;
262 }
263
264 switch (state_machine.GetParseError()) {
265 case ConfigParserStateMachine::ParserError::kResourceIncomplete:
266 scan_err0(state_machine.lexical_parser_,
267 _("End of conf file reached with unclosed resource."));
268 return false;
269 case ConfigParserStateMachine::ParserError::kParserError:
270 scan_err0(state_machine.lexical_parser_, _("Parser Error occurred."));
271 return false;
272 case ConfigParserStateMachine::ParserError::kNoError:
273 break;
274 }
275
276 } while (!state_machine.Finished());
277
278 state_machine.DumpResourcesAfterSecondPass();
279
280 Dmsg0(900, "Leave ParseConfigFile()\n");
281 return true;
282 }
283
AppendToResourcesChain(BareosResource * new_resource,int rcode)284 bool ConfigurationParser::AppendToResourcesChain(BareosResource* new_resource,
285 int rcode)
286 {
287 int rindex = rcode - r_first_;
288
289 if (!new_resource->resource_name_) {
290 Emsg1(M_ERROR, 0,
291 _("Name item is required in %s resource, but not found.\n"),
292 resources_[rindex].name);
293 return false;
294 }
295
296 if (!res_head_[rindex]) {
297 res_head_[rindex] = new_resource;
298 Dmsg3(900, "Inserting first %s res: %s index=%d\n", ResToStr(rcode),
299 new_resource->resource_name_, rindex);
300 } else { // append
301 BareosResource* last = nullptr;
302 BareosResource* current = res_head_[rindex];
303 do {
304 if (bstrcmp(current->resource_name_, new_resource->resource_name_)) {
305 Emsg2(M_ERROR, 0,
306 _("Attempt to define second %s resource named \"%s\" is not "
307 "permitted.\n"),
308 resources_[rindex].name, new_resource->resource_name_);
309 return false;
310 }
311 last = current;
312 current = last->next_;
313 } while (current);
314 last->next_ = new_resource;
315 Dmsg3(900, _("Inserting %s res: %s index=%d\n"), ResToStr(rcode),
316 new_resource->resource_name_, rindex);
317 }
318 return true;
319 }
320
GetResourceTableIndex(int resource_type)321 int ConfigurationParser::GetResourceTableIndex(int resource_type)
322 {
323 int rindex = -1;
324
325 if ((resource_type >= r_first_) && (resource_type <= r_last_)) {
326 rindex = resource_type = r_first_;
327 }
328
329 return rindex;
330 }
331
GetResourceTable(int resource_type)332 ResourceTable* ConfigurationParser::GetResourceTable(int resource_type)
333 {
334 ResourceTable* result = nullptr;
335 int rindex = GetResourceTableIndex(resource_type);
336
337 if (rindex >= 0) { result = &resources_[rindex]; }
338
339 return result;
340 }
341
GetResourceTable(const char * resource_type_name)342 ResourceTable* ConfigurationParser::GetResourceTable(
343 const char* resource_type_name)
344 {
345 ResourceTable* result = nullptr;
346 int i;
347
348 for (i = 0; resources_[i].name; i++) {
349 if (Bstrcasecmp(resources_[i].name, resource_type_name)) {
350 result = &resources_[i];
351 }
352 }
353
354 return result;
355 }
356
GetResourceItemIndex(ResourceItem * resource_items_,const char * item)357 int ConfigurationParser::GetResourceItemIndex(ResourceItem* resource_items_,
358 const char* item)
359 {
360 int result = -1;
361 int i;
362
363 for (i = 0; resource_items_[i].name; i++) {
364 if (Bstrcasecmp(resource_items_[i].name, item)) {
365 result = i;
366 break;
367 }
368 }
369
370 return result;
371 }
372
GetResourceItem(ResourceItem * resource_items_,const char * item)373 ResourceItem* ConfigurationParser::GetResourceItem(
374 ResourceItem* resource_items_,
375 const char* item)
376 {
377 ResourceItem* result = nullptr;
378 int i = -1;
379
380 if (resource_items_) {
381 i = GetResourceItemIndex(resource_items_, item);
382 if (i >= 0) { result = &resource_items_[i]; }
383 }
384
385 return result;
386 }
387
GetDefaultConfigDir()388 const char* ConfigurationParser::GetDefaultConfigDir()
389 {
390 #if defined(HAVE_WIN32)
391 HRESULT hr;
392 static char szConfigDir[MAX_PATH + 1] = {0};
393
394 if (!p_SHGetFolderPath) {
395 bstrncpy(szConfigDir, DEFAULT_CONFIGDIR, sizeof(szConfigDir));
396 return szConfigDir;
397 }
398
399 if (szConfigDir[0] == '\0') {
400 hr = p_SHGetFolderPath(nullptr, CSIDL_COMMON_APPDATA, nullptr, 0,
401 szConfigDir);
402
403 if (SUCCEEDED(hr)) {
404 bstrncat(szConfigDir, "\\Bareos", sizeof(szConfigDir));
405 } else {
406 bstrncpy(szConfigDir, DEFAULT_CONFIGDIR, sizeof(szConfigDir));
407 }
408 }
409
410 return szConfigDir;
411 #else
412 return CONFDIR;
413 #endif
414 }
415
416 #ifdef HAVE_SETENV
set_env(const char * key,const char * value)417 static inline void set_env(const char* key, const char* value)
418 {
419 setenv(key, value, 1);
420 }
421 #elif HAVE_PUTENV
set_env(const char * key,const char * value)422 static inline void set_env(const char* key, const char* value)
423 {
424 PoolMem env_string;
425
426 Mmsg(env_string, "%s=%s", key, value);
427 putenv(strdup(env_string.c_str()));
428 }
429 #else
set_env(const char * key,const char * value)430 static inline void set_env(const char* key, const char* value) {}
431 #endif
432
GetConfigFile(PoolMem & full_path,const char * config_dir,const char * config_filename)433 bool ConfigurationParser::GetConfigFile(PoolMem& full_path,
434 const char* config_dir,
435 const char* config_filename)
436 {
437 bool found = false;
438
439 if (!PathIsDirectory(config_dir)) { return false; }
440
441 if (config_filename) {
442 full_path.strcpy(config_dir);
443 if (PathAppend(full_path, config_filename)) {
444 if (PathExists(full_path)) {
445 config_dir_ = config_dir;
446 found = true;
447 }
448 }
449 }
450
451 return found;
452 }
453
GetConfigIncludePath(PoolMem & full_path,const char * config_dir)454 bool ConfigurationParser::GetConfigIncludePath(PoolMem& full_path,
455 const char* config_dir)
456 {
457 bool found = false;
458
459 if (!config_include_dir_.empty()) {
460 /*
461 * Set full_path to the initial part of the include path,
462 * so it can be used as result, even on errors.
463 * On success, full_path will be overwritten with the full path.
464 */
465 full_path.strcpy(config_dir);
466 PathAppend(full_path, config_include_dir_.c_str());
467 if (PathIsDirectory(full_path)) {
468 config_dir_ = config_dir;
469 /*
470 * Set full_path to wildcard path.
471 */
472 if (GetPathOfResource(full_path, nullptr, nullptr, nullptr, true)) {
473 use_config_include_dir_ = true;
474 found = true;
475 }
476 }
477 }
478
479 return found;
480 }
481
482 /*
483 * Returns false on error
484 * true on OK, with full_path set to where config file should be
485 */
FindConfigPath(PoolMem & full_path)486 bool ConfigurationParser::FindConfigPath(PoolMem& full_path)
487 {
488 bool found = false;
489 PoolMem config_dir;
490 PoolMem config_path_file;
491
492 if (cf_.empty()) {
493 /*
494 * No path is given, so use the defaults.
495 */
496 found = GetConfigFile(full_path, GetDefaultConfigDir(),
497 config_default_filename_.c_str());
498 if (!found) {
499 config_path_file.strcpy(full_path);
500 found = GetConfigIncludePath(full_path, GetDefaultConfigDir());
501 }
502 if (!found) {
503 Jmsg2(nullptr, M_ERROR, 0,
504 _("Failed to read config file at the default locations "
505 "\"%s\" (config file path) and \"%s\" (config include "
506 "directory).\n"),
507 config_path_file.c_str(), full_path.c_str());
508 }
509 } else if (PathExists(cf_.c_str())) {
510 /*
511 * Path is given and exists.
512 */
513 if (PathIsDirectory(cf_.c_str())) {
514 found = GetConfigFile(full_path, cf_.c_str(),
515 config_default_filename_.c_str());
516 if (!found) {
517 config_path_file.strcpy(full_path);
518 found = GetConfigIncludePath(full_path, cf_.c_str());
519 }
520 if (!found) {
521 Jmsg3(nullptr, M_ERROR, 0,
522 _("Failed to find configuration files under directory \"%s\". "
523 "Did look for \"%s\" (config file path) and \"%s\" (config "
524 "include directory).\n"),
525 cf_.c_str(), config_path_file.c_str(), full_path.c_str());
526 }
527 } else {
528 full_path.strcpy(cf_.c_str());
529 PathGetDirectory(config_dir, full_path);
530 config_dir_ = config_dir.c_str();
531 found = true;
532 }
533 } else if (config_default_filename_.empty()) {
534 /*
535 * Compatibility with older versions.
536 * If config_default_filename_ is not set,
537 * cf_ may contain what is expected in config_default_filename_.
538 */
539 found = GetConfigFile(full_path, GetDefaultConfigDir(), cf_.c_str());
540 if (!found) {
541 Jmsg2(nullptr, M_ERROR, 0,
542 _("Failed to find configuration files at \"%s\" and \"%s\".\n"),
543 cf_.c_str(), full_path.c_str());
544 }
545 } else {
546 Jmsg1(nullptr, M_ERROR, 0, _("Failed to read config file \"%s\"\n"),
547 cf_.c_str());
548 }
549
550 if (found) { set_env("BAREOS_CFGDIR", config_dir_.c_str()); }
551
552 return found;
553 }
554
SaveResources()555 BareosResource** ConfigurationParser::SaveResources()
556 {
557 int num = r_last_ - r_first_ + 1;
558 BareosResource** res
559 = (BareosResource**)malloc(num * sizeof(BareosResource*));
560
561 for (int i = 0; i < num; i++) {
562 res[i] = res_head_[i];
563 res_head_[i] = nullptr;
564 }
565
566 return res;
567 }
568
RemoveResource(int rcode,const char * name)569 bool ConfigurationParser::RemoveResource(int rcode, const char* name)
570 {
571 int rindex = rcode - r_first_;
572 BareosResource* last;
573
574 /*
575 * Remove resource from list.
576 *
577 * Note: this is intended for removing a resource that has just been added,
578 * but proven to be incorrect (added by console command "configure add").
579 * For a general approach, a check if this resource is referenced by other
580 * resources must be added. If it is referenced, don't remove it.
581 */
582 last = nullptr;
583 for (BareosResource* res = res_head_[rindex]; res; res = res->next_) {
584 if (bstrcmp(res->resource_name_, name)) {
585 if (!last) {
586 Dmsg2(900,
587 _("removing resource %s, name=%s (first resource in list)\n"),
588 ResToStr(rcode), name);
589 res_head_[rindex] = res->next_;
590 } else {
591 Dmsg2(900, _("removing resource %s, name=%s\n"), ResToStr(rcode), name);
592 last->next_ = res->next_;
593 }
594 res->next_ = nullptr;
595 FreeResourceCb_(res, rcode);
596 return true;
597 }
598 last = res;
599 }
600
601 /*
602 * Resource with this name not found
603 */
604 return false;
605 }
606
DumpResources(bool sendit (void * sock,const char * fmt,...),void * sock,bool hide_sensitive_data)607 void ConfigurationParser::DumpResources(bool sendit(void* sock,
608 const char* fmt,
609 ...),
610 void* sock,
611 bool hide_sensitive_data)
612 {
613 for (int i = r_first_; i <= r_last_; i++) {
614 if (res_head_[i - r_first_]) {
615 DumpResourceCb_(i, res_head_[i - r_first_], sendit, sock,
616 hide_sensitive_data, false);
617 }
618 }
619 }
620
GetPathOfResource(PoolMem & path,const char * component,const char * resourcetype,const char * name,bool set_wildcards)621 bool ConfigurationParser::GetPathOfResource(PoolMem& path,
622 const char* component,
623 const char* resourcetype,
624 const char* name,
625 bool set_wildcards)
626 {
627 PoolMem rel_path(PM_FNAME);
628 PoolMem directory(PM_FNAME);
629 PoolMem resourcetype_lowercase(resourcetype);
630 resourcetype_lowercase.toLower();
631
632 if (!component) {
633 if (!config_include_dir_.empty()) {
634 component = config_include_dir_.c_str();
635 } else {
636 return false;
637 }
638 }
639
640 if (resourcetype_lowercase.strlen() <= 0) {
641 if (set_wildcards) {
642 resourcetype_lowercase.strcpy("*");
643 } else {
644 return false;
645 }
646 }
647
648 if (!name) {
649 if (set_wildcards) {
650 name = "*";
651 } else {
652 return false;
653 }
654 }
655
656 path.strcpy(config_dir_.c_str());
657 rel_path.bsprintf(config_include_naming_format_.c_str(), component,
658 resourcetype_lowercase.c_str(), name);
659 PathAppend(path, rel_path);
660
661 return true;
662 }
663
GetPathOfNewResource(PoolMem & path,PoolMem & extramsg,const char * component,const char * resourcetype,const char * name,bool error_if_exists,bool create_directories)664 bool ConfigurationParser::GetPathOfNewResource(PoolMem& path,
665 PoolMem& extramsg,
666 const char* component,
667 const char* resourcetype,
668 const char* name,
669 bool error_if_exists,
670 bool create_directories)
671 {
672 PoolMem rel_path(PM_FNAME);
673 PoolMem directory(PM_FNAME);
674 PoolMem resourcetype_lowercase(resourcetype);
675 resourcetype_lowercase.toLower();
676
677 if (!GetPathOfResource(path, component, resourcetype, name, false)) {
678 return false;
679 }
680
681 PathGetDirectory(directory, path);
682
683 if (create_directories) { PathCreate(directory); }
684
685 if (!PathExists(directory)) {
686 extramsg.bsprintf("Resource config directory \"%s\" does not exist.\n",
687 directory.c_str());
688 return false;
689 }
690
691 /*
692 * Store name for temporary file in extramsg.
693 * Can be used, if result is true.
694 * Otherwise it contains an error message.
695 */
696 extramsg.bsprintf("%s.tmp", path.c_str());
697
698 if (!error_if_exists) { return true; }
699
700 /*
701 * File should not exists, as it is going to be created.
702 */
703 if (PathExists(path)) {
704 extramsg.bsprintf("Resource config file \"%s\" already exists.\n",
705 path.c_str());
706 return false;
707 }
708
709 if (PathExists(extramsg)) {
710 extramsg.bsprintf(
711 "Temporary resource config file \"%s.tmp\" already exists.\n",
712 path.c_str());
713 return false;
714 }
715
716 return true;
717 }
718
AddWarning(const std::string & warning)719 void ConfigurationParser::AddWarning(const std::string& warning)
720 {
721 warnings_ << warning;
722 }
723
ClearWarnings()724 void ConfigurationParser::ClearWarnings() { warnings_.clear(); }
725
HasWarnings() const726 bool ConfigurationParser::HasWarnings() const { return !warnings_.empty(); }
727
GetWarnings() const728 const BStringList& ConfigurationParser::GetWarnings() const
729 {
730 return warnings_;
731 }
732