changeset 1172:059391d3f8df db-changes

integration mainline->db-changes
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 22 Sep 2014 13:44:08 +0200
parents 1ea4094d077c (current diff) fd3128b2cf45 (diff)
children 3b372ab992ec
files CMakeLists.txt OrthancServer/OrthancRestApi/OrthancRestResources.cpp
diffstat 23 files changed, 421 insertions(+), 110 deletions(-) [+]
line wrap: on
line diff
--- a/AUTHORS	Wed Sep 17 13:25:37 2014 +0200
+++ b/AUTHORS	Mon Sep 22 13:44:08 2014 +0200
@@ -8,7 +8,7 @@
 * Sebastien Jodogne <s.jodogne@gmail.com>
   Department of Medical Physics, CHU of Liege, Belgium
 
-  Overall design and main developper.
+  Overall design and lead developer.
 
 
 Client library
--- a/Core/RestApi/RestApiOutput.cpp	Wed Sep 17 13:25:37 2014 +0200
+++ b/Core/RestApi/RestApiOutput.cpp	Mon Sep 22 13:44:08 2014 +0200
@@ -129,6 +129,7 @@
   void RestApiOutput::SignalError(HttpStatus status)
   {
     if (status != HttpStatus_403_Forbidden &&
+        status != HttpStatus_500_InternalServerError &&
         status != HttpStatus_415_UnsupportedMediaType)
     {
       throw OrthancException("This HTTP status is not allowed in a REST API");
--- a/NEWS	Wed Sep 17 13:25:37 2014 +0200
+++ b/NEWS	Mon Sep 22 13:44:08 2014 +0200
@@ -1,6 +1,14 @@
 Pending changes in the mainline
 ===============================
 
+
+Version 0.8.4 (2014/09/19)
+==========================
+
+* "/instances-tags" to get the tags of all the child instances of a
+  patient/study/series with a single REST call (bulk tags retrieval)
+* Configuration/Lua to select the accepted C-Store SCP transfer syntaxes
+* Fix reporting of errors in Orthanc Explorer when sending images to peers/modalities
 * Installation of plugin SDK in CMake
 
 
--- a/OrthancCppClient/SharedLibrary/AUTOGENERATED/ExternC.cpp	Wed Sep 17 13:25:37 2014 +0200
+++ b/OrthancCppClient/SharedLibrary/AUTOGENERATED/ExternC.cpp	Mon Sep 22 13:44:08 2014 +0200
@@ -1509,12 +1509,12 @@
 
   LAAW_EXPORT_DLL_API const char* LAAW_CALL_CONVENTION LAAW_EXTERNC_GetFileVersion()
   {
-    return "0.8.0.3";
+    return "0.8.0.4";
   }
 
   LAAW_EXPORT_DLL_API const char* LAAW_CALL_CONVENTION LAAW_EXTERNC_GetFullVersion()
   {
-    return "0.8.3";
+    return "0.8.4";
   }
 
   LAAW_EXPORT_DLL_API void LAAW_CALL_CONVENTION LAAW_EXTERNC_FreeString(char* str)
--- a/OrthancCppClient/SharedLibrary/AUTOGENERATED/Windows32.rc	Wed Sep 17 13:25:37 2014 +0200
+++ b/OrthancCppClient/SharedLibrary/AUTOGENERATED/Windows32.rc	Mon Sep 22 13:44:08 2014 +0200
@@ -1,7 +1,7 @@
 #include <winver.h>
 
 VS_VERSION_INFO VERSIONINFO
-   FILEVERSION 0,8,0,3
+   FILEVERSION 0,8,0,4
    PRODUCTVERSION 0,8,0,0
    FILEOS VOS_NT_WINDOWS32
    FILETYPE VFT_DLL
@@ -10,10 +10,10 @@
       BEGIN
          BLOCK "040904E4"
          BEGIN
-            VALUE "Comments", "Release 0.8.3"
+            VALUE "Comments", "Release 0.8.4"
             VALUE "CompanyName", "CHU of Liege"
             VALUE "FileDescription", "Native client to the REST API of Orthanc"
-            VALUE "FileVersion", "0.8.0.3"
+            VALUE "FileVersion", "0.8.0.4"
             VALUE "InternalName", "OrthancClient"
             VALUE "LegalCopyright", "(c) 2012-2014, Sebastien Jodogne, University Hospital of Liege"
             VALUE "LegalTrademarks", "Licensing information is available on https://code.google.com/p/orthanc/"
--- a/OrthancCppClient/SharedLibrary/AUTOGENERATED/Windows64.rc	Wed Sep 17 13:25:37 2014 +0200
+++ b/OrthancCppClient/SharedLibrary/AUTOGENERATED/Windows64.rc	Mon Sep 22 13:44:08 2014 +0200
@@ -1,7 +1,7 @@
 #include <winver.h>
 
 VS_VERSION_INFO VERSIONINFO
-   FILEVERSION 0,8,0,3
+   FILEVERSION 0,8,0,4
    PRODUCTVERSION 0,8,0,0
    FILEOS VOS_NT_WINDOWS32
    FILETYPE VFT_DLL
@@ -10,10 +10,10 @@
       BEGIN
          BLOCK "040904E4"
          BEGIN
-            VALUE "Comments", "Release 0.8.3"
+            VALUE "Comments", "Release 0.8.4"
             VALUE "CompanyName", "CHU of Liege"
             VALUE "FileDescription", "Native client to the REST API of Orthanc"
-            VALUE "FileVersion", "0.8.0.3"
+            VALUE "FileVersion", "0.8.0.4"
             VALUE "InternalName", "OrthancClient"
             VALUE "LegalCopyright", "(c) 2012-2014, Sebastien Jodogne, University Hospital of Liege"
             VALUE "LegalTrademarks", "Licensing information is available on https://code.google.com/p/orthanc/"
--- a/OrthancCppClient/SharedLibrary/Product.json	Wed Sep 17 13:25:37 2014 +0200
+++ b/OrthancCppClient/SharedLibrary/Product.json	Mon Sep 22 13:44:08 2014 +0200
@@ -4,5 +4,5 @@
   "Company" : "CHU of Liege",
   "Copyright" : "(c) 2012-2014, Sebastien Jodogne, University Hospital of Liege",
   "Legal" : "Licensing information is available on https://code.google.com/p/orthanc/",
-  "Version" : "0.8.3"
+  "Version" : "0.8.4"
 }
--- a/OrthancServer/DicomProtocol/IApplicationEntityFilter.h	Wed Sep 17 13:25:37 2014 +0200
+++ b/OrthancServer/DicomProtocol/IApplicationEntityFilter.h	Mon Sep 22 13:44:08 2014 +0200
@@ -51,5 +51,9 @@
     virtual bool IsAllowedRequest(const std::string& callingIp,
                                   const std::string& callingAet,
                                   DicomRequestType type) = 0;
+
+    virtual bool IsAllowedTransferSyntax(const std::string& callingIp,
+                                         const std::string& callingAet,
+                                         TransferSyntax syntax) = 0;
   };
 }
