changeset 36:dfb159a079ea

merge
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 30 Aug 2012 12:37:48 +0200
parents f6d12037f886 (diff) de56f3951fad (current diff)
children 88f537ca35a5
files PalantirExplorer/explorer.js
diffstat 11 files changed, 199 insertions(+), 41 deletions(-) [+]
line wrap: on
line diff
--- a/Core/DicomFormat/DicomTag.cpp	Thu Aug 30 12:36:26 2012 +0200
+++ b/Core/DicomFormat/DicomTag.cpp	Thu Aug 30 12:37:48 2012 +0200
@@ -24,6 +24,7 @@
 
 #include <iostream>
 #include <iomanip>
+#include <stdio.h>
 
 namespace Palantir
 {
@@ -51,6 +52,14 @@
   }
 
 
+  std::string DicomTag::Format() const
+  {
+    char b[16];
+    sprintf(b, "%04x,%04x", group_, element_);
+    return std::string(b);
+  }
+
+
   const DicomTag DicomTag::ACCESSION_NUMBER = DicomTag(0x0008, 0x0050);
   const DicomTag DicomTag::IMAGE_INDEX = DicomTag(0x0054, 0x1330);
   const DicomTag DicomTag::INSTANCE_UID = DicomTag(0x0008, 0x0018);
--- a/Core/DicomFormat/DicomTag.h	Thu Aug 30 12:36:26 2012 +0200
+++ b/Core/DicomFormat/DicomTag.h	Thu Aug 30 12:37:48 2012 +0200
@@ -54,6 +54,8 @@
 
     bool operator< (const DicomTag& other) const;
 
+    std::string Format() const;
+
     friend std::ostream& operator<< (std::ostream& o, const DicomTag& tag);
 
     // Alias for the most useful tags
--- a/Core/HttpServer/MongooseServer.cpp	Thu Aug 30 12:36:26 2012 +0200
+++ b/Core/HttpServer/MongooseServer.cpp	Thu Aug 30 12:37:48 2012 +0200
@@ -38,6 +38,8 @@
 
 #define PALANTIR_REALM "Palantir Secure Area"
 
+static const long LOCALHOST = (127ll << 24) + 1ll;
+
 
 namespace Palantir
 {
@@ -397,6 +399,15 @@
   }
 
 
+  static void SendUnauthorized(HttpOutput& output)
+  {
+    std::string s = "HTTP/1.1 401 Unauthorized\r\n" 
+      "WWW-Authenticate: Basic realm=\"" PALANTIR_REALM "\""
+      "\r\n\r\n";
+    output.Send(&s[0], s.size());
+  }
+
+
   static bool Authorize(const MongooseServer& that,
                         const HttpHandler::Arguments& headers,
                         HttpOutput& output)
@@ -416,10 +427,7 @@
 
     if (!granted)
     {
-      std::string s = "HTTP/1.1 401 Unauthorized\r\n" 
-        "WWW-Authenticate: Basic realm=\"" PALANTIR_REALM "\""
-        "\r\n\r\n";
-      output.Send(&s[0], s.size());
+      SendUnauthorized(output);
       return false;
     }
     else
