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 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 "ServerEnumerations.h" 36 37 #include "../../OrthancFramework/Sources/OrthancException.h" 38 #include "../../OrthancFramework/Sources/EnumerationDictionary.h" 39 #include "../../OrthancFramework/Sources/Logging.h" 40 #include "../../OrthancFramework/Sources/Toolbox.h" 41 42 #include <boost/thread.hpp> 43 44 namespace Orthanc 45 { 46 typedef std::map<FileContentType, std::string> MimeTypes; 47 48 static boost::mutex enumerationsMutex_; 49 static EnumerationDictionary<MetadataType> dictMetadataType_; 50 static EnumerationDictionary<FileContentType> dictContentType_; 51 static MimeTypes mimeTypes_; 52 InitializeServerEnumerations()53 void InitializeServerEnumerations() 54 { 55 boost::mutex::scoped_lock lock(enumerationsMutex_); 56 57 dictMetadataType_.Clear(); 58 dictContentType_.Clear(); 59 60 dictMetadataType_.Add(MetadataType_Instance_IndexInSeries, "IndexInSeries"); 61 dictMetadataType_.Add(MetadataType_Instance_ReceptionDate, "ReceptionDate"); 62 dictMetadataType_.Add(MetadataType_RemoteAet, "RemoteAET"); 63 dictMetadataType_.Add(MetadataType_Series_ExpectedNumberOfInstances, "ExpectedNumberOfInstances"); 64 dictMetadataType_.Add(MetadataType_ModifiedFrom, "ModifiedFrom"); 65 dictMetadataType_.Add(MetadataType_AnonymizedFrom, "AnonymizedFrom"); 66 dictMetadataType_.Add(MetadataType_LastUpdate, "LastUpdate"); 67 dictMetadataType_.Add(MetadataType_Instance_Origin, "Origin"); 68 dictMetadataType_.Add(MetadataType_Instance_TransferSyntax, "TransferSyntax"); 69 dictMetadataType_.Add(MetadataType_Instance_SopClassUid, "SopClassUid"); 70 dictMetadataType_.Add(MetadataType_Instance_RemoteIp, "RemoteIP"); 71 dictMetadataType_.Add(MetadataType_Instance_CalledAet, "CalledAET"); 72 dictMetadataType_.Add(MetadataType_Instance_HttpUsername, "HttpUsername"); 73 dictMetadataType_.Add(MetadataType_Instance_PixelDataOffset, "PixelDataOffset"); 74 75 dictContentType_.Add(FileContentType_Dicom, "dicom"); 76 dictContentType_.Add(FileContentType_DicomAsJson, "dicom-as-json"); 77 dictContentType_.Add(FileContentType_DicomUntilPixelData, "dicom-until-pixel-data"); 78 } 79 RegisterUserMetadata(int metadata,const std::string & name)80 void RegisterUserMetadata(int metadata, 81 const std::string& name) 82 { 83 boost::mutex::scoped_lock lock(enumerationsMutex_); 84 85 MetadataType type = static_cast<MetadataType>(metadata); 86 87 if (metadata < 0 || 88 !IsUserMetadata(type)) 89 { 90 LOG(ERROR) << "A user content type must have index between " 91 << static_cast<int>(MetadataType_StartUser) << " and " 92 << static_cast<int>(MetadataType_EndUser) << ", but \"" 93 << name << "\" has index " << metadata; 94 95 throw OrthancException(ErrorCode_ParameterOutOfRange); 96 } 97 98 if (dictMetadataType_.Contains(type)) 99 { 100 LOG(ERROR) << "Cannot associate user content type \"" 101 << name << "\" with index " << metadata 102 << ", as this index is already used"; 103 104 throw OrthancException(ErrorCode_ParameterOutOfRange); 105 } 106 107 dictMetadataType_.Add(type, name); 108 } 109 EnumerationToString(MetadataType type)110 std::string EnumerationToString(MetadataType type) 111 { 112 // This function MUST return a "std::string" and not "const 113 // char*", as the result is not a static string 114 boost::mutex::scoped_lock lock(enumerationsMutex_); 115 return dictMetadataType_.Translate(type); 116 } 117 StringToMetadata(const std::string & str)118 MetadataType StringToMetadata(const std::string& str) 119 { 120 boost::mutex::scoped_lock lock(enumerationsMutex_); 121 return dictMetadataType_.Translate(str); 122 } 123 RegisterUserContentType(int contentType,const std::string & name,const std::string & mime)124 void RegisterUserContentType(int contentType, 125 const std::string& name, 126 const std::string& mime) 127 { 128 boost::mutex::scoped_lock lock(enumerationsMutex_); 129 130 FileContentType type = static_cast<FileContentType>(contentType); 131 132 if (contentType < 0 || 133 !IsUserContentType(type)) 134 { 135 LOG(ERROR) << "A user content type must have index between " 136 << static_cast<int>(FileContentType_StartUser) << " and " 137 << static_cast<int>(FileContentType_EndUser) << ", but \"" 138 << name << "\" has index " << contentType; 139 140 throw OrthancException(ErrorCode_ParameterOutOfRange); 141 } 142 143 if (dictContentType_.Contains(type)) 144 { 145 LOG(ERROR) << "Cannot associate user content type \"" 146 << name << "\" with index " << contentType 147 << ", as this index is already used"; 148 149 throw OrthancException(ErrorCode_ParameterOutOfRange); 150 } 151 152 dictContentType_.Add(type, name); 153 mimeTypes_[type] = mime; 154 } 155 EnumerationToString(FileContentType type)156 std::string EnumerationToString(FileContentType type) 157 { 158 // This function MUST return a "std::string" and not "const 159 // char*", as the result is not a static string 160 boost::mutex::scoped_lock lock(enumerationsMutex_); 161 return dictContentType_.Translate(type); 162 } 163 GetFileContentMime(FileContentType type)164 std::string GetFileContentMime(FileContentType type) 165 { 166 if (type >= FileContentType_StartUser && 167 type <= FileContentType_EndUser) 168 { 169 boost::mutex::scoped_lock lock(enumerationsMutex_); 170 171 MimeTypes::const_iterator it = mimeTypes_.find(type); 172 if (it != mimeTypes_.end()) 173 { 174 return it->second; 175 } 176 } 177 178 switch (type) 179 { 180 case FileContentType_Dicom: 181 return EnumerationToString(MimeType_Dicom); 182 183 case FileContentType_DicomAsJson: 184 return MIME_JSON_UTF8; 185 186 default: 187 return EnumerationToString(MimeType_Binary); 188 } 189 } 190 StringToContentType(const std::string & str)191 FileContentType StringToContentType(const std::string& str) 192 { 193 boost::mutex::scoped_lock lock(enumerationsMutex_); 194 return dictContentType_.Translate(str); 195 } 196 197 StringToFindStorageAccessMode(const std::string & value)198 FindStorageAccessMode StringToFindStorageAccessMode(const std::string& value) 199 { 200 if (value == "Always") 201 { 202 return FindStorageAccessMode_DiskOnLookupAndAnswer; 203 } 204 else if (value == "Never") 205 { 206 return FindStorageAccessMode_DatabaseOnly; 207 } 208 else if (value == "Answers") 209 { 210 return FindStorageAccessMode_DiskOnAnswer; 211 } 212 else 213 { 214 throw OrthancException(ErrorCode_ParameterOutOfRange, 215 "Configuration option \"StorageAccessOnFind\" " 216 "should be \"Always\", \"Never\" or \"Answers\": " + value); 217 } 218 } 219 220 StringToBuiltinDecoderTranscoderOrder(const std::string & value)221 BuiltinDecoderTranscoderOrder StringToBuiltinDecoderTranscoderOrder(const std::string& value) 222 { 223 if (value == "Before") 224 { 225 return BuiltinDecoderTranscoderOrder_Before; 226 } 227 else if (value == "After") 228 { 229 return BuiltinDecoderTranscoderOrder_After; 230 } 231 else if (value == "Disabled") 232 { 233 return BuiltinDecoderTranscoderOrder_Disabled; 234 } 235 else 236 { 237 throw OrthancException(ErrorCode_ParameterOutOfRange, 238 "Configuration option \"BuiltinDecoderTranscoderOrder\" " 239 "should be \"After\", \"Before\" or \"Disabled\": " + value); 240 } 241 } 242 243 StringToVerbosity(const std::string & str)244 Verbosity StringToVerbosity(const std::string& str) 245 { 246 if (str == "default") 247 { 248 return Verbosity_Default; 249 } 250 else if (str == "verbose") 251 { 252 return Verbosity_Verbose; 253 } 254 else if (str == "trace") 255 { 256 return Verbosity_Trace; 257 } 258 else 259 { 260 throw OrthancException(ErrorCode_ParameterOutOfRange, 261 "Verbosity can be \"default\", \"verbose\" or \"trace\": " + str); 262 } 263 } 264 265 GetBasePath(ResourceType type,const std::string & publicId)266 std::string GetBasePath(ResourceType type, 267 const std::string& publicId) 268 { 269 switch (type) 270 { 271 case ResourceType_Patient: 272 return "/patients/" + publicId; 273 274 case ResourceType_Study: 275 return "/studies/" + publicId; 276 277 case ResourceType_Series: 278 return "/series/" + publicId; 279 280 case ResourceType_Instance: 281 return "/instances/" + publicId; 282 283 default: 284 throw OrthancException(ErrorCode_ParameterOutOfRange); 285 } 286 } 287 EnumerationToString(SeriesStatus status)288 const char* EnumerationToString(SeriesStatus status) 289 { 290 switch (status) 291 { 292 case SeriesStatus_Complete: 293 return "Complete"; 294 295 case SeriesStatus_Missing: 296 return "Missing"; 297 298 case SeriesStatus_Inconsistent: 299 return "Inconsistent"; 300 301 case SeriesStatus_Unknown: 302 return "Unknown"; 303 304 default: 305 throw OrthancException(ErrorCode_ParameterOutOfRange); 306 } 307 } 308 EnumerationToString(StoreStatus status)309 const char* EnumerationToString(StoreStatus status) 310 { 311 switch (status) 312 { 313 case StoreStatus_Success: 314 return "Success"; 315 316 case StoreStatus_AlreadyStored: 317 return "AlreadyStored"; 318 319 case StoreStatus_Failure: 320 return "Failure"; 321 322 case StoreStatus_FilteredOut: 323 return "FilteredOut"; 324 325 default: 326 throw OrthancException(ErrorCode_ParameterOutOfRange); 327 } 328 } 329 330 EnumerationToString(ChangeType type)331 const char* EnumerationToString(ChangeType type) 332 { 333 switch (type) 334 { 335 case ChangeType_CompletedSeries: 336 return "CompletedSeries"; 337 338 case ChangeType_NewInstance: 339 return "NewInstance"; 340 341 case ChangeType_NewPatient: 342 return "NewPatient"; 343 344 case ChangeType_NewSeries: 345 return "NewSeries"; 346 347 case ChangeType_NewStudy: 348 return "NewStudy"; 349 350 case ChangeType_AnonymizedStudy: 351 return "AnonymizedStudy"; 352 353 case ChangeType_AnonymizedSeries: 354 return "AnonymizedSeries"; 355 356 case ChangeType_ModifiedStudy: 357 return "ModifiedStudy"; 358 359 case ChangeType_ModifiedSeries: 360 return "ModifiedSeries"; 361 362 case ChangeType_AnonymizedPatient: 363 return "AnonymizedPatient"; 364 365 case ChangeType_ModifiedPatient: 366 return "ModifiedPatient"; 367 368 case ChangeType_StablePatient: 369 return "StablePatient"; 370 371 case ChangeType_StableStudy: 372 return "StableStudy"; 373 374 case ChangeType_StableSeries: 375 return "StableSeries"; 376 377 case ChangeType_Deleted: 378 return "Deleted"; 379 380 case ChangeType_NewChildInstance: 381 return "NewChildInstance"; 382 383 case ChangeType_UpdatedAttachment: 384 return "UpdatedAttachment"; 385 386 case ChangeType_UpdatedMetadata: 387 return "UpdatedMetadata"; 388 389 default: 390 throw OrthancException(ErrorCode_ParameterOutOfRange); 391 } 392 } 393 394 EnumerationToString(Verbosity verbosity)395 const char* EnumerationToString(Verbosity verbosity) 396 { 397 switch (verbosity) 398 { 399 case Verbosity_Default: 400 return "default"; 401 402 case Verbosity_Verbose: 403 return "verbose"; 404 405 case Verbosity_Trace: 406 return "trace"; 407 408 default: 409 throw OrthancException(ErrorCode_ParameterOutOfRange); 410 } 411 } 412 413 IsUserMetadata(MetadataType metadata)414 bool IsUserMetadata(MetadataType metadata) 415 { 416 return (metadata >= MetadataType_StartUser && 417 metadata <= MetadataType_EndUser); 418 } 419 420 GetTransferSyntaxGroup(std::set<DicomTransferSyntax> & target,TransferSyntaxGroup source)421 void GetTransferSyntaxGroup(std::set<DicomTransferSyntax>& target, 422 TransferSyntaxGroup source) 423 { 424 target.clear(); 425 426 switch (source) 427 { 428 // Transfer syntaxes supported since Orthanc 0.7.2 429 case TransferSyntaxGroup_Deflated: 430 target.insert(DicomTransferSyntax_DeflatedLittleEndianExplicit); 431 break; 432 433 case TransferSyntaxGroup_Jpeg: 434 target.insert(DicomTransferSyntax_JPEGProcess1); 435 target.insert(DicomTransferSyntax_JPEGProcess2_4); 436 target.insert(DicomTransferSyntax_JPEGProcess3_5); 437 target.insert(DicomTransferSyntax_JPEGProcess6_8); 438 target.insert(DicomTransferSyntax_JPEGProcess7_9); 439 target.insert(DicomTransferSyntax_JPEGProcess10_12); 440 target.insert(DicomTransferSyntax_JPEGProcess11_13); 441 target.insert(DicomTransferSyntax_JPEGProcess14); 442 target.insert(DicomTransferSyntax_JPEGProcess15); 443 target.insert(DicomTransferSyntax_JPEGProcess16_18); 444 target.insert(DicomTransferSyntax_JPEGProcess17_19); 445 target.insert(DicomTransferSyntax_JPEGProcess20_22); 446 target.insert(DicomTransferSyntax_JPEGProcess21_23); 447 target.insert(DicomTransferSyntax_JPEGProcess24_26); 448 target.insert(DicomTransferSyntax_JPEGProcess25_27); 449 target.insert(DicomTransferSyntax_JPEGProcess28); 450 target.insert(DicomTransferSyntax_JPEGProcess29); 451 target.insert(DicomTransferSyntax_JPEGProcess14SV1); 452 break; 453 454 case TransferSyntaxGroup_Jpeg2000: 455 target.insert(DicomTransferSyntax_JPEG2000); 456 target.insert(DicomTransferSyntax_JPEG2000LosslessOnly); 457 target.insert(DicomTransferSyntax_JPEG2000Multicomponent); 458 target.insert(DicomTransferSyntax_JPEG2000MulticomponentLosslessOnly); 459 break; 460 461 case TransferSyntaxGroup_JpegLossless: 462 target.insert(DicomTransferSyntax_JPEGLSLossless); 463 target.insert(DicomTransferSyntax_JPEGLSLossy); 464 break; 465 466 case TransferSyntaxGroup_Jpip: 467 target.insert(DicomTransferSyntax_JPIPReferenced); 468 target.insert(DicomTransferSyntax_JPIPReferencedDeflate); 469 break; 470 471 case TransferSyntaxGroup_Mpeg2: 472 target.insert(DicomTransferSyntax_MPEG2MainProfileAtMainLevel); 473 target.insert(DicomTransferSyntax_MPEG2MainProfileAtHighLevel); 474 break; 475 476 case TransferSyntaxGroup_Rle: 477 target.insert(DicomTransferSyntax_RLELossless); 478 break; 479 480 case TransferSyntaxGroup_Mpeg4: 481 // New in Orthanc 1.6.0 482 target.insert(DicomTransferSyntax_MPEG4BDcompatibleHighProfileLevel4_1); 483 target.insert(DicomTransferSyntax_MPEG4HighProfileLevel4_1); 484 target.insert(DicomTransferSyntax_MPEG4HighProfileLevel4_2_For2DVideo); 485 target.insert(DicomTransferSyntax_MPEG4HighProfileLevel4_2_For3DVideo); 486 target.insert(DicomTransferSyntax_MPEG4StereoHighProfileLevel4_2); 487 break; 488 489 case TransferSyntaxGroup_H265: 490 // New in Orthanc 1.9.0 491 target.insert(DicomTransferSyntax_HEVCMainProfileLevel5_1); 492 target.insert(DicomTransferSyntax_HEVCMain10ProfileLevel5_1); 493 break; 494 495 default: 496 throw OrthancException(ErrorCode_ParameterOutOfRange); 497 } 498 } 499 } 500