--- a/OrthancServer/Internals/CommandDispatcher.cpp	Wed Sep 17 13:25:37 2014 +0200
+++ b/OrthancServer/Internals/CommandDispatcher.cpp	Mon Sep 22 13:44:08 2014 +0200
@@ -459,7 +459,38 @@
         return NULL;
       }
 
-      LOG(INFO) << "Association Received";
+      // Retrieve the AET and the IP address of the remote modality
+      std::string callingAet;
+      std::string callingIp;
+      std::string calledAet;
+  
+      {
+        DIC_AE callingAet_C;
+        DIC_AE calledAet_C;
+        DIC_AE callingIp_C;
+        DIC_AE calledIP_C;
+        if (ASC_getAPTitles(assoc->params, callingAet_C, calledAet_C, NULL).bad() ||
+            ASC_getPresentationAddresses(assoc->params, callingIp_C, calledIP_C).bad())
+        {
+          T_ASC_RejectParameters rej =
+            {
+              ASC_RESULT_REJECTEDPERMANENT,
+              ASC_SOURCE_SERVICEUSER,
+              ASC_REASON_SU_NOREASON
+            };
+          ASC_rejectAssociation(assoc, &rej);
+          AssociationCleanup(assoc);
+          return NULL;
+        }
+
+        callingIp = std::string(/*OFSTRING_GUARD*/(callingIp_C));
+        callingAet = std::string(/*OFSTRING_GUARD*/(callingAet_C));
+        calledAet = (/*OFSTRING_GUARD*/(calledAet_C));
+      }
+
+      LOG(INFO) << "Association Received from AET " << callingAet 
+                << " on IP " << callingIp;
+
 
       std::vector<const char*> transferSyntaxes;
 
@@ -469,36 +500,72 @@
       transferSyntaxes.push_back(UID_LittleEndianImplicitTransferSyntax);
 
       // New transfer syntaxes supported since Orthanc 0.7.2
