comparison OrthancFramework/Sources/DicomNetworking/Internals/MoveScp.cpp @ 4044:d25f4c0fa160 framework

splitting code into OrthancFramework and OrthancServer
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 10 Jun 2020 20:30:34 +0200
parents Core/DicomNetworking/Internals/MoveScp.cpp@2a170a8f1faf
children bf7b9edf6b81
comparison
equal deleted inserted replaced
4043:6c6239aec462 4044:d25f4c0fa160
1 /**
2 * Orthanc - A Lightweight, RESTful DICOM Store
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
4 * Department, University Hospital of Liege, Belgium
5 * Copyright (C) 2017-2020 Osimis S.A., Belgium
6 *
7 * This program is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation, either version 3 of the
10 * License, or (at your option) any later version.
11 *
12 * In addition, as a special exception, the copyright holders of this
13 * program give permission to link the code of its release with the
14 * OpenSSL project's "OpenSSL" library (or with modified versions of it
15 * that use the same license as the "OpenSSL" library), and distribute
16 * the linked executables. You must obey the GNU General Public License
17 * in all respects for all of the code used other than "OpenSSL". If you
18 * modify file(s) with this exception, you may extend this exception to
19 * your version of the file(s), but you are not obligated to do so. If
20 * you do not wish to do so, delete this exception statement from your
21 * version. If you delete this exception statement from all source files
22 * in the program, then also delete it here.
23 *
24 * This program is distributed in the hope that it will be useful, but
25 * WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
27 * General Public License for more details.
28 *
29 * You should have received a copy of the GNU General Public License
30 * along with this program. If not, see <http://www.gnu.org/licenses/>.
31 **/
32
33
34
35
36 /*=========================================================================
37
38 This file is based on portions of the following project:
39
40 Program: DCMTK 3.6.0
41 Module: http://dicom.offis.de/dcmtk.php.en
42
43 Copyright (C) 1994-2011, OFFIS e.V.
44 All rights reserved.
45
46 This software and supporting documentation were developed by
47
48 OFFIS e.V.
49 R&D Division Health
50 Escherweg 2
51 26121 Oldenburg, Germany
52
53 Redistribution and use in source and binary forms, with or without
54 modification, are permitted provided that the following conditions
55 are met:
56
57 - Redistributions of source code must retain the above copyright
58 notice, this list of conditions and the following disclaimer.
59
60 - Redistributions in binary form must reproduce the above copyright
61 notice, this list of conditions and the following disclaimer in the
62 documentation and/or other materials provided with the distribution.
63
64 - Neither the name of OFFIS nor the names of its contributors may be
65 used to endorse or promote products derived from this software
66 without specific prior written permission.
67
68 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
69 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
70 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
71 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
72 HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
73 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
74 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
75 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
76 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
77 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
78 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
79
80 =========================================================================*/
81
82
83 #include "../../PrecompiledHeaders.h"
84 #include "MoveScp.h"
85
86 #include <memory>
87
88 #include "../../DicomParsing/FromDcmtkBridge.h"
89 #include "../../DicomParsing/ToDcmtkBridge.h"
90 #include "../../Logging.h"
91 #include "../../OrthancException.h"
92
93 #include <boost/lexical_cast.hpp>
94
95
96 /**
97 * Macro specifying whether to apply the patch suggested in issue 66:
98 * "Orthanc responses C-MOVE with zero Move Originator Message ID"
99 * https://bitbucket.org/sjodogne/orthanc/issues/66/
100 **/
101
102 #define APPLY_FIX_ISSUE_66 1
103
104
105 namespace Orthanc
106 {
107 namespace
108 {
109 struct MoveScpData
110 {
111 std::string target_;
112 IMoveRequestHandler* handler_;
113 DcmDataset* lastRequest_;
114 unsigned int subOperationCount_;
115 unsigned int failureCount_;
116 unsigned int warningCount_;
117 std::unique_ptr<IMoveRequestIterator> iterator_;
118 const std::string* remoteIp_;
119 const std::string* remoteAet_;
120 const std::string* calledAet_;
121 };
122
123
124 #if APPLY_FIX_ISSUE_66 != 1
125 static uint16_t GetMessageId(const DicomMap& message)
126 {
127 /**
128 * Retrieve the Message ID (0000,0110) for this C-MOVE request, if
129 * any. If present, this Message ID will be stored in the Move
130 * Originator Message ID (0000,1031) field of the C-MOVE response.
131 * http://dicom.nema.org/dicom/2013/output/chtml/part07/chapter_E.html
132 **/
133
134 const DicomValue* value = message.TestAndGetValue(DICOM_TAG_MESSAGE_ID);
135
136 if (value != NULL &&
137 !value->IsNull() &&
138 !value->IsBinary())
139 {
140 try
141 {
142 int tmp = boost::lexical_cast<int>(value->GetContent());
143 if (tmp >= 0 && tmp <= 0xffff)
144 {
145 return static_cast<uint16_t>(tmp);
146 }
147 }
148 catch (boost::bad_lexical_cast&)
149 {
150 LOG(WARNING) << "Cannot convert the Message ID (\"" << value->GetContent()
151 << "\") of an incoming C-MOVE request to an integer, assuming zero";
152 }
153 }
154
155 return 0;
156 }
157 #endif
158
159
160 void MoveScpCallback(
161 /* in */
162 void *callbackData,
163 OFBool cancelled,
164 T_DIMSE_C_MoveRQ *request,
165 DcmDataset *requestIdentifiers,
166 int responseCount,
167 /* out */
168 T_DIMSE_C_MoveRSP *response,
169 DcmDataset **responseIdentifiers,
170 DcmDataset **statusDetail)
171 {
172 bzero(response, sizeof(T_DIMSE_C_MoveRSP));
173 *statusDetail = NULL;
174 *responseIdentifiers = NULL;
175
176 MoveScpData& data = *reinterpret_cast<MoveScpData*>(callbackData);
177 if (data.lastRequest_ == NULL)
178 {
179 DicomMap input;
180 FromDcmtkBridge::ExtractDicomSummary(input, *requestIdentifiers);
181
182 try
183 {
184 #if APPLY_FIX_ISSUE_66 == 1
185 uint16_t messageId = request->MessageID;
186 #else
187 // The line below was the implementation for Orthanc <= 1.3.2
188 uint16_t messageId = GetMessageId(input);
189 #endif
190
191 data.iterator_.reset(data.handler_->Handle(data.target_, input, *data.remoteIp_, *data.remoteAet_,
192 *data.calledAet_, messageId));
193
194 if (data.iterator_.get() == NULL)
195 {
196 // Internal error!
197 response->DimseStatus = STATUS_MOVE_Failed_UnableToProcess;
198 return;
199 }
200
201 data.subOperationCount_ = data.iterator_->GetSubOperationCount();
202 data.failureCount_ = 0;
203 data.warningCount_ = 0;
204 }
205 catch (OrthancException& e)
206 {
207 // Internal error!
208 LOG(ERROR) << "IMoveRequestHandler Failed: " << e.What();
209 response->DimseStatus = STATUS_MOVE_Failed_UnableToProcess;
210 return;
211 }
212
213 data.lastRequest_ = requestIdentifiers;
214 }
215 else if (data.lastRequest_ != requestIdentifiers)
216 {
217 // Internal error!
218 response->DimseStatus = STATUS_MOVE_Failed_UnableToProcess;
219 return;
220 }
221
222 if (data.subOperationCount_ == 0)
223 {
224 response->DimseStatus = STATUS_Success;
225 }
226 else
227 {
228 IMoveRequestIterator::Status status;
229
230 try
231 {
232 status = data.iterator_->DoNext();
233 }
234 catch (OrthancException& e)
235 {
236 // Internal error!
237 LOG(ERROR) << "IMoveRequestHandler Failed: " << e.What();
238 response->DimseStatus = STATUS_MOVE_Failed_UnableToProcess;
239 return;
240 }
241
242 if (status == IMoveRequestIterator::Status_Failure)
243 {
244 data.failureCount_++;
245 }
246 else if (status == IMoveRequestIterator::Status_Warning)
247 {
248 data.warningCount_++;
249 }
250
251 if (responseCount < static_cast<int>(data.subOperationCount_))
252 {
253 response->DimseStatus = STATUS_Pending;
254 }
255 else
256 {
257 response->DimseStatus = STATUS_Success;
258 }
259 }
260
261 response->NumberOfRemainingSubOperations = data.subOperationCount_ - responseCount;
262 response->NumberOfCompletedSubOperations = responseCount;
263 response->NumberOfFailedSubOperations = data.failureCount_;
264 response->NumberOfWarningSubOperations = data.warningCount_;
265 }
266 }
267
268
269 OFCondition Internals::moveScp(T_ASC_Association * assoc,
270 T_DIMSE_Message * msg,
271 T_ASC_PresentationContextID presID,
272 IMoveRequestHandler& handler,
273 const std::string& remoteIp,
274 const std::string& remoteAet,
275 const std::string& calledAet,
276 int timeout)
277 {
278 MoveScpData data;
279 data.target_ = std::string(msg->msg.CMoveRQ.MoveDestination);
280 data.lastRequest_ = NULL;
281 data.handler_ = &handler;
282 data.remoteIp_ = &remoteIp;
283 data.remoteAet_ = &remoteAet;
284 data.calledAet_ = &calledAet;
285
286 OFCondition cond = DIMSE_moveProvider(assoc, presID, &msg->msg.CMoveRQ,
287 MoveScpCallback, &data,
288 /*opt_blockMode*/ (timeout ? DIMSE_NONBLOCKING : DIMSE_BLOCKING),
289 /*opt_dimse_timeout*/ timeout);
290
291 // if some error occured, dump corresponding information and remove the outfile if necessary
292 if (cond.bad())
293 {
294 OFString temp_str;
295 LOG(ERROR) << "Move SCP Failed: " << cond.text();
296 }
297
298 return cond;
299 }
300 }