changeset 3810:e9b7e05bcd42 transcoding

integration mainline->transcoding
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 06 Apr 2020 12:36:49 +0200
parents 3ba143353f95 (current diff) 2bf30ef727e3 (diff)
children 890af7156f19
files Resources/Patches/openssl-1.1.1d-conf.h.in
diffstat 27 files changed, 451 insertions(+), 201 deletions(-) [+]
line wrap: on
line diff
--- a/AUTHORS	Fri Mar 20 12:38:00 2020 +0100
+++ b/AUTHORS	Mon Apr 06 12:36:49 2020 +0200
@@ -15,7 +15,7 @@
   Belgium
 
 * Osimis S.A.
-  Rue du Bois Saint-Jean 15/1
-  4102 Seraing
+  Quai Banning 6
+  4000 Liege
   Belgium
   http://www.osimis.io/
--- a/Core/DicomNetworking/DicomUserConnection.cpp	Fri Mar 20 12:38:00 2020 +0100
+++ b/Core/DicomNetworking/DicomUserConnection.cpp	Mon Apr 06 12:36:49 2020 +0200
@@ -736,9 +736,22 @@
     {
       char buf[16];
       sprintf(buf, "%04X", response.DimseStatus);
-      throw OrthancException(ErrorCode_NetworkProtocol,
-                             "C-FIND SCU to AET \"" + remoteAet +
-                             "\" has failed with DIMSE status 0x" + buf);
+
+      if (response.DimseStatus == STATUS_FIND_Failed_UnableToProcess)
+      {
+        throw OrthancException(ErrorCode_NetworkProtocol,
+                               HttpStatus_422_UnprocessableEntity,
+                               "C-FIND SCU to AET \"" + remoteAet +
+                               "\" has failed with DIMSE status 0x" + buf +
+                               " (unable to process - invalid query ?)"
+                               );
+      }
+      else
+      {
+        throw OrthancException(ErrorCode_NetworkProtocol,
+                               "C-FIND SCU to AET \"" + remoteAet +
+                               "\" has failed with DIMSE status 0x" + buf);
+      }
     }
 
   }
@@ -939,9 +952,22 @@
     {
       char buf[16];
       sprintf(buf, "%04X", response.DimseStatus);
-      throw OrthancException(ErrorCode_NetworkProtocol,
-                             "C-MOVE SCU to AET \"" + remoteAet_ +
-                             "\" has failed with DIMSE status 0x" + buf);
+
+      if (response.DimseStatus == STATUS_MOVE_Failed_UnableToProcess)
+      {
+        throw OrthancException(ErrorCode_NetworkProtocol,
+                               HttpStatus_422_UnprocessableEntity,
+                               "C-MOVE SCU to AET \"" + remoteAet_ +
+                               "\" has failed with DIMSE status 0x" + buf +
+                               " (unable to process - resource not found ?)"
+                               );
+      }
+      else
+      {
+        throw OrthancException(ErrorCode_NetworkProtocol,
+                               "C-MOVE SCU to AET \"" + remoteAet_ +
+                               "\" has failed with DIMSE status 0x" + buf);
+      }
     }
   }
 
@@ -1185,7 +1211,7 @@
 
   void DicomUserConnection::Store(std::string& sopClassUid /* out */,
                                   std::string& sopInstanceUid /* out */,
-                                  const char* buffer, 
+                                  const void* buffer, 
                                   size_t size,
                                   const std::string& moveOriginatorAET,
                                   uint16_t moveOriginatorID)
--- a/Core/DicomNetworking/DicomUserConnection.h	Fri Mar 20 12:38:00 2020 +0100
+++ b/Core/DicomNetworking/DicomUserConnection.h	Mon Apr 06 12:36:49 2020 +0200
@@ -160,14 +160,14 @@
 
     void Store(std::string& sopClassUid /* out */,
                std::string& sopInstanceUid /* out */,
-               const char* buffer, 
+               const void* buffer, 
                size_t size,
                const std::string& moveOriginatorAET,
                uint16_t moveOriginatorID);
 
     void Store(std::string& sopClassUid /* out */,
                std::string& sopInstanceUid /* out */,
-               const char* buffer, 
+               const void* buffer, 
                size_t size)
     {
       Store(sopClassUid, sopInstanceUid, buffer, size, "", 0);  // Not a C-Move
--- a/Core/Enumerations.cpp	Fri Mar 20 12:38:00 2020 +0100
+++ b/Core/Enumerations.cpp	Mon Apr 06 12:36:49 2020 +0200
@@ -189,6 +189,9 @@
       case ErrorCode_BadGeometry:
         return "Geometry error encountered in Stone";
 
+      case ErrorCode_SslInitialization:
+        return "Cannot initialize SSL encryption, check out your certificates";
+
       case ErrorCode_SQLiteNotOpened:
         return "SQLite: The database is not opened";
 
--- a/Core/Enumerations.h	Fri Mar 20 12:38:00 2020 +0100
+++ b/Core/Enumerations.h	Mon Apr 06 12:36:49 2020 +0200
@@ -180,6 +180,7 @@
     ErrorCode_DatabaseUnavailable = 36    /*!< The database is currently not available (probably a transient situation) */,
     ErrorCode_CanceledJob = 37    /*!< This job was canceled */,
     ErrorCode_BadGeometry = 38    /*!< Geometry error encountered in Stone */,
+    ErrorCode_SslInitialization = 39    /*!< Cannot initialize SSL encryption, check out your certificates */,
     ErrorCode_SQLiteNotOpened = 1000    /*!< SQLite: The database is not opened */,
     ErrorCode_SQLiteAlreadyOpened = 1001    /*!< SQLite: Connection is already open */,
     ErrorCode_SQLiteCannotOpen = 1002    /*!< SQLite: Unable to open the database */,
--- a/Core/HttpServer/HttpServer.cpp	Fri Mar 20 12:38:00 2020 +0100
+++ b/Core/HttpServer/HttpServer.cpp	Mon Apr 06 12:36:49 2020 +0200
@@ -72,7 +72,8 @@
 #endif
 
 #if ORTHANC_ENABLE_SSL == 1
-#include <openssl/opensslv.h>
+#  include <openssl/opensslv.h>
+#  include <openssl/err.h>
 #endif
 
 #define ORTHANC_REALM "Orthanc Secure Area"
@@ -1182,8 +1183,35 @@
 
       if (!pimpl_->context_)
       {
-        throw OrthancException(ErrorCode_HttpPortInUse,
-                               " (port = " + boost::lexical_cast<std::string>(port_) + ")");
+        bool isSslError = false;
+
+#if ORTHANC_ENABLE_SSL == 1
+        for (;;)
+        {
+          unsigned long code = ERR_get_error();
+          if (code == 0)
+          {
+            break;
+          }
+          else
+          {
+            isSslError = true;
+            char message[1024];
+            ERR_error_string_n(code, message, sizeof(message) - 1);
+            LOG(ERROR) << "OpenSSL error: " << message;
+          }
+        }        
+#endif
+
+        if (isSslError)
+        {
+          throw OrthancException(ErrorCode_SslInitialization);
+        }
+        else
+        {
+          throw OrthancException(ErrorCode_HttpPortInUse,
+                                 " (port = " + boost::lexical_cast<std::string>(port_) + ")");
+        }
       }
 
       LOG(WARNING) << "HTTP server listening on port: " << GetPortNumber()
--- a/Core/Toolbox.cpp	Fri Mar 20 12:38:00 2020 +0100
+++ b/Core/Toolbox.cpp	Mon Apr 06 12:36:49 2020 +0200
@@ -1680,21 +1680,36 @@
 #endif
 
 
+
+#if ORTHANC_ENABLE_SSL == 0
+  /**
+   * OpenSSL is disabled
+   **/
   void Toolbox::InitializeOpenSsl()
   {
-#if ORTHANC_ENABLE_SSL == 1
+  }
+  
+  void Toolbox::FinalizeOpenSsl()
+  {
+  }  
+
+
+#elif (ORTHANC_ENABLE_SSL == 1 &&               \
+       OPENSSL_VERSION_NUMBER < 0x10100000L) 
+  /**
+   * OpenSSL < 1.1.0
+   **/
+  void Toolbox::InitializeOpenSsl()
+  {
     // https://wiki.openssl.org/index.php/Library_Initialization
     SSL_library_init();
     SSL_load_error_strings();
     OpenSSL_add_all_algorithms();
     ERR_load_crypto_strings();
-#endif
   }
 