-      transferSyntaxes.push_back(UID_DeflatedExplicitVRLittleEndianTransferSyntax); 
-      transferSyntaxes.push_back(UID_JPEGProcess1TransferSyntax);
-      transferSyntaxes.push_back(UID_JPEGProcess2_4TransferSyntax);
-      transferSyntaxes.push_back(UID_JPEGProcess3_5TransferSyntax);
-      transferSyntaxes.push_back(UID_JPEGProcess6_8TransferSyntax);
-      transferSyntaxes.push_back(UID_JPEGProcess7_9TransferSyntax);
-      transferSyntaxes.push_back(UID_JPEGProcess10_12TransferSyntax);
-      transferSyntaxes.push_back(UID_JPEGProcess11_13TransferSyntax);
-      transferSyntaxes.push_back(UID_JPEGProcess14TransferSyntax);
-      transferSyntaxes.push_back(UID_JPEGProcess15TransferSyntax);
-      transferSyntaxes.push_back(UID_JPEGProcess16_18TransferSyntax);
-      transferSyntaxes.push_back(UID_JPEGProcess17_19TransferSyntax);
-      transferSyntaxes.push_back(UID_JPEGProcess20_22TransferSyntax);
-      transferSyntaxes.push_back(UID_JPEGProcess21_23TransferSyntax);
-      transferSyntaxes.push_back(UID_JPEGProcess24_26TransferSyntax);
-      transferSyntaxes.push_back(UID_JPEGProcess25_27TransferSyntax);
-      transferSyntaxes.push_back(UID_JPEGProcess28TransferSyntax);
-      transferSyntaxes.push_back(UID_JPEGProcess29TransferSyntax);
-      transferSyntaxes.push_back(UID_JPEGProcess14SV1TransferSyntax);
-      transferSyntaxes.push_back(UID_JPEGLSLosslessTransferSyntax);
-      transferSyntaxes.push_back(UID_JPEGLSLossyTransferSyntax);
-      transferSyntaxes.push_back(UID_JPEG2000LosslessOnlyTransferSyntax);
-      transferSyntaxes.push_back(UID_JPEG2000TransferSyntax);
-      transferSyntaxes.push_back(UID_JPEG2000Part2MulticomponentImageCompressionLosslessOnlyTransferSyntax);
-      transferSyntaxes.push_back(UID_JPEG2000Part2MulticomponentImageCompressionTransferSyntax);
-      transferSyntaxes.push_back(UID_JPIPReferencedTransferSyntax);
-      transferSyntaxes.push_back(UID_JPIPReferencedDeflateTransferSyntax);
-      transferSyntaxes.push_back(UID_MPEG2MainProfileAtMainLevelTransferSyntax);
-      transferSyntaxes.push_back(UID_MPEG2MainProfileAtHighLevelTransferSyntax);
-      transferSyntaxes.push_back(UID_RLELosslessTransferSyntax);
+      if (!server.HasApplicationEntityFilter() ||
+          server.GetApplicationEntityFilter().IsAllowedTransferSyntax(callingIp, callingAet, TransferSyntax_Deflated))
+      {
+        transferSyntaxes.push_back(UID_DeflatedExplicitVRLittleEndianTransferSyntax); 
+      }
+
+      if (!server.HasApplicationEntityFilter() ||
+          server.GetApplicationEntityFilter().IsAllowedTransferSyntax(callingIp, callingAet, TransferSyntax_Jpeg))
+      {
+        transferSyntaxes.push_back(UID_JPEGProcess1TransferSyntax);
+        transferSyntaxes.push_back(UID_JPEGProcess2_4TransferSyntax);
+        transferSyntaxes.push_back(UID_JPEGProcess3_5TransferSyntax);
+        transferSyntaxes.push_back(UID_JPEGProcess6_8TransferSyntax);
+        transferSyntaxes.push_back(UID_JPEGProcess7_9TransferSyntax);
+        transferSyntaxes.push_back(UID_JPEGProcess10_12TransferSyntax);
+        transferSyntaxes.push_back(UID_JPEGProcess11_13TransferSyntax);
+        transferSyntaxes.push_back(UID_JPEGProcess14TransferSyntax);
+        transferSyntaxes.push_back(UID_JPEGProcess15TransferSyntax);
+        transferSyntaxes.push_back(UID_JPEGProcess16_18TransferSyntax);
+        transferSyntaxes.push_back(UID_JPEGProcess17_19TransferSyntax);
+        transferSyntaxes.push_back(UID_JPEGProcess20_22TransferSyntax);
+        transferSyntaxes.push_back(UID_JPEGProcess21_23TransferSyntax);
+        transferSyntaxes.push_back(UID_JPEGProcess24_26TransferSyntax);
+        transferSyntaxes.push_back(UID_JPEGProcess25_27TransferSyntax);
+        transferSyntaxes.push_back(UID_JPEGProcess28TransferSyntax);
+        transferSyntaxes.push_back(UID_JPEGProcess29TransferSyntax);
+        transferSyntaxes.push_back(UID_JPEGProcess14SV1TransferSyntax);
+        transferSyntaxes.push_back(UID_JPEGLSLosslessTransferSyntax);
+        transferSyntaxes.push_back(UID_JPEGLSLossyTransferSyntax);
+      }
+
+      if (!server.HasApplicationEntityFilter() ||
+          server.GetApplicationEntityFilter().IsAllowedTransferSyntax(callingIp, callingAet, TransferSyntax_Jpeg2000))
+      {
+        transferSyntaxes.push_back(UID_JPEG2000LosslessOnlyTransferSyntax);
+        transferSyntaxes.push_back(UID_JPEG2000TransferSyntax);
+      }
+
+      if (!server.HasApplicationEntityFilter() ||
+          server.GetApplicationEntityFilter().IsAllowedTransferSyntax(callingIp, callingAet, TransferSyntax_JpegLossless))
+      {
+        transferSyntaxes.push_back(UID_JPEG2000LosslessOnlyTransferSyntax);
+        transferSyntaxes.push_back(UID_JPEG2000TransferSyntax);
+        transferSyntaxes.push_back(UID_JPEG2000Part2MulticomponentImageCompressionLosslessOnlyTransferSyntax);
+        transferSyntaxes.push_back(UID_JPEG2000Part2MulticomponentImageCompressionTransferSyntax);
+      }
+
+      if (!server.HasApplicationEntityFilter() ||
+          server.GetApplicationEntityFilter().IsAllowedTransferSyntax(callingIp, callingAet, TransferSyntax_Jpip))
+      {
+        transferSyntaxes.push_back(UID_JPIPReferencedTransferSyntax);
+        transferSyntaxes.push_back(UID_JPIPReferencedDeflateTransferSyntax);
+      }
+
+      if (!server.HasApplicationEntityFilter() ||
+          server.GetApplicationEntityFilter().IsAllowedTransferSyntax(callingIp, callingAet, TransferSyntax_Mpeg2))
+      {
+        transferSyntaxes.push_back(UID_MPEG2MainProfileAtMainLevelTransferSyntax);
+        transferSyntaxes.push_back(UID_MPEG2MainProfileAtHighLevelTransferSyntax);
+      }
+
+      if (!server.HasApplicationEntityFilter() ||
+          server.GetApplicationEntityFilter().IsAllowedTransferSyntax(callingIp, callingAet, TransferSyntax_Rle))
+      {
+        transferSyntaxes.push_back(UID_RLELosslessTransferSyntax);
+      }
 
       /* accept the Verification SOP Class if presented */
       cond = ASC_acceptContextsWithPreferredTransferSyntaxes(assoc->params, &knownAbstractSyntaxes[0], knownAbstractSyntaxes.size(), &transferSyntaxes[0], transferSyntaxes.size());
