changeset 3375:73d60d80a74a

merge
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 14 May 2019 16:38:20 +0200
parents d0d6bd633e4c (current diff) 4a8e8a96b233 (diff)
children 56ea9c476dba
files
diffstat 8 files changed, 826 insertions(+), 819 deletions(-) [+]
line wrap: on
line diff
--- a/Core/Images/ImageProcessing.h	Tue May 14 16:38:02 2019 +0200
+++ b/Core/Images/ImageProcessing.h	Tue May 14 16:38:20 2019 +0200
@@ -37,6 +37,7 @@
 #include <vector>
 
 #include <stdint.h>
+#include <algorithm>
 
 namespace Orthanc
 {
@@ -64,6 +65,12 @@
         y_ = y;
       }
 
+      void ClipTo(int32_t minX, int32_t maxX, int32_t minY, int32_t maxY)
+      {
+        x_ = std::max(minX, std::min(maxX, x_));
+        y_ = std::max(minY, std::min(maxY, y_));
+      }
+
       double GetDistanceTo(const ImagePoint& other) const;
     };
 
--- a/INSTALL	Tue May 14 16:38:02 2019 +0200
+++ b/INSTALL	Tue May 14 16:38:20 2019 +0200
@@ -1,162 +1,162 @@
-Orthanc - A Lightweight, RESTful DICOM Server
-=============================================
-
-
-Dependencies
-------------
-
-1) CMake: Orthanc uses CMake (http://www.cmake.org/) to automate its
-   building process.
-
-2) Python: Some code is autogenerated through Python
-   (http://www.python.org/).
-
-3) Mercurial: To use the cutting edge code, a Mercurial client must be
-   installed (http://mercurial.selenic.com/). We recommend TortoiseHg.
-
-W) 7-Zip: For the native build under Windows, the 7-Zip tool is used
-   to uncompress the third-party packages (http://www.7-zip.org/).
-
-You thus have to download and install CMake, Python, Mercurial and
-possibly 7-Zip first. The path to their executable must be in the
-"PATH" environment variable.
-
-The other third party dependencies are automatically downloaded by the
-CMake scripts. The downloaded packages are stored in the
-"ThirdPartyDownloads" directory.
-
-
-Building Orthanc at a glance
-----------------------------
-
-To build Orthanc, you must:
-
-1) Download the source code (either using Mercurial, or through the
-   official releases). For the examples below, we assume the source
-   directory is "~/Orthanc".
-
-2) Create a build directory. For the examples below, we assume the
-   build directory is "~/OrthancBuild".
-
-3) Depending on your platform, follow the build instructions below.
-
-
-WARNING 1: If you do not create a fresh "~/OrthancBuild" directory
-after upgrading the source code (i.e. if you reuse the build directory
-that was used to build a different version of Orthanc), the build
-might fail because of changes in the compilation/linking flags. Always
-prefer to force a re-build in a new directory.
-
-WARNING 2: If cmake complains about not being able to uncompress
-third-party dependencies, delete the "~/Orthanc/ThirdPartyDownloads/"
-folder, then restart cmake.
-
-WARNING 3: If performance is important to you, make sure to add the
-option "-DCMAKE_BUILD_TYPE=Release" when invoking cmake. Indeed, by
-default, run-time debug assertions are enabled, which can seriously
-impact performance, especially if your Orthanc server stores a lot of
-DICOM instances.
-
-
-Native GNU/Linux Compilation
-----------------------------
-
-See the file "LinuxCompilation.txt".
-
-
-Native OS X Compilation
------------------------
-
-See the file "DarwinCompilation.txt".
-
-
-
-Native Windows build with Microsoft Visual Studio 2008
-------------------------------------------------------
-
-# cd [...]\OrthancBuild
-# cmake -DSTANDALONE_BUILD=ON -DSTATIC_BUILD=ON -DALLOW_DOWNLOADS=ON \
-  -DUSE_LEGACY_JSONCPP=ON -G "Visual Studio 9 2008" [...]\Orthanc
-
-Then open the "[...]/OrthancBuild/Orthanc.sln" with Visual Studio.
-
-NOTES:
-* More recent versions of Visual Studio than 2008 should also
-  work. Type "cmake" without arguments to have the list of generators
-  that are available on your computer.
-* You will have to install the Platform SDK (version 6 or above) for
-  Visual Studio 2005:
-  http://en.wikipedia.org/wiki/Microsoft_Windows_SDK.
-  Read the CMake FAQ: http://goo.gl/By90B 
-* The "-DUSE_LEGACY_JSONCPP=ON" must be set for versions of
-  Visual Studio that do not support C++11
-
-
-Orthanc as compiled above will not work properly with some Asian
-encodings (unit tests will fail). In international setups, you can
-compile Orthanc together with ICU as follows:
-
-# cmake -DSTANDALONE_BUILD=ON -DSTATIC_BUILD=ON -DALLOW_DOWNLOADS=ON \
-  -DBOOST_LOCALE_BACKEND=icu -DUSE_LEGACY_JSONCPP=ON -DUSE_LEGACY_LIBICU=ON \
-  -G "Visual Studio 9 2008" [...]\Orthanc
-
-
-
-Native Windows build with Microsoft Visual Studio 2015, Ninja and QtCreator
----------------------------------------------------------------------------
-
-Open a Visual Studio 2015 x64 Command Prompt.
-
-# cd [...]\OrthancBuild
-# cmake -G Ninja -DSTATIC_BUILD=ON [...]\Orthanc
-# ninja
-
-Then, you can open an existing project in QtCreator:
-* Select the CMakeLists.txt in [...]\Orthanc
-* Import build from [...]\OrthancBuild
-
-
-Instructions to include support for Asian encodings:
-
-# cmake -G Ninja -T host=x64 -DSTATIC_BUILD=ON -DBOOST_LOCALE_BACKEND=icu [...]\Orthanc
-
-The option "-T host=x64" is necessary to prevent error "C1060:
-compiler is out of heap space" when compiling Orthanc with ICU.
-
-
-
-Cross-Compilation for Windows under GNU/Linux
----------------------------------------------
-
-Some versions of MinGW-W64 might have problems with C++11 (notably
-those shipped in Ubuntu 16.04 LTS, in the "mingw-w64" package). Use
-the following command to disable C++11:
-
-# cd ~/OrthancBuild
-# cmake ~/Orthanc \
-        -DCMAKE_BUILD_TYPE=Debug \
-        -DCMAKE_TOOLCHAIN_FILE=~/Orthanc/Resources/MinGW-W64-Toolchain32.cmake \
-        -DSTANDALONE_BUILD=ON \
-        -DSTATIC_BUILD=ON \
-        -DUSE_LEGACY_JSONCPP=ON
-# make
-
-NB: Use the toolchain "MinGW-W64-Toolchain64.cmake" to produce 64bit
-Windows binaries.
-
-
-
-Legacy MinGW32 compilers (notably those shipped in Ubuntu 14.04 LTS,
-in the "mingw32" package) are incompatible with DCMTK 3.6.2 and
-C++11. Use the following command to force using DCMTK 3.6.0 and
-disable C++11:
-
-# cd ~/OrthancBuild
-# cmake ~/Orthanc \
-        -DCMAKE_BUILD_TYPE=Debug \
-        -DCMAKE_TOOLCHAIN_FILE=~/Orthanc/Resources/MinGWToolchain.cmake \
-        -DSTANDALONE_BUILD=ON \
-        -DSTATIC_BUILD=ON \
-        -DDCMTK_STATIC_VERSION=3.6.0 \
-        -DUSE_LEGACY_JSONCPP=ON
-# make
+Orthanc - A Lightweight, RESTful DICOM Server
+=============================================
+
+
+Dependencies
+------------
+
+1) CMake: Orthanc uses CMake (http://www.cmake.org/) to automate its
+   building process.
+
+2) Python: Some code is autogenerated through Python
+   (http://www.python.org/).
+
+3) Mercurial: To use the cutting edge code, a Mercurial client must be
+   installed (http://mercurial.selenic.com/). We recommend TortoiseHg.
+
+W) 7-Zip: For the native build under Windows, the 7-Zip tool is used
+   to uncompress the third-party packages (http://www.7-zip.org/).
+
+You thus have to download and install CMake, Python, Mercurial and
+possibly 7-Zip first. The path to their executable must be in the
+"PATH" environment variable.
+
+The other third party dependencies are automatically downloaded by the
+CMake scripts. The downloaded packages are stored in the
+"ThirdPartyDownloads" directory.
+
+
+Building Orthanc at a glance
+----------------------------
+
+To build Orthanc, you must:
+
+1) Download the source code (either using Mercurial, or through the
+   official releases). For the examples below, we assume the source
+   directory is "~/Orthanc".
+
+2) Create a build directory. For the examples below, we assume the
+   build directory is "~/OrthancBuild".
+
+3) Depending on your platform, follow the build instructions below.
+
+
+WARNING 1: If you do not create a fresh "~/OrthancBuild" directory
+after upgrading the source code (i.e. if you reuse the build directory
+that was used to build a different version of Orthanc), the build
+might fail because of changes in the compilation/linking flags. Always
+prefer to force a re-build in a new directory.
+
+WARNING 2: If cmake complains about not being able to uncompress
+third-party dependencies, delete the "~/Orthanc/ThirdPartyDownloads/"
+folder, then restart cmake.
+
+WARNING 3: If performance is important to you, make sure to add the
+option "-DCMAKE_BUILD_TYPE=Release" when invoking cmake. Indeed, by
+default, run-time debug assertions are enabled, which can seriously
+impact performance, especially if your Orthanc server stores a lot of
+DICOM instances.
+
+
+Native GNU/Linux Compilation
+----------------------------
+
+See the file "LinuxCompilation.txt".
+
+
+Native OS X Compilation
+-----------------------
+
+See the file "DarwinCompilation.txt".
+
+
+
+Native Windows build with Microsoft Visual Studio 2008
+------------------------------------------------------
+
+# cd [...]\OrthancBuild
+# cmake -DSTANDALONE_BUILD=ON -DSTATIC_BUILD=ON -DALLOW_DOWNLOADS=ON \
+  -DUSE_LEGACY_JSONCPP=ON -G "Visual Studio 9 2008" [...]\Orthanc
+
+Then open the "[...]/OrthancBuild/Orthanc.sln" with Visual Studio.
+
+NOTES:
+* More recent versions of Visual Studio than 2008 should also
+  work. Type "cmake" without arguments to have the list of generators
+  that are available on your computer.
+* You will have to install the Platform SDK (version 6 or above) for
+  Visual Studio 2005:
+  http://en.wikipedia.org/wiki/Microsoft_Windows_SDK.
+  Read the CMake FAQ: http://goo.gl/By90B 
+* The "-DUSE_LEGACY_JSONCPP=ON" must be set for versions of
+  Visual Studio that do not support C++11
+
+
+Orthanc as compiled above will not work properly with some Asian
+encodings (unit tests will fail). In international setups, you can
+compile Orthanc together with ICU as follows:
+
+# cmake -DSTANDALONE_BUILD=ON -DSTATIC_BUILD=ON -DALLOW_DOWNLOADS=ON \
+  -DBOOST_LOCALE_BACKEND=icu -DUSE_LEGACY_JSONCPP=ON -DUSE_LEGACY_LIBICU=ON \
+  -G "Visual Studio 9 2008" [...]\Orthanc
+
+
+
+Native Windows build with Microsoft Visual Studio 2015, Ninja and QtCreator
+---------------------------------------------------------------------------
+
+Open a Visual Studio 2015 x64 Command Prompt.
+
+# cd [...]\OrthancBuild
+# cmake -G Ninja -DSTATIC_BUILD=ON [...]\Orthanc
+# ninja
+
+Then, you can open an existing project in QtCreator:
+* Select the CMakeLists.txt in [...]\Orthanc
+* Import build from [...]\OrthancBuild
+
+
+Instructions to include support for Asian encodings:
+
+# cmake -G Ninja -T host=x64 -DSTATIC_BUILD=ON -DBOOST_LOCALE_BACKEND=icu [...]\Orthanc
+
+The option "-T host=x64" is necessary to prevent error "C1060:
+compiler is out of heap space" when compiling Orthanc with ICU.
+
+
+
+Cross-Compilation for Windows under GNU/Linux
+---------------------------------------------
+
+Some versions of MinGW-W64 might have problems with C++11 (notably
+those shipped in Ubuntu 16.04 LTS, in the "mingw-w64" package). Use
+the following command to disable C++11:
+
+# cd ~/OrthancBuild
+# cmake ~/Orthanc \
+        -DCMAKE_BUILD_TYPE=Debug \
+        -DCMAKE_TOOLCHAIN_FILE=~/Orthanc/Resources/MinGW-W64-Toolchain32.cmake \
+        -DSTANDALONE_BUILD=ON \
+        -DSTATIC_BUILD=ON \
+        -DUSE_LEGACY_JSONCPP=ON
+# make
+
+NB: Use the toolchain "MinGW-W64-Toolchain64.cmake" to produce 64bit
+Windows binaries.
+
+
+
+Legacy MinGW32 compilers (notably those shipped in Ubuntu 14.04 LTS,
+in the "mingw32" package) are incompatible with DCMTK 3.6.2 and
+C++11. Use the following command to force using DCMTK 3.6.0 and
+disable C++11:
+
+# cd ~/OrthancBuild
+# cmake ~/Orthanc \
+        -DCMAKE_BUILD_TYPE=Debug \
+        -DCMAKE_TOOLCHAIN_FILE=~/Orthanc/Resources/MinGWToolchain.cmake \
+        -DSTANDALONE_BUILD=ON \
+        -DSTATIC_BUILD=ON \
+        -DDCMTK_STATIC_VERSION=3.6.0 \
+        -DUSE_LEGACY_JSONCPP=ON
+# make
--- a/OrthancExplorer/libs/date.js	Tue May 14 16:38:02 2019 +0200
+++ b/OrthancExplorer/libs/date.js	Tue May 14 16:38:20 2019 +0200
@@ -1,10 +1,10 @@
-/**
- * Version: 1.0 Alpha-1 
- * Build Date: 13-Nov-2007
- * Copyright (c) 2006-2007, Coolite Inc. (http://www.coolite.com/). All rights reserved.
- * License: Licensed under The MIT License. See license.txt and http://www.datejs.com/license/. 
- * Website: http://www.datejs.com/ or http://www.coolite.com/datejs/
- */
+/**
+ * Version: 1.0 Alpha-1 
+ * Build Date: 13-Nov-2007
+ * Copyright (c) 2006-2007, Coolite Inc. (http://www.coolite.com/). All rights reserved.
+ * License: Licensed under The MIT License. See license.txt and http://www.datejs.com/license/. 
+ * Website: http://www.datejs.com/ or http://www.coolite.com/datejs/
+ */
 Date.CultureInfo={name:"en-US",englishName:"English (United States)",nativeName:"English (United States)",dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],abbreviatedDayNames:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],shortestDayNames:["Su","Mo","Tu","We","Th","Fr","Sa"],firstLetterDayNames:["S","M","T","W","T","F","S"],monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"],abbreviatedMonthNames:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],amDesignator:"AM",pmDesignator:"PM",firstDayOfWeek:0,twoDigitYearMax:2029,dateElementOrder:"mdy",formatPatterns:{shortDate:"M/d/yyyy",longDate:"dddd, MMMM dd, yyyy",shortTime:"h:mm tt",longTime:"h:mm:ss tt",fullDateTime:"dddd, MMMM dd, yyyy h:mm:ss tt",sortableDateTime:"yyyy-MM-ddTHH:mm:ss",universalSortableDateTime:"yyyy-MM-dd HH:mm:ssZ",rfc1123:"ddd, dd MMM yyyy HH:mm:ss GMT",monthDay:"MMMM dd",yearMonth:"MMMM, yyyy"},regexPatterns:{jan:/^jan(uary)?/i,feb:/^feb(ruary)?/i,mar:/^mar(ch)?/i,apr:/^apr(il)?/i,may:/^may/i,jun:/^jun(e)?/i,jul:/^jul(y)?/i,aug:/^aug(ust)?/i,sep:/^sep(t(ember)?)?/i,oct:/^oct(ober)?/i,nov:/^nov(ember)?/i,dec:/^dec(ember)?/i,sun:/^su(n(day)?)?/i,mon:/^mo(n(day)?)?/i,tue:/^tu(e(s(day)?)?)?/i,wed:/^we(d(nesday)?)?/i,thu:/^th(u(r(s(day)?)?)?)?/i,fri:/^fr(i(day)?)?/i,sat:/^sa(t(urday)?)?/i,future:/^next/i,past:/^last|past|prev(ious)?/i,add:/^(\+|after|from)/i,subtract:/^(\-|before|ago)/i,yesterday:/^yesterday/i,today:/^t(oday)?/i,tomorrow:/^tomorrow/i,now:/^n(ow)?/i,millisecond:/^ms|milli(second)?s?/i,second:/^sec(ond)?s?/i,minute:/^min(ute)?s?/i,hour:/^h(ou)?rs?/i,week:/^w(ee)?k/i,month:/^m(o(nth)?s?)?/i,day:/^d(ays?)?/i,year:/^y((ea)?rs?)?/i,shortMeridian:/^(a|p)/i,longMeridian:/^(a\.?m?\.?|p\.?m?\.?)/i,timezone:/^((e(s|d)t|c(s|d)t|m(s|d)t|p(s|d)t)|((gmt)?\s*(\+|\-)\s*\d\d\d\d?)|gmt)/i,ordinalSuffix:/^\s*(st|nd|rd|th)/i,timeContext:/^\s*(\:|a|p)/i},abbreviatedTimeZoneStandard:{GMT:"-000",EST:"-0400",CST:"-0500",MST:"-0600",PST:"-0700"},abbreviatedTimeZoneDST:{GMT:"-000",EDT:"-0500",CDT:"-0600",MDT:"-0700",PDT:"-0800"}};
 Date.getMonthNumberFromName=function(name){var n=Date.CultureInfo.monthNames,m=Date.CultureInfo.abbreviatedMonthNames,s=name.toLowerCase();for(var i=0;i<n.length;i++){if(n[i].toLowerCase()==s||m[i].toLowerCase()==s){return i;}}
 return-1;};Date.getDayNumberFromName=function(name){var n=Date.CultureInfo.dayNames,m=Date.CultureInfo.abbreviatedDayNames,o=Date.CultureInfo.shortestDayNames,s=name.toLowerCase();for(var i=0;i<n.length;i++){if(n[i].toLowerCase()==s||m[i].toLowerCase()==s){return i;}}
@@ -101,4 +101,4 @@
 return g._start.call({},s);};}());Date._parse=Date.parse;Date.parse=function(s){var r=null;if(!s){return null;}
 try{r=Date.Grammar.start.call({},s);}catch(e){return null;}
 return((r[1].length===0)?r[0]:null);};Date.getParseFunction=function(fx){var fn=Date.Grammar.formats(fx);return function(s){var r=null;try{r=fn.call({},s);}catch(e){return null;}
-return((r[1].length===0)?r[0]:null);};};Date.parseExact=function(s,fx){return Date.getParseFunction(fx)(s);};
+return((r[1].length===0)?r[0]:null);};};Date.parseExact=function(s,fx){return Date.getParseFunction(fx)(s);};
--- a/OrthancExplorer/query-retrieve.js	Tue May 14 16:38:02 2019 +0200
+++ b/OrthancExplorer/query-retrieve.js	Tue May 14 16:38:20 2019 +0200
@@ -1,337 +1,337 @@
-function JavascriptDateToDicom(date)
-{
-  var s = date.toISOString();
-  return s.substring(0, 4) + s.substring(5, 7) + s.substring(8, 10);
-}
-
-function GenerateDicomDate(days)
-{
-  var today = new Date();
-  var other = new Date(today);
-  other.setDate(today.getDate() + days);
-  return JavascriptDateToDicom(other);
-}
-
-
-$('#query-retrieve').live('pagebeforeshow', function() {
-  var targetDate;
-
-  $.ajax({
-    url: '../modalities',
-    dataType: 'json',
-    async: false,
-    cache: false,
-    success: function(modalities) {
-      var targetServer = $('#qr-server');
-      var option;
-
-      $('option', targetServer).remove();
-
-      for (var i = 0; i < modalities.length; i++) {
-        option = $('<option>').text(modalities[i]);
-        targetServer.append(option);
-      }
-
-      targetServer.selectmenu('refresh');
-    }
-  });
-
-  targetDate = $('#qr-date');
-  $('option', targetDate).remove();
-  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'));
-  targetDate.append($('<option>').attr('value', GenerateDicomDate(-31) + '-').text('Last 31 days'));
-  targetDate.append($('<option>').attr('value', GenerateDicomDate(-31 * 3) + '-').text('Last 3 months'));
-  targetDate.append($('<option>').attr('value', GenerateDicomDate(-365) + '-').text('Last year'));
-  targetDate.selectmenu('refresh');
-});
-
-
-$('#qr-echo').live('click', function() {
-  var server = $('#qr-server').val();
-  var message = 'Error: The C-Echo has failed!';
-
-  $.ajax({
-    url: '../modalities/' + server + '/echo',
-    type: 'POST', 
-    cache: false,
-    async: false,
-    success: function() {
-      message = 'The C-Echo has succeeded!';
-    }
-  });
-
-  $('<div>').simpledialog2({
-    mode: 'button',
-    headerText: 'Echo result',
-    headerClose: true,
-    buttonPrompt: message,
-    animate: false,
-    buttons : {
-      'OK': { click: function () { } }
-    }
-  });
-
-  return false;
-});
-
-
-$('#qr-submit').live('click', function() {
-  var query, server, modalities, field;
-
-  query = {
-    'Level' : 'Study',
-    'Query' : {
-      'AccessionNumber' : '',
-      'PatientBirthDate' : '',
-      'PatientID' : '',
-      'PatientName' : '',
-      'PatientSex' : '',
-      'StudyDate' : $('#qr-date').val(),
-      'StudyDescription' : '*'
-    }
-  };
-
-  modalities = '';
-
-  field = $('#qr-fields input:checked').val();
-  query['Query'][field] = $('#qr-value').val().toUpperCase();
-
-  $('#qr-modalities input:checked').each(function() {
-    var s = $(this).attr('name');
-    if (modalities == '')
-      modalities = s;
-    else
-      modalities += '\\' + s;
-  });
-
-  if (modalities.length > 0) {
-    query['Query']['ModalitiesInStudy'] = modalities;
-  }
-
-
-  server = $('#qr-server').val();
-  $.ajax({
-    url: '../modalities/' + server + '/query',
-    type: 'POST', 
-    data: JSON.stringify(query),
-    dataType: 'json',
-    async: false,
-    error: function() {
-      alert('Error during query (C-Find)');
-    },
-    success: function(result) {
-      ChangePage('query-retrieve-2', {
-        'server' : server,
-        'uuid' : result['ID']
-      });
-    }
-  });
-
-  return false;
-});
-
-
-
-$('#query-retrieve-2').live('pagebeforeshow', function() {
-  var pageData, uri;
-  
-  if ($.mobile.pageData) {
-    pageData = DeepCopy($.mobile.pageData);
-
-    uri = '../queries/' + pageData.uuid + '/answers';
-
-    $.ajax({
-      url: uri,
-      dataType: 'json',
-      async: false,
-      success: function(answers) {
-        var target = $('#query-retrieve-2 ul');
-        $('li', target).remove();
-
-        for (var i = 0; i < answers.length; i++) {
-          $.ajax({
-            url: uri + '/' + answers[i] + '/content?simplify',
-            dataType: 'json',
-            async: false,
-            success: function(study) {
-              var series = '#query-retrieve-3?server=' + pageData.server + '&uuid=' + study['StudyInstanceUID'];
-
-              var content = ($('<div>')
-                             .append($('<h3>').text(study['PatientID'] + ' - ' + study['PatientName']))
-                             .append($('<p>').text('Accession number: ')
-                                     .append($('<b>').text(study['AccessionNumber'])))
-                             .append($('<p>').text('Birth date: ')
-                                     .append($('<b>').text(study['PatientBirthDate'])))
-                             .append($('<p>').text('Patient sex: ')
-                                     .append($('<b>').text(study['PatientSex'])))
-                             .append($('<p>').text('Study description: ')
-                                     .append($('<b>').text(study['StudyDescription'])))
-                             .append($('<p>').text('Study date: ')
-                                     .append($('<b>').text(FormatDicomDate(study['StudyDate'])))));
-
-              var info = $('<a>').attr('href', series).html(content);
-              
-              var answerId = answers[i];
-              var retrieve = $('<a>').text('Retrieve all study').click(function() {
-                ChangePage('query-retrieve-4', {
-                  'query' : pageData.uuid,
-                  'answer' : answerId,
-                  'server' : pageData.server
-                });
-              });
-
-              target.append($('<li>').append(info).append(retrieve));
-            }
-          });
-        }
-
-        target.listview('refresh');
-      }
-    });
-  }
-});
-
-
-$('#query-retrieve-3').live('pagebeforeshow', function() {
-  var pageData, query;
-
-  if ($.mobile.pageData) {
-    pageData = DeepCopy($.mobile.pageData);
-
-    query = {
-      'Level' : 'Series',
-      'Query' : {
-        'Modality' : '*',
-        'ProtocolName' : '*',
-        'SeriesDescription' : '*',
-        'SeriesInstanceUID' : '*',
-        'StudyInstanceUID' : pageData.uuid
-      }
-    };
-
-    $.ajax({
-      url: '../modalities/' + pageData.server + '/query',
-      type: 'POST', 
-      data: JSON.stringify(query),
-      dataType: 'json',
-      async: false,
-      error: function() {
-        alert('Error during query (C-Find)');
-      },
-      success: function(answer) {
-        var queryUuid = answer['ID'];
-        var uri = '../queries/' + answer['ID'] + '/answers';
-
-        $.ajax({
-          url: uri,
-          dataType: 'json',
-          async: false,
-          success: function(answers) {
-            
-            var target = $('#query-retrieve-3 ul');
-            $('li', target).remove();
-
-            for (var i = 0; i < answers.length; i++) {
-              $.ajax({
-                url: uri + '/' + answers[i] + '/content?simplify',
-                dataType: 'json',
-                async: false,
-                success: function(series) {
-                  var content = ($('<div>')
-                                 .append($('<h3>').text(series['SeriesDescription']))
-                                 .append($('<p>').text('Modality: ')
-                                         .append($('<b>').text(series['Modality'])))
-                                 .append($('<p>').text('ProtocolName: ')
-                                         .append($('<b>').text(series['ProtocolName']))));
-
-                  var info = $('<a>').html(content);
-
-                  var answerId = answers[i];
-                  info.click(function() {
-                    ChangePage('query-retrieve-4', {
-                      'query' : queryUuid,
-                      'study' : pageData.uuid,
-                      'answer' : answerId,
-                      'server' : pageData.server
-                    });
-                  });
-
-                  target.append($('<li>').attr('data-icon', 'arrow-d').append(info));
-                }
-              });
-            }
-
-            target.listview('refresh');
-          }
-        });
-      }
-    });
-  }
-});
-
-
-
-$('#query-retrieve-4').live('pagebeforeshow', function() {
-  var pageData, uri;
-  
-  if ($.mobile.pageData) {
-    var pageData = DeepCopy($.mobile.pageData);
-    var uri = '../queries/' + pageData.query + '/answers/' + pageData.answer + '/retrieve';
-
-    $.ajax({
-      url: '../system',
-      dataType: 'json',
-      async: false,
-      cache: false,
-      success: function(system) {
-        $('#retrieve-target').val(system['DicomAet']);
-
-        $('#retrieve-form').submit(function(event) {
-          var aet;
-
-          event.preventDefault();
-
-          aet = $('#retrieve-target').val();
-          if (aet.length == 0) {
-            aet = system['DicomAet'];
-          }
-
-          $.ajax({
-            url: uri,
-            type: 'POST',
-            async: true,  // Necessary to block UI
-            dataType: 'text',
-            data: aet,
-            beforeSend: function() {
-              $.blockUI({ message: $('#info-retrieve') });
-            },
-            complete: function(s) {
-              $.unblockUI();
-            },
-            success: function() {
-              if (pageData.study) {
-                // Go back to the list of series
-                ChangePage('query-retrieve-3', {
-                  'server' : pageData.server,
-                  'uuid' : pageData.study
-                });
-              } else {
-                // Go back to the list of studies
-                ChangePage('query-retrieve-2', {
-                  'server' : pageData.server,
-                  'uuid' : pageData.query
-                });
-              }
-            },
-            error: function() {
-              alert('Error during retrieve');
-            }
-          });
-        });
-      }
-    });
-  }
-});
+function JavascriptDateToDicom(date)
+{
+  var s = date.toISOString();
+  return s.substring(0, 4) + s.substring(5, 7) + s.substring(8, 10);
+}
+
+function GenerateDicomDate(days)
+{
+  var today = new Date();
+  var other = new Date(today);
+  other.setDate(today.getDate() + days);
+  return JavascriptDateToDicom(other);
+}
+
+
+$('#query-retrieve').live('pagebeforeshow', function() {
+  var targetDate;
+
+  $.ajax({
+    url: '../modalities',
+    dataType: 'json',
+    async: false,
+    cache: false,
+    success: function(modalities) {
+      var targetServer = $('#qr-server');
+      var option;
+
+      $('option', targetServer).remove();
+
+      for (var i = 0; i < modalities.length; i++) {
+        option = $('<option>').text(modalities[i]);
+        targetServer.append(option);
+      }
+
+      targetServer.selectmenu('refresh');
+    }
+  });
+
+  targetDate = $('#qr-date');
+  $('option', targetDate).remove();
+  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'));
+  targetDate.append($('<option>').attr('value', GenerateDicomDate(-31) + '-').text('Last 31 days'));
+  targetDate.append($('<option>').attr('value', GenerateDicomDate(-31 * 3) + '-').text('Last 3 months'));
+  targetDate.append($('<option>').attr('value', GenerateDicomDate(-365) + '-').text('Last year'));
+  targetDate.selectmenu('refresh');
+});
+
+
+$('#qr-echo').live('click', function() {
+  var server = $('#qr-server').val();
+  var message = 'Error: The C-Echo has failed!';
+
+  $.ajax({
+    url: '../modalities/' + server + '/echo',
+    type: 'POST', 
+    cache: false,
+    async: false,
+    success: function() {
+      message = 'The C-Echo has succeeded!';
+    }
+  });
+
+  $('<div>').simpledialog2({
+    mode: 'button',
+    headerText: 'Echo result',
+    headerClose: true,
+    buttonPrompt: message,
+    animate: false,
+    buttons : {
+      'OK': { click: function () { } }
+    }
+  });
+
+  return false;
+});
+
+
+$('#qr-submit').live('click', function() {
+  var query, server, modalities, field;
+
+  query = {
+    'Level' : 'Study',
+    'Query' : {
+      'AccessionNumber' : '',
+      'PatientBirthDate' : '',
+      'PatientID' : '',
+      'PatientName' : '',
+      'PatientSex' : '',
+      'StudyDate' : $('#qr-date').val(),
+      'StudyDescription' : '*'
+    }
+  };
+
+  modalities = '';
+
+  field = $('#qr-fields input:checked').val();
+  query['Query'][field] = $('#qr-value').val().toUpperCase();
+
+  $('#qr-modalities input:checked').each(function() {
+    var s = $(this).attr('name');
+    if (modalities == '')
+      modalities = s;
+    else
+      modalities += '\\' + s;
+  });
+
+  if (modalities.length > 0) {
+    query['Query']['ModalitiesInStudy'] = modalities;
+  }
+
+
+  server = $('#qr-server').val();
+  $.ajax({
+    url: '../modalities/' + server + '/query',
+    type: 'POST', 
+    data: JSON.stringify(query),
+    dataType: 'json',
+    async: false,
+    error: function() {
+      alert('Error during query (C-Find)');
+    },
+    success: function(result) {
+      ChangePage('query-retrieve-2', {
+        'server' : server,
+        'uuid' : result['ID']
+      });
+    }
+  });
+
+  return false;
+});
+
+
+
+$('#query-retrieve-2').live('pagebeforeshow', function() {
+  var pageData, uri;
+  
+  if ($.mobile.pageData) {
+    pageData = DeepCopy($.mobile.pageData);
+
+    uri = '../queries/' + pageData.uuid + '/answers';
+
+    $.ajax({
+      url: uri,
+      dataType: 'json',
+      async: false,
+      success: function(answers) {
+        var target = $('#query-retrieve-2 ul');
+        $('li', target).remove();
+
+        for (var i = 0; i < answers.length; i++) {
+          $.ajax({
+            url: uri + '/' + answers[i] + '/content?simplify',
+            dataType: 'json',
+            async: false,
+            success: function(study) {
+              var series = '#query-retrieve-3?server=' + pageData.server + '&uuid=' + study['StudyInstanceUID'];
+
+              var content = ($('<div>')
+                             .append($('<h3>').text(study['PatientID'] + ' - ' + study['PatientName']))
+                             .append($('<p>').text('Accession number: ')
+                                     .append($('<b>').text(study['AccessionNumber'])))
+                             .append($('<p>').text('Birth date: ')
+                                     .append($('<b>').text(study['PatientBirthDate'])))
+                             .append($('<p>').text('Patient sex: ')
+                                     .append($('<b>').text(study['PatientSex'])))
+                             .append($('<p>').text('Study description: ')
+                                     .append($('<b>').text(study['StudyDescription'])))
+                             .append($('<p>').text('Study date: ')
+                                     .append($('<b>').text(FormatDicomDate(study['StudyDate'])))));
+
+              var info = $('<a>').attr('href', series).html(content);
+              
+              var answerId = answers[i];
+              var retrieve = $('<a>').text('Retrieve all study').click(function() {
+                ChangePage('query-retrieve-4', {
+                  'query' : pageData.uuid,
+                  'answer' : answerId,
+                  'server' : pageData.server
+                });
+              });
+
+              target.append($('<li>').append(info).append(retrieve));
+            }
+          });
+        }
+
+        target.listview('refresh');
+      }
+    });
+  }
+});
+
+
+$('#query-retrieve-3').live('pagebeforeshow', function() {
+  var pageData, query;
+
+  if ($.mobile.pageData) {
+    pageData = DeepCopy($.mobile.pageData);
+
+    query = {
+      'Level' : 'Series',
+      'Query' : {
+        'Modality' : '*',
+        'ProtocolName' : '*',
+        'SeriesDescription' : '*',
+        'SeriesInstanceUID' : '*',
+        'StudyInstanceUID' : pageData.uuid
+      }
+    };
+
+    $.ajax({
+      url: '../modalities/' + pageData.server + '/query',
+      type: 'POST', 
+      data: JSON.stringify(query),
+      dataType: 'json',
+      async: false,
+      error: function() {
+        alert('Error during query (C-Find)');
+      },
+      success: function(answer) {
+        var queryUuid = answer['ID'];
+        var uri = '../queries/' + answer['ID'] + '/answers';
+
+        $.ajax({
+          url: uri,
+          dataType: 'json',
+          async: false,
+          success: function(answers) {
+            
+            var target = $('#query-retrieve-3 ul');
+            $('li', target).remove();
+
+            for (var i = 0; i < answers.length; i++) {
+              $.ajax({
+                url: uri + '/' + answers[i] + '/content?simplify',
+                dataType: 'json',
+                async: false,
+                success: function(series) {
+                  var content = ($('<div>')
+                                 .append($('<h3>').text(series['SeriesDescription']))
+                                 .append($('<p>').text('Modality: ')
+                                         .append($('<b>').text(series['Modality'])))
+                                 .append($('<p>').text('ProtocolName: ')
+                                         .append($('<b>').text(series['ProtocolName']))));
+
+                  var info = $('<a>').html(content);
+
+                  var answerId = answers[i];
+                  info.click(function() {
+                    ChangePage('query-retrieve-4', {
+                      'query' : queryUuid,
+                      'study' : pageData.uuid,
+                      'answer' : answerId,
+                      'server' : pageData.server
+                    });
+                  });
+
+                  target.append($('<li>').attr('data-icon', 'arrow-d').append(info));
+                }
+              });
+            }
+
+            target.listview('refresh');
+          }
+        });
+      }
+    });
+  }
+});
+
+
+
+$('#query-retrieve-4').live('pagebeforeshow', function() {
+  var pageData, uri;
+  
+  if ($.mobile.pageData) {
+    var pageData = DeepCopy($.mobile.pageData);
+    var uri = '../queries/' + pageData.query + '/answers/' + pageData.answer + '/retrieve';
+
+    $.ajax({
+      url: '../system',
+      dataType: 'json',
+      async: false,
+      cache: false,
+      success: function(system) {
+        $('#retrieve-target').val(system['DicomAet']);
+
+        $('#retrieve-form').submit(function(event) {
+          var aet;
+
+          event.preventDefault();
+
+          aet = $('#retrieve-target').val();
+          if (aet.length == 0) {
+            aet = system['DicomAet'];
+          }
+
+          $.ajax({
+            url: uri,
+            type: 'POST',
+            async: true,  // Necessary to block UI
+            dataType: 'text',
+            data: aet,
+            beforeSend: function() {
+              $.blockUI({ message: $('#info-retrieve') });
+            },
+            complete: function(s) {
+              $.unblockUI();
+            },
+            success: function() {
+              if (pageData.study) {
+                // Go back to the list of series
+                ChangePage('query-retrieve-3', {
+                  'server' : pageData.server,
+                  'uuid' : pageData.study
+                });
+              } else {
+                // Go back to the list of studies
+                ChangePage('query-retrieve-2', {
+                  'server' : pageData.server,
+                  'uuid' : pageData.query
+                });
+              }
+            },
+            error: function() {
+              alert('Error during retrieve');
+            }
+          });
+        });
+      }
+    });
+  }
+});
--- a/Plugins/Samples/Common/OrthancPlugins.cmake	Tue May 14 16:38:02 2019 +0200
+++ b/Plugins/Samples/Common/OrthancPlugins.cmake	Tue May 14 16:38:20 2019 +0200
@@ -1,32 +1,32 @@
-set(ORTHANC_ROOT ${SAMPLES_ROOT}/../..)
-include(CheckIncludeFiles)
-include(CheckIncludeFileCXX)
-include(CheckLibraryExists)
-include(FindPythonInterp)
-include(${ORTHANC_ROOT}/Resources/CMake/AutoGeneratedCode.cmake)
-include(${ORTHANC_ROOT}/Resources/CMake/DownloadPackage.cmake)
-include(${ORTHANC_ROOT}/Resources/CMake/Compiler.cmake)
-
-
-if (CMAKE_COMPILER_IS_GNUCXX)
-  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -pedantic")
-  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -pedantic")
-endif()
-
-
-if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
-  # Linking with "pthread" is necessary, otherwise the software crashes
-  # http://sourceware.org/bugzilla/show_bug.cgi?id=10652#c17
-  link_libraries(dl rt pthread)
-endif()
-
-
-include_directories(${SAMPLES_ROOT}/../Include/)
-
-
-if (MSVC)
-  include_directories(${SAMPLES_ROOT}/../../Resources/ThirdParty/VisualStudio/)
-endif()
-
-
-add_definitions(-DHAS_ORTHANC_EXCEPTION=0)
+set(ORTHANC_ROOT ${SAMPLES_ROOT}/../..)
+include(CheckIncludeFiles)
+include(CheckIncludeFileCXX)
+include(CheckLibraryExists)
+include(FindPythonInterp)
+include(${ORTHANC_ROOT}/Resources/CMake/AutoGeneratedCode.cmake)
+include(${ORTHANC_ROOT}/Resources/CMake/DownloadPackage.cmake)
+include(${ORTHANC_ROOT}/Resources/CMake/Compiler.cmake)
+
+
+if (CMAKE_COMPILER_IS_GNUCXX)
+  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -pedantic")
+  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -pedantic")
+endif()
+
+
+if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
+  # Linking with "pthread" is necessary, otherwise the software crashes
+  # http://sourceware.org/bugzilla/show_bug.cgi?id=10652#c17
+  link_libraries(dl rt pthread)
+endif()
+
+
+include_directories(${SAMPLES_ROOT}/../Include/)
+
+
+if (MSVC)
+  include_directories(${SAMPLES_ROOT}/../../Resources/ThirdParty/VisualStudio/)
+endif()
+
+
+add_definitions(-DHAS_ORTHANC_EXCEPTION=0)
--- a/Resources/Patches/mongoose-3.1-patch.diff	Tue May 14 16:38:02 2019 +0200
+++ b/Resources/Patches/mongoose-3.1-patch.diff	Tue May 14 16:38:20 2019 +0200
@@ -1,54 +1,54 @@
 --- /home/jodogne/Subversion/Orthanc/ThirdPartyDownloads/mongoose/mongoose.c	2012-03-11 23:41:35.000000000 +0100
 +++ mongoose.c	2013-03-07 10:07:00.566266153 +0100
 @@ -92,8 +92,9 @@
