changeset 4468:9c070a34de18

IApplicationEntityFilter::GetAcceptedTransferSyntaxes()
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 25 Jan 2021 15:18:34 +0100
parents c92ec129698a
children 5ffa4e14e4bd
files OrthancFramework/Resources/CodeGeneration/CheckDcmtkTransferSyntaxes.py OrthancFramework/Resources/CodeGeneration/GenerateTransferSyntaxes.py OrthancFramework/Resources/CodeGeneration/GenerateTransferSyntaxesEnumerations.mustache OrthancFramework/Sources/DicomNetworking/IApplicationEntityFilter.h OrthancFramework/Sources/DicomNetworking/Internals/CommandDispatcher.cpp OrthancFramework/Sources/Enumerations.h OrthancFramework/Sources/Enumerations_TransferSyntaxes.impl.h OrthancServer/Sources/main.cpp
diffstat 8 files changed, 332 insertions(+), 163 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OrthancFramework/Resources/CodeGeneration/CheckDcmtkTransferSyntaxes.py	Mon Jan 25 15:18:34 2021 +0100
@@ -0,0 +1,49 @@
+#!/usr/bin/python
+
+# Orthanc - A Lightweight, RESTful DICOM Store
+# Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+# Department, University Hospital of Liege, Belgium
+# Copyright (C) 2017-2021 Osimis S.A., Belgium
+#
+# This program is free software: you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public License
+# as published by the Free Software Foundation, either version 3 of
+# the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program. If not, see
+# <http://www.gnu.org/licenses/>.
+
+
+import json
+import os
+import re
+import sys
+
+if len(sys.argv) != 2:
+    print('Usage: %s [Path to DCMTK source code]' % sys.argv[0])
+    exit(-1)
+
+
+orthancSyntaxes = []
+    
+
+with open(os.path.join(os.path.dirname(__file__), 'DicomTransferSyntaxes.json'), 'r') as f:
+    for syntax in json.loads(f.read()):
+        orthancSyntaxes.append(syntax['UID'])
+
+
+with open(os.path.join(sys.argv[1], 'dcmdata/include/dcmtk/dcmdata/dcuid.h'), 'r') as f:
+    r = re.compile(r'^#define UID_([A-Za-z0-9_]+)TransferSyntax\s+"([0-9.]+)"$')
+    
+    for line in f.readlines():
+        m = r.match(line)
+        if m != None:
+            syntax = m.group(2)
+            if not syntax in orthancSyntaxes:
+                print('Missing syntax: %s => %s' % (syntax, m.group(1)))
--- a/OrthancFramework/Resources/CodeGeneration/GenerateTransferSyntaxes.py	Thu Jan 21 19:06:00 2021 +0100
+++ b/OrthancFramework/Resources/CodeGeneration/GenerateTransferSyntaxes.py	Mon Jan 25 15:18:34 2021 +0100
@@ -26,7 +26,7 @@
 import sys
 import pystache
 
-BASE = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
+BASE = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))
 
 
 
@@ -34,7 +34,7 @@
 ## https://cedocs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=EDICOM_transfer_syntax
 
 
-with open(os.path.join(BASE, 'Resources', 'DicomTransferSyntaxes.json'), 'r') as f:
+with open(os.path.join(BASE, 'Resources', 'CodeGeneration', 'DicomTransferSyntaxes.json'), 'r') as f:
     SYNTAXES = json.loads(f.read())
 
 
@@ -43,7 +43,7 @@
 ## Generate the "DicomTransferSyntax" enumeration in "Enumerations.h"
 ##
 
-path = os.path.join(BASE, 'Core', 'Enumerations.h')
+path = os.path.join(BASE, 'Sources', 'Enumerations.h')
 with open(path, 'r') as f:
     a = f.read()
 
@@ -60,14 +60,14 @@
 ## Generate the implementations
 ##
 
-with open(os.path.join(BASE, 'Core', 'Enumerations_TransferSyntaxes.impl.h'), 'w') as b:
-    with open(os.path.join(BASE, 'Resources', 'GenerateTransferSyntaxesEnumerations.mustache'), 'r') as a:
+with open(os.path.join(BASE, 'Sources', 'Enumerations_TransferSyntaxes.impl.h'), 'w') as b:
+    with open(os.path.join(BASE, 'Resources', 'CodeGeneration', 'GenerateTransferSyntaxesEnumerations.mustache'), 'r') as a:
         b.write(pystache.render(a.read(), {
             'Syntaxes' : SYNTAXES
         }))
 
