comparison OrthancServer/Sources/OrthancInitialization.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/OrthancInitialization.cpp@058b5ade8acd
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
36 #if defined(_WIN32)
37 // "Please include winsock2.h before windows.h"
38 # include <winsock2.h>
39 #endif
40
41 #include "OrthancInitialization.h"
42
43 #include "../Core/DicomParsing/FromDcmtkBridge.h"
44 #include "../Core/FileStorage/FilesystemStorage.h"
45 #include "../Core/Logging.h"
46 #include "../Core/OrthancException.h"
47
48 #include "Database/SQLiteDatabaseWrapper.h"
49 #include "OrthancConfiguration.h"
50
51 #include <OrthancServerResources.h>
52
53 #include <dcmtk/dcmnet/dul.h> // For dcmDisableGethostbyaddr()
54
55
56
57 namespace Orthanc
58 {
59 static void RegisterUserMetadata(const Json::Value& config)
60 {
61 if (config.isMember("UserMetadata"))
62 {
63 const Json::Value& parameter = config["UserMetadata"];
64
65 Json::Value::Members members = parameter.getMemberNames();
66 for (size_t i = 0; i < members.size(); i++)
67 {
68 const std::string& name = members[i];
69
70 if (!parameter[name].isInt())
71 {
72 throw OrthancException(ErrorCode_BadParameterType,
73 "Not a number in this user-defined metadata: " + name);
74 }
75
76 int metadata = parameter[name].asInt();
77
78 LOG(INFO) << "Registering user-defined metadata: " << name << " (index "
79 << metadata << ")";
80
81 try
82 {
83 RegisterUserMetadata(metadata, name);
84 }
85 catch (OrthancException&)
86 {
87 LOG(ERROR) << "Cannot register this user-defined metadata: " << name;
88 throw;
89 }
90 }
91 }
92 }
93
94
95 static void RegisterUserContentType(const Json::Value& config)
96 {
97 if (config.isMember("UserContentType"))
98 {
99 const Json::Value& parameter = config["UserContentType"];
100
101 Json::Value::Members members = parameter.getMemberNames();
102 for (size_t i = 0; i < members.size(); i++)
103 {
104 const std::string& name = members[i];
105 std::string mime = MIME_BINARY;
106
107 const Json::Value& value = parameter[name];
108 int contentType;
109
110 if (value.isArray() &&
111 value.size() == 2 &&
112 value[0].isInt() &&
113 value[1].isString())
114 {
115 contentType = value[0].asInt();
116 mime = value[1].asString();
117 }
118 else if (value.isInt())
119 {
120 contentType = value.asInt();
121 }
122 else
123 {
124 throw OrthancException(ErrorCode_BadParameterType,
125 "Not a number in this user-defined attachment type: " + name);
126 }
127
128 LOG(INFO) << "Registering user-defined attachment type: " << name << " (index "
129 << contentType << ") with MIME type \"" << mime << "\"";
130
131 try
132 {
133 RegisterUserContentType(contentType, name, mime);
134 }
135 catch (OrthancException&)
136 {
137 throw;
138 }
139 }
140 }
141 }
142
143
144 static void LoadCustomDictionary(const Json::Value& configuration)
145 {
146 if (configuration.type() != Json::objectValue ||
147 !configuration.isMember("Dictionary") ||
148 configuration["Dictionary"].type() != Json::objectValue)
149 {
150 return;
151 }
152
153 Json::Value::Members tags(configuration["Dictionary"].getMemberNames());
154
155 for (Json::Value::ArrayIndex i = 0; i < tags.size(); i++)
156 {
157 const Json::Value& content = configuration["Dictionary"][tags[i]];
158 if (content.type() != Json::arrayValue ||
159 content.size() < 2 ||
160 content.size() > 5 ||
161 content[0].type() != Json::stringValue ||
162 content[1].type() != Json::stringValue ||
163 (content.size() >= 3 && content[2].type() != Json::intValue) ||
164 (content.size() >= 4 && content[3].type() != Json::intValue) ||
165 (content.size() >= 5 && content[4].type() != Json::stringValue))
166 {
167 throw OrthancException(ErrorCode_BadFileFormat, "The definition of the '" + tags[i] + "' dictionary entry is invalid.");
168 }
169
170 DicomTag tag(FromDcmtkBridge::ParseTag(tags[i]));
171 ValueRepresentation vr = StringToValueRepresentation(content[0].asString(), true);
172 std::string name = content[1].asString();
173 unsigned int minMultiplicity = (content.size() >= 2) ? content[2].asUInt() : 1;
174 unsigned int maxMultiplicity = (content.size() >= 3) ? content[3].asUInt() : 1;
175 std::string privateCreator = (content.size() >= 4) ? content[4].asString() : "";
176
177 FromDcmtkBridge::RegisterDictionaryTag(tag, vr, name, minMultiplicity, maxMultiplicity, privateCreator);
178 }
179 }
180
181
182 static void ConfigurePkcs11(const Json::Value& config)
183 {
184 if (config.type() != Json::objectValue ||
185 !config.isMember("Module") ||
186 config["Module"].type() != Json::stringValue)
187 {
188 throw OrthancException(ErrorCode_BadFileFormat,
189 "No path to the PKCS#11 module (DLL or .so) is provided "
190 "for HTTPS client authentication");
191 }
192
193 std::string pin;
194 if (config.isMember("Pin"))
195 {
196 if (config["Pin"].type() == Json::stringValue)
197 {
198 pin = config["Pin"].asString();
199 }
200 else
201 {
202 throw OrthancException(ErrorCode_BadFileFormat,
203 "The PIN number in the PKCS#11 configuration must be a string");
204 }
205 }
206
207 bool verbose = false;
208 if (config.isMember("Verbose"))
209 {
210 if (config["Verbose"].type() == Json::booleanValue)
211 {
212 verbose = config["Verbose"].asBool();
213 }
214 else
215 {
216 throw OrthancException(ErrorCode_BadFileFormat,
217 "The Verbose option in the PKCS#11 configuration must be a Boolean");
218 }
219 }
220
221 HttpClient::InitializePkcs11(config["Module"].asString(), pin, verbose);
222 }
223
224
225
226 void OrthancInitialize(const char* configurationFile)
227 {
228 OrthancConfiguration::WriterLock lock;
229
230 InitializeServerEnumerations();
231
232 // Read the user-provided configuration
233 lock.GetConfiguration().Read(configurationFile);
234
235 {
236 std::string locale;
237
238 if (lock.GetJson().isMember("Locale"))
239 {
240 locale = lock.GetConfiguration().GetStringParameter("Locale", "");
241 }
242
243 bool loadPrivate = lock.GetConfiguration().GetBooleanParameter("LoadPrivateDictionary", true);
244 Orthanc::InitializeFramework(locale, loadPrivate);
245 }
246
247 // The Orthanc framework is now initialized
248
249 if (lock.GetJson().isMember("DefaultEncoding"))
250 {
251 std::string encoding = lock.GetConfiguration().GetStringParameter("DefaultEncoding", "");
252 SetDefaultDicomEncoding(StringToEncoding(encoding.c_str()));
253 }
254 else
255 {
256 SetDefaultDicomEncoding(ORTHANC_DEFAULT_DICOM_ENCODING);
257 }
258
259 if (lock.GetJson().isMember("Pkcs11"))
260 {
261 ConfigurePkcs11(lock.GetJson()["Pkcs11"]);
262 }
263
264 RegisterUserMetadata(lock.GetJson());
265 RegisterUserContentType(lock.GetJson());
266
267 LoadCustomDictionary(lock.GetJson());
268
269 lock.GetConfiguration().RegisterFont(ServerResources::FONT_UBUNTU_MONO_BOLD_16);
270 }
271
272
273
274 void OrthancFinalize()
275 {
276 OrthancConfiguration::WriterLock lock;
277 Orthanc::FinalizeFramework();
278 }
279
280
281 static IDatabaseWrapper* CreateSQLiteWrapper()
282 {
283 OrthancConfiguration::ReaderLock lock;
284
285 std::string storageDirectoryStr =
286 lock.GetConfiguration().GetStringParameter("StorageDirectory", "OrthancStorage");
287
288 // Open the database
289 boost::filesystem::path indexDirectory = lock.GetConfiguration().InterpretStringParameterAsPath(
290 lock.GetConfiguration().GetStringParameter("IndexDirectory", storageDirectoryStr));
291
292 LOG(WARNING) << "SQLite index directory: " << indexDirectory;
293
294 try
295 {
296 boost::filesystem::create_directories(indexDirectory);
297 }
298 catch (boost::filesystem::filesystem_error&)
299 {
300 }
301
302 return new SQLiteDatabaseWrapper(indexDirectory.string() + "/index");
303 }
304
305
306 namespace
307 {
308 // Anonymous namespace to avoid clashes between compilation modules
309
310 class FilesystemStorageWithoutDicom : public IStorageArea
311 {
312 private:
313 FilesystemStorage storage_;
314
315 public:
316 FilesystemStorageWithoutDicom(const std::string& path) : storage_(path)
317 {
318 }
319
320 virtual void Create(const std::string& uuid,
321 const void* content,
322 size_t size,
323 FileContentType type)
324 {
325 if (type != FileContentType_Dicom)
326 {
327 storage_.Create(uuid, content, size, type);
328 }
329 }
330
331 virtual void Read(std::string& content,
332 const std::string& uuid,
333 FileContentType type)
334 {
335 if (type != FileContentType_Dicom)
336 {
337 storage_.Read(content, uuid, type);
338 }
339 else
340 {
341 throw OrthancException(ErrorCode_UnknownResource);
342 }
343 }
344
345 virtual void Remove(const std::string& uuid,
346 FileContentType type)
347 {
348 if (type != FileContentType_Dicom)
349 {
350 storage_.Remove(uuid, type);
351 }
352 }
353 };
354 }
355
356
357 static IStorageArea* CreateFilesystemStorage()
358 {
359 OrthancConfiguration::ReaderLock lock;
360
361 std::string storageDirectoryStr =
362 lock.GetConfiguration().GetStringParameter("StorageDirectory", "OrthancStorage");
363
364 boost::filesystem::path storageDirectory =
365 lock.GetConfiguration().InterpretStringParameterAsPath(storageDirectoryStr);
366
367 LOG(WARNING) << "Storage directory: " << storageDirectory;
368
369 if (lock.GetConfiguration().GetBooleanParameter("StoreDicom", true))
370 {
371 return new FilesystemStorage(storageDirectory.string());
372 }
373 else
374 {
375 LOG(WARNING) << "The DICOM files will not be stored, Orthanc running in index-only mode";
376 return new FilesystemStorageWithoutDicom(storageDirectory.string());
377 }
378 }
379
380
381 IDatabaseWrapper* CreateDatabaseWrapper()
382 {
383 return CreateSQLiteWrapper();
384 }
385
386
387 IStorageArea* CreateStorageArea()
388 {
389 return CreateFilesystemStorage();
390 }
391 }