changeset 6400:ded8a2be0d46 pixel-anon

integration mainline->pixel-anon
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 12 Nov 2025 19:07:18 +0100
parents b4b7b6d39ee3 (current diff) 55db426a8667 (diff)
children d0920767534e
files OrthancFramework/Sources/Images/ImageProcessing.cpp OrthancServer/Sources/OrthancRestApi/OrthancRestAnonymizeModify.cpp OrthancServer/Sources/ServerContext.cpp OrthancServer/Sources/ServerContext.h OrthancServer/Sources/ServerJobs/ResourceModificationJob.cpp TODO
diffstat 84 files changed, 468 insertions(+), 303 deletions(-) [+]
line wrap: on
line diff
--- a/NEWS	Tue Nov 04 15:58:06 2025 +0100
+++ b/NEWS	Wed Nov 12 19:07:18 2025 +0100
@@ -5,7 +5,7 @@
 -------
 
 * New configuration "HttpBindAddresses" to list the IP addresses on which the
-  HTTP server listens.  By default, this list is empty and the HTTP server listens
+  HTTP server listens. By default, this list is empty and the HTTP server listens
   on all network interfaces.
 
 REST API
@@ -13,59 +13,67 @@
 
 * Fix: C-Get SCU jobs or HTTP responses were always successful even if the C-Get operation
   actually failed.
-* C-Store, C-Move and C-Get jobs and HTTP responses now include a new "DimseErrorStatus" 
+* C-Store, C-Move and C-Get jobs and HTTP responses now include a new "DimseErrorStatus"
   field (ONLY if the operation fails).
-
+* C-Move and C-Get jobs and HTTP responses now include a new "Details" field with the "DimseErrorStatus"
+  of each operation and the list of "RetrievedInstancesIds".  Note that, if you have multiple
+  resources to retrieve and one of them fails, you'll only get those details if you have set
+  "Permissive" to true to allow other operations to continue after the failure.
 
 Maintenance
 -----------
 
-* Under Windows, the console output is now configured to interpret strings as UTF-8.
-  This notably allow cyrillic strings to display correctly in the console.
-* Improved streaming of HTTP responses under OS pressure.  In some conditions, when the OS
-  was not able to send a full buffer over the network, Orthanc was not retrying 
-  to send the remaining part.  This is now fixed.  
+* Under Microsoft Windows, the console output is now configured to interpret strings as UTF-8.
+  This notably allow Cyrillic strings to display correctly in the console.
+* Improved streaming of HTTP responses under OS pressure. In some conditions, when the OS
+  was not able to send a full buffer over the network, Orthanc was not retrying
+  to send the remaining part.
   (https://discourse.orthanc-server.org/t/incomplete-zip-downloads-from-get-studies-id-media/6046)
 * Reworked all the paths handling, improving general support of non ASCII-only paths on Windows,
   specifically for the Storage directories and for the configuration files.
-  However, on Windows, some features might still not support non ASCII-only paths:
+  However, on Microsoft Windows, some features might still not support non ASCII-only paths:
   - Reading a DCMTK dictionary ("ExternalDictionaries" configuration)
   - Using a "TemporaryDirectory" to save zip file or to export DICOMDIR
   - The "SslCertificate" and other related configurations
 * Fix: DicomGetScu jobs are now saved in DB.
 * Fix: When the configuration option "MaximumStorageCacheSize" was set to 0, the default value (128)
-       was actually applied.  From now on, a value of 0 really means that the storage cache is disabled.
-* Fix: Orthanc was unable to convert the tags into dicom+json format if the instance contained an 
-       empty element in a sequence.  This was preventing access to /dicom-web/../metadata routes and prevented
+       was actually applied. From now on, a value of 0 really means that the storage cache is disabled.
+* Fix: Orthanc was unable to convert the tags into dicom+json format if the instance contained an
+       empty element in a sequence. This was preventing access to /dicom-web/../metadata routes and
        visualization in e.g. the Stone Web viewer and OHIF.
-* Fix issue 252: Disallow colons in HTTP basic usernames
+* Fix issue #252: Disallow colons in HTTP basic usernames
 * Fix: Decoding of LUT with less than 256 entries:
        https://discourse.orthanc-server.org/t/cannot-preview-dicom-file/6275
 * Fix: Possible deadlocks when using ZipLoaderThreads > 1 when the HttpClient disconnects
        while downloading the response.
+* Fix issue #227: In SQL, string literals must use 'single quotes'
+  https://stackoverflow.com/a/25141338
 * Upgraded dependencies for static builds:
   - civetweb 1.16, including patch for CVE-2025-55763
-
+  - SQLite 3.50.4
 
 Plugins
 -------
 
 * Worklists plugin:
-  - The Worklists plugin now provides a Rest API to:
+  - The Worklists plugin now provides a REST API to:
     - create worklists through POST at /worklists/create
-    - list the worklists throuhg GET at /worklists 
+    - list the worklists through GET at /worklists
     - view a single worklist through GET at /worklists/{uuid}
     - modify a worklist through PUT at /worklists/{uuid}
     - delete a worklist through DELETE at /worklists/{uuid}
-    All details are available in the Orthanc book: https://orthanc.uclouvain.be/book/plugins/worklists-plugin.html
-  - New configurations:
-    - "SaveInOrthancDatabase" to store the worklists in the Orthanc DB (provided that you are using SQLite or PostgreSQL)
-    - "DeleteWorklistsOnStableStudy" to delete the worklist once its related study has been received and is stable
-    - "SetStudyInstanceUidIfMissing" to add a StudyInstanceUID if the Rest API request does not include one.
-    - "DeleteWorklistsDelay" to delete a worklist N hours after it has been created (only available if using the "SaveInOrthancDatabase" mode).
-    - "HousekeepingInterval" to define the delay between 2 executions of the Worklist Housekeeping thread that deletes
-      the worklists when required.
-  - Note: the previous "Database" configuration has now been renamed in "Directory" to better differentiate the "File" or "DB modes.
+    All details are available in the Orthanc book:
+    https://orthanc.uclouvain.be/book/plugins/worklists-plugin.html
+  - New configuration options:
+    - "SaveInOrthancDatabase" to store the worklists in the Orthanc DB (provided that you are using SQLite or PostgreSQL).
+    - "DeleteWorklistsOnStableStudy" to delete the worklist once its related study has been received and is stable.
+    - "SetStudyInstanceUidIfMissing" to add a StudyInstanceUID if the REST API request does not include one.
+    - "DeleteWorklistsDelay" to delete a worklist N hours after it has been created
+      (only available if using the "SaveInOrthancDatabase" mode).
+    - "HousekeepingInterval" to define the delay between 2 executions of the Worklist Housekeeping thread
+      that deletes the worklists when required.
+  - Note: the previous "Database" configuration has now been renamed in "Directory" to better differentiate
+    the "File" or "DB modes.
 
 
 Version 1.12.9 (2025-08-11)
@@ -392,7 +400,7 @@
 * Upgraded dependencies for static builds:
   - boost 1.86.0
   - curl 8.9.0
-  - SQLite 3.46
+  - SQLite 3.46.1
 
 
 Version 1.12.4 (2024-06-05)
@@ -669,7 +677,7 @@
 * Enforce the existence of the patient/study/instance while creating its archive
 * Security: New configuration option "RestApiWriteToFileSystemEnabled"
   to allow "/instances/../export" (the latter is now disabled by default)
-* Fix issue 214: VOILUTSequence is not returned in Wado-RS
+* Fix issue #214: VOILUTSequence is not returned in Wado-RS
 * Fix /tools/reset crashing when ExtraMainDicomTags were defined
 * Fix Housekeeper plugin infinite loop if Orthanc is empty.
 * Fix a crash in /tools/reconstruct triggered by the Housekeeper plugin 
--- a/OrthancFramework/Resources/CMake/SQLiteConfiguration.cmake	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancFramework/Resources/CMake/SQLiteConfiguration.cmake	Wed Nov 12 19:07:18 2025 +0100
@@ -28,9 +28,9 @@
 
 
 if (SQLITE_STATIC)
-  SET(SQLITE_SOURCES_DIR ${CMAKE_BINARY_DIR}/sqlite-amalgamation-3460100)
-  SET(SQLITE_MD5 "1fb0f7ebbee45752098cf453b6dffff3")
-  SET(SQLITE_URL "https://orthanc.uclouvain.be/downloads/third-party-downloads/sqlite-amalgamation-3460100.zip")
+  SET(SQLITE_SOURCES_DIR ${CMAKE_BINARY_DIR}/sqlite-amalgamation-3500400)
+  SET(SQLITE_MD5 "440abd85c5ee3297dd388ade51fec0cc")
+  SET(SQLITE_URL "https://orthanc.uclouvain.be/downloads/third-party-downloads/sqlite-amalgamation-3500400.zip")
 
   set(ORTHANC_SQLITE_VERSION 3046001)
 
--- a/OrthancFramework/Sources/DicomFormat/DicomIntegerPixelAccessor.h	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancFramework/Sources/DicomFormat/DicomIntegerPixelAccessor.h	Wed Nov 12 19:07:18 2025 +0100
@@ -51,7 +51,7 @@
                               const void* pixelData,
                               size_t size);
 
-    const DicomImageInformation GetInformation() const
+    const DicomImageInformation& GetInformation() const
     {
       return information_;
     }
--- a/OrthancFramework/Sources/DicomFormat/DicomStreamReader.cpp	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancFramework/Sources/DicomFormat/DicomStreamReader.cpp	Wed Nov 12 19:07:18 2025 +0100
@@ -156,7 +156,7 @@
   }
 
 
