# HG changeset patch # User Sebastien Jodogne # Date 1478708693 -3600 # Node ID 1a90184363f898a7cbc7dc7966d4e7f3ff581cdb # Parent 8189115c0da57f48d55d55bc4515a1bb17360c7e sync diff -r 8189115c0da5 -r 1a90184363f8 CMakeLists.txt --- a/CMakeLists.txt Wed Nov 09 15:31:51 2016 +0100 +++ b/CMakeLists.txt Wed Nov 09 17:24:53 2016 +0100 @@ -126,6 +126,7 @@ -DORTHANC_ENABLE_MD5=0 -DORTHANC_ENABLE_BASE64=0 -DORTHANC_ENABLE_LOGGING=0 + -DORTHANC_SANDBOXED=0 ) set(CORE_SOURCES @@ -147,8 +148,8 @@ ${CMAKE_SOURCE_DIR}/Orthanc/Core/SQLite/StatementId.cpp ${CMAKE_SOURCE_DIR}/Orthanc/Core/SQLite/StatementReference.cpp ${CMAKE_SOURCE_DIR}/Orthanc/Core/SQLite/Transaction.cpp + ${CMAKE_SOURCE_DIR}/Orthanc/Core/SystemToolbox.cpp ${CMAKE_SOURCE_DIR}/Orthanc/Core/Toolbox.cpp - ${CMAKE_SOURCE_DIR}/Orthanc/Core/Uuid.cpp ${CMAKE_SOURCE_DIR}/Orthanc/Core/DicomFormat/DicomMap.cpp ${CMAKE_SOURCE_DIR}/Orthanc/Core/DicomFormat/DicomTag.cpp ${CMAKE_SOURCE_DIR}/Orthanc/Core/DicomFormat/DicomValue.cpp diff -r 8189115c0da5 -r 1a90184363f8 Orthanc/Core/FileStorage/FilesystemStorage.cpp --- a/Orthanc/Core/FileStorage/FilesystemStorage.cpp Wed Nov 09 15:31:51 2016 +0100 +++ b/Orthanc/Core/FileStorage/FilesystemStorage.cpp Wed Nov 09 17:24:53 2016 +0100 @@ -39,7 +39,7 @@ #include "../Logging.h" #include "../OrthancException.h" #include "../Toolbox.h" -#include "../Uuid.h" +#include "../SystemToolbox.h" #include @@ -83,7 +83,7 @@ //root_ = boost::filesystem::absolute(root).string(); root_ = root; - Toolbox::MakeDirectory(root); + SystemToolbox::MakeDirectory(root); } @@ -143,7 +143,7 @@ } } - Toolbox::WriteFile(content, size, path.string()); + SystemToolbox::WriteFile(content, size, path.string()); } @@ -155,7 +155,7 @@ << "\" content type"; content.clear(); - Toolbox::ReadFile(content, GetPath(uuid).string()); + SystemToolbox::ReadFile(content, GetPath(uuid).string()); } @@ -177,7 +177,7 @@ { for (fs::recursive_directory_iterator current(root_), end; current != end ; ++current) { - if (Toolbox::IsRegularFile(current->path().string())) + if (SystemToolbox::IsRegularFile(current->path().string())) { try { diff -r 8189115c0da5 -r 1a90184363f8 Orthanc/Core/PrecompiledHeaders.h --- a/Orthanc/Core/PrecompiledHeaders.h Wed Nov 09 15:31:51 2016 +0100 +++ b/Orthanc/Core/PrecompiledHeaders.h Wed Nov 09 17:24:53 2016 +0100 @@ -56,6 +56,5 @@ #include "Logging.h" #include "OrthancException.h" #include "Toolbox.h" -#include "Uuid.h" #endif diff -r 8189115c0da5 -r 1a90184363f8 Orthanc/Core/SystemToolbox.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Orthanc/Core/SystemToolbox.cpp Wed Nov 09 17:24:53 2016 +0100 @@ -0,0 +1,502 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, 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 . + **/ + + +#include "PrecompiledHeaders.h" +#include "SystemToolbox.h" + + +#if BOOST_HAS_DATE_TIME == 1 +#include +#endif + + +#if defined(_WIN32) +#include +#include // For "_spawnvp()" and "_getpid()" +#else +#include // For "execvp()" +#include // For "waitpid()" +#endif + +#if defined(__APPLE__) && defined(__MACH__) +#include /* _NSGetExecutablePath */ +#include /* PATH_MAX */ +#endif + +#if defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__) +#include /* PATH_MAX */ +#include +#include +#endif + + +#include "Logging.h" +#include "OrthancException.h" +#include "Toolbox.h" + +#include +#include + + +namespace Orthanc +{ + static bool finish_; + static ServerBarrierEvent barrierEvent_; + +#if defined(_WIN32) + static BOOL WINAPI ConsoleControlHandler(DWORD dwCtrlType) + { + // http://msdn.microsoft.com/en-us/library/ms683242(v=vs.85).aspx + finish_ = true; + return true; + } +#else + static void SignalHandler(int signal) + { + if (signal == SIGHUP) + { + barrierEvent_ = ServerBarrierEvent_Reload; + } + + finish_ = true; + } +#endif + + + static ServerBarrierEvent ServerBarrierInternal(const bool* stopFlag) + { +#if defined(_WIN32) + SetConsoleCtrlHandler(ConsoleControlHandler, true); +#else + signal(SIGINT, SignalHandler); + signal(SIGQUIT, SignalHandler); + signal(SIGTERM, SignalHandler); + signal(SIGHUP, SignalHandler); +#endif + + // Active loop that awakens every 100ms + finish_ = false; + barrierEvent_ = ServerBarrierEvent_Stop; + while (!(*stopFlag || finish_)) + { + Toolbox::USleep(100 * 1000); + } + +#if defined(_WIN32) + SetConsoleCtrlHandler(ConsoleControlHandler, false); +#else + signal(SIGINT, NULL); + signal(SIGQUIT, NULL); + signal(SIGTERM, NULL); + signal(SIGHUP, NULL); +#endif + + return barrierEvent_; + } + + + ServerBarrierEvent SystemToolbox::ServerBarrier(const bool& stopFlag) + { + return ServerBarrierInternal(&stopFlag); + } + + + ServerBarrierEvent SystemToolbox::ServerBarrier() + { + const bool stopFlag = false; + return ServerBarrierInternal(&stopFlag); + } + + + static std::streamsize GetStreamSize(std::istream& f) + { + // http://www.cplusplus.com/reference/iostream/istream/tellg/ + f.seekg(0, std::ios::end); + std::streamsize size = f.tellg(); + f.seekg(0, std::ios::beg); + + return size; + } + + + void SystemToolbox::ReadFile(std::string& content, + const std::string& path) + { + if (!IsRegularFile(path)) + { + LOG(ERROR) << std::string("The path does not point to a regular file: ") << path; + throw OrthancException(ErrorCode_RegularFileExpected); + } + + boost::filesystem::ifstream f; + f.open(path, std::ifstream::in | std::ifstream::binary); + if (!f.good()) + { + throw OrthancException(ErrorCode_InexistentFile); + } + + std::streamsize size = GetStreamSize(f); + content.resize(size); + if (size != 0) + { + f.read(reinterpret_cast(&content[0]), size); + } + + f.close(); + } + + + bool SystemToolbox::ReadHeader(std::string& header, + const std::string& path, + size_t headerSize) + { + if (!IsRegularFile(path)) + { + LOG(ERROR) << std::string("The path does not point to a regular file: ") << path; + throw OrthancException(ErrorCode_RegularFileExpected); + } + + boost::filesystem::ifstream f; + f.open(path, std::ifstream::in | std::ifstream::binary); + if (!f.good()) + { + throw OrthancException(ErrorCode_InexistentFile); + } + + bool full = true; + + { + std::streamsize size = GetStreamSize(f); + if (size <= 0) + { + headerSize = 0; + full = false; + } + else if (static_cast(size) < headerSize) + { + headerSize = size; // Truncate to the size of the file + full = false; + } + } + + header.resize(headerSize); + if (headerSize != 0) + { + f.read(reinterpret_cast(&header[0]), headerSize); + } + + f.close(); + + return full; + } + + + void SystemToolbox::WriteFile(const void* content, + size_t size, + const std::string& path) + { + boost::filesystem::ofstream f; + f.open(path, std::ofstream::out | std::ofstream::binary); + if (!f.good()) + { + throw OrthancException(ErrorCode_CannotWriteFile); + } + + if (size != 0) + { + f.write(reinterpret_cast(content), size); + + if (!f.good()) + { + f.close(); + throw OrthancException(ErrorCode_FileStorageCannotWrite); + } + } + + f.close(); + } + + + void SystemToolbox::WriteFile(const std::string& content, + const std::string& path) + { + WriteFile(content.size() > 0 ? content.c_str() : NULL, + content.size(), path); + } + + + void SystemToolbox::RemoveFile(const std::string& path) + { + if (boost::filesystem::exists(path)) + { + if (IsRegularFile(path)) + { + boost::filesystem::remove(path); + } + else + { + throw OrthancException(ErrorCode_RegularFileExpected); + } + } + } + + + uint64_t SystemToolbox::GetFileSize(const std::string& path) + { + try + { + return static_cast(boost::filesystem::file_size(path)); + } + catch (boost::filesystem::filesystem_error&) + { + throw OrthancException(ErrorCode_InexistentFile); + } + } + + + void SystemToolbox::MakeDirectory(const std::string& path) + { + if (boost::filesystem::exists(path)) + { + if (!boost::filesystem::is_directory(path)) + { + throw OrthancException(ErrorCode_DirectoryOverFile); + } + } + else + { + if (!boost::filesystem::create_directories(path)) + { + throw OrthancException(ErrorCode_MakeDirectory); + } + } + } + + + bool SystemToolbox::IsExistingFile(const std::string& path) + { + return boost::filesystem::exists(path); + } + + +#if defined(_WIN32) + static std::string GetPathToExecutableInternal() + { + // Yes, this is ugly, but there is no simple way to get the + // required buffer size, so we use a big constant + std::vector buffer(32768); + /*int bytes =*/ GetModuleFileNameA(NULL, &buffer[0], static_cast(buffer.size() - 1)); + return std::string(&buffer[0]); + } + +#elif defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__) + static std::string GetPathToExecutableInternal() + { + std::vector buffer(PATH_MAX + 1); + ssize_t bytes = readlink("/proc/self/exe", &buffer[0], buffer.size() - 1); + if (bytes == 0) + { + throw OrthancException(ErrorCode_PathToExecutable); + } + + return std::string(&buffer[0]); + } + +#elif defined(__APPLE__) && defined(__MACH__) + static std::string GetPathToExecutableInternal() + { + char pathbuf[PATH_MAX + 1]; + unsigned int bufsize = static_cast(sizeof(pathbuf)); + + _NSGetExecutablePath( pathbuf, &bufsize); + + return std::string(pathbuf); + } + +#else +#error Support your platform here +#endif + + + std::string SystemToolbox::GetPathToExecutable() + { + boost::filesystem::path p(GetPathToExecutableInternal()); + return boost::filesystem::absolute(p).string(); + } + + + std::string SystemToolbox::GetDirectoryOfExecutable() + { + boost::filesystem::path p(GetPathToExecutableInternal()); + return boost::filesystem::absolute(p.parent_path()).string(); + } + + + void SystemToolbox::ExecuteSystemCommand(const std::string& command, + const std::vector& arguments) + { + // Convert the arguments as a C array + std::vector args(arguments.size() + 2); + + args.front() = const_cast(command.c_str()); + + for (size_t i = 0; i < arguments.size(); i++) + { + args[i + 1] = const_cast(arguments[i].c_str()); + } + + args.back() = NULL; + + int status; + +#if defined(_WIN32) + // http://msdn.microsoft.com/en-us/library/275khfab.aspx + status = static_cast(_spawnvp(_P_OVERLAY, command.c_str(), &args[0])); + +#else + int pid = fork(); + + if (pid == -1) + { + // Error in fork() +#if ORTHANC_ENABLE_LOGGING == 1 + LOG(ERROR) << "Cannot fork a child process"; +#endif + + throw OrthancException(ErrorCode_SystemCommand); + } + else if (pid == 0) + { + // Execute the system command in the child process + execvp(command.c_str(), &args[0]); + + // We should never get here + _exit(1); + } + else + { + // Wait for the system command to exit + waitpid(pid, &status, 0); + } +#endif + + if (status != 0) + { +#if ORTHANC_ENABLE_LOGGING == 1 + LOG(ERROR) << "System command failed with status code " << status; +#endif + + throw OrthancException(ErrorCode_SystemCommand); + } + } + + + int SystemToolbox::GetProcessId() + { +#if defined(_WIN32) + return static_cast(_getpid()); +#else + return static_cast(getpid()); +#endif + } + + + bool SystemToolbox::IsRegularFile(const std::string& path) + { + namespace fs = boost::filesystem; + + try + { + if (fs::exists(path)) + { + fs::file_status status = fs::status(path); + return (status.type() == boost::filesystem::regular_file || + status.type() == boost::filesystem::reparse_file); // Fix BitBucket issue #11 + } + } + catch (fs::filesystem_error&) + { + } + + return false; + } + + + FILE* SystemToolbox::OpenFile(const std::string& path, + FileMode mode) + { +#if defined(_WIN32) + // TODO Deal with special characters by converting to the current locale +#endif + + const char* m; + switch (mode) + { + case FileMode_ReadBinary: + m = "rb"; + break; + + case FileMode_WriteBinary: + m = "wb"; + break; + + default: + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + + return fopen(path.c_str(), m); + } + + +#if BOOST_HAS_DATE_TIME == 1 + std::string SystemToolbox::GetNowIsoString() + { + boost::posix_time::ptime now = boost::posix_time::second_clock::local_time(); + return boost::posix_time::to_iso_string(now); + } + + void SystemToolbox::GetNowDicom(std::string& date, + std::string& time) + { + boost::posix_time::ptime now = boost::posix_time::second_clock::local_time(); + tm tm = boost::posix_time::to_tm(now); + + char s[32]; + sprintf(s, "%04d%02d%02d", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); + date.assign(s); + + // TODO milliseconds + sprintf(s, "%02d%02d%02d.%06d", tm.tm_hour, tm.tm_min, tm.tm_sec, 0); + time.assign(s); + } +#endif +} diff -r 8189115c0da5 -r 1a90184363f8 Orthanc/Core/SystemToolbox.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Orthanc/Core/SystemToolbox.h Wed Nov 09 17:24:53 2016 +0100 @@ -0,0 +1,100 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, 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 . + **/ + + +#pragma once + +#if !defined(ORTHANC_SANDBOXED) +# error The macro ORTHANC_SANDBOXED must be defined +#endif + +#if ORTHANC_SANDBOXED == 1 +# error The namespace SystemToolbox cannot be used in sandboxed environments +#endif + +#include "Enumerations.h" + +#include +#include +#include + +namespace Orthanc +{ + namespace SystemToolbox + { + ServerBarrierEvent ServerBarrier(const bool& stopFlag); + + ServerBarrierEvent ServerBarrier(); + + void ReadFile(std::string& content, + const std::string& path); + + bool ReadHeader(std::string& header, + const std::string& path, + size_t headerSize); + + void WriteFile(const void* content, + size_t size, + const std::string& path); + + void WriteFile(const std::string& content, + const std::string& path); + + void RemoveFile(const std::string& path); + + uint64_t GetFileSize(const std::string& path); + + void MakeDirectory(const std::string& path); + + bool IsExistingFile(const std::string& path); + + std::string GetPathToExecutable(); + + std::string GetDirectoryOfExecutable(); + + void ExecuteSystemCommand(const std::string& command, + const std::vector& arguments); + + int GetProcessId(); + + bool IsRegularFile(const std::string& path); + + FILE* OpenFile(const std::string& path, + FileMode mode); + +#if BOOST_HAS_DATE_TIME == 1 + std::string GetNowIsoString(); + + void GetNowDicom(std::string& date, + std::string& time); +#endif + } +} diff -r 8189115c0da5 -r 1a90184363f8 Orthanc/Core/Toolbox.cpp --- a/Orthanc/Core/Toolbox.cpp Wed Nov 09 15:31:51 2016 +0100 +++ b/Orthanc/Core/Toolbox.cpp Wed Nov 09 17:24:53 2016 +0100 @@ -36,55 +36,29 @@ #include "OrthancException.h" #include "Logging.h" +#include +#include +#include +#include + #include #include #include -#include -#include -#include -#include #include #include -#if BOOST_HAS_DATE_TIME == 1 -#include -#endif - #if BOOST_HAS_REGEX == 1 -#include -#endif - -#if defined(_WIN32) -#include -#include // For "_spawnvp()" and "_getpid()" -#else -#include // For "execvp()" -#include // For "waitpid()" -#endif - -#if defined(__APPLE__) && defined(__MACH__) -#include /* _NSGetExecutablePath */ -#include /* PATH_MAX */ -#endif - -#if defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__) -#include /* PATH_MAX */ -#include -#include +# include #endif #if BOOST_HAS_LOCALE != 1 -#error Since version 0.7.6, Orthanc entirely relies on boost::locale +# error Since version 0.7.6, Orthanc entirely relies on boost::locale #endif -#include - - #if ORTHANC_ENABLE_MD5 == 1 # include "../Resources/ThirdParty/md5/md5.h" #endif - #if ORTHANC_ENABLE_BASE64 == 1 # include "../Resources/ThirdParty/base64/base64.h" #endif @@ -103,9 +77,23 @@ #endif + +// Inclusions for UUID +// http://stackoverflow.com/a/1626302 + +extern "C" +{ +#ifdef WIN32 +# include +#else +# include +#endif +} + + #if ORTHANC_ENABLE_PUGIXML == 1 -#include "ChunkedBuffer.h" -#include +# include "ChunkedBuffer.h" +# include #endif @@ -123,75 +111,6 @@ } -#if ORTHANC_SANDBOXED == 0 - static bool finish_; - static ServerBarrierEvent barrierEvent_; - -#if defined(_WIN32) - static BOOL WINAPI ConsoleControlHandler(DWORD dwCtrlType) - { - // http://msdn.microsoft.com/en-us/library/ms683242(v=vs.85).aspx - finish_ = true; - return true; - } -#else - static void SignalHandler(int signal) - { - if (signal == SIGHUP) - { - barrierEvent_ = ServerBarrierEvent_Reload; - } - - finish_ = true; - } -#endif - - - static ServerBarrierEvent ServerBarrierInternal(const bool* stopFlag) - { -#if defined(_WIN32) - SetConsoleCtrlHandler(ConsoleControlHandler, true); -#else - signal(SIGINT, SignalHandler); - signal(SIGQUIT, SignalHandler); - signal(SIGTERM, SignalHandler); - signal(SIGHUP, SignalHandler); -#endif - - // Active loop that awakens every 100ms - finish_ = false; - barrierEvent_ = ServerBarrierEvent_Stop; - while (!(*stopFlag || finish_)) - { - Toolbox::USleep(100 * 1000); - } - -#if defined(_WIN32) - SetConsoleCtrlHandler(ConsoleControlHandler, false); -#else - signal(SIGINT, NULL); - signal(SIGQUIT, NULL); - signal(SIGTERM, NULL); - signal(SIGHUP, NULL); -#endif - - return barrierEvent_; - } - - - ServerBarrierEvent Toolbox::ServerBarrier(const bool& stopFlag) - { - return ServerBarrierInternal(&stopFlag); - } - - ServerBarrierEvent Toolbox::ServerBarrier() - { - const bool stopFlag = false; - return ServerBarrierInternal(&stopFlag); - } -#endif /* ORTHANC_SANDBOXED */ - - void Toolbox::ToUpperCase(std::string& s) { std::transform(s.begin(), s.end(), s.begin(), toupper); @@ -219,149 +138,6 @@ } - static std::streamsize GetStreamSize(std::istream& f) - { - // http://www.cplusplus.com/reference/iostream/istream/tellg/ - f.seekg(0, std::ios::end); - std::streamsize size = f.tellg(); - f.seekg(0, std::ios::beg); - - return size; - } - - -#if ORTHANC_SANDBOXED == 0 - void Toolbox::ReadFile(std::string& content, - const std::string& path) - { - if (!IsRegularFile(path)) - { - LOG(ERROR) << std::string("The path does not point to a regular file: ") << path; - throw OrthancException(ErrorCode_RegularFileExpected); - } - - boost::filesystem::ifstream f; - f.open(path, std::ifstream::in | std::ifstream::binary); - if (!f.good()) - { - throw OrthancException(ErrorCode_InexistentFile); - } - - std::streamsize size = GetStreamSize(f); - content.resize(size); - if (size != 0) - { - f.read(reinterpret_cast(&content[0]), size); - } - - f.close(); - } -#endif - - -#if ORTHANC_SANDBOXED == 0 - bool Toolbox::ReadHeader(std::string& header, - const std::string& path, - size_t headerSize) - { - if (!IsRegularFile(path)) - { - LOG(ERROR) << std::string("The path does not point to a regular file: ") << path; - throw OrthancException(ErrorCode_RegularFileExpected); - } - - boost::filesystem::ifstream f; - f.open(path, std::ifstream::in | std::ifstream::binary); - if (!f.good()) - { - throw OrthancException(ErrorCode_InexistentFile); - } - - bool full = true; - - { - std::streamsize size = GetStreamSize(f); - if (size <= 0) - { - headerSize = 0; - full = false; - } - else if (static_cast(size) < headerSize) - { - headerSize = size; // Truncate to the size of the file - full = false; - } - } - - header.resize(headerSize); - if (headerSize != 0) - { - f.read(reinterpret_cast(&header[0]), headerSize); - } - - f.close(); - - return full; - } -#endif - - -#if ORTHANC_SANDBOXED == 0 - void Toolbox::WriteFile(const void* content, - size_t size, - const std::string& path) - { - boost::filesystem::ofstream f; - f.open(path, std::ofstream::out | std::ofstream::binary); - if (!f.good()) - { - throw OrthancException(ErrorCode_CannotWriteFile); - } - - if (size != 0) - { - f.write(reinterpret_cast(content), size); - - if (!f.good()) - { - f.close(); - throw OrthancException(ErrorCode_FileStorageCannotWrite); - } - } - - f.close(); - } -#endif - - -#if ORTHANC_SANDBOXED == 0 - void Toolbox::WriteFile(const std::string& content, - const std::string& path) - { - WriteFile(content.size() > 0 ? content.c_str() : NULL, - content.size(), path); - } -#endif - - -#if ORTHANC_SANDBOXED == 0 - void Toolbox::RemoveFile(const std::string& path) - { - if (boost::filesystem::exists(path)) - { - if (IsRegularFile(path)) - { - boost::filesystem::remove(path); - } - else - { - throw OrthancException(ErrorCode_RegularFileExpected); - } - } - } -#endif - - void Toolbox::SplitUriComponents(UriComponents& components, const std::string& uri) { @@ -529,22 +305,6 @@ } - -#if ORTHANC_SANDBOXED == 0 - uint64_t Toolbox::GetFileSize(const std::string& path) - { - try - { - return static_cast(boost::filesystem::file_size(path)); - } - catch (boost::filesystem::filesystem_error&) - { - throw OrthancException(ErrorCode_InexistentFile); - } - } -#endif - - #if ORTHANC_ENABLE_MD5 == 1 static char GetHexadecimalCharacter(uint8_t value) { @@ -661,65 +421,6 @@ #endif - -#if defined(_WIN32) - static std::string GetPathToExecutableInternal() - { - // Yes, this is ugly, but there is no simple way to get the - // required buffer size, so we use a big constant - std::vector buffer(32768); - /*int bytes =*/ GetModuleFileNameA(NULL, &buffer[0], static_cast(buffer.size() - 1)); - return std::string(&buffer[0]); - } - -#elif defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__) - static std::string GetPathToExecutableInternal() - { - std::vector buffer(PATH_MAX + 1); - ssize_t bytes = readlink("/proc/self/exe", &buffer[0], buffer.size() - 1); - if (bytes == 0) - { - throw OrthancException(ErrorCode_PathToExecutable); - } - - return std::string(&buffer[0]); - } - -#elif defined(__APPLE__) && defined(__MACH__) - static std::string GetPathToExecutableInternal() - { - char pathbuf[PATH_MAX + 1]; - unsigned int bufsize = static_cast(sizeof(pathbuf)); - - _NSGetExecutablePath( pathbuf, &bufsize); - - return std::string(pathbuf); - } - -#elif ORTHANC_SANDBOXED == 1 - // Sandboxed Orthanc, no access to the executable - -#else -#error Support your platform here -#endif - - -#if ORTHANC_SANDBOXED == 0 - std::string Toolbox::GetPathToExecutable() - { - boost::filesystem::path p(GetPathToExecutableInternal()); - return boost::filesystem::absolute(p).string(); - } - - - std::string Toolbox::GetDirectoryOfExecutable() - { - boost::filesystem::path p(GetPathToExecutableInternal()); - return boost::filesystem::absolute(p.parent_path()).string(); - } -#endif - - static const char* GetBoostLocaleEncoding(const Encoding sourceEncoding) { switch (sourceEncoding) @@ -997,30 +698,6 @@ } -#if BOOST_HAS_DATE_TIME == 1 - std::string Toolbox::GetNowIsoString() - { - boost::posix_time::ptime now = boost::posix_time::second_clock::local_time(); - return boost::posix_time::to_iso_string(now); - } - - void Toolbox::GetNowDicom(std::string& date, - std::string& time) - { - boost::posix_time::ptime now = boost::posix_time::second_clock::local_time(); - tm tm = boost::posix_time::to_tm(now); - - char s[32]; - sprintf(s, "%04d%02d%02d", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); - date.assign(s); - - // TODO milliseconds - sprintf(s, "%02d%02d%02d.%06d", tm.tm_hour, tm.tm_min, tm.tm_sec, 0); - time.assign(s); - } -#endif - - std::string Toolbox::StripSpaces(const std::string& source) { size_t first = 0; @@ -1178,35 +855,6 @@ } -#if ORTHANC_SANDBOXED == 0 - void Toolbox::MakeDirectory(const std::string& path) - { - if (boost::filesystem::exists(path)) - { - if (!boost::filesystem::is_directory(path)) - { - throw OrthancException(ErrorCode_DirectoryOverFile); - } - } - else - { - if (!boost::filesystem::create_directories(path)) - { - throw OrthancException(ErrorCode_MakeDirectory); - } - } - } -#endif - - -#if ORTHANC_SANDBOXED == 0 - bool Toolbox::IsExistingFile(const std::string& path) - { - return boost::filesystem::exists(path); - } -#endif - - #if ORTHANC_ENABLE_PUGIXML == 1 class ChunkedBufferWriter : public pugi::xml_writer { @@ -1329,66 +977,6 @@ #endif -#if ORTHANC_SANDBOXED == 0 - void Toolbox::ExecuteSystemCommand(const std::string& command, - const std::vector& arguments) - { - // Convert the arguments as a C array - std::vector args(arguments.size() + 2); - - args.front() = const_cast(command.c_str()); - - for (size_t i = 0; i < arguments.size(); i++) - { - args[i + 1] = const_cast(arguments[i].c_str()); - } - - args.back() = NULL; - - int status; - -#if defined(_WIN32) - // http://msdn.microsoft.com/en-us/library/275khfab.aspx - status = static_cast(_spawnvp(_P_OVERLAY, command.c_str(), &args[0])); - -#else - int pid = fork(); - - if (pid == -1) - { - // Error in fork() -#if ORTHANC_ENABLE_LOGGING == 1 - LOG(ERROR) << "Cannot fork a child process"; -#endif - - throw OrthancException(ErrorCode_SystemCommand); - } - else if (pid == 0) - { - // Execute the system command in the child process - execvp(command.c_str(), &args[0]); - - // We should never get here - _exit(1); - } - else - { - // Wait for the system command to exit - waitpid(pid, &status, 0); - } -#endif - - if (status != 0) - { -#if ORTHANC_ENABLE_LOGGING == 1 - LOG(ERROR) << "System command failed with status code " << status; -#endif - - throw OrthancException(ErrorCode_SystemCommand); - } - } -#endif - bool Toolbox::IsInteger(const std::string& str) { @@ -1496,68 +1084,6 @@ return str.compare(0, prefix.size(), prefix) == 0; } } - - -#if ORTHANC_SANDBOXED == 0 - int Toolbox::GetProcessId() - { -#if defined(_WIN32) - return static_cast(_getpid()); -#else - return static_cast(getpid()); -#endif - } -#endif - - -#if ORTHANC_SANDBOXED == 0 - bool Toolbox::IsRegularFile(const std::string& path) - { - namespace fs = boost::filesystem; - - try - { - if (fs::exists(path)) - { - fs::file_status status = fs::status(path); - return (status.type() == boost::filesystem::regular_file || - status.type() == boost::filesystem::reparse_file); // Fix BitBucket issue #11 - } - } - catch (fs::filesystem_error&) - { - } - - return false; - } -#endif - - - FILE* Toolbox::OpenFile(const std::string& path, - FileMode mode) - { -#if defined(_WIN32) - // TODO Deal with special characters by converting to the current locale -#endif - - const char* m; - switch (mode) - { - case FileMode_ReadBinary: - m = "rb"; - break; - - case FileMode_WriteBinary: - m = "wb"; - break; - - default: - throw OrthancException(ErrorCode_ParameterOutOfRange); - } - - return fopen(path.c_str(), m); - } - static bool IsUnreservedCharacter(char c) @@ -1698,4 +1224,73 @@ return static_cast(v); } } + + + std::string Toolbox::GenerateUuid() + { +#ifdef WIN32 + UUID uuid; + UuidCreate ( &uuid ); + + unsigned char * str; + UuidToStringA ( &uuid, &str ); + + std::string s( ( char* ) str ); + + RpcStringFreeA ( &str ); +#else + uuid_t uuid; + uuid_generate_random ( uuid ); + char s[37]; + uuid_unparse ( uuid, s ); +#endif + return s; + } + + + bool Toolbox::IsUuid(const std::string& str) + { + if (str.size() != 36) + { + return false; + } + + for (size_t i = 0; i < str.length(); i++) + { + if (i == 8 || i == 13 || i == 18 || i == 23) + { + if (str[i] != '-') + return false; + } + else + { + if (!isalnum(str[i])) + return false; + } + } + + return true; + } + + + bool Toolbox::StartsWithUuid(const std::string& str) + { + if (str.size() < 36) + { + return false; + } + + if (str.size() == 36) + { + return IsUuid(str); + } + + assert(str.size() > 36); + if (!isspace(str[36])) + { + return false; + } + + return IsUuid(str.substr(0, 36)); + } } diff -r 8189115c0da5 -r 1a90184363f8 Orthanc/Core/Toolbox.h --- a/Orthanc/Core/Toolbox.h Wed Nov 09 15:31:51 2016 +0100 +++ b/Orthanc/Core/Toolbox.h Wed Nov 09 17:24:53 2016 +0100 @@ -56,9 +56,17 @@ # error The macro BOOST_HAS_REGEX must be defined #endif -#if !defined(ORTHANC_SANDBOXED) -# define ORTHANC_SANDBOXED 0 -#endif + +/** + * NOTE: GUID vs. UUID + * The simple answer is: no difference, they are the same thing. Treat + * them as a 16 byte (128 bits) value that is used as a unique + * value. In Microsoft-speak they are called GUIDs, but call them + * UUIDs when not using Microsoft-speak. + * http://stackoverflow.com/questions/246930/is-there-any-difference-between-a-guid-and-a-uuid + **/ + + namespace Orthanc { @@ -72,12 +80,6 @@ { void USleep(uint64_t microSeconds); -#if ORTHANC_SANDBOXED == 0 - ServerBarrierEvent ServerBarrier(const bool& stopFlag); - - ServerBarrierEvent ServerBarrier(); -#endif - void ToUpperCase(std::string& s); // Inplace version void ToLowerCase(std::string& s); // Inplace version @@ -88,24 +90,6 @@ void ToLowerCase(std::string& result, const std::string& source); -#if ORTHANC_SANDBOXED == 0 - void ReadFile(std::string& content, - const std::string& path); - - bool ReadHeader(std::string& header, - const std::string& path, - size_t headerSize); - - void WriteFile(const std::string& content, - const std::string& path); - - void WriteFile(const void* content, - size_t size, - const std::string& path); - - void RemoveFile(const std::string& path); -#endif - void SplitUriComponents(UriComponents& components, const std::string& uri); @@ -121,10 +105,6 @@ std::string FlattenUri(const UriComponents& components, size_t fromLevel = 0); -#if ORTHANC_SANDBOXED == 0 - uint64_t GetFileSize(const std::string& path); -#endif - #if ORTHANC_ENABLE_MD5 == 1 void ComputeMD5(std::string& result, const std::string& data); @@ -164,12 +144,6 @@ const std::string& content); #endif -#if ORTHANC_SANDBOXED == 0 - std::string GetPathToExecutable(); - - std::string GetDirectoryOfExecutable(); -#endif - std::string ConvertToUtf8(const std::string& source, Encoding sourceEncoding); @@ -183,13 +157,6 @@ std::string StripSpaces(const std::string& source); -#if BOOST_HAS_DATE_TIME == 1 - std::string GetNowIsoString(); - - void GetNowDicom(std::string& date, - std::string& time); -#endif - // In-place percent-decoding for URL void UrlDecode(std::string& s); @@ -203,12 +170,6 @@ const std::string& source, char separator); -#if ORTHANC_SANDBOXED == 0 - void MakeDirectory(const std::string& path); - - bool IsExistingFile(const std::string& path); -#endif - #if ORTHANC_ENABLE_PUGIXML == 1 void JsonToXml(std::string& target, const Json::Value& source, @@ -216,11 +177,6 @@ const std::string& arrayElement = "item"); #endif -#if ORTHANC_SANDBOXED == 0 - void ExecuteSystemCommand(const std::string& command, - const std::vector& arguments); -#endif - bool IsInteger(const std::string& str); void CopyJsonWithoutComments(Json::Value& target, @@ -229,15 +185,6 @@ bool StartsWith(const std::string& str, const std::string& prefix); -#if ORTHANC_SANDBOXED == 0 - int GetProcessId(); - - bool IsRegularFile(const std::string& path); -#endif - - FILE* OpenFile(const std::string& path, - FileMode mode); - void UriEncode(std::string& target, const std::string& source); @@ -256,5 +203,11 @@ unsigned int GetJsonUnsignedIntegerField(const ::Json::Value& json, const std::string& key, unsigned int defaultValue); + + std::string GenerateUuid(); + + bool IsUuid(const std::string& str); + + bool StartsWithUuid(const std::string& str); } } diff -r 8189115c0da5 -r 1a90184363f8 Orthanc/Core/Uuid.cpp --- a/Orthanc/Core/Uuid.cpp Wed Nov 09 15:31:51 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,162 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, 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 . - **/ - - -#include "PrecompiledHeaders.h" -#include "Uuid.h" - -// http://stackoverflow.com/a/1626302 - -extern "C" -{ -#ifdef WIN32 -#include -#else -#include -#endif -} - -#include - -namespace Orthanc -{ - namespace Toolbox - { - std::string GenerateUuid() - { -#ifdef WIN32 - UUID uuid; - UuidCreate ( &uuid ); - - unsigned char * str; - UuidToStringA ( &uuid, &str ); - - std::string s( ( char* ) str ); - - RpcStringFreeA ( &str ); -#else - uuid_t uuid; - uuid_generate_random ( uuid ); - char s[37]; - uuid_unparse ( uuid, s ); -#endif - return s; - } - - - bool IsUuid(const std::string& str) - { - if (str.size() != 36) - { - return false; - } - - for (size_t i = 0; i < str.length(); i++) - { - if (i == 8 || i == 13 || i == 18 || i == 23) - { - if (str[i] != '-') - return false; - } - else - { - if (!isalnum(str[i])) - return false; - } - } - - return true; - } - - - bool StartsWithUuid(const std::string& str) - { - if (str.size() < 36) - { - return false; - } - - if (str.size() == 36) - { - return IsUuid(str); - } - - assert(str.size() > 36); - if (!isspace(str[36])) - { - return false; - } - - return IsUuid(str.substr(0, 36)); - } - - - static std::string CreateTemporaryPath(const char* extension) - { -#if BOOST_HAS_FILESYSTEM_V3 == 1 - boost::filesystem::path tmpDir = boost::filesystem::temp_directory_path(); -#elif defined(__linux__) - boost::filesystem::path tmpDir("/tmp"); -#else -#error Support your platform here -#endif - - // We use UUID to create unique path to temporary files - std::string filename = "Orthanc-" + Orthanc::Toolbox::GenerateUuid(); - - if (extension != NULL) - { - filename.append(extension); - } - - tmpDir /= filename; - return tmpDir.string(); - } - - - TemporaryFile::TemporaryFile() : - path_(CreateTemporaryPath(NULL)) - { - } - - - TemporaryFile::TemporaryFile(const char* extension) : - path_(CreateTemporaryPath(extension)) - { - } - - - TemporaryFile::~TemporaryFile() - { - boost::filesystem::remove(path_); - } - } -} diff -r 8189115c0da5 -r 1a90184363f8 Orthanc/Core/Uuid.h --- a/Orthanc/Core/Uuid.h Wed Nov 09 15:31:51 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,86 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, 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 . - **/ - - -#pragma once - -#include - -/** - * GUID vs. UUID - * The simple answer is: no difference, they are the same thing. Treat - * them as a 16 byte (128 bits) value that is used as a unique - * value. In Microsoft-speak they are called GUIDs, but call them - * UUIDs when not using Microsoft-speak. - * http://stackoverflow.com/questions/246930/is-there-any-difference-between-a-guid-and-a-uuid - **/ - -#include "Toolbox.h" - -namespace Orthanc -{ - namespace Toolbox - { - std::string GenerateUuid(); - - bool IsUuid(const std::string& str); - - bool StartsWithUuid(const std::string& str); - - class TemporaryFile - { - private: - std::string path_; - - public: - TemporaryFile(); - - TemporaryFile(const char* extension); - - ~TemporaryFile(); - - const std::string& GetPath() const - { - return path_; - } - - void Write(const std::string& content) - { - Toolbox::WriteFile(content, path_); - } - - void Read(std::string& content) const - { - Toolbox::ReadFile(content, path_); - } - }; - } -} diff -r 8189115c0da5 -r 1a90184363f8 Orthanc/Resources/CMake/BoostConfiguration.cmake --- a/Orthanc/Resources/CMake/BoostConfiguration.cmake Wed Nov 09 15:31:51 2016 +0100 +++ b/Orthanc/Resources/CMake/BoostConfiguration.cmake Wed Nov 09 17:24:53 2016 +0100 @@ -125,10 +125,11 @@ add_definitions( -DBOOST_HAS_FILESYSTEM_V3=0 -D__INTEGRITY=1 - -DORTHANC_SANDBOXED=1 ) else() - add_definitions(-DBOOST_HAS_FILESYSTEM_V3=1) + add_definitions( + -DBOOST_HAS_FILESYSTEM_V3=1 + ) list(APPEND BOOST_SOURCES ${BOOST_FILESYSTEM_SOURCES_DIR}/codecvt_error_category.cpp ${BOOST_FILESYSTEM_SOURCES_DIR}/operations.cpp diff -r 8189115c0da5 -r 1a90184363f8 Plugin/Cache/CacheManager.cpp --- a/Plugin/Cache/CacheManager.cpp Wed Nov 09 15:31:51 2016 +0100 +++ b/Plugin/Cache/CacheManager.cpp Wed Nov 09 17:24:53 2016 +0100 @@ -20,7 +20,7 @@ #include "CacheManager.h" -#include "../../Orthanc/Core/Uuid.h" +#include "../../Orthanc/Core/Toolbox.h" #include "../../Orthanc/Core/SQLite/Transaction.h" #include diff -r 8189115c0da5 -r 1a90184363f8 Resources/SyncOrthancFolder.py --- a/Resources/SyncOrthancFolder.py Wed Nov 09 15:31:51 2016 +0100 +++ b/Resources/SyncOrthancFolder.py Wed Nov 09 17:24:53 2016 +0100 @@ -58,10 +58,10 @@ 'Core/SQLite/StatementReference.h', 'Core/SQLite/Transaction.cpp', 'Core/SQLite/Transaction.h', + 'Core/SystemToolbox.cpp', + 'Core/SystemToolbox.h', 'Core/Toolbox.cpp', 'Core/Toolbox.h', - 'Core/Uuid.cpp', - 'Core/Uuid.h', 'Plugins/Samples/Common/ExportedSymbols.list', 'Plugins/Samples/Common/VersionScript.map', 'Plugins/Samples/GdcmDecoder/README', diff -r 8189115c0da5 -r 1a90184363f8 UnitTestsSources/UnitTestsMain.cpp --- a/UnitTestsSources/UnitTestsMain.cpp Wed Nov 09 15:31:51 2016 +0100 +++ b/UnitTestsSources/UnitTestsMain.cpp Wed Nov 09 17:24:53 2016 +0100 @@ -25,7 +25,7 @@ static char** argv_; #include "../Orthanc/Core/OrthancException.h" -#include "../Orthanc/Core/Toolbox.h" +#include "../Orthanc/Core/SystemToolbox.h" #include "../Plugin/Cache/CacheManager.h" #include "../Plugin/Cache/CacheScheduler.h" #include "../Plugin/Cache/ICacheFactory.h" @@ -46,7 +46,7 @@ { storage_.reset(new Orthanc::FilesystemStorage("UnitTestsResults")); storage_->Clear(); - Orthanc::Toolbox::RemoveFile("UnitTestsResults/cache.db"); + Orthanc::SystemToolbox::RemoveFile("UnitTestsResults/cache.db"); db_.reset(new Orthanc::SQLite::Connection()); db_->Open("UnitTestsResults/cache.db");