-with open(os.path.join(BASE, 'Core', 'DicomParsing', 'FromDcmtkBridge_TransferSyntaxes.impl.h'), 'w') as b:
-    with open(os.path.join(BASE, 'Resources', 'GenerateTransferSyntaxesDcmtk.mustache'), 'r') as a:
+with open(os.path.join(BASE, 'Sources', 'DicomParsing', 'FromDcmtkBridge_TransferSyntaxes.impl.h'), 'w') as b:
+    with open(os.path.join(BASE, 'Resources', 'CodeGeneration', 'GenerateTransferSyntaxesDcmtk.mustache'), 'r') as a:
         b.write(pystache.render(a.read(), {
             'Syntaxes' : SYNTAXES
         }))
--- a/OrthancFramework/Resources/CodeGeneration/GenerateTransferSyntaxesEnumerations.mustache	Thu Jan 21 19:06:00 2021 +0100
+++ b/OrthancFramework/Resources/CodeGeneration/GenerateTransferSyntaxesEnumerations.mustache	Mon Jan 25 15:18:34 2021 +0100
@@ -71,4 +71,13 @@
     {{/Syntaxes}}
     return false;
   }
+
+
+  void GetAllTransferSyntaxes(std::set<DicomTransferSyntax>& target)
+  {
+    target.clear();
+    {{#Syntaxes}}
+    target.insert(DicomTransferSyntax_{{Value}});
+    {{/Syntaxes}}
+  }
 }
--- a/OrthancFramework/Sources/DicomNetworking/IApplicationEntityFilter.h	Thu Jan 21 19:06:00 2021 +0100
+++ b/OrthancFramework/Sources/DicomNetworking/IApplicationEntityFilter.h	Mon Jan 25 15:18:34 2021 +0100
@@ -22,6 +22,7 @@
 
 #pragma once
 
+#include "../Compatibility.h"
 #include "../Enumerations.h"
 
 #include <string>
@@ -44,10 +45,15 @@
                                   const std::string& calledAet,
                                   DicomRequestType type) = 0;
 