-  void DicomStreamReader::HandlePreamble(IVisitor& visitor,
+  void DicomStreamReader::HandlePreamble(const IVisitor& visitor,
                                          const std::string& block)
   {
     assert(block.size() == 144u);
--- a/OrthancFramework/Sources/DicomFormat/DicomStreamReader.h	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancFramework/Sources/DicomFormat/DicomStreamReader.h	Wed Nov 12 19:07:18 2025 +0100
@@ -89,7 +89,7 @@
     
     bool IsLittleEndian() const;
     
-    void HandlePreamble(IVisitor& visitor,
+    void HandlePreamble(const IVisitor& visitor,
                         const std::string& block);
     
     void HandleMetaHeader(IVisitor& visitor,
--- a/OrthancFramework/Sources/DicomNetworking/DicomAssociation.cpp	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancFramework/Sources/DicomNetworking/DicomAssociation.cpp	Wed Nov 12 19:07:18 2025 +0100
@@ -388,8 +388,8 @@
     LST_HEAD **l = &params_->DULparams.acceptedPresentationContext;
     if (*l != NULL)
     {
-      DUL_PRESENTATIONCONTEXT* pc = (DUL_PRESENTATIONCONTEXT*) LST_Head(l);
-      LST_Position(l, (LST_NODE*)pc);
+      DUL_PRESENTATIONCONTEXT* pc = reinterpret_cast<DUL_PRESENTATIONCONTEXT*>(LST_Head(l));
+      LST_Position(l, reinterpret_cast<LST_NODE*>(pc));
       while (pc)
       {
         if (pc->result == ASC_P_ACCEPTANCE && strlen(pc->abstractSyntax) > 0)
@@ -409,7 +409,7 @@
           }
         }
             
-        pc = (DUL_PRESENTATIONCONTEXT*) LST_Next(l);
+        pc = reinterpret_cast<DUL_PRESENTATIONCONTEXT*>(LST_Next(l));
       }
     }
 
--- a/OrthancFramework/Sources/DicomNetworking/DicomControlUserConnection.cpp	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancFramework/Sources/DicomNetworking/DicomControlUserConnection.cpp	Wed Nov 12 19:07:18 2025 +0100
@@ -426,7 +426,8 @@
     
   void DicomControlUserConnection::MoveInternal(const std::string& targetAet,
                                                 ResourceType level,
-                                                const DicomMap& fields)
+                                                const DicomMap& fields,
+                                                uint16_t messageId)
   {
     assert(association_.get() != NULL);
     association_->Open(parameters_);
@@ -448,7 +449,15 @@
 
     T_DIMSE_C_MoveRQ request;
     memset(&request, 0, sizeof(request));
-    request.MessageID = association_->GetDcmtkAssociation().nextMsgID++;
+    if (messageId == 0)
+    {
+      request.MessageID = association_->GetDcmtkAssociation().nextMsgID++;
+    }
+    else
+    {
+      request.MessageID = messageId;
+    }
+    
     strncpy(request.AffectedSOPClassUID, sopClass, DIC_UI_LEN);
     request.Priority = DIMSE_PRIORITY_MEDIUM;
     request.DataSetType = DIMSE_DATASET_PRESENT;
@@ -569,7 +578,7 @@
     }
 
     T_DIMSE_Message msgGetRequest;
-    memset((char*)&msgGetRequest, 0, sizeof(msgGetRequest));
+    memset(reinterpret_cast<void*>(&msgGetRequest), 0, sizeof(msgGetRequest));
     msgGetRequest.CommandField = DIMSE_C_GET_RQ;
 
     T_DIMSE_C_GetRQ* request = &(msgGetRequest.msg.CGetRQ);
@@ -608,7 +617,7 @@
     {
         T_DIMSE_Message rsp;
         // Make sure everything is zeroed (especially options)
-        memset((char*)&rsp, 0, sizeof(rsp));
+        memset(reinterpret_cast<void*>(&rsp), 0, sizeof(rsp));
 
         // DcmDataset* statusDetail = NULL;
         T_ASC_PresentationContextID cmdPresId = 0;
@@ -702,7 +711,7 @@
 
             // send the Store response
             T_DIMSE_Message storeResponse;
-            memset((char*)&storeResponse, 0, sizeof(storeResponse));
+            memset(reinterpret_cast<void*>(&storeResponse), 0, sizeof(storeResponse));
             storeResponse.CommandField         = DIMSE_C_STORE_RSP;
 
             T_DIMSE_C_StoreRSP& storeRsp       = storeResponse.msg.CStoreRSP;
@@ -907,7 +916,8 @@
 
   void DicomControlUserConnection::Move(const std::string& targetAet,
                                         ResourceType level,
-                                        const DicomMap& moveQuery)
+                                        const DicomMap& moveQuery,
+                                        uint16_t messageId)
   {
     DicomMap move;
     switch (level)
@@ -935,12 +945,13 @@
         throw OrthancException(ErrorCode_InternalError);
     }
 
-    MoveInternal(targetAet, level, move);
+    MoveInternal(targetAet, level, move, messageId);
   }
 
 
   void DicomControlUserConnection::Move(const std::string& targetAet,
-                                        const DicomMap& moveQuery)
+                                        const DicomMap& moveQuery,
+                                        uint16_t messageId)
   {
     if (!moveQuery.HasTag(DICOM_TAG_QUERY_RETRIEVE_LEVEL))
     {
@@ -950,49 +961,7 @@
     const std::string tmp = moveQuery.GetValue(DICOM_TAG_QUERY_RETRIEVE_LEVEL).GetContent();
     ResourceType level = StringToResourceType(tmp.c_str());
 
-    Move(targetAet, level, moveQuery);
-  }
-
-
-  void DicomControlUserConnection::MovePatient(const std::string& targetAet,
-                                               const std::string& patientId)
-  {
-    DicomMap query;
-    query.SetValue(DICOM_TAG_PATIENT_ID, patientId, false);
-    MoveInternal(targetAet, ResourceType_Patient, query);
-  }
-    
-
-  void DicomControlUserConnection::MoveStudy(const std::string& targetAet,
-                                             const std::string& studyUid)
-  {
-    DicomMap query;
-    query.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, studyUid, false);
-    MoveInternal(targetAet, ResourceType_Study, query);
-  }
-
-    
-  void DicomControlUserConnection::MoveSeries(const std::string& targetAet,
-                                              const std::string& studyUid,
-                                              const std::string& seriesUid)
-  {
-    DicomMap query;
-    query.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, studyUid, false);
-    query.SetValue(DICOM_TAG_SERIES_INSTANCE_UID, seriesUid, false);
-    MoveInternal(targetAet, ResourceType_Series, query);
-  }
-
-
-  void DicomControlUserConnection::MoveInstance(const std::string& targetAet,
-                                                const std::string& studyUid,
-                                                const std::string& seriesUid,
-                                                const std::string& instanceUid)
-  {
-    DicomMap query;
-    query.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, studyUid, false);
-    query.SetValue(DICOM_TAG_SERIES_INSTANCE_UID, seriesUid, false);
-    query.SetValue(DICOM_TAG_SOP_INSTANCE_UID, instanceUid, false);
-    MoveInternal(targetAet, ResourceType_Instance, query);
+    Move(targetAet, level, moveQuery, messageId);
   }
 
 
--- a/OrthancFramework/Sources/DicomNetworking/DicomControlUserConnection.h	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancFramework/Sources/DicomNetworking/DicomControlUserConnection.h	Wed Nov 12 19:07:18 2025 +0100
@@ -90,7 +90,8 @@
     
     void MoveInternal(const std::string& targetAet,
                       ResourceType level,
-                      const DicomMap& fields);
+                      const DicomMap& fields,
+                      uint16_t messageId);
     
   public:
     explicit DicomControlUserConnection(const DicomAssociationParameters& params, ScuOperationFlags scuOperation);
@@ -126,26 +127,13 @@
 
     void Move(const std::string& targetAet,
               ResourceType level,
-              const DicomMap& moveQuery);
+              const DicomMap& moveQuery,
+              uint16_t messageId = 0);
     
     void Move(const std::string& targetAet,
-              const DicomMap& moveQuery);
+              const DicomMap& moveQuery,
+              uint16_t messageId = 0);
     
-    void MovePatient(const std::string& targetAet,
-                     const std::string& patientId);
-
-    void MoveStudy(const std::string& targetAet,
-                   const std::string& studyUid);
-
-    void MoveSeries(const std::string& targetAet,
-                    const std::string& studyUid,
-                    const std::string& seriesUid);
-
-    void MoveInstance(const std::string& targetAet,
-                      const std::string& studyUid,
-                      const std::string& seriesUid,
-                      const std::string& instanceUid);
-
     void FindWorklist(DicomFindAnswers& result,
                       ParsedDicomFile& query);
   };
--- a/OrthancFramework/Sources/DicomNetworking/IStoreRequestHandler.h	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancFramework/Sources/DicomNetworking/IStoreRequestHandler.h	Wed Nov 12 19:07:18 2025 +0100
@@ -44,6 +44,8 @@
     virtual uint16_t Handle(DcmDataset& dicom,
                             const std::string& remoteIp,
                             const std::string& remoteAet,
-                            const std::string& calledAet) = 0;
+                            const std::string& calledAet,
+                            uint16_t originatorMessageId,
+                            const std::string& originatorAet) = 0;
   };
 }
--- a/OrthancFramework/Sources/DicomNetworking/Internals/CommandDispatcher.h	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancFramework/Sources/DicomNetworking/Internals/CommandDispatcher.h	Wed Nov 12 19:07:18 2025 +0100
@@ -62,9 +62,9 @@
                         unsigned int maximumPduLength,
                         IApplicationEntityFilter* filter);
 
-      virtual ~CommandDispatcher();
+      virtual ~CommandDispatcher() ORTHANC_OVERRIDE;
 
-      virtual bool Step();
+      virtual bool Step() ORTHANC_OVERRIDE;
     };
 
     CommandDispatcher* AcceptAssociation(const DicomServer& server, 
--- a/OrthancFramework/Sources/DicomNetworking/Internals/FindScp.cpp	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancFramework/Sources/DicomNetworking/Internals/FindScp.cpp	Wed Nov 12 19:07:18 2025 +0100
@@ -112,13 +112,13 @@
   // in case the sequence attribute contains exactly one item with an empty
   // ReferencedSOPClassUID and an empty ReferencedSOPInstanceUID, remove the item
   if( dataset->findAndGetElement( sequenceTagKey, sequenceAttribute ).good() &&
-      ( (DcmSequenceOfItems*)sequenceAttribute )->card() == 1 &&
-      ( (DcmSequenceOfItems*)sequenceAttribute )->getItem(0)->findAndGetElement( DCM_ReferencedSOPClassUID, referencedSOPClassUIDAttribute ).good() &&
+      ( reinterpret_cast<DcmSequenceOfItems*>(sequenceAttribute) )->card() == 1 &&
+      ( reinterpret_cast<DcmSequenceOfItems*>(sequenceAttribute) )->getItem(0)->findAndGetElement( DCM_ReferencedSOPClassUID, referencedSOPClassUIDAttribute ).good() &&
       referencedSOPClassUIDAttribute->getLength() == 0 &&
-      ( (DcmSequenceOfItems*)sequenceAttribute )->getItem(0)->findAndGetElement( DCM_ReferencedSOPInstanceUID, referencedSOPInstanceUIDAttribute, OFFalse ).good() &&
+      ( reinterpret_cast<DcmSequenceOfItems*>(sequenceAttribute) )->getItem(0)->findAndGetElement( DCM_ReferencedSOPInstanceUID, referencedSOPInstanceUIDAttribute, OFFalse ).good() &&
       referencedSOPInstanceUIDAttribute->getLength() == 0 )
   {
-    DcmItem *item = ((DcmSequenceOfItems*)sequenceAttribute)->remove( ((DcmSequenceOfItems*)sequenceAttribute)->getItem(0) );
+    DcmItem *item = (reinterpret_cast<DcmSequenceOfItems*>(sequenceAttribute))->remove( (reinterpret_cast<DcmSequenceOfItems*>(sequenceAttribute))->getItem(0) );
     delete item;
   }
 }
--- a/OrthancFramework/Sources/DicomNetworking/Internals/StoreScp.cpp	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancFramework/Sources/DicomNetworking/Internals/StoreScp.cpp	Wed Nov 12 19:07:18 2025 +0100
@@ -184,7 +184,7 @@
               {
                 try
                 {
-                  rsp->DimseStatus = cbdata->handler->Handle(**imageDataSet, *cbdata->remoteIp, cbdata->remoteAET, cbdata->calledAET);
+                  rsp->DimseStatus = cbdata->handler->Handle(**imageDataSet, *cbdata->remoteIp, cbdata->remoteAET, cbdata->calledAET, req->MoveOriginatorID, req->MoveOriginatorApplicationEntityTitle);
                 }
                 catch (OrthancException& e)
                 {
--- a/OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.cpp	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.cpp	Wed Nov 12 19:07:18 2025 +0100
@@ -2019,13 +2019,13 @@
           }
           else
           {
-            ok = element.putUint16Array((const Uint16*) decoded->c_str(), decoded->size() / sizeof(Uint16)).good();
+            ok = element.putUint16Array(reinterpret_cast<const Uint16*>(decoded->c_str()), decoded->size() / sizeof(Uint16)).good();
           }
           
           break;
       
         default:
-          ok = element.putUint8Array((const Uint8*) decoded->c_str(), decoded->size()).good();
+          ok = element.putUint8Array(reinterpret_cast<const Uint8*>(decoded->c_str()), decoded->size()).good();
           break;
       }
       
--- a/OrthancFramework/Sources/FileStorage/MemoryStorageArea.h	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancFramework/Sources/FileStorage/MemoryStorageArea.h	Wed Nov 12 19:07:18 2025 +0100
@@ -42,7 +42,7 @@
     Content  content_;
     
   public:
-    virtual ~MemoryStorageArea();
+    virtual ~MemoryStorageArea() ORTHANC_OVERRIDE;
     
     virtual void Create(const std::string& uuid,
                         const void* content,
--- a/OrthancFramework/Sources/HttpServer/HttpOutput.cpp	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancFramework/Sources/HttpServer/HttpOutput.cpp	Wed Nov 12 19:07:18 2025 +0100
@@ -976,7 +976,7 @@
       stateMachine_.AddHeader("Content-Disposition", "filename=\"" + std::string(filename) + "\"");
     }
 
-    stateMachine_.StartStream(contentType.c_str());
+    stateMachine_.StartStream(contentType);
 
     while (stream.ReadNextChunk())
     {
@@ -988,7 +988,7 @@
 
   void HttpOutput::StartStream(const std::string& contentType)
   {
-    stateMachine_.StartStream(contentType.c_str());
+    stateMachine_.StartStream(contentType);
   }
 
   void HttpOutput::SendStreamItem(const void* data,
--- a/OrthancFramework/Sources/Images/ImageProcessing.cpp	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancFramework/Sources/Images/ImageProcessing.cpp	Wed Nov 12 19:07:18 2025 +0100
@@ -2161,7 +2161,7 @@
           PolygonEdge edge(v1, v2, GetPolygonNextY(points, i));
           globalEdgeTable[v1.GetY()].push_back(edge);
         }
-        else if (v1.GetY() > v2.GetY())
+        else
         {
           // Down-going edge
           PolygonEdge edge(v2, v1, yPrev);
--- a/OrthancFramework/Sources/Images/PamReader.h	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancFramework/Sources/Images/PamReader.h	Wed Nov 12 19:07:18 2025 +0100
@@ -73,7 +73,7 @@
     */
     explicit PamReader(bool enforceAligned);
 
-    virtual ~PamReader();
+    virtual ~PamReader() ORTHANC_OVERRIDE;
 
 #if ORTHANC_SANDBOXED == 0
     void ReadFromFile(const boost::filesystem::path& filename);
--- a/OrthancFramework/Sources/Images/PngReader.cpp	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancFramework/Sources/Images/PngReader.cpp	Wed Nov 12 19:07:18 2025 +0100
@@ -102,9 +102,9 @@
       }
 
       endInfo_ = png_create_info_struct(png_);
-      if (!info_)
+      if (!endInfo_)
       {
-        png_destroy_read_struct(&png_, &info_, NULL);
+        png_destroy_read_struct(&png_, &endInfo_, NULL);
         throw OrthancException(ErrorCode_NotEnoughMemory);
       }
     }