@@ -555,62 +622,38 @@
         return NULL;
       }
 
-      std::string callingIP;
-      std::string callingTitle;
-  
       /* check the AETs */
+      if (!server.IsMyAETitle(calledAet))
       {
-        DIC_AE callingTitle_C;
-        DIC_AE calledTitle_C;
-        DIC_AE callingIP_C;
-        DIC_AE calledIP_C;
-        if (ASC_getAPTitles(assoc->params, callingTitle_C, calledTitle_C, NULL).bad() ||
-            ASC_getPresentationAddresses(assoc->params, callingIP_C, calledIP_C).bad())
-        {
-          T_ASC_RejectParameters rej =
-            {
-              ASC_RESULT_REJECTEDPERMANENT,
-              ASC_SOURCE_SERVICEUSER,
-              ASC_REASON_SU_NOREASON
-            };
-          ASC_rejectAssociation(assoc, &rej);
-          AssociationCleanup(assoc);
-          return NULL;
-        }
-
-        callingIP = std::string(/*OFSTRING_GUARD*/(callingIP_C));
-        callingTitle = std::string(/*OFSTRING_GUARD*/(callingTitle_C));
-        std::string calledTitle(/*OFSTRING_GUARD*/(calledTitle_C));
-
-        if (!server.IsMyAETitle(calledTitle))
-        {
-          T_ASC_RejectParameters rej =
-            {
-              ASC_RESULT_REJECTEDPERMANENT,
-              ASC_SOURCE_SERVICEUSER,
-              ASC_REASON_SU_CALLEDAETITLENOTRECOGNIZED
-            };
-          ASC_rejectAssociation(assoc, &rej);
-          AssociationCleanup(assoc);
-          return NULL;
-        }
-
-        if (server.HasApplicationEntityFilter() &&
-            !server.GetApplicationEntityFilter().IsAllowedConnection(callingIP, callingTitle))
-        {
-          T_ASC_RejectParameters rej =
-            {
-              ASC_RESULT_REJECTEDPERMANENT,
-              ASC_SOURCE_SERVICEUSER,
-              ASC_REASON_SU_CALLINGAETITLENOTRECOGNIZED
-            };
-          ASC_rejectAssociation(assoc, &rej);
-          AssociationCleanup(assoc);
-          return NULL;
-        }
+        LOG(WARNING) << "Rejected association, because of a bad called AET in the request (" << calledAet << ")";
+        T_ASC_RejectParameters rej =
+          {
+            ASC_RESULT_REJECTEDPERMANENT,
+            ASC_SOURCE_SERVICEUSER,
+            ASC_REASON_SU_CALLEDAETITLENOTRECOGNIZED
+          };
+        ASC_rejectAssociation(assoc, &rej);
+        AssociationCleanup(assoc);
+        return NULL;
       }
 