-
   void Toolbox::FinalizeOpenSsl()
   {
-#if ORTHANC_ENABLE_SSL == 1
     // Finalize OpenSSL
     // https://wiki.openssl.org/index.php/Library_Initialization#Cleanup
 #ifdef FIPS_mode_set
@@ -1710,8 +1725,28 @@
     CRYPTO_cleanup_all_ex_data();
     ERR_remove_state(0);
     ERR_free_strings();
+  }
+
+  
+#elif (ORTHANC_ENABLE_SSL == 1 &&               \
+       OPENSSL_VERSION_NUMBER >= 0x10100000L) 
+  /**
+   * OpenSSL >= 1.1.0. In this case, the initialization is
+   * automatically done by the functions of OpenSSL.
+   * https://wiki.openssl.org/index.php/Library_Initialization
+   **/
+  void Toolbox::InitializeOpenSsl()
+  {
+  }
+
+  void Toolbox::FinalizeOpenSsl()
+  {
+  }
+
+#else
+#  error "Support your platform here"
 #endif
-  }
+  
 
 
   std::string Toolbox::GenerateUuid()
--- a/NEWS	Fri Mar 20 12:38:00 2020 +0100
+++ b/NEWS	Mon Apr 06 12:36:49 2020 +0200
@@ -2,6 +2,28 @@
 ===============================
 
 
+REST API
+--------
+
+* API version has been upgraded to 6
+* Added:
+  - "/modalities/{id}/store-straight": Synchronously send the DICOM instance in POST
+    body to another modality (alternative to command-line tools such as "storescu")
+
+
+Maintenance
+-----------
+
+* Source code repository moved from BitBucket to self-hosted server
+* Fix OpenSSL initialization on Linux Standard Base
+* Fix lookup form in Orthanc Explorer (wildcards not allowed in StudyDate)
+* Fix signature of "OrthancPluginRegisterStorageCommitmentScpCallback()" in plugins SDK
+* Error reporting on failure while initializing SSL
+* Upgraded dependencies for static builds (notably on Windows):
+  - civetweb 1.12
+  - openssl 1.1.1f
+
+
 Version 1.6.0 (2020-03-18)
 ==========================
 
--- a/OrthancExplorer/explorer.js	Fri Mar 20 12:38:00 2020 +0100
+++ b/OrthancExplorer/explorer.js	Mon Apr 06 12:36:49 2020 +0200
@@ -431,7 +431,7 @@
   // NB: "GenerateDicomDate()" is defined in "query-retrieve.js"
   var target = $('#lookup-study-date');
   $('option', target).remove();
-  target.append($('<option>').attr('value', '*').text('Any date'));
+  target.append($('<option>').attr('value', '').text('Any date'));
   target.append($('<option>').attr('value', GenerateDicomDate(0)).text('Today'));
   target.append($('<option>').attr('value', GenerateDicomDate(-1)).text('Yesterday'));
   target.append($('<option>').attr('value', GenerateDicomDate(-7) + '-').text('Last 7 days'));
--- a/OrthancExplorer/query-retrieve.js	Fri Mar 20 12:38:00 2020 +0100
+++ b/OrthancExplorer/query-retrieve.js	Mon Apr 06 12:36:49 2020 +0200
@@ -38,7 +38,7 @@
 
   targetDate = $('#qr-date');
   $('option', targetDate).remove();
-  targetDate.append($('<option>').attr('value', '*').text('Any date'));
+  targetDate.append($('<option>').attr('value', '').text('Any date'));
   targetDate.append($('<option>').attr('value', GenerateDicomDate(0)).text('Today'));
   targetDate.append($('<option>').attr('value', GenerateDicomDate(-1)).text('Yesterday'));
   targetDate.append($('<option>').attr('value', GenerateDicomDate(-7) + '-').text('Last 7 days'));
@@ -90,7 +90,7 @@
       'PatientName' : '',
       'PatientSex' : '',
       'StudyDate' : $('#qr-date').val(),
-      'StudyDescription' : '*'
+      'StudyDescription' : ''
     }
   };
 
