Mercurial > hg > orthanc
changeset 4640:66109d24d26e
"ETag" headers for metadata and attachments now allow strong comparison (MD5 is included)
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Mon, 26 Apr 2021 15:22:44 +0200 |
parents | c638dd444de0 |
children | b02dc8303cf6 |
files | NEWS OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp OrthancServer/Sources/Database/StatelessDatabaseOperations.h OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp OrthancServer/Sources/ServerContext.cpp OrthancServer/Sources/ServerContext.h OrthancServer/Sources/ServerIndex.cpp OrthancServer/Sources/ServerIndex.h OrthancServer/Sources/ServerToolbox.cpp OrthancServer/Sources/main.cpp OrthancServer/UnitTestsSources/ServerIndexTests.cpp |
diffstat | 11 files changed, 206 insertions(+), 97 deletions(-) [+] |
line wrap: on
line diff
--- a/NEWS Thu Apr 22 13:27:57 2021 +0200 +++ b/NEWS Mon Apr 26 15:22:44 2021 +0200 @@ -1,6 +1,8 @@ Pending changes in the mainline =============================== +* "ETag" headers for metadata and attachments now allow strong comparison (MD5 is included) + Version 1.9.2 (2021-04-22) ==========================
--- a/OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp Thu Apr 22 13:27:57 2021 +0200 +++ b/OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp Mon Apr 26 15:22:44 2021 +0200 @@ -2199,7 +2199,8 @@ MetadataType type, const std::string& value, bool hasOldRevision, - int64_t oldRevision) + int64_t oldRevision, + const std::string& oldMD5) { class Operations : public IReadWriteOperations { @@ -2210,6 +2211,7 @@ const std::string& value_; bool hasOldRevision_; int64_t oldRevision_; + const std::string& oldMD5_; public: Operations(int64_t& newRevision, @@ -2217,13 +2219,15 @@ MetadataType type, const std::string& value, bool hasOldRevision, - int64_t oldRevision) : + int64_t oldRevision, + const std::string& oldMD5) : newRevision_(newRevision), publicId_(publicId), type_(type), value_(value), hasOldRevision_(hasOldRevision), - oldRevision_(oldRevision) + oldRevision_(oldRevision), + oldMD5_(oldMD5) { } @@ -2241,15 +2245,19 @@ int64_t expectedRevision; if (transaction.LookupMetadata(oldValue, expectedRevision, id, type_)) { - if (hasOldRevision_ && - expectedRevision != oldRevision_) + if (hasOldRevision_) { - throw OrthancException(ErrorCode_Revision); + std::string expectedMD5; + Toolbox::ComputeMD5(expectedMD5, oldValue); + + if (expectedRevision != oldRevision_ || + expectedMD5 != oldMD5_) + { + throw OrthancException(ErrorCode_Revision); + } } - else - { - newRevision_ = expectedRevision + 1; - } + + newRevision_ = expectedRevision + 1; } else { @@ -2268,7 +2276,7 @@ } }; - Operations operations(newRevision, publicId, type, value, hasOldRevision, oldRevision); + Operations operations(newRevision, publicId, type, value, hasOldRevision, oldRevision, oldMD5); Apply(operations); } @@ -2278,14 +2286,15 @@ const std::string& value) { int64_t newRevision; // Unused - SetMetadata(newRevision, publicId, type, value, false /* no old revision */, -1 /* dummy */); + SetMetadata(newRevision, publicId, type, value, false /* no old revision */, -1 /* dummy */, "" /* dummy */); } bool StatelessDatabaseOperations::DeleteMetadata(const std::string& publicId, MetadataType type, bool hasRevision, - int64_t revision) + int64_t revision, + const std::string& md5) { class Operations : public IReadWriteOperations { @@ -2294,17 +2303,20 @@ MetadataType type_; bool hasRevision_; int64_t revision_; + const std::string& md5_; bool found_; public: Operations(const std::string& publicId, MetadataType type, bool hasRevision, - int64_t revision) : + int64_t revision, + const std::string& md5) : publicId_(publicId), type_(type), hasRevision_(hasRevision), revision_(revision), + md5_(md5), found_(false) { } @@ -2324,19 +2336,25 @@ } else { - std::string s; + std::string value; int64_t expectedRevision; - if (transaction.LookupMetadata(s, expectedRevision, id, type_)) + if (transaction.LookupMetadata(value, expectedRevision, id, type_)) { - if (hasRevision_ && - expectedRevision != revision_) + if (hasRevision_) { - throw OrthancException(ErrorCode_Revision); + std::string expectedMD5; + Toolbox::ComputeMD5(expectedMD5, value); + + if (expectedRevision != revision_ || + expectedMD5 != md5_) + { + throw OrthancException(ErrorCode_Revision); + } } found_ = true; transaction.DeleteMetadata(id, type_); - + if (IsUserMetadata(type_)) { transaction.LogChange(id, ChangeType_UpdatedMetadata, resourceType, publicId_); @@ -2350,7 +2368,7 @@ } }; - Operations operations(publicId, type, hasRevision, revision); + Operations operations(publicId, type, hasRevision, revision, md5); Apply(operations); return operations.HasFound(); } @@ -2485,7 +2503,8 @@ bool StatelessDatabaseOperations::DeleteAttachment(const std::string& publicId, FileContentType type, bool hasRevision, - int64_t revision) + int64_t revision, + const std::string& md5) { class Operations : public IReadWriteOperations { @@ -2494,17 +2513,20 @@ FileContentType type_; bool hasRevision_; int64_t revision_; + const std::string& md5_; bool found_; public: Operations(const std::string& publicId, FileContentType type, bool hasRevision, - int64_t revision) : + int64_t revision, + const std::string& md5) : publicId_(publicId), type_(type), hasRevision_(hasRevision), revision_(revision), + md5_(md5), found_(false) { } @@ -2529,7 +2551,8 @@ if (transaction.LookupAttachment(info, expectedRevision, id, type_)) { if (hasRevision_ && - expectedRevision != revision_) + (expectedRevision != revision_ || + info.GetUncompressedMD5() != md5_)) { throw OrthancException(ErrorCode_Revision); } @@ -2550,7 +2573,7 @@ } }; - Operations operations(publicId, type, hasRevision, revision); + Operations operations(publicId, type, hasRevision, revision, md5); Apply(operations); return operations.HasFound(); } @@ -3261,7 +3284,8 @@ uint64_t maximumStorageSize, unsigned int maximumPatients, bool hasOldRevision, - int64_t oldRevision) + int64_t oldRevision, + const std::string& oldMD5) { class Operations : public IReadWriteOperations { @@ -3274,6 +3298,7 @@ unsigned int maximumPatientCount_; bool hasOldRevision_; int64_t oldRevision_; + const std::string& oldMD5_; public: Operations(int64_t& newRevision, @@ -3282,7 +3307,8 @@ uint64_t maximumStorageSize, unsigned int maximumPatientCount, bool hasOldRevision, - int64_t oldRevision) : + int64_t oldRevision, + const std::string& oldMD5) : newRevision_(newRevision), status_(StoreStatus_Failure), attachment_(attachment), @@ -3290,7 +3316,8 @@ maximumStorageSize_(maximumStorageSize), maximumPatientCount_(maximumPatientCount), hasOldRevision_(hasOldRevision), - oldRevision_(oldRevision) + oldRevision_(oldRevision), + oldMD5_(oldMD5) { } @@ -3316,7 +3343,8 @@ if (transaction.LookupAttachment(oldFile, expectedRevision, resourceId, attachment_.GetContentType())) { if (hasOldRevision_ && - expectedRevision != oldRevision_) + (expectedRevision != oldRevision_ || + oldFile.GetUncompressedMD5() != oldMD5_)) { throw OrthancException(ErrorCode_Revision); } @@ -3371,7 +3399,8 @@ }; - Operations operations(newRevision, attachment, publicId, maximumStorageSize, maximumPatients, hasOldRevision, oldRevision); + Operations operations(newRevision, attachment, publicId, maximumStorageSize, maximumPatients, + hasOldRevision, oldRevision, oldMD5); Apply(operations); return operations.GetStatus(); }
--- a/OrthancServer/Sources/Database/StatelessDatabaseOperations.h Thu Apr 22 13:27:57 2021 +0200 +++ b/OrthancServer/Sources/Database/StatelessDatabaseOperations.h Mon Apr 26 15:22:44 2021 +0200 @@ -580,7 +580,8 @@ MetadataType type, const std::string& value, bool hasOldRevision, - int64_t oldRevision); + int64_t oldRevision, + const std::string& oldMD5); // Same as "SetMetadata()", but doesn't care about revisions void OverwriteMetadata(const std::string& publicId, @@ -590,7 +591,8 @@ bool DeleteMetadata(const std::string& publicId, MetadataType type, bool hasRevision, - int64_t revision); + int64_t revision, + const std::string& md5); uint64_t IncrementGlobalSequence(GlobalProperty sequence, bool shared); @@ -606,7 +608,8 @@ bool DeleteAttachment(const std::string& publicId, FileContentType type, bool hasRevision, - int64_t revision); + int64_t revision, + const std::string& md5); void LogChange(int64_t internalId, ChangeType changeType, @@ -634,6 +637,7 @@ uint64_t maximumStorageSize, unsigned int maximumPatients, bool hasOldRevision, - int64_t oldRevision); + int64_t oldRevision, + const std::string& oldMd5); }; }
--- a/OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp Thu Apr 22 13:27:57 2021 +0200 +++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp Mon Apr 26 15:22:44 2021 +0200 @@ -1450,7 +1450,49 @@ } + static void SetStringContentETag(RestApiOutput& output, + int64_t revision, + const std::string& value) + { + std::string md5; + Toolbox::ComputeMD5(md5, value); + const std::string etag = "\"" + boost::lexical_cast<std::string>(revision) + "-" + md5 + "\""; + output.GetLowLevelOutput().AddHeader("ETag", etag); + } + + + static void SetBufferContentETag(RestApiOutput& output, + int64_t revision, + const void* data, + size_t size) + { + std::string md5; + Toolbox::ComputeMD5(md5, data, size); + const std::string etag = "\"" + boost::lexical_cast<std::string>(revision) + "-" + md5 + "\""; + output.GetLowLevelOutput().AddHeader("ETag", etag); + } + + + static void SetAttachmentETag(RestApiOutput& output, + int64_t revision, + const FileInfo& info) + { + const std::string etag = ("\"" + boost::lexical_cast<std::string>(revision) + "-" + + info.GetUncompressedMD5() + "\""); + output.GetLowLevelOutput().AddHeader("ETag", etag); + } + + + static std::string GetMD5(const std::string& value) + { + std::string md5; + Toolbox::ComputeMD5(md5, value); + return md5; + } + + static bool GetRevisionHeader(int64_t& revision /* out */, + std::string& md5 /* out */, const RestApiCall& call, const std::string& header) { @@ -1469,14 +1511,20 @@ try { - revision = boost::lexical_cast<int64_t>(value); - return true; + size_t comma = value.find('-'); + if (comma != std::string::npos) + { + revision = boost::lexical_cast<int64_t>(value.substr(0, comma)); + md5 = value.substr(comma + 1); + return true; + } } catch (boost::bad_lexical_cast&) { - throw OrthancException(ErrorCode_ParameterOutOfRange, "The \"" + header + - "\" HTTP header should contain the revision as an integer, but found: " + value); } + + throw OrthancException(ErrorCode_ParameterOutOfRange, "The \"" + header + + "\" HTTP header should contain the ETag (revision followed by MD5 hash), but found: " + value); } } @@ -1510,12 +1558,13 @@ int64_t revision; if (OrthancRestApi::GetIndex(call).LookupMetadata(value, revision, publicId, level, metadata)) { - call.GetOutput().GetLowLevelOutput(). - AddHeader("ETag", "\"" + boost::lexical_cast<std::string>(revision) + "\""); // New in Orthanc 1.9.2 + SetStringContentETag(call.GetOutput(), revision, value); // New in Orthanc 1.9.2 int64_t userRevision; - if (GetRevisionHeader(userRevision, call, "If-None-Match") && - revision == userRevision) + std::string userMD5; + if (GetRevisionHeader(userRevision, userMD5, call, "If-None-Match") && + userRevision == revision && + userMD5 == GetMD5(value)) { call.GetOutput().GetLowLevelOutput().SendStatus(HttpStatus_304_NotModified); } @@ -1555,9 +1604,10 @@ { bool found; int64_t revision; - if (GetRevisionHeader(revision, call, "if-match")) + std::string md5; + if (GetRevisionHeader(revision, md5, call, "if-match")) { - found = OrthancRestApi::GetIndex(call).DeleteMetadata(publicId, metadata, true, revision); + found = OrthancRestApi::GetIndex(call).DeleteMetadata(publicId, metadata, true, revision, md5); } else { @@ -1569,7 +1619,7 @@ } else { - found = OrthancRestApi::GetIndex(call).DeleteMetadata(publicId, metadata, false, -1 /* dummy value */); + found = OrthancRestApi::GetIndex(call).DeleteMetadata(publicId, metadata, false, -1 /* dummy value */, ""); } } @@ -1619,7 +1669,8 @@ if (IsUserMetadata(metadata)) // It is forbidden to modify internal metadata { int64_t oldRevision; - bool hasOldRevision = GetRevisionHeader(oldRevision, call, "if-match"); + std::string oldMD5; + bool hasOldRevision = GetRevisionHeader(oldRevision, oldMD5, call, "if-match"); if (!hasOldRevision) { @@ -1631,15 +1682,15 @@ // inexistent as expected hasOldRevision = true; oldRevision = -1; // dummy value + oldMD5.clear(); // dummy value } } int64_t newRevision; - OrthancRestApi::GetIndex(call).SetMetadata(newRevision, publicId, metadata, value, hasOldRevision, oldRevision); - - call.GetOutput().GetLowLevelOutput(). - AddHeader("ETag", "\"" + boost::lexical_cast<std::string>(newRevision) + "\""); // New in Orthanc 1.9.2 - + OrthancRestApi::GetIndex(call).SetMetadata(newRevision, publicId, metadata, value, + hasOldRevision, oldRevision, oldMD5); + + SetStringContentETag(call.GetOutput(), newRevision, value); // New in Orthanc 1.9.2 call.GetOutput().AnswerBuffer("", MimeType_PlainText); } else @@ -1709,12 +1760,13 @@ int64_t revision; if (OrthancRestApi::GetIndex(call).LookupAttachment(info, revision, publicId, contentType)) { - call.GetOutput().GetLowLevelOutput(). - AddHeader("ETag", "\"" + boost::lexical_cast<std::string>(revision) + "\""); // New in Orthanc 1.9.2 + SetAttachmentETag(call.GetOutput(), revision, info); // New in Orthanc 1.9.2 int64_t userRevision; - if (GetRevisionHeader(userRevision, call, "If-None-Match") && - revision == userRevision) + std::string userMD5; + if (GetRevisionHeader(userRevision, userMD5, call, "If-None-Match") && + revision == userRevision && + info.GetUncompressedMD5() == userMD5) { call.GetOutput().GetLowLevelOutput().SendStatus(HttpStatus_304_NotModified); return false; @@ -1810,33 +1862,34 @@ std::string publicId = call.GetUriComponent("id", ""); FileContentType type = StringToContentType(call.GetUriComponent("name", "")); - if (uncompress) + FileInfo info; + if (GetAttachmentInfo(info, call)) { - FileInfo info; - if (GetAttachmentInfo(info, call)) + // NB: "SetAttachmentETag()" is already invoked by "GetAttachmentInfo()" + + if (uncompress) { context.AnswerAttachment(call.GetOutput(), publicId, type); } - } - else - { - // Return the raw data (possibly compressed), as stored on the filesystem - std::string content; - int64_t revision; - context.ReadAttachment(content, revision, publicId, type, false); - - call.GetOutput().GetLowLevelOutput(). - AddHeader("ETag", "\"" + boost::lexical_cast<std::string>(revision) + "\""); // New in Orthanc 1.9.2 - - int64_t userRevision; - if (GetRevisionHeader(userRevision, call, "If-None-Match") && - revision == userRevision) - { - call.GetOutput().GetLowLevelOutput().SendStatus(HttpStatus_304_NotModified); - } else { - call.GetOutput().AnswerBuffer(content, MimeType_Binary); + // Return the raw data (possibly compressed), as stored on the filesystem + std::string content; + int64_t revision; + context.ReadAttachment(content, revision, publicId, type, false); + + int64_t userRevision; + std::string userMD5; + if (GetRevisionHeader(userRevision, userMD5, call, "If-None-Match") && + revision == userRevision && + info.GetUncompressedMD5() == userMD5) + { + call.GetOutput().GetLowLevelOutput().SendStatus(HttpStatus_304_NotModified); + } + else + { + call.GetOutput().AnswerBuffer(content, MimeType_Binary); + } } } } @@ -2037,7 +2090,8 @@ if (IsUserContentType(contentType)) // It is forbidden to modify internal attachments { int64_t oldRevision; - bool hasOldRevision = GetRevisionHeader(oldRevision, call, "if-match"); + std::string oldMD5; + bool hasOldRevision = GetRevisionHeader(oldRevision, oldMD5, call, "if-match"); if (!hasOldRevision) { @@ -2049,16 +2103,15 @@ // inexistent as expected hasOldRevision = true; oldRevision = -1; // dummy value + oldMD5.clear(); // dummy value } } int64_t newRevision; context.AddAttachment(newRevision, publicId, StringToContentType(name), call.GetBodyData(), - call.GetBodySize(), hasOldRevision, oldRevision); - - call.GetOutput().GetLowLevelOutput(). - AddHeader("ETag", "\"" + boost::lexical_cast<std::string>(newRevision) + "\""); // New in Orthanc 1.9.2 - + call.GetBodySize(), hasOldRevision, oldRevision, oldMD5); + + SetBufferContentETag(call.GetOutput(), newRevision, call.GetBodyData(), call.GetBodySize()); // New in Orthanc 1.9.2 call.GetOutput().AnswerBuffer("{}", MimeType_Json); } else @@ -2119,9 +2172,10 @@ { bool found; int64_t revision; - if (GetRevisionHeader(revision, call, "if-match")) + std::string md5; + if (GetRevisionHeader(revision, md5, call, "if-match")) { - found = OrthancRestApi::GetIndex(call).DeleteAttachment(publicId, contentType, true, revision); + found = OrthancRestApi::GetIndex(call).DeleteAttachment(publicId, contentType, true, revision, md5); } else { @@ -2133,7 +2187,8 @@ } else { - found = OrthancRestApi::GetIndex(call).DeleteAttachment(publicId, contentType, false, -1 /* dummy value */); + found = OrthancRestApi::GetIndex(call).DeleteAttachment(publicId, contentType, + false, -1 /* dummy value */, "" /* dummy value */); } } @@ -2986,7 +3041,8 @@ for (std::list<std::string>::const_iterator instance = instances.begin(); instance != instances.end(); ++instance) { - index.DeleteAttachment(*instance, FileContentType_DicomAsJson, false /* no revision checks */, -1 /* dummy */); + index.DeleteAttachment(*instance, FileContentType_DicomAsJson, + false /* no revision checks */, -1 /* dummy */, "" /* dummy */); } }
--- a/OrthancServer/Sources/ServerContext.cpp Thu Apr 22 13:27:57 2021 +0200 +++ b/OrthancServer/Sources/ServerContext.cpp Mon Apr 26 15:22:44 2021 +0200 @@ -799,7 +799,8 @@ try { int64_t newRevision; // ignored - StoreStatus status = index_.AddAttachment(newRevision, modified, resourceId, true, revision); + StoreStatus status = index_.AddAttachment(newRevision, modified, resourceId, + true, revision, modified.GetUncompressedMD5()); if (status != StoreStatus_Success) { accessor.Remove(modified); @@ -986,7 +987,7 @@ int64_t newRevision; AddAttachment(newRevision, instancePublicId, FileContentType_DicomUntilPixelData, dicom.empty() ? NULL: dicom.c_str(), pixelDataOffset, - false /* no old revision */, -1 /* dummy revision */); + false /* no old revision */, -1 /* dummy revision */, "" /* dummy MD5 */); } } } @@ -1162,7 +1163,8 @@ const void* data, size_t size, bool hasOldRevision, - int64_t oldRevision) + int64_t oldRevision, + const std::string& oldMD5) { LOG(INFO) << "Adding attachment " << EnumerationToString(attachmentType) << " to resource " << resourceId; @@ -1172,7 +1174,8 @@ StorageAccessor accessor(area_, GetMetricsRegistry()); FileInfo attachment = accessor.Write(data, size, attachmentType, compression, storeMD5_); - StoreStatus status = index_.AddAttachment(newRevision, attachment, resourceId, hasOldRevision, oldRevision); + StoreStatus status = index_.AddAttachment( + newRevision, attachment, resourceId, hasOldRevision, oldRevision, oldMD5); if (status != StoreStatus_Success) { accessor.Remove(attachment);
--- a/OrthancServer/Sources/ServerContext.h Thu Apr 22 13:27:57 2021 +0200 +++ b/OrthancServer/Sources/ServerContext.h Mon Apr 26 15:22:44 2021 +0200 @@ -301,7 +301,8 @@ const void* data, size_t size, bool hasOldRevision, - int64_t oldRevision); + int64_t oldRevision, + const std::string& oldMD5); StoreStatus Store(std::string& resultPublicId, DicomInstanceToStore& dicom,
--- a/OrthancServer/Sources/ServerIndex.cpp Thu Apr 22 13:27:57 2021 +0200 +++ b/OrthancServer/Sources/ServerIndex.cpp Mon Apr 26 15:22:44 2021 +0200 @@ -550,7 +550,8 @@ const FileInfo& attachment, const std::string& publicId, bool hasOldRevision, - int64_t oldRevision) + int64_t oldRevision, + const std::string& oldMD5) { uint64_t maximumStorageSize; unsigned int maximumPatients; @@ -562,6 +563,7 @@ } return StatelessDatabaseOperations::AddAttachment( - newRevision, attachment, publicId, maximumStorageSize, maximumPatients, hasOldRevision, oldRevision); + newRevision, attachment, publicId, maximumStorageSize, maximumPatients, + hasOldRevision, oldRevision, oldMD5); } }
--- a/OrthancServer/Sources/ServerIndex.h Thu Apr 22 13:27:57 2021 +0200 +++ b/OrthancServer/Sources/ServerIndex.h Mon Apr 26 15:22:44 2021 +0200 @@ -101,6 +101,7 @@ const FileInfo& attachment, const std::string& publicId, bool hasOldRevision, - int64_t oldRevision); + int64_t oldRevision, + const std::string& oldMD5); }; }
--- a/OrthancServer/Sources/ServerToolbox.cpp Thu Apr 22 13:27:57 2021 +0200 +++ b/OrthancServer/Sources/ServerToolbox.cpp Mon Apr 26 15:22:44 2021 +0200 @@ -295,8 +295,8 @@ ServerContext::DicomCacheLocker locker(context, *it); // Delay the reconstruction of DICOM-as-JSON to its next access through "ServerContext" - context.GetIndex().DeleteAttachment( - *it, FileContentType_DicomAsJson, false /* no revision */, -1 /* dummy revision */); + context.GetIndex().DeleteAttachment(*it, FileContentType_DicomAsJson, false /* no revision */, + -1 /* dummy revision */, "" /* dummy MD5 */); context.GetIndex().ReconstructInstance(locker.GetDicom()); }
--- a/OrthancServer/Sources/main.cpp Thu Apr 22 13:27:57 2021 +0200 +++ b/OrthancServer/Sources/main.cpp Mon Apr 26 15:22:44 2021 +0200 @@ -1493,6 +1493,7 @@ static const char* const CHECK_REVISIONS = "CheckRevisions"; OrthancConfiguration::ReaderLock lock; + if (lock.GetConfiguration().GetBooleanParameter(CHECK_REVISIONS, false)) { if (database.HasRevisionsSupport()) @@ -1505,6 +1506,16 @@ LOG(WARNING) << "The custom database back-end has *no* support for revisions of metadata and attachments, " << "but configuration option \"" << CHECK_REVISIONS << "\" is set to \"true\""; } + + static const char* const STORE_MD5 = "StoreMD5ForAttachments"; + + if (!lock.GetConfiguration().GetBooleanParameter(STORE_MD5, true)) + { + throw OrthancException( + ErrorCode_ParameterOutOfRange, + "The revision system is enabled by configuration option \"" + std::string(CHECK_REVISIONS) + + "\", but won't work properly for attachments if \"" + std::string(STORE_MD5) + "\" is set to \"false\""); + } } }
--- a/OrthancServer/UnitTestsSources/ServerIndexTests.cpp Thu Apr 22 13:27:57 2021 +0200 +++ b/OrthancServer/UnitTestsSources/ServerIndexTests.cpp Mon Apr 26 15:22:44 2021 +0200 @@ -786,7 +786,7 @@ { FileInfo info(Toolbox::GenerateUuid(), FileContentType_Dicom, 1, "md5"); int64_t revision = -1; - index.AddAttachment(revision, info, ids[i], false /* no previous revision */, -1); + index.AddAttachment(revision, info, ids[i], false /* no previous revision */, -1, ""); ASSERT_EQ(0, revision); index.GetGlobalStatistics(diskSize, uncompressedSize, countPatients,