- #define strtoll(x, y, z) strtol(x, y, z)
- #else
- #define __func__  __FUNCTION__
--#define strtoull(x, y, z) _strtoui64(x, y, z)
--#define strtoll(x, y, z) _strtoi64(x, y, z)
-+#include <stdlib.h>
-+//#define strtoull(x, y, z) _strtoui64(x, y, z)
-+//#define strtoll(x, y, z) _strtoi64(x, y, z)
- #endif // _MSC_VER
- 
- #define ERRNO   GetLastError()
+ #define strtoll(x, y, z) strtol(x, y, z)
+ #else
+ #define __func__  __FUNCTION__
+-#define strtoull(x, y, z) _strtoui64(x, y, z)
+-#define strtoll(x, y, z) _strtoi64(x, y, z)
++#include <stdlib.h>
++//#define strtoull(x, y, z) _strtoui64(x, y, z)
++//#define strtoll(x, y, z) _strtoi64(x, y, z)
+ #endif // _MSC_VER
+ 
+ #define ERRNO   GetLastError()
 @@ -253,6 +254,14 @@
- #define MSG_NOSIGNAL 0
- #endif
- 
-+#if __gnu_hurd__ == 1
-+/**
-+ * There is no limit on the length on a path under GNU Hurd, so we set
-+ * it to an arbitrary constant.
-+ **/
-+#define PATH_MAX 4096
-+#endif
-+
- typedef void * (*mg_thread_func_t)(void *);
- 
- static const char *http_500_error = "Internal Server Error";
+ #define MSG_NOSIGNAL 0
+ #endif
+ 
++#if __gnu_hurd__ == 1
++/**
++ * There is no limit on the length on a path under GNU Hurd, so we set
++ * it to an arbitrary constant.
++ **/
++#define PATH_MAX 4096
++#endif
++
+ typedef void * (*mg_thread_func_t)(void *);
+ 
+ static const char *http_500_error = "Internal Server Error";
 @@ -3844,10 +3853,8 @@
