Mercurial > hg > orthanc
changeset 6037:ee903fefedbf pixel-anon
merge default -> pixel-anon
author | Alain Mazy <am@orthanc.team> |
---|---|
date | Tue, 11 Mar 2025 10:47:31 +0100 |
parents | 6ad530603d23 (current diff) cba3e8ca3a87 (diff) |
children | 4742b4fe824b |
files | TODO |
diffstat | 11 files changed, 157 insertions(+), 75 deletions(-) [+] |
line wrap: on
line diff
--- a/NEWS Fri Mar 07 17:25:34 2025 +0100 +++ b/NEWS Tue Mar 11 10:47:31 2025 +0100 @@ -25,6 +25,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/Sources/HttpServer/HttpServer.cpp Fri Mar 07 17:25:34 2025 +0100 +++ b/OrthancFramework/Sources/HttpServer/HttpServer.cpp Tue Mar 11 10:47:31 2025 +0100 @@ -699,7 +699,7 @@ const HttpToolbox::Arguments& headers, const HttpToolbox::GetArguments& argumentsGET) { - std::string overriden; + std::string overridden; // Check whether some PUT/DELETE faking is done @@ -709,7 +709,7 @@ if (methodOverride != headers.end()) { - overriden = methodOverride->second; + overridden = methodOverride->second; } else if (!strcmp(request->request_method, "GET")) { @@ -719,25 +719,25 @@ { if (argumentsGET[i].first == "_method") { - overriden = argumentsGET[i].second; + overridden = argumentsGET[i].second; break; } } } - if (overriden.size() > 0) + if (overridden.size() > 0) { // A faking has been done within this request - Toolbox::ToUpperCase(overriden); + Toolbox::ToUpperCase(overridden); - CLOG(INFO, HTTP) << "HTTP method faking has been detected for " << overriden; + CLOG(INFO, HTTP) << "HTTP method faking has been detected for " << overridden; - if (overriden == "PUT") + if (overridden == "PUT") { method = HttpMethod_Put; return true; } - else if (overriden == "DELETE") + else if (overridden == "DELETE") { method = HttpMethod_Delete; return true;
--- a/OrthancFramework/UnitTestsSources/GithubCACertificates.h Fri Mar 07 17:25:34 2025 +0100 +++ b/OrthancFramework/UnitTestsSources/GithubCACertificates.h Tue Mar 11 10:47:31 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 Fri Mar 07 17:25:34 2025 +0100 +++ b/OrthancFramework/UnitTestsSources/RestApiTests.cpp Tue Mar 11 10:47:31 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 Fri Mar 07 17:25:34 2025 +0100 +++ b/OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp Tue Mar 11 10:47:31 2025 +0100 @@ -4151,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 @@ -4216,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 Fri Mar 07 17:25:34 2025 +0100 +++ b/OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.h Tue Mar 11 10:47:31 2025 +0100 @@ -1531,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) { @@ -1587,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 Fri Mar 07 17:25:34 2025 +0100 +++ b/OrthancServer/Plugins/Samples/Housekeeper/Plugin.cpp Tue Mar 11 10:47:31 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 Fri Mar 07 17:25:34 2025 +0100 +++ b/OrthancServer/Resources/Configuration.json Tue Mar 11 10:47:31 2025 +0100 @@ -214,7 +214,7 @@ // "?" accepts any single character at that position. // "*" accepts any string value at that position. // If you want to add a a SOP class that is not defined in - // DCMTK defaults, you must add it explicitely. + // DCMTK defaults, you must add it explicitly. // (new in Orthanc 1.12.6) // Example to add a non standard class // and keep the default ones: @@ -531,9 +531,9 @@ // accept C-FIND requests from Orthanc (new in Orthanc 1.8.1). "DicomEchoChecksFind" : false, - // Wheter Orthanc uses C-MOVE or C-GET to retrieve a resource after + // Whether Orthanc uses C-MOVE or C-GET to retrieve a resource after // a C-Find (when calling /queries/.../retrieve). - // This configuration can be overriden for each modality by providing + // This configuration can be overridden for each modality by providing // "RetrieveMethod" in the "DicomModalities" entry. // (new in Orthanc 1.12.6) "DicomDefaultRetrieveMethod" : "C-MOVE", @@ -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/OrthancServer/Sources/OrthancRestApi/OrthancRestModalities.cpp Fri Mar 07 17:25:34 2025 +0100 +++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestModalities.cpp Tue Mar 11 10:47:31 2025 +0100 @@ -1697,7 +1697,7 @@ "Usage of wildcards is prohibited and the query shall only contain DICOM ID tags. " "Additionally, you may provide SOPClassesInStudy to limit the scope of the DICOM " "negotiation to certain SOPClassUID or to present uncommon SOPClassUID during " - "the DICOM negotation. By default, " + "the DICOM negotiation. By default, " "Orhanc will propose the most 120 common SOPClassUIDs.", true) .SetRequestField(KEY_QUERY, RestApiCallDocumentation::Type_JsonObject, "A query object identifying all the DICOM resources to be retrieved", true)
--- a/OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp Fri Mar 07 17:25:34 2025 +0100 +++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp Tue Mar 11 10:47:31 2025 +0100 @@ -946,7 +946,7 @@ } else { - // if present and not explicitely set to false + // if present and not explicitly set to false if (call.HasArgument("returnUnsupportedImage") && call.GetBooleanArgument("returnUnsupportedImage", true)) { std::string root = "";
--- a/TODO Fri Mar 07 17:25:34 2025 +0100 +++ b/TODO Tue Mar 11 10:47:31 2025 +0100 @@ -36,7 +36,7 @@ For a DICOM Transfer, that would be nice to include the modality in the context + a study identifier or a job id. * (1) Accept extra DICOM tags dictionaries in the DCMTK format '.dic' (easier to use than declare them in the Orthanc configuration file). Even the standard dictionaries could be - overriden by these custom dictionaries. + overridden by these custom dictionaries. * Add configurations to enable/disable warnings: - Modifying an instance while keeping its original SOPInstanceUID: This should be avoided! - Modifying a study while keeping its original StudyInstanceUID: This should be avoided! @@ -206,6 +206,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. ---------