Mercurial > hg > orthanc
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 } |