# HG changeset patch # User Sebastien Jodogne # Date 1449823507 -3600 # Node ID 7bef560b9782db0ee99e901fd3039ea121b8b28b # Parent 950745f3f48bc87f9c53e0138210b86ff3e7b1c3# Parent 71356f41ec2f76da813c72d91a451c53773ede2c integration mainline->dcmtk-3.6.1 diff -r 950745f3f48b -r 7bef560b9782 CMakeLists.txt diff -r 950745f3f48b -r 7bef560b9782 Core/HttpServer/HttpOutput.cpp --- a/Core/HttpServer/HttpOutput.cpp Mon Dec 07 10:39:23 2015 +0100 +++ b/Core/HttpServer/HttpOutput.cpp Fri Dec 11 09:45:07 2015 +0100 @@ -449,12 +449,59 @@ } - void HttpOutput::StateMachine::SendMultipartItem(const void* item, size_t length) + void HttpOutput::StateMachine::SendMultipartItem(const void* item, + size_t length, + const std::map& headers) { + if (state_ != State_WritingMultipart) + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + std::string header = "--" + multipartBoundary_ + "\r\n"; - header += "Content-Type: " + multipartContentType_ + "\r\n"; - header += "Content-Length: " + boost::lexical_cast(length) + "\r\n"; - header += "MIME-Version: 1.0\r\n\r\n"; + + bool hasContentType = false; + bool hasContentLength = false; + bool hasMimeVersion = false; + + for (std::map::const_iterator + it = headers.begin(); it != headers.end(); ++it) + { + header += it->first + ": " + it->second + "\r\n"; + + std::string tmp; + Toolbox::ToLowerCase(tmp, it->first); + + if (tmp == "content-type") + { + hasContentType = true; + } + + if (tmp == "content-length") + { + hasContentLength = true; + } + + if (tmp == "mime-version") + { + hasMimeVersion = true; + } + } + + if (!hasContentType) + { + header += "Content-Type: " + multipartContentType_ + "\r\n"; + } + + if (!hasContentLength) + { + header += "Content-Length: " + boost::lexical_cast(length) + "\r\n"; + } + + if (!hasMimeVersion) + { + header += "MIME-Version: 1.0\r\n\r\n"; + } stream_.Send(false, header.c_str(), header.size()); @@ -463,7 +510,7 @@ stream_.Send(false, item, length); } - stream_.Send(false, "\r\n", 1); + stream_.Send(false, "\r\n", 2); } @@ -489,19 +536,6 @@ } - void HttpOutput::SendMultipartItem(const std::string& item) - { - if (item.size() > 0) - { - stateMachine_.SendMultipartItem(item.c_str(), item.size()); - } - else - { - stateMachine_.SendMultipartItem(NULL, 0); - } - } - - void HttpOutput::Answer(IHttpStreamAnswer& stream) { HttpCompression compression = stream.SetupHttpCompression(isGzipAllowed_, isDeflateAllowed_); diff -r 950745f3f48b -r 7bef560b9782 Core/HttpServer/HttpOutput.h --- a/Core/HttpServer/HttpOutput.h Mon Dec 07 10:39:23 2015 +0100 +++ b/Core/HttpServer/HttpOutput.h Fri Dec 11 09:45:07 2015 +0100 @@ -99,7 +99,9 @@ void StartMultipart(const std::string& subType, const std::string& contentType); - void SendMultipartItem(const void* item, size_t length); + void SendMultipartItem(const void* item, + size_t length, + const std::map& headers); void CloseMultipart(); @@ -202,11 +204,11 @@ stateMachine_.StartMultipart(subType, contentType); } - void SendMultipartItem(const std::string& item); - - void SendMultipartItem(const void* item, size_t size) + void SendMultipartItem(const void* item, + size_t size, + const std::map& headers) { - stateMachine_.SendMultipartItem(item, size); + stateMachine_.SendMultipartItem(item, size, headers); } void CloseMultipart() diff -r 950745f3f48b -r 7bef560b9782 NEWS --- a/NEWS Mon Dec 07 10:39:23 2015 +0100 +++ b/NEWS Fri Dec 11 09:45:07 2015 +0100 @@ -1,10 +1,19 @@ Pending changes in the mainline =============================== + +* New function in plugin SDK: "OrthancPluginSendMultipartItem2()" + + +Version 0.9.6 (2015/12/08) +========================== + +* Promiscuous mode (accept unknown SOP class UID) is now turned off by default +* Fix serialization of DICOM buffers that might contain garbage trailing * Fix modality worklists server if some fields are null * More tolerant "/series/.../ordered-slices" with broken series -* Promiscuous mode is now turned off by default * Improved logging information if upgrade fails +* Fix formatting of multipart HTTP answers (bis) Version 0.9.5 (2015/12/02) diff -r 950745f3f48b -r 7bef560b9782 OrthancServer/FromDcmtkBridge.cpp --- a/OrthancServer/FromDcmtkBridge.cpp Mon Dec 07 10:39:23 2015 +0100 +++ b/OrthancServer/FromDcmtkBridge.cpp Fri Dec 11 09:45:07 2015 +0100 @@ -1040,9 +1040,12 @@ ff.removeInvalidGroups(); // Create a memory buffer with the proper size - uint32_t s = ff.calcElementLength(xfer, encodingType); - buffer.resize(s); - DcmOutputBufferStream ob(&buffer[0], s); + { + const uint32_t estimatedSize = ff.calcElementLength(xfer, encodingType); // (*) + buffer.resize(estimatedSize); + } + + DcmOutputBufferStream ob(&buffer[0], buffer.size()); // Fill the memory buffer with the meta-header and the dataset ff.transferInit(); @@ -1051,13 +1054,23 @@ /*opt_paddingType*/ EPD_withoutPadding); ff.transferEnd(); - // Handle errors if (c.good()) { + // The DICOM file is successfully written, truncate the target + // buffer if its size was overestimated by (*) + ob.flush(); + + size_t effectiveSize = static_cast(ob.tell()); + if (effectiveSize < buffer.size()) + { + buffer.resize(effectiveSize); + } + return true; } else { + // Error buffer.clear(); return false; } diff -r 950745f3f48b -r 7bef560b9782 Plugins/Engine/OrthancPlugins.cpp --- a/Plugins/Engine/OrthancPlugins.cpp Mon Dec 07 10:39:23 2015 +0100 +++ b/Plugins/Engine/OrthancPlugins.cpp Fri Dec 11 09:45:07 2015 +0100 @@ -1574,7 +1574,39 @@ *(p.target) = ReturnImage(result); } - + + + void OrthancPlugins::ApplySendMultipartItem(const void* parameters) + { + // An exception might be raised in this function if the + // connection was closed by the HTTP client. + const _OrthancPluginAnswerBuffer& p = + *reinterpret_cast(parameters); + + HttpOutput* output = reinterpret_cast(p.output); + + std::map headers; // No custom headers + output->SendMultipartItem(p.answer, p.answerSize, headers); + } + + + void OrthancPlugins::ApplySendMultipartItem2(const void* parameters) + { + // An exception might be raised in this function if the + // connection was closed by the HTTP client. + const _OrthancPluginSendMultipartItem2& p = + *reinterpret_cast(parameters); + HttpOutput* output = reinterpret_cast(p.output); + + std::map headers; + for (uint32_t i = 0; i < p.headersCount; i++) + { + headers[p.headersKeys[i]] = p.headersValues[i]; + } + + output->SendMultipartItem(p.answer, p.answerSize, headers); + } + void OrthancPlugins::DatabaseAnswer(const void* parameters) { @@ -1969,15 +2001,12 @@ } case _OrthancPluginService_SendMultipartItem: - { - // An exception might be raised in this function if the - // connection was closed by the HTTP client. - const _OrthancPluginAnswerBuffer& p = - *reinterpret_cast(parameters); - HttpOutput* output = reinterpret_cast(p.output); - output->SendMultipartItem(p.answer, p.answerSize); + ApplySendMultipartItem(parameters); return true; - } + + case _OrthancPluginService_SendMultipartItem2: + ApplySendMultipartItem2(parameters); + return true; case _OrthancPluginService_ReadFile: { diff -r 950745f3f48b -r 7bef560b9782 Plugins/Engine/OrthancPlugins.h --- a/Plugins/Engine/OrthancPlugins.h Mon Dec 07 10:39:23 2015 +0100 +++ b/Plugins/Engine/OrthancPlugins.h Fri Dec 11 09:45:07 2015 +0100 @@ -152,6 +152,10 @@ void ApplyLookupDictionary(const void* parameters); + void ApplySendMultipartItem(const void* parameters); + + void ApplySendMultipartItem2(const void* parameters); + void ComputeHash(_OrthancPluginService service, const void* parameters); diff -r 950745f3f48b -r 7bef560b9782 Plugins/Include/orthanc/OrthancCPlugin.h --- a/Plugins/Include/orthanc/OrthancCPlugin.h Mon Dec 07 10:39:23 2015 +0100 +++ b/Plugins/Include/orthanc/OrthancCPlugin.h Fri Dec 11 09:45:07 2015 +0100 @@ -114,7 +114,7 @@ #define ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER 0 #define ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER 9 -#define ORTHANC_PLUGINS_MINIMAL_REVISION_NUMBER 5 +#define ORTHANC_PLUGINS_MINIMAL_REVISION_NUMBER 7 @@ -424,6 +424,7 @@ _OrthancPluginService_SendMultipartItem = 2009, _OrthancPluginService_SendHttpStatus = 2010, _OrthancPluginService_CompressAndAnswerImage = 2011, + _OrthancPluginService_SendMultipartItem2 = 2012, /* Access to the Orthanc database and API */ _OrthancPluginService_GetDicomForInstance = 3000, @@ -2719,7 +2720,7 @@ * @param subType The sub-type of the multipart answer ("mixed" or "related"). * @param contentType The MIME type of the items in the multipart answer. * @return 0 if success, or the error code if failure. - * @see OrthancPluginSendMultipartItem() + * @see OrthancPluginSendMultipartItem(), OrthancPluginSendMultipartItem2() * @ingroup REST **/ ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode OrthancPluginStartMultipartAnswer( @@ -2748,6 +2749,7 @@ * @param answerSize Number of bytes of the item. * @return 0 if success, or the error code if failure (this notably happens * if the connection is closed by the client). + * @see OrthancPluginSendMultipartItem2() * @ingroup REST **/ ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode OrthancPluginSendMultipartItem( @@ -4677,6 +4679,58 @@ } + + typedef struct + { + OrthancPluginRestOutput* output; + const char* answer; + uint32_t answerSize; + uint32_t headersCount; + const char* const* headersKeys; + const char* const* headersValues; + } _OrthancPluginSendMultipartItem2; + + /** + * @brief Send an item as a part of some HTTP multipart answer, with custom headers. + * + * This function sends an item as a part of some HTTP multipart + * answer that was initiated by OrthancPluginStartMultipartAnswer(). In addition to + * OrthancPluginSendMultipartItem(), this function will set HTTP header associated + * with the item. + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param output The HTTP connection to the client application. + * @param answer Pointer to the memory buffer containing the item. + * @param answerSize Number of bytes of the item. + * @param headersCount The number of HTTP headers. + * @param headersKeys Array containing the keys of the HTTP headers. + * @param headersValues Array containing the values of the HTTP headers. + * @return 0 if success, or the error code if failure (this notably happens + * if the connection is closed by the client). + * @see OrthancPluginSendMultipartItem() + * @ingroup REST + **/ + ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode OrthancPluginSendMultipartItem2( + OrthancPluginContext* context, + OrthancPluginRestOutput* output, + const char* answer, + uint32_t answerSize, + uint32_t headersCount, + const char* const* headersKeys, + const char* const* headersValues) + { + _OrthancPluginSendMultipartItem2 params; + params.output = output; + params.answer = answer; + params.answerSize = answerSize; + params.headersCount = headersCount; + params.headersKeys = headersKeys; + params.headersValues = headersValues; + + return context->InvokeService(context, _OrthancPluginService_SendMultipartItem2, ¶ms); + } + + #ifdef __cplusplus } #endif diff -r 950745f3f48b -r 7bef560b9782 Resources/CMake/DcmtkConfiguration.cmake --- a/Resources/CMake/DcmtkConfiguration.cmake Mon Dec 07 10:39:23 2015 +0100 +++ b/Resources/CMake/DcmtkConfiguration.cmake Fri Dec 11 09:45:07 2015 +0100 @@ -179,7 +179,7 @@ elseif (EXISTS "${DCMTK_DIR}/config/osconfig.h") # This is for Arch Linux set(DCMTK_CONFIGURATION_FILE "${DCMTK_DIR}/config/osconfig.h") else() - message(FATAL_ERROR "Please install libdcmtk1-dev") + message(FATAL_ERROR "Please install libdcmtk*-dev") endif() # Autodetection of the version of DCMTK @@ -208,8 +208,12 @@ if (DCMTK_DICTIONARY_DIR STREQUAL "") find_path(DCMTK_DICTIONARY_DIR_AUTO dicom.dic /usr/share/dcmtk + /usr/share/libdcmtk1 /usr/share/libdcmtk2 + /usr/share/libdcmtk3 /usr/share/libdcmtk4 + /usr/share/libdcmtk5 + /usr/share/libdcmtk6 /usr/local/share/dcmtk )