changeset 1323:5a92665dee23

Sample plugin: Serve folders
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 13 Feb 2015 14:28:16 +0100
parents f497a72d9f71
children 1609f71dc259
files CMakeLists.txt Plugins/Engine/PluginsManager.cpp Plugins/Samples/ServeFolders/CMakeLists.txt Plugins/Samples/ServeFolders/Plugin.cpp Resources/CMake/BoostConfiguration.cmake
diffstat 5 files changed, 269 insertions(+), 10 deletions(-) [+]
line wrap: on
line diff
--- a/CMakeLists.txt	Fri Feb 13 13:28:01 2015 +0100
+++ b/CMakeLists.txt	Fri Feb 13 14:28:16 2015 +0100
@@ -41,9 +41,12 @@
 
 # Distribution-specific settings
 SET(USE_GTEST_DEBIAN_SOURCE_PACKAGE OFF CACHE BOOL "Use the sources of Google Test shipped with libgtest-dev (Debian only)")
+SET(SYSTEM_MONGOOSE_USE_CALLBACKS ON CACHE BOOL "The system version of Mongoose uses callbacks (version >= 3.7)")
+SET(USE_BOOST_ICONV ON CACHE BOOL "Use iconv instead of wconv (Windows only)")
+
 mark_as_advanced(USE_GTEST_DEBIAN_SOURCE_PACKAGE)
-SET(SYSTEM_MONGOOSE_USE_CALLBACKS ON CACHE BOOL "The system version of Mongoose uses callbacks (version >= 3.7)")
 mark_as_advanced(SYSTEM_MONGOOSE_USE_CALLBACKS)
+mark_as_advanced(USE_BOOST_ICONV)
 
 # Path to the root folder of the Orthanc distribution
 set(ORTHANC_ROOT ${CMAKE_SOURCE_DIR})