--- a/OrthancFramework/Sources/JobsEngine/JobsRegistry.cpp	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancFramework/Sources/JobsEngine/JobsRegistry.cpp	Wed Nov 12 19:07:18 2025 +0100
@@ -375,8 +375,8 @@
   };
 
 
-  bool JobsRegistry::PriorityComparator::operator() (JobHandler* const& a,
-                                                     JobHandler* const& b) const
+  bool JobsRegistry::PriorityComparator::operator() (const JobHandler* const& a,
+                                                     const JobHandler* const& b) const
   {
     return a->GetPriority() < b->GetPriority();
   }
@@ -404,7 +404,7 @@
     return false;
   }
 
-  bool JobsRegistry::IsCompletedJob(JobHandler& job) const
+  bool JobsRegistry::IsCompletedJob(const JobHandler& job) const
   {
     for (CompletedJobs::const_iterator it = completedJobs_.begin();
          it != completedJobs_.end(); ++it)
@@ -918,12 +918,7 @@
         {
           // Success, try and retrieve the status of the job
           JobsIndex::const_iterator it = jobsIndex_.find(id);
-          if (it == jobsIndex_.end())
-          {
-            // Should not happen
-            state = JobState_Failure;
-          }
-          else
+          if (it != jobsIndex_.end())
           {
             const JobStatus& status = it->second->GetLastStatus();
             successContent = status.GetPublicContent();
@@ -1597,7 +1592,7 @@
     for (JobsIndex::const_iterator it = jobsIndex_.begin();
          it != jobsIndex_.end(); ++it)
     {
-      JobHandler& job = *it->second;
+      const JobHandler& job = *it->second;
 
       switch (job.GetState())
       {
--- a/OrthancFramework/Sources/JobsEngine/JobsRegistry.h	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancFramework/Sources/JobsEngine/JobsRegistry.h	Wed Nov 12 19:07:18 2025 +0100
@@ -73,8 +73,8 @@
 
     struct PriorityComparator
     {
-      bool operator() (JobHandler* const& a,
-                       JobHandler* const& b) const;
+      bool operator() (const JobHandler* const& a,
+                       const JobHandler* const& b) const;
     };
 
     typedef std::map<std::string, JobHandler*>              JobsIndex;
@@ -100,7 +100,7 @@
 #ifndef NDEBUG
     bool IsPendingJob(const JobHandler& job) const;
 
-    bool IsCompletedJob(JobHandler& job) const;
+    bool IsCompletedJob(const JobHandler& job) const;
 
     bool IsRetryJob(JobHandler& job) const;
 #endif
--- a/OrthancFramework/Sources/JobsEngine/Operations/SequenceOfOperationsJob.cpp	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancFramework/Sources/JobsEngine/Operations/SequenceOfOperationsJob.cpp	Wed Nov 12 19:07:18 2025 +0100
@@ -460,7 +460,7 @@
     done_(false)
   {
     std::string jobType;
-    GetJobType(jobType);
+    SequenceOfOperationsJob::GetJobType(jobType);
     
     if (SerializationToolbox::ReadString(serialized, TYPE) != jobType ||
         !serialized.isMember(OPERATIONS) ||
--- a/OrthancFramework/Sources/JobsEngine/Operations/SequenceOfOperationsJob.h	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancFramework/Sources/JobsEngine/Operations/SequenceOfOperationsJob.h	Wed Nov 12 19:07:18 2025 +0100
@@ -70,7 +70,7 @@
     SequenceOfOperationsJob(IJobUnserializer& unserializer,
                             const Json::Value& serialized);
 
-    virtual ~SequenceOfOperationsJob();
+    virtual ~SequenceOfOperationsJob() ORTHANC_OVERRIDE;
 
     void SetDescription(const std::string& description);
 
--- a/OrthancFramework/Sources/JobsEngine/SetOfCommandsJob.h	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancFramework/Sources/JobsEngine/SetOfCommandsJob.h	Wed Nov 12 19:07:18 2025 +0100
@@ -71,7 +71,7 @@
     SetOfCommandsJob(ICommandUnserializer* unserializer  /* takes ownership */,
                      const Json::Value& source);
 
-    virtual ~SetOfCommandsJob();
+    virtual ~SetOfCommandsJob() ORTHANC_OVERRIDE;
 
     size_t GetPosition() const;
 
@@ -79,7 +79,7 @@
 
     const std::string& GetDescription() const;
 
-    void Reserve(size_t size);
+    virtual void Reserve(size_t size);
 
     size_t GetCommandsCount() const;
 
--- a/OrthancFramework/Sources/JobsEngine/SetOfInstancesJob.cpp	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancFramework/Sources/JobsEngine/SetOfInstancesJob.cpp	Wed Nov 12 19:07:18 2025 +0100
@@ -106,7 +106,7 @@
     {
     }
 
-    virtual ICommand* Unserialize(const Json::Value& source) const
+    virtual ICommand* Unserialize(const Json::Value& source) const ORTHANC_OVERRIDE
     {
       if (source.type() == Json::nullValue)
       {
--- a/OrthancFramework/Sources/MallocMemoryBuffer.h	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancFramework/Sources/MallocMemoryBuffer.h	Wed Nov 12 19:07:18 2025 +0100
@@ -45,7 +45,7 @@
   public:
     MallocMemoryBuffer();
 
-    virtual ~MallocMemoryBuffer()
+    virtual ~MallocMemoryBuffer() ORTHANC_OVERRIDE
     {
       Clear();
     }
--- a/OrthancFramework/Sources/MultiThreading/IRunnableBySteps.h	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancFramework/Sources/MultiThreading/IRunnableBySteps.h	Wed Nov 12 19:07:18 2025 +0100
@@ -25,13 +25,14 @@
 #pragma once
 
 #include "../IDynamicObject.h"
+#include "../Compatibility.h"
 
 namespace Orthanc
 {
   class IRunnableBySteps : public IDynamicObject
   {
   public:
-    virtual ~IRunnableBySteps()
+    virtual ~IRunnableBySteps() ORTHANC_OVERRIDE
     {
     }
 
--- a/OrthancFramework/Sources/RestApi/RestApiHierarchy.cpp	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancFramework/Sources/RestApi/RestApiHierarchy.cpp	Wed Nov 12 19:07:18 2025 +0100
@@ -169,9 +169,9 @@
   }
 
 
-  void RestApiHierarchy::DeleteChildren(Children& children)
+  void RestApiHierarchy::DeleteChildren(const Children& children)
   {
-    for (Children::iterator it = children.begin();
+    for (Children::const_iterator it = children.begin();
          it != children.end(); ++it)
     {
       delete it->second;
--- a/OrthancFramework/Sources/RestApi/RestApiHierarchy.h	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancFramework/Sources/RestApi/RestApiHierarchy.h	Wed Nov 12 19:07:18 2025 +0100
@@ -97,7 +97,7 @@
     static RestApiHierarchy& AddChild(Children& children,
                                       const std::string& name);
 
-    static void DeleteChildren(Children& children);
+    static void DeleteChildren(const Children& children);
 
     template <typename Handler>
     void RegisterInternal(const RestApiPath& path,
--- a/OrthancFramework/Sources/SystemToolbox.cpp	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancFramework/Sources/SystemToolbox.cpp	Wed Nov 12 19:07:18 2025 +0100
@@ -1029,27 +1029,25 @@
 
 
   boost::filesystem::path SystemToolbox::InterpretRelativePath(const boost::filesystem::path& baseDirectory,
-                                                               const std::string& relativePath)
+                                                               const boost::filesystem::path& relativePath)
   {
-    boost::filesystem::path relative = SystemToolbox::PathFromUtf8(relativePath);
-
     /**
        The following lines should be equivalent to this one: 
 
-       return (base / relative);
+       return (baseDirectory / relativePath);
 
        However, for some unknown reason, some versions of Boost do not
        make the proper path resolution when "baseDirectory" is an
        absolute path. So, a hack is used below.
     **/
 
-    if (relative.is_absolute())
+    if (relativePath.is_absolute())
     {
-      return relative;
+      return relativePath;
     }
     else
     {
-      return baseDirectory / relative;
+      return baseDirectory / relativePath;
     }
   }
 
--- a/OrthancFramework/Sources/SystemToolbox.h	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancFramework/Sources/SystemToolbox.h	Wed Nov 12 19:07:18 2025 +0100
@@ -136,7 +136,7 @@
     static void GetEnvironmentVariables(std::map<std::string, std::string>& env);
 
     static boost::filesystem::path InterpretRelativePath(const boost::filesystem::path& baseDirectory,
-                                                         const std::string& relativePath);
+                                                         const boost::filesystem::path& relativePath);
 
     static void ReadFileRange(std::string& content,
                               const boost::filesystem::path& path,
--- a/OrthancFramework/Sources/Toolbox.cpp	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancFramework/Sources/Toolbox.cpp	Wed Nov 12 19:07:18 2025 +0100
@@ -2935,17 +2935,10 @@
     else if (ORTHANC_SCANF(version, "%4d", &a) == 1 &&
              a >= 0)
     {
-      if (a >= 0)
-      {
-        major = static_cast<unsigned int>(a);
-        minor = 0;
-        revision = 0;
-        return true;
-      }
-      else
-      {
-        return false;
-      }
+      major = static_cast<unsigned int>(a);
+      minor = 0;
+      revision = 0;
+      return true;
     }
     else
     {
--- a/OrthancFramework/UnitTestsSources/FileStorageTests.cpp	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancFramework/UnitTestsSources/FileStorageTests.cpp	Wed Nov 12 19:07:18 2025 +0100
@@ -59,7 +59,7 @@
 
   std::string data = Toolbox::GenerateUuid();
   std::string uid = Toolbox::GenerateUuid();
-  s.Create(uid.c_str(), &data[0], data.size(), FileContentType_Unknown);
+  s.Create(uid, &data[0], data.size(), FileContentType_Unknown);
   std::string d;
   {
     std::unique_ptr<IMemoryBuffer> buffer(s.ReadWhole(uid, FileContentType_Unknown));
@@ -83,7 +83,7 @@
   std::vector<uint8_t> data;
   StringToVector(data, Toolbox::GenerateUuid());
   std::string uid = Toolbox::GenerateUuid();
-  s.Create(uid.c_str(), &data[0], data.size(), FileContentType_Unknown);
+  s.Create(uid, &data[0], data.size(), FileContentType_Unknown);
   std::string d;
   {
     std::unique_ptr<IMemoryBuffer> buffer(s.ReadWhole(uid, FileContentType_Unknown));
@@ -167,7 +167,7 @@
   {
     std::string t = Toolbox::GenerateUuid();
     std::string uid = Toolbox::GenerateUuid();
-    s.Create(uid.c_str(), &t[0], t.size(), FileContentType_Unknown);
+    s.Create(uid, &t[0], t.size(), FileContentType_Unknown);
     u.push_back(uid);
   }
 
--- a/OrthancFramework/UnitTestsSources/FrameworkTests.cpp	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancFramework/UnitTestsSources/FrameworkTests.cpp	Wed Nov 12 19:07:18 2025 +0100
@@ -499,7 +499,7 @@
   // This is a Latin-1 test string
   const unsigned char data[10] = { 0xe0, 0xe9, 0xea, 0xe7, 0x26, 0xc6, 0x61, 0x62, 0x63, 0x00 };
   
-  std::string s((char*) &data[0], 10);
+  std::string s(reinterpret_cast<const char*>(&data[0]), 10);
   ASSERT_EQ("&abc", Toolbox::ConvertToAscii(s));
 
   // Open in Emacs, then save with UTF-8 encoding, then "hexdump -C"
@@ -528,7 +528,7 @@
   // This is a Latin-1 test string: "crane" with a circumflex accent
   const unsigned char latin1[] = { 0x63, 0x72, 0xe2, 0x6e, 0x65 };
 
-  std::string s((char*) &latin1[0], sizeof(latin1) / sizeof(char));
+  std::string s(reinterpret_cast<const char*>(&latin1[0]), sizeof(latin1) / sizeof(char));
 
   ASSERT_EQ(s, Toolbox::ConvertFromUtf8(Toolbox::ConvertToUtf8(s, Encoding_Latin1, false, false), Encoding_Latin1));
   ASSERT_EQ("cre", Toolbox::ConvertToUtf8(s, Encoding_Utf8, false, false));
@@ -539,7 +539,7 @@
                           size_t size,
                           size_t expectedLength)
 {
-  std::string s((char*) &data[0], size);
+  std::string s(reinterpret_cast<const char*>(&data[0]), size);
   uint32_t unicode;
   size_t length;
   Toolbox::Utf8ToUnicodeCharacter(unicode, length, s, 0);
--- a/OrthancFramework/UnitTestsSources/FromDcmtkTests.cpp	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancFramework/UnitTestsSources/FromDcmtkTests.cpp	Wed Nov 12 19:07:18 2025 +0100
@@ -3630,9 +3630,9 @@
         std::string source;
         Orthanc::SystemToolbox::ReadFile(source, SystemToolbox::PathFromUtf8(path));
 
-        std::string c, k;
         try
         {
+          std::string c, k;
           scu.Transcode(c, k, transcoder, source.c_str(), source.size(),
                         DicomTransferSyntax_LittleEndianExplicit, false, "", 0);
         }
@@ -3678,8 +3678,6 @@
     std::set<DicomTransferSyntax> s;
     s.insert(a);
 
-    std::string t;
-
     IDicomTranscoder::DicomImage source, target;
     source.AcquireParsed(dynamic_cast<DcmFileFormat*>(toto->clone()));
 
@@ -3698,7 +3696,6 @@
                     a == DicomTransferSyntax_JPEGProcess2_4 ||
                     a == DicomTransferSyntax_JPEGLSLossy);
       
-      printf("SIZE: %d\n", static_cast<int>(t.size()));
       if (sourceUid == IDicomTranscoder::GetSopInstanceUid(target.GetParsed()))
       {
         ASSERT_FALSE(lossy);
--- a/OrthancFramework/UnitTestsSources/ImageTests.cpp	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancFramework/UnitTestsSources/ImageTests.cpp	Wed Nov 12 19:07:18 2025 +0100
@@ -252,7 +252,7 @@
     v = 0;
     for (unsigned int y = 0; y < height; y++)
     {
-      const uint16_t *p = reinterpret_cast<const uint16_t*>((const uint8_t*) r.GetConstBuffer() + y * r.GetPitch());
+      const uint16_t *p = reinterpret_cast<const uint16_t*>(reinterpret_cast<const uint8_t*>(r.GetConstBuffer()) + y * r.GetPitch());
       ASSERT_EQ(p, r.GetConstRow(y));
       for (unsigned int x = 0; x < width; x++, p++, v++)
       {
@@ -276,7 +276,7 @@
     v = 0;
     for (unsigned int y = 0; y < height; y++)
     {
-      const uint16_t *p = reinterpret_cast<const uint16_t*>((const uint8_t*) r2.GetConstBuffer() + y * r2.GetPitch());
+      const uint16_t *p = reinterpret_cast<const uint16_t*>(reinterpret_cast<const uint8_t*>(r2.GetConstBuffer()) + y * r2.GetPitch());
       ASSERT_EQ(p, r2.GetConstRow(y));
       for (unsigned int x = 0; x < width; x++, p++, v++)
       {
@@ -500,7 +500,7 @@
     for (unsigned int y = 0; y < height; y++)
     {
       const uint16_t *p = reinterpret_cast<const uint16_t*>
-        ((const uint8_t*) r.GetConstBuffer() + y * r.GetPitch());
+        (reinterpret_cast<const uint8_t*>(r.GetConstBuffer()) + y * r.GetPitch());
       ASSERT_EQ(p, r.GetConstRow(y));
       for (unsigned int x = 0; x < width; x++, p++, v++)
       {
@@ -522,7 +522,7 @@
     for (unsigned int y = 0; y < height; y++)
     {
       const uint16_t* p = reinterpret_cast<const uint16_t*>
-        ((const uint8_t*)r.GetConstBuffer() + y * r.GetPitch());
+        (reinterpret_cast<const uint8_t*>(r.GetConstBuffer()) + y * r.GetPitch());
       ASSERT_EQ(p, r.GetConstRow(y));
       for (unsigned int x = 0; x < width; x++, p++, v++)
       {
@@ -547,7 +547,7 @@
     for (unsigned int y = 0; y < height; y++)
     {
       const uint16_t *p = reinterpret_cast<const uint16_t*>
-        ((const uint8_t*) r2.GetConstBuffer() + y * r2.GetPitch());
+        (reinterpret_cast<const uint8_t*>(r2.GetConstBuffer()) + y * r2.GetPitch());
       ASSERT_EQ(p, r2.GetConstRow(y));
       for (unsigned int x = 0; x < width; x++, p++, v++)
       {
@@ -574,7 +574,7 @@
     for (unsigned int y = 0; y < height; y++)
     {
       const uint16_t* p = reinterpret_cast<const uint16_t*>
-        ((const uint8_t*)r2.GetConstBuffer() + y * r2.GetPitch());
+        (reinterpret_cast<const uint8_t*>(r2.GetConstBuffer()) + y * r2.GetPitch());
       ASSERT_EQ(p, r2.GetConstRow(y));
       for (unsigned int x = 0; x < width; x++, p++, v++)
       {
--- a/OrthancFramework/UnitTestsSources/JobsTests.cpp	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancFramework/UnitTestsSources/JobsTests.cpp	Wed Nov 12 19:07:18 2025 +0100
@@ -282,10 +282,10 @@
 
 TEST(MultiThreading, SharedMessageQueueClean)
 {
-  std::set<int> s;
-
   try
   {
+    std::set<int> s;
+
     SharedMessageQueue q;
     q.Enqueue(new DynamicInteger(10, s));
     q.Enqueue(new DynamicInteger(20, s));  
@@ -733,25 +733,26 @@
 }
 
 
-TEST(JobsEngine, DISABLED_SequenceOfOperationsJob)
+TEST(JobsEngine, SequenceOfOperationsJob)
 {
   JobsEngine engine(10);
   engine.SetThreadSleep(10);
   engine.SetWorkersCount(3);
   engine.Start();
 
-  std::string id;
-  SequenceOfOperationsJob* job = NULL;
-
   {
-    std::unique_ptr<SequenceOfOperationsJob> a(new SequenceOfOperationsJob);
-    job = a.get();
-    engine.GetRegistry().Submit(id, a.release(), 0);
+    std::string id;
+    std::unique_ptr<SequenceOfOperationsJob> seq(new SequenceOfOperationsJob);
+    engine.GetRegistry().Submit(id, seq.release(), 0);
   }
 
   boost::this_thread::sleep(boost::posix_time::milliseconds(500));
 
   {
+    SequenceOfOperationsJob* job = NULL;
+    std::unique_ptr<SequenceOfOperationsJob> seq(new SequenceOfOperationsJob);
+    job = seq.get();
+
     SequenceOfOperationsJob::Lock lock(*job);
     size_t i = lock.AddOperation(new LogJobOperation);
     size_t j = lock.AddOperation(new LogJobOperation);
@@ -1137,9 +1138,9 @@
 TEST(JobsSerialization, Registry)
 {   
   Json::Value s;
-  std::string i1, i2;
 
   {
+    std::string i1, i2;
     JobsRegistry registry(10);
     registry.Submit(i1, new DummyJob(), 10);
     registry.Submit(i2, new SequenceOfOperationsJob(), 30);
--- a/OrthancFramework/UnitTestsSources/RestApiTests.cpp	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancFramework/UnitTestsSources/RestApiTests.cpp	Wed Nov 12 19:07:18 2025 +0100
@@ -402,7 +402,7 @@
     void Reset()
     {
       HttpContentNegociation::Dictionary parameters;
-      Handle("nope", "nope", parameters);
+      AcceptHandler::Handle("nope", "nope", parameters);
     }
 
     const std::string& GetType() const
--- a/OrthancServer/OrthancExplorer/explorer.js	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancServer/OrthancExplorer/explorer.js	Wed Nov 12 19:07:18 2025 +0100
@@ -173,8 +173,8 @@
 
 var authorizationTokens = GetAuthorizationTokensFromUrl();
 
-/* Copy the authoziation toekn from the url search parameters into HTTP headers in every request to the Rest API.  
-Thanks to this behaviour, you may specify a ?token=xxx in your url and this will be passed 
+/* Copy the authoziation token from the url search parameters into HTTP headers in every request to the REST API.
+Thanks to this behaviour, you may specify a ?token=xxx in your url and this will be passed
 as the "token" header in every request to the API allowing you to use the authorization plugin */
 $.ajaxSetup(
   {
--- a/OrthancServer/Plugins/Engine/OrthancPluginDatabase.cpp	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancServer/Plugins/Engine/OrthancPluginDatabase.cpp	Wed Nov 12 19:07:18 2025 +0100
@@ -238,7 +238,7 @@
       ResetAnswers();
     }
 
-    virtual ~Transaction()
+    virtual ~Transaction() ORTHANC_OVERRIDE
     {
       assert(that_.activeTransaction_ != NULL);    
       that_.activeTransaction_ = NULL;
--- a/OrthancServer/Plugins/Engine/OrthancPluginDatabaseV3.h	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancServer/Plugins/Engine/OrthancPluginDatabaseV3.h	Wed Nov 12 19:07:18 2025 +0100
@@ -54,7 +54,7 @@
                             void* database,
                             const std::string& serverIdentifier);
 
-    virtual ~OrthancPluginDatabaseV3();
+    virtual ~OrthancPluginDatabaseV3() ORTHANC_OVERRIDE;
 
     virtual void Open() ORTHANC_OVERRIDE;
 
--- a/OrthancServer/Plugins/Engine/OrthancPluginDatabaseV4.cpp	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancServer/Plugins/Engine/OrthancPluginDatabaseV4.cpp	Wed Nov 12 19:07:18 2025 +0100
@@ -527,7 +527,7 @@
     }
 
     
-    virtual ~Transaction()
+    virtual ~Transaction() ORTHANC_OVERRIDE
     {
       try
       {
--- a/OrthancServer/Plugins/Engine/OrthancPluginDatabaseV4.h	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancServer/Plugins/Engine/OrthancPluginDatabaseV4.h	Wed Nov 12 19:07:18 2025 +0100
@@ -53,7 +53,7 @@
                             const _OrthancPluginRegisterDatabaseBackendV4& database,
                             const std::string& serverIdentifier);
 
-    virtual ~OrthancPluginDatabaseV4();
+    virtual ~OrthancPluginDatabaseV4() ORTHANC_OVERRIDE;
 
     const _OrthancPluginRegisterDatabaseBackendV4& GetDefinition() const
     {
--- a/OrthancServer/Plugins/Engine/OrthancPlugins.cpp	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancServer/Plugins/Engine/OrthancPlugins.cpp	Wed Nov 12 19:07:18 2025 +0100
@@ -1562,7 +1562,7 @@
           }
         }
 
-        virtual ~Handler()
+        virtual ~Handler() ORTHANC_OVERRIDE
         {
           assert(handler_ != NULL);
           parameters_.destructor(handler_);
@@ -5888,7 +5888,7 @@
       case _OrthancPluginService_KeysValuesIteratorGetKey:
       {
         const _OrthancPluginKeysValuesIteratorGetKey& p = *reinterpret_cast<const _OrthancPluginKeysValuesIteratorGetKey*>(parameters);
-        StatelessDatabaseOperations::KeysValuesIterator& iterator = *reinterpret_cast<StatelessDatabaseOperations::KeysValuesIterator*>(p.iterator);
+        const StatelessDatabaseOperations::KeysValuesIterator& iterator = *reinterpret_cast<const StatelessDatabaseOperations::KeysValuesIterator*>(p.iterator);
         *p.target = iterator.GetKey().c_str();
         return true;
       }
@@ -5896,7 +5896,7 @@
       case _OrthancPluginService_KeysValuesIteratorGetValue:
       {
         const _OrthancPluginKeysValuesIteratorGetValue& p = *reinterpret_cast<const _OrthancPluginKeysValuesIteratorGetValue*>(parameters);
-        StatelessDatabaseOperations::KeysValuesIterator& iterator = *reinterpret_cast<StatelessDatabaseOperations::KeysValuesIterator*>(p.iterator);
+        const StatelessDatabaseOperations::KeysValuesIterator& iterator = *reinterpret_cast<const StatelessDatabaseOperations::KeysValuesIterator*>(p.iterator);
         CopyToMemoryBuffer(p.target, iterator.GetValue());
         return true;
       }
@@ -6687,7 +6687,7 @@
       assert(reader_ != NULL);
     }
 
-    virtual ~HttpServerChunkedReader()
+    virtual ~HttpServerChunkedReader() ORTHANC_OVERRIDE
     {
       assert(reader_ != NULL);
       parameters_.finalize(reader_);
--- a/OrthancServer/Plugins/Engine/OrthancPlugins.h	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancServer/Plugins/Engine/OrthancPlugins.h	Wed Nov 12 19:07:18 2025 +0100
@@ -281,7 +281,7 @@
   public:
     explicit OrthancPlugins(const std::string& databaseServerIdentifier);
 
-    virtual ~OrthancPlugins();
+    virtual ~OrthancPlugins() ORTHANC_OVERRIDE;
 
     void SetServerContext(ServerContext& context);
 
--- a/OrthancServer/Plugins/Engine/PluginMemoryBuffer32.h	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancServer/Plugins/Engine/PluginMemoryBuffer32.h	Wed Nov 12 19:07:18 2025 +0100
@@ -44,7 +44,7 @@
   public:
     PluginMemoryBuffer32();
 
-    virtual ~PluginMemoryBuffer32()
+    virtual ~PluginMemoryBuffer32() ORTHANC_OVERRIDE
     {
       Clear();
     }
--- a/OrthancServer/Plugins/Engine/PluginMemoryBuffer64.h	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancServer/Plugins/Engine/PluginMemoryBuffer64.h	Wed Nov 12 19:07:18 2025 +0100
@@ -44,7 +44,7 @@
   public:
     PluginMemoryBuffer64();
 
-    virtual ~PluginMemoryBuffer64()
+    virtual ~PluginMemoryBuffer64() ORTHANC_OVERRIDE
     {
       Clear();
     }
--- a/OrthancServer/Plugins/Engine/PluginsJob.h	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancServer/Plugins/Engine/PluginsJob.h	Wed Nov 12 19:07:18 2025 +0100
@@ -47,7 +47,7 @@
 
     explicit PluginsJob(const _OrthancPluginCreateJob2& parameters);
 
-    virtual ~PluginsJob();
+    virtual ~PluginsJob() ORTHANC_OVERRIDE;
 
     virtual void Start() ORTHANC_OVERRIDE
     {
--- a/OrthancServer/Plugins/Samples/Basic/Plugin.c	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancServer/Plugins/Samples/Basic/Plugin.c	Wed Nov 12 19:07:18 2025 +0100
@@ -239,7 +239,7 @@
   else
   {
     const char* studyId = request->groups[0];
-    int32_t statusHasChanged = 0;
+    uint8_t statusHasChanged = 0;
 
     if (strcmp(request->groups[1], "stabilize") == 0)
     {
--- a/OrthancServer/Plugins/Samples/Housekeeper/Plugin.cpp	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancServer/Plugins/Samples/Housekeeper/Plugin.cpp	Wed Nov 12 19:07:18 2025 +0100
@@ -107,7 +107,7 @@
   bool isInPeriod() const
   {
     time_t now = time(NULL);
-    tm* nowLocalTime = localtime(&now);
+    const tm* nowLocalTime = localtime(&now);
 
     if (nowLocalTime->tm_wday != weekday_)
     {
--- a/OrthancServer/Plugins/Samples/ModalityWorklists/Plugin.cpp	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancServer/Plugins/Samples/ModalityWorklists/Plugin.cpp	Wed Nov 12 19:07:18 2025 +0100
@@ -76,12 +76,11 @@
 
 
   Worklist(const std::string& id, const Json::Value& jsonWl) :
-    id_(id)
+    id_(id),
+    createdAt_(jsonWl["CreatedAt"].asString())
   {
-    createdAt_ = jsonWl["CreatedAt"].asString();
     std::string b64DicomContent = jsonWl["Dicom"].asString();
     Orthanc::Toolbox::DecodeBase64(dicomContent_, b64DicomContent);
-
   }
 
   void Serialize(std::string& target) const
--- a/OrthancServer/Plugins/Samples/MultitenantDicom/StoreRequestHandler.cpp	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancServer/Plugins/Samples/MultitenantDicom/StoreRequestHandler.cpp	Wed Nov 12 19:07:18 2025 +0100
@@ -36,7 +36,9 @@
 uint16_t StoreRequestHandler::Handle(DcmDataset& dicom,
                                      const std::string& remoteIp,
                                      const std::string& remoteAet,
-                                     const std::string& calledAet)
+                                     const std::string& calledAet,
+                                     uint16_t originatorMessageId,
+                                     const std::string& originatorAet)
 {
   std::string buffer;
   std::string errorMessage;
--- a/OrthancServer/Plugins/Samples/MultitenantDicom/StoreRequestHandler.h	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancServer/Plugins/Samples/MultitenantDicom/StoreRequestHandler.h	Wed Nov 12 19:07:18 2025 +0100
@@ -44,5 +44,7 @@
   virtual uint16_t Handle(DcmDataset& dicom,
                           const std::string& remoteIp,
                           const std::string& remoteAet,
-                          const std::string& calledAet) ORTHANC_OVERRIDE;
+                          const std::string& calledAet,
+                          uint16_t originatorMessageId,
+                          const std::string& originatorAet) ORTHANC_OVERRIDE;
 };
--- a/OrthancServer/Resources/RunCppCheck.sh	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancServer/Resources/RunCppCheck.sh	Wed Nov 12 19:07:18 2025 +0100
@@ -24,11 +24,8 @@
 syntaxError:../../OrthancFramework/Sources/SQLite/FunctionContext.h:53
 syntaxError:../../OrthancFramework/UnitTestsSources/DicomMapTests.cpp:74
 syntaxError:../../OrthancFramework/UnitTestsSources/ZipTests.cpp:133
-syntaxError:../../OrthancServer/UnitTestsSources/UnitTestsMain.cpp:322
-uninitMemberVar:../../OrthancServer/Sources/ServerJobs/StorageCommitmentScpJob.cpp:417
-unreadVariable:../../OrthancFramework/Sources/FileStorage/StorageAccessor.cpp
-unreadVariable:../../OrthancServer/Sources/OrthancRestApi/OrthancRestModalities.cpp:1173
-unusedFunction
+syntaxError:../../OrthancServer/UnitTestsSources/UnitTestsMain.cpp:325
+uninitMemberVar:../../OrthancServer/Sources/ServerJobs/StorageCommitmentScpJob.cpp:419
 useInitializationList:../../OrthancFramework/Sources/Images/PngReader.cpp:91
 useInitializationList:../../OrthancFramework/Sources/Images/PngWriter.cpp:99
 useInitializationList:../../OrthancServer/Sources/ServerJobs/DicomModalityStoreJob.cpp:275
@@ -39,9 +36,46 @@
 assertWithSideEffect:../../OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp:3066
 assertWithSideEffect:../../OrthancServer/Sources/ServerJobs/ResourceModificationJob.cpp:286
 assertWithSideEffect:../../OrthancFramework/Sources/DicomNetworking/Internals/CommandDispatcher.cpp:454
+variableScope:../../OrthancServer/Sources/OrthancRestApi/OrthancRestApi.cpp:228
+variableScope:../../OrthancServer/Sources/ServerJobs/OrthancPeerStoreJob.cpp:94
+uselessOverride:../../OrthancFramework/Sources/MultiThreading/IRunnableBySteps.h:35
+uselessOverride:../../OrthancFramework/Sources/JobsEngine/SetOfInstancesJob.h:76
+cstyleCast:../../OrthancServer/Plugins/Engine/PluginsManager.cpp:85
+cstyleCast:../../OrthancServer/Plugins/Engine/PluginsManager.cpp:108
+cstyleCast:../../OrthancServer/Plugins/Engine/PluginsManager.cpp:124
+cstyleCast:../../OrthancServer/Plugins/Engine/PluginsManager.cpp:140
+constParameterPointer:../../OrthancServer/Plugins/Include/orthanc/OrthancCPlugin.h
+constParameterPointer:../../OrthancFramework/Sources/Logging.cpp:447
+constParameterPointer:../../OrthancFramework/Sources/Logging.cpp:451
+constParameterPointer:../../OrthancFramework/Sources/Toolbox.cpp:3053
+knownConditionTrueFalse:../../OrthancFramework/Sources/DicomNetworking/Internals/CommandDispatcher.cpp:114
+knownConditionTrueFalse:../../OrthancFramework/Sources/DicomParsing/Internals/DicomImageDecoder.cpp:425
+knownConditionTrueFalse:../../OrthancFramework/Sources/JobsEngine/Operations/SequenceOfOperationsJob.cpp:345
+throwInNoexceptFunction:../../OrthancFramework/Sources/Cache/MemoryStringCache.cpp:121
+throwInNoexceptFunction:../../OrthancFramework/Sources/Cache/MemoryCache.cpp:91
+throwInNoexceptFunction:../../OrthancFramework/Sources/Cache/MemoryObjectCache.cpp:99
+throwInNoexceptFunction:../../OrthancFramework/Sources/MallocMemoryBuffer.h:50
+throwInNoexceptFunction:../../OrthancFramework/Sources/MetricsRegistry.cpp:620
+throwInNoexceptFunction:../../OrthancFramework/Sources/MetricsRegistry.cpp:632
+throwInNoexceptFunction:../../OrthancFramework/Sources/MetricsRegistry.cpp:676
+throwInNoexceptFunction:../../OrthancFramework/Sources/JobsEngine/JobsRegistry.cpp:1296
+throwInNoexceptFunction:../../OrthancServer/Sources/LuaScripting.cpp:830
+throwInNoexceptFunction:../../OrthancServer/Sources/StorageCommitmentReports.cpp:187
+throwInNoexceptFunction:../../OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.h:218
+throwInNoexceptFunction:../../OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.h:376
+throwInNoexceptFunction:../../OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.h:496
+rethrowNoCurrentException:../../OrthancFramework/UnitTestsSources/FromDcmtkTests.cpp
+rethrowNoCurrentException:../../OrthancFramework/UnitTestsSources/RestApiTests.cpp
 EOF
 
-${CPPCHECK} --enable=all --quiet --std=c++11 \
+# TODO: re-enable nullPointerOutOfMemory
+
+${CPPCHECK} -j 8 --enable=all --quiet --std=c++11 \
+            --suppress=missingIncludeSystem \
+            --suppress=missingInclude \
+            --suppress=useStlAlgorithm \
+            --suppress=nullPointerOutOfMemory \
+            --check-level=exhaustive \
             --suppressions-list=/tmp/cppcheck-suppressions.txt \
             -DBOOST_HAS_DATE_TIME=1 \
             -DBOOST_HAS_FILESYSTEM_V3=1 \
--- a/OrthancServer/Sources/Database/Compatibility/GenericFind.cpp	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancServer/Sources/Database/Compatibility/GenericFind.cpp	Wed Nov 12 19:07:18 2025 +0100
@@ -187,7 +187,6 @@
                                request.GetOrthancIdentifiers(), ResourceType_Series, request.GetLevel());
       }
       else if (request.GetMetadataConstraintsCount() == 0 &&
-               request.GetOrdering().empty() &&
                !request.GetOrthancIdentifiers().HasPatientId() &&
                !request.GetOrthancIdentifiers().HasStudyId() &&
                !request.GetOrthancIdentifiers().HasSeriesId() &&
--- a/OrthancServer/Sources/Database/PrepareDatabase.sql	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancServer/Sources/Database/PrepareDatabase.sql	Wed Nov 12 19:07:18 2025 +0100
@@ -160,4 +160,4 @@
 
 -- Set the version of the database schema
 -- The "1" corresponds to the "GlobalProperty_DatabaseSchemaVersion" enumeration
-INSERT INTO GlobalProperties VALUES (1, "6");
+INSERT INTO GlobalProperties VALUES (1, 6);
--- a/OrthancServer/Sources/LuaScripting.cpp	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancServer/Sources/LuaScripting.cpp	Wed Nov 12 19:07:18 2025 +0100
@@ -1034,7 +1034,7 @@
     return true;
   }
 
-  bool LuaScripting::FilterIncomingCStoreInstance(uint16_t& dimseStatus,
+  bool LuaScripting::FilterIncomingCStoreInstance(uint16_t& /*dimseStatus*/,
                                                   const DicomInstanceToStore& instance,
                                                   const Json::Value& simplified)
   {
--- a/OrthancServer/Sources/OrthancFindRequestHandler.cpp	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancServer/Sources/OrthancFindRequestHandler.cpp	Wed Nov 12 19:07:18 2025 +0100
@@ -82,8 +82,7 @@
   }
 
 
-  bool OrthancFindRequestHandler::FilterQueryTag(std::string& value /* can be modified */,
-                                                 ResourceType level,
+  bool OrthancFindRequestHandler::FilterQueryTag(ResourceType level,
                                                  const DicomTag& tag,
                                                  ModalityManufacturer manufacturer)
   {
@@ -417,7 +416,7 @@
         continue;
       }
 
-      if (FilterQueryTag(value, level, tag, manufacturer))
+      if (FilterQueryTag(level, tag, manufacturer))
       {
         ValueRepresentation vr = FromDcmtkBridge::LookupValueRepresentation(tag);
 
--- a/OrthancServer/Sources/OrthancFindRequestHandler.h	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancServer/Sources/OrthancFindRequestHandler.h	Wed Nov 12 19:07:18 2025 +0100
@@ -40,8 +40,7 @@
     bool HasReachedLimit(const DicomFindAnswers& answers,
                          ResourceType level) const;
 
-    bool FilterQueryTag(std::string& value /* can be modified */,
-                        ResourceType level,
+    bool FilterQueryTag(ResourceType level,
                         const DicomTag& tag,
                         ModalityManufacturer manufacturer);
 
--- a/OrthancServer/Sources/OrthancGetRequestHandler.cpp	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancServer/Sources/OrthancGetRequestHandler.cpp	Wed Nov 12 19:07:18 2025 +0100
@@ -133,13 +133,14 @@
     LST_HEAD **l = &assoc->params->DULparams.acceptedPresentationContext;
     if (*l != NULL)
     {
-      DUL_PRESENTATIONCONTEXT* pc = (DUL_PRESENTATIONCONTEXT*) LST_Head(l);
-      LST_Position(l, (LST_NODE*)pc);
+      DUL_PRESENTATIONCONTEXT* pc = reinterpret_cast<DUL_PRESENTATIONCONTEXT*>(LST_Head(l));
+      LST_Position(l, reinterpret_cast<LST_NODE*>(pc));
       while (pc)
       {
-        DicomTransferSyntax transferSyntax;
         if (pc->result == ASC_P_ACCEPTANCE)
         {
+          DicomTransferSyntax transferSyntax;
+          
           if (LookupTransferSyntax(transferSyntax, pc->acceptedTransferSyntax))
           {
             /*CLOG(TRACE, DICOM) << "C-GET SCP accepted: SOP class " << pc->abstractSyntax
@@ -156,7 +157,7 @@
           }
         }
             
-        pc = (DUL_PRESENTATIONCONTEXT*) LST_Next(l);
+        pc = reinterpret_cast<DUL_PRESENTATIONCONTEXT*>(LST_Next(l));
       }
     }
 
--- a/OrthancServer/Sources/OrthancMoveRequestHandler.cpp	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancServer/Sources/OrthancMoveRequestHandler.cpp	Wed Nov 12 19:07:18 2025 +0100
@@ -85,12 +85,12 @@
         }
       }
 
-      virtual unsigned int GetSubOperationCount() const
+      virtual unsigned int GetSubOperationCount() const ORTHANC_OVERRIDE
       {
         return instances_.size();
       }
 
-      virtual Status DoNext()
+      virtual Status DoNext() ORTHANC_OVERRIDE
       {
         if (position_ >= instances_.size())
         {
@@ -169,12 +169,12 @@
         }
       }
 
-      virtual unsigned int GetSubOperationCount() const
+      virtual unsigned int GetSubOperationCount() const ORTHANC_OVERRIDE
       {
         return countInstances_;
       }
 
-      virtual Status DoNext()
+      virtual Status DoNext() ORTHANC_OVERRIDE
       {
         if (position_ >= countInstances_)
         {
--- a/OrthancServer/Sources/OrthancRestApi/OrthancRestAnonymizeModify.cpp	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestAnonymizeModify.cpp	Wed Nov 12 19:07:18 2025 +0100
@@ -601,7 +601,7 @@
 
 
   static void CreateDicomV1(ParsedDicomFile& dicom,
-                            RestApiPostCall& call,
+                            const RestApiPostCall& call,
                             const Json::Value& request)
   {
     // curl http://localhost:8042/tools/create-dicom -X POST -d '{"PatientName":"Hello^World"}'
--- a/OrthancServer/Sources/OrthancRestApi/OrthancRestApi.cpp	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestApi.cpp	Wed Nov 12 19:07:18 2025 +0100
@@ -72,7 +72,7 @@
 
 
   void OrthancRestApi::AnswerStoredInstance(RestApiPostCall& call,
-                                            DicomInstanceToStore& instance,
+                                            const DicomInstanceToStore& instance,
                                             StoreStatus status,
                                             const std::string& instanceId) const
   {
@@ -186,10 +186,10 @@
           std::unique_ptr<DicomInstanceToStore> toStore(DicomInstanceToStore::CreateFromBuffer(content));
           toStore->SetOrigin(DicomInstanceOrigin::FromRest(call));
 
-          std::string publicId;
-
           try
           {
+            std::string publicId;
+
             ServerContext::StoreResult result = context.Store(publicId, *toStore, StoreInstanceMode_Default);
 
             Json::Value info;
--- a/OrthancServer/Sources/OrthancRestApi/OrthancRestApi.h	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestApi.h	Wed Nov 12 19:07:18 2025 +0100
@@ -103,7 +103,7 @@
     // WARNING: "instanceId" can be different from
     // "instance.GetHasher().HashInstance()" if transcoding is enabled
     void AnswerStoredInstance(RestApiPostCall& call,
-                              DicomInstanceToStore& instance,
+                              const DicomInstanceToStore& instance,
                               StoreStatus status,
                               const std::string& instanceId) const;
 
--- a/OrthancServer/Sources/OrthancWebDav.cpp	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancServer/Sources/OrthancWebDav.cpp	Wed Nov 12 19:07:18 2025 +0100
@@ -597,7 +597,7 @@
     virtual INode* CreateSubfolder(const std::string& path) = 0;
 
   public:
-    virtual ~InternalNode()
+    virtual ~InternalNode() ORTHANC_OVERRIDE
     {
       for (Children::iterator it = children_.begin(); it != children_.end(); ++it)
       {
@@ -1221,10 +1221,10 @@
             std::unique_ptr<DicomInstanceToStore> instance(DicomInstanceToStore::CreateFromBuffer(uncompressedFile));
             instance->SetOrigin(DicomInstanceOrigin::FromWebDav());
 
-            std::string publicId;
-
             try
             {
+              std::string publicId;
+              
               context_.Store(publicId, *instance, StoreInstanceMode_Default);
             }
             catch (OrthancException& e)
@@ -1256,7 +1256,7 @@
             success = true;
           }
         }
-        catch (OrthancException& e)
+        catch (const OrthancException&)
         {
         }
       }
--- a/OrthancServer/Sources/OrthancWebDav.h	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancServer/Sources/OrthancWebDav.h	Wed Nov 12 19:07:18 2025 +0100
@@ -99,9 +99,9 @@
                   bool allowDicomDelete,
                   bool allowUpload);
 
-    virtual ~OrthancWebDav()
+    virtual ~OrthancWebDav() ORTHANC_OVERRIDE
     {
-      Stop();
+      OrthancWebDav::Stop();
     }
 
     virtual bool IsExistingFolder(const UriComponents& path) ORTHANC_OVERRIDE;
--- a/OrthancServer/Sources/Search/HierarchicalMatcher.cpp	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancServer/Sources/Search/HierarchicalMatcher.cpp	Wed Nov 12 19:07:18 2025 +0100
@@ -272,7 +272,7 @@
           currentPrivateCreator = "";
         }
 
-        std::unique_ptr<DcmElement> cloned(FromDcmtkBridge::CreateElementForTag(*it, currentPrivateCreator.c_str()));
+        std::unique_ptr<DcmElement> cloned(FromDcmtkBridge::CreateElementForTag(*it, currentPrivateCreator));
         cloned->copyFrom(*element);
         target->insert(cloned.release());
       }
--- a/OrthancServer/Sources/ServerContext.cpp	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancServer/Sources/ServerContext.cpp	Wed Nov 12 19:07:18 2025 +0100
@@ -1886,7 +1886,7 @@
           return decoded.release();
         }
       }
-      catch (OrthancException& e)
+      catch (const OrthancException&)
       { // ignore, we'll try other alternatives
       }
     }
@@ -1899,7 +1899,7 @@
       {
         decoded.reset(GetPlugins().Decode(buffer, size, frameIndex));
       }
-      catch (OrthancException& e)
+      catch (const OrthancException&)
       {
       }
       
--- a/OrthancServer/Sources/ServerContext.h	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancServer/Sources/ServerContext.h	Wed Nov 12 19:07:18 2025 +0100
@@ -319,7 +319,7 @@
                   bool readOnly,
                   unsigned int maxConcurrentDcmtkTranscoder);
 
-    ~ServerContext();
+    ~ServerContext() ORTHANC_OVERRIDE;
 
     void SetupJobsEngine(bool unitTesting,
                          bool loadJobsFromDatabase);
--- a/OrthancServer/Sources/ServerIndex.cpp	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancServer/Sources/ServerIndex.cpp	Wed Nov 12 19:07:18 2025 +0100
@@ -236,7 +236,7 @@
     {
     }
 
-    virtual ITransactionContext* Create()
+    virtual ITransactionContext* Create() ORTHANC_OVERRIDE
     {
       // There can be concurrent calls to this method, which is not an
       // issue because we simply create an object
--- a/OrthancServer/Sources/ServerJobs/ArchiveJob.cpp	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancServer/Sources/ServerJobs/ArchiveJob.cpp	Wed Nov 12 19:07:18 2025 +0100
@@ -207,9 +207,9 @@
       }
     }
 
-    virtual ~ThreadedInstanceLoader()
+    virtual ~ThreadedInstanceLoader() ORTHANC_OVERRIDE
     {
-      Clear();
+      ThreadedInstanceLoader::Clear();
     }
 
     virtual void Clear() ORTHANC_OVERRIDE
@@ -781,7 +781,6 @@
       }
         
       void Apply(HierarchicalZipWriter& writer,
-                 ServerContext& context,
                  InstanceLoader& instanceLoader,
                  DicomDirWriter* dicomDir,
                  const std::string& dicomDirFolder,
@@ -814,16 +813,11 @@
 
             writer.OpenFile(filename_.c_str());
 
-            std::unique_ptr<ParsedDicomFile> parsed;
-            
             writer.Write(content);
 
             if (dicomDir != NULL)
             {
-              if (parsed.get() == NULL)
-              {
-                parsed.reset(new ParsedDicomFile(content));
-              }
+              std::unique_ptr<ParsedDicomFile> parsed(new ParsedDicomFile(content));
 
               dicomDir->Add(dicomDirFolder, filename_, *parsed);
             }
@@ -844,7 +838,6 @@
 
       
     void ApplyInternal(HierarchicalZipWriter& writer,
-                       ServerContext& context,
                        InstanceLoader& instanceLoader,
                        size_t index,
                        DicomDirWriter* dicomDir,
@@ -857,7 +850,7 @@
         throw OrthancException(ErrorCode_ParameterOutOfRange);
       }
 
-      commands_[index]->Apply(writer, context, instanceLoader, dicomDir, dicomDirFolder, transcode, transferSyntax);
+      commands_[index]->Apply(writer, instanceLoader, dicomDir, dicomDirFolder, transcode, transferSyntax);
     }
       
   public:
@@ -895,7 +888,6 @@
 
     // "media" flavor (with DICOMDIR)
     void Apply(HierarchicalZipWriter& writer,
-               ServerContext& context,
                InstanceLoader& instanceLoader,
                size_t index,
                DicomDirWriter& dicomDir,
@@ -903,18 +895,17 @@
                bool transcode,
                DicomTransferSyntax transferSyntax) const
     {
-      ApplyInternal(writer, context, instanceLoader, index, &dicomDir, dicomDirFolder, transcode, transferSyntax);
+      ApplyInternal(writer, instanceLoader, index, &dicomDir, dicomDirFolder, transcode, transferSyntax);
     }
 
     // "archive" flavor (without DICOMDIR)
     void Apply(HierarchicalZipWriter& writer,
-               ServerContext& context,
                InstanceLoader& instanceLoader,
                size_t index,
                bool transcode,
                DicomTransferSyntax transferSyntax) const
     {
-      ApplyInternal(writer, context, instanceLoader, index, NULL, "", transcode, transferSyntax);
+      ApplyInternal(writer, instanceLoader, index, NULL, "", transcode, transferSyntax);
     }
       
     void AddOpenDirectory(const std::string& filename)
@@ -1244,13 +1235,13 @@
         if (isMedia_)
         {
           assert(dicomDir_.get() != NULL);
-          commands_.Apply(*zip_, context_, instanceLoader_, index, *dicomDir_,
+          commands_.Apply(*zip_, instanceLoader_, index, *dicomDir_,
                           MEDIA_IMAGES_FOLDER, transcode, transferSyntax);
         }
         else
         {
           assert(dicomDir_.get() == NULL);
-          commands_.Apply(*zip_, context_, instanceLoader_, index, transcode, transferSyntax);
+          commands_.Apply(*zip_, instanceLoader_, index, transcode, transferSyntax);
         }
       }
     }
--- a/OrthancServer/Sources/ServerJobs/ArchiveJob.h	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancServer/Sources/ServerJobs/ArchiveJob.h	Wed Nov 12 19:07:18 2025 +0100
@@ -83,7 +83,7 @@
                bool enableExtendedSopClass,
                ResourceType jobLevel);
     
-    virtual ~ArchiveJob();
+    virtual ~ArchiveJob() ORTHANC_OVERRIDE;
 
     void AcquireSynchronousTarget(ZipWriter::IOutputStream* synchronousTarget);
 
--- a/OrthancServer/Sources/ServerJobs/DicomGetScuJob.cpp	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancServer/Sources/ServerJobs/DicomGetScuJob.cpp	Wed Nov 12 19:07:18 2025 +0100
@@ -46,7 +46,7 @@
                                           const std::string& calledAet)
   {
     // this code is equivalent to OrthancStoreRequestHandler
-    ServerContext* context = reinterpret_cast<ServerContext*>(callbackContext);
+    DicomRetrieveScuBaseJob* job = reinterpret_cast<DicomRetrieveScuBaseJob*>(callbackContext);
 
     std::unique_ptr<DicomInstanceToStore> toStore(DicomInstanceToStore::CreateFromDcmDataset(dataset));
     
@@ -56,7 +56,14 @@
                          (remoteIp.c_str(), remoteAet.c_str(), calledAet.c_str()));
 
       std::string id;
-      ServerContext::StoreResult result = context->Store(id, *toStore, StoreInstanceMode_Default);
+      ServerContext::StoreResult result = job->GetContext().Store(id, *toStore, StoreInstanceMode_Default);
+
+      // keep track of the received instances in the job
+      if (result.GetStatus() == StoreStatus_Success || result.GetStatus() == StoreStatus_AlreadyStored)
+      {
+        job->AddReceivedInstance(id);
+      }
+
       return result.GetCStoreStatusCode();
     }
 
@@ -68,11 +75,12 @@
     if (connection_.get() == NULL)
     {
       std::set<std::string> sopClassesToPropose;
-      std::set<std::string> acceptedSopClasses;
       std::list<DicomTransferSyntax> proposedTransferSyntaxes;
 
       if (sopClassesFromResourcesToRetrieve_.size() > 0)
       {
+        std::set<std::string> acceptedSopClasses;
+        
         context_.GetAcceptedSopClasses(acceptedSopClasses, 0); 
 
         // keep the sop classes from the resources to retrieve only if they are accepted by Orthanc
@@ -99,7 +107,7 @@
     }
 
     connection_->SetProgressListener(this);
-    connection_->Get(findAnswer, InstanceReceivedHandler, &context_);
+    connection_->Get(findAnswer, InstanceReceivedHandler, this);
   }
 
   void DicomGetScuJob::AddFindAnswer(const DicomMap& answer)
--- a/OrthancServer/Sources/ServerJobs/DicomMoveScuJob.cpp	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancServer/Sources/ServerJobs/DicomMoveScuJob.cpp	Wed Nov 12 19:07:18 2025 +0100
@@ -47,7 +47,9 @@
     }
     
     connection_->SetProgressListener(this);
-    connection_->Move(targetAet_, findAnswer);
+    
+    // we use a unique message ID in order to know to which Move request a stored instance relates to.
+    connection_->Move(targetAet_, findAnswer, GetMessageId(GetParameters().GetLocalApplicationEntityTitle()));
   }
 
 
--- a/OrthancServer/Sources/ServerJobs/DicomRetrieveScuBaseJob.cpp	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancServer/Sources/ServerJobs/DicomRetrieveScuBaseJob.cpp	Wed Nov 12 19:07:18 2025 +0100
@@ -29,6 +29,7 @@
 #include <dcmtk/dcmnet/dimse.h>
 #include <algorithm>
 #include "../../../OrthancFramework/Sources/Logging.h"
+#include <boost/thread/mutex.hpp>
 
 static const char* const LOCAL_AET = "LocalAet";
 static const char* const QUERY = "Query";
@@ -36,8 +37,106 @@
 static const char* const REMOTE = "Remote";
 static const char* const TIMEOUT = "Timeout";
 
+static std::map<std::string, Orthanc::SetOfCommandsJob::ICommand*> messagesRegistry; 
+static uint16_t messageRegistryCurrentId = 1000;
+static boost::mutex messageRegistryMutex;
+
 namespace Orthanc
 {
+  std::string GetKey(const std::string& aet, uint16_t messageId)
+  {
+    return aet + "-" + boost::lexical_cast<std::string>(messageId);
+  }
+
+  uint16_t DicomRetrieveScuBaseJob::GetMessageId(const std::string& localAet)
+  {
+    assert(currentCommand_ != NULL);
+    boost::mutex::scoped_lock lock(messageRegistryMutex);
+    
+    // Each resource retrieval (command) has its own messageId.  
+    // We start at 1000 to clearly differentiate them from other messages.  We can actually use ANY value between 0 & 65535.
+    messageRegistryCurrentId = std::max(1000, (messageRegistryCurrentId + 1) % 0xFFFF);
+    messagesRegistry[GetKey(localAet, messageRegistryCurrentId)] = currentCommand_;
+    
+    return messageRegistryCurrentId;
+  }
+
+  void DicomRetrieveScuBaseJob::AddReceivedInstanceFromCStore(uint16_t originatorMessageId, 
+                                                              const std::string& originatorAet, 
+                                                              const std::string& instanceId)
+  {
+    boost::mutex::scoped_lock lock(messageRegistryMutex);
+
+    std::string key = GetKey(originatorAet, originatorMessageId);
+
+    if (messagesRegistry.find(key) != messagesRegistry.end())
+    {
+      dynamic_cast<DicomRetrieveScuBaseJob::Command*>(messagesRegistry[key])->AddReceivedInstance(instanceId);
+    }
+  }
+
+  DicomRetrieveScuBaseJob::Command::~Command()
+  {
+    // remove the command from the messageRegistry
+    boost::mutex::scoped_lock lock(messageRegistryMutex);
+
+    for (std::map<std::string, Orthanc::SetOfCommandsJob::ICommand*>::const_iterator it = messagesRegistry.begin();
+         it != messagesRegistry.end(); ++it)
+    {
+      if (it->second == this)
+      {
+        messagesRegistry.erase(it->first);
+        return;
+      }
+    }
+  }
+
+  bool DicomRetrieveScuBaseJob::Command::Execute(const std::string &jobId)
+  {
+    try
+    {
+      that_.currentCommand_ = this;
+      that_.Retrieve(*findAnswer_); // here
+    }
+    catch (OrthancException& e)
+    {
+      dimseErrorStatus_ = e.GetDimseErrorStatus();
+      throw e;
+    }
+    return true;
+  }
+
+  void DicomRetrieveScuBaseJob::Command::Serialize(Json::Value &target) const
+  {
+    findAnswer_->Serialize(target["Query"]);
+    target["DimseErrorStatus"] = dimseErrorStatus_;
+    SerializationToolbox::WriteListOfStrings(target, receivedInstancesIds_, "ReceivedInstancesIds");
+  }
+
+  SetOfCommandsJob::ICommand* DicomRetrieveScuBaseJob::Unserializer::Unserialize(const Json::Value &source) const
+  {
+    DicomMap findAnswer;
+
+    if (!source.isMember("Query")) // old format before 1.12.10, just in case we need to read old jobs after an upgrade
+    {
+      findAnswer.Unserialize(source);
+      return new DicomRetrieveScuBaseJob::Command(that_, findAnswer);
+    }
+
+    findAnswer.Unserialize(source["Query"]);
+
+    std::unique_ptr<DicomRetrieveScuBaseJob::Command> command(new DicomRetrieveScuBaseJob::Command(that_, findAnswer));
+    command->SetDimseErrorStatus(static_cast<uint16_t>(source["DimseErrorStatus"].asUInt()));
+
+    std::list<std::string> receivedInstancesIds;
+    SerializationToolbox::ReadListOfStrings(receivedInstancesIds, source, "ReceivedInstancesIds");
+    for (std::list<std::string>::const_iterator it = receivedInstancesIds.begin(); it != receivedInstancesIds.end(); ++it)
+    {
+      command->AddReceivedInstance(*it);
+    }
+
+    return command.release();
+  }
 
   static void AddToQuery(DicomFindAnswers& query,
                          const DicomMap& item)
@@ -152,6 +251,20 @@
 
     value[QUERY] = Json::objectValue;
     query_.ToJson(value[QUERY], queryFormat_);
+
+    value["Details"] = Json::arrayValue;
+    
+    for (size_t i = 0; i < GetCommandsCount(); ++i)
+    {
+      const DicomRetrieveScuBaseJob::Command& command = dynamic_cast<const DicomRetrieveScuBaseJob::Command&>(GetCommand(i));
+
+      Json::Value v;
+      v["DimseErrorStatus"] = command.GetDimseErrorStatus();
+      query_.ToJson(v["Query"], i, DicomToJsonFormat_Short);
+      SerializationToolbox::WriteListOfStrings(v, command.GetReceivedInstancesIds(), "ReceivedInstancesIds");
+
+      value["Details"].append(v);
+    }
   }
 
 
@@ -162,7 +275,8 @@
     nbRemainingSubOperations_(0),
     nbCompletedSubOperations_(0),
     nbFailedSubOperations_(0),
-    nbWarningSubOperations_(0)
+    nbWarningSubOperations_(0),
+    currentCommand_(NULL)
   {
   }
 
@@ -172,12 +286,13 @@
     SetOfCommandsJob(new Unserializer(*this), serialized),
     context_(context),
     parameters_(DicomAssociationParameters::UnserializeJob(serialized)),
-    query_(true),
+    query_(false /* this is not for worklists */),
     queryFormat_(DicomToJsonFormat_Short),
     nbRemainingSubOperations_(0),
     nbCompletedSubOperations_(0),
     nbFailedSubOperations_(0),
-    nbWarningSubOperations_(0)  
+    nbWarningSubOperations_(0),
+    currentCommand_(NULL)
   {
     if (serialized.isMember(QUERY))
     {
@@ -245,4 +360,12 @@
 
     return float(nbCompletedSubOperations_ + nbFailedSubOperations_ + nbWarningSubOperations_) / float(totalOperations);
   }
+
+  void DicomRetrieveScuBaseJob::AddReceivedInstance(const std::string& instanceId)
+  {
+    if (currentCommand_ != NULL)
+    {
+      currentCommand_->AddReceivedInstance(instanceId);
+    }
+  }
 }
--- a/OrthancServer/Sources/ServerJobs/DicomRetrieveScuBaseJob.h	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancServer/Sources/ServerJobs/DicomRetrieveScuBaseJob.h	Wed Nov 12 19:07:18 2025 +0100
@@ -36,29 +36,50 @@
   class DicomRetrieveScuBaseJob : public SetOfCommandsJob, public DicomControlUserConnection::IProgressListener
   {
   protected:
+    uint16_t GetMessageId(const std::string& localAet);
+    
     class Command : public SetOfCommandsJob::ICommand
     {
     private:
       DicomRetrieveScuBaseJob &that_;
       std::unique_ptr<DicomMap> findAnswer_;
 
+      uint16_t                dimseErrorStatus_;
+      std::list<std::string>  receivedInstancesIds_;
+
     public:
       Command(DicomRetrieveScuBaseJob &that,
               const DicomMap &findAnswer) :
       that_(that),
-      findAnswer_(findAnswer.Clone())
+      findAnswer_(findAnswer.Clone()),
+      dimseErrorStatus_(0)
       {
       }
 
-      virtual bool Execute(const std::string &jobId) ORTHANC_OVERRIDE
+      virtual ~Command() ORTHANC_OVERRIDE;
+
+      virtual bool Execute(const std::string &jobId) ORTHANC_OVERRIDE;
+
+      virtual void Serialize(Json::Value &target) const ORTHANC_OVERRIDE;
+
+      uint16_t GetDimseErrorStatus() const
       {
-        that_.Retrieve(*findAnswer_);
-        return true;
+        return dimseErrorStatus_;
       }
 
-      virtual void Serialize(Json::Value &target) const ORTHANC_OVERRIDE
+      void SetDimseErrorStatus(uint16_t dimseErrorStatus)
+      {
+        dimseErrorStatus_ = dimseErrorStatus;
+      }
+
+      void AddReceivedInstance(const std::string& instanceId)
       {
-        findAnswer_->Serialize(target);
+        receivedInstancesIds_.push_back(instanceId);
+      }
+
+      const std::list<std::string>& GetReceivedInstancesIds() const
+      {
+        return receivedInstancesIds_;
       }
     };
 
@@ -73,12 +94,7 @@
       {
       }
 
-      virtual ICommand *Unserialize(const Json::Value &source) const ORTHANC_OVERRIDE
-      {
-        DicomMap findAnswer;
-        findAnswer.Unserialize(source);
-        return new Command(that_, findAnswer);
-      }
+      virtual ICommand *Unserialize(const Json::Value &source) const ORTHANC_OVERRIDE;
     };
 
     ServerContext &context_;
@@ -94,6 +110,8 @@
     uint16_t nbFailedSubOperations_;
     uint16_t nbWarningSubOperations_;
 
+    Command* currentCommand_;
+
     virtual void Retrieve(const DicomMap &findAnswer) = 0;
 
     explicit DicomRetrieveScuBaseJob(ServerContext &context);
@@ -145,5 +163,15 @@
       return true;
     }
 
+    ServerContext& GetContext()
+    {
+      return context_;
+    }
+
+    void AddReceivedInstance(const std::string& instanceId);
+
+    static void AddReceivedInstanceFromCStore(uint16_t originatorMessageId, 
+                                              const std::string& originatorAet, 
+                                              const std::string& instanceId);
   };
 }
--- a/OrthancServer/Sources/ServerJobs/ResourceModificationJob.cpp	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancServer/Sources/ServerJobs/ResourceModificationJob.cpp	Wed Nov 12 19:07:18 2025 +0100
@@ -185,7 +185,7 @@
       for (std::set<std::string>::const_iterator it = instancesToReconstruct_.begin(); it != instancesToReconstruct_.end(); ++it)
       {
         ServerContext::DicomCacheLocker locker(GetContext(), *it);
-        ParsedDicomFile& modifiedDicom = locker.GetDicom();
+        const ParsedDicomFile& modifiedDicom = locker.GetDicom();
 
         GetContext().GetIndex().ReconstructInstance(modifiedDicom, false, ResourceType_Instance /* dummy */);
       }
@@ -219,7 +219,7 @@
     try
     {
       ServerContext::DicomCacheLocker locker(GetContext(), instance);
-      ParsedDicomFile& original = locker.GetDicom();
+      const ParsedDicomFile& original = locker.GetDicom();
 
       originalHasher.reset(new DicomInstanceHasher(original.GetHasher()));
       modified.reset(original.Clone(true));
@@ -689,7 +689,7 @@
       }
 
       // and we must make sure that we overwite them with the modified resources
-      if (IsKeepSource() && !GetContext().IsOverwriteInstances())
+      if (!GetContext().IsOverwriteInstances())
       {
         throw OrthancException(ErrorCode_BadRequest,
                               "When keeping StudyInstanceUID, SeriesInstanceUID and SOPInstanceUID tag, you must have the 'OverwriteInstances' Orthanc configuration set to true in order to replace the modified resources");
--- a/OrthancServer/Sources/ServerJobs/StorageCommitmentScpJob.cpp	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancServer/Sources/ServerJobs/StorageCommitmentScpJob.cpp	Wed Nov 12 19:07:18 2025 +0100
@@ -375,6 +375,8 @@
     }
     else
     {
+      SetOfCommandsJob::Reserve(size);
+
       sopClassUids_.reserve(size);
       sopInstanceUids_.reserve(size);
     }
--- a/OrthancServer/Sources/ServerJobs/StorageCommitmentScpJob.h	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancServer/Sources/ServerJobs/StorageCommitmentScpJob.h	Wed Nov 12 19:07:18 2025 +0100
@@ -78,7 +78,7 @@
     StorageCommitmentScpJob(ServerContext& context,
                             const Json::Value& serialized);
 
-    void Reserve(size_t size);
+    virtual void Reserve(size_t size) ORTHANC_OVERRIDE;
     
     void AddInstance(const std::string& sopClassUid,
                      const std::string& sopInstanceUid);
--- a/OrthancServer/Sources/ServerJobs/ThreadedSetOfInstancesJob.h	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancServer/Sources/ServerJobs/ThreadedSetOfInstancesJob.h	Wed Nov 12 19:07:18 2025 +0100
@@ -84,7 +84,7 @@
                                        bool hasTrailingStep,
                                        bool defaultKeepSource);
 
-    virtual ~ThreadedSetOfInstancesJob();
+    virtual ~ThreadedSetOfInstancesJob() ORTHANC_OVERRIDE;
 
   protected:
     virtual bool HandleInstance(const std::string& instance) = 0;
--- a/OrthancServer/Sources/SimpleInstanceOrdering.cpp	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancServer/Sources/SimpleInstanceOrdering.cpp	Wed Nov 12 19:07:18 2025 +0100
@@ -96,7 +96,6 @@
     for (size_t i = 0; i < response.GetSize(); ++i)
     {
       const FindResponse::Resource& resource = response.GetResourceByIndex(i);
-      std::string instanceId = resource.GetIdentifier();
 
       std::string strIndexInSeries;
       uint32_t indexInSeries = 0;
@@ -110,6 +109,8 @@
 
       if (resource.LookupAttachment(fileInfo, revisionNotUsed, FileContentType_Dicom))
       {
+        std::string instanceId = resource.GetIdentifier();
+        
         allIndexInSeries.insert(indexInSeries);
         instances_.push_back(new SimpleInstanceOrdering::Instance(instanceId, indexInSeries, fileInfo));
       }
--- a/OrthancServer/Sources/SliceOrdering.cpp	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancServer/Sources/SliceOrdering.cpp	Wed Nov 12 19:07:18 2025 +0100
@@ -192,12 +192,12 @@
       }
 
       hasNormal_ = ComputeNormal(normal_, instance);
-
-      std::string s;
       hasIndexInSeries_ = false;
 
       try
       {
+        std::string s;
+  
         if (index.LookupMetadata(s, instanceId, ResourceType_Instance, MetadataType_Instance_IndexInSeries))
         {
           indexInSeries_ = boost::lexical_cast<size_t>(Toolbox::StripSpaces(s));
--- a/OrthancServer/Sources/main.cpp	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancServer/Sources/main.cpp	Wed Nov 12 19:07:18 2025 +0100
@@ -47,6 +47,7 @@
 #include "ServerContext.h"
 #include "ServerEnumerations.h"
 #include "ServerJobs/StorageCommitmentScpJob.h"
+#include "ServerJobs/DicomRetrieveScuBaseJob.h"
 #include "ServerToolbox.h"
 #include "StorageCommitmentReports.h"
 
@@ -86,7 +87,9 @@
   virtual uint16_t Handle(DcmDataset& dicom,
                           const std::string& remoteIp,
                           const std::string& remoteAet,
-                          const std::string& calledAet) ORTHANC_OVERRIDE 
+                          const std::string& calledAet,
+                          uint16_t originatorMessageId,
+                          const std::string& originatorAet) ORTHANC_OVERRIDE 
   {
     std::unique_ptr<DicomInstanceToStore> toStore(DicomInstanceToStore::CreateFromDcmDataset(dicom));
     
@@ -97,6 +100,13 @@
 
       std::string id;
       ServerContext::StoreResult result = context_.Store(id, *toStore, StoreInstanceMode_Default);
+
+      if (result.GetStatus() == StoreStatus_Success || result.GetStatus() == StoreStatus_AlreadyStored)
+      {
+        // In case this C-Store was triggered from a C-Move job, keep track of the instances that have been received
+        DicomRetrieveScuBaseJob::AddReceivedInstanceFromCStore(originatorMessageId, originatorAet, id);
+      }
+
       return result.GetCStoreStatusCode();
     }
 
--- a/OrthancServer/UnitTestsSources/ServerJobsTests.cpp	Tue Nov 04 15:58:06 2025 +0100
+++ b/OrthancServer/UnitTestsSources/ServerJobsTests.cpp	Wed Nov 12 19:07:18 2025 +0100
@@ -1274,10 +1274,12 @@
 TEST_F(OrthancJobsSerialization, DicomMoveScuJob)
 {
   Json::Value command = Json::objectValue;
-  command["0008,0005"]["Type"] = "String";
-  command["0008,0005"]["Content"] = "ISO_IR 100";
-  command["0010,0020"]["Type"] = "String";
-  command["0010,0020"]["Content"] = "1234";
+  command["Query"]["0008,0005"]["Type"] = "String";
+  command["Query"]["0008,0005"]["Content"] = "ISO_IR 100";
+  command["Query"]["0010,0020"]["Type"] = "String";
+  command["Query"]["0010,0020"]["Content"] = "1234";
+  command["DimseErrorStatus"] = 0;
+  command["ReceivedInstancesIds"] = Json::arrayValue;
 
   Json::Value query = Json::objectValue;
   query["0010,0020"] = "456";
--- a/TODO	Tue Nov 04 15:58:06 2025 +0100
+++ b/TODO	Wed Nov 12 19:07:18 2025 +0100
@@ -64,7 +64,8 @@
   https://discourse.orthanc-server.org/t/instances-id-content-api-results-are-different-in-docker-swarm-replicas-of-orthanc/4582
 * Allow saving PrivateTags in ExtraMainDicomTags.
   Note: they can actually be stored but they then appear as "Unknown Tag & Data" in the responses.
-  If we try to add the PrivateCreator in the ExtraMainDicomTags, then, the DICOMWeb plugin fails to initialize because the private tags are not known.
+  If we try to add the PrivateCreator in the ExtraMainDicomTags, then, the DICOMweb plugin fails
+  to initialize because the private tags are not known.
 * Support hashed passwords in RegisteredUsers.  E.g:
   "RegisteredUsers": { 
     "admin": {
@@ -216,6 +217,16 @@
 * Log outgoing C-Find queries
 * Support other Transfer Syntaxes in the Worklist plugin:
   https://discourse.orthanc-server.org/t/could-you-please-create-an-option-to-set-the-transfer-syntax-in-the-worklist-plugin-currently-little-endian-explicit-is-fixed/4871
+* For C-Find (including C-Find for worklists) Orthanc always uses the "DefaultEncoding" in the response even if it needs to 
+  return e.g. an Utf-8 PatientName that can not be represented in the the "DefaultEncoding" e.g. "Latin1".  
+  According to the standard (PS 3.4 C.4.1.1.3.2):
+    Conditionally, the Attribute Specific Character Set (0008,0005). This Attribute shall be included if expanded or replacement 
+    character sets may be used in any of the Attributes in the Response Identifier. It shall not be included otherwise. 
+    The C-FIND SCP is not required to return responses in the Specific Character Set requested by the SCU if that character set 
+    is not supported by the SCP. The SCP may return responses with a different Specific Character Set.
+  Therefore, Orthanc should:
+    - Preferrably use the same SpecificCharacterSet as the one used in the C-Find request.
+    - If Orthanc is unable to represent a string with this encoding, it shall switch to UTF-8 in the response
 * Deidentify Trace level logs: https://discourse.orthanc-server.org/t/dicom-log-deidentification-at-trace-log-level/5563
   We should implement something like GetDeidentifiedContent(const DcmDataSet& dataset) that would 
   reproduce DcmDataSet::print but hide individual elements that contain PHI.
@@ -256,7 +267,7 @@
 * (3) Check out rapidjson: https://github.com/miloyip/nativejson-benchmark
 
 * Long-shot & not sure it is even feasible at all: try to reduce memory usage by implementing streaming
-  when receiving DICOM instances from the Rest API or from DICOM and store files directly to disk as they
+  when receiving DICOM instances from the REST API or from DICOM and store files directly to disk as they
   are received.  Note that this would likely require rewriting large parts of Orthanc.  Note sure that 
   would be compatible with Transcoding.
   Use case: receiving 10 1GB instances in parallel can consume up to 20 GB