Mercurial > hg > orthanc-stone
diff Framework/Orthanc/Core/HttpClient.cpp @ 1:2dbe613f6c93
add orthanc core
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Fri, 14 Oct 2016 15:39:01 +0200 |
parents | |
children | 4b7e0244881f |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Orthanc/Core/HttpClient.cpp Fri Oct 14 15:39:01 2016 +0200 @@ -0,0 +1,834 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, 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 <http://www.gnu.org/licenses/>. + **/ + + +#include "PrecompiledHeaders.h" +#include "HttpClient.h" + +#include "Toolbox.h" +#include "OrthancException.h" +#include "Logging.h" +#include "ChunkedBuffer.h" + +#include <string.h> +#include <curl/curl.h> +#include <boost/algorithm/string/predicate.hpp> +#include <boost/thread/mutex.hpp> + + +#if ORTHANC_SSL_ENABLED == 1 +// For OpenSSL initialization and finalization +# include <openssl/conf.h> +# include <openssl/engine.h> +# include <openssl/err.h> +# include <openssl/evp.h> +# include <openssl/ssl.h> +#endif + + +#if ORTHANC_PKCS11_ENABLED == 1 +# include "Pkcs11.h" +#endif + + +extern "C" +{ + static CURLcode GetHttpStatus(CURLcode code, CURL* curl, long* status) + { + if (code == CURLE_OK) + { + code = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, status); + return code; + } + else + { + *status = 0; + return code; + } + } + + // This is a dummy wrapper function to suppress any OpenSSL-related + // problem in valgrind. Inlining is prevented. +#if defined(__GNUC__) || defined(__clang__) + __attribute__((noinline)) +#endif + static CURLcode OrthancHttpClientPerformSSL(CURL* curl, long* status) + { + return GetHttpStatus(curl_easy_perform(curl), curl, status); + } +} + + + +namespace Orthanc +{ + class HttpClient::GlobalParameters + { + private: + boost::mutex mutex_; + bool httpsVerifyPeers_; + std::string httpsCACertificates_; + std::string proxy_; + long timeout_; + + GlobalParameters() : + httpsVerifyPeers_(true), + timeout_(0) + { + } + + public: + // Singleton pattern + static GlobalParameters& GetInstance() + { + static GlobalParameters parameters; + return parameters; + } + + void ConfigureSsl(bool httpsVerifyPeers, + const std::string& httpsCACertificates) + { + boost::mutex::scoped_lock lock(mutex_); + httpsVerifyPeers_ = httpsVerifyPeers; + httpsCACertificates_ = httpsCACertificates; + } + + void GetSslConfiguration(bool& httpsVerifyPeers, + std::string& httpsCACertificates) + { + boost::mutex::scoped_lock lock(mutex_); + httpsVerifyPeers = httpsVerifyPeers_; + httpsCACertificates = httpsCACertificates_; + } + + void SetDefaultProxy(const std::string& proxy) + { + LOG(INFO) << "Setting the default proxy for HTTP client connections: " << proxy; + + { + boost::mutex::scoped_lock lock(mutex_); + proxy_ = proxy; + } + } + + void GetDefaultProxy(std::string& target) + { + boost::mutex::scoped_lock lock(mutex_); + target = proxy_; + } + + void SetDefaultTimeout(long seconds) + { + LOG(INFO) << "Setting the default timeout for HTTP client connections: " << seconds << " seconds"; + + { + boost::mutex::scoped_lock lock(mutex_); + timeout_ = seconds; + } + } + + long GetDefaultTimeout() + { + boost::mutex::scoped_lock lock(mutex_); + return timeout_; + } + +#if ORTHANC_PKCS11_ENABLED == 1 + bool IsPkcs11Initialized() + { + boost::mutex::scoped_lock lock(mutex_); + return Pkcs11::IsInitialized(); + } + + void InitializePkcs11(const std::string& module, + const std::string& pin, + bool verbose) + { + boost::mutex::scoped_lock lock(mutex_); + Pkcs11::Initialize(module, pin, verbose); + } +#endif + }; + + + struct HttpClient::PImpl + { + CURL* curl_; + struct curl_slist *defaultPostHeaders_; + struct curl_slist *userHeaders_; + }; + + + static void ThrowException(HttpStatus status) + { + switch (status) + { + case HttpStatus_400_BadRequest: + throw OrthancException(ErrorCode_BadRequest); + + case HttpStatus_401_Unauthorized: + case HttpStatus_403_Forbidden: + throw OrthancException(ErrorCode_Unauthorized); + + case HttpStatus_404_NotFound: + throw OrthancException(ErrorCode_UnknownResource); + + default: + throw OrthancException(ErrorCode_NetworkProtocol); + } + } + + + static CURLcode CheckCode(CURLcode code) + { + if (code == CURLE_NOT_BUILT_IN) + { + LOG(ERROR) << "Your libcurl does not contain a required feature, " + << "please recompile Orthanc with -DUSE_SYSTEM_CURL=OFF"; + throw OrthancException(ErrorCode_InternalError); + } + + if (code != CURLE_OK) + { + LOG(ERROR) << "libCURL error: " + std::string(curl_easy_strerror(code)); + throw OrthancException(ErrorCode_NetworkProtocol); + } + + return code; + } + + + static size_t CurlBodyCallback(void *buffer, size_t size, size_t nmemb, void *payload) + { + ChunkedBuffer& target = *(static_cast<ChunkedBuffer*>(payload)); + + size_t length = size * nmemb; + if (length == 0) + { + return 0; + } + else + { + target.AddChunk(buffer, length); + return length; + } + } + + + struct CurlHeaderParameters + { + bool lowerCase_; + HttpClient::HttpHeaders* headers_; + }; + + + static size_t CurlHeaderCallback(void *buffer, size_t size, size_t nmemb, void *payload) + { + CurlHeaderParameters& parameters = *(static_cast<CurlHeaderParameters*>(payload)); + assert(parameters.headers_ != NULL); + + size_t length = size * nmemb; + if (length == 0) + { + return 0; + } + else + { + std::string s(reinterpret_cast<const char*>(buffer), length); + std::size_t colon = s.find(':'); + std::size_t eol = s.find("\r\n"); + if (colon != std::string::npos && + eol != std::string::npos) + { + std::string tmp(s.substr(0, colon)); + + if (parameters.lowerCase_) + { + Toolbox::ToLowerCase(tmp); + } + + std::string key = Toolbox::StripSpaces(tmp); + + if (!key.empty()) + { + std::string value = Toolbox::StripSpaces(s.substr(colon + 1, eol)); + (*parameters.headers_) [key] = value; + } + } + + return length; + } + } + + + void HttpClient::Setup() + { + pimpl_->userHeaders_ = NULL; + pimpl_->defaultPostHeaders_ = NULL; + if ((pimpl_->defaultPostHeaders_ = curl_slist_append(pimpl_->defaultPostHeaders_, "Expect:")) == NULL) + { + throw OrthancException(ErrorCode_NotEnoughMemory); + } + + pimpl_->curl_ = curl_easy_init(); + if (!pimpl_->curl_) + { + curl_slist_free_all(pimpl_->defaultPostHeaders_); + throw OrthancException(ErrorCode_NotEnoughMemory); + } + + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_WRITEFUNCTION, &CurlBodyCallback)); + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HEADER, 0)); + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_FOLLOWLOCATION, 1)); + + // This fixes the "longjmp causes uninitialized stack frame" crash + // that happens on modern Linux versions. + // http://stackoverflow.com/questions/9191668/error-longjmp-causes-uninitialized-stack-frame + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_NOSIGNAL, 1)); + + url_ = ""; + method_ = HttpMethod_Get; + lastStatus_ = HttpStatus_200_Ok; + isVerbose_ = false; + timeout_ = GlobalParameters::GetInstance().GetDefaultTimeout(); + GlobalParameters::GetInstance().GetDefaultProxy(proxy_); + GlobalParameters::GetInstance().GetSslConfiguration(verifyPeers_, caCertificates_); + } + + + HttpClient::HttpClient() : + pimpl_(new PImpl), + verifyPeers_(true), + pkcs11Enabled_(false), + headersToLowerCase_(true) + { + Setup(); + } + + + HttpClient::HttpClient(const WebServiceParameters& service, + const std::string& uri) : + pimpl_(new PImpl), + verifyPeers_(true), + headersToLowerCase_(true) + { + Setup(); + + if (service.GetUsername().size() != 0 && + service.GetPassword().size() != 0) + { + SetCredentials(service.GetUsername().c_str(), + service.GetPassword().c_str()); + } + + if (!service.GetCertificateFile().empty()) + { + SetClientCertificate(service.GetCertificateFile(), + service.GetCertificateKeyFile(), + service.GetCertificateKeyPassword()); + } + + SetPkcs11Enabled(service.IsPkcs11Enabled()); + + SetUrl(service.GetUrl() + uri); + } + + + HttpClient::~HttpClient() + { + curl_easy_cleanup(pimpl_->curl_); + curl_slist_free_all(pimpl_->defaultPostHeaders_); + ClearHeaders(); + } + + + void HttpClient::SetVerbose(bool isVerbose) + { + isVerbose_ = isVerbose; + + if (isVerbose_) + { + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_VERBOSE, 1)); + } + else + { + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_VERBOSE, 0)); + } + } + + + void HttpClient::AddHeader(const std::string& key, + const std::string& value) + { + if (key.empty()) + { + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + + std::string s = key + ": " + value; + + if ((pimpl_->userHeaders_ = curl_slist_append(pimpl_->userHeaders_, s.c_str())) == NULL) + { + throw OrthancException(ErrorCode_NotEnoughMemory); + } + } + + + void HttpClient::ClearHeaders() + { + if (pimpl_->userHeaders_ != NULL) + { + curl_slist_free_all(pimpl_->userHeaders_); + pimpl_->userHeaders_ = NULL; + } + } + + + bool HttpClient::ApplyInternal(std::string& answerBody, + HttpHeaders* answerHeaders) + { + answerBody.clear(); + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_URL, url_.c_str())); + + CurlHeaderParameters headerParameters; + + if (answerHeaders == NULL) + { + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HEADERFUNCTION, NULL)); + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HEADERDATA, NULL)); + } + else + { + headerParameters.lowerCase_ = headersToLowerCase_; + headerParameters.headers_ = answerHeaders; + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HEADERFUNCTION, &CurlHeaderCallback)); + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HEADERDATA, &headerParameters)); + } + +#if ORTHANC_SSL_ENABLED == 1 + // Setup HTTPS-related options + + if (verifyPeers_) + { + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_CAINFO, caCertificates_.c_str())); + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSL_VERIFYHOST, 2)); // libcurl default is strict verifyhost + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSL_VERIFYPEER, 1)); + } + else + { + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSL_VERIFYHOST, 0)); + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSL_VERIFYPEER, 0)); + } +#endif + + // Setup the HTTPS client certificate + if (!clientCertificateFile_.empty() && + pkcs11Enabled_) + { + LOG(ERROR) << "Cannot enable both client certificates and PKCS#11 authentication"; + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + + if (pkcs11Enabled_) + { +#if ORTHANC_PKCS11_ENABLED == 1 + if (GlobalParameters::GetInstance().IsPkcs11Initialized()) + { + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSLENGINE, Pkcs11::GetEngineIdentifier())); + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSLKEYTYPE, "ENG")); + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSLCERTTYPE, "ENG")); + } + else + { + LOG(ERROR) << "Cannot use PKCS#11 for a HTTPS request, because it has not been initialized"; + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } +#else + LOG(ERROR) << "This version of Orthanc is compiled without support for PKCS#11"; + throw OrthancException(ErrorCode_InternalError); +#endif + } + else if (!clientCertificateFile_.empty()) + { +#if ORTHANC_SSL_ENABLED == 1 + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSLCERTTYPE, "PEM")); + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSLCERT, clientCertificateFile_.c_str())); + + if (!clientCertificateKeyPassword_.empty()) + { + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_KEYPASSWD, clientCertificateKeyPassword_.c_str())); + } + + // NB: If no "clientKeyFile_" is provided, the key must be + // prepended to the certificate file + if (!clientCertificateKeyFile_.empty()) + { + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSLKEYTYPE, "PEM")); + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSLKEY, clientCertificateKeyFile_.c_str())); + } +#else + LOG(ERROR) << "This version of Orthanc is compiled without OpenSSL support, cannot use HTTPS client authentication"; + throw OrthancException(ErrorCode_InternalError); +#endif + } + + // Reset the parameters from previous calls to Apply() + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HTTPHEADER, pimpl_->userHeaders_)); + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HTTPGET, 0L)); + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POST, 0L)); + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_NOBODY, 0L)); + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_CUSTOMREQUEST, NULL)); + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POSTFIELDS, NULL)); + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POSTFIELDSIZE, 0L)); + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_PROXY, NULL)); + + if (redirectionFollowed_) + { + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_FOLLOWLOCATION, 1L)); + } + else + { + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_FOLLOWLOCATION, 0L)); + } + + // Set timeouts + if (timeout_ <= 0) + { + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_TIMEOUT, 10)); /* default: 10 seconds */ + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_CONNECTTIMEOUT, 10)); /* default: 10 seconds */ + } + else + { + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_TIMEOUT, timeout_)); + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_CONNECTTIMEOUT, timeout_)); + } + + if (credentials_.size() != 0) + { + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_USERPWD, credentials_.c_str())); + } + + if (proxy_.size() != 0) + { + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_PROXY, proxy_.c_str())); + } + + switch (method_) + { + case HttpMethod_Get: + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HTTPGET, 1L)); + break; + + case HttpMethod_Post: + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POST, 1L)); + + if (pimpl_->userHeaders_ == NULL) + { + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HTTPHEADER, pimpl_->defaultPostHeaders_)); + } + + break; + + case HttpMethod_Delete: + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_NOBODY, 1L)); + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_CUSTOMREQUEST, "DELETE")); + break; + + case HttpMethod_Put: + // http://stackoverflow.com/a/7570281/881731: Don't use + // CURLOPT_PUT if there is a body + + // CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_PUT, 1L)); + + curl_easy_setopt(pimpl_->curl_, CURLOPT_CUSTOMREQUEST, "PUT"); /* !!! */ + + if (pimpl_->userHeaders_ == NULL) + { + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HTTPHEADER, pimpl_->defaultPostHeaders_)); + } + + break; + + default: + throw OrthancException(ErrorCode_InternalError); + } + + + if (method_ == HttpMethod_Post || + method_ == HttpMethod_Put) + { + if (body_.size() > 0) + { + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POSTFIELDS, body_.c_str())); + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POSTFIELDSIZE, body_.size())); + } + else + { + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POSTFIELDS, NULL)); + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POSTFIELDSIZE, 0)); + } + } + + + // Do the actual request + CURLcode code; + long status = 0; + + ChunkedBuffer buffer; + CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_WRITEDATA, &buffer)); + + if (boost::starts_with(url_, "https://")) + { + code = OrthancHttpClientPerformSSL(pimpl_->curl_, &status); + } + else + { + code = GetHttpStatus(curl_easy_perform(pimpl_->curl_), pimpl_->curl_, &status); + } + + CheckCode(code); + + if (status == 0) + { + // This corresponds to a call to an inexistent host + lastStatus_ = HttpStatus_500_InternalServerError; + } + else + { + lastStatus_ = static_cast<HttpStatus>(status); + } + + bool success = (status >= 200 && status < 300); + + if (success) + { + buffer.Flatten(answerBody); + } + else + { + answerBody.clear(); + LOG(INFO) << "Error in HTTP request, received HTTP status " << status + << " (" << EnumerationToString(lastStatus_) << ")"; + } + + return success; + } + + + bool HttpClient::ApplyInternal(Json::Value& answerBody, + HttpClient::HttpHeaders* answerHeaders) + { + std::string s; + if (ApplyInternal(s, answerHeaders)) + { + Json::Reader reader; + return reader.parse(s, answerBody); + } + else + { + return false; + } + } + + + void HttpClient::SetCredentials(const char* username, + const char* password) + { + credentials_ = std::string(username) + ":" + std::string(password); + } + + + void HttpClient::ConfigureSsl(bool httpsVerifyPeers, + const std::string& httpsVerifyCertificates) + { +#if ORTHANC_SSL_ENABLED == 1 + if (httpsVerifyPeers) + { + if (httpsVerifyCertificates.empty()) + { + LOG(WARNING) << "No certificates are provided to validate peers, " + << "set \"HttpsCACertificates\" if you need to do HTTPS requests"; + } + else + { + LOG(WARNING) << "HTTPS will use the CA certificates from this file: " << httpsVerifyCertificates; + } + } + else + { + LOG(WARNING) << "The verification of the peers in HTTPS requests is disabled"; + } +#endif + + GlobalParameters::GetInstance().ConfigureSsl(httpsVerifyPeers, httpsVerifyCertificates); + } + + + void HttpClient::GlobalInitialize() + { +#if ORTHANC_SSL_ENABLED == 1 + CheckCode(curl_global_init(CURL_GLOBAL_ALL)); +#else + CheckCode(curl_global_init(CURL_GLOBAL_ALL & ~CURL_GLOBAL_SSL)); +#endif + } + + + void HttpClient::GlobalFinalize() + { + curl_global_cleanup(); + +#if ORTHANC_PKCS11_ENABLED == 1 + Pkcs11::Finalize(); +#endif + } + + + void HttpClient::SetDefaultProxy(const std::string& proxy) + { + GlobalParameters::GetInstance().SetDefaultProxy(proxy); + } + + + void HttpClient::SetDefaultTimeout(long timeout) + { + GlobalParameters::GetInstance().SetDefaultTimeout(timeout); + } + + + void HttpClient::ApplyAndThrowException(std::string& answerBody) + { + if (!Apply(answerBody)) + { + ThrowException(GetLastStatus()); + } + } + + + void HttpClient::ApplyAndThrowException(Json::Value& answerBody) + { + if (!Apply(answerBody)) + { + ThrowException(GetLastStatus()); + } + } + + + void HttpClient::ApplyAndThrowException(std::string& answerBody, + HttpHeaders& answerHeaders) + { + if (!Apply(answerBody, answerHeaders)) + { + ThrowException(GetLastStatus()); + } + } + + + void HttpClient::ApplyAndThrowException(Json::Value& answerBody, + HttpHeaders& answerHeaders) + { + if (!Apply(answerBody, answerHeaders)) + { + ThrowException(GetLastStatus()); + } + } + + + void HttpClient::SetClientCertificate(const std::string& certificateFile, + const std::string& certificateKeyFile, + const std::string& certificateKeyPassword) + { + if (certificateFile.empty()) + { + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + + if (!Toolbox::IsRegularFile(certificateFile)) + { + LOG(ERROR) << "Cannot open certificate file: " << certificateFile; + throw OrthancException(ErrorCode_InexistentFile); + } + + if (!certificateKeyFile.empty() && + !Toolbox::IsRegularFile(certificateKeyFile)) + { + LOG(ERROR) << "Cannot open key file: " << certificateKeyFile; + throw OrthancException(ErrorCode_InexistentFile); + } + + clientCertificateFile_ = certificateFile; + clientCertificateKeyFile_ = certificateKeyFile; + clientCertificateKeyPassword_ = certificateKeyPassword; + } + + + void HttpClient::InitializePkcs11(const std::string& module, + const std::string& pin, + bool verbose) + { +#if ORTHANC_PKCS11_ENABLED == 1 + LOG(INFO) << "Initializing PKCS#11 using " << module + << (pin.empty() ? " (no PIN provided)" : " (PIN is provided)"); + GlobalParameters::GetInstance().InitializePkcs11(module, pin, verbose); +#else + LOG(ERROR) << "This version of Orthanc is compiled without support for PKCS#11"; + throw OrthancException(ErrorCode_InternalError); +#endif + } + + + void HttpClient::InitializeOpenSsl() + { +#if ORTHANC_SSL_ENABLED == 1 + // https://wiki.openssl.org/index.php/Library_Initialization + SSL_library_init(); + SSL_load_error_strings(); + OpenSSL_add_all_algorithms(); + ERR_load_crypto_strings(); +#endif + } + + + void HttpClient::FinalizeOpenSsl() + { + #if ORTHANC_SSL_ENABLED == 1 + // Finalize OpenSSL + // https://wiki.openssl.org/index.php/Library_Initialization#Cleanup + FIPS_mode_set(0); + ENGINE_cleanup(); + CONF_modules_unload(1); + EVP_cleanup(); + CRYPTO_cleanup_all_ex_data(); + ERR_remove_state(0); + ERR_free_strings(); +#endif + } +}