# HG changeset patch # User am@osimis.io # Date 1543412237 -3600 # Node ID d658f5785b7b82d6d7b3ea4c1f084f7b5775d3e1 # Parent 1ab59dfd4afca4f4ba7848586b1e4fbfdc542251# Parent 2dfa40b9ca42b17c607c6c6f93f09ff8963d3d93 merge diff -r 1ab59dfd4afc -r d658f5785b7b CMakeLists.txt --- a/CMakeLists.txt Wed Nov 28 14:36:33 2018 +0100 +++ b/CMakeLists.txt Wed Nov 28 14:37:17 2018 +0100 @@ -58,6 +58,7 @@ OrthancServer/DicomInstanceToStore.cpp OrthancServer/ExportedResource.cpp OrthancServer/LuaScripting.cpp + OrthancServer/OrthancConfiguration.cpp OrthancServer/OrthancFindRequestHandler.cpp OrthancServer/OrthancHttpHandler.cpp OrthancServer/OrthancInitialization.cpp diff -r 1ab59dfd4afc -r d658f5785b7b Core/SystemToolbox.cpp --- a/Core/SystemToolbox.cpp Wed Nov 28 14:36:33 2018 +0100 +++ b/Core/SystemToolbox.cpp Wed Nov 28 14:37:17 2018 +0100 @@ -710,4 +710,31 @@ } } } + + + std::string SystemToolbox::InterpretRelativePath(const std::string& baseDirectory, + const std::string& relativePath) + { + boost::filesystem::path base(baseDirectory); + boost::filesystem::path relative(relativePath); + + /** + The following lines should be equivalent to this one: + + return (base / relative).string(); + + However, for some unknown reason, some versions of Boost do not + make the proper path resolution when "baseDirectory" is an + absolute path. So, a hack is used below. + **/ + + if (relative.is_absolute()) + { + return relative.string(); + } + else + { + return (base / relative).string(); + } + } } diff -r 1ab59dfd4afc -r d658f5785b7b Core/SystemToolbox.h --- a/Core/SystemToolbox.h Wed Nov 28 14:36:33 2018 +0100 +++ b/Core/SystemToolbox.h Wed Nov 28 14:37:17 2018 +0100 @@ -105,5 +105,8 @@ MimeType AutodetectMimeType(const std::string& path); void GetEnvironmentVariables(std::map& env); + + std::string InterpretRelativePath(const std::string& baseDirectory, + const std::string& relativePath); } } diff -r 1ab59dfd4afc -r d658f5785b7b OrthancServer/OrthancConfiguration.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancServer/OrthancConfiguration.cpp Wed Nov 28 14:37:17 2018 +0100 @@ -0,0 +1,721 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2018 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#include "PrecompiledHeadersServer.h" +#include "OrthancConfiguration.h" + +#include "../Core/HttpServer/MongooseServer.h" +#include "../Core/Logging.h" +#include "../Core/OrthancException.h" +#include "../Core/SystemToolbox.h" +#include "../Core/Toolbox.h" + +namespace Orthanc +{ + static void AddFileToConfiguration(Json::Value& target, + const boost::filesystem::path& path) + { + std::map env; + SystemToolbox::GetEnvironmentVariables(env); + + LOG(WARNING) << "Reading the configuration from: " << path; + + Json::Value config; + + { + std::string content; + SystemToolbox::ReadFile(content, path.string()); + + content = Toolbox::SubstituteVariables(content, env); + + Json::Value tmp; + Json::Reader reader; + if (!reader.parse(content, tmp) || + tmp.type() != Json::objectValue) + { + LOG(ERROR) << "The configuration file does not follow the JSON syntax: " << path; + throw OrthancException(ErrorCode_BadJson); + } + + Toolbox::CopyJsonWithoutComments(config, tmp); + } + + if (target.size() == 0) + { + target = config; + } + else + { + // Merge the newly-added file with the previous content of "target" + Json::Value::Members members = config.getMemberNames(); + for (Json::Value::ArrayIndex i = 0; i < members.size(); i++) + { + if (target.isMember(members[i])) + { + LOG(ERROR) << "The configuration section \"" << members[i] << "\" is defined in 2 different configuration files"; + throw OrthancException(ErrorCode_BadFileFormat); + } + else + { + target[members[i]] = config[members[i]]; + } + } + } + } + + + static void ScanFolderForConfiguration(Json::Value& target, + const char* folder) + { + using namespace boost::filesystem; + + LOG(WARNING) << "Scanning folder \"" << folder << "\" for configuration files"; + + directory_iterator end_it; // default construction yields past-the-end + for (directory_iterator it(folder); + it != end_it; + ++it) + { + if (!is_directory(it->status())) + { + std::string extension = boost::filesystem::extension(it->path()); + Toolbox::ToLowerCase(extension); + + if (extension == ".json") + { + AddFileToConfiguration(target, it->path().string()); + } + } + } + } + + + static void ReadConfiguration(Json::Value& target, + const char* configurationFile) + { + target = Json::objectValue; + + if (configurationFile) + { + if (!boost::filesystem::exists(configurationFile)) + { + LOG(ERROR) << "Inexistent path to configuration: " << configurationFile; + throw OrthancException(ErrorCode_InexistentFile); + } + + if (boost::filesystem::is_directory(configurationFile)) + { + ScanFolderForConfiguration(target, configurationFile); + } + else + { + AddFileToConfiguration(target, configurationFile); + } + } + else + { +#if ORTHANC_STANDALONE == 1 + // No default path for the standalone configuration + LOG(WARNING) << "Using the default Orthanc configuration"; + return; + +#else + // In a non-standalone build, we use the + // "Resources/Configuration.json" from the Orthanc source code + + boost::filesystem::path p = ORTHANC_PATH; + p /= "Resources"; + p /= "Configuration.json"; + + AddFileToConfiguration(target, p); +#endif + } + } + + + void OrthancConfiguration::ValidateConfiguration() const + { + std::set ids; + + GetListOfOrthancPeers(ids); + for (std::set::const_iterator it = ids.begin(); it != ids.end(); ++it) + { + WebServiceParameters peer; + GetOrthancPeer(peer, *it); + peer.CheckClientCertificate(); + } + + GetListOfDicomModalities(ids); + for (std::set::const_iterator it = ids.begin(); it != ids.end(); ++it) + { + RemoteModalityParameters modality; + GetDicomModalityUsingSymbolicName(modality, *it); + } + } + + + OrthancConfiguration& OrthancConfiguration::GetInstance() + { + static OrthancConfiguration configuration; + return configuration; + } + + + std::string OrthancConfiguration::GetStringParameter(const std::string& parameter, + const std::string& defaultValue) const + { + if (json_.isMember(parameter)) + { + if (json_[parameter].type() != Json::stringValue) + { + LOG(ERROR) << "The configuration option \"" << parameter << "\" must be a string"; + throw OrthancException(ErrorCode_BadParameterType); + } + else + { + return json_[parameter].asString(); + } + } + else + { + return defaultValue; + } + } + + + int OrthancConfiguration::GetIntegerParameter(const std::string& parameter, + int defaultValue) const + { + if (json_.isMember(parameter)) + { + if (json_[parameter].type() != Json::intValue) + { + LOG(ERROR) << "The configuration option \"" << parameter << "\" must be an integer"; + throw OrthancException(ErrorCode_BadParameterType); + } + else + { + return json_[parameter].asInt(); + } + } + else + { + return defaultValue; + } + } + + + unsigned int OrthancConfiguration::GetUnsignedIntegerParameter( + const std::string& parameter, + unsigned int defaultValue) const + { + int v = GetIntegerParameter(parameter, defaultValue); + + if (v < 0) + { + LOG(ERROR) << "The configuration option \"" << parameter << "\" must be a positive integer"; + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + else + { + return static_cast(v); + } + } + + + bool OrthancConfiguration::GetBooleanParameter(const std::string& parameter, + bool defaultValue) const + { + if (json_.isMember(parameter)) + { + if (json_[parameter].type() != Json::booleanValue) + { + LOG(ERROR) << "The configuration option \"" << parameter + << "\" must be a Boolean (true or false)"; + throw OrthancException(ErrorCode_BadParameterType); + } + else + { + return json_[parameter].asBool(); + } + } + else + { + return defaultValue; + } + } + + + void OrthancConfiguration::Read(const char* configurationFile) + { + // Read the content of the configuration + configurationFileArg_ = configurationFile; + ReadConfiguration(json_, configurationFile); + + // Adapt the paths to the configurations + defaultDirectory_ = boost::filesystem::current_path(); + configurationAbsolutePath_ = ""; + + if (configurationFile) + { + if (boost::filesystem::is_directory(configurationFile)) + { + defaultDirectory_ = boost::filesystem::path(configurationFile); + configurationAbsolutePath_ = boost::filesystem::absolute(configurationFile).parent_path().string(); + } + else + { + defaultDirectory_ = boost::filesystem::path(configurationFile).parent_path(); + configurationAbsolutePath_ = boost::filesystem::absolute(configurationFile).string(); + } + } + else + { +#if ORTHANC_STANDALONE != 1 + // In a non-standalone build, we use the + // "Resources/Configuration.json" from the Orthanc source code + + boost::filesystem::path p = ORTHANC_PATH; + p /= "Resources"; + p /= "Configuration.json"; + configurationAbsolutePath_ = boost::filesystem::absolute(p).string(); +#endif + } + + ValidateConfiguration(); + } + + + void OrthancConfiguration::GetDicomModalityUsingSymbolicName( + RemoteModalityParameters& modality, + const std::string& name) const + { + if (!json_.isMember("DicomModalities")) + { + LOG(ERROR) << "No modality with symbolic name: " << name; + throw OrthancException(ErrorCode_InexistentItem); + } + + const Json::Value& modalities = json_["DicomModalities"]; + if (modalities.type() != Json::objectValue || + !modalities.isMember(name)) + { + LOG(ERROR) << "No modality with symbolic name: " << name; + throw OrthancException(ErrorCode_InexistentItem); + } + + try + { + modality.Unserialize(modalities[name]); + } + catch (OrthancException&) + { + LOG(ERROR) << "Syntax error in the definition of DICOM modality \"" << name + << "\". Please check your configuration file."; + throw; + } + } + + + bool OrthancConfiguration::GetOrthancPeer(WebServiceParameters& peer, + const std::string& name) const + { + if (!json_.isMember("OrthancPeers")) + { + return false; + } + + try + { + const Json::Value& modalities = json_["OrthancPeers"]; + if (modalities.type() != Json::objectValue || + !modalities.isMember(name)) + { + return false; + } + else + { + peer.Unserialize(modalities[name]); + return true; + } + } + catch (OrthancException&) + { + LOG(ERROR) << "Syntax error in the definition of peer \"" << name + << "\". Please check your configuration file."; + throw; + } + } + + + bool OrthancConfiguration::ReadKeys(std::set& target, + const char* parameter, + bool onlyAlphanumeric) const + { + target.clear(); + + if (!json_.isMember(parameter)) + { + return true; + } + + const Json::Value& modalities = json_[parameter]; + if (modalities.type() != Json::objectValue) + { + LOG(ERROR) << "Bad format of the \"DicomModalities\" configuration section"; + throw OrthancException(ErrorCode_BadFileFormat); + } + + Json::Value::Members members = modalities.getMemberNames(); + for (size_t i = 0; i < members.size(); i++) + { + if (onlyAlphanumeric) + { + for (size_t j = 0; j < members[i].size(); j++) + { + if (!isalnum(members[i][j]) && members[i][j] != '-') + { + return false; + } + } + } + + target.insert(members[i]); + } + + return true; + } + + + void OrthancConfiguration::GetListOfDicomModalities(std::set& target) const + { + target.clear(); + + if (!ReadKeys(target, "DicomModalities", true)) + { + LOG(ERROR) << "Only alphanumeric and dash characters are allowed in the names of the modalities"; + throw OrthancException(ErrorCode_BadFileFormat); + } + } + + + void OrthancConfiguration::GetListOfOrthancPeers(std::set& target) const + { + target.clear(); + + if (!ReadKeys(target, "OrthancPeers", true)) + { + LOG(ERROR) << "Only alphanumeric and dash characters are allowed in the names of Orthanc peers"; + throw OrthancException(ErrorCode_BadFileFormat); + } + } + + + void OrthancConfiguration::SetupRegisteredUsers(MongooseServer& httpServer) const + { + httpServer.ClearUsers(); + + if (!json_.isMember("RegisteredUsers")) + { + return; + } + + const Json::Value& users = json_["RegisteredUsers"]; + if (users.type() != Json::objectValue) + { + LOG(ERROR) << "Badly formatted list of users"; + throw OrthancException(ErrorCode_BadFileFormat); + } + + Json::Value::Members usernames = users.getMemberNames(); + for (size_t i = 0; i < usernames.size(); i++) + { + const std::string& username = usernames[i]; + std::string password = users[username].asString(); + httpServer.RegisterUser(username.c_str(), password.c_str()); + } + } + + + std::string OrthancConfiguration::InterpretStringParameterAsPath( + const std::string& parameter) const + { + return SystemToolbox::InterpretRelativePath(defaultDirectory_.string(), parameter); + } + + + void OrthancConfiguration::GetGlobalListOfStringsParameter( + std::list& target, + const std::string& key) const + { + target.clear(); + + if (!json_.isMember(key)) + { + return; + } + + const Json::Value& lst = json_[key]; + + if (lst.type() != Json::arrayValue) + { + LOG(ERROR) << "Badly formatted list of strings"; + throw OrthancException(ErrorCode_BadFileFormat); + } + + for (Json::Value::ArrayIndex i = 0; i < lst.size(); i++) + { + target.push_back(lst[i].asString()); + } + } + + + bool OrthancConfiguration::IsSameAETitle(const std::string& aet1, + const std::string& aet2) const + { + if (GetBooleanParameter("StrictAetComparison", false)) + { + // Case-sensitive matching + return aet1 == aet2; + } + else + { + // Case-insensitive matching (default) + std::string tmp1, tmp2; + Toolbox::ToLowerCase(tmp1, aet1); + Toolbox::ToLowerCase(tmp2, aet2); + return tmp1 == tmp2; + } + } + + + bool OrthancConfiguration::LookupDicomModalityUsingAETitle( + RemoteModalityParameters& modality, + const std::string& aet) const + { + std::set modalities; + GetListOfDicomModalities(modalities); + + for (std::set::const_iterator + it = modalities.begin(); it != modalities.end(); ++it) + { + try + { + GetDicomModalityUsingSymbolicName(modality, *it); + + if (IsSameAETitle(aet, modality.GetApplicationEntityTitle())) + { + return true; + } + } + catch (OrthancException&) + { + } + } + + return false; + } + + + bool OrthancConfiguration::IsKnownAETitle(const std::string& aet, + const std::string& ip) const + { + RemoteModalityParameters modality; + + if (!LookupDicomModalityUsingAETitle(modality, aet)) + { + LOG(WARNING) << "Modality \"" << aet + << "\" is not listed in the \"DicomModalities\" configuration option"; + return false; + } + else if (!GetBooleanParameter("DicomCheckModalityHost", false) || + ip == modality.GetHost()) + { + return true; + } + else + { + LOG(WARNING) << "Forbidding access from AET \"" << aet + << "\" given its hostname (" << ip << ") does not match " + << "the \"DicomModalities\" configuration option (" + << modality.GetHost() << " was expected)"; + return false; + } + } + + + RemoteModalityParameters OrthancConfiguration::GetModalityUsingSymbolicName( + const std::string& name) const + { + RemoteModalityParameters modality; + GetDicomModalityUsingSymbolicName(modality, name); + + return modality; + } + + + RemoteModalityParameters OrthancConfiguration::GetModalityUsingAet( + const std::string& aet) const + { + RemoteModalityParameters modality; + + if (LookupDicomModalityUsingAETitle(modality, aet)) + { + return modality; + } + else + { + LOG(ERROR) << "Unknown modality for AET: " << aet; + throw OrthancException(ErrorCode_InexistentItem); + } + } + + + void OrthancConfiguration::UpdateModality(const std::string& symbolicName, + const RemoteModalityParameters& modality) + { + if (!json_.isMember("DicomModalities")) + { + json_["DicomModalities"] = Json::objectValue; + } + + Json::Value& modalities = json_["DicomModalities"]; + if (modalities.type() != Json::objectValue) + { + LOG(ERROR) << "Bad file format for modality: " << symbolicName; + throw OrthancException(ErrorCode_BadFileFormat); + } + + modalities.removeMember(symbolicName); + + Json::Value v; + modality.Serialize(v, true /* force advanced format */); + modalities[symbolicName] = v; + } + + + void OrthancConfiguration::RemoveModality(const std::string& symbolicName) + { + if (!json_.isMember("DicomModalities")) + { + LOG(ERROR) << "No modality with symbolic name: " << symbolicName; + throw OrthancException(ErrorCode_BadFileFormat); + } + + Json::Value& modalities = json_["DicomModalities"]; + if (modalities.type() != Json::objectValue) + { + LOG(ERROR) << "Bad file format for the \"DicomModalities\" configuration section"; + throw OrthancException(ErrorCode_BadFileFormat); + } + + modalities.removeMember(symbolicName.c_str()); + } + + + void OrthancConfiguration::UpdatePeer(const std::string& symbolicName, + const WebServiceParameters& peer) + { + peer.CheckClientCertificate(); + + if (!json_.isMember("OrthancPeers")) + { + LOG(ERROR) << "No peer with symbolic name: " << symbolicName; + json_["OrthancPeers"] = Json::objectValue; + } + + Json::Value& peers = json_["OrthancPeers"]; + if (peers.type() != Json::objectValue) + { + LOG(ERROR) << "Bad file format for the \"OrthancPeers\" configuration section"; + throw OrthancException(ErrorCode_BadFileFormat); + } + + peers.removeMember(symbolicName); + + Json::Value v; + peer.Serialize(v, + false /* use simple format if possible */, + true /* include passwords */); + peers[symbolicName] = v; + } + + + void OrthancConfiguration::RemovePeer(const std::string& symbolicName) + { + if (!json_.isMember("OrthancPeers")) + { + LOG(ERROR) << "No peer with symbolic name: " << symbolicName; + throw OrthancException(ErrorCode_BadFileFormat); + } + + Json::Value& peers = json_["OrthancPeers"]; + if (peers.type() != Json::objectValue) + { + LOG(ERROR) << "Bad file format for the \"OrthancPeers\" configuration section"; + throw OrthancException(ErrorCode_BadFileFormat); + } + + peers.removeMember(symbolicName.c_str()); + } + + + void OrthancConfiguration::Format(std::string& result) const + { + Json::StyledWriter w; + result = w.write(json_); + } + + + void OrthancConfiguration::SetDefaultEncoding(Encoding encoding) + { + SetDefaultDicomEncoding(encoding); + + // Propagate the encoding to the configuration file that is + // stored in memory + json_["DefaultEncoding"] = EnumerationToString(encoding); + } + + + bool OrthancConfiguration::HasConfigurationChanged() const + { + Json::Value current; + ReadConfiguration(current, configurationFileArg_); + + Json::FastWriter writer; + std::string a = writer.write(json_); + std::string b = writer.write(current); + + return a != b; + } +} diff -r 1ab59dfd4afc -r d658f5785b7b OrthancServer/OrthancConfiguration.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancServer/OrthancConfiguration.h Wed Nov 28 14:37:17 2018 +0100 @@ -0,0 +1,209 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2018 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "../Core/Images/FontRegistry.h" +#include "../Core/WebServiceParameters.h" +#include "../Core/DicomNetworking/RemoteModalityParameters.h" + +#include + +#include +#include + +namespace Orthanc +{ + class MongooseServer; + + class OrthancConfiguration : public boost::noncopyable + { + private: + boost::shared_mutex mutex_; + Json::Value json_; + boost::filesystem::path defaultDirectory_; + std::string configurationAbsolutePath_; + FontRegistry fontRegistry_; + const char* configurationFileArg_; + + OrthancConfiguration() : + configurationFileArg_(NULL) + { + } + + void ValidateConfiguration() const; + + static OrthancConfiguration& GetInstance(); + + public: + class Reader : public boost::noncopyable + { + private: + OrthancConfiguration& configuration_; + boost::shared_lock lock_; + + public: + Reader() : + configuration_(GetInstance()), + lock_(configuration_.mutex_) + { + } + + const OrthancConfiguration& GetConfiguration() const + { + return configuration_; + } + + const Json::Value& GetJson() const + { + return configuration_.json_; + } + }; + + + class Writer : public boost::noncopyable + { + private: + OrthancConfiguration& configuration_; + boost::unique_lock lock_; + + public: + Writer() : + configuration_(GetInstance()), + lock_(configuration_.mutex_) + { + } + + OrthancConfiguration& GetConfiguration() + { + return configuration_; + } + + const OrthancConfiguration& GetConfiguration() const + { + return configuration_; + } + + const Json::Value& GetJson() const + { + return configuration_.json_; + } + }; + + + const Json::Value& GetContent() const + { + return json_; + } + + const std::string& GetConfigurationAbsolutePath() const + { + return configurationAbsolutePath_; + } + + const FontRegistry& GetFontRegistry() const + { + return fontRegistry_; + } + + void Read(const char* configurationFile); + + void RegisterFont(EmbeddedResources::FileResourceId resource) + { + fontRegistry_.AddFromResource(resource); + } + + std::string GetStringParameter(const std::string& parameter, + const std::string& defaultValue) const; + + int GetIntegerParameter(const std::string& parameter, + int defaultValue) const; + + unsigned int GetUnsignedIntegerParameter(const std::string& parameter, + unsigned int defaultValue) const; + + bool GetBooleanParameter(const std::string& parameter, + bool defaultValue) const; + + void GetDicomModalityUsingSymbolicName(RemoteModalityParameters& modality, + const std::string& name) const; + + bool GetOrthancPeer(WebServiceParameters& peer, + const std::string& name) const; + + bool ReadKeys(std::set& target, + const char* parameter, + bool onlyAlphanumeric) const; + + void GetListOfDicomModalities(std::set& target) const; + + void GetListOfOrthancPeers(std::set& target) const; + + void SetupRegisteredUsers(MongooseServer& httpServer) const; + + std::string InterpretStringParameterAsPath(const std::string& parameter) const; + + void GetGlobalListOfStringsParameter(std::list& target, + const std::string& key) const; + + bool IsSameAETitle(const std::string& aet1, + const std::string& aet2) const; + + bool LookupDicomModalityUsingAETitle(RemoteModalityParameters& modality, + const std::string& aet) const; + + bool IsKnownAETitle(const std::string& aet, + const std::string& ip) const; + + RemoteModalityParameters GetModalityUsingSymbolicName(const std::string& name) const; + + RemoteModalityParameters GetModalityUsingAet(const std::string& aet) const; + + void UpdateModality(const std::string& symbolicName, + const RemoteModalityParameters& modality); + + void RemoveModality(const std::string& symbolicName); + + void UpdatePeer(const std::string& symbolicName, + const WebServiceParameters& peer); + + void RemovePeer(const std::string& symbolicName); + + + void Format(std::string& result) const; + + void SetDefaultEncoding(Encoding encoding); + + bool HasConfigurationChanged() const; + }; +} diff -r 1ab59dfd4afc -r d658f5785b7b OrthancServer/OrthancInitialization.cpp --- a/OrthancServer/OrthancInitialization.cpp Wed Nov 28 14:36:33 2018 +0100 +++ b/OrthancServer/OrthancInitialization.cpp Wed Nov 28 14:37:17 2018 +0100 @@ -743,36 +743,10 @@ } - std::string Configuration::InterpretRelativePath(const std::string& baseDirectory, - const std::string& relativePath) - { - boost::filesystem::path base(baseDirectory); - boost::filesystem::path relative(relativePath); - - /** - The following lines should be equivalent to this one: - - return (base / relative).string(); - - However, for some unknown reason, some versions of Boost do not - make the proper path resolution when "baseDirectory" is an - absolute path. So, a hack is used below. - **/ - - if (relative.is_absolute()) - { - return relative.string(); - } - else - { - return (base / relative).string(); - } - } - std::string Configuration::InterpretStringParameterAsPath(const std::string& parameter) { boost::recursive_mutex::scoped_lock lock(globalMutex_); - return InterpretRelativePath(defaultDirectory_.string(), parameter); + return SystemToolbox::InterpretRelativePath(defaultDirectory_.string(), parameter); } diff -r 1ab59dfd4afc -r d658f5785b7b OrthancServer/OrthancInitialization.h --- a/OrthancServer/OrthancInitialization.h Wed Nov 28 14:36:33 2018 +0100 +++ b/OrthancServer/OrthancInitialization.h Wed Nov 28 14:37:17 2018 +0100 @@ -89,9 +89,6 @@ static void SetupRegisteredUsers(MongooseServer& httpServer); - static std::string InterpretRelativePath(const std::string& baseDirectory, - const std::string& relativePath); - static std::string InterpretStringParameterAsPath(const std::string& parameter); static void GetGlobalListOfStringsParameter(std::list& target, diff -r 1ab59dfd4afc -r d658f5785b7b UnitTestsSources/UnitTestsMain.cpp --- a/UnitTestsSources/UnitTestsMain.cpp Wed Nov 28 14:36:33 2018 +0100 +++ b/UnitTestsSources/UnitTestsMain.cpp Wed Nov 28 14:37:17 2018 +0100 @@ -499,10 +499,10 @@ #if defined(__linux__) -TEST(OrthancInitialization, AbsoluteDirectory) +TEST(Toolbox, AbsoluteDirectory) { - ASSERT_EQ("/tmp/hello", Configuration::InterpretRelativePath("/tmp", "hello")); - ASSERT_EQ("/tmp", Configuration::InterpretRelativePath("/tmp", "/tmp")); + ASSERT_EQ("/tmp/hello", SystemToolbox::InterpretRelativePath("/tmp", "hello")); + ASSERT_EQ("/tmp", SystemToolbox::InterpretRelativePath("/tmp", "/tmp")); } #endif