-      if (opt_rejectWithoutImplementationUID && strlen(assoc->params->theirImplementationClassUID) == 0)
+      if (server.HasApplicationEntityFilter() &&
+          !server.GetApplicationEntityFilter().IsAllowedConnection(callingIp, callingAet))
+      {
+        LOG(WARNING) << "Rejected association for remote AET " << callingAet << " on IP " << callingIp;
+        T_ASC_RejectParameters rej =
+          {
+            ASC_RESULT_REJECTEDPERMANENT,
+            ASC_SOURCE_SERVICEUSER,
+            ASC_REASON_SU_CALLINGAETITLENOTRECOGNIZED
+          };
+        ASC_rejectAssociation(assoc, &rej);
+        AssociationCleanup(assoc);
+        return NULL;
+      }
+
+      if (opt_rejectWithoutImplementationUID && 
+          strlen(assoc->params->theirImplementationClassUID) == 0)
       {
         /* reject: the no implementation Class UID provided */
         T_ASC_RejectParameters rej =
@@ -644,7 +687,7 @@
       }
 
       IApplicationEntityFilter* filter = server.HasApplicationEntityFilter() ? &server.GetApplicationEntityFilter() : NULL;
-      return new CommandDispatcher(server, assoc, callingIP, callingTitle, filter);
+      return new CommandDispatcher(server, assoc, callingIp, callingAet, filter);
     }
 
     bool CommandDispatcher::Step()
--- a/OrthancServer/OrthancRestApi/OrthancRestModalities.cpp	Wed Sep 17 13:25:37 2014 +0200
+++ b/OrthancServer/OrthancRestApi/OrthancRestModalities.cpp	Mon Sep 22 13:44:08 2014 +0200
@@ -80,12 +80,17 @@
     {
       if (locker.GetConnection().Echo())
       {
+        // Echo has succeeded
         call.GetOutput().AnswerBuffer("{}", "application/json");
+        return;
       }
     }
     catch (OrthancException&)
     {
     }
+
+    // Echo has failed
+    call.GetOutput().SignalError(HttpStatus_500_InternalServerError);
   }
 
 
@@ -357,13 +362,20 @@
     for (std::list<std::string>::const_iterator 
            it = instances.begin(); it != instances.end(); ++it)
     {
-      job.AddCommand(new StoreScuCommand(context, p)).AddInput(*it);
+      job.AddCommand(new StoreScuCommand(context, p, false)).AddInput(*it);
     }
 
     job.SetDescription("HTTP request: Store-SCU to peer \"" + remote + "\"");
-    context.GetScheduler().SubmitAndWait(job);
 
-    call.GetOutput().AnswerBuffer("{}", "application/json");
+    if (context.GetScheduler().SubmitAndWait(job))
+    {
+      // Success
+      call.GetOutput().AnswerBuffer("{}", "application/json");
+    }
+    else
+    {
+      call.GetOutput().SignalError(HttpStatus_500_InternalServerError);
+    }
   }
 
 
@@ -421,13 +433,20 @@
     for (std::list<std::string>::const_iterator 
            it = instances.begin(); it != instances.end(); ++it)
     {
-      job.AddCommand(new StorePeerCommand(context, peer)).AddInput(*it);
+      job.AddCommand(new StorePeerCommand(context, peer, false)).AddInput(*it);
     }
 
     job.SetDescription("HTTP request: POST to peer \"" + remote + "\"");
-    context.GetScheduler().SubmitAndWait(job);
 
-    call.GetOutput().AnswerBuffer("{}", "application/json");
+    if (context.GetScheduler().SubmitAndWait(job))
+    {
+      // Success
+      call.GetOutput().AnswerBuffer("{}", "application/json");
+    }
+    else
+    {
+      call.GetOutput().SignalError(HttpStatus_500_InternalServerError);
+    }
   }
 
 
--- a/OrthancServer/OrthancRestApi/OrthancRestResources.cpp	Wed Sep 17 13:25:37 2014 +0200
+++ b/OrthancServer/OrthancRestApi/OrthancRestResources.cpp	Mon Sep 22 13:44:08 2014 +0200
@@ -876,6 +876,42 @@
   }
 
 
