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