-    virtual bool IsAllowedTransferSyntax(const std::string& remoteIp,
-                                         const std::string& remoteAet,
-                                         const std::string& calledAet,
-                                         TransferSyntax syntax) = 0;
+    virtual void GetAcceptedTransferSyntaxes(std::set<DicomTransferSyntax>& target,
+                                             const std::string& remoteIp,
+                                             const std::string& remoteAet,
+                                             const std::string& calledAet) = 0;
+    
+    ORTHANC_DEPRECATED(virtual bool IsAllowedTransferSyntax(const std::string& remoteIp,
+                                                            const std::string& remoteAet,
+                                                            const std::string& calledAet,
+                                                            TransferSyntax syntax)) = 0;
 
     virtual bool IsUnknownSopClassAccepted(const std::string& remoteIp,
                                            const std::string& remoteAet,
--- a/OrthancFramework/Sources/DicomNetworking/Internals/CommandDispatcher.cpp	Thu Jan 21 19:06:00 2021 +0100
+++ b/OrthancFramework/Sources/DicomNetworking/Internals/CommandDispatcher.cpp	Mon Jan 25 15:18:34 2021 +0100
@@ -409,185 +409,152 @@
 
       {
         /* accept the abstract syntaxes for C-STORE, if presented */
-
-        std::vector<const char*> storageTransferSyntaxes;
-
-        // This is the list of the transfer syntaxes that were supported up to Orthanc 0.7.1
-        storageTransferSyntaxes.push_back(UID_LittleEndianExplicitTransferSyntax);
-        storageTransferSyntaxes.push_back(UID_BigEndianExplicitTransferSyntax);
-        storageTransferSyntaxes.push_back(UID_LittleEndianImplicitTransferSyntax);
+        
+        std::set<DicomTransferSyntax> storageTransferSyntaxes;
 
-        // New transfer syntaxes supported since Orthanc 0.7.2
-        if (!server.HasApplicationEntityFilter() ||
-            server.GetApplicationEntityFilter().IsAllowedTransferSyntax(remoteIp, remoteAet, calledAet, TransferSyntax_Deflated))
-        {
-          storageTransferSyntaxes.push_back(UID_DeflatedExplicitVRLittleEndianTransferSyntax); 
-        }
-
-        if (!server.HasApplicationEntityFilter() ||
-            server.GetApplicationEntityFilter().IsAllowedTransferSyntax(remoteIp, remoteAet, calledAet, TransferSyntax_Jpeg))
+        if (server.HasApplicationEntityFilter())
         {
-          storageTransferSyntaxes.push_back(UID_JPEGProcess1TransferSyntax);
-          storageTransferSyntaxes.push_back(UID_JPEGProcess2_4TransferSyntax);
-          storageTransferSyntaxes.push_back(UID_JPEGProcess3_5TransferSyntax);
-          storageTransferSyntaxes.push_back(UID_JPEGProcess6_8TransferSyntax);
-          storageTransferSyntaxes.push_back(UID_JPEGProcess7_9TransferSyntax);
-          storageTransferSyntaxes.push_back(UID_JPEGProcess10_12TransferSyntax);
-          storageTransferSyntaxes.push_back(UID_JPEGProcess11_13TransferSyntax);
-          storageTransferSyntaxes.push_back(UID_JPEGProcess14TransferSyntax);
-          storageTransferSyntaxes.push_back(UID_JPEGProcess15TransferSyntax);
-          storageTransferSyntaxes.push_back(UID_JPEGProcess16_18TransferSyntax);
-          storageTransferSyntaxes.push_back(UID_JPEGProcess17_19TransferSyntax);
-          storageTransferSyntaxes.push_back(UID_JPEGProcess20_22TransferSyntax);
-          storageTransferSyntaxes.push_back(UID_JPEGProcess21_23TransferSyntax);
-          storageTransferSyntaxes.push_back(UID_JPEGProcess24_26TransferSyntax);
-          storageTransferSyntaxes.push_back(UID_JPEGProcess25_27TransferSyntax);
-          storageTransferSyntaxes.push_back(UID_JPEGProcess28TransferSyntax);
-          storageTransferSyntaxes.push_back(UID_JPEGProcess29TransferSyntax);
-          storageTransferSyntaxes.push_back(UID_JPEGProcess14SV1TransferSyntax);
+          server.GetApplicationEntityFilter().GetAcceptedTransferSyntaxes(
+            storageTransferSyntaxes, remoteIp, remoteAet, calledAet);
         }
-
-        if (!server.HasApplicationEntityFilter() ||
-            server.GetApplicationEntityFilter().IsAllowedTransferSyntax(remoteIp, remoteAet, calledAet, TransferSyntax_Jpeg2000))
+        else
         {
-          storageTransferSyntaxes.push_back(UID_JPEG2000LosslessOnlyTransferSyntax);
-          storageTransferSyntaxes.push_back(UID_JPEG2000TransferSyntax);
-          storageTransferSyntaxes.push_back(UID_JPEG2000LosslessOnlyTransferSyntax);
-          storageTransferSyntaxes.push_back(UID_JPEG2000TransferSyntax);
-          storageTransferSyntaxes.push_back(UID_JPEG2000Part2MulticomponentImageCompressionLosslessOnlyTransferSyntax);
-          storageTransferSyntaxes.push_back(UID_JPEG2000Part2MulticomponentImageCompressionTransferSyntax);
-        }
-
-        if (!server.HasApplicationEntityFilter() ||
-            server.GetApplicationEntityFilter().IsAllowedTransferSyntax(remoteIp, remoteAet, calledAet, TransferSyntax_JpegLossless))
-        {
-          storageTransferSyntaxes.push_back(UID_JPEGLSLosslessTransferSyntax);
-          storageTransferSyntaxes.push_back(UID_JPEGLSLossyTransferSyntax);
-        }
-
-        if (!server.HasApplicationEntityFilter() ||
-            server.GetApplicationEntityFilter().IsAllowedTransferSyntax(remoteIp, remoteAet, calledAet, TransferSyntax_Jpip))
-        {
-          storageTransferSyntaxes.push_back(UID_JPIPReferencedTransferSyntax);
-          storageTransferSyntaxes.push_back(UID_JPIPReferencedDeflateTransferSyntax);
-        }
-
-        if (!server.HasApplicationEntityFilter() ||
-            server.GetApplicationEntityFilter().IsAllowedTransferSyntax(remoteIp, remoteAet, calledAet, TransferSyntax_Mpeg2))
-        {
-          storageTransferSyntaxes.push_back(UID_MPEG2MainProfileAtMainLevelTransferSyntax);
-          storageTransferSyntaxes.push_back(UID_MPEG2MainProfileAtHighLevelTransferSyntax);
+          /**
+           * In the absence of filter, accept all the known transfer
+           * syntaxes. Note that this is different from Orthanc
+           * framework <= 1.8.2, where only the uncompressed transfer
+           * syntaxes are accepted by default.
+           **/
+          GetAllTransferSyntaxes(storageTransferSyntaxes);
         }
 
-#if DCMTK_VERSION_NUMBER >= 361
-        // New in Orthanc 1.6.0
-        if (!server.HasApplicationEntityFilter() ||
-            server.GetApplicationEntityFilter().IsAllowedTransferSyntax(remoteIp, remoteAet, calledAet, TransferSyntax_Mpeg4))
+        if (storageTransferSyntaxes.empty())
+        {
+          LOG(WARNING) << "The DICOM server accepts no transfer syntax, thus C-STORE SCP is disabled";
+        }
+        else
         {
-          storageTransferSyntaxes.push_back(UID_MPEG4BDcompatibleHighProfileLevel4_1TransferSyntax);
-          storageTransferSyntaxes.push_back(UID_MPEG4HighProfileLevel4_1TransferSyntax);
-          storageTransferSyntaxes.push_back(UID_MPEG4HighProfileLevel4_2_For2DVideoTransferSyntax);
-          storageTransferSyntaxes.push_back(UID_MPEG4HighProfileLevel4_2_For3DVideoTransferSyntax);
-          storageTransferSyntaxes.push_back(UID_MPEG4StereoHighProfileLevel4_2TransferSyntax);
-        }
-#endif
+          /**
+           * If accepted, put "Little Endian Explicit" at the first
+           * place in the accepted transfer syntaxes. This first place
+           * has an impact on the result of "getscu" (cf. integration
+           * test "test_getscu"). We choose "Little Endian Explicit",
+           * as this preserves the VR of the private tags, even if the
+           * remote modality doesn't have the dictionary of private tags.
+           *
+           * TODO - Should "PREFERRED_TRANSFER_SYNTAX" be an option of
+           * class "DicomServer"?
+           **/
+          const DicomTransferSyntax PREFERRED_TRANSFER_SYNTAX = DicomTransferSyntax_LittleEndianExplicit;
+          
+          std::vector<const char*> storageTransferSyntaxesC;
+          storageTransferSyntaxesC.reserve(storageTransferSyntaxes.size());
 
-        if (!server.HasApplicationEntityFilter() ||
-            server.GetApplicationEntityFilter().IsAllowedTransferSyntax(remoteIp, remoteAet, calledAet, TransferSyntax_Rle))
-        {
-          storageTransferSyntaxes.push_back(UID_RLELosslessTransferSyntax);
-        }
+          if (storageTransferSyntaxes.find(PREFERRED_TRANSFER_SYNTAX) != storageTransferSyntaxes.end())
+          {
+            storageTransferSyntaxesC.push_back(GetTransferSyntaxUid(PREFERRED_TRANSFER_SYNTAX));
+          }
+          
+          for (std::set<DicomTransferSyntax>::const_iterator
+                 it = storageTransferSyntaxes.begin(); it != storageTransferSyntaxes.end(); ++it)
+          {
+            if (*it != PREFERRED_TRANSFER_SYNTAX)  // Don't add the preferred transfer syntax twice
+            {
+              storageTransferSyntaxesC.push_back(GetTransferSyntaxUid(*it));
+            }
+          }
 
-        /* the array of Storage SOP Class UIDs that is defined within "dcmdata/libsrc/dcuid.cc" */
-        size_t count = 0;
-        while (dcmAllStorageSOPClassUIDs[count] != NULL)
-        {
-          count++;
-        }
+          /* the array of Storage SOP Class UIDs that is defined within "dcmdata/libsrc/dcuid.cc" */
+          size_t count = 0;
+          while (dcmAllStorageSOPClassUIDs[count] != NULL)
+          {
+            count++;
+          }
         
 #if DCMTK_VERSION_NUMBER >= 362
-        // The global variable "numberOfDcmAllStorageSOPClassUIDs" is
-        // only published if DCMTK >= 3.6.2:
-        // https://bitbucket.org/sjodogne/orthanc/issues/137
-        assert(static_cast<int>(count) == numberOfDcmAllStorageSOPClassUIDs);
+          // The global variable "numberOfDcmAllStorageSOPClassUIDs" is
+          // only published if DCMTK >= 3.6.2:
+          // https://bitbucket.org/sjodogne/orthanc/issues/137
+          assert(static_cast<int>(count) == numberOfDcmAllStorageSOPClassUIDs);
 #endif
       
-        if (!server.HasGetRequestHandlerFactory())    // dcmqrsrv.cc line 828
-        {
-          // This branch exactly corresponds to Orthanc <= 1.6.1 (in
-          // which C-GET SCP was not supported)
-          cond = ASC_acceptContextsWithPreferredTransferSyntaxes(
-            assoc->params, dcmAllStorageSOPClassUIDs, count,
-            &storageTransferSyntaxes[0], storageTransferSyntaxes.size());
-          if (cond.bad())
+          if (!server.HasGetRequestHandlerFactory())    // dcmqrsrv.cc line 828
           {
-            CLOG(INFO, DICOM) << cond.text();
-            AssociationCleanup(assoc);
-            return NULL;
+            // This branch exactly corresponds to Orthanc <= 1.6.1 (in
+            // which C-GET SCP was not supported)
+            cond = ASC_acceptContextsWithPreferredTransferSyntaxes(
+              assoc->params, dcmAllStorageSOPClassUIDs, count,
+              &storageTransferSyntaxesC[0], storageTransferSyntaxesC.size());
+            if (cond.bad())
+            {
+              CLOG(INFO, DICOM) << cond.text();
+              AssociationCleanup(assoc);
+              return NULL;
+            }
           }
-        }
-        else                                         // see dcmqrsrv.cc lines 839 - 876
-        {
-          /* accept storage syntaxes with proposed role */
-          int npc = ASC_countPresentationContexts(assoc->params);
-          for (int i = 0; i < npc; i++)
+          else                                         // see dcmqrsrv.cc lines 839 - 876
           {
-            T_ASC_PresentationContext pc;
-            ASC_getPresentationContext(assoc->params, i, &pc);
-            if (dcmIsaStorageSOPClassUID(pc.abstractSyntax))
+            /* accept storage syntaxes with proposed role */
+            int npc = ASC_countPresentationContexts(assoc->params);
+            for (int i = 0; i < npc; i++)
             {
-              /**
-               * We are prepared to accept whatever role the caller
-               * proposes.  Normally we can be the SCP of the Storage
-               * Service Class.  When processing the C-GET operation
-               * we can be the SCU of the Storage Service Class.
-               **/
-              const T_ASC_SC_ROLE role = pc.proposedRole;
-            
-              /**
-               * Accept in the order "least wanted" to "most wanted"
-               * transfer syntax.  Accepting a transfer syntax will
-               * override previously accepted transfer syntaxes.
-               **/
-              for (int k = static_cast<int>(storageTransferSyntaxes.size()) - 1; k >= 0; k--)
+              T_ASC_PresentationContext pc;
+              ASC_getPresentationContext(assoc->params, i, &pc);
+              if (dcmIsaStorageSOPClassUID(pc.abstractSyntax))
               {
-                for (int j = 0; j < static_cast<int>(pc.transferSyntaxCount); j++)
+                /**
+                 * We are prepared to accept whatever role the caller
+                 * proposes.  Normally we can be the SCP of the Storage
+                 * Service Class.  When processing the C-GET operation
+                 * we can be the SCU of the Storage Service Class.
+                 **/
+                const T_ASC_SC_ROLE role = pc.proposedRole;
+            
+                /**
+                 * Accept in the order "least wanted" to "most wanted"
+                 * transfer syntax.  Accepting a transfer syntax will
+                 * override previously accepted transfer syntaxes.
+                 **/
+                for (int k = static_cast<int>(storageTransferSyntaxesC.size()) - 1; k >= 0; k--)
                 {
-                  /**
-                   * If the transfer syntax was proposed then we can accept it
-                   * appears in our supported list of transfer syntaxes
-                   **/
-                  if (strcmp(pc.proposedTransferSyntaxes[j], storageTransferSyntaxes[k]) == 0)
+                  for (int j = 0; j < static_cast<int>(pc.transferSyntaxCount); j++)
                   {
-                    cond = ASC_acceptPresentationContext(
-                      assoc->params, pc.presentationContextID, storageTransferSyntaxes[k], role);
-                    if (cond.bad())
+                    /**
+                     * If the transfer syntax was proposed then we can accept it
+                     * appears in our supported list of transfer syntaxes
+                     **/
+                    if (strcmp(pc.proposedTransferSyntaxes[j], storageTransferSyntaxesC[k]) == 0)
                     {
-                      CLOG(INFO, DICOM) << cond.text();
-                      AssociationCleanup(assoc);
-                      return NULL;
+                      cond = ASC_acceptPresentationContext(
+                        assoc->params, pc.presentationContextID, storageTransferSyntaxesC[k], role);
+                      if (cond.bad())
+                      {
+                        CLOG(INFO, DICOM) << cond.text();
+                        AssociationCleanup(assoc);
+                        return NULL;
+                      }
                     }
                   }
                 }
               }
-            }
-          } /* for */
-        }
+            } /* for */
+          }
 