@@ -437,9 +445,16 @@
     if (event == MG_NEW_REQUEST) 
     {
       MongooseServer* that = (MongooseServer*) (request->user_data);
+      MongooseOutput output(connection);
+
+      if (!that->IsRemoteAccessAllowed() &&
+          request->remote_ip != LOCALHOST)
+      {
+        SendUnauthorized(output);
+        return (void*) "";
+      }
 
       HttpHandler::Arguments arguments, headers;
-      MongooseOutput c(connection);
 
       for (int i = 0; i < request->num_headers; i++)
       {
@@ -450,7 +465,7 @@
 
       // Authenticate this connection
       if (that->IsAuthenticationEnabled() &&
-          !Authorize(*that, headers, c))
+          !Authorize(*that, headers, output))
       {
         return (void*) "";
       }
@@ -466,7 +481,7 @@
         HttpHandler::Arguments::const_iterator ct = headers.find("content-type");
         if (ct == headers.end())
         {
-          c.SendHeader(HttpStatus_400_BadRequest);
+          output.SendHeader(HttpStatus_400_BadRequest);
           return (void*) "";
         }
 
@@ -486,15 +501,15 @@
         switch (status)
         {
         case PostDataStatus_NoLength:
-          c.SendHeader(HttpStatus_411_LengthRequired);
+          output.SendHeader(HttpStatus_411_LengthRequired);
           return (void*) "";
 
         case PostDataStatus_Failure:
-          c.SendHeader(HttpStatus_400_BadRequest);
+          output.SendHeader(HttpStatus_400_BadRequest);
           return (void*) "";
 
         case PostDataStatus_Pending:
-          c.AnswerBuffer("");
+          output.AnswerBuffer("");
           return (void*) "";
 
         default:
@@ -510,18 +525,18 @@
       {
         try
         {
-          handler->Handle(c, std::string(request->request_method),
+          handler->Handle(output, std::string(request->request_method),
                           uri, headers, arguments, postData);
         }
         catch (PalantirException& e)
         {
           std::cerr << "MongooseServer Exception [" << e.What() << "]" << std::endl;
-          c.SendHeader(HttpStatus_500_InternalServerError);        
+          output.SendHeader(HttpStatus_500_InternalServerError);        
         }
       }
       else
       {
-        c.SendHeader(HttpStatus_404_NotFound);
+        output.SendHeader(HttpStatus_404_NotFound);
       }
 
       // Mark as processed
@@ -543,6 +558,7 @@
   MongooseServer::MongooseServer() : pimpl_(new PImpl)
   {
     pimpl_->context_ = NULL;
+    remoteAllowed_ = false;
     authentication_ = false;
     ssl_ = false;
     port_ = 8000;
@@ -664,6 +680,13 @@
     certificate_ = path;
   }
 
+  void MongooseServer::SetRemoteAccessAllowed(bool allowed)
+  {
+    Stop();
+    remoteAllowed_ = allowed;
+  }
+
+
   bool MongooseServer::IsValidBasicHttpAuthentication(const std::string& basic) const
   {
     return registeredUsers_.find(basic) != registeredUsers_.end();
--- a/Core/HttpServer/MongooseServer.h	Thu Aug 30 12:36:26 2012 +0200
+++ b/Core/HttpServer/MongooseServer.h	Thu Aug 30 12:37:48 2012 +0200
@@ -44,6 +44,7 @@
     typedef std::set<std::string> RegisteredUsers;
     RegisteredUsers registeredUsers_;
 
+    bool remoteAllowed_;
     bool authentication_;
     bool ssl_;
     std::string certificate_;
@@ -95,6 +96,13 @@
 
     void SetSslCertificate(const char* path);
 
+    bool IsRemoteAccessAllowed() const
+    {
+      return remoteAllowed_;
+    }
+
+    void SetRemoteAccessAllowed(bool allowed);
+
     void ClearHandlers();
 
     // Can return NULL if no handler is associated to this URI
--- a/PalantirExplorer/explorer.html	Thu Aug 30 12:36:26 2012 +0200
+++ b/PalantirExplorer/explorer.html	Thu Aug 30 12:37:48 2012 +0200
@@ -178,6 +178,10 @@
             <div style="padding:10px">
               <div class="ui-body ui-body-b">
                 <h1>DICOM Tags</h1>
+                <p align="right">
+                  <input type="checkbox" id="show-tag-name" checked="checked" class="custom" data-mini="true" />
+                  <label for="show-tag-name">Show tag description</label>
+                </p>
                 <div id="dicom-tree"></div>
               </div>
             </div>
--- a/PalantirExplorer/explorer.js	Thu Aug 30 12:36:26 2012 +0200
+++ b/PalantirExplorer/explorer.js	Thu Aug 30 12:37:48 2012 +0200
@@ -393,33 +393,42 @@
   var result = [];
 
   for (var i in dicom) {
-    if (dicom [i] != null) {
-      if (typeof dicom[i] == 'string')
+    if (dicom[i] != null) {
+      var label = i + '<span class="tag-name"> (<i>' + dicom[i]["Name"] + '</i>)</span>: ';
+
+      if (dicom[i]["Type"] == 'String')
       {
         result.push({
-          label: i + ': <strong>' + dicom[i] + '</strong>',
+          label: label + '<strong>' + dicom[i]["Value"] + '</strong>',
           children: []
         });
       }
-      else if (typeof dicom[i] == 'number')
+      else if (dicom[i]["Type"] == 'TooLong')
       {
         result.push({
-          label: i + ': <i>Too long</i>',
+          label: label + '<i>Too long</i>',
           children: []
         });
       }
-      else
+      else if (dicom[i]["Type"] == 'Null')
+      {
+        result.push({
+          label: label + '<i>Null</i>',
+          children: []
+        });
+      }
+      else if (dicom[i]["Type"] == 'Sequence')
       {
         var c = [];
-        for (var j = 0; j < dicom[i].length; j++) {
+        for (var j = 0; j < dicom[i]["Value"].length; j++) {
           c.push({
             label: 'Item ' + j,
-            children: ConvertForTree(dicom[i][j])
+            children: ConvertForTree(dicom[i]["Value"][j])
           });
         }
 
         result.push({
-          label: i + '[]',
+          label: label + '[]',
           children: c
         });
       }
@@ -450,7 +459,7 @@
               .listview('refresh');
 
             $.ajax({
-              url: '/instances/' + instance.ID + '/all-tags',
+              url: '/instances/' + instance.ID + '/tags',
               dataType: 'json',
               success: function(s) {
                 $('#dicom-tree').tree('loadData', ConvertForTree(s));
@@ -543,7 +552,7 @@
 $('#instance-download-json').live('click', function(e) {
   // http://stackoverflow.com/a/1296101
   e.preventDefault();  //stop the browser from following
-  window.location.href = '/instances/' + $.mobile.pageData.uuid + '/all-tags';
+  window.location.href = '/instances/' + $.mobile.pageData.uuid + '/tags';
 });
 
 
@@ -655,3 +664,12 @@
     }
   });
 });
+
+
+$('#show-tag-name').live('change', function(e) {
+  var checked = e.currentTarget.checked;
+  if (checked)
+    $('.tag-name').show();
+  else
+    $('.tag-name').hide();
+});
--- a/PalantirServer/DicomIntegerPixelAccessor.cpp	Thu Aug 30 12:36:26 2012 +0200
+++ b/PalantirServer/DicomIntegerPixelAccessor.cpp	Thu Aug 30 12:37:48 2012 +0200
@@ -80,8 +80,8 @@
       throw PalantirException(ErrorCode_NotImplemented);
     }
 
-    printf("%d %d %d %d %d %d %d\n", width_, height_, samplesPerPixel_, bitsAllocated,
-           bitsStored, highBit, pixelRepresentation);
+    /*printf("%d %d %d %d %d %d %d\n", width_, height_, samplesPerPixel_, bitsAllocated,
+      bitsStored, highBit, pixelRepresentation);*/
 
     bytesPerPixel_ = bitsAllocated / 8;
     shift_ = highBit + 1 - bitsStored;
--- a/PalantirServer/FromDcmtkBridge.cpp	Thu Aug 30 12:36:26 2012 +0200
+++ b/PalantirServer/FromDcmtkBridge.cpp	Thu Aug 30 12:37:48 2012 +0200
@@ -298,14 +298,20 @@
   {
     assert(target.type() == Json::objectValue);
 
-    const std::string tagName = FromDcmtkBridge::GetName(FromDcmtkBridge::GetTag(element));
+    DicomTag tag(FromDcmtkBridge::GetTag(element));
+    const std::string tagName = FromDcmtkBridge::GetName(tag);
+    const std::string formattedTag = tag.Format();
 
     if (element.isLeaf())
     {
+      Json::Value value(Json::objectValue);
+      value["Name"] = tagName;
+
       std::auto_ptr<DicomValue> v(FromDcmtkBridge::ConvertLeafElement(element));
       if (v->IsNull())
       {
-        target[tagName] = Json::nullValue;
+        value["Type"] = "Null";
+        value["Value"] = Json::nullValue;
       }
       else
       {
@@ -313,18 +319,21 @@
         if (maxStringLength == 0 ||
             s.size() <= maxStringLength)
         {
-          target[tagName] = s;
+          value["Type"] = "String";
+          value["Value"] = s;
         }
         else
         {
-          // An integer value of 0 in JSON indicates too long field
-          target[tagName] = 0; 
+          value["Type"] = "TooLong";
+          value["Value"] = Json::nullValue;
         }
       }
+
+      target[formattedTag] = value;
     }
     else
     {
-      target[tagName] = Json::Value(Json::arrayValue);
+      Json::Value children(Json::arrayValue);
 
       // "All subclasses of DcmElement except for DcmSequenceOfItems
       // are leaf nodes, while DcmSequenceOfItems, DcmItem, DcmDataset
@@ -334,9 +343,13 @@
       for (unsigned long i = 0; i < sequence.card(); i++)
       {
         DcmItem* child = sequence.getItem(i);
-        Json::Value& v = target[tagName].append(Json::objectValue);
+        Json::Value& v = children.append(Json::objectValue);
         StoreItem(v, *child, maxStringLength);
       }  
+
+      target[formattedTag]["Name"] = tagName;
+      target[formattedTag]["Type"] = "Sequence";
+      target[formattedTag]["Value"] = children;
     }
   }
 
--- a/PalantirServer/PalantirRestApi.cpp	Thu Aug 30 12:36:26 2012 +0200
+++ b/PalantirServer/PalantirRestApi.cpp	Thu Aug 30 12:37:48 2012 +0200
@@ -38,6 +38,71 @@
     output.AnswerBufferWithContentType(s, "application/json");
   }
 
+
+  static void SimplifyTagsRecursion(Json::Value& target,
+                                    const Json::Value& source)
+  {
+    assert(source.isObject());
+
+    target = Json::objectValue;
+    Json::Value::Members members = source.getMemberNames();
+
+    for (size_t i = 0; i < members.size(); i++)
+    {
+      const Json::Value& v = source[members[i]];
+      const std::string& name = v["Name"].asString();
+      const std::string& type = v["Type"].asString();
+
+      if (type == "String")
+      {
+        target[name] = v["Value"].asString();
+      }
+      else if (type == "TooLong" ||
+               type == "Null")
+      {
+        target[name] = Json::nullValue;
+      }
+      else if (type == "Sequence")
+      {
+        const Json::Value& array = v["Value"];
+        assert(array.isArray());
+
+        Json::Value children = Json::arrayValue;
+        for (size_t i = 0; i < array.size(); i++)
+        {
+          Json::Value c;
+          SimplifyTagsRecursion(c, array[i]);
+          children.append(c);
+        }
+
+        target[name] = children;
+      }
+      else
+      {
+        assert(0);
+      }
+    }
+  }
+
+
+  static void SimplifyTags(Json::Value& target,
+                           const FileStorage& storage,
+                           const std::string& fileUuid)
+  {
+    std::string s;
+    storage.ReadFile(s, fileUuid);
+
+    Json::Value source;
+    Json::Reader reader;
+    if (!reader.parse(s, source))
+    {
+      throw PalantirException("Corrupted JSON file");
+    }
+
+    SimplifyTagsRecursion(target, source);
+  }
+
+
   bool PalantirRestApi::Store(Json::Value& result,
                               const std::string& postData)
   {
@@ -49,7 +114,7 @@
     }
     is.setEos();
 
-    printf("[%d]\n", postData.size());
+    //printf("[%d]\n", postData.size());
 
     DcmFileFormat dicomFile;
     if (dicomFile.read(is).good())
@@ -467,7 +532,8 @@
     else if (uri.size() == 3 &&
              uri[0] == "instances" &&
              (uri[2] == "file" || 
-              uri[2] == "all-tags"))
+              uri[2] == "tags" || 
+              uri[2] == "simplified-tags"))
     {
       std::string fileUuid, contentType;
       if (uri[2] == "file")
@@ -475,7 +541,8 @@
         existingResource = index_.GetDicomFile(fileUuid, uri[1]);
         contentType = "application/dicom";
       }
-      else
+      else if (uri[2] == "tags" ||
+               uri[2] == "simplified-tags")
       {
         existingResource = index_.GetJsonFile(fileUuid, uri[1]);
         contentType = "application/json";
@@ -483,8 +550,18 @@
 
       if (existingResource)
       {
-        output.AnswerFile(storage_, fileUuid, contentType);
-        return;
+        if (uri[2] == "simplified-tags")
+        {
+          Json::Value v;
+          SimplifyTags(v, storage_, fileUuid);
+          SendJson(output, v);
+          return;
+        }
+        else
+        {
+          output.AnswerFile(storage_, fileUuid, contentType);
+          return;
+        }
       }
     }
 
--- a/PalantirServer/main.cpp	Thu Aug 30 12:36:26 2012 +0200
+++ b/PalantirServer/main.cpp	Thu Aug 30 12:37:48 2012 +0200
@@ -49,14 +49,14 @@
   virtual void Handle(const std::vector<uint8_t>& dicomFile,
                       const DicomMap& dicomSummary,
                       const Json::Value& dicomJson,
-                      const std::string& distantAet)
+                      const std::string& remoteAet)
   {
     std::string instanceUuid;
     if (dicomFile.size() > 0)
     {
       index_.Store(instanceUuid, storage_, 
                    reinterpret_cast<const char*>(&dicomFile[0]), dicomFile.size(),
-                   dicomSummary, dicomJson, distantAet);
+                   dicomSummary, dicomJson, remoteAet);
     }
   }
 };
@@ -121,6 +121,7 @@
       // HTTP server
       MongooseServer httpServer;
       httpServer.SetPort(GetGlobalIntegerParameter("HttpPort", 8000));
+      httpServer.SetRemoteAccessAllowed(GetGlobalBoolParameter("RemoteAccessAllowed", false));
 
       httpServer.SetAuthenticationEnabled(GetGlobalBoolParameter("AuthenticationEnabled", false));
       SetupRegisteredUsers(httpServer);
--- a/Resources/Configuration.json	Thu Aug 30 12:36:26 2012 +0200
+++ b/Resources/Configuration.json	Thu Aug 30 12:37:48 2012 +0200
@@ -33,6 +33,9 @@
      * Security-related options
      **/
 
+    // Whether remote hosts can connect to the HTTP server
+    "RemoteAccessAllowed" : false,
+
     // Whether or not SSL is enabled
     "SslEnabled" : false,
 
@@ -40,7 +43,7 @@
     "SslCertificate" : "certificate.pem",
 
     // Whether or not the password protection is enabled
-    "AuthenticationEnabled" : true,
+    "AuthenticationEnabled" : false,
 
     // The list of the registered users. Because Palantir uses HTTP
     // Basic Authentication, the passwords are stored as plain text.