+  static void GetChildInstancesTags(RestApiGetCall& call)
+  {
+    ServerContext& context = OrthancRestApi::GetContext(call);
+    std::string publicId = call.GetUriComponent("id", "");
+    bool simplify = call.HasArgument("simplify");
+
+    // Retrieve all the instances of this patient/study/series
+    typedef std::list<std::string> Instances;
+    Instances instances;
+
+    context.GetIndex().GetChildInstances(instances, publicId);  // (*)
+
+    Json::Value result = Json::arrayValue;
+
+    for (Instances::const_iterator it = instances.begin();
+         it != instances.end(); it++)
+    {
+      Json::Value full;
+      context.ReadJson(full, *it);
+
+      if (simplify)
+      {
+        Json::Value simplified;
+        SimplifyTags(simplified, full);
+        result.append(simplified);
+      }
+      else
+      {
+        result.append(full);
+      }
+    }
+    
+    call.GetOutput().AnswerJson(result);
+  }
+
+
 
   void OrthancRestApi::RegisterResources()
   {
@@ -954,6 +990,10 @@
     Register("/studies/{id}/instances", GetChildResources<ResourceType_Study, ResourceType_Instance>);
     Register("/series/{id}/instances", GetChildResources<ResourceType_Series, ResourceType_Instance>);
 
+    Register("/patients/{id}/instances-tags", GetChildInstancesTags);
+    Register("/studies/{id}/instances-tags", GetChildInstancesTags);
+    Register("/series/{id}/instances-tags", GetChildInstancesTags);
+
     Register("/instances/{id}/content/*", GetRawContent);
   }
 }
--- a/OrthancServer/Scheduler/StorePeerCommand.cpp	Wed Sep 17 13:25:37 2014 +0200
+++ b/OrthancServer/Scheduler/StorePeerCommand.cpp	Mon Sep 22 13:44:08 2014 +0200
@@ -39,9 +39,11 @@
 namespace Orthanc
 {
   StorePeerCommand::StorePeerCommand(ServerContext& context,
-                                     const OrthancPeerParameters& peer) : 
+                                     const OrthancPeerParameters& peer,
+                                     bool ignoreExceptions) : 
     context_(context),
-    peer_(peer)
+    peer_(peer),
+    ignoreExceptions_(ignoreExceptions)
   {
   }
 
@@ -84,6 +86,11 @@
       {
         LOG(ERROR) << "Unable to forward to an Orthanc peer in a Lua script (instance " 
                    << *it << ", peer " << peer_.GetUrl() << "): " << e.What();
+
+        if (!ignoreExceptions_)
+        {
+          throw;
+        }
       }
     }
 
--- a/OrthancServer/Scheduler/StorePeerCommand.h	Wed Sep 17 13:25:37 2014 +0200
+++ b/OrthancServer/Scheduler/StorePeerCommand.h	Mon Sep 22 13:44:08 2014 +0200
@@ -43,10 +43,12 @@
   private:
     ServerContext& context_;
     OrthancPeerParameters peer_;
+    bool ignoreExceptions_;
 
   public:
     StorePeerCommand(ServerContext& context,
-                     const OrthancPeerParameters& peer);
+                     const OrthancPeerParameters& peer,
+                     bool ignoreExceptions);
     
     virtual bool Apply(ListOfStrings& outputs,
                        const ListOfStrings& inputs);
--- a/OrthancServer/Scheduler/StoreScuCommand.cpp	Wed Sep 17 13:25:37 2014 +0200
+++ b/OrthancServer/Scheduler/StoreScuCommand.cpp	Mon Sep 22 13:44:08 2014 +0200
@@ -37,9 +37,11 @@
 namespace Orthanc
 {
   StoreScuCommand::StoreScuCommand(ServerContext& context,
-                                 const RemoteModalityParameters& modality) : 
+                                   const RemoteModalityParameters& modality,
+                                   bool ignoreExceptions) : 
     context_(context),
-    modality_(modality)
+    modality_(modality),
+    ignoreExceptions_(ignoreExceptions)
   {
   }
 
@@ -69,6 +71,11 @@
         // powered off)
         LOG(ERROR) << "Unable to forward to a modality in a Lua script (instance " 
                    << *it << "): " << e.What();
+
+        if (!ignoreExceptions_)
+        {
+          throw;
+        }
       }
     }
 
--- a/OrthancServer/Scheduler/StoreScuCommand.h	Wed Sep 17 13:25:37 2014 +0200
+++ b/OrthancServer/Scheduler/StoreScuCommand.h	Mon Sep 22 13:44:08 2014 +0200
@@ -42,10 +42,12 @@
   private:
     ServerContext& context_;
     RemoteModalityParameters modality_;
+    bool ignoreExceptions_;
 
   public:
     StoreScuCommand(ServerContext& context,
-                   const RemoteModalityParameters& modality);
+                    const RemoteModalityParameters& modality,
+                    bool ignoreExceptions);
 
     virtual bool Apply(ListOfStrings& outputs,
                        const ListOfStrings& inputs);
