changeset 693:01d8611c4a60

md5 for attached files
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 04 Feb 2014 17:52:51 +0100
parents 1a3f9d90a2dd
children 72dc919a028c
files CMakeLists.txt Core/Enumerations.h Core/FileStorage/CompressedFileStorageAccessor.cpp Core/FileStorage/FileInfo.h Core/FileStorage/FileStorageAccessor.cpp Core/FileStorage/StorageAccessor.h Core/OrthancException.cpp Core/Toolbox.cpp Core/Toolbox.h OrthancServer/DatabaseWrapper.cpp OrthancServer/PrepareDatabase.sql OrthancServer/Upgrade3To4.sql UnitTestsSources/ServerIndex.cpp
diffstat 13 files changed, 140 insertions(+), 27 deletions(-) [+]
line wrap: on
line diff
--- 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
   )
--- 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
   };
 
   /**
--- 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:
--- 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_;
+    }
   };
 }
--- 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);
   }
 }
--- 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)
--- 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 "???";
--- 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<const md5_byte_t*>(&data[0]), 
-                 static_cast<int>(data.size()));
+      md5_append(&state, 
+                 reinterpret_cast<const md5_byte_t*>(data), 
+                 static_cast<int>(length));
     }
 
     md5_byte_t actualHash[16];
--- 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);
 
--- 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<FileContentType>(context.GetIntValue(1)),
                       static_cast<uint64_t>(context.GetInt64Value(2)),
+                      context.GetStringValue(5),
                       static_cast<CompressionType>(context.GetIntValue(3)),
-                      static_cast<uint64_t>(context.GetInt64Value(4)));
+                      static_cast<uint64_t>(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<CompressionType>(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<unsigned int>(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);
     }
 
--- 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");
--- /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"
--- 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<std::string>(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<std::string>(i)));
     ASSERT_FALSE(index.IsProtectedPatient(patients[i]));
   }
 
@@ -352,7 +364,8 @@
   {
     std::string p = "Patient " + boost::lexical_cast<std::string>(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<std::string>(i)));
     ASSERT_FALSE(index.IsProtectedPatient(patients[i]));
   }