-        if (!server.HasApplicationEntityFilter() ||
-            server.GetApplicationEntityFilter().IsUnknownSopClassAccepted(remoteIp, remoteAet, calledAet))
-        {
-          /*
-           * Promiscous mode is enabled: Accept everything not known not
-           * to be a storage SOP class.
-           **/
-          cond = acceptUnknownContextsWithPreferredTransferSyntaxes(
-            assoc->params, &storageTransferSyntaxes[0], storageTransferSyntaxes.size(), ASC_SC_ROLE_DEFAULT);
-          if (cond.bad())
+          if (!server.HasApplicationEntityFilter() ||
+              server.GetApplicationEntityFilter().IsUnknownSopClassAccepted(remoteIp, remoteAet, calledAet))
           {
-            CLOG(INFO, DICOM) << cond.text();
-            AssociationCleanup(assoc);
-            return NULL;
+            /*
+             * Promiscous mode is enabled: Accept everything not known not
+             * to be a storage SOP class.
+             **/
+            cond = acceptUnknownContextsWithPreferredTransferSyntaxes(
+              assoc->params, &storageTransferSyntaxesC[0], storageTransferSyntaxesC.size(), ASC_SC_ROLE_DEFAULT);
+            if (cond.bad())
+            {
+              CLOG(INFO, DICOM) << cond.text();
+              AssociationCleanup(assoc);
+              return NULL;
+            }
           }
         }
       }
