ECConfigImpl.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648
  1. /*
  2. * Copyright 2005 - 2016 Zarafa and its licensors
  3. *
  4. * This program is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU Affero General Public License, version 3,
  6. * as published by the Free Software Foundation.
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU Affero General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU Affero General Public License
  14. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. *
  16. */
  17. #include <kopano/zcdefs.h>
  18. #include <kopano/platform.h>
  19. #include <memory>
  20. #include <mutex>
  21. #include <utility>
  22. #include <cerrno>
  23. #include <climits>
  24. #include <cstdio>
  25. #include <cstdlib>
  26. #include <cstring>
  27. #include <algorithm>
  28. #include <cassert>
  29. #include <sys/stat.h>
  30. #include <kopano/lockhelper.hpp>
  31. #include <kopano/memory.hpp>
  32. #include <kopano/stringutil.h>
  33. #include "ECConfigImpl.h"
  34. #include <kopano/charset/convert.h>
  35. using namespace std;
  36. using namespace KCHL;
  37. namespace KC {
  38. const directive_t ECConfigImpl::s_sDirectives[] = {
  39. { "include", &ECConfigImpl::HandleInclude },
  40. { "propmap", &ECConfigImpl::HandlePropMap },
  41. { NULL }
  42. };
  43. // Configuration file parser
  44. ECConfigImpl::ECConfigImpl(const configsetting_t *lpDefaults,
  45. const char *const *lpszDirectives) :
  46. m_lpDefaults(lpDefaults)
  47. {
  48. // allowed directives in this config object
  49. for (int i = 0; lpszDirectives != NULL && lpszDirectives[i] != NULL; ++i)
  50. m_lDirectives.push_back(lpszDirectives[i]);
  51. InitDefaults(LOADSETTING_INITIALIZING | LOADSETTING_UNKNOWN | LOADSETTING_OVERWRITE);
  52. }
  53. bool ECConfigImpl::LoadSettings(const char *szFilename)
  54. {
  55. m_szConfigFile = szFilename;
  56. return InitConfigFile(LOADSETTING_OVERWRITE);
  57. }
  58. static int tounderscore(int c)
  59. {
  60. return c == '-' ? '_' : c;
  61. }
  62. /**
  63. * Parse commandline parameters to override the values loaded from the
  64. * config files.
  65. *
  66. * This function accepts only long options in the form
  67. * --option-name=value. All dashes in the option-name will be converted
  68. * to underscores. The option-name should then match a valid config option.
  69. * This config option will be set to value. No processing is done on value
  70. * except for removing leading and trailing whitespaces.
  71. *
  72. * The aray in argv will be reordered so all non-long-option values will
  73. * be located after the long-options. On return *lpargidx will be the
  74. * index of the first non-long-option in the array.
  75. *
  76. * @param[in] argc The number of arguments to parse.
  77. * @param[in] argv The parameters to parse. The size of the
  78. * array must be at least argc.
  79. * @param[out] lpargidx Pointer to an integer that will be set to
  80. * the index in argv where parsing stopped.
  81. * This parameter may be NULL.
  82. * @retval true
  83. */
  84. int ECConfigImpl::ParseParams(int argc, char **argv)
  85. {
  86. for (int i = 0; i < argc; ++i) {
  87. char *arg = argv[i];
  88. if (arg == nullptr)
  89. continue;
  90. if (arg[0] != '-' || arg[1] != '-') {
  91. // Move non-long-option to end of list
  92. --argc;
  93. for (int j = i; j < argc; ++j)
  94. argv[j] = argv[j+1];
  95. argv[argc] = arg;
  96. --i;
  97. continue;
  98. }
  99. const char *eq = strchr(arg, '=');
  100. if (eq == nullptr) {
  101. errors.push_back("Commandline option '" + string(arg+2) + "' cannot be empty!");
  102. continue;
  103. }
  104. string strName(arg+2, eq-arg-2);
  105. string strValue(eq+1);
  106. strName = trim(strName, " \t\r\n");
  107. strValue = trim(strValue, " \t\r\n");
  108. std::transform(strName.begin(), strName.end(), strName.begin(), tounderscore);
  109. configsetting_t setting = {strName.c_str(), strValue.c_str(), 0, 0};
  110. // Overwrite an existing setting, and make sure it is not reloadable during HUP
  111. AddSetting(&setting, LOADSETTING_OVERWRITE | LOADSETTING_CMDLINE_PARAM);
  112. }
  113. return argc;
  114. }
  115. bool ECConfigImpl::ReloadSettings()
  116. {
  117. // unsetting this value isn't possible
  118. if (!m_szConfigFile)
  119. return false;
  120. // Check if we can still open the main config file. Do not reset to Defaults
  121. FILE *fp = fopen(m_szConfigFile, "rt");
  122. if (fp == nullptr)
  123. return false;
  124. fclose(fp);
  125. // reset to defaults because unset items in config file should return to default values.
  126. InitDefaults(LOADSETTING_OVERWRITE_RELOAD);
  127. return InitConfigFile(LOADSETTING_OVERWRITE_RELOAD);
  128. }
  129. bool ECConfigImpl::AddSetting(const char *szName, const char *szValue, const unsigned int ulGroup)
  130. {
  131. configsetting_t sSetting;
  132. sSetting.szName = szName;
  133. sSetting.szValue = szValue;
  134. sSetting.ulFlags = 0;
  135. sSetting.ulGroup = ulGroup;
  136. return AddSetting(&sSetting, ulGroup ? LOADSETTING_OVERWRITE_GROUP : LOADSETTING_OVERWRITE);
  137. }
  138. static void freeSettings(settingmap_t::value_type &entry)
  139. {
  140. // see InsertOrReplace
  141. delete [] entry.second;
  142. }
  143. void ECConfigImpl::CleanupMap(settingmap_t *lpMap)
  144. {
  145. if (!lpMap->empty())
  146. for_each(lpMap->begin(), lpMap->end(), freeSettings);
  147. }
  148. ECConfigImpl::~ECConfigImpl()
  149. {
  150. std::lock_guard<KC::shared_mutex> lset(m_settingsRWLock);
  151. CleanupMap(&m_mapSettings);
  152. CleanupMap(&m_mapAliases);
  153. }
  154. /**
  155. * Returns the size in bytes for a size marked config value
  156. *
  157. * @param szValue input value from config file
  158. *
  159. * @return size in bytes
  160. */
  161. size_t ECConfigImpl::GetSize(const char *szValue)
  162. {
  163. if (szValue == nullptr)
  164. return 0;
  165. char *end = NULL;
  166. unsigned long long rv = strtoull(szValue, &end, 10);
  167. if (rv == 0 || end <= szValue || *end == '\0')
  168. return rv;
  169. while (*end != '\0' && (*end == ' ' || *end == '\t'))
  170. ++end;
  171. switch (tolower(*end)) {
  172. case 'k': return rv << 10; break;
  173. case 'm': return rv << 20; break;
  174. case 'g': return rv << 30; break;
  175. }
  176. return rv;
  177. }
  178. /**
  179. * Adds a new setting to the map, or replaces the current data.
  180. * Only the first 1024 bytes of the value are saved, longer values are truncated.
  181. * The map must be locked by the m_settingsRWLock.
  182. *
  183. * @param lpMap settings map to set value in
  184. * @param s key to access map point
  185. * @param szValue new value to set in map
  186. */
  187. void ECConfigImpl::InsertOrReplace(settingmap_t *lpMap, const settingkey_t &s, const char* szValue, bool bIsSize)
  188. {
  189. char* data = NULL;
  190. size_t len = std::min((size_t)1023, strlen(szValue));
  191. auto i = lpMap->find(s);
  192. if (i == lpMap->cend()) {
  193. // Insert new value
  194. data = new char[1024];
  195. lpMap->insert(make_pair(s, data));
  196. } else {
  197. // Actually remove and re-insert the map entry since we may be modifying
  198. // ulFlags in the key (this is a bit of a hack, since you shouldn't be modifying
  199. // stuff in the key, but this is the easiest)
  200. data = i->second;
  201. lpMap->erase(i);
  202. lpMap->insert(make_pair(s, data));
  203. }
  204. if (bIsSize)
  205. len = snprintf(data, 1024, "%zu", GetSize(szValue));
  206. else
  207. strncpy(data, szValue, len);
  208. data[len] = '\0';
  209. }
  210. const char *ECConfigImpl::GetMapEntry(const settingmap_t *lpMap,
  211. const char *szName)
  212. {
  213. const char *retval = NULL;
  214. if (szName == NULL)
  215. return NULL;
  216. settingkey_t key = {""};
  217. if (strlen(szName) >= sizeof(key.s))
  218. return NULL;
  219. strcpy(key.s, szName);
  220. KC::shared_lock<KC::shared_mutex> lset(m_settingsRWLock);
  221. auto itor = lpMap->find(key);
  222. if (itor != lpMap->cend())
  223. retval = itor->second;
  224. return retval;
  225. }
  226. const char *ECConfigImpl::GetSetting(const char *szName)
  227. {
  228. return GetMapEntry(&m_mapSettings, szName);
  229. }
  230. const char *ECConfigImpl::GetAlias(const char *szName)
  231. {
  232. return GetMapEntry(&m_mapAliases, szName);
  233. }
  234. const char *ECConfigImpl::GetSetting(const char *szName, const char *equal,
  235. const char *other)
  236. {
  237. const char *value = this->GetSetting(szName);
  238. if (value == equal || (value && equal && !strcmp(value, equal)))
  239. return other;
  240. else
  241. return value;
  242. }
  243. const wchar_t *ECConfigImpl::GetSettingW(const char *szName)
  244. {
  245. const char *value = GetSetting(szName);
  246. auto result = m_convertCache.insert(ConvertCache::value_type(value, L""));
  247. auto iter = result.first;
  248. if (result.second)
  249. iter->second = convert_to<wstring>(value);
  250. return iter->second.c_str();
  251. }
  252. const wchar_t *ECConfigImpl::GetSettingW(const char *szName,
  253. const wchar_t *equal, const wchar_t *other)
  254. {
  255. const wchar_t *value = this->GetSettingW(szName);
  256. if (value == equal || (value && equal && !wcscmp(value, equal)))
  257. return other;
  258. else
  259. return value;
  260. }
  261. list<configsetting_t> ECConfigImpl::GetSettingGroup(unsigned int ulGroup)
  262. {
  263. list<configsetting_t> lGroup;
  264. configsetting_t sSetting;
  265. for (const auto &s : m_mapSettings)
  266. if ((s.first.ulGroup & ulGroup) == ulGroup &&
  267. CopyConfigSetting(&s.first, s.second, &sSetting))
  268. lGroup.push_back(std::move(sSetting));
  269. return lGroup;
  270. }
  271. std::list<configsetting_t> ECConfigImpl::GetAllSettings()
  272. {
  273. list<configsetting_t> lSettings;
  274. configsetting_t sSetting;
  275. for (const auto &s : m_mapSettings)
  276. if (CopyConfigSetting(&s.first, s.second, &sSetting))
  277. lSettings.push_back(std::move(sSetting));
  278. return lSettings;
  279. }
  280. bool ECConfigImpl::InitDefaults(unsigned int ulFlags)
  281. {
  282. unsigned int i = 0;
  283. /* throw error? this is unacceptable! useless object, since it won't set new settings */
  284. if (!m_lpDefaults)
  285. return false;
  286. while (m_lpDefaults[i].szName != NULL) {
  287. if (m_lpDefaults[i].ulFlags & CONFIGSETTING_ALIAS) {
  288. /* Aliases are only initialized once */
  289. if (ulFlags & LOADSETTING_INITIALIZING)
  290. AddAlias(&m_lpDefaults[i]);
  291. } else
  292. AddSetting(&m_lpDefaults[i], ulFlags);
  293. ++i;
  294. }
  295. return true;
  296. }
  297. bool ECConfigImpl::InitConfigFile(unsigned int ulFlags)
  298. {
  299. bool bResult = false;
  300. assert(m_readFiles.empty());
  301. if (!m_szConfigFile)
  302. return false;
  303. bResult = ReadConfigFile(m_szConfigFile, ulFlags);
  304. m_readFiles.clear();
  305. return bResult;
  306. }
  307. bool ECConfigImpl::ReadConfigFile(const std::string &file,
  308. unsigned int ulFlags, unsigned int ulGroup)
  309. {
  310. FILE *fp = NULL;
  311. bool bReturn = false;
  312. char cBuffer[MAXLINELEN] = {0};
  313. string strFilename;
  314. string strLine;
  315. string strName;
  316. string strValue;
  317. size_t pos;
  318. std::unique_ptr<char, cstdlib_deleter> normalized_file(realpath(file.c_str(), nullptr));
  319. if (normalized_file == nullptr) {
  320. errors.push_back("Cannot normalize path \"" + file + "\": " + strerror(errno));
  321. return false;
  322. }
  323. struct stat sb;
  324. if (stat(normalized_file.get(), &sb) < 0) {
  325. errors.push_back("Config file \"" + file + "\" cannot be read: " + strerror(errno));
  326. return false;
  327. }
  328. if (!S_ISREG(sb.st_mode)) {
  329. errors.push_back("Config file \"" + file + "\" is not a file");
  330. return false;
  331. }
  332. // Store the path of the previous file in case we're recursively processing files.
  333. // We need to keep track of the current path so we can handle relative includes in HandleInclude
  334. std::string prevFile = m_currentFile;
  335. m_currentFile = normalized_file.get();
  336. /* Check if we read this file before. */
  337. if (std::find(m_readFiles.cbegin(), m_readFiles.cend(), m_currentFile) != m_readFiles.cend()) {
  338. bReturn = true;
  339. goto exit;
  340. }
  341. m_readFiles.insert(m_currentFile);
  342. fp = fopen(file.c_str(), "rt");
  343. if (fp == nullptr) {
  344. errors.push_back("Unable to open config file \"" + file + "\"");
  345. goto exit;
  346. }
  347. while (!feof(fp)) {
  348. memset(&cBuffer, 0, sizeof(cBuffer));
  349. if (!fgets(cBuffer, sizeof(cBuffer), fp))
  350. continue;
  351. strLine = string(cBuffer);
  352. /* Skip empty lines any lines which start with # */
  353. if (strLine.empty() || strLine[0] == '#')
  354. continue;
  355. /* Handle special directives which start with '!' */
  356. if (strLine[0] == '!') {
  357. if (!HandleDirective(strLine, ulFlags))
  358. goto exit;
  359. continue;
  360. }
  361. /* Get setting name */
  362. pos = strLine.find('=');
  363. if (pos != string::npos) {
  364. strName = strLine.substr(0, pos);
  365. strValue = strLine.substr(pos + 1);
  366. } else
  367. continue;
  368. /*
  369. * The line is build up like this:
  370. * config_name = bla bla
  371. *
  372. * Whe should clean it in such a way that it resolves to:
  373. * config_name=bla bla
  374. *
  375. * Be careful _not_ to remove any whitespace characters
  376. * within the configuration value itself.
  377. */
  378. strName = trim(strName, " \t\r\n");
  379. strValue = trim(strValue, " \t\r\n");
  380. if(!strName.empty()) {
  381. // Save it
  382. configsetting_t setting = { strName.c_str(), strValue.c_str(), 0, static_cast<unsigned short int>(ulGroup) };
  383. AddSetting(&setting, ulFlags);
  384. }
  385. }
  386. bReturn = true;
  387. exit:
  388. if(fp)
  389. fclose(fp);
  390. // Restore the path of the previous file.
  391. m_currentFile.swap(prevFile);
  392. return bReturn;
  393. }
  394. bool ECConfigImpl::HandleDirective(const string &strLine, unsigned int ulFlags)
  395. {
  396. size_t pos = strLine.find_first_of(" \t", 1);
  397. string strName = strLine.substr(1, pos - 1);
  398. /* Check if this directive is known */
  399. for (int i = 0; s_sDirectives[i].lpszDirective != NULL; ++i) {
  400. if (strName.compare(s_sDirectives[i].lpszDirective) != 0)
  401. continue;
  402. /* Check if this directive is supported */
  403. auto f = find(m_lDirectives.cbegin(), m_lDirectives.cend(), strName);
  404. if (f != m_lDirectives.cend())
  405. return (this->*s_sDirectives[i].fExecute)(strLine.substr(pos).c_str(), ulFlags);
  406. warnings.push_back("Unsupported directive '" + strName + "' found!");
  407. return true;
  408. }
  409. warnings.push_back("Unknown directive '" + strName + "' found!");
  410. return true;
  411. }
  412. bool ECConfigImpl::HandleInclude(const char *lpszArgs, unsigned int ulFlags)
  413. {
  414. string strValue;
  415. std::string file;
  416. file = (strValue = trim(lpszArgs, " \t\r\n"));
  417. if (file[0] != PATH_SEPARATOR) {
  418. // Rebuild the path. m_currentFile is always a normalized path.
  419. auto pos = m_currentFile.find_last_of(PATH_SEPARATOR);
  420. file = (pos != std::string::npos) ? m_currentFile.substr(0, pos) : ".";
  421. file += PATH_SEPARATOR;
  422. file += strValue;
  423. }
  424. return ReadConfigFile(file, ulFlags);
  425. }
  426. bool ECConfigImpl::HandlePropMap(const char *lpszArgs, unsigned int ulFlags)
  427. {
  428. string strValue;
  429. bool bResult;
  430. strValue = trim(lpszArgs, " \t\r\n");
  431. bResult = ReadConfigFile(strValue.c_str(), LOADSETTING_UNKNOWN | LOADSETTING_OVERWRITE_GROUP, CONFIGGROUP_PROPMAP);
  432. return bResult;
  433. }
  434. bool ECConfigImpl::CopyConfigSetting(const configsetting_t *lpsSetting, settingkey_t *lpsKey)
  435. {
  436. if (lpsSetting->szName == NULL || lpsSetting->szValue == NULL)
  437. return false;
  438. memset(lpsKey, 0, sizeof(*lpsKey));
  439. kc_strlcpy(lpsKey->s, lpsSetting->szName, sizeof(lpsKey->s));
  440. lpsKey->ulFlags = lpsSetting->ulFlags;
  441. lpsKey->ulGroup = lpsSetting->ulGroup;
  442. return true;
  443. }
  444. bool ECConfigImpl::CopyConfigSetting(const settingkey_t *lpsKey, const char *szValue, configsetting_t *lpsSetting)
  445. {
  446. if (strlen(lpsKey->s) == 0 || szValue == NULL)
  447. return false;
  448. lpsSetting->szName = lpsKey->s;
  449. lpsSetting->szValue = szValue;
  450. lpsSetting->ulFlags = lpsKey->ulFlags;
  451. lpsSetting->ulGroup = lpsKey->ulGroup;
  452. return true;
  453. }
  454. bool ECConfigImpl::AddSetting(const configsetting_t *lpsConfig, unsigned int ulFlags)
  455. {
  456. settingmap_t::const_iterator iterSettings;
  457. settingkey_t s;
  458. char *valid = NULL;
  459. const char *szAlias = NULL;
  460. if (!CopyConfigSetting(lpsConfig, &s))
  461. return false;
  462. // Lookup name as alias
  463. szAlias = GetAlias(lpsConfig->szName);
  464. if (szAlias) {
  465. if (!(ulFlags & LOADSETTING_INITIALIZING))
  466. warnings.push_back("Option '" + string(lpsConfig->szName) + "' is deprecated! New name for option is '" + szAlias + "'.");
  467. kc_strlcpy(s.s, szAlias, sizeof(s.s));
  468. }
  469. std::lock_guard<KC::shared_mutex> lset(m_settingsRWLock);
  470. iterSettings = m_mapSettings.find(s);
  471. if (iterSettings == m_mapSettings.cend()) {
  472. // new items from file are illegal, add error
  473. if (!(ulFlags & LOADSETTING_UNKNOWN)) {
  474. errors.push_back("Unknown option '" + string(lpsConfig->szName) + "' found!");
  475. return true;
  476. }
  477. } else {
  478. // Check for permissions before overwriting
  479. if (ulFlags & LOADSETTING_OVERWRITE_GROUP) {
  480. if (iterSettings->first.ulGroup != lpsConfig->ulGroup) {
  481. errors.push_back("option '" + string(lpsConfig->szName) + "' cannot be overridden (different group)!");
  482. return false;
  483. }
  484. } else if (ulFlags & LOADSETTING_OVERWRITE_RELOAD) {
  485. if (!(iterSettings->first.ulFlags & CONFIGSETTING_RELOADABLE))
  486. return false;
  487. } else if (!(ulFlags & LOADSETTING_OVERWRITE)) {
  488. errors.push_back("option '" + string(lpsConfig->szName) + "' cannot be overridden!");
  489. return false;
  490. }
  491. if (!(ulFlags & LOADSETTING_INITIALIZING) &&
  492. (iterSettings->first.ulFlags & CONFIGSETTING_UNUSED))
  493. warnings.push_back("Option '" + string(lpsConfig->szName) + "' is not used anymore.");
  494. s.ulFlags = iterSettings->first.ulFlags;
  495. // If this is a commandline parameter, mark the setting as non-reloadable since you do not want to
  496. // change the value after a HUP
  497. if (ulFlags & LOADSETTING_CMDLINE_PARAM)
  498. s.ulFlags &= ~ CONFIGSETTING_RELOADABLE;
  499. }
  500. if (lpsConfig->szValue[0] == '$' && (s.ulFlags & CONFIGSETTING_EXACT) == 0) {
  501. const char *szValue = getenv(lpsConfig->szValue + 1);
  502. if (szValue == NULL) {
  503. warnings.push_back("'" + string(lpsConfig->szValue + 1) + "' not found in environment, using '" + lpsConfig->szValue + "' for options '" + lpsConfig->szName + "'.");
  504. szValue = lpsConfig->szValue;
  505. }
  506. if (s.ulFlags & CONFIGSETTING_SIZE) {
  507. strtoul(szValue, &valid, 10);
  508. if (valid == szValue) {
  509. errors.push_back("Option '" + string(lpsConfig->szName) + "' must be a size value (number + optional k/m/g multiplier).");
  510. return false;
  511. }
  512. }
  513. InsertOrReplace(&m_mapSettings, s, szValue, lpsConfig->ulFlags & CONFIGSETTING_SIZE);
  514. return true;
  515. }
  516. if (s.ulFlags & CONFIGSETTING_SIZE) {
  517. strtoul(lpsConfig->szValue, &valid, 10);
  518. if (valid == lpsConfig->szValue) {
  519. errors.push_back("Option '" + string(lpsConfig->szName) + "' must be a size value (number + optional k/m/g multiplier).");
  520. return false;
  521. }
  522. }
  523. InsertOrReplace(&m_mapSettings, s, lpsConfig->szValue, s.ulFlags & CONFIGSETTING_SIZE);
  524. return true;
  525. }
  526. void ECConfigImpl::AddAlias(const configsetting_t *lpsAlias)
  527. {
  528. settingkey_t s;
  529. if (!CopyConfigSetting(lpsAlias, &s))
  530. return;
  531. std::lock_guard<KC::shared_mutex> lset(m_settingsRWLock);
  532. InsertOrReplace(&m_mapAliases, s, lpsAlias->szValue, false);
  533. }
  534. bool ECConfigImpl::HasWarnings() {
  535. return !warnings.empty();
  536. }
  537. bool ECConfigImpl::HasErrors() {
  538. /* First validate the configuration settings */
  539. KC::shared_lock<KC::shared_mutex> lset(m_settingsRWLock);
  540. for (const auto &s : m_mapSettings)
  541. if (s.first.ulFlags & CONFIGSETTING_NONEMPTY)
  542. if (!s.second || strlen(s.second) == 0)
  543. errors.push_back("Option '" + string(s.first.s) + "' cannot be empty!");
  544. return !errors.empty();
  545. }
  546. } /* namespace */