--- a/Plugins/Engine/PluginsManager.cpp	Fri Feb 13 13:28:01 2015 +0100
+++ b/Plugins/Engine/PluginsManager.cpp	Fri Feb 13 14:28:16 2015 +0100
@@ -292,7 +292,10 @@
       }
       else
       {
-        if (boost::filesystem::extension(it->path()) == PLUGIN_EXTENSION)
+        std::string extension = boost::filesystem::extension(it->path());
+        Toolbox::ToLowerCase(extension);
+
+        if (extension == PLUGIN_EXTENSION)
         {
           LOG(INFO) << "Found a shared library: " << it->path();
 
--- a/Plugins/Samples/ServeFolders/CMakeLists.txt	Fri Feb 13 13:28:01 2015 +0100
+++ b/Plugins/Samples/ServeFolders/CMakeLists.txt	Fri Feb 13 14:28:16 2015 +0100
@@ -2,6 +2,11 @@
 
 project(ServeFolders)
 
+set(ALLOW_DOWNLOADS ON)
+set(USE_SYSTEM_JSONCPP OFF)
+include(../../../Resources/CMake/DownloadPackage.cmake)
+include(../../../Resources/CMake/JsonCppConfiguration.cmake)
+
 if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
   link_libraries(uuid)
   SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pthread")
@@ -18,10 +23,13 @@
 endif()
 
 include_directories(${CMAKE_SOURCE_DIR}/../../Include/)
-add_library(PluginTest SHARED Plugin.cpp)
+add_library(ServerFolders SHARED 
+  Plugin.cpp
+  ${THIRD_PARTY_SOURCES}
+  )
 
 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
-  target_link_libraries(PluginTest pthread dl)
+  target_link_libraries(ServerFolders dl rt)
 endif()
--- a/Plugins/Samples/ServeFolders/Plugin.cpp	Fri Feb 13 13:28:01 2015 +0100
+++ b/Plugins/Samples/ServeFolders/Plugin.cpp	Fri Feb 13 14:28:16 2015 +0100
@@ -27,16 +27,218 @@
 
 #include <OrthancCPlugin.h>
 
+#include <json/reader.h>
+#include <json/value.h>
 #include <string.h>
 #include <stdio.h>
+#include <fstream>
+#include <algorithm>
+#include <sys/stat.h>
 
 static OrthancPluginContext* context_ = NULL;
+static std::map<std::string, std::string> folders_;
+static const char* INDEX_URI = "/app/plugin-serve-folders.html";
+
+
+static const char* GetMimeType(const std::string& path)
+{
+  size_t dot = path.find_last_of('.');
+
+  std::string extension = (dot == std::string::npos) ? "" : path.substr(dot);
+  std::transform(extension.begin(), extension.end(), extension.begin(), tolower);
+
+  if (extension == ".html")
+  {
+    return "text/html";
+  }
+  else if (extension == ".css")
+  {
+    return "text/css";
+  }
+  else if (extension == ".js")
+  {
+    return "application/javascript";
+  }
+  else if (extension == ".gif")
+  {
+    return "image/gif";
+  }
+  else if (extension == ".svg")
+  {
+    return "image/svg+xml";
+  }
+  else if (extension == ".json")
+  {
+    return "application/json";
+  }
+  else if (extension == ".xml")
+  {
+    return "application/xml";
+  }
+  else if (extension == ".png")
+  {
+    return "image/png";
+  }
+  else if (extension == ".jpg" || extension == ".jpeg")
+  {
+    return "image/jpeg";
+  }
+  else
+  {
+    std::string s = "Unknown MIME type for extension: " + extension;
+    OrthancPluginLogWarning(context_, s.c_str());
+    return "application/octet-stream";
+  }
+}
+
 
 
-static int32_t Callback(OrthancPluginRestOutput* output,
-                        const char* url,
-                        const OrthancPluginHttpRequest* request)
+static bool ReadFile(std::string& content,
+                     const std::string& path)
+{
+  struct stat s;
+  if (stat(path.c_str(), &s) != 0 ||
+      !(s.st_mode & S_IFREG))
+  {
+    // Either the path does not exist, or it is not a regular file
+    return false;
+  }
+
+  FILE* fp = fopen(path.c_str(), "rb");
+  if (fp == NULL)
+  {
+    return false;
+  }
+
+  long size;
+
+  if (fseek(fp, 0, SEEK_END) == -1 ||
+      (size = ftell(fp)) < 0)
+  {
+    fclose(fp);
+    return false;
+  }
+
+  content.resize(size);
+      
+  if (fseek(fp, 0, SEEK_SET) == -1)
+  {
+    fclose(fp);
+    return false;
+  }
+
+  bool ok = true;
+
+  if (size > 0 &&
+      fread(&content[0], size, 1, fp) != 1)
+  {
+    ok = false;
+  }
+
+  fclose(fp);
+
+  return ok;
+}
+
+
+static bool ReadConfiguration(Json::Value& configuration,
+                              OrthancPluginContext* context)
 {
+  std::string path;
+
+  {
+    char* pathTmp = OrthancPluginGetConfigurationPath(context);
+    if (pathTmp == NULL)
+    {
+      OrthancPluginLogError(context, "No configuration file is provided");
+      return false;
+    }
+
+    path = std::string(pathTmp);
+
+    OrthancPluginFreeString(context, pathTmp);
+  }
+
+  std::ifstream f(path.c_str());
+
+  Json::Reader reader;
+  if (!reader.parse(f, configuration) ||
+      configuration.type() != Json::objectValue)
+  {
+    std::string s = "Unable to parse the configuration file: " + std::string(path);
+    OrthancPluginLogError(context, s.c_str());
+    return false;
+  }
+
+  return true;
+}
+
+
+static int32_t FolderCallback(OrthancPluginRestOutput* output,
+                              const char* url,
+                              const OrthancPluginHttpRequest* request)
+{
+  if (request->method != OrthancPluginHttpMethod_Get)
+  {
+    OrthancPluginSendMethodNotAllowed(context_, output, "GET");
+    return 0;
+  }
+
+  const std::string uri = request->groups[0];
+  const std::string item = request->groups[1];
+
+  std::map<std::string, std::string>::const_iterator found = folders_.find(uri);
+  if (found == folders_.end())
+  {
+    std::string s = "Unknown URI in plugin server-folders: " + uri;
+    OrthancPluginLogError(context_, s.c_str());
+    OrthancPluginSendHttpStatusCode(context_, output, 404);
+    return 0;
+  }
+
+  std::string path = found->second + "/" + item;
+  const char* mime = GetMimeType(path);
+
+  std::string s;
+  if (ReadFile(s, path))
+  {
+    const char* resource = s.size() ? s.c_str() : NULL;
+    OrthancPluginAnswerBuffer(context_, output, resource, s.size(), mime);
+  }
+  else
+  {
+    std::string s = "Inexistent file in served folder: " + path;
+    OrthancPluginLogError(context_, s.c_str());
+    OrthancPluginSendHttpStatusCode(context_, output, 404);
+  }
+
+  return 0;
+}
+
+
+static int32_t IndexCallback(OrthancPluginRestOutput* output,
+                             const char* url,
+                             const OrthancPluginHttpRequest* request)
+{
+  if (request->method != OrthancPluginHttpMethod_Get)
+  {
+    OrthancPluginSendMethodNotAllowed(context_, output, "GET");
+    return 0;
+  }
+
+  std::string s = "<html><body><h1>Additional folders served by Orthanc</h1><ul>\n";
+
+  for (std::map<std::string, std::string>::const_iterator
+         it = folders_.begin(); it != folders_.end(); it++)
+  {
+    s += "<li><a href=\"" + it->first + "/index.html\">" + it->first + "</li>\n";
+  }
+
+  s += "</ul></body></html>";
+
+  OrthancPluginAnswerBuffer(context_, output, s.c_str(), s.size(), "text/html");
+
+  return 0;
 }
 
 
@@ -56,12 +258,52 @@
               ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER,
               ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER,
               ORTHANC_PLUGINS_MINIMAL_REVISION_NUMBER);
-      OrthancPluginLogError(context, info);
+      OrthancPluginLogError(context_, info);
       return -1;
     }
 
     OrthancPluginSetDescription(context_, "Serve additional folders with the HTTP server of Orthanc.");
 