--- a/OrthancFramework/Sources/Enumerations.h	Thu Jan 21 19:06:00 2021 +0100
+++ b/OrthancFramework/Sources/Enumerations.h	Mon Jan 25 15:18:34 2021 +0100
@@ -24,6 +24,7 @@
 
 #include "OrthancFramework.h"
 
+#include <set>
 #include <string>
 
 
@@ -894,4 +895,7 @@
   const char* GetResourceTypeText(ResourceType type,
                                   bool isPlural,
                                   bool isLowerCase);
+
+  ORTHANC_PUBLIC
+  void GetAllTransferSyntaxes(std::set<DicomTransferSyntax>& target);
 }
--- a/OrthancFramework/Sources/Enumerations_TransferSyntaxes.impl.h	Thu Jan 21 19:06:00 2021 +0100
+++ b/OrthancFramework/Sources/Enumerations_TransferSyntaxes.impl.h	Mon Jan 25 15:18:34 2021 +0100
@@ -552,4 +552,52 @@
     
     return false;
   }
+
+
+  void GetAllTransferSyntaxes(std::set<DicomTransferSyntax>& target)
+  {
+    target.clear();
+    target.insert(DicomTransferSyntax_LittleEndianImplicit);
+    target.insert(DicomTransferSyntax_LittleEndianExplicit);
+    target.insert(DicomTransferSyntax_DeflatedLittleEndianExplicit);
+    target.insert(DicomTransferSyntax_BigEndianExplicit);
+    target.insert(DicomTransferSyntax_JPEGProcess1);
+    target.insert(DicomTransferSyntax_JPEGProcess2_4);
+    target.insert(DicomTransferSyntax_JPEGProcess3_5);
+    target.insert(DicomTransferSyntax_JPEGProcess6_8);
+    target.insert(DicomTransferSyntax_JPEGProcess7_9);
+    target.insert(DicomTransferSyntax_JPEGProcess10_12);
+    target.insert(DicomTransferSyntax_JPEGProcess11_13);
+    target.insert(DicomTransferSyntax_JPEGProcess14);
+    target.insert(DicomTransferSyntax_JPEGProcess15);
+    target.insert(DicomTransferSyntax_JPEGProcess16_18);
+    target.insert(DicomTransferSyntax_JPEGProcess17_19);
+    target.insert(DicomTransferSyntax_JPEGProcess20_22);
+    target.insert(DicomTransferSyntax_JPEGProcess21_23);
+    target.insert(DicomTransferSyntax_JPEGProcess24_26);
+    target.insert(DicomTransferSyntax_JPEGProcess25_27);
+    target.insert(DicomTransferSyntax_JPEGProcess28);
+    target.insert(DicomTransferSyntax_JPEGProcess29);
+    target.insert(DicomTransferSyntax_JPEGProcess14SV1);
+    target.insert(DicomTransferSyntax_JPEGLSLossless);
+    target.insert(DicomTransferSyntax_JPEGLSLossy);
+    target.insert(DicomTransferSyntax_JPEG2000LosslessOnly);
+    target.insert(DicomTransferSyntax_JPEG2000);
+    target.insert(DicomTransferSyntax_JPEG2000MulticomponentLosslessOnly);
+    target.insert(DicomTransferSyntax_JPEG2000Multicomponent);
+    target.insert(DicomTransferSyntax_JPIPReferenced);
+    target.insert(DicomTransferSyntax_JPIPReferencedDeflate);
+    target.insert(DicomTransferSyntax_MPEG2MainProfileAtMainLevel);
+    target.insert(DicomTransferSyntax_MPEG2MainProfileAtHighLevel);
+    target.insert(DicomTransferSyntax_MPEG4HighProfileLevel4_1);
+    target.insert(DicomTransferSyntax_MPEG4BDcompatibleHighProfileLevel4_1);
+    target.insert(DicomTransferSyntax_MPEG4HighProfileLevel4_2_For2DVideo);
+    target.insert(DicomTransferSyntax_MPEG4HighProfileLevel4_2_For3DVideo);
+    target.insert(DicomTransferSyntax_MPEG4StereoHighProfileLevel4_2);
+    target.insert(DicomTransferSyntax_HEVCMainProfileLevel5_1);
+    target.insert(DicomTransferSyntax_HEVCMain10ProfileLevel5_1);
+    target.insert(DicomTransferSyntax_RLELossless);
+    target.insert(DicomTransferSyntax_RFC2557MimeEncapsulation);
+    target.insert(DicomTransferSyntax_XML);
+  }
 }