--- a/OrthancServer/ServerContext.cpp	Wed Sep 17 13:25:37 2014 +0200
+++ b/OrthancServer/ServerContext.cpp	Mon Sep 22 13:44:08 2014 +0200
@@ -138,7 +138,7 @@
       std::string modality = parameters["Modality"].asString();
       LOG(INFO) << "Lua script to send instance " << parameters["Instance"].asString()
                 << " to modality " << modality << " using Store-SCU";
-      return new StoreScuCommand(context, Configuration::GetModalityUsingSymbolicName(modality));
+      return new StoreScuCommand(context, Configuration::GetModalityUsingSymbolicName(modality), true);
     }
 
     if (operation == "store-peer")
@@ -149,7 +149,7 @@
 
       OrthancPeerParameters parameters;
       Configuration::GetOrthancPeer(parameters, peer);
-      return new StorePeerCommand(context, parameters);
+      return new StorePeerCommand(context, parameters, true);
     }
 
     if (operation == "modify")
--- a/OrthancServer/ServerEnumerations.cpp	Wed Sep 17 13:25:37 2014 +0200
+++ b/OrthancServer/ServerEnumerations.cpp	Mon Sep 22 13:44:08 2014 +0200
@@ -324,7 +324,6 @@
         return "Store";
         break;
 
-
       default: 
         throw OrthancException(ErrorCode_ParameterOutOfRange);
     }
@@ -359,4 +358,36 @@
       throw OrthancException(ErrorCode_ParameterOutOfRange);
     }
   }
+
+
+  const char* EnumerationToString(TransferSyntax syntax)
+  {
+    switch (syntax)
+    {
+      case TransferSyntax_Deflated:
+        return "Deflated";
+
+      case TransferSyntax_Jpeg:
+        return "JPEG";
+
+      case TransferSyntax_Jpeg2000:
+        return "JPEG2000";
+
+      case TransferSyntax_JpegLossless:
+        return "JPEG Lossless";
+
+      case TransferSyntax_Jpip:
+        return "JPIP";
+
+      case TransferSyntax_Mpeg2:
+        return "MPEG2";
+
+      case TransferSyntax_Rle:
+        return "RLE";
+
+      default: 
+        throw OrthancException(ErrorCode_ParameterOutOfRange);
+    }
+  }
+
 }
--- a/OrthancServer/ServerEnumerations.h	Wed Sep 17 13:25:37 2014 +0200
+++ b/OrthancServer/ServerEnumerations.h	Mon Sep 22 13:44:08 2014 +0200
@@ -79,6 +79,17 @@
     DicomReplaceMode_IgnoreIfAbsent
   };
 
+  enum TransferSyntax
+  {
+    TransferSyntax_Deflated,
+    TransferSyntax_Jpeg,
+    TransferSyntax_Jpeg2000,
+    TransferSyntax_JpegLossless,
+    TransferSyntax_Jpip,
+    TransferSyntax_Mpeg2,
+    TransferSyntax_Rle
+  };
+
 
   /**
    * WARNING: Do not change the explicit values in the enumerations
@@ -157,6 +168,8 @@
 
   const char* EnumerationToString(DicomRequestType type);
 
+  const char* EnumerationToString(TransferSyntax syntax);
+
   ModalityManufacturer StringToModalityManufacturer(const std::string& manufacturer);
 
   ResourceType GetParentResourceType(ResourceType type);
--- a/OrthancServer/main.cpp	Wed Sep 17 13:25:37 2014 +0200
+++ b/OrthancServer/main.cpp	Mon Sep 22 13:44:08 2014 +0200
@@ -150,7 +150,14 @@
 
 class OrthancApplicationEntityFilter : public IApplicationEntityFilter
 {
+private:
+  ServerContext& context_;
+
 public:
+  OrthancApplicationEntityFilter(ServerContext& context) : context_(context)
+  {
+  }
+
   virtual bool IsAllowedConnection(const std::string& /*callingIp*/,
                                    const std::string& /*callingAet*/)
   {
@@ -177,6 +184,63 @@
       return true;
     }
   }
+
+  virtual bool IsAllowedTransferSyntax(const std::string& callingIp,
+                                       const std::string& callingAet,
+                                       TransferSyntax syntax)
+  {
+    std::string configuration;
+
+    switch (syntax)
+    {
+      case TransferSyntax_Deflated:
+        configuration = "DeflatedTransferSyntaxAccepted";
+        break;
+
+      case TransferSyntax_Jpeg:
+        configuration = "JpegTransferSyntaxAccepted";
+        break;
+
+      case TransferSyntax_Jpeg2000:
+        configuration = "Jpeg2000TransferSyntaxAccepted";
+        break;
+
+      case TransferSyntax_JpegLossless:
+        configuration = "JpegLosslessTransferSyntaxAccepted";
+        break;
+
+      case TransferSyntax_Jpip:
+        configuration = "JpipTransferSyntaxAccepted";
+        break;
+
+      case TransferSyntax_Mpeg2:
+        configuration = "Mpeg2TransferSyntaxAccepted";
+        break;
+
+      case TransferSyntax_Rle:
+        configuration = "RleTransferSyntaxAccepted";
+        break;
+
+      default: 
+        throw OrthancException(ErrorCode_ParameterOutOfRange);
+    }
+
+    {
+      std::string lua = "Is" + configuration;
+
+      ServerContext::LuaContextLocker locker(context_);
+      
+      if (locker.GetLua().IsExistingFunction(lua.c_str()))
+      {
+        LuaFunctionCall call(locker.GetLua(), lua.c_str());
+        call.PushString(callingAet);
+        call.PushString(callingIp);
+        return call.ExecutePredicate();
+      }
+    }
+
+    return Configuration::GetGlobalBoolParameter(configuration, true);
+  }
 };
 
 
