comparison OrthancServer/Sources/OrthancConfiguration.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 OrthancServer/OrthancConfiguration.cpp@e3b3af80732d
children 05b8fd21089c
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 #include "PrecompiledHeadersServer.h"
35 #include "OrthancConfiguration.h"
36
37 #include "../Core/HttpServer/HttpServer.h"
38 #include "../Core/Logging.h"
39 #include "../Core/OrthancException.h"
40 #include "../Core/SystemToolbox.h"
41 #include "../Core/TemporaryFile.h"
42 #include "../Core/Toolbox.h"
43
44 #include "ServerIndex.h"
45
46
47 static const char* const DICOM_MODALITIES = "DicomModalities";
48 static const char* const DICOM_MODALITIES_IN_DB = "DicomModalitiesInDatabase";
49 static const char* const ORTHANC_PEERS = "OrthancPeers";
50 static const char* const ORTHANC_PEERS_IN_DB = "OrthancPeersInDatabase";
51 static const char* const TEMPORARY_DIRECTORY = "TemporaryDirectory";
52
53 namespace Orthanc
54 {
55 static void AddFileToConfiguration(Json::Value& target,
56 const boost::filesystem::path& path)
57 {
58 std::map<std::string, std::string> env;
59 SystemToolbox::GetEnvironmentVariables(env);
60
61 LOG(WARNING) << "Reading the configuration from: " << path;
62
63 Json::Value config;
64
65 {
66 std::string content;
67 SystemToolbox::ReadFile(content, path.string());
68
69 content = Toolbox::SubstituteVariables(content, env);
70
71 Json::Value tmp;
72 Json::Reader reader;
73 if (!reader.parse(content, tmp) ||
74 tmp.type() != Json::objectValue)
75 {
76 throw OrthancException(ErrorCode_BadJson,
77 "The configuration file does not follow the JSON syntax: " + path.string());
78 }
79
80 Toolbox::CopyJsonWithoutComments(config, tmp);
81 }
82
83 if (target.size() == 0)
84 {
85 target = config;
86 }
87 else
88 {
89 // Merge the newly-added file with the previous content of "target"
90 Json::Value::Members members = config.getMemberNames();
91 for (Json::Value::ArrayIndex i = 0; i < members.size(); i++)
92 {
93 if (target.isMember(members[i]))
94 {
95 throw OrthancException(ErrorCode_BadFileFormat,
96 "The configuration section \"" + members[i] +
97 "\" is defined in 2 different configuration files");
98 }
99 else
100 {
101 target[members[i]] = config[members[i]];
102 }
103 }
104 }
105 }
106
107
108 static void ScanFolderForConfiguration(Json::Value& target,
109 const char* folder)
110 {
111 using namespace boost::filesystem;
112
113 LOG(WARNING) << "Scanning folder \"" << folder << "\" for configuration files";
114
115 directory_iterator end_it; // default construction yields past-the-end
116 for (directory_iterator it(folder);
117 it != end_it;
118 ++it)
119 {
120 if (!is_directory(it->status()))
121 {
122 std::string extension = boost::filesystem::extension(it->path());
123 Toolbox::ToLowerCase(extension);
124
125 if (extension == ".json")
126 {
127 AddFileToConfiguration(target, it->path().string());
128 }
129 }
130 }
131 }
132
133
134 static void ReadConfiguration(Json::Value& target,
135 const char* configurationFile)
136 {
137 target = Json::objectValue;
138
139 if (configurationFile != NULL)
140 {
141 if (!boost::filesystem::exists(configurationFile))
142 {
143 throw OrthancException(ErrorCode_InexistentFile,
144 "Inexistent path to configuration: " +
145 std::string(configurationFile));
146 }
147
148 if (boost::filesystem::is_directory(configurationFile))
149 {
150 ScanFolderForConfiguration(target, configurationFile);
151 }
152 else
153 {
154 AddFileToConfiguration(target, configurationFile);
155 }
156 }
157 else
158 {
159 #if ORTHANC_STANDALONE == 1
160 // No default path for the standalone configuration
161 LOG(WARNING) << "Using the default Orthanc configuration";
162 return;
163
164 #else
165 // In a non-standalone build, we use the
166 // "Resources/Configuration.json" from the Orthanc source code
167
168 boost::filesystem::path p = ORTHANC_PATH;
169 p /= "Resources";
170 p /= "Configuration.json";
171
172 AddFileToConfiguration(target, p);
173 #endif
174 }
175 }
176
177
178 static void CheckAlphanumeric(const std::string& s)
179 {
180 for (size_t j = 0; j < s.size(); j++)
181 {
182 if (!isalnum(s[j]) &&
183 s[j] != '-')
184 {
185 throw OrthancException(ErrorCode_BadFileFormat,
186 "Only alphanumeric and dash characters are allowed "
187 "in the names of modalities/peers, but found: " + s);
188 }
189 }
190 }
191
192
193 void OrthancConfiguration::LoadModalitiesFromJson(const Json::Value& source)
194 {
195 modalities_.clear();
196
197 if (source.type() != Json::objectValue)
198 {
199 throw OrthancException(ErrorCode_BadFileFormat,
200 "Bad format of the \"" + std::string(DICOM_MODALITIES) +
201 "\" configuration section");
202 }
203
204 Json::Value::Members members = source.getMemberNames();
205
206 for (size_t i = 0; i < members.size(); i++)
207 {
208 const std::string& name = members[i];
209 CheckAlphanumeric(name);
210
211 RemoteModalityParameters modality;
212 modality.Unserialize(source[name]);
213 modalities_[name] = modality;
214 }
215 }
216
217
218 void OrthancConfiguration::LoadPeersFromJson(const Json::Value& source)
219 {
220 peers_.clear();
221
222 if (source.type() != Json::objectValue)
223 {
224 throw OrthancException(ErrorCode_BadFileFormat,
225 "Bad format of the \"" + std::string(ORTHANC_PEERS) +
226 "\" configuration section");
227 }
228
229 Json::Value::Members members = source.getMemberNames();
230
231 for (size_t i = 0; i < members.size(); i++)
232 {
233 const std::string& name = members[i];
234 CheckAlphanumeric(name);
235
236 WebServiceParameters peer;
237 peer.Unserialize(source[name]);
238 peers_[name] = peer;
239 }
240 }
241
242
243 void OrthancConfiguration::LoadModalities()
244 {
245 if (GetBooleanParameter(DICOM_MODALITIES_IN_DB, false))
246 {
247 // Modalities are stored in the database
248 if (serverIndex_ == NULL)
249 {
250 throw Orthanc::OrthancException(ErrorCode_BadSequenceOfCalls);
251 }
252 else
253 {
254 std::string property = serverIndex_->GetGlobalProperty(GlobalProperty_Modalities, "{}");
255
256 Json::Reader reader;
257 Json::Value modalities;
258 if (reader.parse(property, modalities))
259 {
260 LoadModalitiesFromJson(modalities);
261 }
262 else
263 {
264 throw OrthancException(ErrorCode_InternalError,
265 "Cannot unserialize the list of modalities from the Orthanc database");
266 }
267 }
268 }
269 else
270 {
271 // Modalities are stored in the configuration files
272 if (json_.isMember(DICOM_MODALITIES))
273 {
274 LoadModalitiesFromJson(json_[DICOM_MODALITIES]);
275 }
276 else
277 {
278 modalities_.clear();
279 }
280 }
281 }
282
283 void OrthancConfiguration::LoadPeers()
284 {
285 if (GetBooleanParameter(ORTHANC_PEERS_IN_DB, false))
286 {
287 // Peers are stored in the database
288 if (serverIndex_ == NULL)
289 {
290 throw Orthanc::OrthancException(ErrorCode_BadSequenceOfCalls);
291 }
292 else
293 {
294 std::string property = serverIndex_->GetGlobalProperty(GlobalProperty_Peers, "{}");
295
296 Json::Reader reader;
297 Json::Value peers;
298 if (reader.parse(property, peers))
299 {
300 LoadPeersFromJson(peers);
301 }
302 else
303 {
304 throw OrthancException(ErrorCode_InternalError,
305 "Cannot unserialize the list of peers from the Orthanc database");
306 }
307 }
308 }
309 else
310 {
311 // Peers are stored in the configuration files
312 if (json_.isMember(ORTHANC_PEERS))
313 {
314 LoadPeersFromJson(json_[ORTHANC_PEERS]);
315 }
316 else
317 {
318 peers_.clear();
319 }
320 }
321 }
322
323
324 void OrthancConfiguration::SaveModalitiesToJson(Json::Value& target)
325 {
326 target = Json::objectValue;
327
328 for (Modalities::const_iterator it = modalities_.begin(); it != modalities_.end(); ++it)
329 {
330 Json::Value modality;
331 it->second.Serialize(modality, true /* force advanced format */);
332
333 target[it->first] = modality;
334 }
335 }
336
337
338 void OrthancConfiguration::SavePeersToJson(Json::Value& target)
339 {
340 target = Json::objectValue;
341
342 for (Peers::const_iterator it = peers_.begin(); it != peers_.end(); ++it)
343 {
344 Json::Value peer;
345 it->second.Serialize(peer,
346 false /* use simple format if possible */,
347 true /* include passwords */);
348
349 target[it->first] = peer;
350 }
351 }
352
353
354 void OrthancConfiguration::SaveModalities()
355 {
356 if (GetBooleanParameter(DICOM_MODALITIES_IN_DB, false))
357 {
358 // Modalities are stored in the database
359 if (serverIndex_ == NULL)
360 {
361 throw Orthanc::OrthancException(ErrorCode_BadSequenceOfCalls);
362 }
363 else
364 {
365 Json::Value modalities;
366 SaveModalitiesToJson(modalities);
367
368 Json::FastWriter writer;
369 std::string s = writer.write(modalities);
370
371 serverIndex_->SetGlobalProperty(GlobalProperty_Modalities, s);
372 }
373 }
374 else
375 {
376 // Modalities are stored in the configuration files
377 if (!modalities_.empty() ||
378 json_.isMember(DICOM_MODALITIES))
379 {
380 SaveModalitiesToJson(json_[DICOM_MODALITIES]);
381 }
382 }
383 }
384
385
386 void OrthancConfiguration::SavePeers()
387 {
388 if (GetBooleanParameter(ORTHANC_PEERS_IN_DB, false))
389 {
390 // Peers are stored in the database
391 if (serverIndex_ == NULL)
392 {
393 throw Orthanc::OrthancException(ErrorCode_BadSequenceOfCalls);
394 }
395 else
396 {
397 Json::Value peers;
398 SavePeersToJson(peers);
399
400 Json::FastWriter writer;
401 std::string s = writer.write(peers);
402
403 serverIndex_->SetGlobalProperty(GlobalProperty_Peers, s);
404 }
405 }
406 else
407 {
408 // Peers are stored in the configuration files
409 if (!peers_.empty() ||
410 json_.isMember(ORTHANC_PEERS))
411 {
412 SavePeersToJson(json_[ORTHANC_PEERS]);
413 }
414 }
415 }
416
417
418 OrthancConfiguration& OrthancConfiguration::GetInstance()
419 {
420 static OrthancConfiguration configuration;
421 return configuration;
422 }
423
424
425 bool OrthancConfiguration::LookupStringParameter(std::string& target,
426 const std::string& parameter) const
427 {
428 if (json_.isMember(parameter))
429 {
430 if (json_[parameter].type() != Json::stringValue)
431 {
432 throw OrthancException(ErrorCode_BadParameterType,
433 "The configuration option \"" + parameter + "\" must be a string");
434 }
435 else
436 {
437 target = json_[parameter].asString();
438 return true;
439 }
440 }
441 else
442 {
443 return false;
444 }
445 }
446
447
448 std::string OrthancConfiguration::GetStringParameter(const std::string& parameter,
449 const std::string& defaultValue) const
450 {
451 std::string value;
452 if (LookupStringParameter(value, parameter))
453 {
454 return value;
455 }
456 else
457 {
458 return defaultValue;
459 }
460 }
461
462
463 int OrthancConfiguration::GetIntegerParameter(const std::string& parameter,
464 int defaultValue) const
465 {
466 if (json_.isMember(parameter))
467 {
468 if (json_[parameter].type() != Json::intValue)
469 {
470 throw OrthancException(ErrorCode_BadParameterType,
471 "The configuration option \"" + parameter + "\" must be an integer");
472 }
473 else
474 {
475 return json_[parameter].asInt();
476 }
477 }
478 else
479 {
480 return defaultValue;
481 }
482 }
483
484
485 unsigned int OrthancConfiguration::GetUnsignedIntegerParameter(
486 const std::string& parameter,
487 unsigned int defaultValue) const
488 {
489 int v = GetIntegerParameter(parameter, defaultValue);
490
491 if (v < 0)
492 {
493 throw OrthancException(ErrorCode_ParameterOutOfRange,
494 "The configuration option \"" + parameter + "\" must be a positive integer");
495 }
496 else
497 {
498 return static_cast<unsigned int>(v);
499 }
500 }
501
502
503 bool OrthancConfiguration::LookupBooleanParameter(bool& target,
504 const std::string& parameter) const
505 {
506 if (json_.isMember(parameter))
507 {
508 if (json_[parameter].type() != Json::booleanValue)
509 {
510 throw OrthancException(ErrorCode_BadParameterType,
511 "The configuration option \"" + parameter +
512 "\" must be a Boolean (true or false)");
513 }
514 else
515 {
516 target = json_[parameter].asBool();
517 return true;
518 }
519 }
520 else
521 {
522 return false;
523 }
524 }
525
526
527 bool OrthancConfiguration::GetBooleanParameter(const std::string& parameter,
528 bool defaultValue) const
529 {
530 bool value;
531 if (LookupBooleanParameter(value, parameter))
532 {
533 return value;
534 }
535 else
536 {
537 return defaultValue;
538 }
539 }
540
541
542 void OrthancConfiguration::Read(const char* configurationFile)
543 {
544 // Read the content of the configuration
545 configurationFileArg_ = configurationFile;
546 ReadConfiguration(json_, configurationFile);
547
548 // Adapt the paths to the configurations
549 defaultDirectory_ = boost::filesystem::current_path();
550 configurationAbsolutePath_ = "";
551
552 if (configurationFile)
553 {
554 if (boost::filesystem::is_directory(configurationFile))
555 {
556 defaultDirectory_ = boost::filesystem::path(configurationFile);
557 configurationAbsolutePath_ = boost::filesystem::absolute(configurationFile).parent_path().string();
558 }
559 else
560 {
561 defaultDirectory_ = boost::filesystem::path(configurationFile).parent_path();
562 configurationAbsolutePath_ = boost::filesystem::absolute(configurationFile).string();
563 }
564 }
565 else
566 {
567 #if ORTHANC_STANDALONE != 1
568 // In a non-standalone build, we use the
569 // "Resources/Configuration.json" from the Orthanc source code
570
571 boost::filesystem::path p = ORTHANC_PATH;
572 p /= "Resources";
573 p /= "Configuration.json";
574 configurationAbsolutePath_ = boost::filesystem::absolute(p).string();
575 #endif
576 }
577 }
578
579
580 void OrthancConfiguration::LoadModalitiesAndPeers()
581 {
582 LoadModalities();
583 LoadPeers();
584 }
585
586
587 void OrthancConfiguration::RegisterFont(ServerResources::FileResourceId resource)
588 {
589 std::string content;
590 ServerResources::GetFileResource(content, resource);
591 fontRegistry_.AddFromMemory(content);
592 }
593
594
595 void OrthancConfiguration::GetDicomModalityUsingSymbolicName(
596 RemoteModalityParameters& modality,
597 const std::string& name) const
598 {
599 Modalities::const_iterator found = modalities_.find(name);
600
601 if (found == modalities_.end())
602 {
603 throw OrthancException(ErrorCode_InexistentItem,
604 "No modality with symbolic name: " + name);
605 }
606 else
607 {
608 modality = found->second;
609 }
610 }
611
612
613 bool OrthancConfiguration::LookupOrthancPeer(WebServiceParameters& peer,
614 const std::string& name) const
615 {
616 Peers::const_iterator found = peers_.find(name);
617
618 if (found == peers_.end())
619 {
620 LOG(ERROR) << "No peer with symbolic name: " << name;
621 return false;
622 }
623 else
624 {
625 peer = found->second;
626 return true;
627 }
628 }
629
630
631 void OrthancConfiguration::GetListOfDicomModalities(std::set<std::string>& target) const
632 {
633 target.clear();
634
635 for (Modalities::const_iterator
636 it = modalities_.begin(); it != modalities_.end(); ++it)
637 {
638 target.insert(it->first);
639 }
640 }
641
642
643 void OrthancConfiguration::GetListOfOrthancPeers(std::set<std::string>& target) const
644 {
645 target.clear();
646
647 for (Peers::const_iterator it = peers_.begin(); it != peers_.end(); ++it)
648 {
649 target.insert(it->first);
650 }
651 }
652
653
654 bool OrthancConfiguration::SetupRegisteredUsers(HttpServer& httpServer) const
655 {
656 httpServer.ClearUsers();
657
658 if (!json_.isMember("RegisteredUsers"))
659 {
660 return false;
661 }
662
663 const Json::Value& users = json_["RegisteredUsers"];
664 if (users.type() != Json::objectValue)
665 {
666 throw OrthancException(ErrorCode_BadFileFormat, "Badly formatted list of users");
667 }
668
669 bool hasUser = false;
670 Json::Value::Members usernames = users.getMemberNames();
671 for (size_t i = 0; i < usernames.size(); i++)
672 {
673 const std::string& username = usernames[i];
674 std::string password = users[username].asString();
675 httpServer.RegisterUser(username.c_str(), password.c_str());
676 hasUser = true;
677 }
678
679 return hasUser;
680 }
681
682
683 std::string OrthancConfiguration::InterpretStringParameterAsPath(
684 const std::string& parameter) const
685 {
686 return SystemToolbox::InterpretRelativePath(defaultDirectory_.string(), parameter);
687 }
688
689
690 void OrthancConfiguration::GetListOfStringsParameter(std::list<std::string>& target,
691 const std::string& key) const
692 {
693 target.clear();
694
695 if (!json_.isMember(key))
696 {
697 return;
698 }
699
700 const Json::Value& lst = json_[key];
701
702 if (lst.type() != Json::arrayValue)
703 {
704 throw OrthancException(ErrorCode_BadFileFormat, "Badly formatted list of strings");
705 }
706
707 for (Json::Value::ArrayIndex i = 0; i < lst.size(); i++)
708 {
709 target.push_back(lst[i].asString());
710 }
711 }
712
713
714 bool OrthancConfiguration::IsSameAETitle(const std::string& aet1,
715 const std::string& aet2) const
716 {
717 if (GetBooleanParameter("StrictAetComparison", false))
718 {
719 // Case-sensitive matching
720 return aet1 == aet2;
721 }
722 else
723 {
724 // Case-insensitive matching (default)
725 std::string tmp1, tmp2;
726 Toolbox::ToLowerCase(tmp1, aet1);
727 Toolbox::ToLowerCase(tmp2, aet2);
728 return tmp1 == tmp2;
729 }
730 }
731
732
733 bool OrthancConfiguration::LookupDicomModalityUsingAETitle(RemoteModalityParameters& modality,
734 const std::string& aet) const
735 {
736 for (Modalities::const_iterator it = modalities_.begin(); it != modalities_.end(); ++it)
737 {
738 if (IsSameAETitle(aet, it->second.GetApplicationEntityTitle()))
739 {
740 modality = it->second;
741 return true;
742 }
743 }
744
745 return false;
746 }
747
748
749 bool OrthancConfiguration::IsKnownAETitle(const std::string& aet,
750 const std::string& ip) const
751 {
752 RemoteModalityParameters modality;
753
754 if (!LookupDicomModalityUsingAETitle(modality, aet))
755 {
756 LOG(WARNING) << "Modality \"" << aet
757 << "\" is not listed in the \"DicomModalities\" configuration option";
758 return false;
759 }
760 else if (!GetBooleanParameter("DicomCheckModalityHost", false) ||
761 ip == modality.GetHost())
762 {
763 return true;
764 }
765 else
766 {
767 LOG(WARNING) << "Forbidding access from AET \"" << aet
768 << "\" given its hostname (" << ip << ") does not match "
769 << "the \"DicomModalities\" configuration option ("
770 << modality.GetHost() << " was expected)";
771 return false;
772 }
773 }
774
775
776 RemoteModalityParameters
777 OrthancConfiguration::GetModalityUsingSymbolicName(const std::string& name) const
778 {
779 RemoteModalityParameters modality;
780 GetDicomModalityUsingSymbolicName(modality, name);
781
782 return modality;
783 }
784
785
786 RemoteModalityParameters
787 OrthancConfiguration::GetModalityUsingAet(const std::string& aet) const
788 {
789 RemoteModalityParameters modality;
790
791 if (LookupDicomModalityUsingAETitle(modality, aet))
792 {
793 return modality;
794 }
795 else
796 {
797 throw OrthancException(ErrorCode_InexistentItem,
798 "Unknown modality for AET: " + aet);
799 }
800 }
801
802
803 void OrthancConfiguration::UpdateModality(const std::string& symbolicName,
804 const RemoteModalityParameters& modality)
805 {
806 CheckAlphanumeric(symbolicName);
807
808 modalities_[symbolicName] = modality;
809 SaveModalities();
810 }
811
812
813 void OrthancConfiguration::RemoveModality(const std::string& symbolicName)
814 {
815 modalities_.erase(symbolicName);
816 SaveModalities();
817 }
818
819
820 void OrthancConfiguration::UpdatePeer(const std::string& symbolicName,
821 const WebServiceParameters& peer)
822 {
823 CheckAlphanumeric(symbolicName);
824
825 peer.CheckClientCertificate();
826
827 peers_[symbolicName] = peer;
828 SavePeers();
829 }
830
831
832 void OrthancConfiguration::RemovePeer(const std::string& symbolicName)
833 {
834 peers_.erase(symbolicName);
835 SavePeers();
836 }
837
838
839 void OrthancConfiguration::Format(std::string& result) const
840 {
841 Json::StyledWriter w;
842 result = w.write(json_);
843 }
844
845
846 void OrthancConfiguration::SetDefaultEncoding(Encoding encoding)
847 {
848 SetDefaultDicomEncoding(encoding);
849
850 // Propagate the encoding to the configuration file that is
851 // stored in memory
852 json_["DefaultEncoding"] = EnumerationToString(encoding);
853 }
854
855
856 bool OrthancConfiguration::HasConfigurationChanged() const
857 {
858 Json::Value current;
859 ReadConfiguration(current, configurationFileArg_);
860
861 Json::FastWriter writer;
862 std::string a = writer.write(json_);
863 std::string b = writer.write(current);
864
865 return a != b;
866 }
867
868
869 void OrthancConfiguration::SetServerIndex(ServerIndex& index)
870 {
871 serverIndex_ = &index;
872 }
873
874
875 void OrthancConfiguration::ResetServerIndex()
876 {
877 serverIndex_ = NULL;
878 }
879
880
881 TemporaryFile* OrthancConfiguration::CreateTemporaryFile() const
882 {
883 if (json_.isMember(TEMPORARY_DIRECTORY))
884 {
885 return new TemporaryFile(InterpretStringParameterAsPath(GetStringParameter(TEMPORARY_DIRECTORY, ".")), "");
886 }
887 else
888 {
889 return new TemporaryFile;
890 }
891 }
892
893
894 std::string OrthancConfiguration::GetDefaultPrivateCreator() const
895 {
896 // New configuration option in Orthanc 1.6.0
897 return GetStringParameter("DefaultPrivateCreator", "");
898 }
899 }