Mercurial > hg > orthanc
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Resources/WebAssembly/dcdict.cc Wed Mar 28 18:02:07 2018 +0200 @@ -0,0 +1,1087 @@ +/* + * + * Copyright (C) 1994-2016, OFFIS e.V. + * All rights reserved. See COPYRIGHT file for details. + * + * This software and supporting documentation were developed by + * + * OFFIS e.V. + * R&D Division Health + * Escherweg 2 + * D-26121 Oldenburg, Germany + * + * + * Module: dcmdata + * + * Author: Andrew Hewett + * + * Purpose: loadable DICOM data dictionary + * + */ + + +#include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ + +#include "dcmtk/ofstd/ofstd.h" +#include "dcmtk/dcmdata/dcdict.h" +#include "dcmtk/ofstd/ofdefine.h" +#include "dcmtk/dcmdata/dcdicent.h" +#include "dcmtk/dcmdata/dctypes.h" + +#define INCLUDE_CSTDLIB +#define INCLUDE_CSTDIO +#define INCLUDE_CSTRING +#define INCLUDE_CCTYPE +#include "dcmtk/ofstd/ofstdinc.h" + +/* +** The separator character between fields in the data dictionary file(s) +*/ +#define DCM_DICT_FIELD_SEPARATOR_CHAR '\t' + +/* +** Comment character for the data dictionary file(s) +*/ +#define DCM_DICT_COMMENT_CHAR '#' + +/* +** THE Global DICOM Data Dictionary +*/ + +GlobalDcmDataDictionary dcmDataDict; + + +/* +** Member Functions +*/ + +static DcmDictEntry* +makeSkelEntry(Uint16 group, Uint16 element, + Uint16 upperGroup, Uint16 upperElement, + DcmEVR evr, const char* tagName, int vmMin, int vmMax, + const char* standardVersion, + DcmDictRangeRestriction groupRestriction, + DcmDictRangeRestriction elementRestriction, + const char* privCreator) +{ + DcmDictEntry* e = NULL; + e = new DcmDictEntry(group, element, upperGroup, upperElement, evr, + tagName, vmMin, vmMax, standardVersion, OFFalse, privCreator); + if (e != NULL) { + e->setGroupRangeRestriction(groupRestriction); + e->setElementRangeRestriction(elementRestriction); + } + return e; +} + + +OFBool DcmDataDictionary::loadSkeletonDictionary() +{ + /* + ** We need to know about Group Lengths to compute them + */ + DcmDictEntry* e = NULL; + e = makeSkelEntry(0x0000, 0x0000, 0xffff, 0x0000, + EVR_UL, "GenericGroupLength", 1, 1, "GENERIC", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, NULL); + addEntry(e); + + /* + ** We need to know about Items and Delimitation Items to parse + ** (and construct) sequences. + */ + e = makeSkelEntry(0xfffe, 0xe000, 0xfffe, 0xe000, + EVR_na, "Item", 1, 1, "DICOM", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, NULL); + addEntry(e); + e = makeSkelEntry(0xfffe, 0xe00d, 0xfffe, 0xe00d, + EVR_na, "ItemDelimitationItem", 1, 1, "DICOM", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, NULL); + addEntry(e); + e = makeSkelEntry(0xfffe, 0xe0dd, 0xfffe, 0xe0dd, + EVR_na, "SequenceDelimitationItem", 1, 1, "DICOM", + DcmDictRange_Unspecified, DcmDictRange_Unspecified, NULL); + addEntry(e); + + skeletonCount = numberOfEntries(); + return OFTrue; +} + + +DcmDataDictionary::DcmDataDictionary(OFBool loadBuiltin, OFBool loadExternal) + : hashDict(), + repDict(), + skeletonCount(0), + dictionaryLoaded(OFFalse) +{ + reloadDictionaries(loadBuiltin, loadExternal); +} + +DcmDataDictionary::~DcmDataDictionary() +{ + clear(); +} + + +void DcmDataDictionary::clear() +{ + hashDict.clear(); + repDict.clear(); + skeletonCount = 0; + dictionaryLoaded = OFFalse; +} + + +static void +stripWhitespace(char* s) +{ + if (s) + { + unsigned char c; + unsigned char *t; + unsigned char *p; + t=p=OFreinterpret_cast(unsigned char *, s); + while ((c = *t++)) if (!isspace(c)) *p++ = c; + *p = '\0'; + } +} + +static char* +stripTrailingWhitespace(char* s) +{ + if (s == NULL) return s; + for + ( + char* it = s + strlen(s) - 1; + it >= s && isspace(OFstatic_cast(unsigned char, *it)); + *it-- = '\0' + ); + return s; +} + +static void +stripLeadingWhitespace(char* s) +{ + if (s) + { + unsigned char c; + unsigned char *t; + unsigned char *p; + t=p=OFreinterpret_cast(unsigned char *, s); + while (isspace(*t)) t++; + while ((c = *t++)) *p++ = c; + *p = '\0'; + } +} + +static OFBool +parseVMField(char* vmField, int& vmMin, int& vmMax) +{ + OFBool ok = OFTrue; + char c = 0; + int dummy = 0; + + /* strip any whitespace */ + stripWhitespace(vmField); + + if (sscanf(vmField, "%d-%d%c", &vmMin, &dummy, &c) == 3) { + /* treat "2-2n" like "2-n" for the moment */ + if ((c == 'n') || (c == 'N')) { + vmMax = DcmVariableVM; + } else { + ok = OFFalse; + } + } else if (sscanf(vmField, "%d-%d", &vmMin, &vmMax) == 2) { + /* range VM (e.g. "2-6") */ + } else if (sscanf(vmField, "%d-%c", &vmMin, &c) == 2) { + if ((c == 'n') || (c == 'N')) { + vmMax = DcmVariableVM; + } else { + ok = OFFalse; + } + } else if (sscanf(vmField, "%d%c", &vmMin, &c) == 2) { + /* treat "2n" like "2-n" for the moment */ + if ((c == 'n') || (c == 'N')) { + vmMax = DcmVariableVM; + } else { + ok = OFFalse; + } + } else if (sscanf(vmField, "%d", &vmMin) == 1) { + /* fixed VM */ + vmMax = vmMin; + } else if (sscanf(vmField, "%c", &c) == 1) { + /* treat "n" like "1-n" */ + if ((c == 'n') || (c == 'N')) { + vmMin = 1; + vmMax = DcmVariableVM; + } else { + ok = OFFalse; + } + } else { + ok = OFFalse; + } + return ok; +} + +static int +splitFields(const char* line, char* fields[], int maxFields, char splitChar) +{ + const char *p; + int foundFields = 0; + size_t len; + + do { +#ifdef __BORLANDC__ + // Borland Builder expects a non-const argument + p = strchr(OFconst_cast(char *, line), splitChar); +#else + p = strchr(line, splitChar); +#endif + if (p == NULL) { + len = strlen(line); + } else { + len = p - line; + } + fields[foundFields] = OFstatic_cast(char *, malloc(len + 1)); + strncpy(fields[foundFields], line, len); + fields[foundFields][len] = '\0'; + foundFields++; + line = p + 1; + } while ((foundFields < maxFields) && (p != NULL)); + + return foundFields; +} + +static OFBool +parseTagPart(char *s, unsigned int& l, unsigned int& h, + DcmDictRangeRestriction& r) +{ + OFBool ok = OFTrue; + char restrictor = ' '; + + r = DcmDictRange_Unspecified; /* by default */ + + if (sscanf(s, "%x-%c-%x", &l, &restrictor, &h) == 3) { + switch (restrictor) { + case 'o': + case 'O': + r = DcmDictRange_Odd; + break; + case 'e': + case 'E': + r = DcmDictRange_Even; + break; + case 'u': + case 'U': + r = DcmDictRange_Unspecified; + break; + default: + DCMDATA_ERROR("DcmDataDictionary: Unknown range restrictor: " << restrictor); + ok = OFFalse; + break; + } + } else if (sscanf(s, "%x-%x", &l, &h) == 2) { + r = DcmDictRange_Even; /* by default */ + } else if (sscanf(s, "%x", &l) == 1) { + h = l; + } else { + ok = OFFalse; + } + return ok; +} + +static OFBool +parseWholeTagField(char* s, DcmTagKey& key, + DcmTagKey& upperKey, + DcmDictRangeRestriction& groupRestriction, + DcmDictRangeRestriction& elementRestriction, + char *&privCreator) +{ + unsigned int gl, gh, el, eh; + groupRestriction = DcmDictRange_Unspecified; + elementRestriction = DcmDictRange_Unspecified; + + stripLeadingWhitespace(s); + stripTrailingWhitespace(s); + + char gs[64]; + char es[64]; + char pc[64]; + size_t slen = strlen(s); + + if (s[0] != '(') return OFFalse; + if (s[slen - 1] != ')') return OFFalse; + if (strchr(s, ',') == NULL) return OFFalse; + + /* separate the group and element parts */ + int i = 1; /* after the '(' */ + int gi = 0; + for (; s[i] != ',' && s[i] != '\0'; i++) + { + gs[gi] = s[i]; + gi++; + } + gs[gi] = '\0'; + + if (s[i] == '\0') return OFFalse; /* element part missing */ + i++; /* after the ',' */ + + stripLeadingWhitespace(s + i); + + int pi = 0; + if (s[i] == '\"') /* private creator */ + { + i++; // skip opening quotation mark + for (; s[i] != '\"' && s[i] != '\0'; i++) pc[pi++] = s[i]; + pc[pi] = '\0'; + if (s[i] == '\0') return OFFalse; /* closing quotation mark missing */ + i++; + stripLeadingWhitespace(s + i); + if (s[i] != ',') return OFFalse; /* element part missing */ + i++; /* after the ',' */ + } + + int ei = 0; + for (; s[i] != ')' && s[i] != '\0'; i++) { + es[ei] = s[i]; + ei++; + } + es[ei] = '\0'; + + /* parse the tag parts into their components */ + stripWhitespace(gs); + if (parseTagPart(gs, gl, gh, groupRestriction) == OFFalse) + return OFFalse; + + stripWhitespace(es); + if (parseTagPart(es, el, eh, elementRestriction) == OFFalse) + return OFFalse; + + if (pi > 0) + { + // copy private creator name + privCreator = new char[strlen(pc) + 1]; // deleted by caller + if (privCreator) strcpy(privCreator,pc); + } + + key.set(OFstatic_cast(unsigned short, gl), OFstatic_cast(unsigned short, el)); + upperKey.set(OFstatic_cast(unsigned short, gh), OFstatic_cast(unsigned short, eh)); + + return OFTrue; +} + +static OFBool +onlyWhitespace(const char* s) +{ + size_t len = strlen(s); + int charsFound = OFFalse; + + for (size_t i = 0; (!charsFound) && (i < len); ++i) { + charsFound = !isspace(OFstatic_cast(unsigned char, s[i])); + } + return (!charsFound)? (OFTrue) : (OFFalse); +} + +static char* +getLine(char* line, int maxLineLen, FILE* f) +{ + char* s; + + s = fgets(line, maxLineLen, f); + + /* strip any trailing white space */ + stripTrailingWhitespace(line); + + return s; +} + +static OFBool +isaCommentLine(const char* s) +{ + OFBool isComment = OFFalse; /* assumption */ + size_t len = strlen(s); + size_t i = 0; + for (i = 0; i < len && isspace(OFstatic_cast(unsigned char, s[i])); ++i) /*loop*/; + isComment = (s[i] == DCM_DICT_COMMENT_CHAR); + return isComment; +} + +OFBool +DcmDataDictionary::reloadDictionaries(OFBool loadBuiltin, OFBool loadExternal) +{ + OFBool result = OFTrue; + clear(); + loadSkeletonDictionary(); + if (loadBuiltin) { + loadBuiltinDictionary(); + dictionaryLoaded = (numberOfEntries() > skeletonCount); + if (!dictionaryLoaded) result = OFFalse; + } + if (loadExternal) { + if (loadExternalDictionaries()) + dictionaryLoaded = OFTrue; + else + result = OFFalse; + } + return result; +} + +OFBool +DcmDataDictionary::loadDictionary(const char* fileName, OFBool errorIfAbsent) +{ + + char lineBuf[DCM_MAXDICTLINESIZE + 1]; + FILE* f = NULL; + int lineNumber = 0; + char* lineFields[DCM_MAXDICTFIELDS + 1]; + int fieldsPresent; + DcmDictEntry* e; + int errorsEncountered = 0; + OFBool errorOnThisLine = OFFalse; + int i; + + DcmTagKey key, upperKey; + DcmDictRangeRestriction groupRestriction = DcmDictRange_Unspecified; + DcmDictRangeRestriction elementRestriction = DcmDictRange_Unspecified; + DcmVR vr; + char* vrName; + char* tagName; + char* privCreator; + int vmMin, vmMax = 1; + const char* standardVersion; + + /* first, check whether 'fileName' really points to a file (and not to a directory or the like) */ + if (!OFStandard::fileExists(fileName) || (f = fopen(fileName, "r")) == NULL) { + if (errorIfAbsent) { + DCMDATA_ERROR("DcmDataDictionary: Cannot open file: " << fileName); + } + return OFFalse; + } + + DCMDATA_DEBUG("DcmDataDictionary: Loading file: " << fileName); + + while (getLine(lineBuf, DCM_MAXDICTLINESIZE, f)) { + lineNumber++; + + if (onlyWhitespace(lineBuf)) { + continue; /* ignore this line */ + } + if (isaCommentLine(lineBuf)) { + continue; /* ignore this line */ + } + + errorOnThisLine = OFFalse; + + /* fields are tab separated */ + fieldsPresent = splitFields(lineBuf, lineFields, + DCM_MAXDICTFIELDS, + DCM_DICT_FIELD_SEPARATOR_CHAR); + + /* initialize dict entry fields */ + vrName = NULL; + tagName = NULL; + privCreator = NULL; + vmMin = vmMax = 1; + standardVersion = "DICOM"; + + switch (fieldsPresent) { + case 0: + case 1: + case 2: + DCMDATA_ERROR("DcmDataDictionary: "<< fileName << ": " + << "too few fields (line " << lineNumber << ")"); + errorOnThisLine = OFTrue; + break; + default: + DCMDATA_ERROR("DcmDataDictionary: " << fileName << ": " + << "too many fields (line " << lineNumber << "): "); + errorOnThisLine = OFTrue; + break; + case 5: + stripWhitespace(lineFields[4]); + standardVersion = lineFields[4]; + /* drop through to next case label */ + case 4: + /* the VM field is present */ + if (!parseVMField(lineFields[3], vmMin, vmMax)) { + DCMDATA_ERROR("DcmDataDictionary: " << fileName << ": " + << "bad VM field (line " << lineNumber << "): " << lineFields[3]); + errorOnThisLine = OFTrue; + } + /* drop through to next case label */ + case 3: + if (!parseWholeTagField(lineFields[0], key, upperKey, + groupRestriction, elementRestriction, privCreator)) + { + DCMDATA_ERROR("DcmDataDictionary: " << fileName << ": " + << "bad Tag field (line " << lineNumber << "): " << lineFields[0]); + errorOnThisLine = OFTrue; + } else { + /* all is OK */ + vrName = lineFields[1]; + stripWhitespace(vrName); + + tagName = lineFields[2]; + stripWhitespace(tagName); + } + } + + if (!errorOnThisLine) { + /* check the VR Field */ + vr.setVR(vrName); + if (vr.getEVR() == EVR_UNKNOWN) { + DCMDATA_ERROR("DcmDataDictionary: " << fileName << ": " + << "bad VR field (line " << lineNumber << "): " << vrName); + errorOnThisLine = OFTrue; + } + } + + if (!errorOnThisLine) { + e = new DcmDictEntry( + key.getGroup(), key.getElement(), + upperKey.getGroup(), upperKey.getElement(), + vr, tagName, vmMin, vmMax, standardVersion, OFTrue, + privCreator); + + e->setGroupRangeRestriction(groupRestriction); + e->setElementRangeRestriction(elementRestriction); + addEntry(e); + } + + for (i = 0; i < fieldsPresent; i++) { + free(lineFields[i]); + lineFields[i] = NULL; + } + + delete[] privCreator; + + if (errorOnThisLine) { + errorsEncountered++; + } + } + + fclose(f); + + /* return OFFalse in case of errors and set internal state accordingly */ + if (errorsEncountered == 0) { + dictionaryLoaded = OFTrue; + return OFTrue; + } + else { + dictionaryLoaded = OFFalse; + return OFFalse; + } +} + +#ifndef HAVE_GETENV + +static +char* getenv() { + return NULL; +} + +#endif /* !HAVE_GETENV */ + + + +OFBool +DcmDataDictionary::loadExternalDictionaries() +{ + const char* env = NULL; + size_t len; + int sepCnt = 0; + OFBool msgIfDictAbsent = OFTrue; + OFBool loadFailed = OFFalse; + + env = getenv(DCM_DICT_ENVIRONMENT_VARIABLE); + if ((env == NULL) || (strlen(env) == 0)) { + env = DCM_DICT_DEFAULT_PATH; + msgIfDictAbsent = OFFalse; + } + + if ((env != NULL) && (strlen(env) != 0)) { + len = strlen(env); + for (size_t i = 0; i < len; ++i) { + if (env[i] == ENVIRONMENT_PATH_SEPARATOR) { + sepCnt++; + } + } + + if (sepCnt == 0) { + if (!loadDictionary(env, msgIfDictAbsent)) { + return OFFalse; + } + } else { + char** dictArray; + + dictArray = OFstatic_cast(char **, malloc((sepCnt + 1) * sizeof(char*))); + + int ndicts = splitFields(env, dictArray, sepCnt + 1, + ENVIRONMENT_PATH_SEPARATOR); + + for (int ii = 0; ii < ndicts; ii++) { + if ((dictArray[ii] != NULL) && (strlen(dictArray[ii]) > 0)) { + if (!loadDictionary(dictArray[ii], msgIfDictAbsent)) { + loadFailed = OFTrue; + } + } + free(dictArray[ii]); + } + free(dictArray); + } + } + + return (loadFailed) ? (OFFalse) : (OFTrue); +} + + +void +DcmDataDictionary::addEntry(DcmDictEntry* e) +{ + if (e->isRepeating()) { + /* + * Find the best position in repeating tag list + * Existing entries are replaced if the ranges and repetition + * constraints are the same. + * If a range represents a subset of an existing range then it + * will be placed before it in the list. This ensures that a + * search will find the subset rather than the superset. + * Otherwise entries are appended to the end of the list. + */ + OFBool inserted = OFFalse; + + DcmDictEntryListIterator iter(repDict.begin()); + DcmDictEntryListIterator last(repDict.end()); + for (; !inserted && iter != last; ++iter) { + if (e->setEQ(**iter)) { + /* replace the old entry with the new */ + DcmDictEntry *old = *iter; + *iter = e; +#ifdef PRINT_REPLACED_DICTIONARY_ENTRIES + DCMDATA_WARN("replacing " << *old); +#endif + delete old; + inserted = OFTrue; + } else if (e->subset(**iter)) { + /* e is a subset of the current list position, insert before */ + repDict.insert(iter, e); + inserted = OFTrue; + } + } + if (!inserted) { + /* insert at end */ + repDict.push_back(e); + inserted = OFTrue; + } + } else { + hashDict.put(e); + } +} + +void +DcmDataDictionary::deleteEntry(const DcmDictEntry& entry) +{ + DcmDictEntry* e = NULL; + e = OFconst_cast(DcmDictEntry *, findEntry(entry)); + if (e != NULL) { + if (e->isRepeating()) { + repDict.remove(e); + delete e; + } else { + hashDict.del(entry.getKey(), entry.getPrivateCreator()); + } + } +} + +const DcmDictEntry* +DcmDataDictionary::findEntry(const DcmDictEntry& entry) const +{ + const DcmDictEntry* e = NULL; + + if (entry.isRepeating()) { + OFBool found = OFFalse; + DcmDictEntryListConstIterator iter(repDict.begin()); + DcmDictEntryListConstIterator last(repDict.end()); + for (; !found && iter != last; ++iter) { + if (entry.setEQ(**iter)) { + found = OFTrue; + e = *iter; + } + } + } else { + e = hashDict.get(entry, entry.getPrivateCreator()); + } + return e; +} + +const DcmDictEntry* +DcmDataDictionary::findEntry(const DcmTagKey& key, const char *privCreator) const +{ + /* search first in the normal tags dictionary and if not found + * then search in the repeating tags list. + */ + const DcmDictEntry* e = NULL; + + e = hashDict.get(key, privCreator); + if (e == NULL) { + /* search in the repeating tags dictionary */ + OFBool found = OFFalse; + DcmDictEntryListConstIterator iter(repDict.begin()); + DcmDictEntryListConstIterator last(repDict.end()); + for (; !found && iter != last; ++iter) { + if ((*iter)->contains(key, privCreator)) { + found = OFTrue; + e = *iter; + } + } + } + return e; +} + +const DcmDictEntry* +DcmDataDictionary::findEntry(const char *name) const +{ + const DcmDictEntry* e = NULL; + const DcmDictEntry* ePrivate = NULL; + + /* search first in the normal tags dictionary and if not found + * then search in the repeating tags list. + */ + DcmHashDictIterator iter; + for (iter = hashDict.begin(); (e == NULL) && (iter != hashDict.end()); ++iter) { + if ((*iter)->contains(name)) { + e = *iter; + if (e->getGroup() % 2) + { + /* tag is a private tag - continue search to be sure to find non-private keys first */ + if (!ePrivate) ePrivate = e; + e = NULL; + } + } + } + + if (e == NULL) { + /* search in the repeating tags dictionary */ + OFBool found = OFFalse; + DcmDictEntryListConstIterator iter2(repDict.begin()); + DcmDictEntryListConstIterator last(repDict.end()); + for (; !found && iter2 != last; ++iter2) { + if ((*iter2)->contains(name)) { + found = OFTrue; + e = *iter2; + } + } + } + + if (e == NULL && ePrivate != NULL) { + /* no standard key found - use the first private key found */ + e = ePrivate; + } + + return e; +} + + +/* ================================================================== */ + + +GlobalDcmDataDictionary::GlobalDcmDataDictionary() + : dataDict(NULL) +#ifdef WITH_THREADS + , dataDictLock() +#endif +{ +} + +GlobalDcmDataDictionary::~GlobalDcmDataDictionary() +{ + /* No threads may be active any more, so no locking needed */ + delete dataDict; +} + +void GlobalDcmDataDictionary::createDataDict() +{ + /* Make sure only one thread tries to initialize the dictionary */ +#ifdef WITH_THREADS + dataDictLock.wrlock(); +#endif +#ifdef DONT_LOAD_EXTERNAL_DICTIONARIES + const OFBool loadExternal = OFFalse; +#else + const OFBool loadExternal = OFTrue; +#endif + /* Make sure no other thread managed to create the dictionary + * before we got our write lock. */ + if (!dataDict) + dataDict = new DcmDataDictionary(OFTrue /*loadBuiltin*/, loadExternal); +#ifdef WITH_THREADS + dataDictLock.unlock(); +#endif +} + +const DcmDataDictionary& GlobalDcmDataDictionary::rdlock() +{ +#ifdef WITH_THREADS + dataDictLock.rdlock(); +#endif + if (!dataDict) + { + /* dataDictLock must not be locked during createDataDict() */ +#ifdef WITH_THREADS + dataDictLock.unlock(); +#endif + createDataDict(); +#ifdef WITH_THREADS + dataDictLock.rdlock(); +#endif + } + return *dataDict; +} + +DcmDataDictionary& GlobalDcmDataDictionary::wrlock() +{ +#ifdef WITH_THREADS + dataDictLock.wrlock(); +#endif + if (!dataDict) + { + /* dataDictLock must not be locked during createDataDict() */ +#ifdef WITH_THREADS + dataDictLock.unlock(); +#endif + createDataDict(); +#ifdef WITH_THREADS + dataDictLock.wrlock(); +#endif + } + return *dataDict; +} + +void GlobalDcmDataDictionary::unlock() +{ +#ifdef WITH_THREADS + dataDictLock.unlock(); +#endif +} + +OFBool GlobalDcmDataDictionary::isDictionaryLoaded() +{ + OFBool result = rdlock().isDictionaryLoaded(); + unlock(); + return result; +} + +void GlobalDcmDataDictionary::clear() +{ + wrlock().clear(); + unlock(); +} + + + + +// Function by the Orthanc project to load a dictionary from a memory +// buffer, which is necessary in sandboxed environments. This is an +// adapted version of DcmDataDictionary::loadDictionary(). + + +#include <boost/noncopyable.hpp> + +struct OrthancLinesIterator; + +// This plain old C class is implemented in "../../Core/Toolbox.h" +OrthancLinesIterator* OrthancLinesIterator_Create(const std::string& content); + +bool OrthancLinesIterator_GetLine(std::string& target, + const OrthancLinesIterator* iterator); + +void OrthancLinesIterator_Next(OrthancLinesIterator* iterator); + +void OrthancLinesIterator_Free(OrthancLinesIterator* iterator); + + +class LinesIterator : public boost::noncopyable +{ +private: + OrthancLinesIterator* iterator_; + +public: + LinesIterator(const std::string& content) : + iterator_(NULL) + { + iterator_ = OrthancLinesIterator_Create(content); + } + + ~LinesIterator() + { + if (iterator_ != NULL) + { + OrthancLinesIterator_Free(iterator_); + iterator_ = NULL; + } + } + + bool GetLine(std::string& target) const + { + if (iterator_ != NULL) + { + return OrthancLinesIterator_GetLine(target, iterator_); + } + else + { + return false; + } + } + + void Next() + { + if (iterator_ != NULL) + { + OrthancLinesIterator_Next(iterator_); + } + } +}; + + + +OFBool +DcmDataDictionary::loadFromMemory(const std::string& content, OFBool errorIfAbsent) +{ + int lineNumber = 0; + char* lineFields[DCM_MAXDICTFIELDS + 1]; + int fieldsPresent; + DcmDictEntry* e; + int errorsEncountered = 0; + OFBool errorOnThisLine = OFFalse; + int i; + + DcmTagKey key, upperKey; + DcmDictRangeRestriction groupRestriction = DcmDictRange_Unspecified; + DcmDictRangeRestriction elementRestriction = DcmDictRange_Unspecified; + DcmVR vr; + char* vrName; + char* tagName; + char* privCreator; + int vmMin, vmMax = 1; + const char* standardVersion; + + LinesIterator iterator(content); + + std::string line; + while (iterator.GetLine(line)) { + iterator.Next(); + + if (line.size() >= DCM_MAXDICTLINESIZE) { + DCMDATA_ERROR("DcmDataDictionary: Too long line: " << line); + continue; + } + + lineNumber++; + + if (onlyWhitespace(line.c_str())) { + continue; /* ignore this line */ + } + if (isaCommentLine(line.c_str())) { + continue; /* ignore this line */ + } + + errorOnThisLine = OFFalse; + + /* fields are tab separated */ + fieldsPresent = splitFields(line.c_str(), lineFields, + DCM_MAXDICTFIELDS, + DCM_DICT_FIELD_SEPARATOR_CHAR); + + /* initialize dict entry fields */ + vrName = NULL; + tagName = NULL; + privCreator = NULL; + vmMin = vmMax = 1; + standardVersion = "DICOM"; + + switch (fieldsPresent) { + case 0: + case 1: + case 2: + DCMDATA_ERROR("DcmDataDictionary: " + << "too few fields (line " << lineNumber << ")"); + errorOnThisLine = OFTrue; + break; + default: + DCMDATA_ERROR("DcmDataDictionary: " + << "too many fields (line " << lineNumber << "): "); + errorOnThisLine = OFTrue; + break; + case 5: + stripWhitespace(lineFields[4]); + standardVersion = lineFields[4]; + /* drop through to next case label */ + case 4: + /* the VM field is present */ + if (!parseVMField(lineFields[3], vmMin, vmMax)) { + DCMDATA_ERROR("DcmDataDictionary: " + << "bad VM field (line " << lineNumber << "): " << lineFields[3]); + errorOnThisLine = OFTrue; + } + /* drop through to next case label */ + case 3: + if (!parseWholeTagField(lineFields[0], key, upperKey, + groupRestriction, elementRestriction, privCreator)) + { + DCMDATA_ERROR("DcmDataDictionary: " + << "bad Tag field (line " << lineNumber << "): " << lineFields[0]); + errorOnThisLine = OFTrue; + } else { + /* all is OK */ + vrName = lineFields[1]; + stripWhitespace(vrName); + + tagName = lineFields[2]; + stripWhitespace(tagName); + } + } + + if (!errorOnThisLine) { + /* check the VR Field */ + vr.setVR(vrName); + if (vr.getEVR() == EVR_UNKNOWN) { + DCMDATA_ERROR("DcmDataDictionary: " + << "bad VR field (line " << lineNumber << "): " << vrName); + errorOnThisLine = OFTrue; + } + } + + if (!errorOnThisLine) { + e = new DcmDictEntry( + key.getGroup(), key.getElement(), + upperKey.getGroup(), upperKey.getElement(), + vr, tagName, vmMin, vmMax, standardVersion, OFTrue, + privCreator); + + e->setGroupRangeRestriction(groupRestriction); + e->setElementRangeRestriction(elementRestriction); + addEntry(e); + } + + for (i = 0; i < fieldsPresent; i++) { + free(lineFields[i]); + lineFields[i] = NULL; + } + + delete[] privCreator; + + if (errorOnThisLine) { + errorsEncountered++; + } + } + + /* return OFFalse in case of errors and set internal state accordingly */ + if (errorsEncountered == 0) { + dictionaryLoaded = OFTrue; + return OFTrue; + } + else { + dictionaryLoaded = OFFalse; + return OFFalse; + } +}