@@ -204,10 +204,10 @@
     query = {
       'Level' : 'Series',
       'Query' : {
-        'Modality' : '*',
-        'ProtocolName' : '*',
-        'SeriesDescription' : '*',
-        'SeriesInstanceUID' : '*',
+        'Modality' : '',
+        'ProtocolName' : '',
+        'SeriesDescription' : '',
+        'SeriesInstanceUID' : '',
         'StudyInstanceUID' : pageData.uuid
       }
     };
--- a/OrthancServer/Database/SQLiteDatabaseWrapper.cpp	Fri Mar 20 12:38:00 2020 +0100
+++ b/OrthancServer/Database/SQLiteDatabaseWrapper.cpp	Mon Apr 06 12:36:49 2020 +0200
@@ -295,19 +295,36 @@
 
   int64_t SQLiteDatabaseWrapper::GetTableRecordCount(const std::string& table)
   {
-    char buf[128];
-    sprintf(buf, "SELECT COUNT(*) FROM %s", table.c_str());
-    SQLite::Statement s(db_, buf);
+    /**
+     * "Generally one cannot use SQL parameters/placeholders for
+     * database identifiers (tables, columns, views, schemas, etc.) or
+     * database functions (e.g., CURRENT_DATE), but instead only for
+     * binding literal values." => To avoid any SQL injection, we
+     * check that the "table" parameter has only alphabetic
+     * characters.
+     * https://stackoverflow.com/a/1274764/881731
+     **/
+    for (size_t i = 0; i < table.size(); i++)
+    {
+      if (!isalpha(table[i]))
+      {
+        throw OrthancException(ErrorCode_ParameterOutOfRange);
+      }
+    }
 
-    if (!s.Step())
+    // Don't use "SQLITE_FROM_HERE", otherwise "table" would be cached
+    SQLite::Statement s(db_, "SELECT COUNT(*) FROM " + table);
+
+    if (s.Step())
+    {
+      int64_t c = s.ColumnInt(0);
+      assert(!s.Step());
+      return c;
+    }
+    else
     {
       throw OrthancException(ErrorCode_InternalError);
     }
-
-    int64_t c = s.ColumnInt(0);
-    assert(!s.Step());
-
-    return c;
   }
 
     
--- a/OrthancServer/OrthancRestApi/OrthancRestModalities.cpp	Fri Mar 20 12:38:00 2020 +0100
+++ b/OrthancServer/OrthancRestApi/OrthancRestModalities.cpp	Mon Apr 06 12:36:49 2020 +0200
@@ -55,6 +55,8 @@
   static const char* const KEY_QUERY = "Query";
   static const char* const KEY_NORMALIZE = "Normalize";
   static const char* const KEY_RESOURCES = "Resources";
+  static const char* const SOP_CLASS_UID = "SOPClassUID";
+  static const char* const SOP_INSTANCE_UID = "SOPInstanceUID";
 
   
   static RemoteModalityParameters MyGetModalityUsingSymbolicName(const std::string& name)
@@ -975,6 +977,29 @@
   }
 
 
+  static void DicomStoreStraight(RestApiPostCall& call)
+  {
+    ServerContext& context = OrthancRestApi::GetContext(call);
+
+    const std::string& localAet = context.GetDefaultLocalApplicationEntityTitle();
+    RemoteModalityParameters remote =
+      MyGetModalityUsingSymbolicName(call.GetUriComponent("id", ""));
+
+    DicomUserConnection connection(localAet, remote);
+    connection.Open();
+
+    std::string sopClassUid, sopInstanceUid;
+    connection.Store(sopClassUid, sopInstanceUid,
+                     call.GetBodyData(), call.GetBodySize());
+
+    Json::Value answer = Json::objectValue;
+    answer[SOP_CLASS_UID] = sopClassUid;
+    answer[SOP_INSTANCE_UID] = sopInstanceUid;
+    
+    call.GetOutput().AnswerJson(answer);
+  }
+
+
   /***************************************************************************
    * DICOM C-Move SCU
    ***************************************************************************/
