# HG changeset patch # User Sebastien Jodogne # Date 1391532771 -3600 # Node ID 01d8611c4a60b7b4fc1f382f5954ea59e945915a # Parent 1a3f9d90a2ddba124da74f08e38edd07ce53e7d4 md5 for attached files diff -r 1a3f9d90a2dd -r 01d8611c4a60 CMakeLists.txt --- a/CMakeLists.txt Tue Feb 04 16:57:34 2014 +0100 +++ b/CMakeLists.txt Tue Feb 04 17:52:51 2014 +0100 @@ -93,6 +93,7 @@ # Prepare the embedded files set(EMBEDDED_FILES PREPARE_DATABASE ${CMAKE_CURRENT_SOURCE_DIR}/OrthancServer/PrepareDatabase.sql + UPGRADE_3_TO_4 ${CMAKE_CURRENT_SOURCE_DIR}/OrthancServer/Upgrade3To4.sql CONFIGURATION_SAMPLE ${CMAKE_CURRENT_SOURCE_DIR}/Resources/Configuration.json LUA_TOOLBOX ${CMAKE_CURRENT_SOURCE_DIR}/Resources/Toolbox.lua ) diff -r 1a3f9d90a2dd -r 01d8611c4a60 Core/Enumerations.h --- a/Core/Enumerations.h Tue Feb 04 16:57:34 2014 +0100 +++ b/Core/Enumerations.h Tue Feb 04 17:52:51 2014 +0100 @@ -66,7 +66,8 @@ ErrorCode_Timeout, ErrorCode_UnknownResource, ErrorCode_IncompatibleDatabaseVersion, - ErrorCode_FullStorage + ErrorCode_FullStorage, + ErrorCode_CorruptedFile }; /** diff -r 1a3f9d90a2dd -r 01d8611c4a60 Core/FileStorage/CompressedFileStorageAccessor.cpp --- a/Core/FileStorage/CompressedFileStorageAccessor.cpp Tue Feb 04 16:57:34 2014 +0100 +++ b/Core/FileStorage/CompressedFileStorageAccessor.cpp Tue Feb 04 17:52:51 2014 +0100 @@ -42,21 +42,36 @@ size_t size, FileContentType type) { + std::string md5; + + if (storeMD5_) + { + Toolbox::ComputeMD5(md5, data, size); + } + switch (compressionType_) { case CompressionType_None: { std::string uuid = storage_.Create(data, size); - return FileInfo(uuid, type, size); + return FileInfo(uuid, type, size, md5); } case CompressionType_Zlib: { std::string compressed; zlib_.Compress(compressed, data, size); + + std::string compressedMD5; + + if (storeMD5_) + { + Toolbox::ComputeMD5(compressedMD5, compressed); + } + std::string uuid = storage_.Create(compressed); - return FileInfo(uuid, type, size, - CompressionType_Zlib, compressed.size()); + return FileInfo(uuid, type, size, md5, + CompressionType_Zlib, compressed.size(), compressedMD5); } default: diff -r 1a3f9d90a2dd -r 01d8611c4a60 Core/FileStorage/FileInfo.h --- a/Core/FileStorage/FileInfo.h Tue Feb 04 16:57:34 2014 +0100 +++ b/Core/FileStorage/FileInfo.h Tue Feb 04 17:52:51 2014 +0100 @@ -43,9 +43,13 @@ private: std::string uuid_; FileContentType contentType_; + uint64_t uncompressedSize_; + std::string uncompressedMD5_; + CompressionType compressionType_; uint64_t compressedSize_; + std::string compressedMD5_; public: FileInfo() @@ -57,12 +61,15 @@ **/ FileInfo(const std::string& uuid, FileContentType contentType, - uint64_t size) : + uint64_t size, + const std::string& md5) : uuid_(uuid), contentType_(contentType), uncompressedSize_(size), + uncompressedMD5_(md5), compressionType_(CompressionType_None), - compressedSize_(size) + compressedSize_(size), + compressedMD5_(md5) { } @@ -72,13 +79,17 @@ FileInfo(const std::string& uuid, FileContentType contentType, uint64_t uncompressedSize, + const std::string& uncompressedMD5, CompressionType compressionType, - uint64_t compressedSize) : + uint64_t compressedSize, + const std::string& compressedMD5) : uuid_(uuid), contentType_(contentType), uncompressedSize_(uncompressedSize), + uncompressedMD5_(uncompressedMD5), compressionType_(compressionType), - compressedSize_(compressedSize) + compressedSize_(compressedSize), + compressedMD5_(compressedMD5) { } @@ -106,5 +117,15 @@ { return compressedSize_; } + + const std::string& GetCompressedMD5() const + { + return compressedMD5_; + } + + const std::string& GetUncompressedMD5() const + { + return uncompressedMD5_; + } }; } diff -r 1a3f9d90a2dd -r 01d8611c4a60 Core/FileStorage/FileStorageAccessor.cpp --- a/Core/FileStorage/FileStorageAccessor.cpp Tue Feb 04 16:57:34 2014 +0100 +++ b/Core/FileStorage/FileStorageAccessor.cpp Tue Feb 04 17:52:51 2014 +0100 @@ -38,6 +38,13 @@ size_t size, FileContentType type) { - return FileInfo(storage_.Create(data, size), type, size); + std::string md5; + + if (storeMD5_) + { + Toolbox::ComputeMD5(md5, data, size); + } + + return FileInfo(storage_.Create(data, size), type, size, md5); } } diff -r 1a3f9d90a2dd -r 01d8611c4a60 Core/FileStorage/StorageAccessor.h --- a/Core/FileStorage/StorageAccessor.h Tue Feb 04 16:57:34 2014 +0100 +++ b/Core/FileStorage/StorageAccessor.h Tue Feb 04 17:52:51 2014 +0100 @@ -45,15 +45,32 @@ class StorageAccessor : boost::noncopyable { protected: + bool storeMD5_; + virtual FileInfo WriteInternal(const void* data, size_t size, FileContentType type) = 0; public: + StorageAccessor() + { + storeMD5_ = true; + } + virtual ~StorageAccessor() { } + void SetStoreMD5(bool storeMD5) + { + storeMD5_ = storeMD5; + } + + bool IsStoreMD5() const + { + return storeMD5_; + } + FileInfo Write(const void* data, size_t size, FileContentType type) diff -r 1a3f9d90a2dd -r 01d8611c4a60 Core/OrthancException.cpp --- a/Core/OrthancException.cpp Tue Feb 04 16:57:34 2014 +0100 +++ b/Core/OrthancException.cpp Tue Feb 04 17:52:51 2014 +0100 @@ -105,6 +105,9 @@ case ErrorCode_NetworkProtocol: return "Error in the network protocol"; + case ErrorCode_CorruptedFile: + return "Corrupted file (inconsistent MD5 hash)"; + case ErrorCode_Custom: default: return "???"; diff -r 1a3f9d90a2dd -r 01d8611c4a60 Core/Toolbox.cpp --- a/Core/Toolbox.cpp Tue Feb 04 16:57:34 2014 +0100 +++ b/Core/Toolbox.cpp Tue Feb 04 17:52:51 2014 +0100 @@ -465,13 +465,29 @@ void Toolbox::ComputeMD5(std::string& result, const std::string& data) { + if (data.size() > 0) + { + ComputeMD5(result, &data[0], data.size()); + } + else + { + ComputeMD5(result, NULL, 0); + } + } + + + void Toolbox::ComputeMD5(std::string& result, + const void* data, + size_t length) + { md5_state_s state; md5_init(&state); - if (data.size() > 0) + if (length > 0) { - md5_append(&state, reinterpret_cast(&data[0]), - static_cast(data.size())); + md5_append(&state, + reinterpret_cast(data), + static_cast(length)); } md5_byte_t actualHash[16]; diff -r 1a3f9d90a2dd -r 01d8611c4a60 Core/Toolbox.h --- a/Core/Toolbox.h Tue Feb 04 16:57:34 2014 +0100 +++ b/Core/Toolbox.h Tue Feb 04 17:52:51 2014 +0100 @@ -88,6 +88,10 @@ void ComputeMD5(std::string& result, const std::string& data); + void ComputeMD5(std::string& result, + const void* data, + size_t length); + void ComputeSHA1(std::string& result, const std::string& data); diff -r 1a3f9d90a2dd -r 01d8611c4a60 OrthancServer/DatabaseWrapper.cpp --- a/OrthancServer/DatabaseWrapper.cpp Tue Feb 04 16:57:34 2014 +0100 +++ b/OrthancServer/DatabaseWrapper.cpp Tue Feb 04 17:52:51 2014 +0100 @@ -62,7 +62,7 @@ virtual unsigned int GetCardinality() const { - return 5; + return 7; } virtual void Compute(SQLite::FunctionContext& context) @@ -70,8 +70,10 @@ FileInfo info(context.GetStringValue(0), static_cast(context.GetIntValue(1)), static_cast(context.GetInt64Value(2)), + context.GetStringValue(5), static_cast(context.GetIntValue(3)), - static_cast(context.GetInt64Value(4))); + static_cast(context.GetInt64Value(4)), + context.GetStringValue(6)); listener_.SignalFileDeleted(info); } @@ -433,13 +435,15 @@ void DatabaseWrapper::AddAttachment(int64_t id, const FileInfo& attachment) { - SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO AttachedFiles VALUES(?, ?, ?, ?, ?, ?)"); + SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO AttachedFiles VALUES(?, ?, ?, ?, ?, ?, ?, ?)"); s.BindInt64(0, id); s.BindInt(1, attachment.GetContentType()); s.BindString(2, attachment.GetUuid()); s.BindInt64(3, attachment.GetCompressedSize()); s.BindInt64(4, attachment.GetUncompressedSize()); s.BindInt(5, attachment.GetCompressionType()); + s.BindString(6, attachment.GetUncompressedMD5()); + s.BindString(7, attachment.GetCompressedMD5()); s.Run(); } @@ -463,7 +467,7 @@ FileContentType contentType) { SQLite::Statement s(db_, SQLITE_FROM_HERE, - "SELECT uuid, uncompressedSize, compressionType, compressedSize FROM AttachedFiles WHERE id=? AND fileType=?"); + "SELECT uuid, uncompressedSize, compressionType, compressedSize, uncompressedMD5, compressedMD5 FROM AttachedFiles WHERE id=? AND fileType=?"); s.BindInt64(0, id); s.BindInt(1, contentType); @@ -476,8 +480,10 @@ attachment = FileInfo(s.ColumnString(0), contentType, s.ColumnInt(1), + s.ColumnString(4), static_cast(s.ColumnInt(2)), - s.ColumnInt(3)); + s.ColumnInt(3), + s.ColumnString(5)); return true; } } @@ -820,9 +826,11 @@ LOG(INFO) << "Version of the Orthanc database: " << version; unsigned int v = boost::lexical_cast(version); - // This version of Orthanc is only compatible with version 3 of - // the DB schema (since Orthanc 0.3.2) - ok = (v == 3); + // Version 3: from Orthanc 0.3.2 to Orthanc 0.7.2 (inclusive) + // Version 4: from Orthanc 0.7.3 (inclusive) + + // This version of Orthanc is only compatible with version 4 of the DB schema + ok = (v == 4); } catch (boost::bad_lexical_cast&) { @@ -830,6 +838,7 @@ if (!ok) { + LOG(ERROR) << "Incompatible version of the Orthanc database: " << version; throw OrthancException(ErrorCode_IncompatibleDatabaseVersion); } diff -r 1a3f9d90a2dd -r 01d8611c4a60 OrthancServer/PrepareDatabase.sql --- a/OrthancServer/PrepareDatabase.sql Tue Feb 04 16:57:34 2014 +0100 +++ b/OrthancServer/PrepareDatabase.sql Tue Feb 04 17:52:51 2014 +0100 @@ -32,6 +32,8 @@ compressedSize INTEGER, uncompressedSize INTEGER, compressionType INTEGER, + uncompressedMD5 TEXT, -- New in Orthanc 0.7.3 (database v4) + compressedMD5 TEXT, -- New in Orthanc 0.7.3 (database v4) PRIMARY KEY(id, fileType) ); @@ -75,7 +77,8 @@ AFTER DELETE ON AttachedFiles BEGIN SELECT SignalFileDeleted(old.uuid, old.fileType, old.uncompressedSize, - old.compressionType, old.compressedSize); + old.compressionType, old.compressedSize, + old.uncompressedMD5, old.compressedMD5); END; CREATE TRIGGER ResourceDeleted @@ -103,4 +106,4 @@ -- Set the version of the database schema -- The "1" corresponds to the "GlobalProperty_DatabaseSchemaVersion" enumeration -INSERT INTO GlobalProperties VALUES (1, "3"); +INSERT INTO GlobalProperties VALUES (1, "4"); diff -r 1a3f9d90a2dd -r 01d8611c4a60 OrthancServer/Upgrade3To4.sql --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancServer/Upgrade3To4.sql Tue Feb 04 17:52:51 2014 +0100 @@ -0,0 +1,3 @@ +-- Add 2 columns at "AttachedFiles" + +-- Delete & recreate trigger "AttachedFileDeleted" diff -r 1a3f9d90a2dd -r 01d8611c4a60 UnitTestsSources/ServerIndex.cpp --- a/UnitTestsSources/ServerIndex.cpp Tue Feb 04 16:57:34 2014 +0100 +++ b/UnitTestsSources/ServerIndex.cpp Tue Feb 04 17:52:51 2014 +0100 @@ -140,9 +140,10 @@ index.ListAvailableMetadata(md, a[4]); ASSERT_EQ(0u, md.size()); - index.AddAttachment(a[4], FileInfo("my json file", FileContentType_Json, 42, CompressionType_Zlib, 21)); - index.AddAttachment(a[4], FileInfo("my dicom file", FileContentType_Dicom, 42)); - index.AddAttachment(a[6], FileInfo("world", FileContentType_Dicom, 44)); + index.AddAttachment(a[4], FileInfo("my json file", FileContentType_Json, 42, "md5", + CompressionType_Zlib, 21, "compressedMD5")); + index.AddAttachment(a[4], FileInfo("my dicom file", FileContentType_Dicom, 42, "md5")); + index.AddAttachment(a[6], FileInfo("world", FileContentType_Dicom, 44, "md5")); index.SetMetadata(a[4], MetadataType_Instance_RemoteAet, "PINNACLE"); index.ListAvailableMetadata(md, a[4]); @@ -185,9 +186,19 @@ ASSERT_TRUE(index.LookupAttachment(att, a[4], FileContentType_Json)); ASSERT_EQ("my json file", att.GetUuid()); ASSERT_EQ(21u, att.GetCompressedSize()); + ASSERT_EQ("md5", att.GetUncompressedMD5()); + ASSERT_EQ("compressedMD5", att.GetCompressedMD5()); ASSERT_EQ(42u, att.GetUncompressedSize()); ASSERT_EQ(CompressionType_Zlib, att.GetCompressionType()); + ASSERT_TRUE(index.LookupAttachment(att, a[6], FileContentType_Dicom)); + ASSERT_EQ("world", att.GetUuid()); + ASSERT_EQ(44u, att.GetCompressedSize()); + ASSERT_EQ("md5", att.GetUncompressedMD5()); + ASSERT_EQ("md5", att.GetCompressedMD5()); + ASSERT_EQ(44u, att.GetUncompressedSize()); + ASSERT_EQ(CompressionType_None, att.GetCompressionType()); + ASSERT_EQ(0u, listener.deletedFiles_.size()); ASSERT_EQ(7u, index.GetTableRecordCount("Resources")); ASSERT_EQ(3u, index.GetTableRecordCount("AttachedFiles")); @@ -300,7 +311,8 @@ { std::string p = "Patient " + boost::lexical_cast(i); patients.push_back(index.CreateResource(p, ResourceType_Patient)); - index.AddAttachment(patients[i], FileInfo(p, FileContentType_Dicom, i + 10)); + index.AddAttachment(patients[i], FileInfo(p, FileContentType_Dicom, i + 10, + "md5-" + boost::lexical_cast(i))); ASSERT_FALSE(index.IsProtectedPatient(patients[i])); } @@ -352,7 +364,8 @@ { std::string p = "Patient " + boost::lexical_cast(i); patients.push_back(index.CreateResource(p, ResourceType_Patient)); - index.AddAttachment(patients[i], FileInfo(p, FileContentType_Dicom, i + 10)); + index.AddAttachment(patients[i], FileInfo(p, FileContentType_Dicom, i + 10, + "md5-" + boost::lexical_cast(i))); ASSERT_FALSE(index.IsProtectedPatient(patients[i])); }