comparison Framework/Orthanc/Core/Toolbox.cpp @ 15:da2cf3ace87a

sync
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 09 Nov 2016 17:20:44 +0100
parents 0b9034112fde
children
comparison
equal deleted inserted replaced
14:0b9034112fde 15:da2cf3ace87a
34 #include "Toolbox.h" 34 #include "Toolbox.h"
35 35
36 #include "OrthancException.h" 36 #include "OrthancException.h"
37 #include "Logging.h" 37 #include "Logging.h"
38 38
39 #include <boost/algorithm/string/replace.hpp>
40 #include <boost/lexical_cast.hpp>
41 #include <boost/locale.hpp>
42 #include <boost/uuid/sha1.hpp>
43
39 #include <string> 44 #include <string>
40 #include <stdint.h> 45 #include <stdint.h>
41 #include <string.h> 46 #include <string.h>
42 #include <boost/filesystem.hpp>
43 #include <boost/filesystem/fstream.hpp>
44 #include <boost/uuid/sha1.hpp>
45 #include <boost/lexical_cast.hpp>
46 #include <algorithm> 47 #include <algorithm>
47 #include <ctype.h> 48 #include <ctype.h>
48 49
49 #if BOOST_HAS_DATE_TIME == 1 50 #if BOOST_HAS_REGEX == 1
50 #include <boost/date_time/posix_time/posix_time.hpp> 51 # include <boost/regex.hpp>
51 #endif 52 #endif
52 53
53 #if BOOST_HAS_REGEX == 1 54 #if BOOST_HAS_LOCALE != 1
54 #include <boost/regex.hpp> 55 # error Since version 0.7.6, Orthanc entirely relies on boost::locale
55 #endif 56 #endif
56
57 #if defined(_WIN32)
58 #include <windows.h>
59 #include <process.h> // For "_spawnvp()" and "_getpid()"
60 #else
61 #include <unistd.h> // For "execvp()"
62 #include <sys/wait.h> // For "waitpid()"
63 #endif
64
65 #if defined(__APPLE__) && defined(__MACH__)
66 #include <mach-o/dyld.h> /* _NSGetExecutablePath */
67 #include <limits.h> /* PATH_MAX */
68 #endif
69
70 #if defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__)
71 #include <limits.h> /* PATH_MAX */
72 #include <signal.h>
73 #include <unistd.h>
74 #endif
75
76 #if BOOST_HAS_LOCALE != 1
77 #error Since version 0.7.6, Orthanc entirely relies on boost::locale
78 #endif
79
80 #include <boost/locale.hpp>
81
82 57
83 #if ORTHANC_ENABLE_MD5 == 1 58 #if ORTHANC_ENABLE_MD5 == 1
84 # include "../Resources/ThirdParty/md5/md5.h" 59 # include "../Resources/ThirdParty/md5/md5.h"
85 #endif 60 #endif
86
87 61
88 #if ORTHANC_ENABLE_BASE64 == 1 62 #if ORTHANC_ENABLE_BASE64 == 1
89 # include "../Resources/ThirdParty/base64/base64.h" 63 # include "../Resources/ThirdParty/base64/base64.h"
90 #endif 64 #endif
91 65
101 } 75 }
102 } 76 }
103 #endif 77 #endif
104 78
105 79
80
81 // Inclusions for UUID
82 // http://stackoverflow.com/a/1626302
83
84 extern "C"
85 {
86 #ifdef WIN32
87 # include <rpc.h>
88 #else
89 # include <uuid/uuid.h>
90 #endif
91 }
92
93
106 #if ORTHANC_ENABLE_PUGIXML == 1 94 #if ORTHANC_ENABLE_PUGIXML == 1
107 #include "ChunkedBuffer.h" 95 # include "ChunkedBuffer.h"
108 #include <pugixml.hpp> 96 # include <pugixml.hpp>
109 #endif 97 #endif
110 98
111 99
112 namespace Orthanc 100 namespace Orthanc
113 { 101 {
121 #error Support your platform here 109 #error Support your platform here
122 #endif 110 #endif
123 } 111 }
124 112
125 113
126 #if ORTHANC_SANDBOXED == 0
127 static bool finish_;
128 static ServerBarrierEvent barrierEvent_;
129
130 #if defined(_WIN32)
131 static BOOL WINAPI ConsoleControlHandler(DWORD dwCtrlType)
132 {
133 // http://msdn.microsoft.com/en-us/library/ms683242(v=vs.85).aspx
134 finish_ = true;
135 return true;
136 }
137 #else
138 static void SignalHandler(int signal)
139 {
140 if (signal == SIGHUP)
141 {
142 barrierEvent_ = ServerBarrierEvent_Reload;
143 }
144
145 finish_ = true;
146 }
147 #endif
148
149
150 static ServerBarrierEvent ServerBarrierInternal(const bool* stopFlag)
151 {
152 #if defined(_WIN32)
153 SetConsoleCtrlHandler(ConsoleControlHandler, true);
154 #else
155 signal(SIGINT, SignalHandler);
156 signal(SIGQUIT, SignalHandler);
157 signal(SIGTERM, SignalHandler);
158 signal(SIGHUP, SignalHandler);
159 #endif
160
161 // Active loop that awakens every 100ms
162 finish_ = false;
163 barrierEvent_ = ServerBarrierEvent_Stop;
164 while (!(*stopFlag || finish_))
165 {
166 Toolbox::USleep(100 * 1000);
167 }
168
169 #if defined(_WIN32)
170 SetConsoleCtrlHandler(ConsoleControlHandler, false);
171 #else
172 signal(SIGINT, NULL);
173 signal(SIGQUIT, NULL);
174 signal(SIGTERM, NULL);
175 signal(SIGHUP, NULL);
176 #endif
177
178 return barrierEvent_;
179 }
180
181
182 ServerBarrierEvent Toolbox::ServerBarrier(const bool& stopFlag)
183 {
184 return ServerBarrierInternal(&stopFlag);
185 }
186
187 ServerBarrierEvent Toolbox::ServerBarrier()
188 {
189 const bool stopFlag = false;
190 return ServerBarrierInternal(&stopFlag);
191 }
192 #endif /* ORTHANC_SANDBOXED */
193
194
195 void Toolbox::ToUpperCase(std::string& s) 114 void Toolbox::ToUpperCase(std::string& s)
196 { 115 {
197 std::transform(s.begin(), s.end(), s.begin(), toupper); 116 std::transform(s.begin(), s.end(), s.begin(), toupper);
198 } 117 }
199 118
215 const std::string& source) 134 const std::string& source)
216 { 135 {
217 result = source; 136 result = source;
218 ToLowerCase(result); 137 ToLowerCase(result);
219 } 138 }
220
221
222 static std::streamsize GetStreamSize(std::istream& f)
223 {
224 // http://www.cplusplus.com/reference/iostream/istream/tellg/
225 f.seekg(0, std::ios::end);
226 std::streamsize size = f.tellg();
227 f.seekg(0, std::ios::beg);
228
229 return size;
230 }
231
232
233 #if ORTHANC_SANDBOXED == 0
234 void Toolbox::ReadFile(std::string& content,
235 const std::string& path)
236 {
237 if (!IsRegularFile(path))
238 {
239 LOG(ERROR) << std::string("The path does not point to a regular file: ") << path;
240 throw OrthancException(ErrorCode_RegularFileExpected);
241 }
242
243 boost::filesystem::ifstream f;
244 f.open(path, std::ifstream::in | std::ifstream::binary);
245 if (!f.good())
246 {
247 throw OrthancException(ErrorCode_InexistentFile);
248 }
249
250 std::streamsize size = GetStreamSize(f);
251 content.resize(size);
252 if (size != 0)
253 {
254 f.read(reinterpret_cast<char*>(&content[0]), size);
255 }
256
257 f.close();
258 }
259 #endif
260
261
262 #if ORTHANC_SANDBOXED == 0
263 bool Toolbox::ReadHeader(std::string& header,
264 const std::string& path,
265 size_t headerSize)
266 {
267 if (!IsRegularFile(path))
268 {
269 LOG(ERROR) << std::string("The path does not point to a regular file: ") << path;
270 throw OrthancException(ErrorCode_RegularFileExpected);
271 }
272
273 boost::filesystem::ifstream f;
274 f.open(path, std::ifstream::in | std::ifstream::binary);
275 if (!f.good())
276 {
277 throw OrthancException(ErrorCode_InexistentFile);
278 }
279
280 bool full = true;
281
282 {
283 std::streamsize size = GetStreamSize(f);
284 if (size <= 0)
285 {
286 headerSize = 0;
287 full = false;
288 }
289 else if (static_cast<size_t>(size) < headerSize)
290 {
291 headerSize = size; // Truncate to the size of the file
292 full = false;
293 }
294 }
295
296 header.resize(headerSize);
297 if (headerSize != 0)
298 {
299 f.read(reinterpret_cast<char*>(&header[0]), headerSize);
300 }
301
302 f.close();
303
304 return full;
305 }
306 #endif
307
308
309 #if ORTHANC_SANDBOXED == 0
310 void Toolbox::WriteFile(const void* content,
311 size_t size,
312 const std::string& path)
313 {
314 boost::filesystem::ofstream f;
315 f.open(path, std::ofstream::out | std::ofstream::binary);
316 if (!f.good())
317 {
318 throw OrthancException(ErrorCode_CannotWriteFile);
319 }
320
321 if (size != 0)
322 {
323 f.write(reinterpret_cast<const char*>(content), size);
324
325 if (!f.good())
326 {
327 f.close();
328 throw OrthancException(ErrorCode_FileStorageCannotWrite);
329 }
330 }
331
332 f.close();
333 }
334 #endif
335
336
337 #if ORTHANC_SANDBOXED == 0
338 void Toolbox::WriteFile(const std::string& content,
339 const std::string& path)
340 {
341 WriteFile(content.size() > 0 ? content.c_str() : NULL,
342 content.size(), path);
343 }
344 #endif
345
346
347 #if ORTHANC_SANDBOXED == 0
348 void Toolbox::RemoveFile(const std::string& path)
349 {
350 if (boost::filesystem::exists(path))
351 {
352 if (IsRegularFile(path))
353 {
354 boost::filesystem::remove(path);
355 }
356 else
357 {
358 throw OrthancException(ErrorCode_RegularFileExpected);
359 }
360 }
361 }
362 #endif
363 139
364 140
365 void Toolbox::SplitUriComponents(UriComponents& components, 141 void Toolbox::SplitUriComponents(UriComponents& components,
366 const std::string& uri) 142 const std::string& uri)
367 { 143 {
527 return r; 303 return r;
528 } 304 }
529 } 305 }
530 306
531 307
532
533 #if ORTHANC_SANDBOXED == 0
534 uint64_t Toolbox::GetFileSize(const std::string& path)
535 {
536 try
537 {
538 return static_cast<uint64_t>(boost::filesystem::file_size(path));
539 }
540 catch (boost::filesystem::filesystem_error&)
541 {
542 throw OrthancException(ErrorCode_InexistentFile);
543 }
544 }
545 #endif
546
547
548 #if ORTHANC_ENABLE_MD5 == 1 308 #if ORTHANC_ENABLE_MD5 == 1
549 static char GetHexadecimalCharacter(uint8_t value) 309 static char GetHexadecimalCharacter(uint8_t value)
550 { 310 {
551 assert(value < 16); 311 assert(value < 16);
552 312
656 const std::string& content) 416 const std::string& content)
657 { 417 {
658 result = "data:" + mime + ";base64," + base64_encode(content); 418 result = "data:" + mime + ";base64," + base64_encode(content);
659 } 419 }
660 420
661 #endif
662
663
664
665 #if defined(_WIN32)
666 static std::string GetPathToExecutableInternal()
667 {
668 // Yes, this is ugly, but there is no simple way to get the
669 // required buffer size, so we use a big constant
670 std::vector<char> buffer(32768);
671 /*int bytes =*/ GetModuleFileNameA(NULL, &buffer[0], static_cast<DWORD>(buffer.size() - 1));
672 return std::string(&buffer[0]);
673 }
674
675 #elif defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__)
676 static std::string GetPathToExecutableInternal()
677 {
678 std::vector<char> buffer(PATH_MAX + 1);
679 ssize_t bytes = readlink("/proc/self/exe", &buffer[0], buffer.size() - 1);
680 if (bytes == 0)
681 {
682 throw OrthancException(ErrorCode_PathToExecutable);
683 }
684
685 return std::string(&buffer[0]);
686 }
687
688 #elif defined(__APPLE__) && defined(__MACH__)
689 static std::string GetPathToExecutableInternal()
690 {
691 char pathbuf[PATH_MAX + 1];
692 unsigned int bufsize = static_cast<int>(sizeof(pathbuf));
693
694 _NSGetExecutablePath( pathbuf, &bufsize);
695
696 return std::string(pathbuf);
697 }
698
699 #elif ORTHANC_SANDBOXED == 1
700 // Sandboxed Orthanc, no access to the executable
701
702 #else
703 #error Support your platform here
704 #endif
705
706
707 #if ORTHANC_SANDBOXED == 0
708 std::string Toolbox::GetPathToExecutable()
709 {
710 boost::filesystem::path p(GetPathToExecutableInternal());
711 return boost::filesystem::absolute(p).string();
712 }
713
714
715 std::string Toolbox::GetDirectoryOfExecutable()
716 {
717 boost::filesystem::path p(GetPathToExecutableInternal());
718 return boost::filesystem::absolute(p.parent_path()).string();
719 }
720 #endif 421 #endif
721 422
722 423
723 static const char* GetBoostLocaleEncoding(const Encoding sourceEncoding) 424 static const char* GetBoostLocaleEncoding(const Encoding sourceEncoding)
724 { 425 {
993 else 694 else
994 { 695 {
995 return IsSHA1(s.c_str(), s.size()); 696 return IsSHA1(s.c_str(), s.size());
996 } 697 }
997 } 698 }
998
999
1000 #if BOOST_HAS_DATE_TIME == 1
1001 std::string Toolbox::GetNowIsoString()
1002 {
1003 boost::posix_time::ptime now = boost::posix_time::second_clock::local_time();
1004 return boost::posix_time::to_iso_string(now);
1005 }
1006
1007 void Toolbox::GetNowDicom(std::string& date,
1008 std::string& time)
1009 {
1010 boost::posix_time::ptime now = boost::posix_time::second_clock::local_time();
1011 tm tm = boost::posix_time::to_tm(now);
1012
1013 char s[32];
1014 sprintf(s, "%04d%02d%02d", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
1015 date.assign(s);
1016
1017 // TODO milliseconds
1018 sprintf(s, "%02d%02d%02d.%06d", tm.tm_hour, tm.tm_min, tm.tm_sec, 0);
1019 time.assign(s);
1020 }
1021 #endif
1022 699
1023 700
1024 std::string Toolbox::StripSpaces(const std::string& source) 701 std::string Toolbox::StripSpaces(const std::string& source)
1025 { 702 {
1026 size_t first = 0; 703 size_t first = 0;
1176 853
1177 result.push_back(currentItem); 854 result.push_back(currentItem);
1178 } 855 }
1179 856
1180 857
1181 #if ORTHANC_SANDBOXED == 0
1182 void Toolbox::MakeDirectory(const std::string& path)
1183 {
1184 if (boost::filesystem::exists(path))
1185 {
1186 if (!boost::filesystem::is_directory(path))
1187 {
1188 throw OrthancException(ErrorCode_DirectoryOverFile);
1189 }
1190 }
1191 else
1192 {
1193 if (!boost::filesystem::create_directories(path))
1194 {
1195 throw OrthancException(ErrorCode_MakeDirectory);
1196 }
1197 }
1198 }
1199 #endif
1200
1201
1202 #if ORTHANC_SANDBOXED == 0
1203 bool Toolbox::IsExistingFile(const std::string& path)
1204 {
1205 return boost::filesystem::exists(path);
1206 }
1207 #endif
1208
1209
1210 #if ORTHANC_ENABLE_PUGIXML == 1 858 #if ORTHANC_ENABLE_PUGIXML == 1
1211 class ChunkedBufferWriter : public pugi::xml_writer 859 class ChunkedBufferWriter : public pugi::xml_writer
1212 { 860 {
1213 private: 861 private:
1214 ChunkedBuffer buffer_; 862 ChunkedBuffer buffer_;
1326 writer.Flatten(target); 974 writer.Flatten(target);
1327 } 975 }
1328 976
1329 #endif 977 #endif
1330 978
1331
1332 #if ORTHANC_SANDBOXED == 0
1333 void Toolbox::ExecuteSystemCommand(const std::string& command,
1334 const std::vector<std::string>& arguments)
1335 {
1336 // Convert the arguments as a C array
1337 std::vector<char*> args(arguments.size() + 2);
1338
1339 args.front() = const_cast<char*>(command.c_str());
1340
1341 for (size_t i = 0; i < arguments.size(); i++)
1342 {
1343 args[i + 1] = const_cast<char*>(arguments[i].c_str());
1344 }
1345
1346 args.back() = NULL;
1347
1348 int status;
1349
1350 #if defined(_WIN32)
1351 // http://msdn.microsoft.com/en-us/library/275khfab.aspx
1352 status = static_cast<int>(_spawnvp(_P_OVERLAY, command.c_str(), &args[0]));
1353
1354 #else
1355 int pid = fork();
1356
1357 if (pid == -1)
1358 {
1359 // Error in fork()
1360 #if ORTHANC_ENABLE_LOGGING == 1
1361 LOG(ERROR) << "Cannot fork a child process";
1362 #endif
1363
1364 throw OrthancException(ErrorCode_SystemCommand);
1365 }
1366 else if (pid == 0)
1367 {
1368 // Execute the system command in the child process
1369 execvp(command.c_str(), &args[0]);
1370
1371 // We should never get here
1372 _exit(1);
1373 }
1374 else
1375 {
1376 // Wait for the system command to exit
1377 waitpid(pid, &status, 0);
1378 }
1379 #endif
1380
1381 if (status != 0)
1382 {
1383 #if ORTHANC_ENABLE_LOGGING == 1
1384 LOG(ERROR) << "System command failed with status code " << status;
1385 #endif
1386
1387 throw OrthancException(ErrorCode_SystemCommand);
1388 }
1389 }
1390 #endif
1391 979
1392 980
1393 bool Toolbox::IsInteger(const std::string& str) 981 bool Toolbox::IsInteger(const std::string& str)
1394 { 982 {
1395 std::string s = StripSpaces(str); 983 std::string s = StripSpaces(str);
1494 else 1082 else
1495 { 1083 {
1496 return str.compare(0, prefix.size(), prefix) == 0; 1084 return str.compare(0, prefix.size(), prefix) == 0;
1497 } 1085 }
1498 } 1086 }
1499
1500
1501 #if ORTHANC_SANDBOXED == 0
1502 int Toolbox::GetProcessId()
1503 {
1504 #if defined(_WIN32)
1505 return static_cast<int>(_getpid());
1506 #else
1507 return static_cast<int>(getpid());
1508 #endif
1509 }
1510 #endif
1511
1512
1513 #if ORTHANC_SANDBOXED == 0
1514 bool Toolbox::IsRegularFile(const std::string& path)
1515 {
1516 namespace fs = boost::filesystem;
1517
1518 try
1519 {
1520 if (fs::exists(path))
1521 {
1522 fs::file_status status = fs::status(path);
1523 return (status.type() == boost::filesystem::regular_file ||
1524 status.type() == boost::filesystem::reparse_file); // Fix BitBucket issue #11
1525 }
1526 }
1527 catch (fs::filesystem_error&)
1528 {
1529 }
1530
1531 return false;
1532 }
1533 #endif
1534
1535
1536 FILE* Toolbox::OpenFile(const std::string& path,
1537 FileMode mode)
1538 {
1539 #if defined(_WIN32)
1540 // TODO Deal with special characters by converting to the current locale
1541 #endif
1542
1543 const char* m;
1544 switch (mode)
1545 {
1546 case FileMode_ReadBinary:
1547 m = "rb";
1548 break;
1549
1550 case FileMode_WriteBinary:
1551 m = "wb";
1552 break;
1553
1554 default:
1555 throw OrthancException(ErrorCode_ParameterOutOfRange);
1556 }
1557
1558 return fopen(path.c_str(), m);
1559 }
1560
1561 1087
1562 1088
1563 static bool IsUnreservedCharacter(char c) 1089 static bool IsUnreservedCharacter(char c)
1564 { 1090 {
1565 // This function checks whether "c" is an unserved character 1091 // This function checks whether "c" is an unserved character
1696 else 1222 else
1697 { 1223 {
1698 return static_cast<unsigned int>(v); 1224 return static_cast<unsigned int>(v);
1699 } 1225 }
1700 } 1226 }
1227
1228
1229 std::string Toolbox::GenerateUuid()
1230 {
1231 #ifdef WIN32
1232 UUID uuid;
1233 UuidCreate ( &uuid );
1234
1235 unsigned char * str;
1236 UuidToStringA ( &uuid, &str );
1237
1238 std::string s( ( char* ) str );
1239
1240 RpcStringFreeA ( &str );
1241 #else
1242 uuid_t uuid;
1243 uuid_generate_random ( uuid );
1244 char s[37];
1245 uuid_unparse ( uuid, s );
1246 #endif
1247 return s;
1248 }
1249
1250
1251 bool Toolbox::IsUuid(const std::string& str)
1252 {
1253 if (str.size() != 36)
1254 {
1255 return false;
1256 }
1257
1258 for (size_t i = 0; i < str.length(); i++)
1259 {
1260 if (i == 8 || i == 13 || i == 18 || i == 23)
1261 {
1262 if (str[i] != '-')
1263 return false;
1264 }
1265 else
1266 {
1267 if (!isalnum(str[i]))
1268 return false;
1269 }
1270 }
1271
1272 return true;
1273 }
1274
1275
1276 bool Toolbox::StartsWithUuid(const std::string& str)
1277 {
1278 if (str.size() < 36)
1279 {
1280 return false;
1281 }
1282
1283 if (str.size() == 36)
1284 {
1285 return IsUuid(str);
1286 }
1287
1288 assert(str.size() > 36);
1289 if (!isspace(str[36]))
1290 {
1291 return false;
1292 }
1293
1294 return IsUuid(str.substr(0, 36));
1295 }
1701 } 1296 }