@@ -1312,8 +1337,6 @@
   {
     static const char* const ORTHANC_RESOURCES = "Resources";
     static const char* const DICOM_INSTANCES = "DicomInstances";
-    static const char* const SOP_CLASS_UID = "SOPClassUID";
-    static const char* const SOP_INSTANCE_UID = "SOPInstanceUID";
 
     ServerContext& context = OrthancRestApi::GetContext(call);
 
@@ -1564,6 +1587,7 @@
     Register("/modalities/{id}/find-instance", DicomFindInstance);
     Register("/modalities/{id}/find", DicomFind);
     Register("/modalities/{id}/store", DicomStore);
+    Register("/modalities/{id}/store-straight", DicomStoreStraight);  // New in 1.6.1
     Register("/modalities/{id}/move", DicomMove);
 
     // For Query/Retrieve
--- a/OrthancServer/Search/DicomTagConstraint.cpp	Fri Mar 20 12:38:00 2020 +0100
+++ b/OrthancServer/Search/DicomTagConstraint.cpp	Mon Apr 06 12:36:49 2020 +0200
@@ -105,7 +105,8 @@
         (value.find('*') != std::string::npos ||
          value.find('?') != std::string::npos))
     {
-      throw OrthancException(ErrorCode_ParameterOutOfRange);
+      throw OrthancException(ErrorCode_ParameterOutOfRange,
+                             "Wildcards are not allowed on tag " + tag_.Format());
     }
 
     if (constraintType_ == ConstraintType_Equal ||
--- a/OrthancServer/main.cpp	Fri Mar 20 12:38:00 2020 +0100
+++ b/OrthancServer/main.cpp	Mon Apr 06 12:36:49 2020 +0200
@@ -700,6 +700,7 @@
     PrintErrorCode(ErrorCode_DatabaseUnavailable, "The database is currently not available (probably a transient situation)");
     PrintErrorCode(ErrorCode_CanceledJob, "This job was canceled");
     PrintErrorCode(ErrorCode_BadGeometry, "Geometry error encountered in Stone");
+    PrintErrorCode(ErrorCode_SslInitialization, "Cannot initialize SSL encryption, check out your certificates");
     PrintErrorCode(ErrorCode_SQLiteNotOpened, "SQLite: The database is not opened");
     PrintErrorCode(ErrorCode_SQLiteAlreadyOpened, "SQLite: Connection is already open");
     PrintErrorCode(ErrorCode_SQLiteCannotOpen, "SQLite: Unable to open the database");
--- a/Plugins/Include/orthanc/OrthancCPlugin.h	Fri Mar 20 12:38:00 2020 +0100
+++ b/Plugins/Include/orthanc/OrthancCPlugin.h	Mon Apr 06 12:36:49 2020 +0200
@@ -243,6 +243,7 @@
     OrthancPluginErrorCode_DatabaseUnavailable = 36    /*!< The database is currently not available (probably a transient situation) */,
     OrthancPluginErrorCode_CanceledJob = 37    /*!< This job was canceled */,
     OrthancPluginErrorCode_BadGeometry = 38    /*!< Geometry error encountered in Stone */,
+    OrthancPluginErrorCode_SslInitialization = 39    /*!< Cannot initialize SSL encryption, check out your certificates */,
     OrthancPluginErrorCode_SQLiteNotOpened = 1000    /*!< SQLite: The database is not opened */,
     OrthancPluginErrorCode_SQLiteAlreadyOpened = 1001    /*!< SQLite: Connection is already open */,
     OrthancPluginErrorCode_SQLiteCannotOpen = 1002    /*!< SQLite: Unable to open the database */,
@@ -1062,7 +1063,7 @@
    * @brief Opaque structure to an object that can be used to check whether a DICOM instance matches a C-Find query.
    * @ingroup Toolbox
    **/
-  typedef struct _OrthancPluginFindAnswers_t OrthancPluginFindMatcher;
+  typedef struct _OrthancPluginFindMatcher_t OrthancPluginFindMatcher;
 
 
   
@@ -2778,7 +2779,7 @@
    * @return The pointer to the DICOM data, NULL in case of error.
    * @ingroup Callbacks
    **/
-  ORTHANC_PLUGIN_INLINE const char* OrthancPluginGetInstanceData(
+  ORTHANC_PLUGIN_INLINE const void* OrthancPluginGetInstanceData(
     OrthancPluginContext*        context,
     OrthancPluginDicomInstance*  instance)
   {
@@ -7398,7 +7399,7 @@
    * @return 0 if success, other value if error.
    * @ingroup DicomCallbacks
    **/
-  ORTHANC_PLUGIN_INLINE void OrthancPluginRegisterStorageCommitmentScpCallback(
+  ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode OrthancPluginRegisterStorageCommitmentScpCallback(
     OrthancPluginContext*                     context,
     OrthancPluginStorageCommitmentFactory     factory,
     OrthancPluginStorageCommitmentDestructor  destructor,
@@ -7408,7 +7409,7 @@
     params.factory = factory;
     params.destructor = destructor;
     params.lookup = lookup;
-    context->InvokeService(context, _OrthancPluginService_RegisterStorageCommitmentScpCallback, &params);
+    return context->InvokeService(context, _OrthancPluginService_RegisterStorageCommitmentScpCallback, &params);
   }
   
 #ifdef  __cplusplus
--- a/Plugins/Samples/Common/OrthancPluginCppWrapper.h	Fri Mar 20 12:38:00 2020 +0100
+++ b/Plugins/Samples/Common/OrthancPluginCppWrapper.h	Mon Apr 06 12:36:49 2020 +0200
@@ -303,7 +303,7 @@
   public:
     OrthancConfiguration();
 
-    OrthancConfiguration(bool load);
+    explicit OrthancConfiguration(bool load);
 
     const Json::Value& GetJson() const
     {
@@ -369,7 +369,7 @@
   public:
     OrthancImage();
 
-    OrthancImage(OrthancPluginImage*    image);
+    explicit OrthancImage(OrthancPluginImage* image);
 
     OrthancImage(OrthancPluginPixelFormat  format,
                  uint32_t                  width,
@@ -435,15 +435,15 @@
                     uint32_t               size);
 
   public:
-    FindMatcher(const OrthancPluginWorklistQuery*  worklist);
+    explicit FindMatcher(const OrthancPluginWorklistQuery*  worklist);
 
-    FindMatcher(const void*            query,
-                uint32_t               size)
+    FindMatcher(const void*  query,
+                uint32_t     size)
     {
       SetupDicom(query, size);
     }
 
-    FindMatcher(const MemoryBuffer&    dicom)
+    explicit FindMatcher(const MemoryBuffer&  dicom)
     {
       SetupDicom(dicom.GetData(), dicom.GetSize());
     }
@@ -810,7 +810,7 @@
     boost::posix_time::ptime  start_;
 
   public:
-    MetricsTimer(const char* name);
+    explicit MetricsTimer(const char* name);
 
     ~MetricsTimer();
   };
--- a/Resources/CMake/CivetwebConfiguration.cmake	Fri Mar 20 12:38:00 2020 +0100
+++ b/Resources/CMake/CivetwebConfiguration.cmake	Mon Apr 06 12:36:49 2020 +0200
@@ -1,7 +1,20 @@
 if (STATIC_BUILD OR NOT USE_SYSTEM_CIVETWEB)
-  set(CIVETWEB_SOURCES_DIR ${CMAKE_BINARY_DIR}/civetweb-1.11)
-  set(CIVETWEB_URL "http://orthanc.osimis.io/ThirdPartyDownloads/civetweb-1.11.tar.gz")
-  set(CIVETWEB_MD5 "b6d2175650a27924bccb747cbe084cd4")
+
+  ## WARNING: "civetweb-1.12.tar.gz" comes with a subfolder
+  ## "civetweb-1.12/test/nonlatin" that cannot be removed by "hg purge
+  ## --all" on Windows hosts. We thus created a custom
+  ## "civetweb-1.12-fixed.tar.gz" as follows:
+  ##
+  ##  $ cd /tmp
+  ##  $ wget http://orthanc.osimis.io/ThirdPartyDownloads/civetweb-1.12.tar.gz
+  ##  $ tar xvf civetweb-1.12.tar.gz
+  ##  $ rm -rf civetweb-1.12/src/third_party/ civetweb-1.12/test/
+  ##  $ tar cvfz civetweb-1.12-fixed.tar.gz civetweb-1.12
+  ##
+  
+  set(CIVETWEB_SOURCES_DIR ${CMAKE_BINARY_DIR}/civetweb-1.12)
+  set(CIVETWEB_URL "http://orthanc.osimis.io/ThirdPartyDownloads/civetweb-1.12-fixed.tar.gz")
+  set(CIVETWEB_MD5 "016ed7cd26cbc46b5941f0cbfb2e4ac8")
 
   if (IS_DIRECTORY "${CIVETWEB_SOURCES_DIR}")
     set(FirstRun OFF)
@@ -13,7 +26,7 @@
 
   execute_process(
     COMMAND ${PATCH_EXECUTABLE} -p0 -N -i
-    ${ORTHANC_ROOT}/Resources/Patches/civetweb-1.11.patch
+    ${ORTHANC_ROOT}/Resources/Patches/civetweb-1.12.patch
     WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
     RESULT_VARIABLE Failure
     )
--- a/Resources/CMake/OpenSslConfigurationStatic-1.1.1.cmake	Fri Mar 20 12:38:00 2020 +0100
+++ b/Resources/CMake/OpenSslConfigurationStatic-1.1.1.cmake	Mon Apr 06 12:36:49 2020 +0200
@@ -1,6 +1,6 @@
-SET(OPENSSL_SOURCES_DIR ${CMAKE_BINARY_DIR}/openssl-1.1.1d)
-SET(OPENSSL_URL "http://orthanc.osimis.io/ThirdPartyDownloads/openssl-1.1.1d.tar.gz")
-SET(OPENSSL_MD5 "3be209000dbc7e1b95bcdf47980a3baa")
+SET(OPENSSL_SOURCES_DIR ${CMAKE_BINARY_DIR}/openssl-1.1.1f)
+SET(OPENSSL_URL "http://orthanc.osimis.io/ThirdPartyDownloads/openssl-1.1.1f.tar.gz")
+SET(OPENSSL_MD5 "3f486f2f4435ef14b81814dbbc7b48bb")
 
 if (IS_DIRECTORY "${OPENSSL_SOURCES_DIR}")
   set(FirstRun OFF)
@@ -16,18 +16,18 @@
 #define PLATFORM \"\"
 #define compiler_flags \"\"
 ")
-  file(WRITE ${OPENSSL_SOURCES_DIR}/crypto/include/internal/bn_conf.h "")
-  file(WRITE ${OPENSSL_SOURCES_DIR}/crypto/include/internal/dso_conf.h "")
+  file(WRITE ${OPENSSL_SOURCES_DIR}/crypto/bn_conf.h "")
+  file(WRITE ${OPENSSL_SOURCES_DIR}/crypto/dso_conf.h "")
 
   configure_file(
-    ${ORTHANC_ROOT}/Resources/Patches/openssl-1.1.1d-conf.h.in
+    ${ORTHANC_ROOT}/Resources/Patches/openssl-1.1.1-conf.h.in
     ${OPENSSL_SOURCES_DIR}/include/openssl/opensslconf.h
     )
 
   # Apply the patches
   execute_process(
     COMMAND ${PATCH_EXECUTABLE} -p0 -N -i
-    ${ORTHANC_ROOT}/Resources/Patches/openssl-1.1.1d.patch
+    ${ORTHANC_ROOT}/Resources/Patches/openssl-1.1.1f.patch
     WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
     RESULT_VARIABLE Failure
     )
@@ -231,9 +231,22 @@
     )
  
 elseif ("${CMAKE_SYSTEM_VERSION}" STREQUAL "LinuxStandardBase")
-  # In order for "crypto/mem_sec.c" to compile on LSB
   add_definitions(
+    # In order for "crypto/mem_sec.c" to compile on LSB
     -DOPENSSL_NO_SECURE_MEMORY
+
+    # The "OPENSSL_RAND_SEED_OS" value implies a syscall() to
+    # "__NR_getrandom" (i.e. system call "getentropy(2)") in
+    # "rand_unix.c", which is not available in LSB.
+    -DOPENSSL_RAND_SEED_DEVRANDOM
+    )
+
+else()
+  # Fixes error "OpenSSL error: error:2406C06E:random number
+  # generator:RAND_DRBG_instantiate:error retrieving entropy" that was
+  # present in Orthanc 1.6.0, if statically linking on Ubuntu 18.04
+  add_definitions(
+    -DOPENSSL_RAND_SEED_OS
     )
 endif()
 
--- a/Resources/CMake/OrthancFrameworkParameters.cmake	Fri Mar 20 12:38:00 2020 +0100
+++ b/Resources/CMake/OrthancFrameworkParameters.cmake	Mon Apr 06 12:36:49 2020 +0200
@@ -17,7 +17,7 @@
 # Version of the Orthanc API, can be retrieved from "/system" URI in
 # order to check whether new URI endpoints are available even if using
 # the mainline version of Orthanc
-set(ORTHANC_API_VERSION "5")
+set(ORTHANC_API_VERSION "6")
 
 
 #####################################################################
--- a/Resources/ErrorCodes.json	Fri Mar 20 12:38:00 2020 +0100
+++ b/Resources/ErrorCodes.json	Mon Apr 06 12:36:49 2020 +0200
@@ -217,6 +217,11 @@
     "Name": "BadGeometry", 
     "Description": "Geometry error encountered in Stone"
   }, 
+  {
+    "Code": 39, 
+    "Name": "SslInitialization", 
+    "Description": "Cannot initialize SSL encryption, check out your certificates"
+  }, 
 
 
 
--- a/Resources/LinuxStandardBaseToolchain.cmake	Fri Mar 20 12:38:00 2020 +0100
+++ b/Resources/LinuxStandardBaseToolchain.cmake	Mon Apr 06 12:36:49 2020 +0200
@@ -1,4 +1,12 @@
-# LSB_CC=gcc-4.8 LSB_CXX=g++-4.8 cmake .. -DCMAKE_BUILD_TYPE=Debug -DCMAKE_TOOLCHAIN_FILE=../Resources/LinuxStandardBaseToolchain.cmake -DUSE_LEGACY_JSONCPP=ON -DUSE_LEGACY_LIBICU=ON -DBOOST_LOCALE_BACKEND=icu -G Ninja
+#
+# Full build, as used on the BuildBot CIS:
+#
+#   $ LSB_CC=gcc-4.8 LSB_CXX=g++-4.8 cmake .. -DCMAKE_BUILD_TYPE=Debug -DCMAKE_TOOLCHAIN_FILE=../Resources/LinuxStandardBaseToolchain.cmake -DUSE_LEGACY_JSONCPP=ON -DUSE_LEGACY_LIBICU=ON -DBOOST_LOCALE_BACKEND=icu -DENABLE_PKCS11=ON -G Ninja
+#
+# Or, more lightweight version (without libp11 and ICU):
+#
+#   $ LSB_CC=gcc-4.8 LSB_CXX=g++-4.8 cmake .. -DCMAKE_BUILD_TYPE=Debug -DCMAKE_TOOLCHAIN_FILE=../Resources/LinuxStandardBaseToolchain.cmake -DUSE_LEGACY_JSONCPP=ON -G Ninja
+#
 
 INCLUDE(CMakeForceCompiler)
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/Patches/civetweb-1.12.patch	Mon Apr 06 12:36:49 2020 +0200
@@ -0,0 +1,29 @@
+diff -urEb civetweb-1.12.orig/include/civetweb.h civetweb-1.12/include/civetweb.h
+--- civetweb-1.12.orig/include/civetweb.h	2020-04-02 12:07:20.727054140 +0200
++++ civetweb-1.12/include/civetweb.h	2020-04-02 12:07:42.734996559 +0200
+@@ -1614,6 +1614,9 @@
+                                   struct mg_error_data *error);
+ #endif
+ 
++// Added by SJ
++CIVETWEB_API void mg_disable_keep_alive(struct mg_connection *conn);
++
+ #ifdef __cplusplus
+ }
+ #endif /* __cplusplus */
+diff -urEb civetweb-1.12.orig/src/civetweb.c civetweb-1.12/src/civetweb.c
+--- civetweb-1.12.orig/src/civetweb.c	2020-04-02 12:07:20.731054129 +0200
++++ civetweb-1.12/src/civetweb.c	2020-04-02 12:07:52.250971600 +0200
+@@ -20704,5 +20704,12 @@
+ 	return 1;
+ }
+ 
++// Added by SJ
++void mg_disable_keep_alive(struct mg_connection *conn)
++{
++  if (conn != NULL) {
++    conn->must_close = 1;
++  }
++}
+ 
+ /* End of civetweb.c */
--- a/Resources/Patches/libp11-0.4.0.patch	Fri Mar 20 12:38:00 2020 +0100
+++ b/Resources/Patches/libp11-0.4.0.patch	Mon Apr 06 12:36:49 2020 +0200
@@ -1,6 +1,6 @@
 diff -urEb libp11-0.4.0.orig/src/atfork.c libp11-0.4.0/src/atfork.c
