comparison Resources/WebAssembly/dcdict.cc @ 2513:97a74f0eac7a

loading DICOM dictionaries in sandboxed environments
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 28 Mar 2018 18:02:07 +0200
parents
children
comparison
equal deleted inserted replaced
2512:4dcafa8d6633 2513:97a74f0eac7a
1 /*
2 *
3 * Copyright (C) 1994-2016, OFFIS e.V.
4 * All rights reserved. See COPYRIGHT file for details.
5 *
6 * This software and supporting documentation were developed by
7 *
8 * OFFIS e.V.
9 * R&D Division Health
10 * Escherweg 2
11 * D-26121 Oldenburg, Germany
12 *
13 *
14 * Module: dcmdata
15 *
16 * Author: Andrew Hewett
17 *
18 * Purpose: loadable DICOM data dictionary
19 *
20 */
21
22
23 #include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */
24
25 #include "dcmtk/ofstd/ofstd.h"
26 #include "dcmtk/dcmdata/dcdict.h"
27 #include "dcmtk/ofstd/ofdefine.h"
28 #include "dcmtk/dcmdata/dcdicent.h"
29 #include "dcmtk/dcmdata/dctypes.h"
30
31 #define INCLUDE_CSTDLIB
32 #define INCLUDE_CSTDIO
33 #define INCLUDE_CSTRING
34 #define INCLUDE_CCTYPE
35 #include "dcmtk/ofstd/ofstdinc.h"
36
37 /*
38 ** The separator character between fields in the data dictionary file(s)
39 */
40 #define DCM_DICT_FIELD_SEPARATOR_CHAR '\t'
41
42 /*
43 ** Comment character for the data dictionary file(s)
44 */
45 #define DCM_DICT_COMMENT_CHAR '#'
46
47 /*
48 ** THE Global DICOM Data Dictionary
49 */
50
51 GlobalDcmDataDictionary dcmDataDict;
52
53
54 /*
55 ** Member Functions
56 */
57
58 static DcmDictEntry*
59 makeSkelEntry(Uint16 group, Uint16 element,
60 Uint16 upperGroup, Uint16 upperElement,
61 DcmEVR evr, const char* tagName, int vmMin, int vmMax,
62 const char* standardVersion,
63 DcmDictRangeRestriction groupRestriction,
64 DcmDictRangeRestriction elementRestriction,
65 const char* privCreator)
66 {
67 DcmDictEntry* e = NULL;
68 e = new DcmDictEntry(group, element, upperGroup, upperElement, evr,
69 tagName, vmMin, vmMax, standardVersion, OFFalse, privCreator);
70 if (e != NULL) {
71 e->setGroupRangeRestriction(groupRestriction);
72 e->setElementRangeRestriction(elementRestriction);
73 }
74 return e;
75 }
76
77
78 OFBool DcmDataDictionary::loadSkeletonDictionary()
79 {
80 /*
81 ** We need to know about Group Lengths to compute them
82 */
83 DcmDictEntry* e = NULL;
84 e = makeSkelEntry(0x0000, 0x0000, 0xffff, 0x0000,
85 EVR_UL, "GenericGroupLength", 1, 1, "GENERIC",
86 DcmDictRange_Unspecified, DcmDictRange_Unspecified, NULL);
87 addEntry(e);
88
89 /*
90 ** We need to know about Items and Delimitation Items to parse
91 ** (and construct) sequences.
92 */
93 e = makeSkelEntry(0xfffe, 0xe000, 0xfffe, 0xe000,
94 EVR_na, "Item", 1, 1, "DICOM",
95 DcmDictRange_Unspecified, DcmDictRange_Unspecified, NULL);
96 addEntry(e);
97 e = makeSkelEntry(0xfffe, 0xe00d, 0xfffe, 0xe00d,
98 EVR_na, "ItemDelimitationItem", 1, 1, "DICOM",
99 DcmDictRange_Unspecified, DcmDictRange_Unspecified, NULL);
100 addEntry(e);
101 e = makeSkelEntry(0xfffe, 0xe0dd, 0xfffe, 0xe0dd,
102 EVR_na, "SequenceDelimitationItem", 1, 1, "DICOM",
103 DcmDictRange_Unspecified, DcmDictRange_Unspecified, NULL);
104 addEntry(e);
105
106 skeletonCount = numberOfEntries();
107 return OFTrue;
108 }
109
110
111 DcmDataDictionary::DcmDataDictionary(OFBool loadBuiltin, OFBool loadExternal)
112 : hashDict(),
113 repDict(),
114 skeletonCount(0),
115 dictionaryLoaded(OFFalse)
116 {
117 reloadDictionaries(loadBuiltin, loadExternal);
118 }
119
120 DcmDataDictionary::~DcmDataDictionary()
121 {
122 clear();
123 }
124
125
126 void DcmDataDictionary::clear()
127 {
128 hashDict.clear();
129 repDict.clear();
130 skeletonCount = 0;
131 dictionaryLoaded = OFFalse;
132 }
133
134
135 static void
136 stripWhitespace(char* s)
137 {
138 if (s)
139 {
140 unsigned char c;
141 unsigned char *t;
142 unsigned char *p;
143 t=p=OFreinterpret_cast(unsigned char *, s);
144 while ((c = *t++)) if (!isspace(c)) *p++ = c;
145 *p = '\0';
146 }
147 }
148
149 static char*
150 stripTrailingWhitespace(char* s)
151 {
152 if (s == NULL) return s;
153 for
154 (
155 char* it = s + strlen(s) - 1;
156 it >= s && isspace(OFstatic_cast(unsigned char, *it));
157 *it-- = '\0'
158 );
159 return s;
160 }
161
162 static void
163 stripLeadingWhitespace(char* s)
164 {
165 if (s)
166 {
167 unsigned char c;
168 unsigned char *t;
169 unsigned char *p;
170 t=p=OFreinterpret_cast(unsigned char *, s);
171 while (isspace(*t)) t++;
172 while ((c = *t++)) *p++ = c;
173 *p = '\0';
174 }
175 }
176
177 static OFBool
178 parseVMField(char* vmField, int& vmMin, int& vmMax)
179 {
180 OFBool ok = OFTrue;
181 char c = 0;
182 int dummy = 0;
183
184 /* strip any whitespace */
185 stripWhitespace(vmField);
186
187 if (sscanf(vmField, "%d-%d%c", &vmMin, &dummy, &c) == 3) {
188 /* treat "2-2n" like "2-n" for the moment */
189 if ((c == 'n') || (c == 'N')) {
190 vmMax = DcmVariableVM;
191 } else {
192 ok = OFFalse;
193 }
194 } else if (sscanf(vmField, "%d-%d", &vmMin, &vmMax) == 2) {
195 /* range VM (e.g. "2-6") */
196 } else if (sscanf(vmField, "%d-%c", &vmMin, &c) == 2) {
197 if ((c == 'n') || (c == 'N')) {
198 vmMax = DcmVariableVM;
199 } else {
200 ok = OFFalse;
201 }
202 } else if (sscanf(vmField, "%d%c", &vmMin, &c) == 2) {
203 /* treat "2n" like "2-n" for the moment */
204 if ((c == 'n') || (c == 'N')) {
205 vmMax = DcmVariableVM;
206 } else {
207 ok = OFFalse;
208 }
209 } else if (sscanf(vmField, "%d", &vmMin) == 1) {
210 /* fixed VM */
211 vmMax = vmMin;
212 } else if (sscanf(vmField, "%c", &c) == 1) {
213 /* treat "n" like "1-n" */
214 if ((c == 'n') || (c == 'N')) {
215 vmMin = 1;
216 vmMax = DcmVariableVM;
217 } else {
218 ok = OFFalse;
219 }
220 } else {
221 ok = OFFalse;
222 }
223 return ok;
224 }
225
226 static int
227 splitFields(const char* line, char* fields[], int maxFields, char splitChar)
228 {
229 const char *p;
230 int foundFields = 0;
231 size_t len;
232
233 do {
234 #ifdef __BORLANDC__
235 // Borland Builder expects a non-const argument
236 p = strchr(OFconst_cast(char *, line), splitChar);
237 #else
238 p = strchr(line, splitChar);
239 #endif
240 if (p == NULL) {
241 len = strlen(line);
242 } else {
243 len = p - line;
244 }
245 fields[foundFields] = OFstatic_cast(char *, malloc(len + 1));
246 strncpy(fields[foundFields], line, len);
247 fields[foundFields][len] = '\0';
248 foundFields++;
249 line = p + 1;
250 } while ((foundFields < maxFields) && (p != NULL));
251
252 return foundFields;
253 }
254
255 static OFBool
256 parseTagPart(char *s, unsigned int& l, unsigned int& h,
257 DcmDictRangeRestriction& r)
258 {
259 OFBool ok = OFTrue;
260 char restrictor = ' ';
261
262 r = DcmDictRange_Unspecified; /* by default */
263
264 if (sscanf(s, "%x-%c-%x", &l, &restrictor, &h) == 3) {
265 switch (restrictor) {
266 case 'o':
267 case 'O':
268 r = DcmDictRange_Odd;
269 break;
270 case 'e':
271 case 'E':
272 r = DcmDictRange_Even;
273 break;
274 case 'u':
275 case 'U':
276 r = DcmDictRange_Unspecified;
277 break;
278 default:
279 DCMDATA_ERROR("DcmDataDictionary: Unknown range restrictor: " << restrictor);
280 ok = OFFalse;
281 break;
282 }
283 } else if (sscanf(s, "%x-%x", &l, &h) == 2) {
284 r = DcmDictRange_Even; /* by default */
285 } else if (sscanf(s, "%x", &l) == 1) {
286 h = l;
287 } else {
288 ok = OFFalse;
289 }
290 return ok;
291 }
292
293 static OFBool
294 parseWholeTagField(char* s, DcmTagKey& key,
295 DcmTagKey& upperKey,
296 DcmDictRangeRestriction& groupRestriction,
297 DcmDictRangeRestriction& elementRestriction,
298 char *&privCreator)
299 {
300 unsigned int gl, gh, el, eh;
301 groupRestriction = DcmDictRange_Unspecified;
302 elementRestriction = DcmDictRange_Unspecified;
303
304 stripLeadingWhitespace(s);
305 stripTrailingWhitespace(s);
306
307 char gs[64];
308 char es[64];
309 char pc[64];
310 size_t slen = strlen(s);
311
312 if (s[0] != '(') return OFFalse;
313 if (s[slen - 1] != ')') return OFFalse;
314 if (strchr(s, ',') == NULL) return OFFalse;
315
316 /* separate the group and element parts */
317 int i = 1; /* after the '(' */
318 int gi = 0;
319 for (; s[i] != ',' && s[i] != '\0'; i++)
320 {
321 gs[gi] = s[i];
322 gi++;
323 }
324 gs[gi] = '\0';
325
326 if (s[i] == '\0') return OFFalse; /* element part missing */
327 i++; /* after the ',' */
328
329 stripLeadingWhitespace(s + i);
330
331 int pi = 0;
332 if (s[i] == '\"') /* private creator */
333 {
334 i++; // skip opening quotation mark
335 for (; s[i] != '\"' && s[i] != '\0'; i++) pc[pi++] = s[i];
336 pc[pi] = '\0';
337 if (s[i] == '\0') return OFFalse; /* closing quotation mark missing */
338 i++;
339 stripLeadingWhitespace(s + i);
340 if (s[i] != ',') return OFFalse; /* element part missing */
341 i++; /* after the ',' */
342 }
343
344 int ei = 0;
345 for (; s[i] != ')' && s[i] != '\0'; i++) {
346 es[ei] = s[i];
347 ei++;
348 }
349 es[ei] = '\0';
350
351 /* parse the tag parts into their components */
352 stripWhitespace(gs);
353 if (parseTagPart(gs, gl, gh, groupRestriction) == OFFalse)
354 return OFFalse;
355
356 stripWhitespace(es);
357 if (parseTagPart(es, el, eh, elementRestriction) == OFFalse)
358 return OFFalse;
359
360 if (pi > 0)
361 {
362 // copy private creator name
363 privCreator = new char[strlen(pc) + 1]; // deleted by caller
364 if (privCreator) strcpy(privCreator,pc);
365 }
366
367 key.set(OFstatic_cast(unsigned short, gl), OFstatic_cast(unsigned short, el));
368 upperKey.set(OFstatic_cast(unsigned short, gh), OFstatic_cast(unsigned short, eh));
369
370 return OFTrue;
371 }
372
373 static OFBool
374 onlyWhitespace(const char* s)
375 {
376 size_t len = strlen(s);
377 int charsFound = OFFalse;
378
379 for (size_t i = 0; (!charsFound) && (i < len); ++i) {
380 charsFound = !isspace(OFstatic_cast(unsigned char, s[i]));
381 }
382 return (!charsFound)? (OFTrue) : (OFFalse);
383 }
384
385 static char*
386 getLine(char* line, int maxLineLen, FILE* f)
387 {
388 char* s;
389
390 s = fgets(line, maxLineLen, f);
391
392 /* strip any trailing white space */
393 stripTrailingWhitespace(line);
394
395 return s;
396 }
397
398 static OFBool
399 isaCommentLine(const char* s)
400 {
401 OFBool isComment = OFFalse; /* assumption */
402 size_t len = strlen(s);
403 size_t i = 0;
404 for (i = 0; i < len && isspace(OFstatic_cast(unsigned char, s[i])); ++i) /*loop*/;
405 isComment = (s[i] == DCM_DICT_COMMENT_CHAR);
406 return isComment;
407 }
408
409 OFBool
410 DcmDataDictionary::reloadDictionaries(OFBool loadBuiltin, OFBool loadExternal)
411 {
412 OFBool result = OFTrue;
413 clear();
414 loadSkeletonDictionary();
415 if (loadBuiltin) {
416 loadBuiltinDictionary();
417 dictionaryLoaded = (numberOfEntries() > skeletonCount);
418 if (!dictionaryLoaded) result = OFFalse;
419 }
420 if (loadExternal) {
421 if (loadExternalDictionaries())
422 dictionaryLoaded = OFTrue;
423 else
424 result = OFFalse;
425 }
426 return result;
427 }
428
429 OFBool
430 DcmDataDictionary::loadDictionary(const char* fileName, OFBool errorIfAbsent)
431 {
432
433 char lineBuf[DCM_MAXDICTLINESIZE + 1];
434 FILE* f = NULL;
435 int lineNumber = 0;
436 char* lineFields[DCM_MAXDICTFIELDS + 1];
437 int fieldsPresent;
438 DcmDictEntry* e;
439 int errorsEncountered = 0;
440 OFBool errorOnThisLine = OFFalse;
441 int i;
442
443 DcmTagKey key, upperKey;
444 DcmDictRangeRestriction groupRestriction = DcmDictRange_Unspecified;
445 DcmDictRangeRestriction elementRestriction = DcmDictRange_Unspecified;
446 DcmVR vr;
447 char* vrName;
448 char* tagName;
449 char* privCreator;
450 int vmMin, vmMax = 1;
451 const char* standardVersion;
452
453 /* first, check whether 'fileName' really points to a file (and not to a directory or the like) */
454 if (!OFStandard::fileExists(fileName) || (f = fopen(fileName, "r")) == NULL) {
455 if (errorIfAbsent) {
456 DCMDATA_ERROR("DcmDataDictionary: Cannot open file: " << fileName);
457 }
458 return OFFalse;
459 }
460
461 DCMDATA_DEBUG("DcmDataDictionary: Loading file: " << fileName);
462
463 while (getLine(lineBuf, DCM_MAXDICTLINESIZE, f)) {
464 lineNumber++;
465
466 if (onlyWhitespace(lineBuf)) {
467 continue; /* ignore this line */
468 }
469 if (isaCommentLine(lineBuf)) {
470 continue; /* ignore this line */
471 }
472
473 errorOnThisLine = OFFalse;
474
475 /* fields are tab separated */
476 fieldsPresent = splitFields(lineBuf, lineFields,
477 DCM_MAXDICTFIELDS,
478 DCM_DICT_FIELD_SEPARATOR_CHAR);
479
480 /* initialize dict entry fields */
481 vrName = NULL;
482 tagName = NULL;
483 privCreator = NULL;
484 vmMin = vmMax = 1;
485 standardVersion = "DICOM";
486
487 switch (fieldsPresent) {
488 case 0:
489 case 1:
490 case 2:
491 DCMDATA_ERROR("DcmDataDictionary: "<< fileName << ": "
492 << "too few fields (line " << lineNumber << ")");
493 errorOnThisLine = OFTrue;
494 break;
495 default:
496 DCMDATA_ERROR("DcmDataDictionary: " << fileName << ": "
497 << "too many fields (line " << lineNumber << "): ");
498 errorOnThisLine = OFTrue;
499 break;
500 case 5:
501 stripWhitespace(lineFields[4]);
502 standardVersion = lineFields[4];
503 /* drop through to next case label */
504 case 4:
505 /* the VM field is present */
506 if (!parseVMField(lineFields[3], vmMin, vmMax)) {
507 DCMDATA_ERROR("DcmDataDictionary: " << fileName << ": "
508 << "bad VM field (line " << lineNumber << "): " << lineFields[3]);
509 errorOnThisLine = OFTrue;
510 }
511 /* drop through to next case label */
512 case 3:
513 if (!parseWholeTagField(lineFields[0], key, upperKey,
514 groupRestriction, elementRestriction, privCreator))
515 {
516 DCMDATA_ERROR("DcmDataDictionary: " << fileName << ": "
517 << "bad Tag field (line " << lineNumber << "): " << lineFields[0]);
518 errorOnThisLine = OFTrue;
519 } else {
520 /* all is OK */
521 vrName = lineFields[1];
522 stripWhitespace(vrName);
523
524 tagName = lineFields[2];
525 stripWhitespace(tagName);
526 }
527 }
528
529 if (!errorOnThisLine) {
530 /* check the VR Field */
531 vr.setVR(vrName);
532 if (vr.getEVR() == EVR_UNKNOWN) {
533 DCMDATA_ERROR("DcmDataDictionary: " << fileName << ": "
534 << "bad VR field (line " << lineNumber << "): " << vrName);
535 errorOnThisLine = OFTrue;
536 }
537 }
538
539 if (!errorOnThisLine) {
540 e = new DcmDictEntry(
541 key.getGroup(), key.getElement(),
542 upperKey.getGroup(), upperKey.getElement(),
543 vr, tagName, vmMin, vmMax, standardVersion, OFTrue,
544 privCreator);
545
546 e->setGroupRangeRestriction(groupRestriction);
547 e->setElementRangeRestriction(elementRestriction);
548 addEntry(e);
549 }
550
551 for (i = 0; i < fieldsPresent; i++) {
552 free(lineFields[i]);
553 lineFields[i] = NULL;
554 }
555
556 delete[] privCreator;
557
558 if (errorOnThisLine) {
559 errorsEncountered++;
560 }
561 }
562
563 fclose(f);
564
565 /* return OFFalse in case of errors and set internal state accordingly */
566 if (errorsEncountered == 0) {
567 dictionaryLoaded = OFTrue;
568 return OFTrue;
569 }
570 else {
571 dictionaryLoaded = OFFalse;
572 return OFFalse;
573 }
574 }
575
576 #ifndef HAVE_GETENV
577
578 static
579 char* getenv() {
580 return NULL;
581 }
582
583 #endif /* !HAVE_GETENV */
584
585
586
587 OFBool
588 DcmDataDictionary::loadExternalDictionaries()
589 {
590 const char* env = NULL;
591 size_t len;
592 int sepCnt = 0;
593 OFBool msgIfDictAbsent = OFTrue;
594 OFBool loadFailed = OFFalse;
595
596 env = getenv(DCM_DICT_ENVIRONMENT_VARIABLE);
597 if ((env == NULL) || (strlen(env) == 0)) {
598 env = DCM_DICT_DEFAULT_PATH;
599 msgIfDictAbsent = OFFalse;
600 }
601
602 if ((env != NULL) && (strlen(env) != 0)) {
603 len = strlen(env);
604 for (size_t i = 0; i < len; ++i) {
605 if (env[i] == ENVIRONMENT_PATH_SEPARATOR) {
606 sepCnt++;
607 }
608 }
609
610 if (sepCnt == 0) {
611 if (!loadDictionary(env, msgIfDictAbsent)) {
612 return OFFalse;
613 }
614 } else {
615 char** dictArray;
616
617 dictArray = OFstatic_cast(char **, malloc((sepCnt + 1) * sizeof(char*)));
618
619 int ndicts = splitFields(env, dictArray, sepCnt + 1,
620 ENVIRONMENT_PATH_SEPARATOR);
621
622 for (int ii = 0; ii < ndicts; ii++) {
623 if ((dictArray[ii] != NULL) && (strlen(dictArray[ii]) > 0)) {
624 if (!loadDictionary(dictArray[ii], msgIfDictAbsent)) {
625 loadFailed = OFTrue;
626 }
627 }
628 free(dictArray[ii]);
629 }
630 free(dictArray);
631 }
632 }
633
634 return (loadFailed) ? (OFFalse) : (OFTrue);
635 }
636
637
638 void
639 DcmDataDictionary::addEntry(DcmDictEntry* e)
640 {
641 if (e->isRepeating()) {
642 /*
643 * Find the best position in repeating tag list
644 * Existing entries are replaced if the ranges and repetition
645 * constraints are the same.
646 * If a range represents a subset of an existing range then it
647 * will be placed before it in the list. This ensures that a
648 * search will find the subset rather than the superset.
649 * Otherwise entries are appended to the end of the list.
650 */
651 OFBool inserted = OFFalse;
652
653 DcmDictEntryListIterator iter(repDict.begin());
654 DcmDictEntryListIterator last(repDict.end());
655 for (; !inserted && iter != last; ++iter) {
656 if (e->setEQ(**iter)) {
657 /* replace the old entry with the new */
658 DcmDictEntry *old = *iter;
659 *iter = e;
660 #ifdef PRINT_REPLACED_DICTIONARY_ENTRIES
661 DCMDATA_WARN("replacing " << *old);
662 #endif
663 delete old;
664 inserted = OFTrue;
665 } else if (e->subset(**iter)) {
666 /* e is a subset of the current list position, insert before */
667 repDict.insert(iter, e);
668 inserted = OFTrue;
669 }
670 }
671 if (!inserted) {
672 /* insert at end */
673 repDict.push_back(e);
674 inserted = OFTrue;
675 }
676 } else {
677 hashDict.put(e);
678 }
679 }
680
681 void
682 DcmDataDictionary::deleteEntry(const DcmDictEntry& entry)
683 {
684 DcmDictEntry* e = NULL;
685 e = OFconst_cast(DcmDictEntry *, findEntry(entry));
686 if (e != NULL) {
687 if (e->isRepeating()) {
688 repDict.remove(e);
689 delete e;
690 } else {
691 hashDict.del(entry.getKey(), entry.getPrivateCreator());
692 }
693 }
694 }
695
696 const DcmDictEntry*
697 DcmDataDictionary::findEntry(const DcmDictEntry& entry) const
698 {
699 const DcmDictEntry* e = NULL;
700
701 if (entry.isRepeating()) {
702 OFBool found = OFFalse;
703 DcmDictEntryListConstIterator iter(repDict.begin());
704 DcmDictEntryListConstIterator last(repDict.end());
705 for (; !found && iter != last; ++iter) {
706 if (entry.setEQ(**iter)) {
707 found = OFTrue;
708 e = *iter;
709 }
710 }
711 } else {
712 e = hashDict.get(entry, entry.getPrivateCreator());
713 }
714 return e;
715 }
716
717 const DcmDictEntry*
718 DcmDataDictionary::findEntry(const DcmTagKey& key, const char *privCreator) const
719 {
720 /* search first in the normal tags dictionary and if not found
721 * then search in the repeating tags list.
722 */
723 const DcmDictEntry* e = NULL;
724
725 e = hashDict.get(key, privCreator);
726 if (e == NULL) {
727 /* search in the repeating tags dictionary */
728 OFBool found = OFFalse;
729 DcmDictEntryListConstIterator iter(repDict.begin());
730 DcmDictEntryListConstIterator last(repDict.end());
731 for (; !found && iter != last; ++iter) {
732 if ((*iter)->contains(key, privCreator)) {
733 found = OFTrue;
734 e = *iter;
735 }
736 }
737 }
738 return e;
739 }
740
741 const DcmDictEntry*
742 DcmDataDictionary::findEntry(const char *name) const
743 {
744 const DcmDictEntry* e = NULL;
745 const DcmDictEntry* ePrivate = NULL;
746
747 /* search first in the normal tags dictionary and if not found
748 * then search in the repeating tags list.
749 */
750 DcmHashDictIterator iter;
751 for (iter = hashDict.begin(); (e == NULL) && (iter != hashDict.end()); ++iter) {
752 if ((*iter)->contains(name)) {
753 e = *iter;
754 if (e->getGroup() % 2)
755 {
756 /* tag is a private tag - continue search to be sure to find non-private keys first */
757 if (!ePrivate) ePrivate = e;
758 e = NULL;
759 }
760 }
761 }
762
763 if (e == NULL) {
764 /* search in the repeating tags dictionary */
765 OFBool found = OFFalse;
766 DcmDictEntryListConstIterator iter2(repDict.begin());
767 DcmDictEntryListConstIterator last(repDict.end());
768 for (; !found && iter2 != last; ++iter2) {
769 if ((*iter2)->contains(name)) {
770 found = OFTrue;
771 e = *iter2;
772 }
773 }
774 }
775
776 if (e == NULL && ePrivate != NULL) {
777 /* no standard key found - use the first private key found */
778 e = ePrivate;
779 }
780
781 return e;
782 }
783
784
785 /* ================================================================== */
786
787
788 GlobalDcmDataDictionary::GlobalDcmDataDictionary()
789 : dataDict(NULL)
790 #ifdef WITH_THREADS
791 , dataDictLock()
792 #endif
793 {
794 }
795
796 GlobalDcmDataDictionary::~GlobalDcmDataDictionary()
797 {
798 /* No threads may be active any more, so no locking needed */
799 delete dataDict;
800 }
801
802 void GlobalDcmDataDictionary::createDataDict()
803 {
804 /* Make sure only one thread tries to initialize the dictionary */
805 #ifdef WITH_THREADS
806 dataDictLock.wrlock();
807 #endif
808 #ifdef DONT_LOAD_EXTERNAL_DICTIONARIES
809 const OFBool loadExternal = OFFalse;
810 #else
811 const OFBool loadExternal = OFTrue;
812 #endif
813 /* Make sure no other thread managed to create the dictionary
814 * before we got our write lock. */
815 if (!dataDict)
816 dataDict = new DcmDataDictionary(OFTrue /*loadBuiltin*/, loadExternal);
817 #ifdef WITH_THREADS
818 dataDictLock.unlock();
819 #endif
820 }
821
822 const DcmDataDictionary& GlobalDcmDataDictionary::rdlock()
823 {
824 #ifdef WITH_THREADS
825 dataDictLock.rdlock();
826 #endif
827 if (!dataDict)
828 {
829 /* dataDictLock must not be locked during createDataDict() */
830 #ifdef WITH_THREADS
831 dataDictLock.unlock();
832 #endif
833 createDataDict();
834 #ifdef WITH_THREADS
835 dataDictLock.rdlock();
836 #endif
837 }
838 return *dataDict;
839 }
840
841 DcmDataDictionary& GlobalDcmDataDictionary::wrlock()
842 {
843 #ifdef WITH_THREADS
844 dataDictLock.wrlock();
845 #endif
846 if (!dataDict)
847 {
848 /* dataDictLock must not be locked during createDataDict() */
849 #ifdef WITH_THREADS
850 dataDictLock.unlock();
851 #endif
852 createDataDict();
853 #ifdef WITH_THREADS
854 dataDictLock.wrlock();
855 #endif
856 }
857 return *dataDict;
858 }
859
860 void GlobalDcmDataDictionary::unlock()
861 {
862 #ifdef WITH_THREADS
863 dataDictLock.unlock();
864 #endif
865 }
866
867 OFBool GlobalDcmDataDictionary::isDictionaryLoaded()
868 {
869 OFBool result = rdlock().isDictionaryLoaded();
870 unlock();
871 return result;
872 }
873
874 void GlobalDcmDataDictionary::clear()
875 {
876 wrlock().clear();
877 unlock();
878 }
879
880
881
882
883 // Function by the Orthanc project to load a dictionary from a memory
884 // buffer, which is necessary in sandboxed environments. This is an
885 // adapted version of DcmDataDictionary::loadDictionary().
886
887
888 #include <boost/noncopyable.hpp>
889
890 struct OrthancLinesIterator;
891
892 // This plain old C class is implemented in "../../Core/Toolbox.h"
893 OrthancLinesIterator* OrthancLinesIterator_Create(const std::string& content);
894
895 bool OrthancLinesIterator_GetLine(std::string& target,
896 const OrthancLinesIterator* iterator);
897
898 void OrthancLinesIterator_Next(OrthancLinesIterator* iterator);
899
900 void OrthancLinesIterator_Free(OrthancLinesIterator* iterator);
901
902
903 class LinesIterator : public boost::noncopyable
904 {
905 private:
906 OrthancLinesIterator* iterator_;
907
908 public:
909 LinesIterator(const std::string& content) :
910 iterator_(NULL)
911 {
912 iterator_ = OrthancLinesIterator_Create(content);
913 }
914
915 ~LinesIterator()
916 {
917 if (iterator_ != NULL)
918 {
919 OrthancLinesIterator_Free(iterator_);
920 iterator_ = NULL;
921 }
922 }
923
924 bool GetLine(std::string& target) const
925 {
926 if (iterator_ != NULL)
927 {
928 return OrthancLinesIterator_GetLine(target, iterator_);
929 }
930 else
931 {
932 return false;
933 }
934 }
935
936 void Next()
937 {
938 if (iterator_ != NULL)
939 {
940 OrthancLinesIterator_Next(iterator_);
941 }
942 }
943 };
944
945
946
947 OFBool
948 DcmDataDictionary::loadFromMemory(const std::string& content, OFBool errorIfAbsent)
949 {
950 int lineNumber = 0;
951 char* lineFields[DCM_MAXDICTFIELDS + 1];
952 int fieldsPresent;
953 DcmDictEntry* e;
954 int errorsEncountered = 0;
955 OFBool errorOnThisLine = OFFalse;
956 int i;
957
958 DcmTagKey key, upperKey;
959 DcmDictRangeRestriction groupRestriction = DcmDictRange_Unspecified;
960 DcmDictRangeRestriction elementRestriction = DcmDictRange_Unspecified;
961 DcmVR vr;
962 char* vrName;
963 char* tagName;
964 char* privCreator;
965 int vmMin, vmMax = 1;
966 const char* standardVersion;
967
968 LinesIterator iterator(content);
969
970 std::string line;
971 while (iterator.GetLine(line)) {
972 iterator.Next();
973
974 if (line.size() >= DCM_MAXDICTLINESIZE) {
975 DCMDATA_ERROR("DcmDataDictionary: Too long line: " << line);
976 continue;
977 }
978
979 lineNumber++;
980
981 if (onlyWhitespace(line.c_str())) {
982 continue; /* ignore this line */
983 }
984 if (isaCommentLine(line.c_str())) {
985 continue; /* ignore this line */
986 }
987
988 errorOnThisLine = OFFalse;
989
990 /* fields are tab separated */
991 fieldsPresent = splitFields(line.c_str(), lineFields,
992 DCM_MAXDICTFIELDS,
993 DCM_DICT_FIELD_SEPARATOR_CHAR);
994
995 /* initialize dict entry fields */
996 vrName = NULL;
997 tagName = NULL;
998 privCreator = NULL;
999 vmMin = vmMax = 1;
1000 standardVersion = "DICOM";
1001
1002 switch (fieldsPresent) {
1003 case 0:
1004 case 1:
1005 case 2:
1006 DCMDATA_ERROR("DcmDataDictionary: "
1007 << "too few fields (line " << lineNumber << ")");
1008 errorOnThisLine = OFTrue;
1009 break;
1010 default:
1011 DCMDATA_ERROR("DcmDataDictionary: "
1012 << "too many fields (line " << lineNumber << "): ");
1013 errorOnThisLine = OFTrue;
1014 break;
1015 case 5:
1016 stripWhitespace(lineFields[4]);
1017 standardVersion = lineFields[4];
1018 /* drop through to next case label */
1019 case 4:
1020 /* the VM field is present */
1021 if (!parseVMField(lineFields[3], vmMin, vmMax)) {
1022 DCMDATA_ERROR("DcmDataDictionary: "
1023 << "bad VM field (line " << lineNumber << "): " << lineFields[3]);
1024 errorOnThisLine = OFTrue;
1025 }
1026 /* drop through to next case label */
1027 case 3:
1028 if (!parseWholeTagField(lineFields[0], key, upperKey,
1029 groupRestriction, elementRestriction, privCreator))
1030 {
1031 DCMDATA_ERROR("DcmDataDictionary: "
1032 << "bad Tag field (line " << lineNumber << "): " << lineFields[0]);
1033 errorOnThisLine = OFTrue;
1034 } else {
1035 /* all is OK */
1036 vrName = lineFields[1];
1037 stripWhitespace(vrName);
1038
1039 tagName = lineFields[2];
1040 stripWhitespace(tagName);
1041 }
1042 }
1043
1044 if (!errorOnThisLine) {
1045 /* check the VR Field */
1046 vr.setVR(vrName);
1047 if (vr.getEVR() == EVR_UNKNOWN) {
1048 DCMDATA_ERROR("DcmDataDictionary: "
1049 << "bad VR field (line " << lineNumber << "): " << vrName);
1050 errorOnThisLine = OFTrue;
1051 }
1052 }
1053
1054 if (!errorOnThisLine) {
1055 e = new DcmDictEntry(
1056 key.getGroup(), key.getElement(),
1057 upperKey.getGroup(), upperKey.getElement(),
1058 vr, tagName, vmMin, vmMax, standardVersion, OFTrue,
1059 privCreator);
1060
1061 e->setGroupRangeRestriction(groupRestriction);
1062 e->setElementRangeRestriction(elementRestriction);
1063 addEntry(e);
1064 }
1065
1066 for (i = 0; i < fieldsPresent; i++) {
1067 free(lineFields[i]);
1068 lineFields[i] = NULL;
1069 }
1070
1071 delete[] privCreator;
1072
1073 if (errorOnThisLine) {
1074 errorsEncountered++;
1075 }
1076 }
1077
1078 /* return OFFalse in case of errors and set internal state accordingly */
1079 if (errorsEncountered == 0) {
1080 dictionaryLoaded = OFTrue;
1081 return OFTrue;
1082 }
1083 else {
1084 dictionaryLoaded = OFFalse;
1085 return OFFalse;
1086 }
1087 }