- }
- 
- static void discard_current_request_from_buffer(struct mg_connection *conn) {
--  char *buffered;
-   int buffered_len, body_len;
- 
--  buffered = conn->buf + conn->request_len;
-   buffered_len = conn->data_len - conn->request_len;
-   assert(buffered_len >= 0);
- 
+ }
+ 
+ static void discard_current_request_from_buffer(struct mg_connection *conn) {
+-  char *buffered;
+   int buffered_len, body_len;
+ 
+-  buffered = conn->buf + conn->request_len;
+   buffered_len = conn->data_len - conn->request_len;
+   assert(buffered_len >= 0);
+ 
 @@ -4148,7 +4155,13 @@
- 
-   // Wait until mg_fini() stops
-   while (ctx->stop_flag != 2) {
-+#if defined(__linux__)
-+    usleep(100000);
-+#elif defined(_WIN32)
-+    Sleep(100);
-+#else
-     (void) sleep(0);
-+#endif
-   }
-   free_context(ctx);
- 
+ 
+   // Wait until mg_fini() stops
+   while (ctx->stop_flag != 2) {
++#if defined(__linux__)
++    usleep(100000);
++#elif defined(_WIN32)
++    Sleep(100);
++#else
+     (void) sleep(0);
++#endif
+   }
+   free_context(ctx);
+ 
--- a/Resources/Patches/mongoose-3.8-patch.diff	Tue May 14 16:38:02 2019 +0200
+++ b/Resources/Patches/mongoose-3.8-patch.diff	Tue May 14 16:38:20 2019 +0200
@@ -1,106 +1,106 @@
 --- mongoose.c.orig	2019-01-14 13:06:27.147098524 +0100
 +++ mongoose.c	2019-01-14 12:44:35.331361929 +0100
 @@ -50,6 +50,14 @@
- #define PATH_MAX FILENAME_MAX
- #endif // __SYMBIAN32__
- 
-+#if __gnu_hurd__ == 1
-+/**
-+ * There is no limit on the length on a path under GNU Hurd, so we set
-+ * it to an arbitrary constant.
-+ **/
-+#define PATH_MAX 4096
-+#endif
-+
- #ifndef _WIN32_WCE // Some ANSI #includes are not available on Windows CE
- #include <sys/types.h>
- #include <sys/stat.h>
+ #define PATH_MAX FILENAME_MAX
+ #endif // __SYMBIAN32__
+ 
++#if __gnu_hurd__ == 1
++/**
++ * There is no limit on the length on a path under GNU Hurd, so we set
++ * it to an arbitrary constant.
++ **/
++#define PATH_MAX 4096
++#endif
++
+ #ifndef _WIN32_WCE // Some ANSI #includes are not available on Windows CE
+ #include <sys/types.h>
+ #include <sys/stat.h>
 @@ -108,8 +116,9 @@
- #define strtoll(x, y, z) _atoi64(x)
- #else
- #define __func__  __FUNCTION__
--#define strtoull(x, y, z) _strtoui64(x, y, z)
--#define strtoll(x, y, z) _strtoi64(x, y, z)
-+#include <stdlib.h>
-+//#define strtoull(x, y, z) _strtoui64(x, y, z)
-+//#define strtoll(x, y, z) _strtoi64(x, y, z)
- #endif // _MSC_VER
- 
- #define ERRNO   GetLastError()
+ #define strtoll(x, y, z) _atoi64(x)
+ #else
+ #define __func__  __FUNCTION__
+-#define strtoull(x, y, z) _strtoui64(x, y, z)
+-#define strtoll(x, y, z) _strtoi64(x, y, z)
++#include <stdlib.h>
++//#define strtoull(x, y, z) _strtoui64(x, y, z)
++//#define strtoll(x, y, z) _strtoi64(x, y, z)
+ #endif // _MSC_VER
+ 
+ #define ERRNO   GetLastError()
 @@ -2997,19 +3006,19 @@
-   }
- }
- 
--static int is_valid_http_method(const char *method) {
--  return !strcmp(method, "GET") || !strcmp(method, "POST") ||
-+static int is_valid_http_method(const char *method, int *isValidHttpMethod) {
-+  *isValidHttpMethod = !strcmp(method, "GET") || !strcmp(method, "POST") ||
-     !strcmp(method, "HEAD") || !strcmp(method, "CONNECT") ||
-     !strcmp(method, "PUT") || !strcmp(method, "DELETE") ||
-     !strcmp(method, "OPTIONS") || !strcmp(method, "PROPFIND")
--    || !strcmp(method, "MKCOL")
--          ;
-+    || !strcmp(method, "MKCOL");
-+  return *isValidHttpMethod;
- }
- 
- // Parse HTTP request, fill in mg_request_info structure.
- // This function modifies the buffer by NUL-terminating
- // HTTP request components, header names and header values.
--static int parse_http_message(char *buf, int len, struct mg_request_info *ri) {
-+static int parse_http_message(char *buf, int len, struct mg_request_info *ri, int *isValidHttpMethod) {
-   int is_request, request_length = get_request_len(buf, len);
-   if (request_length > 0) {
-     // Reset attributes. DO NOT TOUCH is_ssl, remote_ip, remote_port
+   }
+ }
+ 
+-static int is_valid_http_method(const char *method) {
+-  return !strcmp(method, "GET") || !strcmp(method, "POST") ||
++static int is_valid_http_method(const char *method, int *isValidHttpMethod) {
++  *isValidHttpMethod = !strcmp(method, "GET") || !strcmp(method, "POST") ||
+     !strcmp(method, "HEAD") || !strcmp(method, "CONNECT") ||
+     !strcmp(method, "PUT") || !strcmp(method, "DELETE") ||
+     !strcmp(method, "OPTIONS") || !strcmp(method, "PROPFIND")
+-    || !strcmp(method, "MKCOL")
+-          ;
++    || !strcmp(method, "MKCOL");
++  return *isValidHttpMethod;
+ }
+ 
+ // Parse HTTP request, fill in mg_request_info structure.
+ // This function modifies the buffer by NUL-terminating
+ // HTTP request components, header names and header values.
+-static int parse_http_message(char *buf, int len, struct mg_request_info *ri) {
++static int parse_http_message(char *buf, int len, struct mg_request_info *ri, int *isValidHttpMethod) {
+   int is_request, request_length = get_request_len(buf, len);
+   if (request_length > 0) {
+     // Reset attributes. DO NOT TOUCH is_ssl, remote_ip, remote_port
 @@ -3025,7 +3034,7 @@
-     ri->request_method = skip(&buf, " ");
-     ri->uri = skip(&buf, " ");
-     ri->http_version = skip(&buf, "\r\n");
--    if (((is_request = is_valid_http_method(ri->request_method)) &&
-+    if (((is_request = is_valid_http_method(ri->request_method, isValidHttpMethod)) &&
-          memcmp(ri->http_version, "HTTP/", 5) != 0) ||
-         (!is_request && memcmp(ri->request_method, "HTTP/", 5)) != 0) {
-       request_length = -1;
+     ri->request_method = skip(&buf, " ");
+     ri->uri = skip(&buf, " ");
+     ri->http_version = skip(&buf, "\r\n");
+-    if (((is_request = is_valid_http_method(ri->request_method)) &&
++    if (((is_request = is_valid_http_method(ri->request_method, isValidHttpMethod)) &&
+          memcmp(ri->http_version, "HTTP/", 5) != 0) ||
+         (!is_request && memcmp(ri->request_method, "HTTP/", 5)) != 0) {
+       request_length = -1;
 @@ -4930,7 +4939,7 @@
-   return uri[0] == '/' || (uri[0] == '*' && uri[1] == '\0');
- }
- 
--static int getreq(struct mg_connection *conn, char *ebuf, size_t ebuf_len) {
-+static int getreq(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *isValidHttpMethod) {
-   const char *cl;
- 
-   ebuf[0] = '\0';
+   return uri[0] == '/' || (uri[0] == '*' && uri[1] == '\0');
+ }
+ 
+-static int getreq(struct mg_connection *conn, char *ebuf, size_t ebuf_len) {
++static int getreq(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *isValidHttpMethod) {
+   const char *cl;
+ 
+   ebuf[0] = '\0';
 @@ -4944,7 +4953,7 @@
-   } else if (conn->request_len <= 0) {
-     snprintf(ebuf, ebuf_len, "%s", "Client closed connection");
-   } else if (parse_http_message(conn->buf, conn->buf_size,
--                                &conn->request_info) <= 0) {
-+                                &conn->request_info, isValidHttpMethod) <= 0) {
-     snprintf(ebuf, ebuf_len, "Bad request: [%.*s]", conn->data_len, conn->buf);
-   } else {
-     // Request is valid
+   } else if (conn->request_len <= 0) {
+     snprintf(ebuf, ebuf_len, "%s", "Client closed connection");
+   } else if (parse_http_message(conn->buf, conn->buf_size,
+-                                &conn->request_info) <= 0) {
++                                &conn->request_info, isValidHttpMethod) <= 0) {
+     snprintf(ebuf, ebuf_len, "Bad request: [%.*s]", conn->data_len, conn->buf);
+   } else {
+     // Request is valid
 @@ -4973,7 +4982,8 @@
-   } else if (mg_vprintf(conn, fmt, ap) <= 0) {
-     snprintf(ebuf, ebuf_len, "%s", "Error sending request");
-   } else {
--    getreq(conn, ebuf, ebuf_len);
-+    int isValidHttpMethod = 1; /* unused in this case */
-+    getreq(conn, ebuf, ebuf_len, &isValidHttpMethod);
-   }
-   if (ebuf[0] != '\0' && conn != NULL) {
-     mg_close_connection(conn);
+   } else if (mg_vprintf(conn, fmt, ap) <= 0) {
+     snprintf(ebuf, ebuf_len, "%s", "Error sending request");
+   } else {
+-    getreq(conn, ebuf, ebuf_len);
++    int isValidHttpMethod = 1; /* unused in this case */
++    getreq(conn, ebuf, ebuf_len, &isValidHttpMethod);
+   }
+   if (ebuf[0] != '\0' && conn != NULL) {
+     mg_close_connection(conn);
 @@ -4995,8 +5005,13 @@
-   // to crule42.
-   conn->data_len = 0;
-   do {
--    if (!getreq(conn, ebuf, sizeof(ebuf))) {
-+    int isValidHttpMethod = 1;
-+    if (!getreq(conn, ebuf, sizeof(ebuf), &isValidHttpMethod)) {
-+      if (isValidHttpMethod) {
-       send_http_error(conn, 500, "Server Error", "%s", ebuf);
-+      } else {
-+        send_http_error(conn, 400, "Bad Request", "%s", ebuf);
-+      }
-       conn->must_close = 1;
-     } else if (!is_valid_uri(conn->request_info.uri)) {
-       snprintf(ebuf, sizeof(ebuf), "Invalid URI: [%s]", ri->uri);
+   // to crule42.
+   conn->data_len = 0;
+   do {
+-    if (!getreq(conn, ebuf, sizeof(ebuf))) {
++    int isValidHttpMethod = 1;
++    if (!getreq(conn, ebuf, sizeof(ebuf), &isValidHttpMethod)) {
++      if (isValidHttpMethod) {
+       send_http_error(conn, 500, "Server Error", "%s", ebuf);
++      } else {
++        send_http_error(conn, 400, "Bad Request", "%s", ebuf);
++      }
+       conn->must_close = 1;
+     } else if (!is_valid_uri(conn->request_info.uri)) {
+       snprintf(ebuf, sizeof(ebuf), "Invalid URI: [%s]", ri->uri);
--- a/UnitTestsSources/ToolboxTests.cpp	Tue May 14 16:38:02 2019 +0200
+++ b/UnitTestsSources/ToolboxTests.cpp	Tue May 14 16:38:20 2019 +0200
@@ -1,136 +1,136 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "PrecompiledHeadersUnitTests.h"
-#include "gtest/gtest.h"
-#include "../Core/OrthancException.h"
-#include "../Core/Toolbox.h"
-
-using namespace Orthanc;
-
-TEST(Toolbox, Base64_allByteValues)
-{
-  std::string toEncode;
-  std::string base64Result;
-  std::string decodedResult;
-
-  size_t size = 2*256;
-  toEncode.reserve(size);
-  for (size_t i = 0; i < size; i++)
-    toEncode.push_back(i % 256);
-
-  Toolbox::EncodeBase64(base64Result, toEncode);
-  Toolbox::DecodeBase64(decodedResult, base64Result);
-
-  ASSERT_EQ(toEncode, decodedResult);
-}
-
-TEST(Toolbox, Base64_multipleSizes)
-{
-  std::string toEncode;
-  std::string base64Result;
-  std::string decodedResult;
-
-  for (size_t size = 0; size <= 5; size++)
-  {
-    printf("base64, testing size %zu\n", size);
-    toEncode.clear();
-    toEncode.reserve(size);
-    for (size_t i = 0; i < size; i++)
-      toEncode.push_back(i % 256);
-
-    Toolbox::EncodeBase64(base64Result, toEncode);
-    Toolbox::DecodeBase64(decodedResult, base64Result);
-
-    ASSERT_EQ(toEncode, decodedResult);
-  }
-}
-
-static std::string EncodeBase64Bis(const std::string& s)
-{
-  std::string result;
-  Toolbox::EncodeBase64(result, s);
-  return result;
-}
-
-
-TEST(Toolbox, Base64)
-{
-  ASSERT_EQ("", EncodeBase64Bis(""));
-  ASSERT_EQ("YQ==", EncodeBase64Bis("a"));
-
-  const std::string hello = "SGVsbG8gd29ybGQ=";
-  ASSERT_EQ(hello, EncodeBase64Bis("Hello world"));
-
-  std::string decoded;
-  Toolbox::DecodeBase64(decoded, hello);
-  ASSERT_EQ("Hello world", decoded);
-
-  // Invalid character
-  ASSERT_THROW(Toolbox::DecodeBase64(decoded, "?"), OrthancException);
-
-  // All the allowed characters
-  Toolbox::DecodeBase64(decoded, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=");
-}
-
-
-#if 0 // enable only when compiling in Release with a C++ 11 compiler
-#include <chrono> // I had troubles to link with boost::chrono ...
-
-TEST(Toolbox, Base64_largeString)
-{
-  std::string toEncode;
-  std::string base64Result;
-  std::string decodedResult;
-
-  size_t size = 10 * 1024 * 1024;
-  toEncode.reserve(size);
-  for (size_t i = 0; i < size; i++)
-    toEncode.push_back(i % 256);
-
-  std::chrono::high_resolution_clock::time_point start;
-  std::chrono::high_resolution_clock::time_point afterEncoding;
-  std::chrono::high_resolution_clock::time_point afterDecoding;
-
-  start = std::chrono::high_resolution_clock::now();
-  Orthanc::Toolbox::EncodeBase64(base64Result, toEncode);
-  afterEncoding = std::chrono::high_resolution_clock::now();
-  Orthanc::Toolbox::DecodeBase64(decodedResult, base64Result);
-  afterDecoding = std::chrono::high_resolution_clock::now();
-
-  ASSERT_EQ(toEncode, decodedResult);
-
-  printf("encoding took %zu ms\n", (std::chrono::duration_cast<std::chrono::milliseconds>(afterEncoding - start)));
-  printf("decoding took %zu ms\n", (std::chrono::duration_cast<std::chrono::milliseconds>(afterDecoding - afterEncoding)));
-}
-#endif
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "PrecompiledHeadersUnitTests.h"
+#include "gtest/gtest.h"
+#include "../Core/OrthancException.h"
+#include "../Core/Toolbox.h"
+
+using namespace Orthanc;
+
+TEST(Toolbox, Base64_allByteValues)
+{
+  std::string toEncode;
+  std::string base64Result;
+  std::string decodedResult;
+
+  size_t size = 2*256;
+  toEncode.reserve(size);
+  for (size_t i = 0; i < size; i++)
+    toEncode.push_back(i % 256);
+
+  Toolbox::EncodeBase64(base64Result, toEncode);
+  Toolbox::DecodeBase64(decodedResult, base64Result);
+
+  ASSERT_EQ(toEncode, decodedResult);
+}
+
+TEST(Toolbox, Base64_multipleSizes)
+{
+  std::string toEncode;
+  std::string base64Result;
+  std::string decodedResult;
+
+  for (size_t size = 0; size <= 5; size++)
+  {
+    printf("base64, testing size %zu\n", size);
+    toEncode.clear();
+    toEncode.reserve(size);
+    for (size_t i = 0; i < size; i++)
+      toEncode.push_back(i % 256);
+
+    Toolbox::EncodeBase64(base64Result, toEncode);
+    Toolbox::DecodeBase64(decodedResult, base64Result);
+
+    ASSERT_EQ(toEncode, decodedResult);
+  }
+}
+
+static std::string EncodeBase64Bis(const std::string& s)
+{
+  std::string result;
+  Toolbox::EncodeBase64(result, s);
+  return result;
+}
+
+
+TEST(Toolbox, Base64)
+{
+  ASSERT_EQ("", EncodeBase64Bis(""));
+  ASSERT_EQ("YQ==", EncodeBase64Bis("a"));
+
+  const std::string hello = "SGVsbG8gd29ybGQ=";
+  ASSERT_EQ(hello, EncodeBase64Bis("Hello world"));
+
+  std::string decoded;
+  Toolbox::DecodeBase64(decoded, hello);
+  ASSERT_EQ("Hello world", decoded);
+
+  // Invalid character
+  ASSERT_THROW(Toolbox::DecodeBase64(decoded, "?"), OrthancException);
+
+  // All the allowed characters
+  Toolbox::DecodeBase64(decoded, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=");
+}
+
+
+#if 0 // enable only when compiling in Release with a C++ 11 compiler
+#include <chrono> // I had troubles to link with boost::chrono ...
+
+TEST(Toolbox, Base64_largeString)
+{
+  std::string toEncode;
+  std::string base64Result;
+  std::string decodedResult;
+
+  size_t size = 10 * 1024 * 1024;
+  toEncode.reserve(size);
+  for (size_t i = 0; i < size; i++)
+    toEncode.push_back(i % 256);
+
+  std::chrono::high_resolution_clock::time_point start;
+  std::chrono::high_resolution_clock::time_point afterEncoding;
+  std::chrono::high_resolution_clock::time_point afterDecoding;
+
+  start = std::chrono::high_resolution_clock::now();
+  Orthanc::Toolbox::EncodeBase64(base64Result, toEncode);
+  afterEncoding = std::chrono::high_resolution_clock::now();
+  Orthanc::Toolbox::DecodeBase64(decodedResult, base64Result);
+  afterDecoding = std::chrono::high_resolution_clock::now();
+
+  ASSERT_EQ(toEncode, decodedResult);
+
+  printf("encoding took %zu ms\n", (std::chrono::duration_cast<std::chrono::milliseconds>(afterEncoding - start)));
+  printf("decoding took %zu ms\n", (std::chrono::duration_cast<std::chrono::milliseconds>(afterDecoding - afterEncoding)));
+}
+#endif