---- libp11-0.4.0.orig/src/atfork.c	2020-03-05 20:48:55.447852662 +0100
-+++ libp11-0.4.0/src/atfork.c	2020-03-05 20:49:05.983770656 +0100
+--- libp11-0.4.0.orig/src/atfork.c	2020-04-02 17:03:55.340634019 +0200
++++ libp11-0.4.0/src/atfork.c	2020-04-02 17:04:10.152619121 +0200
 @@ -25,7 +25,7 @@
  #include <sys/stat.h>
  #include <sys/types.h>
@@ -11,8 +11,8 @@
  #ifdef __sun
  # pragma fini(lib_deinit)
 diff -urEb libp11-0.4.0.orig/src/engine.h libp11-0.4.0/src/engine.h
---- libp11-0.4.0.orig/src/engine.h	2020-03-05 20:48:55.447852662 +0100
-+++ libp11-0.4.0/src/engine.h	2020-03-05 20:49:05.983770656 +0100
+--- libp11-0.4.0.orig/src/engine.h	2020-04-02 17:03:55.340634019 +0200
++++ libp11-0.4.0/src/engine.h	2020-04-02 17:04:10.152619121 +0200
 @@ -29,7 +29,7 @@
  #define _ENGINE_PKCS11_H
  
@@ -23,8 +23,8 @@
  
  #include "libp11.h"
 diff -urEb libp11-0.4.0.orig/src/libp11-int.h libp11-0.4.0/src/libp11-int.h
