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-2021 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 "RemoteModalityParameters.h"
25 
26 #include "../Logging.h"
27 #include "../OrthancException.h"
28 #include "../SerializationToolbox.h"
29 
30 #include <boost/lexical_cast.hpp>
31 #include <stdexcept>
32 
33 
34 static const char* KEY_AET = "AET";
35 static const char* KEY_ALLOW_ECHO = "AllowEcho";
36 static const char* KEY_ALLOW_FIND = "AllowFind";
37 static const char* KEY_ALLOW_GET = "AllowGet";
38 static const char* KEY_ALLOW_MOVE = "AllowMove";
39 static const char* KEY_ALLOW_N_ACTION = "AllowNAction";
40 static const char* KEY_ALLOW_N_EVENT_REPORT = "AllowEventReport";
41 static const char* KEY_ALLOW_STORAGE_COMMITMENT = "AllowStorageCommitment";
42 static const char* KEY_ALLOW_STORE = "AllowStore";
43 static const char* KEY_ALLOW_TRANSCODING = "AllowTranscoding";
44 static const char* KEY_HOST = "Host";
45 static const char* KEY_MANUFACTURER = "Manufacturer";
46 static const char* KEY_PORT = "Port";
47 static const char* KEY_USE_DICOM_TLS = "UseDicomTls";
48 static const char* KEY_LOCAL_AET = "LocalAet";
49 static const char* KEY_TIMEOUT = "Timeout";
50 
51 
52 namespace Orthanc
53 {
Clear()54   void RemoteModalityParameters::Clear()
55   {
56     aet_ = "ORTHANC";
57     host_ = "127.0.0.1";
58     port_ = 104;
59     manufacturer_ = ModalityManufacturer_Generic;
60     allowEcho_ = true;
61     allowStore_ = true;
62     allowFind_ = true;
63     allowMove_ = true;
64     allowGet_ = true;
65     allowNAction_ = true;  // For storage commitment
66     allowNEventReport_ = true;  // For storage commitment
67     allowTranscoding_ = true;
68     useDicomTls_ = false;
69     localAet_.clear();
70     timeout_ = 0;
71   }
72 
73 
RemoteModalityParameters()74   RemoteModalityParameters::RemoteModalityParameters()
75   {
76     Clear();
77   }
78 
RemoteModalityParameters(const Json::Value & serialized)79   RemoteModalityParameters::RemoteModalityParameters(const Json::Value &serialized)
80   {
81     Unserialize(serialized);
82   }
83 
RemoteModalityParameters(const std::string & aet,const std::string & host,uint16_t port,ModalityManufacturer manufacturer)84   RemoteModalityParameters::RemoteModalityParameters(const std::string& aet,
85                                                      const std::string& host,
86                                                      uint16_t port,
87                                                      ModalityManufacturer manufacturer)
88   {
89     Clear();
90     SetApplicationEntityTitle(aet);
91     SetHost(host);
92     SetPortNumber(port);
93     SetManufacturer(manufacturer);
94   }
95 
GetApplicationEntityTitle() const96   const std::string &RemoteModalityParameters::GetApplicationEntityTitle() const
97   {
98     return aet_;
99   }
100 
SetApplicationEntityTitle(const std::string & aet)101   void RemoteModalityParameters::SetApplicationEntityTitle(const std::string &aet)
102   {
103     aet_ = aet;
104   }
105 
GetHost() const106   const std::string &RemoteModalityParameters::GetHost() const
107   {
108     return host_;
109   }
110 
SetHost(const std::string & host)111   void RemoteModalityParameters::SetHost(const std::string &host)
112   {
113     host_ = host;
114   }
115 
GetPortNumber() const116   uint16_t RemoteModalityParameters::GetPortNumber() const
117   {
118     return port_;
119   }
120 
121 
CheckPortNumber(int value)122   static void CheckPortNumber(int value)
123   {
124     if (value <= 0 ||
125         value >= 65535)
126     {
127       throw OrthancException(ErrorCode_ParameterOutOfRange,
128                              "A TCP port number must be in range [1..65534], but found: " +
129                              boost::lexical_cast<std::string>(value));
130     }
131   }
132 
133 
ReadPortNumber(const Json::Value & value)134   static uint16_t ReadPortNumber(const Json::Value& value)
135   {
136     int tmp;
137 
138     switch (value.type())
139     {
140       case Json::intValue:
141       case Json::uintValue:
142         tmp = value.asInt();
143         break;
144 
145       case Json::stringValue:
146         try
147         {
148           tmp = boost::lexical_cast<int>(value.asString());
149         }
150         catch (boost::bad_lexical_cast&)
151         {
152           throw OrthancException(ErrorCode_BadFileFormat);
153         }
154         break;
155 
156       default:
157         throw OrthancException(ErrorCode_BadFileFormat);
158     }
159 
160     CheckPortNumber(tmp);
161     return static_cast<uint16_t>(tmp);
162   }
163 
164 
SetPortNumber(uint16_t port)165   void RemoteModalityParameters::SetPortNumber(uint16_t port)
166   {
167     CheckPortNumber(port);
168     port_ = port;
169   }
170 
GetManufacturer() const171   ModalityManufacturer RemoteModalityParameters::GetManufacturer() const
172   {
173     return manufacturer_;
174   }
175 
SetManufacturer(ModalityManufacturer manufacturer)176   void RemoteModalityParameters::SetManufacturer(ModalityManufacturer manufacturer)
177   {
178     manufacturer_ = manufacturer;
179   }
180 
SetManufacturer(const std::string & manufacturer)181   void RemoteModalityParameters::SetManufacturer(const std::string &manufacturer)
182   {
183     manufacturer_ = StringToModalityManufacturer(manufacturer);
184   }
185 
186 
UnserializeArray(const Json::Value & serialized)187   void RemoteModalityParameters::UnserializeArray(const Json::Value& serialized)
188   {
189     assert(serialized.type() == Json::arrayValue);
190 
191     if ((serialized.size() != 3 &&
192          serialized.size() != 4) ||
193         serialized[0].type() != Json::stringValue ||
194         serialized[1].type() != Json::stringValue ||
195         (serialized.size() == 4 &&
196          serialized[3].type() != Json::stringValue))
197     {
198       throw OrthancException(ErrorCode_BadFileFormat);
199     }
200 
201     aet_ = serialized[0].asString();
202     host_ = serialized[1].asString();
203     port_ = ReadPortNumber(serialized[2]);
204 
205     if (serialized.size() == 4)
206     {
207       manufacturer_ = StringToModalityManufacturer(serialized[3].asString());
208     }
209     else
210     {
211       manufacturer_ = ModalityManufacturer_Generic;
212     }
213   }
214 
215 
UnserializeObject(const Json::Value & serialized)216   void RemoteModalityParameters::UnserializeObject(const Json::Value& serialized)
217   {
218     assert(serialized.type() == Json::objectValue);
219 
220     aet_ = SerializationToolbox::ReadString(serialized, KEY_AET);
221     host_ = SerializationToolbox::ReadString(serialized, KEY_HOST);
222 
223     if (serialized.isMember(KEY_PORT))
224     {
225       port_ = ReadPortNumber(serialized[KEY_PORT]);
226     }
227     else
228     {
229       throw OrthancException(ErrorCode_BadFileFormat);
230     }
231 
232     if (serialized.isMember(KEY_MANUFACTURER))
233     {
234       manufacturer_ = StringToModalityManufacturer
235         (SerializationToolbox::ReadString(serialized, KEY_MANUFACTURER));
236     }
237     else
238     {
239       manufacturer_ = ModalityManufacturer_Generic;
240     }
241 
242     if (serialized.isMember(KEY_ALLOW_ECHO))
243     {
244       allowEcho_ = SerializationToolbox::ReadBoolean(serialized, KEY_ALLOW_ECHO);
245     }
246 
247     if (serialized.isMember(KEY_ALLOW_FIND))
248     {
249       allowFind_ = SerializationToolbox::ReadBoolean(serialized, KEY_ALLOW_FIND);
250     }
251 
252     if (serialized.isMember(KEY_ALLOW_STORE))
253     {
254       allowStore_ = SerializationToolbox::ReadBoolean(serialized, KEY_ALLOW_STORE);
255     }
256 
257     if (serialized.isMember(KEY_ALLOW_GET))
258     {
259       allowGet_ = SerializationToolbox::ReadBoolean(serialized, KEY_ALLOW_GET);
260     }
261 
262     if (serialized.isMember(KEY_ALLOW_MOVE))
263     {
264       allowMove_ = SerializationToolbox::ReadBoolean(serialized, KEY_ALLOW_MOVE);
265     }
266 
267     if (serialized.isMember(KEY_ALLOW_N_ACTION))
268     {
269       allowNAction_ = SerializationToolbox::ReadBoolean(serialized, KEY_ALLOW_N_ACTION);
270     }
271 
272     if (serialized.isMember(KEY_ALLOW_N_EVENT_REPORT))
273     {
274       allowNEventReport_ = SerializationToolbox::ReadBoolean(serialized, KEY_ALLOW_N_EVENT_REPORT);
275     }
276 
277     if (serialized.isMember(KEY_ALLOW_STORAGE_COMMITMENT))
278     {
279       bool allow = SerializationToolbox::ReadBoolean(serialized, KEY_ALLOW_STORAGE_COMMITMENT);
280       allowNAction_ = allow;
281       allowNEventReport_ = allow;
282     }
283 
284     if (serialized.isMember(KEY_ALLOW_TRANSCODING))
285     {
286       allowTranscoding_ = SerializationToolbox::ReadBoolean(serialized, KEY_ALLOW_TRANSCODING);
287     }
288 
289     if (serialized.isMember(KEY_USE_DICOM_TLS))
290     {
291       useDicomTls_ = SerializationToolbox::ReadBoolean(serialized, KEY_USE_DICOM_TLS);
292     }
293 
294     if (serialized.isMember(KEY_LOCAL_AET))
295     {
296       localAet_ = SerializationToolbox::ReadString(serialized, KEY_LOCAL_AET);
297     }
298 
299     if (serialized.isMember(KEY_TIMEOUT))
300     {
301       timeout_ = SerializationToolbox::ReadUnsignedInteger(serialized, KEY_TIMEOUT);
302     }
303   }
304 
305 
IsRequestAllowed(DicomRequestType type) const306   bool RemoteModalityParameters::IsRequestAllowed(DicomRequestType type) const
307   {
308     switch (type)
309     {
310     case DicomRequestType_Echo:
311         return allowEcho_;
312 
313       case DicomRequestType_Find:
314         return allowFind_;
315 
316       case DicomRequestType_Get:
317         return allowGet_;
318 
319       case DicomRequestType_Move:
320         return allowMove_;
321 
322       case DicomRequestType_Store:
323         return allowStore_;
324 
325       case DicomRequestType_NAction:
326         return allowNAction_;
327 
328       case DicomRequestType_NEventReport:
329         return allowNEventReport_;
330 
331       default:
332         throw OrthancException(ErrorCode_ParameterOutOfRange);
333     }
334   }
335 
336 
SetRequestAllowed(DicomRequestType type,bool allowed)337   void RemoteModalityParameters::SetRequestAllowed(DicomRequestType type,
338                                                    bool allowed)
339   {
340     switch (type)
341     {
342       case DicomRequestType_Echo:
343         allowEcho_ = allowed;
344         break;
345 
346       case DicomRequestType_Find:
347         allowFind_ = allowed;
348         break;
349 
350       case DicomRequestType_Get:
351         allowGet_ = allowed;
352         break;
353 
354       case DicomRequestType_Move:
355         allowMove_ = allowed;
356         break;
357 
358       case DicomRequestType_Store:
359         allowStore_ = allowed;
360         break;
361 
362       case DicomRequestType_NAction:
363         allowNAction_ = allowed;
364         break;
365 
366       case DicomRequestType_NEventReport:
367         allowNEventReport_ = allowed;
368         break;
369 
370       default:
371         throw OrthancException(ErrorCode_ParameterOutOfRange);
372     }
373   }
374 
375 
IsAdvancedFormatNeeded() const376   bool RemoteModalityParameters::IsAdvancedFormatNeeded() const
377   {
378     return (!allowEcho_ ||
379             !allowStore_ ||
380             !allowFind_ ||
381             !allowGet_ ||
382             !allowMove_ ||
383             !allowNAction_ ||
384             !allowNEventReport_ ||
385             !allowTranscoding_ ||
386             useDicomTls_ ||
387             HasLocalAet());
388   }
389 
390 
Serialize(Json::Value & target,bool forceAdvancedFormat) const391   void RemoteModalityParameters::Serialize(Json::Value& target,
392                                            bool forceAdvancedFormat) const
393   {
394     if (forceAdvancedFormat ||
395         IsAdvancedFormatNeeded())
396     {
397       target = Json::objectValue;
398       target[KEY_AET] = aet_;
399       target[KEY_HOST] = host_;
400       target[KEY_PORT] = port_;
401       target[KEY_MANUFACTURER] = EnumerationToString(manufacturer_);
402       target[KEY_ALLOW_ECHO] = allowEcho_;
403       target[KEY_ALLOW_STORE] = allowStore_;
404       target[KEY_ALLOW_FIND] = allowFind_;
405       target[KEY_ALLOW_GET] = allowGet_;
406       target[KEY_ALLOW_MOVE] = allowMove_;
407       target[KEY_ALLOW_N_ACTION] = allowNAction_;
408       target[KEY_ALLOW_N_EVENT_REPORT] = allowNEventReport_;
409       target[KEY_ALLOW_TRANSCODING] = allowTranscoding_;
410       target[KEY_USE_DICOM_TLS] = useDicomTls_;
411       target[KEY_LOCAL_AET] = localAet_;
412       target[KEY_TIMEOUT] = timeout_;
413     }
414     else
415     {
416       target = Json::arrayValue;
417       target.append(GetApplicationEntityTitle());
418       target.append(GetHost());
419       target.append(GetPortNumber());
420       target.append(EnumerationToString(GetManufacturer()));
421     }
422   }
423 
424 
Unserialize(const Json::Value & serialized)425   void RemoteModalityParameters::Unserialize(const Json::Value& serialized)
426   {
427     Clear();
428 
429     switch (serialized.type())
430     {
431       case Json::objectValue:
432         UnserializeObject(serialized);
433         break;
434 
435       case Json::arrayValue:
436         UnserializeArray(serialized);
437         break;
438 
439       default:
440         throw OrthancException(ErrorCode_BadFileFormat);
441     }
442   }
443 
IsTranscodingAllowed() const444   bool RemoteModalityParameters::IsTranscodingAllowed() const
445   {
446     return allowTranscoding_;
447   }
448 
SetTranscodingAllowed(bool allowed)449   void RemoteModalityParameters::SetTranscodingAllowed(bool allowed)
450   {
451     allowTranscoding_ = allowed;
452   }
453 
IsDicomTlsEnabled() const454   bool RemoteModalityParameters::IsDicomTlsEnabled() const
455   {
456     return useDicomTls_;
457   }
458 
SetDicomTlsEnabled(bool enabled)459   void RemoteModalityParameters::SetDicomTlsEnabled(bool enabled)
460   {
461     useDicomTls_ = enabled;
462   }
463 
HasLocalAet() const464   bool RemoteModalityParameters::HasLocalAet() const
465   {
466     return !localAet_.empty();
467   }
468 
GetLocalAet() const469   const std::string& RemoteModalityParameters::GetLocalAet() const
470   {
471     if (localAet_.empty())
472     {
473       throw OrthancException(ErrorCode_BadSequenceOfCalls, "You should have called HasLocalAet()");
474     }
475     else
476     {
477       return localAet_;
478     }
479   }
480 
SetLocalAet(const std::string & aet)481   void RemoteModalityParameters::SetLocalAet(const std::string& aet)
482   {
483     if (aet.empty())
484     {
485       throw OrthancException(ErrorCode_ParameterOutOfRange);
486     }
487     else
488     {
489       localAet_ = aet;
490     }
491   }
492 
SetTimeout(uint32_t seconds)493   void RemoteModalityParameters::SetTimeout(uint32_t seconds)
494   {
495     timeout_ = seconds;
496   }
497 
GetTimeout() const498   uint32_t RemoteModalityParameters::GetTimeout() const
499   {
500     return timeout_;
501   }
502 
HasTimeout() const503   bool RemoteModalityParameters::HasTimeout() const
504   {
505     return timeout_ != 0;
506   }
507 }
508