@@ -405,7 +469,7 @@
   {
     // DICOM server
     DicomServer dicomServer;
-    OrthancApplicationEntityFilter dicomFilter;
+    OrthancApplicationEntityFilter dicomFilter(context);
     dicomServer.SetCalledApplicationEntityTitleCheck(Configuration::GetGlobalBoolParameter("DicomCheckCalledAet", false));
     dicomServer.SetStoreRequestHandlerFactory(serverFactory);
     dicomServer.SetMoveRequestHandlerFactory(serverFactory);
@@ -594,6 +658,7 @@
     }
   }
 
+  LOG(WARNING) << "Orthanc version: " << ORTHANC_VERSION;
 
   int status = 0;
   try
--- a/Resources/Configuration.json	Wed Sep 17 13:25:37 2014 +0200
+++ b/Resources/Configuration.json	Mon Sep 22 13:44:08 2014 +0200
@@ -69,6 +69,14 @@
   // and "Chinese".
   "DefaultEncoding" : "Latin1",
 
+  // The transfer syntaxes that are accepted by Orthanc C-Store SCP
+  "DeflatedTransferSyntaxAccepted"     : true,
+  "JpegTransferSyntaxAccepted"         : true,
+  "Jpeg2000TransferSyntaxAccepted"     : true,
+  "JpegLosslessTransferSyntaxAccepted" : true,
+  "JpipTransferSyntaxAccepted"         : true,
+  "Mpeg2TransferSyntaxAccepted"        : true,
+  "RleTransferSyntaxAccepted"          : true,
 
 
   /**
--- a/Resources/DicomConformanceStatement.txt	Wed Sep 17 13:25:37 2014 +0200
+++ b/Resources/DicomConformanceStatement.txt	Mon Sep 22 13:44:08 2014 +0200
@@ -231,6 +231,9 @@
   MPEG2MainProfileAtHighLevelTransferSyntax                             | 1.2.840.10008.1.2.4.101
   RLELosslessTransferSyntax                                             | 1.2.840.10008.1.2.5
 
+It is possible to disable a subset of these transfer syntaxes thanks to the
+"*TransferSyntaxAccepted" options in the Orthanc configuration file.
+
 When possible, Orthanc will prefer the
 LittleEndianImplicitTransferSyntax transfer syntax
 (1.2.840.10008.1.2).
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/Samples/Lua/TransferSyntaxDisable.lua	Mon Sep 22 13:44:08 2014 +0200
@@ -0,0 +1,29 @@
+function IsDeflatedTransferSyntaxAccepted(aet, ip)
+   return false
+end
+
+function IsJpegTransferSyntaxAccepted(aet, ip)
+   return false
+end
+
+function IsJpeg2000TransferSyntaxAccepted(aet, ip)
+   return false
+end
+
+function IsJpegLosslessTransferSyntaxAccepted(aet, ip)
+   return false
+end
+
+function IsJpipTransferSyntaxAccepted(aet, ip)
+   return false
+end
+
+function IsMpeg2TransferSyntaxAccepted(aet, ip)
+   return false
+end
+
+function IsRleTransferSyntaxAccepted(aet, ip)
+   return false
+end
+
+print('All special transfer syntaxes are now disallowed')
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/Samples/Lua/TransferSyntaxEnable.lua	Mon Sep 22 13:44:08 2014 +0200
@@ -0,0 +1,29 @@
+function IsDeflatedTransferSyntaxAccepted(aet, ip)
+   return true
+end
+
+function IsJpegTransferSyntaxAccepted(aet, ip)
+   return true
+end
+
+function IsJpeg2000TransferSyntaxAccepted(aet, ip)
+   return true
+end
+
+function IsJpegLosslessTransferSyntaxAccepted(aet, ip)
+   return true
+end
+
+function IsJpipTransferSyntaxAccepted(aet, ip)
+   return true
+end
+
+function IsMpeg2TransferSyntaxAccepted(aet, ip)
+   return true
+end
+
+function IsRleTransferSyntaxAccepted(aet, ip)
+   return true
+end
+
+print('All special transfer syntaxes are now accepted')