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 Lesser General Public License
9  * as published by the Free Software Foundation, either version 3 of
10  * the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this program. If not, see
19  * <http://www.gnu.org/licenses/>.
20  **/
21 
22 
23 #include "../PrecompiledHeaders.h"
24 #include "DicomValue.h"
25 
26 #include "../OrthancException.h"
27 #include "../SerializationToolbox.h"
28 #include "../Toolbox.h"
29 
30 #include <boost/lexical_cast.hpp>
31 
32 namespace Orthanc
33 {
DicomValue()34   DicomValue::DicomValue() :
35     type_(Type_Null)
36   {
37   }
38 
39 
DicomValue(const DicomValue & other)40   DicomValue::DicomValue(const DicomValue& other) :
41     type_(other.type_),
42     content_(other.content_)
43   {
44   }
45 
46 
DicomValue(const std::string & content,bool isBinary)47   DicomValue::DicomValue(const std::string& content,
48                          bool isBinary) :
49     type_(isBinary ? Type_Binary : Type_String),
50     content_(content)
51   {
52   }
53 
54 
DicomValue(const char * data,size_t size,bool isBinary)55   DicomValue::DicomValue(const char* data,
56                          size_t size,
57                          bool isBinary) :
58     type_(isBinary ? Type_Binary : Type_String)
59   {
60     content_.assign(data, size);
61   }
62 
63 
GetContent() const64   const std::string& DicomValue::GetContent() const
65   {
66     if (type_ == Type_Null)
67     {
68       throw OrthancException(ErrorCode_BadParameterType);
69     }
70     else
71     {
72       return content_;
73     }
74   }
75 
IsNull() const76   bool DicomValue::IsNull() const
77   {
78     return type_ == Type_Null;
79   }
80 
IsBinary() const81   bool DicomValue::IsBinary() const
82   {
83     return type_ == Type_Binary;
84   }
85 
86 
Clone() const87   DicomValue* DicomValue::Clone() const
88   {
89     return new DicomValue(*this);
90   }
91 
92 
93 #if ORTHANC_ENABLE_BASE64 == 1
FormatDataUriScheme(std::string & target,const std::string & mime) const94   void DicomValue::FormatDataUriScheme(std::string& target,
95                                        const std::string& mime) const
96   {
97     Toolbox::EncodeBase64(target, GetContent());
98     target.insert(0, "data:" + mime + ";base64,");
99   }
100 
FormatDataUriScheme(std::string & target) const101   void DicomValue::FormatDataUriScheme(std::string& target) const
102   {
103     FormatDataUriScheme(target, MIME_BINARY);
104   }
105 #endif
106 
107   // same as ParseValue but in case the value actually contains a sequence,
108   // it will return the first value
109   // this has been introduced to support invalid "width/height" DICOM tags in some US
110   // images where the width is stored as "800\0" !
111   template <typename T,
112             bool allowSigned>
ParseFirstValue(T & result,const DicomValue & source)113   static bool ParseFirstValue(T& result,
114                               const DicomValue& source)
115   {
116     if (source.IsBinary() ||
117         source.IsNull())
118     {
119       return false;
120     }
121 
122     try
123     {
124       std::string value = Toolbox::StripSpaces(source.GetContent());
125       if (value.empty())
126       {
127         return false;
128       }
129 
130       if (!allowSigned &&
131           value[0] == '-')
132       {
133         return false;
134       }
135 
136       if (value.find("\\") == std::string::npos)
137       {
138         result = boost::lexical_cast<T>(value);
139         return true;
140       }
141       else
142       {
143         std::vector<std::string> tokens;
144         Toolbox::TokenizeString(tokens, value, '\\');
145 
146         if (tokens.size() >= 1)
147         {
148           result = boost::lexical_cast<T>(tokens[0]);
149           return true;
150         }
151 
152         return false;
153       }
154     }
155     catch (boost::bad_lexical_cast&)
156     {
157       return false;
158     }
159   }
160 
161 
162   template <typename T,
163             bool allowSigned>
ParseValue(T & result,const DicomValue & source)164   static bool ParseValue(T& result,
165                          const DicomValue& source)
166   {
167     if (source.IsBinary() ||
168         source.IsNull())
169     {
170       return false;
171     }
172 
173     try
174     {
175       std::string value = Toolbox::StripSpaces(source.GetContent());
176       if (value.empty())
177       {
178         return false;
179       }
180 
181       if (!allowSigned &&
182           value[0] == '-')
183       {
184         return false;
185       }
186 
187       result = boost::lexical_cast<T>(value);
188       return true;
189     }
190     catch (boost::bad_lexical_cast&)
191     {
192       return false;
193     }
194   }
195 
ParseInteger32(int32_t & result) const196   bool DicomValue::ParseInteger32(int32_t& result) const
197   {
198     int64_t tmp;
199     if (ParseValue<int64_t, true>(tmp, *this))
200     {
201       result = static_cast<int32_t>(tmp);
202       return (tmp == static_cast<int64_t>(result));  // Check no overflow occurs
203     }
204     else
205     {
206       return false;
207     }
208   }
209 
ParseInteger64(int64_t & result) const210   bool DicomValue::ParseInteger64(int64_t& result) const
211   {
212     return ParseValue<int64_t, true>(result, *this);
213   }
214 
ParseUnsignedInteger32(uint32_t & result) const215   bool DicomValue::ParseUnsignedInteger32(uint32_t& result) const
216   {
217     uint64_t tmp;
218     if (ParseValue<uint64_t, false>(tmp, *this))
219     {
220       result = static_cast<uint32_t>(tmp);
221       return (tmp == static_cast<uint64_t>(result));  // Check no overflow occurs
222     }
223     else
224     {
225       return false;
226     }
227   }
228 
ParseUnsignedInteger64(uint64_t & result) const229   bool DicomValue::ParseUnsignedInteger64(uint64_t& result) const
230   {
231     return ParseValue<uint64_t, false>(result, *this);
232   }
233 
ParseFloat(float & result) const234   bool DicomValue::ParseFloat(float& result) const
235   {
236     return ParseValue<float, true>(result, *this);
237   }
238 
ParseDouble(double & result) const239   bool DicomValue::ParseDouble(double& result) const
240   {
241     return ParseValue<double, true>(result, *this);
242   }
243 
ParseFirstFloat(float & result) const244   bool DicomValue::ParseFirstFloat(float& result) const
245   {
246     return ParseFirstValue<float, true>(result, *this);
247   }
248 
ParseFirstUnsignedInteger(unsigned int & result) const249   bool DicomValue::ParseFirstUnsignedInteger(unsigned int& result) const
250   {
251     return ParseFirstValue<unsigned int, true>(result, *this);
252   }
253 
CopyToString(std::string & result,bool allowBinary) const254   bool DicomValue::CopyToString(std::string& result,
255                                 bool allowBinary) const
256   {
257     if (IsNull())
258     {
259       return false;
260     }
261     else if (IsBinary() && !allowBinary)
262     {
263       return false;
264     }
265     else
266     {
267       result.assign(content_);
268       return true;
269     }
270   }
271 
272 
273   static const char* KEY_TYPE = "Type";
274   static const char* KEY_CONTENT = "Content";
275 
Serialize(Json::Value & target) const276   void DicomValue::Serialize(Json::Value& target) const
277   {
278     target = Json::objectValue;
279 
280     switch (type_)
281     {
282       case Type_Null:
283         target[KEY_TYPE] = "Null";
284         break;
285 
286       case Type_String:
287         target[KEY_TYPE] = "String";
288         target[KEY_CONTENT] = content_;
289         break;
290 
291       case Type_Binary:
292       {
293         target[KEY_TYPE] = "Binary";
294 
295         std::string base64;
296         Toolbox::EncodeBase64(base64, content_);
297         target[KEY_CONTENT] = base64;
298         break;
299       }
300 
301       default:
302         throw OrthancException(ErrorCode_InternalError);
303     }
304   }
305 
Unserialize(const Json::Value & source)306   void DicomValue::Unserialize(const Json::Value& source)
307   {
308     std::string type = SerializationToolbox::ReadString(source, KEY_TYPE);
309 
310     if (type == "Null")
311     {
312       type_ = Type_Null;
313       content_.clear();
314     }
315     else if (type == "String")
316     {
317       type_ = Type_String;
318       content_ = SerializationToolbox::ReadString(source, KEY_CONTENT);
319     }
320     else if (type == "Binary")
321     {
322       type_ = Type_Binary;
323 
324       const std::string base64 =SerializationToolbox::ReadString(source, KEY_CONTENT);
325       Toolbox::DecodeBase64(content_, base64);
326     }
327     else
328     {
329       throw OrthancException(ErrorCode_BadFileFormat);
330     }
331   }
332 }
333