Mercurial > hg > orthanc
annotate OrthancServer/QueryRetrieveHandler.cpp @ 2248:69b0f4e8a49b
Escape multipart type parameter value in Content-Type header
## Summary
Multipart responses do not quote/escape the value of their type
parameter (the subtype) even though it always contains at least one
special character (the slash "/"), which confuses standard-compliant
HTTP clients.
## Details
The Content-Type header in HTTP is in RFC 7231, Section 3.1.1.5:
https://tools.ietf.org/html/rfc7231#section-3.1.1.5
The section defers to the media type section (3.1.1.1) for the syntax of
the media type:
https://tools.ietf.org/html/rfc7231#section-3.1.1.1
This states that a parameter value can be quoted:
parameter = token "=" ( token / quoted-string )
A parameter value that matches the token production can be transmitted
either as a token or within a quoted-string. The quoted and unquoted
values are equivalent.
Tokens are defined in RFC 7230, Section 3.2.6 (via RFC 7231, appendix
C):
https://tools.ietf.org/html/rfc7231#appendix-C
https://tools.ietf.org/html/rfc7230#section-3.2.6
Here we observe that tokens cannot contain a slash "/" character:
token = 1*tchar
tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*"
/ "+" / "-" / "." / "^" / "_" / "`" / "|" / "~"
/ DIGIT / ALPHA
; any VCHAR, except delimiters
Delimiters are chosen from the set of US-ASCII visual characters not
allowed in a token (DQUOTE and "(),/:;<=>?@[\]{}").
However, the current implementation does not quote/escape the value of
the type parameter:
multipart/related; type=application/dicom
Instead, it should be:
multipart/related; type="application/dicom"
All of this also seems to apply to the MIME Content-Type header
definition, even though it is a little different:
https://www.iana.org/assignments/message-headers
https://tools.ietf.org/html/rfc2045#section-5.1
https://tools.ietf.org/html/rfc2387
author | Thibault Nélis <tn@osimis.io> |
---|---|
date | Mon, 16 Jan 2017 13:07:11 +0100 |
parents | a3a65de1840f |
children | 7284093111b0 |
rev | line source |
---|---|
1368 | 1 /** |
2 * Orthanc - A Lightweight, RESTful DICOM Store | |
1900 | 3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics |
1368 | 4 * Department, University Hospital of Liege, Belgium |
2244
a3a65de1840f
shared copyright with osimis
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
2221
diff
changeset
|
5 * Copyright (C) 2017 Osimis, Belgium |
1368 | 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 #include "PrecompiledHeadersServer.h" | |
35 #include "QueryRetrieveHandler.h" | |
36 | |
37 #include "OrthancInitialization.h" | |
2218
3eefb84ac0bd
dynamically fix outgoing C-Find requests using "OutgoingFindRequestFilter()"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
2209
diff
changeset
|
38 #include "FromDcmtkBridge.h" |
1368 | 39 |
40 | |
41 namespace Orthanc | |
42 { | |
2218
3eefb84ac0bd
dynamically fix outgoing C-Find requests using "OutgoingFindRequestFilter()"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
2209
diff
changeset
|
43 static void FixQuery(DicomMap& query, |
3eefb84ac0bd
dynamically fix outgoing C-Find requests using "OutgoingFindRequestFilter()"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
2209
diff
changeset
|
44 ServerContext& context, |
3eefb84ac0bd
dynamically fix outgoing C-Find requests using "OutgoingFindRequestFilter()"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
2209
diff
changeset
|
45 const std::string& modality) |
3eefb84ac0bd
dynamically fix outgoing C-Find requests using "OutgoingFindRequestFilter()"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
2209
diff
changeset
|
46 { |
2221
e7beca979562
fix Windows build due to overwriting a macro
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
2218
diff
changeset
|
47 static const char* LUA_CALLBACK = "OutgoingFindRequestFilter"; |
e7beca979562
fix Windows build due to overwriting a macro
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
2218
diff
changeset
|
48 |
2218
3eefb84ac0bd
dynamically fix outgoing C-Find requests using "OutgoingFindRequestFilter()"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
2209
diff
changeset
|
49 LuaScripting::Locker locker(context.GetLua()); |
2221
e7beca979562
fix Windows build due to overwriting a macro
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
2218
diff
changeset
|
50 if (locker.GetLua().IsExistingFunction(LUA_CALLBACK)) |
2218
3eefb84ac0bd
dynamically fix outgoing C-Find requests using "OutgoingFindRequestFilter()"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
2209
diff
changeset
|
51 { |
2221
e7beca979562
fix Windows build due to overwriting a macro
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
2218
diff
changeset
|
52 LuaFunctionCall call(locker.GetLua(), LUA_CALLBACK); |
2218
3eefb84ac0bd
dynamically fix outgoing C-Find requests using "OutgoingFindRequestFilter()"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
2209
diff
changeset
|
53 call.PushDicom(query); |
3eefb84ac0bd
dynamically fix outgoing C-Find requests using "OutgoingFindRequestFilter()"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
2209
diff
changeset
|
54 call.PushJson(modality); |
3eefb84ac0bd
dynamically fix outgoing C-Find requests using "OutgoingFindRequestFilter()"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
2209
diff
changeset
|
55 FromDcmtkBridge::ExecuteToDicom(query, call); |
3eefb84ac0bd
dynamically fix outgoing C-Find requests using "OutgoingFindRequestFilter()"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
2209
diff
changeset
|
56 } |
3eefb84ac0bd
dynamically fix outgoing C-Find requests using "OutgoingFindRequestFilter()"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
2209
diff
changeset
|
57 } |
3eefb84ac0bd
dynamically fix outgoing C-Find requests using "OutgoingFindRequestFilter()"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
2209
diff
changeset
|
58 |
3eefb84ac0bd
dynamically fix outgoing C-Find requests using "OutgoingFindRequestFilter()"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
2209
diff
changeset
|
59 |
3eefb84ac0bd
dynamically fix outgoing C-Find requests using "OutgoingFindRequestFilter()"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
2209
diff
changeset
|
60 static void FixQuery(DicomMap& query, |
3eefb84ac0bd
dynamically fix outgoing C-Find requests using "OutgoingFindRequestFilter()"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
2209
diff
changeset
|
61 ModalityManufacturer manufacturer) |
3eefb84ac0bd
dynamically fix outgoing C-Find requests using "OutgoingFindRequestFilter()"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
2209
diff
changeset
|
62 { |
3eefb84ac0bd
dynamically fix outgoing C-Find requests using "OutgoingFindRequestFilter()"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
2209
diff
changeset
|
63 /** |
3eefb84ac0bd
dynamically fix outgoing C-Find requests using "OutgoingFindRequestFilter()"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
2209
diff
changeset
|
64 * Introduce patches for specific manufacturers below. |
3eefb84ac0bd
dynamically fix outgoing C-Find requests using "OutgoingFindRequestFilter()"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
2209
diff
changeset
|
65 **/ |
3eefb84ac0bd
dynamically fix outgoing C-Find requests using "OutgoingFindRequestFilter()"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
2209
diff
changeset
|
66 |
3eefb84ac0bd
dynamically fix outgoing C-Find requests using "OutgoingFindRequestFilter()"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
2209
diff
changeset
|
67 switch (manufacturer) |
3eefb84ac0bd
dynamically fix outgoing C-Find requests using "OutgoingFindRequestFilter()"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
2209
diff
changeset
|
68 { |
3eefb84ac0bd
dynamically fix outgoing C-Find requests using "OutgoingFindRequestFilter()"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
2209
diff
changeset
|
69 default: |
3eefb84ac0bd
dynamically fix outgoing C-Find requests using "OutgoingFindRequestFilter()"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
2209
diff
changeset
|
70 break; |
3eefb84ac0bd
dynamically fix outgoing C-Find requests using "OutgoingFindRequestFilter()"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
2209
diff
changeset
|
71 } |
3eefb84ac0bd
dynamically fix outgoing C-Find requests using "OutgoingFindRequestFilter()"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
2209
diff
changeset
|
72 } |
3eefb84ac0bd
dynamically fix outgoing C-Find requests using "OutgoingFindRequestFilter()"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
2209
diff
changeset
|
73 |
3eefb84ac0bd
dynamically fix outgoing C-Find requests using "OutgoingFindRequestFilter()"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
2209
diff
changeset
|
74 |
1368 | 75 void QueryRetrieveHandler::Invalidate() |
76 { | |
77 done_ = false; | |
78 answers_.Clear(); | |
79 } | |
80 | |
81 | |
82 void QueryRetrieveHandler::Run() | |
83 { | |
84 if (!done_) | |
85 { | |
2218
3eefb84ac0bd
dynamically fix outgoing C-Find requests using "OutgoingFindRequestFilter()"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
2209
diff
changeset
|
86 // Firstly, fix the content of the query for specific manufacturers |
3eefb84ac0bd
dynamically fix outgoing C-Find requests using "OutgoingFindRequestFilter()"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
2209
diff
changeset
|
87 DicomMap fixed; |
3eefb84ac0bd
dynamically fix outgoing C-Find requests using "OutgoingFindRequestFilter()"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
2209
diff
changeset
|
88 fixed.Assign(query_); |
3eefb84ac0bd
dynamically fix outgoing C-Find requests using "OutgoingFindRequestFilter()"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
2209
diff
changeset
|
89 FixQuery(fixed, modality_.GetManufacturer()); |
3eefb84ac0bd
dynamically fix outgoing C-Find requests using "OutgoingFindRequestFilter()"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
2209
diff
changeset
|
90 |
3eefb84ac0bd
dynamically fix outgoing C-Find requests using "OutgoingFindRequestFilter()"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
2209
diff
changeset
|
91 // Secondly, possibly fix the query with the user-provider Lua callback |
3eefb84ac0bd
dynamically fix outgoing C-Find requests using "OutgoingFindRequestFilter()"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
2209
diff
changeset
|
92 FixQuery(fixed, context_, modality_.GetApplicationEntityTitle()); |
3eefb84ac0bd
dynamically fix outgoing C-Find requests using "OutgoingFindRequestFilter()"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
2209
diff
changeset
|
93 |
3eefb84ac0bd
dynamically fix outgoing C-Find requests using "OutgoingFindRequestFilter()"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
2209
diff
changeset
|
94 { |
3eefb84ac0bd
dynamically fix outgoing C-Find requests using "OutgoingFindRequestFilter()"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
2209
diff
changeset
|
95 // Finally, run the C-FIND SCU against the fixed query |
3eefb84ac0bd
dynamically fix outgoing C-Find requests using "OutgoingFindRequestFilter()"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
2209
diff
changeset
|
96 ReusableDicomUserConnection::Locker locker(context_.GetReusableDicomUserConnection(), localAet_, modality_); |
3eefb84ac0bd
dynamically fix outgoing C-Find requests using "OutgoingFindRequestFilter()"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
2209
diff
changeset
|
97 locker.GetConnection().Find(answers_, level_, fixed); |
3eefb84ac0bd
dynamically fix outgoing C-Find requests using "OutgoingFindRequestFilter()"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
2209
diff
changeset
|
98 } |
3eefb84ac0bd
dynamically fix outgoing C-Find requests using "OutgoingFindRequestFilter()"
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
2209
diff
changeset
|
99 |
1368 | 100 done_ = true; |
101 } | |
102 } | |
103 | |
104 | |
105 QueryRetrieveHandler::QueryRetrieveHandler(ServerContext& context) : | |
106 context_(context), | |
1427
d710ea64f0fd
Custom setting of the local AET during C-Store SCU (both in Lua and in the REST API)
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
1368
diff
changeset
|
107 localAet_(context.GetDefaultLocalApplicationEntityTitle()), |
1368 | 108 done_(false), |
2059 | 109 level_(ResourceType_Study), |
110 answers_(false) | |
1368 | 111 { |
112 } | |
113 | |
114 | |
115 void QueryRetrieveHandler::SetModality(const std::string& symbolicName) | |
116 { | |
117 Invalidate(); | |
118 modalityName_ = symbolicName; | |
119 Configuration::GetDicomModalityUsingSymbolicName(modality_, symbolicName); | |
120 } | |
121 | |
122 | |
123 void QueryRetrieveHandler::SetLevel(ResourceType level) | |
124 { | |
125 Invalidate(); | |
126 level_ = level; | |
127 } | |
128 | |
129 | |
130 void QueryRetrieveHandler::SetQuery(const DicomTag& tag, | |
131 const std::string& value) | |
132 { | |
133 Invalidate(); | |
2007
655489d9165d
DicomMap::ParseDicomMetaInformation()
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
1900
diff
changeset
|
134 query_.SetValue(tag, value, false); |
1368 | 135 } |
136 | |
137 | |
138 size_t QueryRetrieveHandler::GetAnswerCount() | |
139 { | |
140 Run(); | |
141 return answers_.GetSize(); | |
142 } | |
143 | |
144 | |
1787
1b1d5470233f
refactoring of DicomFindAnswers
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
1427
diff
changeset
|
145 void QueryRetrieveHandler::GetAnswer(DicomMap& target, |
1b1d5470233f
refactoring of DicomFindAnswers
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
1427
diff
changeset
|
146 size_t i) |
1368 | 147 { |
148 Run(); | |
2209
e3fd5bc429a2
URI to reconstruct the main DICOM tags, the JSON summary and the metadata
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
2059
diff
changeset
|
149 answers_.GetAnswer(i).ExtractDicomSummary(target); |
1368 | 150 } |
151 | |
152 | |
153 void QueryRetrieveHandler::Retrieve(const std::string& target, | |
154 size_t i) | |
155 { | |
1787
1b1d5470233f
refactoring of DicomFindAnswers
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
1427
diff
changeset
|
156 DicomMap map; |
1b1d5470233f
refactoring of DicomFindAnswers
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
1427
diff
changeset
|
157 GetAnswer(map, i); |
1368 | 158 |
159 { | |
1787
1b1d5470233f
refactoring of DicomFindAnswers
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
1427
diff
changeset
|
160 ReusableDicomUserConnection::Locker locker(context_.GetReusableDicomUserConnection(), localAet_, modality_); |
1b1d5470233f
refactoring of DicomFindAnswers
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
1427
diff
changeset
|
161 locker.GetConnection().Move(target, map); |
1368 | 162 } |
163 } | |
164 | |
165 | |
166 void QueryRetrieveHandler::Retrieve(const std::string& target) | |
167 { | |
168 for (size_t i = 0; i < GetAnswerCount(); i++) | |
169 { | |
170 Retrieve(target, i); | |
171 } | |
172 } | |
173 } |