Mercurial > hg > orthanc
changeset 6036:e282d55e043c attach-custom-data
merge default -> attach-custom-data
author | Alain Mazy <am@orthanc.team> |
---|---|
date | Tue, 11 Mar 2025 10:46:53 +0100 |
parents | 83deef099c75 (current diff) cba3e8ca3a87 (diff) |
children | e83414b2b98d 55f171e6ea4a |
files | NEWS OrthancServer/Resources/Configuration.json TODO |
diffstat | 8 files changed, 167 insertions(+), 61 deletions(-) [+] |
line wrap: on
line diff
--- a/NEWS Mon Feb 24 17:54:27 2025 +0100 +++ b/NEWS Tue Mar 11 10:46:53 2025 +0100 @@ -39,6 +39,9 @@ * Recovered compatibility with Windows XP that was broken because of DCMTK 3.6.9 * Enabled support of the 1.2.840.10008.1.2.1.99 transfer syntax (Deflated Explicit VR Little Endian) in static builds +* Housekeeper plugin: + - When encountering an error, the housekeeper now skips the resource and continues processing. + Version 1.12.6 (2025-01-22)
--- a/OrthancFramework/UnitTestsSources/GithubCACertificates.h Mon Feb 24 17:54:27 2025 +0100 +++ b/OrthancFramework/UnitTestsSources/GithubCACertificates.h Tue Mar 11 10:46:53 2025 +0100 @@ -1,30 +1,37 @@ #define GITHUB_CERTIFICATES \ "-----BEGIN CERTIFICATE-----\n" \ -"MIIEyDCCA7CgAwIBAgIQDPW9BitWAvR6uFAsI8zwZjANBgkqhkiG9w0BAQsFADBh\n" \ -"MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\n" \ -"d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH\n" \ -"MjAeFw0yMTAzMzAwMDAwMDBaFw0zMTAzMjkyMzU5NTlaMFkxCzAJBgNVBAYTAlVT\n" \ -"MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxMzAxBgNVBAMTKkRpZ2lDZXJ0IEdsb2Jh\n" \ -"bCBHMiBUTFMgUlNBIFNIQTI1NiAyMDIwIENBMTCCASIwDQYJKoZIhvcNAQEBBQAD\n" \ -"ggEPADCCAQoCggEBAMz3EGJPprtjb+2QUlbFbSd7ehJWivH0+dbn4Y+9lavyYEEV\n" \ -"cNsSAPonCrVXOFt9slGTcZUOakGUWzUb+nv6u8W+JDD+Vu/E832X4xT1FE3LpxDy\n" \ -"FuqrIvAxIhFhaZAmunjZlx/jfWardUSVc8is/+9dCopZQ+GssjoP80j812s3wWPc\n" \ -"3kbW20X+fSP9kOhRBx5Ro1/tSUZUfyyIxfQTnJcVPAPooTncaQwywa8WV0yUR0J8\n" \ -"osicfebUTVSvQpmowQTCd5zWSOTOEeAqgJnwQ3DPP3Zr0UxJqyRewg2C/Uaoq2yT\n" \ -"zGJSQnWS+Jr6Xl6ysGHlHx+5fwmY6D36g39HaaECAwEAAaOCAYIwggF+MBIGA1Ud\n" \ -"EwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFHSFgMBmx9833s+9KTeqAx2+7c0XMB8G\n" \ -"A1UdIwQYMBaAFE4iVCAYlebjbuYP+vq5Eu0GF485MA4GA1UdDwEB/wQEAwIBhjAd\n" \ -"BgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwdgYIKwYBBQUHAQEEajBoMCQG\n" \ -"CCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQAYIKwYBBQUHMAKG\n" \ -"NGh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEdsb2JhbFJvb3RH\n" \ -"Mi5jcnQwQgYDVR0fBDswOTA3oDWgM4YxaHR0cDovL2NybDMuZGlnaWNlcnQuY29t\n" \ -"L0RpZ2lDZXJ0R2xvYmFsUm9vdEcyLmNybDA9BgNVHSAENjA0MAsGCWCGSAGG/WwC\n" \ -"ATAHBgVngQwBATAIBgZngQwBAgEwCAYGZ4EMAQICMAgGBmeBDAECAzANBgkqhkiG\n" \ -"9w0BAQsFAAOCAQEAkPFwyyiXaZd8dP3A+iZ7U6utzWX9upwGnIrXWkOH7U1MVl+t\n" \ -"wcW1BSAuWdH/SvWgKtiwla3JLko716f2b4gp/DA/JIS7w7d7kwcsr4drdjPtAFVS\n" \ -"slme5LnQ89/nD/7d+MS5EHKBCQRfz5eeLjJ1js+aWNJXMX43AYGyZm0pGrFmCW3R\n" \ -"bpD0ufovARTFXFZkAdl9h6g4U5+LXUZtXMYnhIHUfoyMo5tS58aI7Dd8KvvwVVo4\n" \ -"chDYABPPTHPbqjc1qCmBaZx2vN4Ye5DUys/vZwP9BFohFrH/6j/f3IL16/RZkiMN\n" \ -"JCqVJUzKoZHm1Lesh3Sz8W2jmdv51b2EQJ8HmA==\n" \ +"MIIGEzCCA/ugAwIBAgIQfVtRJrR2uhHbdBYLvFMNpzANBgkqhkiG9w0BAQwFADCB\n" \ +"iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl\n" \ +"cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV\n" \ +"BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTgx\n" \ +"MTAyMDAwMDAwWhcNMzAxMjMxMjM1OTU5WjCBjzELMAkGA1UEBhMCR0IxGzAZBgNV\n" \ +"BAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEYMBYGA1UE\n" \ +"ChMPU2VjdGlnbyBMaW1pdGVkMTcwNQYDVQQDEy5TZWN0aWdvIFJTQSBEb21haW4g\n" \ +"VmFsaWRhdGlvbiBTZWN1cmUgU2VydmVyIENBMIIBIjANBgkqhkiG9w0BAQEFAAOC\n" \ +"AQ8AMIIBCgKCAQEA1nMz1tc8INAA0hdFuNY+B6I/x0HuMjDJsGz99J/LEpgPLT+N\n" \ +"TQEMgg8Xf2Iu6bhIefsWg06t1zIlk7cHv7lQP6lMw0Aq6Tn/2YHKHxYyQdqAJrkj\n" \ +"eocgHuP/IJo8lURvh3UGkEC0MpMWCRAIIz7S3YcPb11RFGoKacVPAXJpz9OTTG0E\n" \ +"oKMbgn6xmrntxZ7FN3ifmgg0+1YuWMQJDgZkW7w33PGfKGioVrCSo1yfu4iYCBsk\n" \ +"Haswha6vsC6eep3BwEIc4gLw6uBK0u+QDrTBQBbwb4VCSmT3pDCg/r8uoydajotY\n" \ +"uK3DGReEY+1vVv2Dy2A0xHS+5p3b4eTlygxfFQIDAQABo4IBbjCCAWowHwYDVR0j\n" \ +"BBgwFoAUU3m/WqorSs9UgOHYm8Cd8rIDZsswHQYDVR0OBBYEFI2MXsRUrYrhd+mb\n" \ +"+ZsF4bgBjWHhMA4GA1UdDwEB/wQEAwIBhjASBgNVHRMBAf8ECDAGAQH/AgEAMB0G\n" \ +"A1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAbBgNVHSAEFDASMAYGBFUdIAAw\n" \ +"CAYGZ4EMAQIBMFAGA1UdHwRJMEcwRaBDoEGGP2h0dHA6Ly9jcmwudXNlcnRydXN0\n" \ +"LmNvbS9VU0VSVHJ1c3RSU0FDZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDB2Bggr\n" \ +"BgEFBQcBAQRqMGgwPwYIKwYBBQUHMAKGM2h0dHA6Ly9jcnQudXNlcnRydXN0LmNv\n" \ +"bS9VU0VSVHJ1c3RSU0FBZGRUcnVzdENBLmNydDAlBggrBgEFBQcwAYYZaHR0cDov\n" \ +"L29jc3AudXNlcnRydXN0LmNvbTANBgkqhkiG9w0BAQwFAAOCAgEAMr9hvQ5Iw0/H\n" \ +"ukdN+Jx4GQHcEx2Ab/zDcLRSmjEzmldS+zGea6TvVKqJjUAXaPgREHzSyrHxVYbH\n" \ +"7rM2kYb2OVG/Rr8PoLq0935JxCo2F57kaDl6r5ROVm+yezu/Coa9zcV3HAO4OLGi\n" \ +"H19+24rcRki2aArPsrW04jTkZ6k4Zgle0rj8nSg6F0AnwnJOKf0hPHzPE/uWLMUx\n" \ +"RP0T7dWbqWlod3zu4f+k+TY4CFM5ooQ0nBnzvg6s1SQ36yOoeNDT5++SR2RiOSLv\n" \ +"xvcRviKFxmZEJCaOEDKNyJOuB56DPi/Z+fVGjmO+wea03KbNIaiGCpXZLoUmGv38\n" \ +"sbZXQm2V0TP2ORQGgkE49Y9Y3IBbpNV9lXj9p5v//cWoaasm56ekBYdbqbe4oyAL\n" \ +"l6lFhd2zi+WJN44pDfwGF/Y4QA5C5BIG+3vzxhFoYt/jmPQT2BVPi7Fp2RBgvGQq\n" \ +"6jG35LWjOhSbJuMLe/0CjraZwTiXWTb2qHSihrZe68Zk6s+go/lunrotEbaGmAhY\n" \ +"LcmsJWTyXnW0OMGuf1pGg+pRyrbxmRE1a6Vqe8YAsOf4vmSyrcjC8azjUeqkk+B5\n" \ +"yOGBQMkKW+ESPMFgKuOXwIlCypTPRpgSabuY0MLTDXJLR27lk8QyKGOHQ+SwMj4K\n" \ +"00u/I5sUKUErmgQfky3xxzlIPK1aEn8=\n" \ "-----END CERTIFICATE-----\n" \ "\n"
--- a/OrthancFramework/UnitTestsSources/RestApiTests.cpp Mon Feb 24 17:54:27 2025 +0100 +++ b/OrthancFramework/UnitTestsSources/RestApiTests.cpp Tue Mar 11 10:46:53 2025 +0100 @@ -99,9 +99,9 @@ #if (UNIT_TESTS_WITH_HTTP_CONNEXIONS == 1) && (ORTHANC_ENABLE_SSL == 1) && (ORTHANC_SANDBOXED != 1) /** - The HTTPS CA certificates for BitBucket were extracted as follows: + The HTTPS CA certificates for Github were extracted as follows: - (1) We retrieve the URI of the root CA of BitBucket: + (1) We retrieve the URI of the root CA of Github: # echo | openssl s_client -servername raw.githubusercontent.com -connect raw.githubusercontent.com:443 2>/dev/null | openssl x509 -text | grep "CA Issuers" @@ -109,7 +109,7 @@ macro that can be used by libcurl: # cd UnitTestsSources - # python2 ../Resources/RetrieveCACertificates.py GITHUB_CERTIFICATES http://cacerts.digicert.com/DigiCertGlobalG2TLSRSASHA2562020CA1-1.crt > GithubCACertificates.h + # python2 ../Resources/RetrieveCACertificates.py GITHUB_CERTIFICATES http://crt.sectigo.com/SectigoRSADomainValidationSecureServerCA.crt > GithubCACertificates.h **/ #include "GithubCACertificates.h"
--- a/OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp Mon Feb 24 17:54:27 2025 +0100 +++ b/OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp Tue Mar 11 10:46:53 2025 +0100 @@ -26,6 +26,7 @@ #include <boost/algorithm/string/predicate.hpp> #include <boost/move/unique_ptr.hpp> #include <boost/thread.hpp> +#include <boost/algorithm/string/join.hpp> #include <json/reader.h> @@ -4077,6 +4078,26 @@ } } + void SerializeGetArguments(std::string& output, const OrthancPluginHttpRequest* request) + { + output.clear(); + std::vector<std::string> arguments; + for (uint32_t i = 0; i < request->getCount; ++i) + { + if (request->getValues[i] && strlen(request->getValues[i]) > 0) + { + arguments.push_back(std::string(request->getKeys[i]) + "=" + std::string(request->getValues[i])); + } + else + { + arguments.push_back(std::string(request->getKeys[i])); + } + } + + output = boost::algorithm::join(arguments, "&"); + } + + #if !ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 12, 4) static void SetPluginProperty(const std::string& pluginIdentifier, _OrthancPluginProperty property, @@ -4130,6 +4151,24 @@ httpStatus_(0) { } + + RestApiClient::RestApiClient(const char* url, + const OrthancPluginHttpRequest* request) : + method_(request->method), + path_(url), + afterPlugins_(false), + httpStatus_(0) + { + OrthancPlugins::GetHttpHeaders(requestHeaders_, request); + + std::string getArguments; + OrthancPlugins::SerializeGetArguments(getArguments, request); + + if (!getArguments.empty()) + { + path_ += "?" + getArguments; + } + } #endif @@ -4195,6 +4234,32 @@ } } } + + void RestApiClient::Forward(OrthancPluginContext* context, OrthancPluginRestOutput* output) + { + if (Execute() && httpStatus_ == 200) + { + const char* mimeType = NULL; + for (HttpHeaders::const_iterator h = answerHeaders_.begin(); h != answerHeaders_.end(); ++h) + { + if (h->first == "content-type") + { + mimeType = h->second.c_str(); + } + } + + AnswerString(answerBody_, mimeType, output); + } + else + { + AnswerHttpError(httpStatus_, output); + } + } + + bool RestApiClient::GetAnswerJson(Json::Value& output) const + { + return ReadJson(output, answerBody_); + } #endif
--- a/OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.h Mon Feb 24 17:54:27 2025 +0100 +++ b/OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.h Tue Mar 11 10:46:53 2025 +0100 @@ -1399,6 +1399,9 @@ // helper method to convert Http headers from the plugin SDK to a std::map void GetHttpHeaders(HttpHeaders& result, const OrthancPluginHttpRequest* request); +// helper method to re-serialize the get arguments from the SDK into a string +void SerializeGetArguments(std::string& output, const OrthancPluginHttpRequest* request); + #if HAS_ORTHANC_PLUGIN_WEBDAV == 1 class IWebDavCollection : public boost::noncopyable { @@ -1528,6 +1531,10 @@ public: RestApiClient(); + + // used to forward a call from the plugin to the core + RestApiClient(const char* url, + const OrthancPluginHttpRequest* request); void SetMethod(OrthancPluginHttpMethod method) { @@ -1584,12 +1591,17 @@ bool Execute(); + // Execute and forward the response as is + void Forward(OrthancPluginContext* context, OrthancPluginRestOutput* output); + uint16_t GetHttpStatus() const; bool LookupAnswerHeader(std::string& value, const std::string& key) const; const std::string& GetAnswerBody() const; + + bool GetAnswerJson(Json::Value& output) const; }; #endif }
--- a/OrthancServer/Plugins/Samples/Housekeeper/Plugin.cpp Mon Feb 24 17:54:27 2025 +0100 +++ b/OrthancServer/Plugins/Samples/Housekeeper/Plugin.cpp Tue Mar 11 10:46:53 2025 +0100 @@ -549,39 +549,46 @@ const Json::Value& change = changes["Changes"][i]; int64_t seq = change["Seq"].asInt64(); - if (!limitToChange_.empty()) // if updating only maindicomtags for a single level + try { - if (change["ChangeType"] == limitToChange_) + if (!limitToChange_.empty()) // if updating only maindicomtags for a single level + { + if (change["ChangeType"] == limitToChange_) + { + Json::Value result; + Json::Value request; + request["ReconstructFiles"] = false; + request["LimitToThisLevelMainDicomTags"] = true; + OrthancPlugins::RestApiPost(result, "/" + limitToUrl_ + "/" + change["ID"].asString() + "/reconstruct", request, false); + } + } + else { - Json::Value result; - Json::Value request; - request["ReconstructFiles"] = false; - request["LimitToThisLevelMainDicomTags"] = true; - OrthancPlugins::RestApiPost(result, "/" + limitToUrl_ + "/" + change["ID"].asString() + "/reconstruct", request, false); + if (change["ChangeType"] == "NewStudy") // some StableStudy might be missing if orthanc was shutdown during a StableAge -> consider only the NewStudy events that can not be missed + { + Json::Value result; + + if (needsReconstruct || needsReingest ||force_) + { + Json::Value request; + if (needsReingest) + { + request["ReconstructFiles"] = true; + } + OrthancPlugins::RestApiPost(result, "/studies/" + change["ID"].asString() + "/reconstruct", request, false); + } + + if (needsDicomWebCaching) + { + Json::Value request; + OrthancPlugins::RestApiPost(result, "/studies/" + change["ID"].asString() + "/update-dicomweb-cache", request, true); + } + } } } - else + catch (...) { - if (change["ChangeType"] == "NewStudy") // some StableStudy might be missing if orthanc was shutdown during a StableAge -> consider only the NewStudy events that can not be missed - { - Json::Value result; - - if (needsReconstruct || needsReingest ||force_) - { - Json::Value request; - if (needsReingest) - { - request["ReconstructFiles"] = true; - } - OrthancPlugins::RestApiPost(result, "/studies/" + change["ID"].asString() + "/reconstruct", request, false); - } - - if (needsDicomWebCaching) - { - Json::Value request; - OrthancPlugins::RestApiPost(result, "/studies/" + change["ID"].asString() + "/update-dicomweb-cache", request, true); - } - } + ORTHANC_PLUGINS_LOG_ERROR("Housekeeper: unhandled error while processing change " + boost::lexical_cast<std::string>(seq) + ", skipping resource."); } { @@ -703,9 +710,17 @@ { if (runningPeriods_.isInPeriod()) { - completed = ProcessChanges(needsReconstruct, needsReingest, needsDicomWebCaching, currentDbConfiguration); - SaveStatusInDb(); - + try + { + completed = ProcessChanges(needsReconstruct, needsReingest, needsDicomWebCaching, currentDbConfiguration); + SaveStatusInDb(); + } + catch (...) + { + ORTHANC_PLUGINS_LOG_ERROR("Housekeeper: unhandled error while processing change " + boost::lexical_cast<std::string>(pluginStatus_.lastProcessedChange) + + " / " + boost::lexical_cast<std::string>(pluginStatus_.lastChangeToProcess)); + } + if (!completed) { boost::recursive_mutex::scoped_lock lock(pluginStatusMutex_);
--- a/OrthancServer/Resources/Configuration.json Mon Feb 24 17:54:27 2025 +0100 +++ b/OrthancServer/Resources/Configuration.json Tue Mar 11 10:46:53 2025 +0100 @@ -947,7 +947,8 @@ // Deidentify/anonymize the contents of the logs (notably C-FIND, // C-GET, and C-MOVE queries submitted to Orthanc) according to - // Table E.1-1 of the DICOM standard (new in Orthanc 1.8.2) + // Table E.1-1 of the DICOM standard (new in Orthanc 1.8.2). + // Note that, the DICOM logs at TRACE level are not deidentified ! "DeidentifyLogs" : true, // If "DeidentifyLogs" is true, this sets the DICOM standard to
--- a/TODO Mon Feb 24 17:54:27 2025 +0100 +++ b/TODO Tue Mar 11 10:46:53 2025 +0100 @@ -198,6 +198,9 @@ * Log outgoing C-Find queries * Support other Transfer Syntaxes in the Worklist plugin: https://discourse.orthanc-server.org/t/could-you-please-create-an-option-to-set-the-transfer-syntax-in-the-worklist-plugin-currently-little-endian-explicit-is-fixed/4871 +* Deidentify Trace level logs: https://discourse.orthanc-server.org/t/dicom-log-deidentification-at-trace-log-level/5563 + We should implement something like GetDeidentifiedContent(const DcmDataSet& dataset) that would + reproduce DcmDataSet::print but hide individual elements that contain PHI. ---------