Mercurial > hg > orthanc
comparison OrthancServer/OrthancInitialization.cpp @ 2940:4767d36679ed
refactoring access to Orthanc configuration
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Thu, 29 Nov 2018 11:47:00 +0100 |
parents | 4a38d7d4f0e0 |
children | bbfd95a0c429 |
comparison
equal
deleted
inserted
replaced
2939:577786f59252 | 2940:4767d36679ed |
---|---|
37 // "Please include winsock2.h before windows.h" | 37 // "Please include winsock2.h before windows.h" |
38 # include <winsock2.h> | 38 # include <winsock2.h> |
39 #endif | 39 #endif |
40 | 40 |
41 #include "OrthancInitialization.h" | 41 #include "OrthancInitialization.h" |
42 #include "ServerContext.h" | 42 |
43 | 43 #include "../Core/DicomParsing/FromDcmtkBridge.h" |
44 #include "../Core/HttpClient.h" | 44 #include "../Core/FileStorage/FilesystemStorage.h" |
45 #include "../Core/Logging.h" | 45 #include "../Core/Logging.h" |
46 #include "../Core/OrthancException.h" | 46 #include "../Core/OrthancException.h" |
47 #include "../Core/Toolbox.h" | 47 |
48 #include "../Core/FileStorage/FilesystemStorage.h" | |
49 | |
50 #include "ServerEnumerations.h" | |
51 #include "DatabaseWrapper.h" | 48 #include "DatabaseWrapper.h" |
52 #include "../Core/DicomParsing/FromDcmtkBridge.h" | 49 #include "OrthancConfiguration.h" |
53 | |
54 #include <boost/lexical_cast.hpp> | |
55 #include <boost/filesystem.hpp> | |
56 #include <curl/curl.h> | |
57 #include <boost/thread/recursive_mutex.hpp> | |
58 | 50 |
59 #include <dcmtk/dcmnet/dul.h> // For dcmDisableGethostbyaddr() | 51 #include <dcmtk/dcmnet/dul.h> // For dcmDisableGethostbyaddr() |
60 | 52 |
61 | 53 |
62 | 54 |
63 namespace Orthanc | 55 namespace Orthanc |
64 { | 56 { |
65 static boost::recursive_mutex globalMutex_; | 57 static void RegisterUserMetadata(const Json::Value& config) |
66 static Json::Value configuration_; | 58 { |
67 static boost::filesystem::path defaultDirectory_; | 59 if (config.isMember("UserMetadata")) |
68 static std::string configurationAbsolutePath_; | 60 { |
69 static FontRegistry fontRegistry_; | 61 const Json::Value& parameter = config["UserMetadata"]; |
70 static const char* configurationFileArg_ = NULL; | |
71 | |
72 | |
73 static std::string GetGlobalStringParameterInternal(const std::string& parameter, | |
74 const std::string& defaultValue) | |
75 { | |
76 if (configuration_.isMember(parameter)) | |
77 { | |
78 if (configuration_[parameter].type() != Json::stringValue) | |
79 { | |
80 LOG(ERROR) << "The configuration option \"" << parameter << "\" must be a string"; | |
81 throw OrthancException(ErrorCode_BadParameterType); | |
82 } | |
83 else | |
84 { | |
85 return configuration_[parameter].asString(); | |
86 } | |
87 } | |
88 else | |
89 { | |
90 return defaultValue; | |
91 } | |
92 } | |
93 | |
94 | |
95 static bool GetGlobalBoolParameterInternal(const std::string& parameter, | |
96 bool defaultValue) | |
97 { | |
98 if (configuration_.isMember(parameter)) | |
99 { | |
100 if (configuration_[parameter].type() != Json::booleanValue) | |
101 { | |
102 LOG(ERROR) << "The configuration option \"" << parameter << "\" must be a Boolean (true or false)"; | |
103 throw OrthancException(ErrorCode_BadParameterType); | |
104 } | |
105 else | |
106 { | |
107 return configuration_[parameter].asBool(); | |
108 } | |
109 } | |
110 else | |
111 { | |
112 return defaultValue; | |
113 } | |
114 } | |
115 | |
116 | |
117 | |
118 static void AddFileToConfiguration(Json::Value& target, | |
119 const boost::filesystem::path& path) | |
120 { | |
121 std::map<std::string, std::string> env; | |
122 SystemToolbox::GetEnvironmentVariables(env); | |
123 | |
124 LOG(WARNING) << "Reading the configuration from: " << path; | |
125 | |
126 Json::Value config; | |
127 | |
128 { | |
129 std::string content; | |
130 SystemToolbox::ReadFile(content, path.string()); | |
131 | |
132 content = Toolbox::SubstituteVariables(content, env); | |
133 | |
134 Json::Value tmp; | |
135 Json::Reader reader; | |
136 if (!reader.parse(content, tmp) || | |
137 tmp.type() != Json::objectValue) | |
138 { | |
139 LOG(ERROR) << "The configuration file does not follow the JSON syntax: " << path; | |
140 throw OrthancException(ErrorCode_BadJson); | |
141 } | |
142 | |
143 Toolbox::CopyJsonWithoutComments(config, tmp); | |
144 } | |
145 | |
146 if (target.size() == 0) | |
147 { | |
148 target = config; | |
149 } | |
150 else | |
151 { | |
152 // Merge the newly-added file with the previous content of "target" | |
153 Json::Value::Members members = config.getMemberNames(); | |
154 for (Json::Value::ArrayIndex i = 0; i < members.size(); i++) | |
155 { | |
156 if (target.isMember(members[i])) | |
157 { | |
158 LOG(ERROR) << "The configuration section \"" << members[i] << "\" is defined in 2 different configuration files"; | |
159 throw OrthancException(ErrorCode_BadFileFormat); | |
160 } | |
161 else | |
162 { | |
163 target[members[i]] = config[members[i]]; | |
164 } | |
165 } | |
166 } | |
167 } | |
168 | |
169 | |
170 static void ScanFolderForConfiguration(Json::Value& target, | |
171 const char* folder) | |
172 { | |
173 using namespace boost::filesystem; | |
174 | |
175 LOG(WARNING) << "Scanning folder \"" << folder << "\" for configuration files"; | |
176 | |
177 directory_iterator end_it; // default construction yields past-the-end | |
178 for (directory_iterator it(folder); | |
179 it != end_it; | |
180 ++it) | |
181 { | |
182 if (!is_directory(it->status())) | |
183 { | |
184 std::string extension = boost::filesystem::extension(it->path()); | |
185 Toolbox::ToLowerCase(extension); | |
186 | |
187 if (extension == ".json") | |
188 { | |
189 AddFileToConfiguration(target, it->path().string()); | |
190 } | |
191 } | |
192 } | |
193 } | |
194 | |
195 | |
196 static void ReadConfiguration(Json::Value& target, | |
197 const char* configurationFile) | |
198 { | |
199 target = Json::objectValue; | |
200 | |
201 if (configurationFile) | |
202 { | |
203 if (!boost::filesystem::exists(configurationFile)) | |
204 { | |
205 LOG(ERROR) << "Inexistent path to configuration: " << configurationFile; | |
206 throw OrthancException(ErrorCode_InexistentFile); | |
207 } | |
208 | |
209 if (boost::filesystem::is_directory(configurationFile)) | |
210 { | |
211 ScanFolderForConfiguration(target, configurationFile); | |
212 } | |
213 else | |
214 { | |
215 AddFileToConfiguration(target, configurationFile); | |
216 } | |
217 } | |
218 else | |
219 { | |
220 #if ORTHANC_STANDALONE == 1 | |
221 // No default path for the standalone configuration | |
222 LOG(WARNING) << "Using the default Orthanc configuration"; | |
223 return; | |
224 | |
225 #else | |
226 // In a non-standalone build, we use the | |
227 // "Resources/Configuration.json" from the Orthanc source code | |
228 | |
229 boost::filesystem::path p = ORTHANC_PATH; | |
230 p /= "Resources"; | |
231 p /= "Configuration.json"; | |
232 | |
233 AddFileToConfiguration(target, p); | |
234 #endif | |
235 } | |
236 } | |
237 | |
238 | |
239 | |
240 static void ReadGlobalConfiguration(const char* configurationFile) | |
241 { | |
242 // Read the content of the configuration | |
243 configurationFileArg_ = configurationFile; | |
244 ReadConfiguration(configuration_, configurationFile); | |
245 | |
246 // Adapt the paths to the configurations | |
247 defaultDirectory_ = boost::filesystem::current_path(); | |
248 configurationAbsolutePath_ = ""; | |
249 | |
250 if (configurationFile) | |
251 { | |
252 if (boost::filesystem::is_directory(configurationFile)) | |
253 { | |
254 defaultDirectory_ = boost::filesystem::path(configurationFile); | |
255 configurationAbsolutePath_ = boost::filesystem::absolute(configurationFile).parent_path().string(); | |
256 } | |
257 else | |
258 { | |
259 defaultDirectory_ = boost::filesystem::path(configurationFile).parent_path(); | |
260 configurationAbsolutePath_ = boost::filesystem::absolute(configurationFile).string(); | |
261 } | |
262 } | |
263 else | |
264 { | |
265 #if ORTHANC_STANDALONE != 1 | |
266 // In a non-standalone build, we use the | |
267 // "Resources/Configuration.json" from the Orthanc source code | |
268 | |
269 boost::filesystem::path p = ORTHANC_PATH; | |
270 p /= "Resources"; | |
271 p /= "Configuration.json"; | |
272 configurationAbsolutePath_ = boost::filesystem::absolute(p).string(); | |
273 #endif | |
274 } | |
275 } | |
276 | |
277 | |
278 static void ValidateGlobalConfiguration() | |
279 { | |
280 std::set<std::string> ids; | |
281 | |
282 Configuration::GetListOfOrthancPeers(ids); | |
283 for (std::set<std::string>::const_iterator it = ids.begin(); it != ids.end(); ++it) | |
284 { | |
285 WebServiceParameters peer; | |
286 Configuration::GetOrthancPeer(peer, *it); | |
287 peer.CheckClientCertificate(); | |
288 } | |
289 | |
290 Configuration::GetListOfDicomModalities(ids); | |
291 for (std::set<std::string>::const_iterator it = ids.begin(); it != ids.end(); ++it) | |
292 { | |
293 RemoteModalityParameters modality; | |
294 Configuration::GetDicomModalityUsingSymbolicName(modality, *it); | |
295 } | |
296 } | |
297 | |
298 | |
299 static void RegisterUserMetadata() | |
300 { | |
301 if (configuration_.isMember("UserMetadata")) | |
302 { | |
303 const Json::Value& parameter = configuration_["UserMetadata"]; | |
304 | 62 |
305 Json::Value::Members members = parameter.getMemberNames(); | 63 Json::Value::Members members = parameter.getMemberNames(); |
306 for (size_t i = 0; i < members.size(); i++) | 64 for (size_t i = 0; i < members.size(); i++) |
307 { | 65 { |
308 const std::string& name = members[i]; | 66 const std::string& name = members[i]; |
330 } | 88 } |
331 } | 89 } |
332 } | 90 } |
333 | 91 |
334 | 92 |
335 static void RegisterUserContentType() | 93 static void RegisterUserContentType(const Json::Value& config) |
336 { | 94 { |
337 if (configuration_.isMember("UserContentType")) | 95 if (config.isMember("UserContentType")) |
338 { | 96 { |
339 const Json::Value& parameter = configuration_["UserContentType"]; | 97 const Json::Value& parameter = config["UserContentType"]; |
340 | 98 |
341 Json::Value::Members members = parameter.getMemberNames(); | 99 Json::Value::Members members = parameter.getMemberNames(); |
342 for (size_t i = 0; i < members.size(); i++) | 100 for (size_t i = 0; i < members.size(); i++) |
343 { | 101 { |
344 const std::string& name = members[i]; | 102 const std::string& name = members[i]; |
462 | 220 |
463 | 221 |
464 | 222 |
465 void OrthancInitialize(const char* configurationFile) | 223 void OrthancInitialize(const char* configurationFile) |
466 { | 224 { |
467 boost::recursive_mutex::scoped_lock lock(globalMutex_); | 225 OrthancConfiguration::WriterLock lock; |
468 | 226 |
469 Toolbox::InitializeOpenSsl(); | 227 Toolbox::InitializeOpenSsl(); |
470 | 228 |
471 InitializeServerEnumerations(); | 229 InitializeServerEnumerations(); |
472 | 230 |
473 // Read the user-provided configuration | 231 // Read the user-provided configuration |
474 ReadGlobalConfiguration(configurationFile); | 232 lock.GetConfiguration().Read(configurationFile); |
475 ValidateGlobalConfiguration(); | 233 |
476 | 234 if (lock.GetJson().isMember("Locale")) |
477 if (configuration_.isMember("Locale")) | 235 { |
478 { | 236 std::string locale = lock.GetConfiguration().GetStringParameter("Locale", ""); |
479 std::string locale = GetGlobalStringParameterInternal("Locale", ""); | 237 Toolbox::InitializeGlobalLocale(lock.GetJson()["Locale"].asCString()); |
480 Toolbox::InitializeGlobalLocale(configuration_["Locale"].asCString()); | |
481 } | 238 } |
482 else | 239 else |
483 { | 240 { |
484 Toolbox::InitializeGlobalLocale(NULL); | 241 Toolbox::InitializeGlobalLocale(NULL); |
485 } | 242 } |
486 | 243 |
487 if (configuration_.isMember("DefaultEncoding")) | 244 if (lock.GetJson().isMember("DefaultEncoding")) |
488 { | 245 { |
489 std::string encoding = GetGlobalStringParameterInternal("DefaultEncoding", ""); | 246 std::string encoding = lock.GetConfiguration().GetStringParameter("DefaultEncoding", ""); |
490 SetDefaultDicomEncoding(StringToEncoding(encoding.c_str())); | 247 SetDefaultDicomEncoding(StringToEncoding(encoding.c_str())); |
491 } | 248 } |
492 else | 249 else |
493 { | 250 { |
494 SetDefaultDicomEncoding(ORTHANC_DEFAULT_DICOM_ENCODING); | 251 SetDefaultDicomEncoding(ORTHANC_DEFAULT_DICOM_ENCODING); |
495 } | 252 } |
496 | 253 |
497 if (configuration_.isMember("Pkcs11")) | 254 if (lock.GetJson().isMember("Pkcs11")) |
498 { | 255 { |
499 ConfigurePkcs11(configuration_["Pkcs11"]); | 256 ConfigurePkcs11(lock.GetJson()["Pkcs11"]); |
500 } | 257 } |
501 | 258 |
502 HttpClient::GlobalInitialize(); | 259 HttpClient::GlobalInitialize(); |
503 | 260 |
504 RegisterUserMetadata(); | 261 RegisterUserMetadata(lock.GetJson()); |
505 RegisterUserContentType(); | 262 RegisterUserContentType(lock.GetJson()); |
506 | 263 |
507 FromDcmtkBridge::InitializeDictionary(GetGlobalBoolParameterInternal("LoadPrivateDictionary", true)); | 264 bool loadPrivate = lock.GetConfiguration().GetBooleanParameter("LoadPrivateDictionary", true); |
508 LoadCustomDictionary(configuration_); | 265 FromDcmtkBridge::InitializeDictionary(loadPrivate); |
266 LoadCustomDictionary(lock.GetJson()); | |
509 | 267 |
510 FromDcmtkBridge::InitializeCodecs(); | 268 FromDcmtkBridge::InitializeCodecs(); |
511 | 269 |
512 fontRegistry_.AddFromResource(EmbeddedResources::FONT_UBUNTU_MONO_BOLD_16); | 270 lock.GetConfiguration().RegisterFont(EmbeddedResources::FONT_UBUNTU_MONO_BOLD_16); |
513 | 271 |
514 /* Disable "gethostbyaddr" (which results in memory leaks) and use raw IP addresses */ | 272 /* Disable "gethostbyaddr" (which results in memory leaks) and use raw IP addresses */ |
515 dcmDisableGethostbyaddr.set(OFTrue); | 273 dcmDisableGethostbyaddr.set(OFTrue); |
516 } | 274 } |
517 | 275 |
518 | 276 |
519 | 277 |
520 void OrthancFinalize() | 278 void OrthancFinalize() |
521 { | 279 { |
522 boost::recursive_mutex::scoped_lock lock(globalMutex_); | 280 OrthancConfiguration::WriterLock lock; |
281 | |
523 HttpClient::GlobalFinalize(); | 282 HttpClient::GlobalFinalize(); |
524 FromDcmtkBridge::FinalizeCodecs(); | 283 FromDcmtkBridge::FinalizeCodecs(); |
525 Toolbox::FinalizeOpenSsl(); | 284 Toolbox::FinalizeOpenSsl(); |
526 Toolbox::FinalizeGlobalLocale(); | 285 Toolbox::FinalizeGlobalLocale(); |
527 } | 286 } |
528 | 287 |
529 | 288 |
530 std::string Configuration::GetGlobalStringParameter(const std::string& parameter, | |
531 const std::string& defaultValue) | |
532 { | |
533 boost::recursive_mutex::scoped_lock lock(globalMutex_); | |
534 return GetGlobalStringParameterInternal(parameter, defaultValue); | |
535 } | |
536 | |
537 | |
538 int Configuration::GetGlobalIntegerParameter(const std::string& parameter, | |
539 int defaultValue) | |
540 { | |
541 boost::recursive_mutex::scoped_lock lock(globalMutex_); | |
542 | |
543 if (configuration_.isMember(parameter)) | |
544 { | |
545 if (configuration_[parameter].type() != Json::intValue) | |
546 { | |
547 LOG(ERROR) << "The configuration option \"" << parameter << "\" must be an integer"; | |
548 throw OrthancException(ErrorCode_BadParameterType); | |
549 } | |
550 else | |
551 { | |
552 return configuration_[parameter].asInt(); | |
553 } | |
554 } | |
555 else | |
556 { | |
557 return defaultValue; | |
558 } | |
559 } | |
560 | |
561 | |
562 unsigned int Configuration::GetGlobalUnsignedIntegerParameter(const std::string& parameter, | |
563 unsigned int defaultValue) | |
564 { | |
565 int v = GetGlobalIntegerParameter(parameter, defaultValue); | |
566 | |
567 if (v < 0) | |
568 { | |
569 LOG(ERROR) << "The configuration option \"" << parameter << "\" must be a positive integer"; | |
570 throw OrthancException(ErrorCode_ParameterOutOfRange); | |
571 } | |
572 else | |
573 { | |
574 return static_cast<unsigned int>(v); | |
575 } | |
576 } | |
577 | |
578 | |
579 bool Configuration::GetGlobalBoolParameter(const std::string& parameter, | |
580 bool defaultValue) | |
581 { | |
582 boost::recursive_mutex::scoped_lock lock(globalMutex_); | |
583 return GetGlobalBoolParameterInternal(parameter, defaultValue); | |
584 } | |
585 | |
586 | |
587 void Configuration::GetDicomModalityUsingSymbolicName(RemoteModalityParameters& modality, | |
588 const std::string& name) | |
589 { | |
590 boost::recursive_mutex::scoped_lock lock(globalMutex_); | |
591 | |
592 if (!configuration_.isMember("DicomModalities")) | |
593 { | |
594 LOG(ERROR) << "No modality with symbolic name: " << name; | |
595 throw OrthancException(ErrorCode_InexistentItem); | |
596 } | |
597 | |
598 const Json::Value& modalities = configuration_["DicomModalities"]; | |
599 if (modalities.type() != Json::objectValue || | |
600 !modalities.isMember(name)) | |
601 { | |
602 LOG(ERROR) << "No modality with symbolic name: " << name; | |
603 throw OrthancException(ErrorCode_InexistentItem); | |
604 } | |
605 | |
606 try | |
607 { | |
608 modality.Unserialize(modalities[name]); | |
609 } | |
610 catch (OrthancException&) | |
611 { | |
612 LOG(ERROR) << "Syntax error in the definition of DICOM modality \"" << name | |
613 << "\". Please check your configuration file."; | |
614 throw; | |
615 } | |
616 } | |
617 | |
618 | |
619 bool Configuration::GetOrthancPeer(WebServiceParameters& peer, | |
620 const std::string& name) | |
621 { | |
622 boost::recursive_mutex::scoped_lock lock(globalMutex_); | |
623 | |
624 if (!configuration_.isMember("OrthancPeers")) | |
625 { | |
626 return false; | |
627 } | |
628 | |
629 try | |
630 { | |
631 const Json::Value& modalities = configuration_["OrthancPeers"]; | |
632 if (modalities.type() != Json::objectValue || | |
633 !modalities.isMember(name)) | |
634 { | |
635 return false; | |
636 } | |
637 else | |
638 { | |
639 peer.Unserialize(modalities[name]); | |
640 return true; | |
641 } | |
642 } | |
643 catch (OrthancException&) | |
644 { | |
645 LOG(ERROR) << "Syntax error in the definition of peer \"" << name | |
646 << "\". Please check your configuration file."; | |
647 throw; | |
648 } | |
649 } | |
650 | |
651 | |
652 static bool ReadKeys(std::set<std::string>& target, | |
653 const char* parameter, | |
654 bool onlyAlphanumeric) | |
655 { | |
656 boost::recursive_mutex::scoped_lock lock(globalMutex_); | |
657 | |
658 target.clear(); | |
659 | |
660 if (!configuration_.isMember(parameter)) | |
661 { | |
662 return true; | |
663 } | |
664 | |
665 const Json::Value& modalities = configuration_[parameter]; | |
666 if (modalities.type() != Json::objectValue) | |
667 { | |
668 LOG(ERROR) << "Bad format of the \"DicomModalities\" configuration section"; | |
669 throw OrthancException(ErrorCode_BadFileFormat); | |
670 } | |
671 | |
672 Json::Value::Members members = modalities.getMemberNames(); | |
673 for (size_t i = 0; i < members.size(); i++) | |
674 { | |
675 if (onlyAlphanumeric) | |
676 { | |
677 for (size_t j = 0; j < members[i].size(); j++) | |
678 { | |
679 if (!isalnum(members[i][j]) && members[i][j] != '-') | |
680 { | |
681 return false; | |
682 } | |
683 } | |
684 } | |
685 | |
686 target.insert(members[i]); | |
687 } | |
688 | |
689 return true; | |
690 } | |
691 | |
692 | |
693 void Configuration::GetListOfDicomModalities(std::set<std::string>& target) | |
694 { | |
695 target.clear(); | |
696 | |
697 if (!ReadKeys(target, "DicomModalities", true)) | |
698 { | |
699 LOG(ERROR) << "Only alphanumeric and dash characters are allowed in the names of the modalities"; | |
700 throw OrthancException(ErrorCode_BadFileFormat); | |
701 } | |
702 } | |
703 | |
704 | |
705 void Configuration::GetListOfOrthancPeers(std::set<std::string>& target) | |
706 { | |
707 target.clear(); | |
708 | |
709 if (!ReadKeys(target, "OrthancPeers", true)) | |
710 { | |
711 LOG(ERROR) << "Only alphanumeric and dash characters are allowed in the names of Orthanc peers"; | |
712 throw OrthancException(ErrorCode_BadFileFormat); | |
713 } | |
714 } | |
715 | |
716 | |
717 | |
718 void Configuration::SetupRegisteredUsers(MongooseServer& httpServer) | |
719 { | |
720 boost::recursive_mutex::scoped_lock lock(globalMutex_); | |
721 | |
722 httpServer.ClearUsers(); | |
723 | |
724 if (!configuration_.isMember("RegisteredUsers")) | |
725 { | |
726 return; | |
727 } | |
728 | |
729 const Json::Value& users = configuration_["RegisteredUsers"]; | |
730 if (users.type() != Json::objectValue) | |
731 { | |
732 LOG(ERROR) << "Badly formatted list of users"; | |
733 throw OrthancException(ErrorCode_BadFileFormat); | |
734 } | |
735 | |
736 Json::Value::Members usernames = users.getMemberNames(); | |
737 for (size_t i = 0; i < usernames.size(); i++) | |
738 { | |
739 const std::string& username = usernames[i]; | |
740 std::string password = users[username].asString(); | |
741 httpServer.RegisterUser(username.c_str(), password.c_str()); | |
742 } | |
743 } | |
744 | |
745 | |
746 std::string Configuration::InterpretStringParameterAsPath(const std::string& parameter) | |
747 { | |
748 boost::recursive_mutex::scoped_lock lock(globalMutex_); | |
749 return SystemToolbox::InterpretRelativePath(defaultDirectory_.string(), parameter); | |
750 } | |
751 | |
752 | |
753 void Configuration::GetGlobalListOfStringsParameter(std::list<std::string>& target, | |
754 const std::string& key) | |
755 { | |
756 boost::recursive_mutex::scoped_lock lock(globalMutex_); | |
757 | |
758 target.clear(); | |
759 | |
760 if (!configuration_.isMember(key)) | |
761 { | |
762 return; | |
763 } | |
764 | |
765 const Json::Value& lst = configuration_[key]; | |
766 | |
767 if (lst.type() != Json::arrayValue) | |
768 { | |
769 LOG(ERROR) << "Badly formatted list of strings"; | |
770 throw OrthancException(ErrorCode_BadFileFormat); | |
771 } | |
772 | |
773 for (Json::Value::ArrayIndex i = 0; i < lst.size(); i++) | |
774 { | |
775 target.push_back(lst[i].asString()); | |
776 } | |
777 } | |
778 | |
779 | |
780 bool Configuration::IsSameAETitle(const std::string& aet1, | |
781 const std::string& aet2) | |
782 { | |
783 if (GetGlobalBoolParameter("StrictAetComparison", false)) | |
784 { | |
785 // Case-sensitive matching | |
786 return aet1 == aet2; | |
787 } | |
788 else | |
789 { | |
790 // Case-insensitive matching (default) | |
791 std::string tmp1, tmp2; | |
792 Toolbox::ToLowerCase(tmp1, aet1); | |
793 Toolbox::ToLowerCase(tmp2, aet2); | |
794 return tmp1 == tmp2; | |
795 } | |
796 } | |
797 | |
798 | |
799 bool Configuration::LookupDicomModalityUsingAETitle(RemoteModalityParameters& modality, | |
800 const std::string& aet) | |
801 { | |
802 std::set<std::string> modalities; | |
803 GetListOfDicomModalities(modalities); | |
804 | |
805 for (std::set<std::string>::const_iterator | |
806 it = modalities.begin(); it != modalities.end(); ++it) | |
807 { | |
808 try | |
809 { | |
810 GetDicomModalityUsingSymbolicName(modality, *it); | |
811 | |
812 if (IsSameAETitle(aet, modality.GetApplicationEntityTitle())) | |
813 { | |
814 return true; | |
815 } | |
816 } | |
817 catch (OrthancException&) | |
818 { | |
819 } | |
820 } | |
821 | |
822 return false; | |
823 } | |
824 | |
825 | |
826 bool Configuration::IsKnownAETitle(const std::string& aet, | |
827 const std::string& ip) | |
828 { | |
829 RemoteModalityParameters modality; | |
830 | |
831 if (!LookupDicomModalityUsingAETitle(modality, aet)) | |
832 { | |
833 LOG(WARNING) << "Modality \"" << aet | |
834 << "\" is not listed in the \"DicomModalities\" configuration option"; | |
835 return false; | |
836 } | |
837 else if (!Configuration::GetGlobalBoolParameter("DicomCheckModalityHost", false) || | |
838 ip == modality.GetHost()) | |
839 { | |
840 return true; | |
841 } | |
842 else | |
843 { | |
844 LOG(WARNING) << "Forbidding access from AET \"" << aet | |
845 << "\" given its hostname (" << ip << ") does not match " | |
846 << "the \"DicomModalities\" configuration option (" | |
847 << modality.GetHost() << " was expected)"; | |
848 return false; | |
849 } | |
850 } | |
851 | |
852 | |
853 RemoteModalityParameters Configuration::GetModalityUsingSymbolicName(const std::string& name) | |
854 { | |
855 RemoteModalityParameters modality; | |
856 GetDicomModalityUsingSymbolicName(modality, name); | |
857 | |
858 return modality; | |
859 } | |
860 | |
861 | |
862 RemoteModalityParameters Configuration::GetModalityUsingAet(const std::string& aet) | |
863 { | |
864 RemoteModalityParameters modality; | |
865 | |
866 if (LookupDicomModalityUsingAETitle(modality, aet)) | |
867 { | |
868 return modality; | |
869 } | |
870 else | |
871 { | |
872 LOG(ERROR) << "Unknown modality for AET: " << aet; | |
873 throw OrthancException(ErrorCode_InexistentItem); | |
874 } | |
875 } | |
876 | |
877 | |
878 void Configuration::UpdateModality(ServerContext& context, | |
879 const std::string& symbolicName, | |
880 const RemoteModalityParameters& modality) | |
881 { | |
882 { | |
883 boost::recursive_mutex::scoped_lock lock(globalMutex_); | |
884 | |
885 if (!configuration_.isMember("DicomModalities")) | |
886 { | |
887 configuration_["DicomModalities"] = Json::objectValue; | |
888 } | |
889 | |
890 Json::Value& modalities = configuration_["DicomModalities"]; | |
891 if (modalities.type() != Json::objectValue) | |
892 { | |
893 LOG(ERROR) << "Bad file format for modality: " << symbolicName; | |
894 throw OrthancException(ErrorCode_BadFileFormat); | |
895 } | |
896 | |
897 modalities.removeMember(symbolicName); | |
898 | |
899 Json::Value v; | |
900 modality.Serialize(v, true /* force advanced format */); | |
901 modalities[symbolicName] = v; | |
902 } | |
903 | |
904 #if ORTHANC_ENABLE_PLUGINS == 1 | |
905 if (context.HasPlugins()) | |
906 { | |
907 context.GetPlugins().SignalUpdatedModalities(); | |
908 } | |
909 #endif | |
910 } | |
911 | |
912 | |
913 void Configuration::RemoveModality(ServerContext& context, | |
914 const std::string& symbolicName) | |
915 { | |
916 { | |
917 boost::recursive_mutex::scoped_lock lock(globalMutex_); | |
918 | |
919 if (!configuration_.isMember("DicomModalities")) | |
920 { | |
921 LOG(ERROR) << "No modality with symbolic name: " << symbolicName; | |
922 throw OrthancException(ErrorCode_BadFileFormat); | |
923 } | |
924 | |
925 Json::Value& modalities = configuration_["DicomModalities"]; | |
926 if (modalities.type() != Json::objectValue) | |
927 { | |
928 LOG(ERROR) << "Bad file format for the \"DicomModalities\" configuration section"; | |
929 throw OrthancException(ErrorCode_BadFileFormat); | |
930 } | |
931 | |
932 modalities.removeMember(symbolicName.c_str()); | |
933 } | |
934 | |
935 #if ORTHANC_ENABLE_PLUGINS == 1 | |
936 if (context.HasPlugins()) | |
937 { | |
938 context.GetPlugins().SignalUpdatedModalities(); | |
939 } | |
940 #endif | |
941 } | |
942 | |
943 | |
944 void Configuration::UpdatePeer(ServerContext& context, | |
945 const std::string& symbolicName, | |
946 const WebServiceParameters& peer) | |
947 { | |
948 peer.CheckClientCertificate(); | |
949 | |
950 { | |
951 boost::recursive_mutex::scoped_lock lock(globalMutex_); | |
952 | |
953 if (!configuration_.isMember("OrthancPeers")) | |
954 { | |
955 LOG(ERROR) << "No peer with symbolic name: " << symbolicName; | |
956 configuration_["OrthancPeers"] = Json::objectValue; | |
957 } | |
958 | |
959 Json::Value& peers = configuration_["OrthancPeers"]; | |
960 if (peers.type() != Json::objectValue) | |
961 { | |
962 LOG(ERROR) << "Bad file format for the \"OrthancPeers\" configuration section"; | |
963 throw OrthancException(ErrorCode_BadFileFormat); | |
964 } | |
965 | |
966 peers.removeMember(symbolicName); | |
967 | |
968 Json::Value v; | |
969 peer.Serialize(v, | |
970 false /* use simple format if possible */, | |
971 true /* include passwords */); | |
972 peers[symbolicName] = v; | |
973 } | |
974 | |
975 #if ORTHANC_ENABLE_PLUGINS == 1 | |
976 if (context.HasPlugins()) | |
977 { | |
978 context.GetPlugins().SignalUpdatedPeers(); | |
979 } | |
980 #endif | |
981 } | |
982 | |
983 | |
984 void Configuration::RemovePeer(ServerContext& context, | |
985 const std::string& symbolicName) | |
986 { | |
987 { | |
988 boost::recursive_mutex::scoped_lock lock(globalMutex_); | |
989 | |
990 if (!configuration_.isMember("OrthancPeers")) | |
991 { | |
992 LOG(ERROR) << "No peer with symbolic name: " << symbolicName; | |
993 throw OrthancException(ErrorCode_BadFileFormat); | |
994 } | |
995 | |
996 Json::Value& peers = configuration_["OrthancPeers"]; | |
997 if (peers.type() != Json::objectValue) | |
998 { | |
999 LOG(ERROR) << "Bad file format for the \"OrthancPeers\" configuration section"; | |
1000 throw OrthancException(ErrorCode_BadFileFormat); | |
1001 } | |
1002 | |
1003 peers.removeMember(symbolicName.c_str()); | |
1004 } | |
1005 | |
1006 #if ORTHANC_ENABLE_PLUGINS == 1 | |
1007 if (context.HasPlugins()) | |
1008 { | |
1009 context.GetPlugins().SignalUpdatedPeers(); | |
1010 } | |
1011 #endif | |
1012 } | |
1013 | |
1014 | |
1015 | |
1016 const std::string& Configuration::GetConfigurationAbsolutePath() | |
1017 { | |
1018 return configurationAbsolutePath_; | |
1019 } | |
1020 | |
1021 | |
1022 static IDatabaseWrapper* CreateSQLiteWrapper() | 289 static IDatabaseWrapper* CreateSQLiteWrapper() |
1023 { | 290 { |
1024 std::string storageDirectoryStr = Configuration::GetGlobalStringParameter("StorageDirectory", "OrthancStorage"); | 291 OrthancConfiguration::ReaderLock lock; |
292 | |
293 std::string storageDirectoryStr = | |
294 lock.GetConfiguration().GetStringParameter("StorageDirectory", "OrthancStorage"); | |
1025 | 295 |
1026 // Open the database | 296 // Open the database |
1027 boost::filesystem::path indexDirectory = Configuration::InterpretStringParameterAsPath( | 297 boost::filesystem::path indexDirectory = lock.GetConfiguration().InterpretStringParameterAsPath( |
1028 Configuration::GetGlobalStringParameter("IndexDirectory", storageDirectoryStr)); | 298 lock.GetConfiguration().GetStringParameter("IndexDirectory", storageDirectoryStr)); |
1029 | 299 |
1030 LOG(WARNING) << "SQLite index directory: " << indexDirectory; | 300 LOG(WARNING) << "SQLite index directory: " << indexDirectory; |
1031 | 301 |
1032 try | 302 try |
1033 { | 303 { |
1092 } | 362 } |
1093 | 363 |
1094 | 364 |
1095 static IStorageArea* CreateFilesystemStorage() | 365 static IStorageArea* CreateFilesystemStorage() |
1096 { | 366 { |
1097 std::string storageDirectoryStr = Configuration::GetGlobalStringParameter("StorageDirectory", "OrthancStorage"); | 367 OrthancConfiguration::ReaderLock lock; |
1098 | 368 |
1099 boost::filesystem::path storageDirectory = Configuration::InterpretStringParameterAsPath(storageDirectoryStr); | 369 std::string storageDirectoryStr = |
370 lock.GetConfiguration().GetStringParameter("StorageDirectory", "OrthancStorage"); | |
371 | |
372 boost::filesystem::path storageDirectory = | |
373 lock.GetConfiguration().InterpretStringParameterAsPath(storageDirectoryStr); | |
374 | |
1100 LOG(WARNING) << "Storage directory: " << storageDirectory; | 375 LOG(WARNING) << "Storage directory: " << storageDirectory; |
1101 | 376 |
1102 if (Configuration::GetGlobalBoolParameter("StoreDicom", true)) | 377 if (lock.GetConfiguration().GetBooleanParameter("StoreDicom", true)) |
1103 { | 378 { |
1104 return new FilesystemStorage(storageDirectory.string()); | 379 return new FilesystemStorage(storageDirectory.string()); |
1105 } | 380 } |
1106 else | 381 else |
1107 { | 382 { |
1109 return new FilesystemStorageWithoutDicom(storageDirectory.string()); | 384 return new FilesystemStorageWithoutDicom(storageDirectory.string()); |
1110 } | 385 } |
1111 } | 386 } |
1112 | 387 |
1113 | 388 |
1114 IDatabaseWrapper* Configuration::CreateDatabaseWrapper() | 389 IDatabaseWrapper* CreateDatabaseWrapper() |
1115 { | 390 { |
1116 return CreateSQLiteWrapper(); | 391 return CreateSQLiteWrapper(); |
1117 } | 392 } |
1118 | 393 |
1119 | 394 |
1120 IStorageArea* Configuration::CreateStorageArea() | 395 IStorageArea* CreateStorageArea() |
1121 { | 396 { |
1122 return CreateFilesystemStorage(); | 397 return CreateFilesystemStorage(); |
1123 } | 398 } |
1124 | |
1125 | |
1126 void Configuration::GetConfiguration(Json::Value& result) | |
1127 { | |
1128 boost::recursive_mutex::scoped_lock lock(globalMutex_); | |
1129 result = configuration_; | |
1130 } | |
1131 | |
1132 | |
1133 void Configuration::FormatConfiguration(std::string& result) | |
1134 { | |
1135 Json::Value config; | |
1136 GetConfiguration(config); | |
1137 | |
1138 Json::StyledWriter w; | |
1139 result = w.write(config); | |
1140 } | |
1141 | |
1142 | |
1143 const FontRegistry& Configuration::GetFontRegistry() | |
1144 { | |
1145 return fontRegistry_; | |
1146 } | |
1147 | |
1148 | |
1149 void Configuration::SetDefaultEncoding(Encoding encoding) | |
1150 { | |
1151 SetDefaultDicomEncoding(encoding); | |
1152 | |
1153 { | |
1154 // Propagate the encoding to the configuration file that is | |
1155 // stored in memory | |
1156 boost::recursive_mutex::scoped_lock lock(globalMutex_); | |
1157 configuration_["DefaultEncoding"] = EnumerationToString(encoding); | |
1158 } | |
1159 } | |
1160 | |
1161 | |
1162 bool Configuration::HasConfigurationChanged() | |
1163 { | |
1164 Json::Value starting; | |
1165 GetConfiguration(starting); | |
1166 | |
1167 Json::Value current; | |
1168 ReadConfiguration(current, configurationFileArg_); | |
1169 | |
1170 Json::FastWriter writer; | |
1171 std::string a = writer.write(starting); | |
1172 std::string b = writer.write(current); | |
1173 | |
1174 return a != b; | |
1175 } | |
1176 } | 399 } |