+    Json::Value configuration;
+    if (!ReadConfiguration(configuration, context_))
+    {
+      return -1;
+    }
+
+    if (configuration.isMember("ServeFolders") &&
+        configuration["ServeFolders"].type() == Json::objectValue)
+    {
+      Json::Value::Members members = configuration["ServeFolders"].getMemberNames();
+
+      // Register the callback for each base URI
+      for (Json::Value::Members::const_iterator 
+             it = members.begin(); it != members.end(); it++)
+      {
+        const std::string& baseUri = *it;
+        const std::string path = configuration["ServeFolders"][*it].asString();
+        const std::string regex = "(" + baseUri + ")/(.*)";
+
+        if (baseUri.empty() ||
+            *baseUri.rbegin() == '/')
+        {
+          std::string message = "The URI of a folder to be server cannot be empty or end with a '/': " + *it;
+          OrthancPluginLogWarning(context_, message.c_str());
+          return -1;
+        }
+
+        OrthancPluginRegisterRestCallback(context, regex.c_str(), FolderCallback);
+        folders_[baseUri] = path;
+      }
+
+      OrthancPluginRegisterRestCallback(context, INDEX_URI, IndexCallback);
+      OrthancPluginSetRootUri(context, INDEX_URI);
+    }
+    else
+    {
+      OrthancPluginLogWarning(context_, "No section \"ServeFolders\" in your configuration file: "
+                              "No additional folder will be served!");
+    }
+
     return 0;
   }
 
--- a/Resources/CMake/BoostConfiguration.cmake	Fri Feb 13 13:28:01 2015 +0100
+++ b/Resources/CMake/BoostConfiguration.cmake	Fri Feb 13 14:28:16 2015 +0100
@@ -82,8 +82,11 @@
     # Windows XP seems not to support properly several codepages
     # (notably "Latin3", "Hebrew", and "Arabic").
 
-    # add_definitions(-DBOOST_LOCALE_WITH_WCONV=1)
-    include(${ORTHANC_ROOT}/Resources/CMake/LibIconvConfiguration.cmake)
+    if (USE_BOOST_ICONV)
+      include(${ORTHANC_ROOT}/Resources/CMake/LibIconvConfiguration.cmake)
+    else()
+      add_definitions(-DBOOST_LOCALE_WITH_WCONV=1)
+    endif()
 
   else()
     message(FATAL_ERROR "Support your platform here")