# HG changeset patch # User Sebastien Jodogne # Date 1433152348 -7200 # Node ID 7a0af291cc90d07ce72e8d6a92508442c54eebc9 # Parent b42eeb4bd1e307ebbfd5f2166279dfecfc135da6 Synchronization with Orthanc mainline diff -r b42eeb4bd1e3 -r 7a0af291cc90 CMakeLists.txt --- a/CMakeLists.txt Fri May 29 11:07:38 2015 +0200 +++ b/CMakeLists.txt Mon Jun 01 11:52:28 2015 +0200 @@ -132,6 +132,7 @@ ${CMAKE_SOURCE_DIR}/Orthanc/Toolbox.cpp ${CMAKE_SOURCE_DIR}/Orthanc/Uuid.cpp ${CMAKE_SOURCE_DIR}/Resources/ThirdParty/base64/base64.cpp + ${CMAKE_SOURCE_DIR}/Resources/ThirdParty/md5/md5.c ${CMAKE_SOURCE_DIR}/Plugin/Cache/CacheManager.cpp ${CMAKE_SOURCE_DIR}/Plugin/Cache/CacheScheduler.cpp diff -r b42eeb4bd1e3 -r 7a0af291cc90 Orthanc/Enumerations.cpp --- a/Orthanc/Enumerations.cpp Fri May 29 11:07:38 2015 +0200 +++ b/Orthanc/Enumerations.cpp Mon Jun 01 11:52:28 2015 +0200 @@ -34,9 +34,465 @@ #include "Enumerations.h" #include "OrthancException.h" +#include "Toolbox.h" namespace Orthanc { + const char* EnumerationToString(HttpMethod method) + { + switch (method) + { + case HttpMethod_Get: + return "GET"; + + case HttpMethod_Post: + return "POST"; + + case HttpMethod_Delete: + return "DELETE"; + + case HttpMethod_Put: + return "PUT"; + + default: + return "?"; + } + } + + + const char* EnumerationToString(HttpStatus status) + { + switch (status) + { + case HttpStatus_100_Continue: + return "Continue"; + + case HttpStatus_101_SwitchingProtocols: + return "Switching Protocols"; + + case HttpStatus_102_Processing: + return "Processing"; + + case HttpStatus_200_Ok: + return "OK"; + + case HttpStatus_201_Created: + return "Created"; + + case HttpStatus_202_Accepted: + return "Accepted"; + + case HttpStatus_203_NonAuthoritativeInformation: + return "Non-Authoritative Information"; + + case HttpStatus_204_NoContent: + return "No Content"; + + case HttpStatus_205_ResetContent: + return "Reset Content"; + + case HttpStatus_206_PartialContent: + return "Partial Content"; + + case HttpStatus_207_MultiStatus: + return "Multi-Status"; + + case HttpStatus_208_AlreadyReported: + return "Already Reported"; + + case HttpStatus_226_IMUsed: + return "IM Used"; + + case HttpStatus_300_MultipleChoices: + return "Multiple Choices"; + + case HttpStatus_301_MovedPermanently: + return "Moved Permanently"; + + case HttpStatus_302_Found: + return "Found"; + + case HttpStatus_303_SeeOther: + return "See Other"; + + case HttpStatus_304_NotModified: + return "Not Modified"; + + case HttpStatus_305_UseProxy: + return "Use Proxy"; + + case HttpStatus_307_TemporaryRedirect: + return "Temporary Redirect"; + + case HttpStatus_400_BadRequest: + return "Bad Request"; + + case HttpStatus_401_Unauthorized: + return "Unauthorized"; + + case HttpStatus_402_PaymentRequired: + return "Payment Required"; + + case HttpStatus_403_Forbidden: + return "Forbidden"; + + case HttpStatus_404_NotFound: + return "Not Found"; + + case HttpStatus_405_MethodNotAllowed: + return "Method Not Allowed"; + + case HttpStatus_406_NotAcceptable: + return "Not Acceptable"; + + case HttpStatus_407_ProxyAuthenticationRequired: + return "Proxy Authentication Required"; + + case HttpStatus_408_RequestTimeout: + return "Request Timeout"; + + case HttpStatus_409_Conflict: + return "Conflict"; + + case HttpStatus_410_Gone: + return "Gone"; + + case HttpStatus_411_LengthRequired: + return "Length Required"; + + case HttpStatus_412_PreconditionFailed: + return "Precondition Failed"; + + case HttpStatus_413_RequestEntityTooLarge: + return "Request Entity Too Large"; + + case HttpStatus_414_RequestUriTooLong: + return "Request-URI Too Long"; + + case HttpStatus_415_UnsupportedMediaType: + return "Unsupported Media Type"; + + case HttpStatus_416_RequestedRangeNotSatisfiable: + return "Requested Range Not Satisfiable"; + + case HttpStatus_417_ExpectationFailed: + return "Expectation Failed"; + + case HttpStatus_422_UnprocessableEntity: + return "Unprocessable Entity"; + + case HttpStatus_423_Locked: + return "Locked"; + + case HttpStatus_424_FailedDependency: + return "Failed Dependency"; + + case HttpStatus_426_UpgradeRequired: + return "Upgrade Required"; + + case HttpStatus_500_InternalServerError: + return "Internal Server Error"; + + case HttpStatus_501_NotImplemented: + return "Not Implemented"; + + case HttpStatus_502_BadGateway: + return "Bad Gateway"; + + case HttpStatus_503_ServiceUnavailable: + return "Service Unavailable"; + + case HttpStatus_504_GatewayTimeout: + return "Gateway Timeout"; + + case HttpStatus_505_HttpVersionNotSupported: + return "HTTP Version Not Supported"; + + case HttpStatus_506_VariantAlsoNegotiates: + return "Variant Also Negotiates"; + + case HttpStatus_507_InsufficientStorage: + return "Insufficient Storage"; + + case HttpStatus_509_BandwidthLimitExceeded: + return "Bandwidth Limit Exceeded"; + + case HttpStatus_510_NotExtended: + return "Not Extended"; + + default: + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + } + + + const char* EnumerationToString(ResourceType type) + { + switch (type) + { + case ResourceType_Patient: + return "Patient"; + + case ResourceType_Study: + return "Study"; + + case ResourceType_Series: + return "Series"; + + case ResourceType_Instance: + return "Instance"; + + default: + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + } + + + const char* EnumerationToString(ImageFormat format) + { + switch (format) + { + case ImageFormat_Png: + return "Png"; + + default: + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + } + + + const char* EnumerationToString(Encoding encoding) + { + switch (encoding) + { + case Encoding_Ascii: + return "Ascii"; + + case Encoding_Utf8: + return "Utf8"; + + case Encoding_Latin1: + return "Latin1"; + + case Encoding_Latin2: + return "Latin2"; + + case Encoding_Latin3: + return "Latin3"; + + case Encoding_Latin4: + return "Latin4"; + + case Encoding_Latin5: + return "Latin5"; + + case Encoding_Cyrillic: + return "Cyrillic"; + + case Encoding_Windows1251: + return "Windows1251"; + + case Encoding_Arabic: + return "Arabic"; + + case Encoding_Greek: + return "Greek"; + + case Encoding_Hebrew: + return "Hebrew"; + + case Encoding_Thai: + return "Thai"; + + case Encoding_Japanese: + return "Japanese"; + + case Encoding_Chinese: + return "Chinese"; + + default: + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + } + + + const char* EnumerationToString(PhotometricInterpretation photometric) + { + switch (photometric) + { + case PhotometricInterpretation_RGB: + return "RGB"; + + case PhotometricInterpretation_Monochrome1: + return "Monochrome1"; + + case PhotometricInterpretation_Monochrome2: + return "Monochrome2"; + + case PhotometricInterpretation_ARGB: + return "ARGB"; + + case PhotometricInterpretation_CMYK: + return "CMYK"; + + case PhotometricInterpretation_HSV: + return "HSV"; + + case PhotometricInterpretation_Palette: + return "Palette color"; + + case PhotometricInterpretation_YBRFull: + return "YBR full"; + + case PhotometricInterpretation_YBRFull422: + return "YBR full 422"; + + case PhotometricInterpretation_YBRPartial420: + return "YBR partial 420"; + + case PhotometricInterpretation_YBRPartial422: + return "YBR partial 422"; + + case PhotometricInterpretation_YBR_ICT: + return "YBR ICT"; + + case PhotometricInterpretation_YBR_RCT: + return "YBR RCT"; + + case PhotometricInterpretation_Unknown: + return "Unknown"; + + default: + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + } + + + Encoding StringToEncoding(const char* encoding) + { + std::string s(encoding); + Toolbox::ToUpperCase(s); + + if (s == "UTF8") + { + return Encoding_Utf8; + } + + if (s == "ASCII") + { + return Encoding_Ascii; + } + + if (s == "LATIN1") + { + return Encoding_Latin1; + } + + if (s == "LATIN2") + { + return Encoding_Latin2; + } + + if (s == "LATIN3") + { + return Encoding_Latin3; + } + + if (s == "LATIN4") + { + return Encoding_Latin4; + } + + if (s == "LATIN5") + { + return Encoding_Latin5; + } + + if (s == "CYRILLIC") + { + return Encoding_Cyrillic; + } + + if (s == "WINDOWS1251") + { + return Encoding_Windows1251; + } + + if (s == "ARABIC") + { + return Encoding_Arabic; + } + + if (s == "GREEK") + { + return Encoding_Greek; + } + + if (s == "HEBREW") + { + return Encoding_Hebrew; + } + + if (s == "THAI") + { + return Encoding_Thai; + } + + if (s == "JAPANESE") + { + return Encoding_Japanese; + } + + if (s == "CHINESE") + { + return Encoding_Chinese; + } + + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + + + ResourceType StringToResourceType(const char* type) + { + std::string s(type); + Toolbox::ToUpperCase(s); + + if (s == "PATIENT" || s == "PATIENTS") + { + return ResourceType_Patient; + } + else if (s == "STUDY" || s == "STUDIES") + { + return ResourceType_Study; + } + else if (s == "SERIES") + { + return ResourceType_Series; + } + else if (s == "INSTANCE" || s == "IMAGE" || + s == "INSTANCES" || s == "IMAGES") + { + return ResourceType_Instance; + } + + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + + + ImageFormat StringToImageFormat(const char* format) + { + std::string s(format); + Toolbox::ToUpperCase(s); + + if (s == "PNG") + { + return ImageFormat_Png; + } + + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + + unsigned int GetBytesPerPixel(PixelFormat format) { switch (format) @@ -58,4 +514,115 @@ throw OrthancException(ErrorCode_ParameterOutOfRange); } } + + + bool GetDicomEncoding(Encoding& encoding, + const char* specificCharacterSet) + { + std::string s = specificCharacterSet; + Toolbox::ToUpperCase(s); + + // http://www.dabsoft.ch/dicom/3/C.12.1.1.2/ + // https://github.com/dcm4che/dcm4che/blob/master/dcm4che-core/src/main/java/org/dcm4che3/data/SpecificCharacterSet.java + if (s == "ISO_IR 6" || + s == "ISO_IR 192" || + s == "ISO 2022 IR 6") + { + encoding = Encoding_Utf8; + } + else if (s == "ISO_IR 100" || + s == "ISO 2022 IR 100") + { + encoding = Encoding_Latin1; + } + else if (s == "ISO_IR 101" || + s == "ISO 2022 IR 101") + { + encoding = Encoding_Latin2; + } + else if (s == "ISO_IR 109" || + s == "ISO 2022 IR 109") + { + encoding = Encoding_Latin3; + } + else if (s == "ISO_IR 110" || + s == "ISO 2022 IR 110") + { + encoding = Encoding_Latin4; + } + else if (s == "ISO_IR 148" || + s == "ISO 2022 IR 148") + { + encoding = Encoding_Latin5; + } + else if (s == "ISO_IR 144" || + s == "ISO 2022 IR 144") + { + encoding = Encoding_Cyrillic; + } + else if (s == "ISO_IR 127" || + s == "ISO 2022 IR 127") + { + encoding = Encoding_Arabic; + } + else if (s == "ISO_IR 126" || + s == "ISO 2022 IR 126") + { + encoding = Encoding_Greek; + } + else if (s == "ISO_IR 138" || + s == "ISO 2022 IR 138") + { + encoding = Encoding_Hebrew; + } + else if (s == "ISO_IR 166" || s == "ISO 2022 IR 166") + { + encoding = Encoding_Thai; + } + else if (s == "ISO_IR 13" || s == "ISO 2022 IR 13") + { + encoding = Encoding_Japanese; + } + else if (s == "GB18030") + { + encoding = Encoding_Chinese; + } + /* + else if (s == "ISO 2022 IR 149") + { + TODO + } + else if (s == "ISO 2022 IR 159") + { + TODO + } + else if (s == "ISO 2022 IR 87") + { + TODO + } + */ + else + { + return false; + } + + // The encoding was properly detected + return true; + } + + + const char* GetMimeType(FileContentType type) + { + switch (type) + { + case FileContentType_Dicom: + return "application/dicom"; + + case FileContentType_DicomAsJson: + return "application/json"; + + default: + return "application/octet-stream"; + } + } } diff -r b42eeb4bd1e3 -r 7a0af291cc90 Orthanc/Enumerations.h --- a/Orthanc/Enumerations.h Fri May 29 11:07:38 2015 +0200 +++ b/Orthanc/Enumerations.h Mon Jun 01 11:52:28 2015 +0200 @@ -76,6 +76,7 @@ ErrorCode_Plugin }; + /** * {summary}{The memory layout of the pixels (resp. voxels) of a 2D (resp. 3D) image.} **/ @@ -115,5 +116,230 @@ }; + /** + * {summary}{The extraction mode specifies the way the values of the pixels are scaled when downloading a 2D image.} + **/ + enum ImageExtractionMode + { + /** + * {summary}{Rescaled to 8bpp.} + * {description}{The minimum value of the image is set to 0, and its maximum value is set to 255.} + **/ + ImageExtractionMode_Preview = 1, + + /** + * {summary}{Truncation to the [0, 255] range.} + **/ + ImageExtractionMode_UInt8 = 2, + + /** + * {summary}{Truncation to the [0, 65535] range.} + **/ + ImageExtractionMode_UInt16 = 3, + + /** + * {summary}{Truncation to the [-32768, 32767] range.} + **/ + ImageExtractionMode_Int16 = 4 + }; + + + /** + * Most common, non-joke and non-experimental HTTP status codes + * http://en.wikipedia.org/wiki/List_of_HTTP_status_codes + **/ + enum HttpStatus + { + HttpStatus_None = -1, + + // 1xx Informational + HttpStatus_100_Continue = 100, + HttpStatus_101_SwitchingProtocols = 101, + HttpStatus_102_Processing = 102, + + // 2xx Success + HttpStatus_200_Ok = 200, + HttpStatus_201_Created = 201, + HttpStatus_202_Accepted = 202, + HttpStatus_203_NonAuthoritativeInformation = 203, + HttpStatus_204_NoContent = 204, + HttpStatus_205_ResetContent = 205, + HttpStatus_206_PartialContent = 206, + HttpStatus_207_MultiStatus = 207, + HttpStatus_208_AlreadyReported = 208, + HttpStatus_226_IMUsed = 226, + + // 3xx Redirection + HttpStatus_300_MultipleChoices = 300, + HttpStatus_301_MovedPermanently = 301, + HttpStatus_302_Found = 302, + HttpStatus_303_SeeOther = 303, + HttpStatus_304_NotModified = 304, + HttpStatus_305_UseProxy = 305, + HttpStatus_307_TemporaryRedirect = 307, + + // 4xx Client Error + HttpStatus_400_BadRequest = 400, + HttpStatus_401_Unauthorized = 401, + HttpStatus_402_PaymentRequired = 402, + HttpStatus_403_Forbidden = 403, + HttpStatus_404_NotFound = 404, + HttpStatus_405_MethodNotAllowed = 405, + HttpStatus_406_NotAcceptable = 406, + HttpStatus_407_ProxyAuthenticationRequired = 407, + HttpStatus_408_RequestTimeout = 408, + HttpStatus_409_Conflict = 409, + HttpStatus_410_Gone = 410, + HttpStatus_411_LengthRequired = 411, + HttpStatus_412_PreconditionFailed = 412, + HttpStatus_413_RequestEntityTooLarge = 413, + HttpStatus_414_RequestUriTooLong = 414, + HttpStatus_415_UnsupportedMediaType = 415, + HttpStatus_416_RequestedRangeNotSatisfiable = 416, + HttpStatus_417_ExpectationFailed = 417, + HttpStatus_422_UnprocessableEntity = 422, + HttpStatus_423_Locked = 423, + HttpStatus_424_FailedDependency = 424, + HttpStatus_426_UpgradeRequired = 426, + + // 5xx Server Error + HttpStatus_500_InternalServerError = 500, + HttpStatus_501_NotImplemented = 501, + HttpStatus_502_BadGateway = 502, + HttpStatus_503_ServiceUnavailable = 503, + HttpStatus_504_GatewayTimeout = 504, + HttpStatus_505_HttpVersionNotSupported = 505, + HttpStatus_506_VariantAlsoNegotiates = 506, + HttpStatus_507_InsufficientStorage = 507, + HttpStatus_509_BandwidthLimitExceeded = 509, + HttpStatus_510_NotExtended = 510 + }; + + + enum HttpMethod + { + HttpMethod_Get = 0, + HttpMethod_Post = 1, + HttpMethod_Delete = 2, + HttpMethod_Put = 3 + }; + + + enum ImageFormat + { + ImageFormat_Png = 1 + }; + + + // http://www.dabsoft.ch/dicom/3/C.12.1.1.2/ + enum Encoding + { + Encoding_Ascii, + Encoding_Utf8, + Encoding_Latin1, + Encoding_Latin2, + Encoding_Latin3, + Encoding_Latin4, + Encoding_Latin5, // Turkish + Encoding_Cyrillic, + Encoding_Windows1251, // Windows-1251 (commonly used for Cyrillic) + Encoding_Arabic, + Encoding_Greek, + Encoding_Hebrew, + Encoding_Thai, // TIS 620-2533 + Encoding_Japanese, // JIS X 0201 (Shift JIS): Katakana + Encoding_Chinese // GB18030 - Chinese simplified + //Encoding_JapaneseKanji, // Multibyte - JIS X 0208: Kanji + //Encoding_JapaneseSupplementaryKanji, // Multibyte - JIS X 0212: Supplementary Kanji set + //Encoding_Korean, // Multibyte - KS X 1001: Hangul and Hanja + }; + + + // https://www.dabsoft.ch/dicom/3/C.7.6.3.1.2/ + enum PhotometricInterpretation + { + PhotometricInterpretation_ARGB, // Retired + PhotometricInterpretation_CMYK, // Retired + PhotometricInterpretation_HSV, // Retired + PhotometricInterpretation_Monochrome1, + PhotometricInterpretation_Monochrome2, + PhotometricInterpretation_Palette, + PhotometricInterpretation_RGB, + PhotometricInterpretation_YBRFull, + PhotometricInterpretation_YBRFull422, + PhotometricInterpretation_YBRPartial420, + PhotometricInterpretation_YBRPartial422, + PhotometricInterpretation_YBR_ICT, + PhotometricInterpretation_YBR_RCT, + PhotometricInterpretation_Unknown + }; + + enum DicomModule + { + DicomModule_Patient, + DicomModule_Study, + DicomModule_Series, + DicomModule_Instance, + DicomModule_Image + }; + + + /** + * WARNING: Do not change the explicit values in the enumerations + * below this point. This would result in incompatible databases + * between versions of Orthanc! + **/ + + enum CompressionType + { + CompressionType_None = 1, + CompressionType_Zlib = 2 + }; + + enum FileContentType + { + // If you add a value below, insert it in "PluginStorageArea" in + // the file "Plugins/Engine/OrthancPlugins.cpp" + FileContentType_Unknown = 0, + FileContentType_Dicom = 1, + FileContentType_DicomAsJson = 2, + + // Make sure that the value "65535" can be stored into this enumeration + FileContentType_StartUser = 1024, + FileContentType_EndUser = 65535 + }; + + enum ResourceType + { + ResourceType_Patient = 1, + ResourceType_Study = 2, + ResourceType_Series = 3, + ResourceType_Instance = 4 + }; + + + const char* EnumerationToString(HttpMethod method); + + const char* EnumerationToString(HttpStatus status); + + const char* EnumerationToString(ResourceType type); + + const char* EnumerationToString(ImageFormat format); + + const char* EnumerationToString(Encoding encoding); + + const char* EnumerationToString(PhotometricInterpretation photometric); + + Encoding StringToEncoding(const char* encoding); + + ResourceType StringToResourceType(const char* type); + + ImageFormat StringToImageFormat(const char* format); + unsigned int GetBytesPerPixel(PixelFormat format); + + bool GetDicomEncoding(Encoding& encoding, + const char* specificCharacterSet); + + const char* GetMimeType(FileContentType type); } diff -r b42eeb4bd1e3 -r 7a0af291cc90 Orthanc/FileStorage/FilesystemStorage.cpp --- a/Orthanc/FileStorage/FilesystemStorage.cpp Fri May 29 11:07:38 2015 +0200 +++ b/Orthanc/FileStorage/FilesystemStorage.cpp Mon Jun 01 11:52:28 2015 +0200 @@ -42,6 +42,10 @@ #include +#if HAVE_GOOGLE_LOG == 1 +#include +#endif + static std::string ToString(const boost::filesystem::path& p) { #if BOOST_HAS_FILESYSTEM_V3 == 1 @@ -81,12 +85,13 @@ //root_ = boost::filesystem::absolute(root).string(); root_ = root; - Toolbox::CreateNewDirectory(root); + Toolbox::MakeDirectory(root); } void FilesystemStorage::Create(const std::string& uuid, const void* content, - size_t size) + size_t size, + FileContentType /*type*/) { boost::filesystem::path path; @@ -136,7 +141,8 @@ void FilesystemStorage::Read(std::string& content, - const std::string& uuid) + const std::string& uuid, + FileContentType /*type*/) { content.clear(); Toolbox::ReadFile(content, GetPath(uuid).string()); @@ -201,13 +207,18 @@ for (List::const_iterator it = result.begin(); it != result.end(); ++it) { - Remove(*it); + Remove(*it, FileContentType_Unknown /*ignored in this class*/); } } - void FilesystemStorage::Remove(const std::string& uuid) + void FilesystemStorage::Remove(const std::string& uuid, + FileContentType /*type*/) { +#if HAVE_GOOGLE_LOG == 1 + LOG(INFO) << "Deleting file " << uuid; +#endif + namespace fs = boost::filesystem; fs::path p = GetPath(uuid); diff -r b42eeb4bd1e3 -r 7a0af291cc90 Orthanc/FileStorage/FilesystemStorage.h --- a/Orthanc/FileStorage/FilesystemStorage.h Fri May 29 11:07:38 2015 +0200 +++ b/Orthanc/FileStorage/FilesystemStorage.h Mon Jun 01 11:52:28 2015 +0200 @@ -32,14 +32,19 @@ #pragma once +#include "IStorageArea.h" + #include #include -#include namespace Orthanc { - class FilesystemStorage : public boost::noncopyable + class FilesystemStorage : public IStorageArea { + // TODO REMOVE THIS + friend class FilesystemHttpSender; + friend class FileStorageAccessor; + private: boost::filesystem::path root_; @@ -50,12 +55,15 @@ virtual void Create(const std::string& uuid, const void* content, - size_t size); + size_t size, + FileContentType type); virtual void Read(std::string& content, - const std::string& uuid); + const std::string& uuid, + FileContentType type); - virtual void Remove(const std::string& uuid); + virtual void Remove(const std::string& uuid, + FileContentType type); void ListAllFiles(std::set& result) const; diff -r b42eeb4bd1e3 -r 7a0af291cc90 Orthanc/FileStorage/IStorageArea.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Orthanc/FileStorage/IStorageArea.h Mon Jun 01 11:52:28 2015 +0200 @@ -0,0 +1,61 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2015 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 "../Enumerations.h" + +#include +#include + +namespace Orthanc +{ + class IStorageArea : public boost::noncopyable + { + public: + virtual ~IStorageArea() + { + } + + virtual void Create(const std::string& uuid, + const void* content, + size_t size, + FileContentType type) = 0; + + virtual void Read(std::string& content, + const std::string& uuid, + FileContentType type) = 0; + + virtual void Remove(const std::string& uuid, + FileContentType type) = 0; + }; +} diff -r b42eeb4bd1e3 -r 7a0af291cc90 Orthanc/ImageFormats/ImageAccessor.cpp --- a/Orthanc/ImageFormats/ImageAccessor.cpp Fri May 29 11:07:38 2015 +0200 +++ b/Orthanc/ImageFormats/ImageAccessor.cpp Mon Jun 01 11:52:28 2015 +0200 @@ -34,17 +34,84 @@ #include "ImageAccessor.h" #include "../OrthancException.h" +#include "../ChunkedBuffer.h" #include #include #include +#if HAVE_GOOGLE_LOG == 1 +#include +#endif + + namespace Orthanc { + template + static void ToMatlabStringInternal(ChunkedBuffer& target, + const ImageAccessor& source) + { + target.AddChunk("double([ "); + + for (unsigned int y = 0; y < source.GetHeight(); y++) + { + const PixelType* p = reinterpret_cast(source.GetConstRow(y)); + + std::string s; + if (y > 0) + { + s = "; "; + } + + s.reserve(source.GetWidth() * 8); + + for (unsigned int x = 0; x < source.GetWidth(); x++, p++) + { + s += boost::lexical_cast(static_cast(*p)) + " "; + } + + target.AddChunk(s); + } + + target.AddChunk("])"); + } + + + static void RGB24ToMatlabString(ChunkedBuffer& target, + const ImageAccessor& source) + { + assert(source.GetFormat() == PixelFormat_RGB24); + + target.AddChunk("double(permute(reshape([ "); + + for (unsigned int y = 0; y < source.GetHeight(); y++) + { + const uint8_t* p = reinterpret_cast(source.GetConstRow(y)); + + std::string s; + s.reserve(source.GetWidth() * 3 * 8); + + for (unsigned int x = 0; x < 3 * source.GetWidth(); x++, p++) + { + s += boost::lexical_cast(static_cast(*p)) + " "; + } + + target.AddChunk(s); + } + + target.AddChunk("], [ 3 " + boost::lexical_cast(source.GetHeight()) + + " " + boost::lexical_cast(source.GetWidth()) + " ]), [ 3 2 1 ]))"); + } + + void* ImageAccessor::GetBuffer() const { if (readOnly_) { +#if HAVE_GOOGLE_LOG == 1 + LOG(ERROR) << "Trying to write on a read-only image"; +#endif + throw OrthancException(ErrorCode_ReadOnly); } @@ -69,6 +136,10 @@ { if (readOnly_) { +#if HAVE_GOOGLE_LOG == 1 + LOG(ERROR) << "Trying to write on a read-only image"; +#endif + throw OrthancException(ErrorCode_ReadOnly); } @@ -126,4 +197,35 @@ assert(GetBytesPerPixel() * width_ <= pitch_); } + + + void ImageAccessor::ToMatlabString(std::string& target) const + { + ChunkedBuffer buffer; + + switch (GetFormat()) + { + case PixelFormat_Grayscale8: + ToMatlabStringInternal(buffer, *this); + break; + + case PixelFormat_Grayscale16: + ToMatlabStringInternal(buffer, *this); + break; + + case PixelFormat_SignedGrayscale16: + ToMatlabStringInternal(buffer, *this); + break; + + case PixelFormat_RGB24: + RGB24ToMatlabString(buffer, *this); + break; + + default: + throw OrthancException(ErrorCode_NotImplemented); + } + + buffer.Flatten(target); + } + } diff -r b42eeb4bd1e3 -r 7a0af291cc90 Orthanc/ImageFormats/ImageAccessor.h --- a/Orthanc/ImageFormats/ImageAccessor.h Fri May 29 11:07:38 2015 +0200 +++ b/Orthanc/ImageFormats/ImageAccessor.h Mon Jun 01 11:52:28 2015 +0200 @@ -34,6 +34,8 @@ #include "../Enumerations.h" +#include + namespace Orthanc { class ImageAccessor @@ -111,5 +113,7 @@ unsigned int height, unsigned int pitch, void *buffer); + + void ToMatlabString(std::string& target) const; }; } diff -r b42eeb4bd1e3 -r 7a0af291cc90 Orthanc/ImageFormats/ImageProcessing.cpp --- a/Orthanc/ImageFormats/ImageProcessing.cpp Fri May 29 11:07:38 2015 +0200 +++ b/Orthanc/ImageFormats/ImageProcessing.cpp Mon Jun 01 11:52:28 2015 +0200 @@ -209,7 +209,7 @@ void MultiplyConstantInternal(ImageAccessor& image, float factor) { - if (abs(factor - 1.0f) <= std::numeric_limits::epsilon()) + if (std::abs(factor - 1.0f) <= std::numeric_limits::epsilon()) { return; } diff -r b42eeb4bd1e3 -r 7a0af291cc90 Orthanc/MultiThreading/SharedMessageQueue.cpp --- a/Orthanc/MultiThreading/SharedMessageQueue.cpp Fri May 29 11:07:38 2015 +0200 +++ b/Orthanc/MultiThreading/SharedMessageQueue.cpp Mon Jun 01 11:52:28 2015 +0200 @@ -61,7 +61,6 @@ **/ - namespace Orthanc { SharedMessageQueue::SharedMessageQueue(unsigned int maxSize) : diff -r b42eeb4bd1e3 -r 7a0af291cc90 Orthanc/PrecompiledHeaders.h --- a/Orthanc/PrecompiledHeaders.h Fri May 29 11:07:38 2015 +0200 +++ b/Orthanc/PrecompiledHeaders.h Mon Jun 01 11:52:28 2015 +0200 @@ -38,12 +38,21 @@ #if ORTHANC_USE_PRECOMPILED_HEADERS == 1 +#include #include #include +#include +#include #include #include + +#include #include +#if ORTHANC_PUGIXML_ENABLED == 1 +#include +#endif + #include "Enumerations.h" #include "OrthancException.h" #include "Toolbox.h" diff -r b42eeb4bd1e3 -r 7a0af291cc90 Orthanc/README.txt --- a/Orthanc/README.txt Fri May 29 11:07:38 2015 +0200 +++ b/Orthanc/README.txt Mon Jun 01 11:52:28 2015 +0200 @@ -1,1 +1,1 @@ -This folder contains an excerpt of the source code of Orthanc 0.8.6. +This folder contains an excerpt of the source code of Orthanc. diff -r b42eeb4bd1e3 -r 7a0af291cc90 Orthanc/SQLite/FunctionContext.cpp --- a/Orthanc/SQLite/FunctionContext.cpp Fri May 29 11:07:38 2015 +0200 +++ b/Orthanc/SQLite/FunctionContext.cpp Mon Jun 01 11:52:28 2015 +0200 @@ -40,7 +40,6 @@ #include "OrthancSQLiteException.h" #include -#include namespace Orthanc { diff -r b42eeb4bd1e3 -r 7a0af291cc90 Orthanc/SQLite/StatementReference.cpp --- a/Orthanc/SQLite/StatementReference.cpp Fri May 29 11:07:38 2015 +0200 +++ b/Orthanc/SQLite/StatementReference.cpp Mon Jun 01 11:52:28 2015 +0200 @@ -47,7 +47,6 @@ #endif #include -#include #include "sqlite3.h" namespace Orthanc diff -r b42eeb4bd1e3 -r 7a0af291cc90 Orthanc/Toolbox.cpp --- a/Orthanc/Toolbox.cpp Fri May 29 11:07:38 2015 +0200 +++ b/Orthanc/Toolbox.cpp Mon Jun 01 11:52:28 2015 +0200 @@ -35,13 +35,28 @@ #include "OrthancException.h" +#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 HAVE_GOOGLE_LOG == 1 +#include +#endif + #if defined(_WIN32) #include #include // For "_spawnvp()" @@ -55,7 +70,7 @@ #include /* PATH_MAX */ #endif -#if defined(__linux) || defined(__FreeBSD_kernel__) +#if defined(__linux) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__) #include /* PATH_MAX */ #include #include @@ -67,57 +82,122 @@ #include +#include "../Resources/ThirdParty/md5/md5.h" +#include "../Resources/ThirdParty/base64/base64.h" + + +#if defined(_MSC_VER) && (_MSC_VER < 1800) +// Patch for the missing "_strtoll" symbol when compiling with Visual Studio < 2013 +extern "C" +{ + int64_t _strtoi64(const char *nptr, char **endptr, int base); + int64_t strtoll(const char *nptr, char **endptr, int base) + { + return _strtoi64(nptr, endptr, base); + } +} +#endif + + +#if ORTHANC_PUGIXML_ENABLED == 1 +#include "ChunkedBuffer.h" +#include +#endif namespace Orthanc { - void Toolbox::TokenizeString(std::vector& result, - const std::string& value, - char separator) - { - result.clear(); - - std::string currentItem; + static bool finish; - for (size_t i = 0; i < value.size(); i++) - { - if (value[i] == separator) - { - result.push_back(currentItem); - currentItem.clear(); - } - else - { - currentItem.push_back(value[i]); - } - } +#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) + { + finish = true; + } +#endif - result.push_back(currentItem); + void Toolbox::USleep(uint64_t microSeconds) + { +#if defined(_WIN32) + ::Sleep(static_cast(microSeconds / static_cast(1000))); +#elif defined(__linux) || defined(__APPLE__) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__) + usleep(microSeconds); +#else +#error Support your platform here +#endif } - void Toolbox::CreateNewDirectory(const std::string& path) + static void ServerBarrierInternal(const bool* stopFlag) { - if (boost::filesystem::exists(path)) +#if defined(_WIN32) + SetConsoleCtrlHandler(ConsoleControlHandler, true); +#else + signal(SIGINT, SignalHandler); + signal(SIGQUIT, SignalHandler); + signal(SIGTERM, SignalHandler); +#endif + + // Active loop that awakens every 100ms + finish = false; + while (!(*stopFlag || finish)) { - if (!boost::filesystem::is_directory(path)) - { - throw OrthancException("Cannot create the directory over an existing file: " + path); - } + Toolbox::USleep(100 * 1000); } - else - { - if (!boost::filesystem::create_directories(path)) - { - throw OrthancException("Unable to create the directory: " + path); - } - } + +#if defined(_WIN32) + SetConsoleCtrlHandler(ConsoleControlHandler, false); +#else + signal(SIGINT, NULL); + signal(SIGQUIT, NULL); + signal(SIGTERM, NULL); +#endif } - bool Toolbox::IsExistingFile(const std::string& path) + void Toolbox::ServerBarrier(const bool& stopFlag) + { + ServerBarrierInternal(&stopFlag); + } + + void Toolbox::ServerBarrier() + { + const bool stopFlag = false; + ServerBarrierInternal(&stopFlag); + } + + + void Toolbox::ToUpperCase(std::string& s) { - return boost::filesystem::exists(path); + std::transform(s.begin(), s.end(), s.begin(), toupper); + } + + + void Toolbox::ToLowerCase(std::string& s) + { + std::transform(s.begin(), s.end(), s.begin(), tolower); + } + + + void Toolbox::ToUpperCase(std::string& result, + const std::string& source) + { + result = source; + ToUpperCase(result); + } + + void Toolbox::ToLowerCase(std::string& result, + const std::string& source) + { + result = source; + ToLowerCase(result); } @@ -179,15 +259,539 @@ - void Toolbox::USleep(uint64_t microSeconds) + void Toolbox::SplitUriComponents(UriComponents& components, + const std::string& uri) + { + static const char URI_SEPARATOR = '/'; + + components.clear(); + + if (uri.size() == 0 || + uri[0] != URI_SEPARATOR) + { + throw OrthancException(ErrorCode_UriSyntax); + } + + // Count the number of slashes in the URI to make an assumption + // about the number of components in the URI + unsigned int estimatedSize = 0; + for (unsigned int i = 0; i < uri.size(); i++) + { + if (uri[i] == URI_SEPARATOR) + estimatedSize++; + } + + components.reserve(estimatedSize - 1); + + unsigned int start = 1; + unsigned int end = 1; + while (end < uri.size()) + { + // This is the loop invariant + assert(uri[start - 1] == '/' && (end >= start)); + + if (uri[end] == '/') + { + components.push_back(std::string(&uri[start], end - start)); + end++; + start = end; + } + else + { + end++; + } + } + + if (start < uri.size()) + { + components.push_back(std::string(&uri[start], end - start)); + } + + for (size_t i = 0; i < components.size(); i++) + { + if (components[i].size() == 0) + { + // Empty component, as in: "/coucou//e" + throw OrthancException(ErrorCode_UriSyntax); + } + } + } + + + void Toolbox::TruncateUri(UriComponents& target, + const UriComponents& source, + size_t fromLevel) + { + target.clear(); + + if (source.size() > fromLevel) + { + target.resize(source.size() - fromLevel); + + size_t j = 0; + for (size_t i = fromLevel; i < source.size(); i++, j++) + { + target[j] = source[i]; + } + + assert(j == target.size()); + } + } + + + + bool Toolbox::IsChildUri(const UriComponents& baseUri, + const UriComponents& testedUri) + { + if (testedUri.size() < baseUri.size()) + { + return false; + } + + for (size_t i = 0; i < baseUri.size(); i++) + { + if (baseUri[i] != testedUri[i]) + return false; + } + + return true; + } + + + std::string Toolbox::AutodetectMimeType(const std::string& path) { + std::string contentType; + size_t lastDot = path.rfind('.'); + size_t lastSlash = path.rfind('/'); + + if (lastDot == std::string::npos || + (lastSlash != std::string::npos && lastDot < lastSlash)) + { + // No trailing dot, unable to detect the content type + } + else + { + const char* extension = &path[lastDot + 1]; + + // http://en.wikipedia.org/wiki/Mime_types + // Text types + if (!strcmp(extension, "txt")) + contentType = "text/plain"; + else if (!strcmp(extension, "html")) + contentType = "text/html"; + else if (!strcmp(extension, "xml")) + contentType = "text/xml"; + else if (!strcmp(extension, "css")) + contentType = "text/css"; + + // Application types + else if (!strcmp(extension, "js")) + contentType = "application/javascript"; + else if (!strcmp(extension, "json")) + contentType = "application/json"; + else if (!strcmp(extension, "pdf")) + contentType = "application/pdf"; + + // Images types + else if (!strcmp(extension, "jpg") || !strcmp(extension, "jpeg")) + contentType = "image/jpeg"; + else if (!strcmp(extension, "gif")) + contentType = "image/gif"; + else if (!strcmp(extension, "png")) + contentType = "image/png"; + } + + return contentType; + } + + + std::string Toolbox::FlattenUri(const UriComponents& components, + size_t fromLevel) + { + if (components.size() <= fromLevel) + { + return "/"; + } + else + { + std::string r; + + for (size_t i = fromLevel; i < components.size(); i++) + { + r += "/" + components[i]; + } + + return r; + } + } + + + + 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); + } + } + + + static char GetHexadecimalCharacter(uint8_t value) + { + assert(value < 16); + + if (value < 10) + return value + '0'; + else + return (value - 10) + 'a'; + } + + + void Toolbox::ComputeMD5(std::string& result, + const std::string& data) + { + if (data.size() > 0) + { + ComputeMD5(result, &data[0], data.size()); + } + else + { + ComputeMD5(result, NULL, 0); + } + } + + + void Toolbox::ComputeMD5(std::string& result, + const void* data, + size_t length) + { + md5_state_s state; + md5_init(&state); + + if (length > 0) + { + md5_append(&state, + reinterpret_cast(data), + static_cast(length)); + } + + md5_byte_t actualHash[16]; + md5_finish(&state, actualHash); + + result.resize(32); + for (unsigned int i = 0; i < 16; i++) + { + result[2 * i] = GetHexadecimalCharacter(actualHash[i] / 16); + result[2 * i + 1] = GetHexadecimalCharacter(actualHash[i] % 16); + } + } + + + void Toolbox::EncodeBase64(std::string& result, + const std::string& data) + { + result = base64_encode(data); + } + + void Toolbox::DecodeBase64(std::string& result, + const std::string& data) + { + result = base64_decode(data); + } + + #if defined(_WIN32) - ::Sleep(static_cast(microSeconds / static_cast(1000))); -#elif defined(__linux) || defined(__APPLE__) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__) - usleep(microSeconds); + 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("Unable to get the path to the executable"); + } + + 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 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(); + } + + + std::string Toolbox::ConvertToUtf8(const std::string& source, + const Encoding sourceEncoding) + { + const char* encoding; + + + // http://bradleyross.users.sourceforge.net/docs/dicom/doc/src-html/org/dcm4che2/data/SpecificCharacterSet.html + switch (sourceEncoding) + { + case Encoding_Utf8: + // Already in UTF-8: No conversion is required + return source; + + case Encoding_Ascii: + return ConvertToAscii(source); + + case Encoding_Latin1: + encoding = "ISO-8859-1"; + break; + + case Encoding_Latin2: + encoding = "ISO-8859-2"; + break; + + case Encoding_Latin3: + encoding = "ISO-8859-3"; + break; + + case Encoding_Latin4: + encoding = "ISO-8859-4"; + break; + + case Encoding_Latin5: + encoding = "ISO-8859-9"; + break; + + case Encoding_Cyrillic: + encoding = "ISO-8859-5"; + break; + + case Encoding_Windows1251: + encoding = "WINDOWS-1251"; + break; + + case Encoding_Arabic: + encoding = "ISO-8859-6"; + break; + + case Encoding_Greek: + encoding = "ISO-8859-7"; + break; + + case Encoding_Hebrew: + encoding = "ISO-8859-8"; + break; + + case Encoding_Japanese: + encoding = "SHIFT-JIS"; + break; + + case Encoding_Chinese: + encoding = "GB18030"; + break; + + case Encoding_Thai: + encoding = "TIS620.2533-0"; + break; + + default: + throw OrthancException(ErrorCode_NotImplemented); + } + + try + { + return boost::locale::conv::to_utf(source, encoding); + } + catch (std::runtime_error&) + { + // Bad input string or bad encoding + return ConvertToAscii(source); + } + } + + + std::string Toolbox::ConvertToAscii(const std::string& source) + { + std::string result; + + result.reserve(source.size() + 1); + for (size_t i = 0; i < source.size(); i++) + { + if (source[i] <= 127 && source[i] >= 0 && !iscntrl(source[i])) + { + result.push_back(source[i]); + } + } + + return result; + } + + void Toolbox::ComputeSHA1(std::string& result, + const std::string& data) + { + boost::uuids::detail::sha1 sha1; + + if (data.size() > 0) + { + sha1.process_bytes(&data[0], data.size()); + } + + unsigned int digest[5]; + + // Sanity check for the memory layout: A SHA-1 digest is 160 bits wide + assert(sizeof(unsigned int) == 4 && sizeof(digest) == (160 / 8)); + + sha1.get_digest(digest); + + result.resize(8 * 5 + 4); + sprintf(&result[0], "%08x-%08x-%08x-%08x-%08x", + digest[0], + digest[1], + digest[2], + digest[3], + digest[4]); + } + + bool Toolbox::IsSHA1(const std::string& str) + { + if (str.size() != 44) + { + return false; + } + + for (unsigned int i = 0; i < 44; i++) + { + if (i == 8 || + i == 17 || + i == 26 || + i == 35) + { + if (str[i] != '-') + return false; + } + else + { + if (!isalnum(str[i])) + return false; + } + } + + return true; + } + + +#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); + } +#endif + + + std::string Toolbox::StripSpaces(const std::string& source) + { + size_t first = 0; + + while (first < source.length() && + isspace(source[first])) + { + first++; + } + + if (first == source.length()) + { + // String containing only spaces + return ""; + } + + size_t last = source.length(); + while (last > first && + isspace(source[last - 1])) + { + last--; + } + + assert(first <= last); + return source.substr(first, last - first); + } + + + static char Hex2Dec(char c) + { + return ((c >= '0' && c <= '9') ? c - '0' : + ((c >= 'a' && c <= 'f') ? c - 'a' + 10 : c - 'A' + 10)); + } + + void Toolbox::UrlDecode(std::string& s) + { + // http://en.wikipedia.org/wiki/Percent-encoding + // http://www.w3schools.com/tags/ref_urlencode.asp + // http://stackoverflow.com/questions/154536/encode-decode-urls-in-c + + if (s.size() == 0) + { + return; + } + + size_t source = 0; + size_t target = 0; + + while (source < s.size()) + { + if (s[source] == '%' && + source + 2 < s.size() && + isalnum(s[source + 1]) && + isalnum(s[source + 2])) + { + s[target] = (Hex2Dec(s[source + 1]) << 4) | Hex2Dec(s[source + 2]); + source += 3; + target += 1; + } + else + { + if (s[source] == '+') + s[target] = ' '; + else + s[target] = s[source]; + + source++; + target++; + } + } + + s.resize(target); } @@ -216,31 +820,322 @@ } - std::string Toolbox::StripSpaces(const std::string& source) +#if BOOST_HAS_REGEX == 1 + std::string Toolbox::WildcardToRegularExpression(const std::string& source) { - size_t first = 0; + // TODO - Speed up this with a regular expression + + std::string result = source; + + // Escape all special characters + boost::replace_all(result, "\\", "\\\\"); + boost::replace_all(result, "^", "\\^"); + boost::replace_all(result, ".", "\\."); + boost::replace_all(result, "$", "\\$"); + boost::replace_all(result, "|", "\\|"); + boost::replace_all(result, "(", "\\("); + boost::replace_all(result, ")", "\\)"); + boost::replace_all(result, "[", "\\["); + boost::replace_all(result, "]", "\\]"); + boost::replace_all(result, "+", "\\+"); + boost::replace_all(result, "/", "\\/"); + boost::replace_all(result, "{", "\\{"); + boost::replace_all(result, "}", "\\}"); + + // Convert wildcards '*' and '?' to their regex equivalents + boost::replace_all(result, "?", "."); + boost::replace_all(result, "*", ".*"); + + return result; + } +#endif + + + + void Toolbox::TokenizeString(std::vector& result, + const std::string& value, + char separator) + { + result.clear(); + + std::string currentItem; + + for (size_t i = 0; i < value.size(); i++) + { + if (value[i] == separator) + { + result.push_back(currentItem); + currentItem.clear(); + } + else + { + currentItem.push_back(value[i]); + } + } + + result.push_back(currentItem); + } + - while (first < source.length() && - isspace(source[first])) +#if BOOST_HAS_REGEX == 1 + void Toolbox::DecodeDataUriScheme(std::string& mime, + std::string& content, + const std::string& source) + { + boost::regex pattern("data:([^;]+);base64,([a-zA-Z0-9=+/]*)", + boost::regex::icase /* case insensitive search */); + + boost::cmatch what; + if (regex_match(source.c_str(), what, pattern)) + { + mime = what[1]; + content = what[2]; + } + else + { + throw OrthancException(ErrorCode_BadFileFormat); + } + } +#endif + + + void Toolbox::MakeDirectory(const std::string& path) + { + if (boost::filesystem::exists(path)) { - first++; + if (!boost::filesystem::is_directory(path)) + { + throw OrthancException("Cannot create the directory over an existing file: " + path); + } + } + else + { + if (!boost::filesystem::create_directories(path)) + { + throw OrthancException("Unable to create the directory: " + path); + } + } + } + + + bool Toolbox::IsExistingFile(const std::string& path) + { + return boost::filesystem::exists(path); + } + + +#if ORTHANC_PUGIXML_ENABLED == 1 + class ChunkedBufferWriter : public pugi::xml_writer + { + private: + ChunkedBuffer buffer_; + + public: + virtual void write(const void *data, size_t size) + { + if (size > 0) + { + buffer_.AddChunk(reinterpret_cast(data), size); + } } - if (first == source.length()) + void Flatten(std::string& s) + { + buffer_.Flatten(s); + } + }; + + + static void JsonToXmlInternal(pugi::xml_node& target, + const Json::Value& source, + const std::string& arrayElement) + { + // http://jsoncpp.sourceforge.net/value_8h_source.html#l00030 + + switch (source.type()) { - // String containing only spaces - return ""; + case Json::nullValue: + { + target.append_child(pugi::node_pcdata).set_value("null"); + break; + } + + case Json::intValue: + { + std::string s = boost::lexical_cast(source.asInt()); + target.append_child(pugi::node_pcdata).set_value(s.c_str()); + break; + } + + case Json::uintValue: + { + std::string s = boost::lexical_cast(source.asUInt()); + target.append_child(pugi::node_pcdata).set_value(s.c_str()); + break; + } + + case Json::realValue: + { + std::string s = boost::lexical_cast(source.asFloat()); + target.append_child(pugi::node_pcdata).set_value(s.c_str()); + break; + } + + case Json::stringValue: + { + target.append_child(pugi::node_pcdata).set_value(source.asString().c_str()); + break; + } + + case Json::booleanValue: + { + target.append_child(pugi::node_pcdata).set_value(source.asBool() ? "true" : "false"); + break; + } + + case Json::arrayValue: + { + for (Json::Value::ArrayIndex i = 0; i < source.size(); i++) + { + pugi::xml_node node = target.append_child(); + node.set_name(arrayElement.c_str()); + JsonToXmlInternal(node, source[i], arrayElement); + } + break; + } + + case Json::objectValue: + { + Json::Value::Members members = source.getMemberNames(); + + for (size_t i = 0; i < members.size(); i++) + { + pugi::xml_node node = target.append_child(); + node.set_name(members[i].c_str()); + JsonToXmlInternal(node, source[members[i]], arrayElement); + } + + break; + } + + default: + throw OrthancException(ErrorCode_NotImplemented); + } + } + + + void Toolbox::JsonToXml(std::string& target, + const Json::Value& source, + const std::string& rootElement, + const std::string& arrayElement) + { + pugi::xml_document doc; + + pugi::xml_node n = doc.append_child(rootElement.c_str()); + JsonToXmlInternal(n, source, arrayElement); + + pugi::xml_node decl = doc.prepend_child(pugi::node_declaration); + decl.append_attribute("version").set_value("1.0"); + decl.append_attribute("encoding").set_value("utf-8"); + + ChunkedBufferWriter writer; + doc.save(writer, " ", pugi::format_default, pugi::encoding_utf8); + writer.Flatten(target); + } + +#endif + + + 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()); } - size_t last = source.length(); - while (last > first && - isspace(source[last - 1])) + 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 HAVE_GOOGLE_LOG == 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) { - last--; - } - - assert(first <= last); - return source.substr(first, last - first); +#if HAVE_GOOGLE_LOG == 1 + LOG(ERROR) << "System command failed with status code " << status; +#endif + + throw OrthancException(ErrorCode_SystemCommand); + } + } + + + bool Toolbox::IsInteger(const std::string& str) + { + std::string s = StripSpaces(str); + + if (s.size() == 0) + { + return false; + } + + size_t pos = 0; + if (s[0] == '-') + { + if (s.size() == 1) + { + return false; + } + + pos = 1; + } + + while (pos < s.size()) + { + if (!isdigit(s[pos])) + { + return false; + } + + pos++; + } + + return true; } } diff -r b42eeb4bd1e3 -r 7a0af291cc90 Orthanc/Toolbox.h --- a/Orthanc/Toolbox.h Fri May 29 11:07:38 2015 +0200 +++ b/Orthanc/Toolbox.h Mon Jun 01 11:52:28 2015 +0200 @@ -37,23 +37,34 @@ #include #include #include + +#if ORTHANC_PUGIXML_ENABLED == 1 #include +#endif namespace Orthanc { typedef std::vector UriComponents; + class NullType + { + }; + namespace Toolbox { - Endianness DetectEndianness(); + void ServerBarrier(const bool& stopFlag); + + void ServerBarrier(); + + void ToUpperCase(std::string& s); // Inplace version - void TokenizeString(std::vector& result, - const std::string& source, - char separator); + void ToLowerCase(std::string& s); // Inplace version - void CreateNewDirectory(const std::string& path); + void ToUpperCase(std::string& result, + const std::string& source); - bool IsExistingFile(const std::string& path); + void ToLowerCase(std::string& result, + const std::string& source); void ReadFile(std::string& content, const std::string& path); @@ -65,6 +76,89 @@ void RemoveFile(const std::string& path); + void SplitUriComponents(UriComponents& components, + const std::string& uri); + + void TruncateUri(UriComponents& target, + const UriComponents& source, + size_t fromLevel); + + bool IsChildUri(const UriComponents& baseUri, + const UriComponents& testedUri); + + std::string AutodetectMimeType(const std::string& path); + + std::string FlattenUri(const UriComponents& components, + size_t fromLevel = 0); + + uint64_t GetFileSize(const std::string& path); + + void ComputeMD5(std::string& result, + const std::string& data); + + void ComputeMD5(std::string& result, + const void* data, + size_t length); + + void ComputeSHA1(std::string& result, + const std::string& data); + + bool IsSHA1(const std::string& str); + + void DecodeBase64(std::string& result, + const std::string& data); + + void EncodeBase64(std::string& result, + const std::string& data); + + std::string GetPathToExecutable(); + + std::string GetDirectoryOfExecutable(); + + std::string ConvertToUtf8(const std::string& source, + const Encoding sourceEncoding); + + std::string ConvertToAscii(const std::string& source); + std::string StripSpaces(const std::string& source); + +#if BOOST_HAS_DATE_TIME == 1 + std::string GetNowIsoString(); +#endif + + // In-place percent-decoding for URL + void UrlDecode(std::string& s); + + Endianness DetectEndianness(); + +#if BOOST_HAS_REGEX == 1 + std::string WildcardToRegularExpression(const std::string& s); +#endif + + void TokenizeString(std::vector& result, + const std::string& source, + char separator); + +#if BOOST_HAS_REGEX == 1 + void DecodeDataUriScheme(std::string& mime, + std::string& content, + const std::string& source); +#endif + + void MakeDirectory(const std::string& path); + + bool IsExistingFile(const std::string& path); + +#if ORTHANC_PUGIXML_ENABLED == 1 + void JsonToXml(std::string& target, + const Json::Value& source, + const std::string& rootElement = "root", + const std::string& arrayElement = "item"); +#endif + + void ExecuteSystemCommand(const std::string& command, + const std::vector& arguments); + + bool IsInteger(const std::string& str); } } diff -r b42eeb4bd1e3 -r 7a0af291cc90 Plugin/Cache/CacheManager.cpp --- a/Plugin/Cache/CacheManager.cpp Fri May 29 11:07:38 2015 +0200 +++ b/Plugin/Cache/CacheManager.cpp Mon Jun 01 11:52:28 2015 +0200 @@ -226,7 +226,7 @@ for (std::list::const_iterator it = toRemove.begin(); it != toRemove.end(); it++) { - pimpl_->storage_.Remove(*it); + pimpl_->storage_.Remove(*it, Orthanc::FileContentType_Unknown); } pimpl_->bundles_[bundleIndex] = bundle; @@ -339,7 +339,7 @@ // Store the cached content on the disk const char* data = content.size() ? &content[0] : NULL; std::string uuid = Toolbox::GenerateUuid(); - pimpl_->storage_.Create(uuid, data, content.size()); + pimpl_->storage_.Create(uuid, data, content.size(), Orthanc::FileContentType_Unknown); bool ok = true; @@ -378,7 +378,7 @@ if (!ok) { // Error: Remove the stored file - pimpl_->storage_.Remove(uuid); + pimpl_->storage_.Remove(uuid, Orthanc::FileContentType_Unknown); } else { @@ -389,7 +389,7 @@ for (std::list::const_iterator it = toRemove.begin(); it != toRemove.end(); it++) { - pimpl_->storage_.Remove(*it); + pimpl_->storage_.Remove(*it, Orthanc::FileContentType_Unknown); } } @@ -466,7 +466,7 @@ bool ok; try { - pimpl_->storage_.Read(content, uuid); + pimpl_->storage_.Read(content, uuid, Orthanc::FileContentType_Unknown); ok = (content.size() == size); } catch (std::runtime_error&) @@ -512,7 +512,7 @@ { transaction->Commit(); pimpl_->bundles_[bundleIndex] = bundle; - pimpl_->storage_.Remove(uuid); + pimpl_->storage_.Remove(uuid, Orthanc::FileContentType_Unknown); } } } @@ -558,7 +558,7 @@ SQLite::Statement s(pimpl_->db_, SQLITE_FROM_HERE, "SELECT fileUuid FROM Cache"); while (s.Step()) { - pimpl_->storage_.Remove(s.ColumnString(0)); + pimpl_->storage_.Remove(s.ColumnString(0), Orthanc::FileContentType_Unknown); } SQLite::Statement t(pimpl_->db_, SQLITE_FROM_HERE, "DELETE FROM Cache"); @@ -579,7 +579,7 @@ s.BindInt(0, bundle); while (s.Step()) { - pimpl_->storage_.Remove(s.ColumnString(0)); + pimpl_->storage_.Remove(s.ColumnString(0), Orthanc::FileContentType_Unknown); } SQLite::Statement t(pimpl_->db_, SQLITE_FROM_HERE, "DELETE FROM Cache WHERE bundle=?"); diff -r b42eeb4bd1e3 -r 7a0af291cc90 Resources/CMake/BoostConfiguration.cmake --- a/Resources/CMake/BoostConfiguration.cmake Fri May 29 11:07:38 2015 +0200 +++ b/Resources/CMake/BoostConfiguration.cmake Mon Jun 01 11:52:28 2015 +0200 @@ -21,7 +21,7 @@ else() include(FindBoost) set(BOOST_STATIC 0) - find_package(Boost COMPONENTS system thread filesystem) + find_package(Boost COMPONENTS system thread filesystem locale) if (NOT Boost_FOUND) message(FATAL_ERROR "Unable to locate Boost on this system") @@ -68,6 +68,7 @@ list(APPEND BOOST_SOURCES ${BOOST_SOURCES_DIR}/libs/system/src/error_code.cpp + ${BOOST_SOURCES_DIR}/libs/locale/src/encoding/codepage.cpp ) @@ -121,4 +122,6 @@ add_definitions( -DBOOST_HAS_FILESYSTEM_V3=1 -DBOOST_HAS_LOCALE=1 + -DBOOST_HAS_DATE_TIME=0 + -DBOOST_HAS_REGEX=0 ) diff -r b42eeb4bd1e3 -r 7a0af291cc90 Resources/SyncOrthancFolder.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Resources/SyncOrthancFolder.py Mon Jun 01 11:52:28 2015 +0200 @@ -0,0 +1,67 @@ +#!/usr/bin/python + +# +# This maintenance script updates the content of the "Orthanc" folder +# to match the latest version of the Orthanc source code. +# + +import os +import shutil + +SOURCE = '/home/jodogne/Subversion/Orthanc/Core' +TARGET = os.path.join(os.path.dirname(__file__), '..', 'Orthanc') + +FILES = [ + 'ChunkedBuffer.cpp', + 'ChunkedBuffer.h', + 'Enumerations.cpp', + 'Enumerations.h', + 'FileStorage/FilesystemStorage.cpp', + 'FileStorage/FilesystemStorage.h', + 'FileStorage/IStorageArea.h', + 'IDynamicObject.h', + 'ImageFormats/ImageAccessor.cpp', + 'ImageFormats/ImageAccessor.h', + 'ImageFormats/ImageBuffer.cpp', + 'ImageFormats/ImageBuffer.h', + 'ImageFormats/ImageProcessing.cpp', + 'ImageFormats/ImageProcessing.h', + 'ImageFormats/PngWriter.cpp', + 'ImageFormats/PngWriter.h', + 'MultiThreading/SharedMessageQueue.cpp', + 'MultiThreading/SharedMessageQueue.h', + 'OrthancException.cpp', + 'OrthancException.h', + 'PrecompiledHeaders.cpp', + 'PrecompiledHeaders.h', + 'SQLite/Connection.cpp', + 'SQLite/Connection.h', + 'SQLite/FunctionContext.cpp', + 'SQLite/FunctionContext.h', + 'SQLite/IScalarFunction.h', + 'SQLite/ITransaction.h', + 'SQLite/NonCopyable.h', + 'SQLite/OrthancSQLiteException.h', + 'SQLite/Statement.cpp', + 'SQLite/Statement.h', + 'SQLite/StatementId.cpp', + 'SQLite/StatementId.h', + 'SQLite/StatementReference.cpp', + 'SQLite/StatementReference.h', + 'SQLite/Transaction.cpp', + 'SQLite/Transaction.h', + 'Toolbox.cpp', + 'Toolbox.h', + 'Uuid.cpp', + 'Uuid.h', +] + +for f in FILES: + source = os.path.join(SOURCE, f) + target = os.path.join(TARGET, f) + try: + os.makedirs(os.path.dirname(target)) + except: + pass + + shutil.copy(source, target) diff -r b42eeb4bd1e3 -r 7a0af291cc90 Resources/ThirdParty/md5/md5.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Resources/ThirdParty/md5/md5.c Mon Jun 01 11:52:28 2015 +0200 @@ -0,0 +1,381 @@ +/* + Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + L. Peter Deutsch + ghost@aladdin.com + + */ +/* $Id: md5.c,v 1.6 2002/04/13 19:20:28 lpd Exp $ */ +/* + Independent implementation of MD5 (RFC 1321). + + This code implements the MD5 Algorithm defined in RFC 1321, whose + text is available at + http://www.ietf.org/rfc/rfc1321.txt + The code is derived from the text of the RFC, including the test suite + (section A.5) but excluding the rest of Appendix A. It does not include + any code or documentation that is identified in the RFC as being + copyrighted. + + The original and principal author of md5.c is L. Peter Deutsch + . Other authors are noted in the change history + that follows (in reverse chronological order): + + 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order + either statically or dynamically; added missing #include + in library. + 2002-03-11 lpd Corrected argument list for main(), and added int return + type, in test program and T value program. + 2002-02-21 lpd Added missing #include in test program. + 2000-07-03 lpd Patched to eliminate warnings about "constant is + unsigned in ANSI C, signed in traditional"; made test program + self-checking. + 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. + 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5). + 1999-05-03 lpd Original version. + */ + +#include "md5.h" +#include + +#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */ +#ifdef ARCH_IS_BIG_ENDIAN +# define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1) +#else +# define BYTE_ORDER 0 +#endif + +#define T_MASK ((md5_word_t)~0) +#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87) +#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9) +#define T3 0x242070db +#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111) +#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050) +#define T6 0x4787c62a +#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec) +#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe) +#define T9 0x698098d8 +#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850) +#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e) +#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841) +#define T13 0x6b901122 +#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c) +#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71) +#define T16 0x49b40821 +#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d) +#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf) +#define T19 0x265e5a51 +#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855) +#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2) +#define T22 0x02441453 +#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e) +#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437) +#define T25 0x21e1cde6 +#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829) +#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278) +#define T28 0x455a14ed +#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa) +#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07) +#define T31 0x676f02d9 +#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375) +#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd) +#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e) +#define T35 0x6d9d6122 +#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3) +#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb) +#define T38 0x4bdecfa9 +#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f) +#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f) +#define T41 0x289b7ec6 +#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805) +#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a) +#define T44 0x04881d05 +#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6) +#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a) +#define T47 0x1fa27cf8 +#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a) +#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb) +#define T50 0x432aff97 +#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58) +#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6) +#define T53 0x655b59c3 +#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d) +#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82) +#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e) +#define T57 0x6fa87e4f +#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f) +#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb) +#define T60 0x4e0811a1 +#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d) +#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca) +#define T63 0x2ad7d2bb +#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e) + + +static void +md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/) +{ + md5_word_t + a = pms->abcd[0], b = pms->abcd[1], + c = pms->abcd[2], d = pms->abcd[3]; + md5_word_t t; +#if BYTE_ORDER > 0 + /* Define storage only for big-endian CPUs. */ + md5_word_t X[16]; +#else + /* Define storage for little-endian or both types of CPUs. */ + md5_word_t xbuf[16]; + const md5_word_t *X; +#endif + + { +#if BYTE_ORDER == 0 + /* + * Determine dynamically whether this is a big-endian or + * little-endian machine, since we can use a more efficient + * algorithm on the latter. + */ + static const int w = 1; + + if (*((const md5_byte_t *)&w)) /* dynamic little-endian */ +#endif +#if BYTE_ORDER <= 0 /* little-endian */ + { + /* + * On little-endian machines, we can process properly aligned + * data without copying it. + */ + if (!((data - (const md5_byte_t *)0) & 3)) { + /* data are properly aligned */ + X = (const md5_word_t *)data; + } else { + /* not aligned */ + memcpy(xbuf, data, 64); + X = xbuf; + } + } +#endif +#if BYTE_ORDER == 0 + else /* dynamic big-endian */ +#endif +#if BYTE_ORDER >= 0 /* big-endian */ + { + /* + * On big-endian machines, we must arrange the bytes in the + * right order. + */ + const md5_byte_t *xp = data; + int i; + +# if BYTE_ORDER == 0 + X = xbuf; /* (dynamic only) */ +# else +# define xbuf X /* (static only) */ +# endif + for (i = 0; i < 16; ++i, xp += 4) + xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24); + } +#endif + } + +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) + + /* Round 1. */ + /* Let [abcd k s i] denote the operation + a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */ +#define F(x, y, z) (((x) & (y)) | (~(x) & (z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + F(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 7, T1); + SET(d, a, b, c, 1, 12, T2); + SET(c, d, a, b, 2, 17, T3); + SET(b, c, d, a, 3, 22, T4); + SET(a, b, c, d, 4, 7, T5); + SET(d, a, b, c, 5, 12, T6); + SET(c, d, a, b, 6, 17, T7); + SET(b, c, d, a, 7, 22, T8); + SET(a, b, c, d, 8, 7, T9); + SET(d, a, b, c, 9, 12, T10); + SET(c, d, a, b, 10, 17, T11); + SET(b, c, d, a, 11, 22, T12); + SET(a, b, c, d, 12, 7, T13); + SET(d, a, b, c, 13, 12, T14); + SET(c, d, a, b, 14, 17, T15); + SET(b, c, d, a, 15, 22, T16); +#undef SET + + /* Round 2. */ + /* Let [abcd k s i] denote the operation + a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ +#define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + G(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 1, 5, T17); + SET(d, a, b, c, 6, 9, T18); + SET(c, d, a, b, 11, 14, T19); + SET(b, c, d, a, 0, 20, T20); + SET(a, b, c, d, 5, 5, T21); + SET(d, a, b, c, 10, 9, T22); + SET(c, d, a, b, 15, 14, T23); + SET(b, c, d, a, 4, 20, T24); + SET(a, b, c, d, 9, 5, T25); + SET(d, a, b, c, 14, 9, T26); + SET(c, d, a, b, 3, 14, T27); + SET(b, c, d, a, 8, 20, T28); + SET(a, b, c, d, 13, 5, T29); + SET(d, a, b, c, 2, 9, T30); + SET(c, d, a, b, 7, 14, T31); + SET(b, c, d, a, 12, 20, T32); +#undef SET + + /* Round 3. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */ +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + H(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 5, 4, T33); + SET(d, a, b, c, 8, 11, T34); + SET(c, d, a, b, 11, 16, T35); + SET(b, c, d, a, 14, 23, T36); + SET(a, b, c, d, 1, 4, T37); + SET(d, a, b, c, 4, 11, T38); + SET(c, d, a, b, 7, 16, T39); + SET(b, c, d, a, 10, 23, T40); + SET(a, b, c, d, 13, 4, T41); + SET(d, a, b, c, 0, 11, T42); + SET(c, d, a, b, 3, 16, T43); + SET(b, c, d, a, 6, 23, T44); + SET(a, b, c, d, 9, 4, T45); + SET(d, a, b, c, 12, 11, T46); + SET(c, d, a, b, 15, 16, T47); + SET(b, c, d, a, 2, 23, T48); +#undef SET + + /* Round 4. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ +#define I(x, y, z) ((y) ^ ((x) | ~(z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + I(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 6, T49); + SET(d, a, b, c, 7, 10, T50); + SET(c, d, a, b, 14, 15, T51); + SET(b, c, d, a, 5, 21, T52); + SET(a, b, c, d, 12, 6, T53); + SET(d, a, b, c, 3, 10, T54); + SET(c, d, a, b, 10, 15, T55); + SET(b, c, d, a, 1, 21, T56); + SET(a, b, c, d, 8, 6, T57); + SET(d, a, b, c, 15, 10, T58); + SET(c, d, a, b, 6, 15, T59); + SET(b, c, d, a, 13, 21, T60); + SET(a, b, c, d, 4, 6, T61); + SET(d, a, b, c, 11, 10, T62); + SET(c, d, a, b, 2, 15, T63); + SET(b, c, d, a, 9, 21, T64); +#undef SET + + /* Then perform the following additions. (That is increment each + of the four registers by the value it had before this block + was started.) */ + pms->abcd[0] += a; + pms->abcd[1] += b; + pms->abcd[2] += c; + pms->abcd[3] += d; +} + +void +md5_init(md5_state_t *pms) +{ + pms->count[0] = pms->count[1] = 0; + pms->abcd[0] = 0x67452301; + pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476; + pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301; + pms->abcd[3] = 0x10325476; +} + +void +md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes) +{ + const md5_byte_t *p = data; + int left = nbytes; + int offset = (pms->count[0] >> 3) & 63; + md5_word_t nbits = (md5_word_t)(nbytes << 3); + + if (nbytes <= 0) + return; + + /* Update the message length. */ + pms->count[1] += nbytes >> 29; + pms->count[0] += nbits; + if (pms->count[0] < nbits) + pms->count[1]++; + + /* Process an initial partial block. */ + if (offset) { + int copy = (offset + nbytes > 64 ? 64 - offset : nbytes); + + memcpy(pms->buf + offset, p, copy); + if (offset + copy < 64) + return; + p += copy; + left -= copy; + md5_process(pms, pms->buf); + } + + /* Process full blocks. */ + for (; left >= 64; p += 64, left -= 64) + md5_process(pms, p); + + /* Process a final partial block. */ + if (left) + memcpy(pms->buf, p, left); +} + +void +md5_finish(md5_state_t *pms, md5_byte_t digest[16]) +{ + static const md5_byte_t pad[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + md5_byte_t data[8]; + int i; + + /* Save the length before padding. */ + for (i = 0; i < 8; ++i) + data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3)); + /* Pad to 56 bytes mod 64. */ + md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1); + /* Append the length. */ + md5_append(pms, data, 8); + for (i = 0; i < 16; ++i) + digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3)); +} diff -r b42eeb4bd1e3 -r 7a0af291cc90 Resources/ThirdParty/md5/md5.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Resources/ThirdParty/md5/md5.h Mon Jun 01 11:52:28 2015 +0200 @@ -0,0 +1,91 @@ +/* + Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + L. Peter Deutsch + ghost@aladdin.com + + */ +/* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */ +/* + Independent implementation of MD5 (RFC 1321). + + This code implements the MD5 Algorithm defined in RFC 1321, whose + text is available at + http://www.ietf.org/rfc/rfc1321.txt + The code is derived from the text of the RFC, including the test suite + (section A.5) but excluding the rest of Appendix A. It does not include + any code or documentation that is identified in the RFC as being + copyrighted. + + The original and principal author of md5.h is L. Peter Deutsch + . Other authors are noted in the change history + that follows (in reverse chronological order): + + 2002-04-13 lpd Removed support for non-ANSI compilers; removed + references to Ghostscript; clarified derivation from RFC 1321; + now handles byte order either statically or dynamically. + 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. + 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5); + added conditionalization for C++ compilation from Martin + Purschke . + 1999-05-03 lpd Original version. + */ + +#ifndef md5_INCLUDED +# define md5_INCLUDED + +/* + * This package supports both compile-time and run-time determination of CPU + * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be + * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is + * defined as non-zero, the code will be compiled to run only on big-endian + * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to + * run on either big- or little-endian CPUs, but will run slightly less + * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined. + */ + +typedef unsigned char md5_byte_t; /* 8-bit byte */ +typedef unsigned int md5_word_t; /* 32-bit word */ + +/* Define the state of the MD5 Algorithm. */ +typedef struct md5_state_s { + md5_word_t count[2]; /* message length in bits, lsw first */ + md5_word_t abcd[4]; /* digest buffer */ + md5_byte_t buf[64]; /* accumulate block */ +} md5_state_t; + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* Initialize the algorithm. */ +void md5_init(md5_state_t *pms); + +/* Append a string to the message. */ +void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes); + +/* Finish the message and return the digest. */ +void md5_finish(md5_state_t *pms, md5_byte_t digest[16]); + +#ifdef __cplusplus +} /* end extern "C" */ +#endif + +#endif /* md5_INCLUDED */