--- a/OrthancServer/Sources/main.cpp	Thu Jan 21 19:06:00 2021 +0100
+++ b/OrthancServer/Sources/main.cpp	Mon Jan 25 15:18:34 2021 +0100
@@ -364,6 +364,92 @@
     }
   }
 
+
+  virtual void GetAcceptedTransferSyntaxes(std::set<DicomTransferSyntax>& target,
+                                           const std::string& remoteIp,
+                                           const std::string& remoteAet,
+                                           const std::string& calledAet) ORTHANC_OVERRIDE
+  {
+    target.clear();
+    
+    // This is the list of the transfer syntaxes that were supported up to Orthanc 0.7.1
+    target.insert(DicomTransferSyntax_LittleEndianExplicit);
+    target.insert(DicomTransferSyntax_BigEndianExplicit);
+    target.insert(DicomTransferSyntax_LittleEndianImplicit);
+
+    // New transfer syntaxes supported since Orthanc 0.7.2
+    if (IsAllowedTransferSyntax(remoteIp, remoteAet, calledAet, TransferSyntax_Deflated))
+    {
+      target.insert(DicomTransferSyntax_DeflatedLittleEndianExplicit);
+    }
+
+    if (IsAllowedTransferSyntax(remoteIp, remoteAet, calledAet, TransferSyntax_Jpeg))
+    {
+      target.insert(DicomTransferSyntax_JPEGProcess1);
+      target.insert(DicomTransferSyntax_JPEGProcess2_4);
+      target.insert(DicomTransferSyntax_JPEGProcess3_5);
+      target.insert(DicomTransferSyntax_JPEGProcess6_8);
+      target.insert(DicomTransferSyntax_JPEGProcess7_9);
+      target.insert(DicomTransferSyntax_JPEGProcess10_12);
+      target.insert(DicomTransferSyntax_JPEGProcess11_13);
+      target.insert(DicomTransferSyntax_JPEGProcess14);
+      target.insert(DicomTransferSyntax_JPEGProcess15);
+      target.insert(DicomTransferSyntax_JPEGProcess16_18);
+      target.insert(DicomTransferSyntax_JPEGProcess17_19);
+      target.insert(DicomTransferSyntax_JPEGProcess20_22);
+      target.insert(DicomTransferSyntax_JPEGProcess21_23);
+      target.insert(DicomTransferSyntax_JPEGProcess24_26);
+      target.insert(DicomTransferSyntax_JPEGProcess25_27);
+      target.insert(DicomTransferSyntax_JPEGProcess28);
+      target.insert(DicomTransferSyntax_JPEGProcess29);
+      target.insert(DicomTransferSyntax_JPEGProcess14SV1);
+    }
+
+    if (IsAllowedTransferSyntax(remoteIp, remoteAet, calledAet, TransferSyntax_Jpeg2000))
+    {
+      target.insert(DicomTransferSyntax_JPEG2000);
+      target.insert(DicomTransferSyntax_JPEG2000LosslessOnly);
+      target.insert(DicomTransferSyntax_JPEG2000Multicomponent);
+      target.insert(DicomTransferSyntax_JPEG2000MulticomponentLosslessOnly);
+    }
+
+    if (IsAllowedTransferSyntax(remoteIp, remoteAet, calledAet, TransferSyntax_JpegLossless))
+    {
+      target.insert(DicomTransferSyntax_JPEGLSLossless);
+      target.insert(DicomTransferSyntax_JPEGLSLossy);
+    }
+
+    if (IsAllowedTransferSyntax(remoteIp, remoteAet, calledAet, TransferSyntax_Jpip))
+    {
+      target.insert(DicomTransferSyntax_JPIPReferenced);
+      target.insert(DicomTransferSyntax_JPIPReferencedDeflate);
+    }
+
+    if (IsAllowedTransferSyntax(remoteIp, remoteAet, calledAet, TransferSyntax_Mpeg2))
+    {
+      target.insert(DicomTransferSyntax_MPEG2MainProfileAtMainLevel);
+      target.insert(DicomTransferSyntax_MPEG2MainProfileAtHighLevel);
+    }
+
+#if DCMTK_VERSION_NUMBER >= 361
+    // New in Orthanc 1.6.0
+    if (IsAllowedTransferSyntax(remoteIp, remoteAet, calledAet, TransferSyntax_Mpeg4))
+    {
+      target.insert(DicomTransferSyntax_MPEG4BDcompatibleHighProfileLevel4_1);
+      target.insert(DicomTransferSyntax_MPEG4HighProfileLevel4_1);
+      target.insert(DicomTransferSyntax_MPEG4HighProfileLevel4_2_For2DVideo);
+      target.insert(DicomTransferSyntax_MPEG4HighProfileLevel4_2_For3DVideo);
+      target.insert(DicomTransferSyntax_MPEG4StereoHighProfileLevel4_2);
+    }
+#endif
+        
+    if (IsAllowedTransferSyntax(remoteIp, remoteAet, calledAet, TransferSyntax_Rle))
+    {
+      target.insert(DicomTransferSyntax_RLELossless);
+    }
+  }
+
+  
   virtual bool IsAllowedTransferSyntax(const std::string& remoteIp,
                                        const std::string& remoteAet,
                                        const std::string& calledAet,