---- libp11-0.4.0.orig/src/libp11-int.h	2020-03-05 20:48:55.447852662 +0100
-+++ libp11-0.4.0/src/libp11-int.h	2020-03-05 20:49:05.983770656 +0100
+--- libp11-0.4.0.orig/src/libp11-int.h	2020-04-02 17:03:55.340634019 +0200
++++ libp11-0.4.0/src/libp11-int.h	2020-04-02 17:04:10.152619121 +0200
 @@ -20,7 +20,7 @@
  #define _LIBP11_INT_H
  
@@ -35,13 +35,15 @@
  
  #include "libp11.h"
 diff -urEb libp11-0.4.0.orig/src/p11_key.c libp11-0.4.0/src/p11_key.c
---- libp11-0.4.0.orig/src/p11_key.c	2020-03-05 20:48:55.447852662 +0100
-+++ libp11-0.4.0/src/p11_key.c	2020-03-05 20:49:24.959625180 +0100
-@@ -21,6 +21,10 @@
+--- libp11-0.4.0.orig/src/p11_key.c	2020-04-02 17:03:55.340634019 +0200
++++ libp11-0.4.0/src/p11_key.c	2020-04-02 17:05:39.892516032 +0200
+@@ -21,6 +21,12 @@
  #include <string.h>
  #include <openssl/bn.h>
  
-+#if OPENSSL_VERSION_NUMBER >= 0x10100000L // OpenSSL 1.0.2
++#if OPENSSL_VERSION_NUMBER >= 0x10100105L // File renamed in OpenSSL 1.1.1e
++#  include <crypto/rsa/rsa_local.h>
++#elif OPENSSL_VERSION_NUMBER >= 0x10100000L // OpenSSL 1.0.2
 +#  include <crypto/rsa/rsa_locl.h>
 +#endif
 +
@@ -49,13 +51,15 @@
  #define strncasecmp strnicmp
  #endif
 diff -urEb libp11-0.4.0.orig/src/p11_rsa.c libp11-0.4.0/src/p11_rsa.c
---- libp11-0.4.0.orig/src/p11_rsa.c	2020-03-05 20:48:55.447852662 +0100
-+++ libp11-0.4.0/src/p11_rsa.c	2020-03-05 20:49:20.095662204 +0100
-@@ -27,6 +27,10 @@
+--- libp11-0.4.0.orig/src/p11_rsa.c	2020-04-02 17:03:55.340634019 +0200
++++ libp11-0.4.0/src/p11_rsa.c	2020-04-02 17:05:49.176504198 +0200
+@@ -27,6 +27,12 @@
  #include <openssl/evp.h>
  #include <openssl/rsa.h>
  
-+#if OPENSSL_VERSION_NUMBER >= 0x10100000L // OpenSSL 1.0.2
++#if OPENSSL_VERSION_NUMBER >= 0x10100105L // File renamed in OpenSSL 1.1.1e
++#  include <crypto/rsa/rsa_local.h>
++#elif OPENSSL_VERSION_NUMBER >= 0x10100000L // OpenSSL 1.0.2
 +#  include <crypto/rsa/rsa_locl.h>
 +#endif
 +
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/Patches/openssl-1.1.1-conf.h.in	Mon Apr 06 12:36:49 2020 +0200
@@ -0,0 +1,122 @@
+/*
+ * {- join("\n * ", @autowarntext) -}
+ *
+ * Copyright 2016-2018 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/opensslv.h>
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+#ifdef OPENSSL_ALGORITHM_DEFINES
+# error OPENSSL_ALGORITHM_DEFINES no longer supported
+#endif
+
+
+/*
+ * Sometimes OPENSSSL_NO_xxx ends up with an empty file and some compilers
+ * don't like that.  This will hopefully silence them.
+ */
+#define NON_EMPTY_TRANSLATION_UNIT static void *dummy = &dummy;
+
+/*
+ * Applications should use -DOPENSSL_API_COMPAT=<version> to suppress the
+ * declarations of functions deprecated in or before <version>. Otherwise, they
+ * still won't see them if the library has been built to disable deprecated
+ * functions.
+ */
+#ifndef DECLARE_DEPRECATED
+# define DECLARE_DEPRECATED(f)   f;
+# ifdef __GNUC__
+#  if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ > 0)
+#   undef DECLARE_DEPRECATED
+#   define DECLARE_DEPRECATED(f)    f __attribute__ ((deprecated));
+#  endif
+# endif
+#endif
+
+#ifndef OPENSSL_FILE
+# ifdef OPENSSL_NO_FILENAMES
+#  define OPENSSL_FILE ""
+#  define OPENSSL_LINE 0
+# else
+#  define OPENSSL_FILE __FILE__
+#  define OPENSSL_LINE __LINE__
+# endif
+#endif
+
+#ifndef OPENSSL_MIN_API
+# define OPENSSL_MIN_API 0
+#endif
+
+#if !defined(OPENSSL_API_COMPAT) || OPENSSL_API_COMPAT < OPENSSL_MIN_API
+# undef OPENSSL_API_COMPAT
+# define OPENSSL_API_COMPAT OPENSSL_MIN_API
+#endif
+
+/*
+ * Do not deprecate things to be deprecated in version 1.2.0 before the
+ * OpenSSL version number matches.
+ */
+#if OPENSSL_VERSION_NUMBER < 0x10200000L
+# define DEPRECATEDIN_1_2_0(f)   f;
+#elif OPENSSL_API_COMPAT < 0x10200000L
+# define DEPRECATEDIN_1_2_0(f)   DECLARE_DEPRECATED(f)
+#else
+# define DEPRECATEDIN_1_2_0(f)
+#endif
+
+#if OPENSSL_API_COMPAT < 0x10100000L
+# define DEPRECATEDIN_1_1_0(f)   DECLARE_DEPRECATED(f)
+#else
+# define DEPRECATEDIN_1_1_0(f)
+#endif
+
+#if OPENSSL_API_COMPAT < 0x10000000L
+# define DEPRECATEDIN_1_0_0(f)   DECLARE_DEPRECATED(f)
+#else
+# define DEPRECATEDIN_1_0_0(f)
+#endif
+
+#if OPENSSL_API_COMPAT < 0x00908000L
+# define DEPRECATEDIN_0_9_8(f)   DECLARE_DEPRECATED(f)
+#else
+# define DEPRECATEDIN_0_9_8(f)
+#endif
+
+
+#define OPENSSL_UNISTD <unistd.h>
+
+#if 0
+/* Generate 80386 code? */
+{- ${processor} eq "386" ? "#define" : "#undef" -} I386_ONLY
+
+#undef OPENSSL_UNISTD
+#define OPENSSL_UNISTD {- ${unistd} -}
+
+{- ${export_var_as_fn} ? "#define" : "#undef" -} OPENSSL_EXPORT_VAR_AS_FUNCTION
+
+/*
+ * The following are cipher-specific, but are part of the public API.
+ */
+#if !defined(OPENSSL_SYS_UEFI)
+{- ${bn_ll} ? "# define" : "# undef" -} BN_LLONG
+/* Only one for the following should be defined */
+{- ${b64l} ? "# define" : "# undef" -} SIXTY_FOUR_BIT_LONG
+{- ${b64}  ? "# define" : "# undef" -} SIXTY_FOUR_BIT
+{- ${b32}  ? "# define" : "# undef" -} THIRTY_TWO_BIT
+#endif
+
+#define RC4_INT {- ${rc4_int} -}
+#endif
+
+#ifdef  __cplusplus
+}
+#endif
--- a/Resources/Patches/openssl-1.1.1d-conf.h.in	Fri Mar 20 12:38:00 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,122 +0,0 @@
-/*
- * {- join("\n * ", @autowarntext) -}
- *
- * Copyright 2016-2018 The OpenSSL Project Authors. All Rights Reserved.
- *
- * Licensed under the OpenSSL license (the "License").  You may not use
- * this file except in compliance with the License.  You can obtain a copy
- * in the file LICENSE in the source distribution or at
- * https://www.openssl.org/source/license.html
- */
-
-#include <openssl/opensslv.h>
-
-#ifdef  __cplusplus
-extern "C" {
-#endif
-
-#ifdef OPENSSL_ALGORITHM_DEFINES
-# error OPENSSL_ALGORITHM_DEFINES no longer supported
-#endif
-
-
-/*
- * Sometimes OPENSSSL_NO_xxx ends up with an empty file and some compilers
- * don't like that.  This will hopefully silence them.
- */
-#define NON_EMPTY_TRANSLATION_UNIT static void *dummy = &dummy;
-
-/*
- * Applications should use -DOPENSSL_API_COMPAT=<version> to suppress the
- * declarations of functions deprecated in or before <version>. Otherwise, they
- * still won't see them if the library has been built to disable deprecated
- * functions.
- */
-#ifndef DECLARE_DEPRECATED
-# define DECLARE_DEPRECATED(f)   f;
-# ifdef __GNUC__
-#  if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ > 0)
-#   undef DECLARE_DEPRECATED
-#   define DECLARE_DEPRECATED(f)    f __attribute__ ((deprecated));
-#  endif
-# endif
-#endif
-
-#ifndef OPENSSL_FILE
-# ifdef OPENSSL_NO_FILENAMES
-#  define OPENSSL_FILE ""
-#  define OPENSSL_LINE 0
-# else
-#  define OPENSSL_FILE __FILE__
-#  define OPENSSL_LINE __LINE__
-# endif
-#endif
-
-#ifndef OPENSSL_MIN_API
-# define OPENSSL_MIN_API 0
-#endif
-
-#if !defined(OPENSSL_API_COMPAT) || OPENSSL_API_COMPAT < OPENSSL_MIN_API
-# undef OPENSSL_API_COMPAT
-# define OPENSSL_API_COMPAT OPENSSL_MIN_API
-#endif
-
-/*
- * Do not deprecate things to be deprecated in version 1.2.0 before the
- * OpenSSL version number matches.
- */
-#if OPENSSL_VERSION_NUMBER < 0x10200000L
-# define DEPRECATEDIN_1_2_0(f)   f;
-#elif OPENSSL_API_COMPAT < 0x10200000L
-# define DEPRECATEDIN_1_2_0(f)   DECLARE_DEPRECATED(f)
-#else
-# define DEPRECATEDIN_1_2_0(f)
-#endif
-
-#if OPENSSL_API_COMPAT < 0x10100000L
-# define DEPRECATEDIN_1_1_0(f)   DECLARE_DEPRECATED(f)
-#else
-# define DEPRECATEDIN_1_1_0(f)
-#endif
-
-#if OPENSSL_API_COMPAT < 0x10000000L
-# define DEPRECATEDIN_1_0_0(f)   DECLARE_DEPRECATED(f)
-#else
-# define DEPRECATEDIN_1_0_0(f)
-#endif
-
-#if OPENSSL_API_COMPAT < 0x00908000L
-# define DEPRECATEDIN_0_9_8(f)   DECLARE_DEPRECATED(f)
-#else
-# define DEPRECATEDIN_0_9_8(f)
-#endif
-
-
-#define OPENSSL_UNISTD <unistd.h>
-
-#if 0
-/* Generate 80386 code? */
-{- ${processor} eq "386" ? "#define" : "#undef" -} I386_ONLY
-
-#undef OPENSSL_UNISTD
-#define OPENSSL_UNISTD {- ${unistd} -}
-
-{- ${export_var_as_fn} ? "#define" : "#undef" -} OPENSSL_EXPORT_VAR_AS_FUNCTION
-
-/*
- * The following are cipher-specific, but are part of the public API.
- */
-#if !defined(OPENSSL_SYS_UEFI)
-{- ${bn_ll} ? "# define" : "# undef" -} BN_LLONG
-/* Only one for the following should be defined */
-{- ${b64l} ? "# define" : "# undef" -} SIXTY_FOUR_BIT_LONG
-{- ${b64}  ? "# define" : "# undef" -} SIXTY_FOUR_BIT
-{- ${b32}  ? "# define" : "# undef" -} THIRTY_TWO_BIT
-#endif
-
-#define RC4_INT {- ${rc4_int} -}
-#endif
-
-#ifdef  __cplusplus
-}
-#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/Patches/openssl-1.1.1f.patch	Mon Apr 06 12:36:49 2020 +0200
@@ -0,0 +1,19 @@
+diff -urEb openssl-1.1.1f.orig/crypto/rand/rand_unix.c openssl-1.1.1f/crypto/rand/rand_unix.c
+--- openssl-1.1.1f.orig/crypto/rand/rand_unix.c	2020-03-31 14:17:45.000000000 +0200
++++ openssl-1.1.1f/crypto/rand/rand_unix.c	2020-04-02 16:38:56.091240847 +0200
+@@ -445,6 +445,7 @@
+              * system call and this should always succeed which renders
+              * this alternative but essentially identical source moot.
+              */
++#if !defined(__LSB_VERSION__)  // "syscall()" is not available in LSB
+             if (uname(&un) == 0) {
+                 kernel[0] = atoi(un.release);
+                 p = strchr(un.release, '.');
+@@ -455,6 +456,7 @@
+                     return 0;
+                 }
+             }
++#endif
+             /* Open /dev/random and wait for it to be readable */
+             if ((fd = open(DEVRANDOM_WAIT, O_RDONLY)) != -1) {
+                 if (DEVRANDM_WAIT_USE_SELECT && fd < FD_SETSIZE) {
--- a/UnitTestsSources/VersionsTests.cpp	Fri Mar 20 12:38:00 2020 +0100
+++ b/UnitTestsSources/VersionsTests.cpp	Mon Apr 06 12:36:49 2020 +0200
@@ -185,7 +185,7 @@
 TEST(Version, OpenSslStatic)
 {
   ASSERT_TRUE(OPENSSL_VERSION_NUMBER == 0x1000210fL /* openssl-1.0.2p */ ||
-              OPENSSL_VERSION_NUMBER == 0x1010104fL /* openssl-1.1.1d */);
+              OPENSSL_VERSION_NUMBER == 0x1010106fL /* openssl-1.1.1f */);
 }
 #endif
 
@@ -208,7 +208,7 @@
 TEST(Version, Civetweb)
 {
   ASSERT_EQ(1, CIVETWEB_VERSION_MAJOR);
-  ASSERT_EQ(11, CIVETWEB_VERSION_MINOR);
+  ASSERT_EQ(12, CIVETWEB_VERSION_MINOR);
   ASSERT_EQ(0, CIVETWEB_VERSION_PATCH);
 }
 #endif