1 /*
2  *
3  *  Copyright (C) 1993-2019, 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:  dcmqrdb
15  *
16  *  Author:  Marco Eichelberg
17  *
18  *  Purpose: classes DcmQueryRetrieveIndexDatabaseHandle,
19  *                   DcmQueryRetrieveIndexDatabaseHandleFactory
20  *
21  */
22 
23 #include "dcmtk/config/osconfig.h"    /* make sure OS specific configuration is included first */
24 
25 BEGIN_EXTERN_C
26 #ifdef HAVE_SYS_STAT_H
27 #include <sys/stat.h>
28 #endif
29 #ifdef HAVE_FCNTL_H
30 #include <fcntl.h>
31 #endif
32 #ifdef HAVE_SYS_PARAM_H
33 #include <sys/param.h>
34 #endif
35 END_EXTERN_C
36 
37 #define INCLUDE_CCTYPE
38 #define INCLUDE_CSTDARG
39 #include "dcmtk/ofstd/ofstdinc.h"
40 #include "dcmtk/ofstd/ofstd.h"
41 
42 #include "dcmtk/dcmqrdb/dcmqrdbs.h"
43 #include "dcmtk/dcmqrdb/dcmqrdbi.h"
44 #include "dcmtk/dcmqrdb/dcmqrcnf.h"
45 #include "dcmtk/dcmqrdb/dcmqropt.h"
46 
47 #include "dcmtk/dcmqrdb/dcmqridx.h"
48 #include "dcmtk/dcmnet/diutil.h"
49 #include "dcmtk/dcmdata/dcfilefo.h"
50 #include "dcmtk/dcmdata/dcmatch.h"
51 
52 /* ========================= static data ========================= */
53 
54 /**** The TbFindAttr table contains the description of tags (keys) supported
55  **** by the DB Module.
56  **** Tags described here have to be present in the Index Record file.
57  **** The order is insignificant.
58  ****
59  **** Each element of this table is described by
60  ****           The tag value
61  ****           The level of this tag (from patient to image)
62  ****           The Key Type (only UNIQUE_KEY values is used)
63  ****
64  **** This table and the IndexRecord structure should contain at least
65  **** all Unique and Required keys.
66  ***/
67 
68 static const DB_FindAttr TbFindAttr [] = {
69         DB_FindAttr( DCM_PatientBirthDate,                      PATIENT_LEVEL,  OPTIONAL_KEY ),
70         DB_FindAttr( DCM_PatientSex,                            PATIENT_LEVEL,  OPTIONAL_KEY ),
71         DB_FindAttr( DCM_PatientName,                           PATIENT_LEVEL,  REQUIRED_KEY ),
72         DB_FindAttr( DCM_PatientID,                             PATIENT_LEVEL,  UNIQUE_KEY   ),
73         DB_FindAttr( DCM_PatientBirthTime,                      PATIENT_LEVEL,  OPTIONAL_KEY ),
74         DB_FindAttr( DCM_RETIRED_OtherPatientIDs,               PATIENT_LEVEL,  OPTIONAL_KEY ),
75         DB_FindAttr( DCM_OtherPatientNames,                     PATIENT_LEVEL,  OPTIONAL_KEY ),
76         DB_FindAttr( DCM_EthnicGroup,                           PATIENT_LEVEL,  OPTIONAL_KEY ),
77         DB_FindAttr( DCM_PatientComments,                       PATIENT_LEVEL,  OPTIONAL_KEY ),
78         DB_FindAttr( DCM_IssuerOfPatientID,                     PATIENT_LEVEL,  OPTIONAL_KEY ),
79         DB_FindAttr( DCM_StudyDate,                             STUDY_LEVEL,    REQUIRED_KEY ),
80         DB_FindAttr( DCM_StudyTime,                             STUDY_LEVEL,    REQUIRED_KEY ),
81         DB_FindAttr( DCM_StudyID,                               STUDY_LEVEL,    REQUIRED_KEY ),
82         DB_FindAttr( DCM_AccessionNumber,                       STUDY_LEVEL,    REQUIRED_KEY ),
83         DB_FindAttr( DCM_ReferringPhysicianName,                STUDY_LEVEL,    OPTIONAL_KEY ),
84         DB_FindAttr( DCM_StudyDescription,                      STUDY_LEVEL,    OPTIONAL_KEY ),
85         DB_FindAttr( DCM_NameOfPhysiciansReadingStudy,          STUDY_LEVEL,    OPTIONAL_KEY ),
86         DB_FindAttr( DCM_StudyInstanceUID,                      STUDY_LEVEL,    UNIQUE_KEY   ),
87         DB_FindAttr( DCM_RETIRED_OtherStudyNumbers,             STUDY_LEVEL,    OPTIONAL_KEY ),
88         DB_FindAttr( DCM_AdmittingDiagnosesDescription,         STUDY_LEVEL,    OPTIONAL_KEY ),
89         DB_FindAttr( DCM_PatientAge,                            STUDY_LEVEL,    OPTIONAL_KEY ),
90         DB_FindAttr( DCM_PatientSize,                           STUDY_LEVEL,    OPTIONAL_KEY ),
91         DB_FindAttr( DCM_PatientWeight,                         STUDY_LEVEL,    OPTIONAL_KEY ),
92         DB_FindAttr( DCM_Occupation,                            STUDY_LEVEL,    OPTIONAL_KEY ),
93         DB_FindAttr( DCM_AdditionalPatientHistory,              STUDY_LEVEL,    OPTIONAL_KEY ),
94         DB_FindAttr( DCM_SeriesNumber,                          SERIE_LEVEL,    REQUIRED_KEY ),
95         DB_FindAttr( DCM_SeriesInstanceUID,                     SERIE_LEVEL,    UNIQUE_KEY   ),
96         DB_FindAttr( DCM_Modality,                              SERIE_LEVEL,    OPTIONAL_KEY ),
97         DB_FindAttr( DCM_InstanceNumber,                        IMAGE_LEVEL,    REQUIRED_KEY ),
98         DB_FindAttr( DCM_SOPInstanceUID,                        IMAGE_LEVEL,    UNIQUE_KEY   )
99   };
100 
101 /**** The NbFindAttr variable contains the length of the TbFindAttr table
102  ***/
103 
104 static int NbFindAttr = ((sizeof (TbFindAttr)) / (sizeof (TbFindAttr [0])));
105 
106 /* ========================= static functions ========================= */
107 
DB_strdup(const char * str)108 static char *DB_strdup(const char* str)
109 {
110     if (str == NULL) return NULL;
111     size_t buflen = strlen(str)+1;
112     char* s = (char*)malloc(buflen);
113     OFStandard::strlcpy(s, str, buflen);
114     return s;
115 }
116 
117 /************
118 **      Add UID in Index Record to the UID found list
119  */
120 
DB_UIDAddFound(DB_Private_Handle * phandle,IdxRecord * idxRec)121 static void DB_UIDAddFound (
122                 DB_Private_Handle       *phandle,
123                 IdxRecord               *idxRec
124                 )
125 {
126     DB_UidList *plist ;
127 
128     plist = (DB_UidList *) malloc (sizeof (DB_UidList)) ;
129     if (plist == NULL) {
130         DCMQRDB_ERROR("DB_UIDAddFound: out of memory");
131         return;
132     }
133     plist->next = phandle->uidList ;
134     plist->patient = NULL ;
135     plist->study = NULL ;
136     plist->serie = NULL ;
137     plist->image = NULL ;
138 
139     if ((int)phandle->queryLevel >= PATIENT_LEVEL)
140         plist->patient = DB_strdup ((char *) idxRec->PatientID) ;
141     if ((int)phandle->queryLevel >= STUDY_LEVEL)
142         plist->study = DB_strdup ((char *) idxRec->StudyInstanceUID) ;
143     if ((int)phandle->queryLevel >= SERIE_LEVEL)
144         plist->serie = DB_strdup ((char *) idxRec->SeriesInstanceUID) ;
145     if ((int)phandle->queryLevel >= IMAGE_LEVEL)
146         plist->image = DB_strdup ((char *) idxRec->SOPInstanceUID) ;
147 
148     phandle->uidList = plist ;
149 }
150 
151 
152 /************
153 **      Search if an Index Record has already been found
154  */
155 
DB_UIDAlreadyFound(DB_Private_Handle * phandle,IdxRecord * idxRec)156 static int DB_UIDAlreadyFound (
157                 DB_Private_Handle       *phandle,
158                 IdxRecord               *idxRec
159                 )
160 {
161     DB_UidList *plist ;
162 
163     for (plist = phandle->uidList ; plist ; plist = plist->next) {
164         if (  ((int)phandle->queryLevel >= PATIENT_LEVEL)
165               && (strcmp (plist->patient, (char *) idxRec->PatientID) != 0)
166             )
167             continue ;
168         if (  ((int)phandle->queryLevel >= STUDY_LEVEL)
169               && (strcmp (plist->study, (char *) idxRec->StudyInstanceUID) != 0)
170             )
171             continue ;
172         if (  ((int)phandle->queryLevel >= SERIE_LEVEL)
173               && (strcmp (plist->serie, (char *) idxRec->SeriesInstanceUID) != 0)
174             )
175             continue ;
176         if (  ((int)phandle->queryLevel >= IMAGE_LEVEL)
177               && (strcmp (plist->image, (char *) idxRec->SOPInstanceUID) != 0)
178             )
179             continue ;
180         return (OFTrue) ;
181     }
182     return (OFFalse) ;
183 }
184 
185 /************
186  *      Initializes addresses in an IdxRecord
187  */
188 
DB_IdxInitRecord(IdxRecord * idx,int linksOnly)189 static void DB_IdxInitRecord (IdxRecord *idx, int linksOnly)
190 {
191     if (! linksOnly)
192     {
193         idx -> param[RECORDIDX_PatientBirthDate]. XTag = DCM_PatientBirthDate ;
194         idx -> param[RECORDIDX_PatientBirthDate]. ValueLength = DA_MAX_LENGTH ;
195         idx -> PatientBirthDate[0] = '\0' ;
196         idx -> param[RECORDIDX_PatientSex]. XTag = DCM_PatientSex ;
197         idx -> param[RECORDIDX_PatientSex]. ValueLength = CS_MAX_LENGTH ;
198         idx -> PatientSex[0] = '\0' ;
199         idx -> param[RECORDIDX_PatientName]. XTag = DCM_PatientName ;
200         idx -> param[RECORDIDX_PatientName]. ValueLength = PN_MAX_LENGTH ;
201         idx -> PatientName[0] = '\0' ;
202         idx -> param[RECORDIDX_PatientID]. XTag = DCM_PatientID ;
203         idx -> param[RECORDIDX_PatientID]. ValueLength = LO_MAX_LENGTH ;
204         idx -> PatientID[0] = '\0' ;
205         idx -> param[RECORDIDX_PatientBirthTime]. XTag = DCM_PatientBirthTime ;
206         idx -> param[RECORDIDX_PatientBirthTime]. ValueLength = TM_MAX_LENGTH ;
207         idx -> PatientBirthTime[0] = '\0' ;
208         idx -> param[RECORDIDX_OtherPatientIDs]. XTag = DCM_RETIRED_OtherPatientIDs ;
209         idx -> param[RECORDIDX_OtherPatientIDs]. ValueLength = LO_MAX_LENGTH ;
210         idx -> OtherPatientIDs[0] = '\0' ;
211         idx -> param[RECORDIDX_OtherPatientNames]. XTag = DCM_OtherPatientNames ;
212         idx -> param[RECORDIDX_OtherPatientNames]. ValueLength = PN_MAX_LENGTH ;
213         idx -> OtherPatientNames[0] = '\0' ;
214         idx -> param[RECORDIDX_EthnicGroup]. XTag = DCM_EthnicGroup ;
215         idx -> param[RECORDIDX_EthnicGroup]. ValueLength = SH_MAX_LENGTH ;
216         idx -> EthnicGroup[0] = '\0' ;
217         idx -> param[RECORDIDX_StudyDate]. XTag = DCM_StudyDate ;
218         idx -> param[RECORDIDX_StudyDate]. ValueLength = DA_MAX_LENGTH ;
219         idx -> StudyDate[0] = '\0' ;
220         idx -> param[RECORDIDX_StudyTime]. XTag = DCM_StudyTime ;
221         idx -> param[RECORDIDX_StudyTime]. ValueLength = TM_MAX_LENGTH ;
222         idx -> StudyTime[0] = '\0' ;
223         idx -> param[RECORDIDX_StudyID]. XTag = DCM_StudyID ;
224         idx -> param[RECORDIDX_StudyID]. ValueLength = CS_MAX_LENGTH ;
225         idx -> StudyID[0] = '\0' ;
226         idx -> param[RECORDIDX_StudyDescription]. XTag = DCM_StudyDescription ;
227         idx -> param[RECORDIDX_StudyDescription]. ValueLength = LO_MAX_LENGTH ;
228         idx -> StudyDescription[0] = '\0' ;
229         idx -> param[RECORDIDX_NameOfPhysiciansReadingStudy]. XTag = DCM_NameOfPhysiciansReadingStudy ;
230         idx -> param[RECORDIDX_NameOfPhysiciansReadingStudy]. ValueLength = PN_MAX_LENGTH ;
231         idx -> NameOfPhysiciansReadingStudy[0] = '\0' ;
232         idx -> param[RECORDIDX_AccessionNumber]. XTag = DCM_AccessionNumber ;
233         idx -> param[RECORDIDX_AccessionNumber]. ValueLength = CS_MAX_LENGTH ;
234         idx -> AccessionNumber[0] = '\0' ;
235         idx -> param[RECORDIDX_ReferringPhysicianName]. XTag = DCM_ReferringPhysicianName ;
236         idx -> param[RECORDIDX_ReferringPhysicianName]. ValueLength = PN_MAX_LENGTH ;
237         idx -> ReferringPhysicianName[0] = '\0' ;
238         idx -> param[RECORDIDX_ProcedureDescription]. XTag = DCM_StudyDescription ;
239         idx -> param[RECORDIDX_ProcedureDescription]. ValueLength = LO_MAX_LENGTH ;
240         idx -> ProcedureDescription[0] = '\0' ;
241         idx -> param[RECORDIDX_AttendingPhysiciansName]. XTag = DCM_NameOfPhysiciansReadingStudy ;
242         idx -> param[RECORDIDX_AttendingPhysiciansName]. ValueLength = PN_MAX_LENGTH ;
243         idx -> AttendingPhysiciansName[0] = '\0' ;
244         idx -> param[RECORDIDX_StudyInstanceUID]. XTag = DCM_StudyInstanceUID ;
245         idx -> param[RECORDIDX_StudyInstanceUID]. ValueLength = UI_MAX_LENGTH ;
246         idx -> StudyInstanceUID[0] = '\0' ;
247         idx -> param[RECORDIDX_OtherStudyNumbers]. XTag = DCM_RETIRED_OtherStudyNumbers ;
248         idx -> param[RECORDIDX_OtherStudyNumbers]. ValueLength = IS_MAX_LENGTH ;
249         idx -> OtherStudyNumbers[0] = '\0' ;
250         idx -> param[RECORDIDX_AdmittingDiagnosesDescription]. XTag = DCM_AdmittingDiagnosesDescription ;
251         idx -> param[RECORDIDX_AdmittingDiagnosesDescription]. ValueLength = LO_MAX_LENGTH ;
252         idx -> AdmittingDiagnosesDescription[0] = '\0' ;
253         idx -> param[RECORDIDX_PatientAge]. XTag = DCM_PatientAge ;
254         idx -> param[RECORDIDX_PatientAge]. ValueLength = AS_MAX_LENGTH ;
255         idx -> PatientAge[0] = '\0' ;
256         idx -> param[RECORDIDX_PatientSize]. XTag = DCM_PatientSize ;
257         idx -> param[RECORDIDX_PatientSize]. ValueLength = DS_MAX_LENGTH ;
258         idx -> PatientSize[0] = '\0' ;
259         idx -> param[RECORDIDX_PatientWeight]. XTag = DCM_PatientWeight ;
260         idx -> param[RECORDIDX_PatientWeight]. ValueLength = DS_MAX_LENGTH ;
261         idx -> PatientWeight[0] = '\0' ;
262         idx -> param[RECORDIDX_Occupation]. XTag = DCM_Occupation ;
263         idx -> param[RECORDIDX_Occupation]. ValueLength = SH_MAX_LENGTH ;
264         idx -> Occupation[0] = '\0' ;
265         idx -> param[RECORDIDX_SeriesNumber]. XTag = DCM_SeriesNumber ;
266         idx -> param[RECORDIDX_SeriesNumber]. ValueLength = IS_MAX_LENGTH ;
267         idx -> SeriesNumber[0] = '\0' ;
268         idx -> param[RECORDIDX_SeriesInstanceUID]. XTag = DCM_SeriesInstanceUID ;
269         idx -> param[RECORDIDX_SeriesInstanceUID]. ValueLength = UI_MAX_LENGTH ;
270         idx -> SeriesInstanceUID[0] = '\0' ;
271         idx -> param[RECORDIDX_Modality]. XTag = DCM_Modality ;
272         idx -> param[RECORDIDX_Modality]. ValueLength = CS_MAX_LENGTH ;
273         idx -> ImageNumber[0] = '\0' ;
274         idx -> param[RECORDIDX_ImageNumber]. XTag = DCM_InstanceNumber ;
275         idx -> param[RECORDIDX_ImageNumber]. ValueLength = IS_MAX_LENGTH ;
276         idx -> ImageNumber[0] = '\0' ;
277         idx -> param[RECORDIDX_SOPInstanceUID]. XTag = DCM_SOPInstanceUID ;
278         idx -> param[RECORDIDX_SOPInstanceUID]. ValueLength = UI_MAX_LENGTH ;
279         idx -> SOPInstanceUID[0] = '\0' ;
280         idx -> param[RECORDIDX_SeriesDate]. XTag = DCM_SeriesDate ;
281         idx -> param[RECORDIDX_SeriesDate]. ValueLength = DA_MAX_LENGTH ;
282         idx -> SeriesDate[0] = '\0' ;
283         idx -> param[RECORDIDX_SeriesTime]. XTag = DCM_SeriesTime ;
284         idx -> param[RECORDIDX_SeriesTime]. ValueLength = TM_MAX_LENGTH ;
285         idx -> SeriesTime[0] = '\0' ;
286         idx -> param[RECORDIDX_SeriesDescription]. XTag = DCM_SeriesDescription ;
287         idx -> param[RECORDIDX_SeriesDescription]. ValueLength = LO_MAX_LENGTH ;
288         idx -> SeriesDescription[0] = '\0' ;
289         idx -> param[RECORDIDX_ProtocolName]. XTag = DCM_ProtocolName ;
290         idx -> param[RECORDIDX_ProtocolName]. ValueLength = LO_MAX_LENGTH ;
291         idx -> ProtocolName[0] = '\0' ;
292         idx -> param[RECORDIDX_OperatorsName ]. XTag = DCM_OperatorsName ;
293         idx -> param[RECORDIDX_OperatorsName ]. ValueLength = PN_MAX_LENGTH ;
294         idx -> OperatorsName[0] = '\0' ;
295         idx -> param[RECORDIDX_PerformingPhysicianName]. XTag = DCM_PerformingPhysicianName ;
296         idx -> param[RECORDIDX_PerformingPhysicianName]. ValueLength = PN_MAX_LENGTH ;
297         idx -> PerformingPhysicianName[0] = '\0' ;
298         idx -> param[RECORDIDX_PresentationLabel]. XTag = DCM_ContentLabel ;
299         idx -> param[RECORDIDX_PresentationLabel]. ValueLength = CS_LABEL_MAX_LENGTH ;
300         idx -> PresentationLabel[0] = '\0' ;
301         idx -> param[RECORDIDX_IssuerOfPatientID]. XTag = DCM_IssuerOfPatientID ;
302         idx -> param[RECORDIDX_IssuerOfPatientID]. ValueLength =  LO_MAX_LENGTH ;
303         idx -> IssuerOfPatientID[0] = '\0' ;
304         idx -> param[RECORDIDX_SpecificCharacterSet]. XTag = DCM_SpecificCharacterSet ;
305         idx -> param[RECORDIDX_SpecificCharacterSet]. ValueLength = CS_MAX_LENGTH*8 ;
306         idx -> SpecificCharacterSet[0] = '\0' ;
307     }
308     idx -> param[RECORDIDX_PatientBirthDate]. PValueField = (char *)idx -> PatientBirthDate ;
309     idx -> param[RECORDIDX_PatientSex]. PValueField = (char *)idx -> PatientSex ;
310     idx -> param[RECORDIDX_PatientName]. PValueField = (char *)idx -> PatientName ;
311     idx -> param[RECORDIDX_PatientID]. PValueField = (char *)idx -> PatientID ;
312     idx -> param[RECORDIDX_PatientBirthTime]. PValueField = (char *)idx -> PatientBirthTime ;
313     idx -> param[RECORDIDX_OtherPatientIDs]. PValueField = (char *)idx -> OtherPatientIDs ;
314     idx -> param[RECORDIDX_OtherPatientNames]. PValueField = (char *)idx -> OtherPatientNames ;
315     idx -> param[RECORDIDX_EthnicGroup]. PValueField = (char *)idx -> EthnicGroup ;
316     idx -> param[RECORDIDX_StudyDate]. PValueField = (char *) idx -> StudyDate ;
317     idx -> param[RECORDIDX_StudyTime]. PValueField = (char *) idx -> StudyTime ;
318     idx -> param[RECORDIDX_StudyID]. PValueField = (char *) idx -> StudyID ;
319     idx -> param[RECORDIDX_StudyDescription]. PValueField = (char *) idx -> StudyDescription ;
320     idx -> param[RECORDIDX_NameOfPhysiciansReadingStudy]. PValueField = (char *) idx ->NameOfPhysiciansReadingStudy;
321     idx -> param[RECORDIDX_AccessionNumber]. PValueField = (char *) idx -> AccessionNumber ;
322     idx -> param[RECORDIDX_ReferringPhysicianName]. PValueField = (char *) idx -> ReferringPhysicianName ;
323     idx -> param[RECORDIDX_ProcedureDescription]. PValueField = (char *) idx -> ProcedureDescription ;
324     idx -> param[RECORDIDX_AttendingPhysiciansName]. PValueField = (char *) idx -> AttendingPhysiciansName ;
325     idx -> param[RECORDIDX_StudyInstanceUID]. PValueField = (char *) idx -> StudyInstanceUID ;
326     idx -> param[RECORDIDX_OtherStudyNumbers]. PValueField = (char *) idx -> OtherStudyNumbers ;
327     idx -> param[RECORDIDX_AdmittingDiagnosesDescription]. PValueField = (char *) idx -> AdmittingDiagnosesDescription ;
328     idx -> param[RECORDIDX_PatientAge]. PValueField = (char *) idx -> PatientAge ;
329     idx -> param[RECORDIDX_PatientSize]. PValueField = (char *) idx -> PatientSize ;
330     idx -> param[RECORDIDX_PatientWeight]. PValueField = (char *) idx -> PatientWeight ;
331     idx -> param[RECORDIDX_Occupation]. PValueField = (char *) idx -> Occupation ;
332     idx -> param[RECORDIDX_SeriesNumber]. PValueField = (char *) idx -> SeriesNumber ;
333     idx -> param[RECORDIDX_SeriesInstanceUID]. PValueField = (char *) idx -> SeriesInstanceUID ;
334     idx -> param[RECORDIDX_Modality]. PValueField = (char *) idx -> Modality ;
335     idx -> param[RECORDIDX_ImageNumber]. PValueField = (char *) idx -> ImageNumber ;
336     idx -> param[RECORDIDX_SOPInstanceUID]. PValueField = (char *) idx -> SOPInstanceUID ;
337     idx -> param[RECORDIDX_SeriesDate]. PValueField = (char *) idx -> SeriesDate ;
338     idx -> param[RECORDIDX_SeriesTime]. PValueField = (char *) idx -> SeriesTime ;
339     idx -> param[RECORDIDX_SeriesDescription]. PValueField = (char *) idx -> SeriesDescription ;
340     idx -> param[RECORDIDX_ProtocolName]. PValueField = (char *) idx -> ProtocolName ;
341     idx -> param[RECORDIDX_OperatorsName ]. PValueField = (char *) idx -> OperatorsName ;
342     idx -> param[RECORDIDX_PerformingPhysicianName]. PValueField = (char *) idx -> PerformingPhysicianName ;
343     idx -> param[RECORDIDX_PresentationLabel]. PValueField = (char *) idx -> PresentationLabel ;
344     idx -> param[RECORDIDX_IssuerOfPatientID]. PValueField = (char *) idx -> IssuerOfPatientID ;
345     idx -> param[RECORDIDX_SpecificCharacterSet]. PValueField = (char *) idx -> SpecificCharacterSet ;
346 }
347 
348 /******************************
349  *      Seek to a file position and do error checking
350  *
351  * Motivation:
352  * We have had situations during demonstrations where size of the DB index file
353  * has exploded.  It seems that a record is being written to a position
354  * way past the end of file.
355  * This seek function does some sanity error checking to try to identify
356  * the problem.
357  */
DB_lseek(int fildes,long offset,int whence)358 static long DB_lseek(int fildes, long offset, int whence)
359 {
360     long pos;
361     long curpos;
362     long endpos;
363 
364     /*
365     ** we should not be seeking to an offset < 0
366     */
367     if (offset < 0) {
368         DCMQRDB_ERROR("*** DB ALERT: attempt to seek before beginning of file");
369     }
370 
371     /* get the current position */
372     curpos = lseek(fildes, 0, SEEK_CUR);
373     if (curpos < 0) {
374         DCMQRDB_ERROR("DB_lseek: cannot get current position: " << OFStandard::getLastSystemErrorCode().message());
375         return curpos;
376     }
377     /* get the end of file position */
378     endpos = lseek(fildes, 0, SEEK_END);
379     if (endpos < 0) {
380         DCMQRDB_ERROR("DB_lseek: cannot get end of file position: " << OFStandard::getLastSystemErrorCode().message());
381         return endpos;
382     }
383 
384     /* return to current position */
385     curpos = lseek(fildes, curpos, SEEK_SET);
386     if (curpos < 0) {
387         DCMQRDB_ERROR("DB_lseek: cannot reset current position: " << OFStandard::getLastSystemErrorCode().message());
388         return curpos;
389     }
390 
391     /* do the requested seek */
392     pos = lseek(fildes, offset, whence);
393     if (pos < 0) {
394         DCMQRDB_ERROR("DB_lseek: cannot seek to " << offset << ": " << OFStandard::getLastSystemErrorCode().message());
395         return pos;
396     }
397 
398     /*
399     ** print an alert if we are seeking to far
400     ** what is the limit? We don't expect the index file to be
401     ** larger than 32Mb
402     */
403     const long maxFileSize = 33554432;
404     if (pos > maxFileSize) {
405         DCMQRDB_ERROR("*** DB ALERT: attempt to seek beyond " << maxFileSize << " bytes");
406     }
407 
408     /* print an alert if we are seeking beyond the end of file.
409      * ignore when file is empty or contains only the version information.
410      */
411     if ((endpos > DBHEADERSIZE) && (pos > endpos)) {
412         DCMQRDB_ERROR("*** DB ALERT: attempt to seek beyond end of file" << OFendl
413             << "              offset=" << offset << " filesize=" << endpos);
414     }
415 
416     return pos;
417 }
418 
419 /******************************
420  *      Read an Index record
421  */
422 
DB_IdxRead(int idx,IdxRecord * idxRec)423 OFCondition DcmQueryRetrieveIndexDatabaseHandle::DB_IdxRead (int idx, IdxRecord *idxRec)
424 {
425 
426     /*** Goto the right index in file
427     **/
428 
429     DB_lseek (handle_ -> pidx, OFstatic_cast(long, DBHEADERSIZE + SIZEOF_STUDYDESC + idx * SIZEOF_IDXRECORD), SEEK_SET) ;
430 
431     /*** Read the record
432     **/
433 
434     if (read (handle_ -> pidx, (char *) idxRec, SIZEOF_IDXRECORD) != SIZEOF_IDXRECORD)
435         return (QR_EC_IndexDatabaseError) ;
436 
437     DB_lseek (handle_ -> pidx, OFstatic_cast(long, DBHEADERSIZE), SEEK_SET) ;
438 
439     /*** Initialize record links
440     **/
441 
442     DB_IdxInitRecord (idxRec, 1) ;
443     return EC_Normal ;
444 }
445 
446 
447 /******************************
448  *      Add an Index record
449  *      Returns the index allocated for this record
450  */
451 
DB_IdxAdd(DB_Private_Handle * phandle,int * idx,IdxRecord * idxRec)452 static OFCondition DB_IdxAdd (DB_Private_Handle *phandle, int *idx, IdxRecord *idxRec)
453 {
454     IdxRecord   rec ;
455     OFCondition cond = EC_Normal;
456 
457     /*** Find free place for the record
458     *** A place is free if filename is empty
459     **/
460 
461     *idx = 0 ;
462 
463     DB_lseek (phandle -> pidx, OFstatic_cast(long, DBHEADERSIZE + SIZEOF_STUDYDESC), SEEK_SET) ;
464     while (read (phandle -> pidx, (char *) &rec, SIZEOF_IDXRECORD) == SIZEOF_IDXRECORD) {
465         if (rec. filename [0] == '\0')
466             break ;
467         (*idx)++ ;
468     }
469 
470     /*** We have either found a free place or we are at the end of file. **/
471 
472 
473     DB_lseek (phandle -> pidx, OFstatic_cast(long, DBHEADERSIZE + SIZEOF_STUDYDESC + (*idx) * SIZEOF_IDXRECORD), SEEK_SET) ;
474 
475     if (write (phandle -> pidx, (char *) idxRec, SIZEOF_IDXRECORD) != SIZEOF_IDXRECORD)
476         cond = QR_EC_IndexDatabaseError ;
477     else
478         cond = EC_Normal ;
479 
480     DB_lseek (phandle -> pidx, OFstatic_cast(long, DBHEADERSIZE), SEEK_SET) ;
481 
482     return cond ;
483 }
484 
485 
486 /******************************
487  *      Change the StudyDescRecord
488  */
489 
DB_StudyDescChange(StudyDescRecord * pStudyDesc)490 OFCondition DcmQueryRetrieveIndexDatabaseHandle::DB_StudyDescChange(StudyDescRecord *pStudyDesc)
491 {
492     OFCondition cond = EC_Normal;
493     DB_lseek (handle_ -> pidx, OFstatic_cast(long, DBHEADERSIZE), SEEK_SET) ;
494     if (write (handle_ -> pidx, (char *) pStudyDesc, SIZEOF_STUDYDESC) != SIZEOF_STUDYDESC)
495         cond = QR_EC_IndexDatabaseError;
496     DB_lseek (handle_ -> pidx, OFstatic_cast(long, DBHEADERSIZE), SEEK_SET) ;
497     return cond ;
498 }
499 
500 /******************************
501  *      Init an Index record loop
502  */
503 
DB_IdxInitLoop(int * idx)504 OFCondition DcmQueryRetrieveIndexDatabaseHandle::DB_IdxInitLoop(int *idx)
505 {
506     DB_lseek (handle_ -> pidx, OFstatic_cast(long, DBHEADERSIZE + SIZEOF_STUDYDESC), SEEK_SET) ;
507     *idx = -1 ;
508     return EC_Normal ;
509 }
510 
511 /******************************
512  *      Get next Index record
513  *      On return, idx is initialized with the index of the record read
514  */
515 
DB_IdxGetNext(int * idx,IdxRecord * idxRec)516 OFCondition DcmQueryRetrieveIndexDatabaseHandle::DB_IdxGetNext(int *idx, IdxRecord *idxRec)
517 {
518 
519     (*idx)++ ;
520     DB_lseek (handle_ -> pidx, OFstatic_cast(long, DBHEADERSIZE + SIZEOF_STUDYDESC + OFstatic_cast(long, *idx) * SIZEOF_IDXRECORD), SEEK_SET) ;
521     while (read (handle_ -> pidx, (char *) idxRec, SIZEOF_IDXRECORD) == SIZEOF_IDXRECORD) {
522         if (idxRec -> filename [0] != '\0') {
523             DB_IdxInitRecord (idxRec, 1) ;
524 
525             return EC_Normal ;
526         }
527         (*idx)++ ;
528     }
529 
530     DB_lseek (handle_ -> pidx, OFstatic_cast(long, DBHEADERSIZE), SEEK_SET) ;
531 
532     return QR_EC_IndexDatabaseError ;
533 }
534 
535 
536 /******************************
537  *      Get next Index record
538  *      On return, idx is initialized with the index of the record read
539  */
540 
DB_GetStudyDesc(StudyDescRecord * pStudyDesc)541 OFCondition DcmQueryRetrieveIndexDatabaseHandle::DB_GetStudyDesc (StudyDescRecord *pStudyDesc)
542 {
543 
544     DB_lseek (handle_ -> pidx, OFstatic_cast(long, DBHEADERSIZE), SEEK_SET) ;
545     if ( read (handle_ -> pidx, (char *) pStudyDesc, SIZEOF_STUDYDESC) == SIZEOF_STUDYDESC )
546         return EC_Normal ;
547 
548     DB_lseek (handle_ -> pidx, OFstatic_cast(long, DBHEADERSIZE), SEEK_SET) ;
549 
550     return QR_EC_IndexDatabaseError ;
551 }
552 
553 
554 /******************************
555  *      Remove an Index record
556  *      Just put a record with filename == ""
557  */
558 
DB_IdxRemove(int idx)559 OFCondition DcmQueryRetrieveIndexDatabaseHandle::DB_IdxRemove(int idx)
560 {
561     IdxRecord   rec ;
562     OFCondition cond = EC_Normal;
563 
564     DB_lseek (handle_ -> pidx, OFstatic_cast(long, DBHEADERSIZE + SIZEOF_STUDYDESC + OFstatic_cast(long, idx) * SIZEOF_IDXRECORD), SEEK_SET) ;
565     DB_IdxInitRecord (&rec, 0) ;
566 
567     rec. filename [0] = '\0' ;
568     if (write (handle_ -> pidx, (char *) &rec, SIZEOF_IDXRECORD) == SIZEOF_IDXRECORD)
569         cond = EC_Normal ;
570     else
571         cond = QR_EC_IndexDatabaseError ;
572 
573     DB_lseek (handle_ -> pidx, OFstatic_cast(long, DBHEADERSIZE), SEEK_SET) ;
574 
575     return cond ;
576 }
577 
DB_lock(OFBool exclusive)578 OFCondition DcmQueryRetrieveIndexDatabaseHandle::DB_lock(OFBool exclusive)
579 {
580     int lockmode;
581 
582     if (exclusive) {
583         lockmode = LOCK_EX;     /* exclusive lock */
584     } else {
585         lockmode = LOCK_SH;     /* shared lock */
586     }
587     if (dcmtk_flock(handle_->pidx, lockmode) < 0) {
588         dcmtk_plockerr("DB_lock");
589         return QR_EC_IndexDatabaseError;
590     }
591     return EC_Normal;
592 }
593 
DB_unlock()594 OFCondition DcmQueryRetrieveIndexDatabaseHandle::DB_unlock()
595 {
596     if (dcmtk_flock(handle_->pidx, LOCK_UN) < 0) {
597         dcmtk_plockerr("DB_unlock");
598         return QR_EC_IndexDatabaseError;
599     }
600     return EC_Normal;
601 }
602 
603 /*******************
604  *    Free an element List
605  */
606 
DB_FreeUidList(DB_UidList * lst)607 static OFCondition DB_FreeUidList (DB_UidList *lst)
608 {
609     while (lst != NULL) {
610         if (lst -> patient)
611             free (lst -> patient);
612         if (lst -> study)
613             free (lst -> study);
614         if (lst -> serie)
615             free (lst -> serie);
616         if (lst -> image)
617             free (lst -> image);
618         DB_UidList *curlst = lst;
619         lst = lst->next;
620         free (curlst);
621     }
622     return EC_Normal;
623 }
624 
625 
626 /*******************
627  *    Free a UID List
628  */
629 
DB_FreeElementList(DB_ElementList * lst)630 static OFCondition DB_FreeElementList (DB_ElementList *lst)
631 {
632     if (lst == NULL) return EC_Normal;
633 
634     OFCondition cond = DB_FreeElementList (lst -> next);
635     if (lst->elem.PValueField != NULL) {
636         free ((char *) lst -> elem. PValueField);
637     }
638     delete lst;
639     return (cond);
640 }
641 
642 /*******************
643  *    Is the specified tag supported
644  */
645 
DB_TagSupported(DcmTagKey tag)646 static int DB_TagSupported (DcmTagKey tag)
647 {
648     int i;
649 
650     for (i = 0; i < NbFindAttr; i++)
651         if (TbFindAttr[i]. tag == tag)
652             return (OFTrue);
653 
654     return (OFFalse);
655 
656 }
657 
658 /*******************
659  *    Get UID tag of a specified level
660  */
661 
DB_GetUIDTag(DB_LEVEL level,DcmTagKey * tag)662 static OFCondition DB_GetUIDTag (DB_LEVEL level, DcmTagKey *tag)
663 {
664     int i;
665 
666     for (i = 0; i < NbFindAttr; i++)
667         if ((TbFindAttr[i]. level == level) && (TbFindAttr[i]. keyAttr == UNIQUE_KEY))
668             break;
669 
670     if (i < NbFindAttr) {
671         *tag = TbFindAttr[i].tag;
672         return (EC_Normal);
673     }
674     else
675     return (QR_EC_IndexDatabaseError);
676 
677 }
678 
679 /*******************
680  *    Get tag level of a specified tag
681  */
682 
DB_GetTagLevel(DcmTagKey tag,DB_LEVEL * level)683 static OFCondition DB_GetTagLevel (DcmTagKey tag, DB_LEVEL *level)
684 {
685     int i;
686 
687     for (i = 0; i < NbFindAttr; i++)
688         if (TbFindAttr[i]. tag == tag)
689             break;
690 
691     if (i < NbFindAttr) {
692         *level = TbFindAttr[i]. level;
693         return (EC_Normal);
694     }
695     else
696     return (QR_EC_IndexDatabaseError);
697 }
698 
699 /*******************
700  *    Get tag key attribute of a specified tag
701  */
702 
DB_GetTagKeyAttr(DcmTagKey tag,DB_KEY_TYPE * keyAttr)703 static OFCondition DB_GetTagKeyAttr (DcmTagKey tag, DB_KEY_TYPE *keyAttr)
704 {
705     int i;
706 
707     for (i = 0; i < NbFindAttr; i++)
708         if (TbFindAttr[i]. tag == tag)
709             break;
710 
711     if (i < NbFindAttr) {
712         *keyAttr = TbFindAttr[i]. keyAttr;
713         return (EC_Normal);
714     }
715     else
716     return (QR_EC_IndexDatabaseError);
717 }
718 
719 /***********************
720  *    Duplicate a DICOM element
721  *    dst space is supposed provided by the caller
722  */
723 
DB_DuplicateElement(DB_SmallDcmElmt * src,DB_SmallDcmElmt * dst)724 static void DB_DuplicateElement (DB_SmallDcmElmt *src, DB_SmallDcmElmt *dst)
725 {
726     bzero( (char*)dst, sizeof (DB_SmallDcmElmt));
727     dst -> XTag = src -> XTag;
728     dst -> ValueLength = src -> ValueLength;
729 
730     if (src -> ValueLength == 0)
731         dst -> PValueField = NULL;
732     else {
733         dst -> PValueField = (char *)malloc ((int) src -> ValueLength+1);
734         bzero(dst->PValueField, (size_t)(src->ValueLength+1));
735         if (dst->PValueField != NULL) {
736             memcpy (dst -> PValueField,  src -> PValueField,
737                 (size_t) src -> ValueLength);
738         } else {
739             DCMQRDB_ERROR("DB_DuplicateElement: out of memory");
740         }
741     }
742 }
743 
744 
745 /***********************
746  *    Compare two ImagesofStudyArray elements
747  */
748 
DB_Compare(const void * ve1,const void * ve2)749 extern "C" int DB_Compare(const void *ve1, const void *ve2)
750 {
751     ImagesofStudyArray *e1 = (ImagesofStudyArray *)ve1;
752     ImagesofStudyArray *e2 = (ImagesofStudyArray *)ve2;
753     if ( e1 -> RecordedDate > e2 -> RecordedDate )
754         return (1);
755     else
756     if ( e1 -> RecordedDate == e2 -> RecordedDate )
757         return (0);
758     else
759         return (-1);
760 
761 }
762 
763 
764 /* ==================================================================== */
765 
~DcmQueryRetrieveDatabaseHandle()766 DcmQueryRetrieveDatabaseHandle::~DcmQueryRetrieveDatabaseHandle()
767 {
768 }
769 
770 /* ========================= FIND ========================= */
771 
772 // helper function to print 'ASCII' instead of an empty string for the value of
773 // Specific Character Set
characterSetName(const OFString & charset)774 static const char* characterSetName( const OFString& charset )
775 {
776     if (charset.empty())
777         return "ASCII";
778     return charset.c_str();
779 }
780 
781 class DcmQueryRetrieveIndexDatabaseHandle::CharsetConsideringMatcher
782 {
783 public:
784 
785     // Constructor, remember references to the find request character set and converter
CharsetConsideringMatcher(DB_Private_Handle & handle)786     CharsetConsideringMatcher(DB_Private_Handle& handle)
787     : findRequestCharacterSet(handle.findRequestCharacterSet)
788     , findRequestConverter(handle.findRequestConverter)
789     , candidateCharacterSet()
790     , candidateConverter()
791     , isFindRequestConversionNecessary(isConversionToUTF8Necessary(findRequestCharacterSet))
792     , isCandidateConversionNecessary()
793     , isConversionNecessary()
794     {
795 
796     }
797 
798     // read access to the candidate's character set value
getCandidateCharacterSet() const799     const OFString& getCandidateCharacterSet() const
800     {
801         return candidateCharacterSet;
802     }
803 
804     // prepare character set conversion for specific index record
setRecord(IdxRecord & idxRec)805     void setRecord(IdxRecord& idxRec)
806     {
807         // copy value of specific character set of the entry, since the converter
808         // would need it as an OFString anyway.
809         candidateCharacterSet.assign(idxRec.param[RECORDIDX_SpecificCharacterSet].PValueField,
810                                      idxRec.param[RECORDIDX_SpecificCharacterSet].ValueLength);
811         // test if conversion is potentially necessary since the character sets differ
812         if (findRequestCharacterSet != candidateCharacterSet) {
813             // determine if the candidate is compatible to UTF-8 or must be converted
814             isCandidateConversionNecessary = isConversionToUTF8Necessary(candidateCharacterSet);
815             // if it must be converted, clear the converter if it was previously initialized,
816             // but for a different character set
817             if (isCandidateConversionNecessary && candidateConverter &&
818                 candidateConverter.getSourceCharacterSet() != candidateCharacterSet) {
819                 candidateConverter.clear();
820             }
821             // even if the character sets differ, they may both be compatible to UTF-8,
822             // in which case conversion is still not necessary
823             isConversionNecessary = isCandidateConversionNecessary || isFindRequestConversionNecessary;
824         } else {
825             // conversion is not necessary
826             isConversionNecessary = OFFalse;
827         }
828     }
829 
830     // Try to match Two DB_ElementList elements
831     // The first one is the query key, the second one the candidate
832     // from the database entry.
833     // Returns OFTrue if both values match, OFFalse otherwise
operator ()(DB_ElementList * query,DB_SmallDcmElmt * candidate)834     OFBool operator()(DB_ElementList* query, DB_SmallDcmElmt* candidate)
835     {
836         // Universal matching is applied if the query value is empty:
837         // always return OFTrue
838         if (!query->elem.ValueLength)
839             return OFTrue;
840 
841         OFString buffer;
842         const char* pQuery = query->elem.PValueField;
843         const char* pQueryEnd = pQuery + query->elem.ValueLength;
844         const char* pCandidate = candidate->PValueField;
845         const char* pCandidateEnd = pCandidate + candidate->ValueLength;
846 
847         DcmVR vr = DcmTag(query->elem.XTag).getVR();
848         if (isConversionNecessary && vr.isAffectedBySpecificCharacterSet()) {
849 #ifdef DCMTK_ENABLE_CHARSET_CONVERSION
850             // convert query, if it isn't UTF-8 or ASCII already
851             if (isFindRequestConversionNecessary) {
852                 // does a value already exist in the cache?
853                 if (!query->utf8Value) {
854                     // fill the cache if it doesn't
855                     query->utf8Value = OFString();
856                     // initialize the converter, if this is the first
857                     // time we need it
858                     OFCondition cond = EC_Normal;
859                     if (!findRequestConverter)
860                         cond = findRequestConverter.selectCharacterSet(findRequestCharacterSet);
861                     if (cond.good()) {
862                         // covert the string and cache the result, using the
863                         // specific delimitation characters for this VR
864                         cond = findRequestConverter.convertString(
865                             query->elem.PValueField,
866                             query->elem.ValueLength,
867                             *query->utf8Value,
868                             vr.getDelimiterChars()
869                         );
870                     }
871                     if (cond.bad()) {
872                         DCMQRDB_WARN("Character set conversion of the query key failed with the following error: '" << cond.text()
873                             << "', will compare values that use different (incompatible) character sets: \""
874                             << characterSetName(findRequestCharacterSet) << "\" and \"" << characterSetName(candidateCharacterSet) << '"');
875                         // put the original value in the cache, since retrying the conversion on the next encounter does not make sense
876                         // (it would only fail again).
877                         query->utf8Value = OFString(query->elem.PValueField, query->elem.ValueLength);
878                     }
879                 }
880                 // use the value from the cache for the following match
881                 // operations
882                 pQuery = query->utf8Value->c_str();
883                 pQueryEnd = pQuery + query->utf8Value->size();
884             }
885             // convert the candidate, if it isn't already UTF-8 or ASCII
886             if (isCandidateConversionNecessary) {
887                 // initialize the converter, if this is the first time
888                 // we need it for this entry
889                 OFCondition cond = EC_Normal;
890                 if (!candidateConverter)
891                     cond = candidateConverter.selectCharacterSet(candidateCharacterSet);
892                 if (cond.good()) {
893                     // convert the string using the local buffer and the
894                     // specific delimitation characters for this VR
895                     cond = candidateConverter.convertString(
896                         candidate->PValueField,
897                         candidate->ValueLength,
898                         buffer,
899                         vr.getDelimiterChars()
900                     );
901                 }
902                 if (cond.good()) {
903                     // assign the buffer contents to the value being used
904                     // in the following match operations
905                     pCandidate = buffer.c_str();
906                     pCandidateEnd = pCandidate + buffer.size();
907                 } else {
908                     DCMQRDB_WARN("Character set conversion of the candidate failed with the following error: '" << cond.text()
909                         << "', will compare values that use different (incompatible) character sets: \""
910                         << characterSetName(findRequestCharacterSet) << "\" and \"" << characterSetName(candidateCharacterSet) << '"');
911                 }
912             }
913 #else
914             DCMQRDB_WARN("Character set conversion is not available, comparing values that use different (incompatible) character sets: \""
915                 << characterSetName(findRequestCharacterSet) << "\" and \"" << characterSetName(candidateCharacterSet) << '"');
916 #endif
917         }
918 
919         // remove leading and trailing spaces before matching
920         if (vr.isaString()) {
921             OFStandard::trimString(pQuery, pQueryEnd);
922             OFStandard::trimString(pCandidate, pCandidateEnd);
923         }
924 
925         // use DcmAttributeMatching to perform the appropriate matching function
926         // for the given VR
927         return DcmAttributeMatching( vr )( pQuery, pQueryEnd - pQuery,
928                                            pCandidate, pCandidateEnd - pCandidate );
929     }
930 
931 private:
932     const OFString& findRequestCharacterSet;
933     DcmSpecificCharacterSet& findRequestConverter;
934     OFString candidateCharacterSet;
935     DcmSpecificCharacterSet candidateConverter;
936     const OFBool isFindRequestConversionNecessary;
937     OFBool isCandidateConversionNecessary;
938     OFBool isConversionNecessary;
939 };
940 
isConversionToUTF8Necessary(const OFString & characterSet)941 OFBool DcmQueryRetrieveIndexDatabaseHandle::isConversionToUTF8Necessary(const OFString& characterSet)
942 {
943     // empty      -> ASCII, subset of UTF-8
944     // ISO_IR 6   -> ASCII, subset of UTF-8
945     // ISO_IR 192 -> UTF-8
946     return !characterSet.empty() &&
947            characterSet != "ISO_IR 192" &&
948            characterSet != "ISO_IR 6"
949     ;
950 }
951 
isConversionNecessary(const OFString & sourceCharacterSet,const OFString & destinationCharacterSet)952 OFBool DcmQueryRetrieveIndexDatabaseHandle::isConversionNecessary(const OFString& sourceCharacterSet,
953                                                                   const OFString& destinationCharacterSet)
954 {
955     // conversion is unnecessary if both are the same character set or if
956     // the destination is UTF-8 and the source is compatible to UTF-8
957     // (i.e. ASCII).
958     return sourceCharacterSet != destinationCharacterSet &&
959     (
960         destinationCharacterSet != "ISO_IR 192" ||
961         isConversionToUTF8Necessary(sourceCharacterSet)
962     );
963 }
964 
965 /************
966 **      Create the response list in specified handle,
967 **      using informations found in an index record.
968 **      Old response list is supposed freed
969 **/
970 
makeResponseList(DB_Private_Handle * phandle,IdxRecord * idxRec)971 void DcmQueryRetrieveIndexDatabaseHandle::makeResponseList (
972                 DB_Private_Handle       *phandle,
973                 IdxRecord               *idxRec
974                 )
975 {
976     int i ;
977     DB_ElementList *pRequestList = NULL;
978     DB_ElementList *plist = NULL;
979     DB_ElementList *last = NULL;
980 
981     phandle->findResponseList = NULL ;
982 
983     /*** For each element in Request identifier
984     **/
985 
986     for (pRequestList = phandle->findRequestList ; pRequestList ; pRequestList = pRequestList->next) {
987 
988         /*** Find Corresponding Tag in index record
989         **/
990 
991         for (i = 0 ; i < NBPARAMETERS ; i++)
992             if (idxRec->param [i]. XTag == pRequestList->elem. XTag)
993                 break ;
994 
995         /*** If Tag not found, skip the element
996         **/
997 
998         if (i >= NBPARAMETERS)
999             continue ;
1000 
1001         /*** Append index record element to response list
1002         **/
1003 
1004         plist = new DB_ElementList ;
1005         if (plist == NULL) {
1006             DCMQRDB_ERROR("makeResponseList: out of memory");
1007             return;
1008         }
1009 
1010         DB_DuplicateElement(&idxRec->param[i], &plist->elem);
1011 
1012         if (phandle->findResponseList == NULL) {
1013             phandle->findResponseList = last = plist ;
1014         }
1015         else {
1016             last->next = plist ;
1017             last = plist ;
1018         }
1019 
1020     }
1021 
1022     /** Specific Character Set stuff
1023     **/
1024 
1025     if (idxRec->param[RECORDIDX_SpecificCharacterSet].ValueLength) {
1026         plist = new DB_ElementList ;
1027         if (plist == NULL) {
1028             DCMQRDB_ERROR("makeResponseList: out of memory");
1029             return;
1030         }
1031 
1032         DB_DuplicateElement(&idxRec->param[RECORDIDX_SpecificCharacterSet], &plist->elem);
1033 
1034         if (phandle->findResponseList == NULL) {
1035             phandle->findResponseList = last = plist ;
1036         }
1037         else {
1038             last->next = plist ;
1039             last = plist ;
1040         }
1041     }
1042 }
1043 
1044 
1045 
1046 /************
1047 **      Test a Find Request List
1048 **      Returns EC_Normal if OK, else returns QR_EC_IndexDatabaseError
1049  */
1050 
testFindRequestList(DB_ElementList * findRequestList,DB_LEVEL queryLevel,DB_LEVEL infLevel,DB_LEVEL lowestLevel)1051 OFCondition DcmQueryRetrieveIndexDatabaseHandle::testFindRequestList (
1052                 DB_ElementList  *findRequestList,
1053                 DB_LEVEL        queryLevel,
1054                 DB_LEVEL        infLevel,
1055                 DB_LEVEL        lowestLevel
1056                 )
1057 {
1058     DB_ElementList *plist ;
1059     DB_LEVEL    XTagLevel = PATIENT_LEVEL; // DB_GetTagLevel() will set this correctly
1060     DB_KEY_TYPE XTagType  = OPTIONAL_KEY;  // DB_GetTagKeyAttr() will set this
1061     int level ;
1062 
1063     /**** Query level must be at least the infLevel
1064     ***/
1065 
1066     if (queryLevel < infLevel) {
1067         DCMQRDB_INFO("Level incompatible with Information Model (level " << queryLevel << ")");
1068         return QR_EC_IndexDatabaseError ;
1069     }
1070 
1071     if (queryLevel > lowestLevel) {
1072         DCMQRDB_DEBUG("Level incompatible with Information Model (level " << queryLevel << ")");
1073         return QR_EC_IndexDatabaseError ;
1074     }
1075 
1076     for (level = PATIENT_LEVEL ; level <= IMAGE_LEVEL ; level++) {
1077 
1078         /**** Manage exception due to StudyRoot Information Model :
1079         **** In this information model, queries may include Patient attributes
1080         **** but only if they are made at the study level
1081         ***/
1082 
1083         if ((level == PATIENT_LEVEL) && (infLevel == STUDY_LEVEL)) {
1084             /** In Study Root Information Model, accept only Patient Tags
1085             ** if the Query Level is the Study level
1086             */
1087 
1088             int atLeastOneKeyFound = OFFalse ;
1089             for (plist = findRequestList ; plist ; plist = plist->next) {
1090                 DB_GetTagLevel (plist->elem. XTag, &XTagLevel) ;
1091                 if (XTagLevel != level)
1092                     continue ;
1093                 atLeastOneKeyFound = OFTrue ;
1094             }
1095             if (atLeastOneKeyFound && (queryLevel != STUDY_LEVEL)) {
1096                 DCMQRDB_DEBUG("Key found in Study Root Information Model (level " << level << ")");
1097                 return QR_EC_IndexDatabaseError ;
1098             }
1099         }
1100 
1101         /**** If current level is above the QueryLevel
1102         ***/
1103 
1104         else if (level < queryLevel) {
1105 
1106             /** For this level, only unique keys are allowed
1107             ** Parse the request list elements referring to
1108             ** this level.
1109             ** Check that only unique key attr are provided
1110             */
1111 
1112             int uniqueKeyFound = OFFalse ;
1113             for (plist = findRequestList ; plist ; plist = plist->next) {
1114                 DB_GetTagLevel (plist->elem. XTag, &XTagLevel) ;
1115                 if (XTagLevel != level)
1116                     continue ;
1117                 DB_GetTagKeyAttr (plist->elem. XTag, &XTagType) ;
1118                 if (XTagType != UNIQUE_KEY) {
1119                     DCMQRDB_DEBUG("Non Unique Key found (level " << level << ")");
1120                     return QR_EC_IndexDatabaseError ;
1121                 }
1122                 else if (uniqueKeyFound) {
1123                     DCMQRDB_DEBUG("More than one Unique Key found (level " << level << ")");
1124                     return QR_EC_IndexDatabaseError ;
1125                 }
1126                 else
1127                     uniqueKeyFound = OFTrue ;
1128             }
1129         }
1130 
1131         /**** If current level is the QueryLevel
1132         ***/
1133 
1134         else if (level == queryLevel) {
1135 
1136             /** For this level, all keys are allowed
1137             ** Parse the request list elements referring to
1138             ** this level.
1139             ** Check that at least one key is provided
1140             */
1141 
1142             int atLeastOneKeyFound = OFFalse ;
1143             for (plist = findRequestList ; plist ; plist = plist->next) {
1144                 DB_GetTagLevel (plist->elem. XTag, &XTagLevel) ;
1145                 if (XTagLevel != level)
1146                     continue ;
1147                 atLeastOneKeyFound = OFTrue ;
1148             }
1149             if (! atLeastOneKeyFound) {
1150                 DCMQRDB_DEBUG("No Key found at query level (level " << level << ")");
1151                 return QR_EC_IndexDatabaseError ;
1152             }
1153         }
1154 
1155         /**** If current level beyond the QueryLevel
1156         ***/
1157 
1158         else if (level > queryLevel) {
1159 
1160             /** For this level, no key is allowed
1161             ** Parse the request list elements referring to
1162             ** this level.
1163             ** Check that no key is provided
1164             */
1165 
1166             int atLeastOneKeyFound = OFFalse ;
1167             for (plist = findRequestList ; plist ; plist = plist->next) {
1168                 DB_GetTagLevel (plist->elem. XTag, &XTagLevel) ;
1169                 if (XTagLevel != level)
1170                     continue ;
1171                 atLeastOneKeyFound = OFTrue ;
1172             }
1173             if (atLeastOneKeyFound) {
1174                 DCMQRDB_DEBUG("Key found beyond query level (level " << level << ")");
1175                 return QR_EC_IndexDatabaseError ;
1176             }
1177         }
1178 
1179     }
1180     return EC_Normal ;
1181 }
1182 
1183 
1184 /************
1185 **      Hierarchical Search Algorithm
1186 **      Returns OFTrue if matching is OK, else returns OFFalse
1187  */
1188 
hierarchicalCompare(DB_Private_Handle * phandle,IdxRecord * idxRec,DB_LEVEL level,DB_LEVEL infLevel,int * match,CharsetConsideringMatcher & dbmatch)1189 OFCondition DcmQueryRetrieveIndexDatabaseHandle::hierarchicalCompare (
1190                 DB_Private_Handle       *phandle,
1191                 IdxRecord               *idxRec,
1192                 DB_LEVEL                level,
1193                 DB_LEVEL                infLevel,
1194                 int                     *match,
1195                 CharsetConsideringMatcher& dbmatch)
1196 {
1197     int                 i ;
1198     DcmTagKey   XTag ;
1199     DB_ElementList *plist ;
1200     DB_LEVEL    XTagLevel  = PATIENT_LEVEL; // DB_GetTagLevel() will set this correctly
1201 
1202     /**** If current level is above the QueryLevel
1203     ***/
1204 
1205     if (level < phandle->queryLevel) {
1206 
1207         /** Get UID Tag for current level
1208          */
1209 
1210         DB_GetUIDTag (level, &XTag) ;
1211 
1212         /** Find Element with this XTag in Identifier list
1213          */
1214 
1215         for (plist = phandle->findRequestList ; plist ; plist = plist->next)
1216             if (plist->elem. XTag == XTag)
1217                 break ;
1218 
1219         /** Element not found
1220          */
1221 
1222         if (plist == NULL) {
1223             *match = OFFalse ;
1224             DCMQRDB_WARN("hierarchicalCompare : No UID Key found at level " << (int) level);
1225             return QR_EC_IndexDatabaseError ;
1226         }
1227 
1228         /** Find element with the same XTag in index record
1229          */
1230 
1231         for (i = 0 ; i < NBPARAMETERS ; i++)
1232             if (idxRec->param [i]. XTag == XTag)
1233                 break ;
1234 
1235         /** Compare with Single value matching
1236         ** If Match fails, return OFFalse
1237         */
1238 
1239         if (!dbmatch(plist, &idxRec->param[i])) {
1240             *match = OFFalse ;
1241             return EC_Normal ;
1242         }
1243 
1244         /** Match succeeded.
1245         ** Try at next level
1246         */
1247 
1248         return hierarchicalCompare (phandle, idxRec, (DB_LEVEL)(level + 1), infLevel, match, dbmatch) ;
1249     }
1250 
1251     /**** If current level is the QueryLevel
1252     ***/
1253 
1254     else if (level == phandle->queryLevel) {
1255 
1256         /*** For each element in Identifier list
1257         **/
1258 
1259         for (plist = phandle->findRequestList ; plist ; plist = plist->next) {
1260 
1261             /** Get the Tag level of this element
1262              */
1263 
1264             DB_GetTagLevel (plist->elem. XTag, &XTagLevel) ;
1265 
1266             /** If we are in the Study Root Information Model exception
1267             ** we must accept patients keys at the study level
1268             */
1269 
1270             if (  (XTagLevel == PATIENT_LEVEL)
1271                   && (phandle->queryLevel == STUDY_LEVEL)
1272                   && (infLevel == STUDY_LEVEL)
1273                 ) ;
1274 
1275             /** In other cases, only keys at the current level are
1276             ** taken into account. So skip this element.
1277             */
1278 
1279             else if (XTagLevel != level)
1280                 continue ;
1281 
1282             /** Find element with the same XTag in index record
1283              */
1284 
1285             for (i = 0 ; i < NBPARAMETERS ; i++)
1286                 if (idxRec->param [i]. XTag == plist->elem. XTag)
1287                     break ;
1288 
1289             /** Compare with appropriate Matching.
1290             ** If Match fails, return OFFalse
1291             */
1292 
1293 
1294             if (!dbmatch(plist, &idxRec->param[i])) {
1295                 *match = OFFalse ;
1296                 return EC_Normal ;
1297             }
1298         }
1299 
1300         /*** If we are here, all matches succeeded at the current level.
1301         *** Perhaps check that we have tried at least one match ??
1302         **/
1303 
1304         *match = OFTrue ;
1305         return EC_Normal ;
1306 
1307     }
1308     return QR_EC_IndexDatabaseError;
1309 }
1310 
1311 /********************
1312 **      Start find in Database
1313 **/
1314 
startFindRequest(const char * SOPClassUID,DcmDataset * findRequestIdentifiers,DcmQueryRetrieveDatabaseStatus * status)1315 OFCondition DcmQueryRetrieveIndexDatabaseHandle::startFindRequest(
1316                 const char      *SOPClassUID,
1317                 DcmDataset      *findRequestIdentifiers,
1318                 DcmQueryRetrieveDatabaseStatus  *status)
1319 {
1320     DB_SmallDcmElmt     elem ;
1321     DB_ElementList      *plist = NULL;
1322     DB_ElementList      *last = NULL;
1323     int                 MatchFound ;
1324     IdxRecord           idxRec ;
1325     DB_LEVEL            qLevel = PATIENT_LEVEL; // highest legal level for a query in the current model
1326     DB_LEVEL            lLevel = IMAGE_LEVEL;   // lowest legal level for a query in the current model
1327 
1328     OFCondition         cond = EC_Normal;
1329     OFBool qrLevelFound = OFFalse;
1330 
1331     /**** Is SOPClassUID supported ?
1332     ***/
1333 
1334     if (strcmp( SOPClassUID, UID_FINDPatientRootQueryRetrieveInformationModel) == 0)
1335         handle_->rootLevel = PATIENT_ROOT ;
1336     else if (strcmp( SOPClassUID, UID_FINDStudyRootQueryRetrieveInformationModel) == 0)
1337         handle_->rootLevel = STUDY_ROOT ;
1338 #ifndef NO_PATIENTSTUDYONLY_SUPPORT
1339     else if (strcmp( SOPClassUID, UID_RETIRED_FINDPatientStudyOnlyQueryRetrieveInformationModel) == 0)
1340         handle_->rootLevel = PATIENT_STUDY ;
1341 #endif
1342     else {
1343         status->setStatus(STATUS_FIND_Refused_SOPClassNotSupported);
1344         return (QR_EC_IndexDatabaseError) ;
1345     }
1346 
1347     /**** Parse Identifiers in the Dicom Object
1348     **** Find Query Level and construct a list
1349     **** of query identifiers
1350     ***/
1351 
1352     if (findRequestIdentifiers->findAndGetOFStringArray(DCM_SpecificCharacterSet, handle_->findRequestCharacterSet).bad())
1353         handle_->findRequestCharacterSet.clear();
1354     if (handle_->findRequestConverter && handle_->findRequestConverter.getSourceCharacterSet() != handle_->findRequestCharacterSet)
1355         handle_->findRequestConverter.clear();
1356 
1357     handle_->findRequestList = NULL ;
1358 
1359     int elemCount = OFstatic_cast(int, findRequestIdentifiers->card());
1360     for (int elemIndex=0; elemIndex<elemCount; elemIndex++) {
1361 
1362         DcmElement* dcelem = findRequestIdentifiers->getElement(elemIndex);
1363 
1364         elem.XTag = dcelem->getTag().getXTag();
1365         if (elem.XTag == DCM_QueryRetrieveLevel || DB_TagSupported(elem.XTag)) {
1366             elem.ValueLength = dcelem->getLength();
1367             if (elem.ValueLength == 0) {
1368                 elem.PValueField = NULL ;
1369             } else if ((elem.PValueField = OFstatic_cast(char*, malloc(OFstatic_cast(size_t, elem.ValueLength+1)))) == NULL) {
1370                 status->setStatus(STATUS_FIND_Refused_OutOfResources);
1371                 return (QR_EC_IndexDatabaseError) ;
1372             } else {
1373                 /* only char string type tags are supported at the moment */
1374                 char *s = NULL;
1375                 dcelem->getString(s);
1376                 /* the available space is always elem.ValueLength+1 */
1377                 OFStandard::strlcpy(elem.PValueField, s, elem.ValueLength+1);
1378             }
1379             /** If element is the Query Level, store it in handle
1380              */
1381 
1382             if (elem.XTag == DCM_QueryRetrieveLevel && elem.PValueField) {
1383                 char *pc ;
1384                 char level [50] ;
1385 
1386                 strncpy(level, (char*)elem.PValueField,
1387                         (elem.ValueLength<50)? (size_t)(elem.ValueLength) : 49) ;
1388 
1389                 /*** Skip this two lines if you want strict comparison
1390                 **/
1391 
1392                 for (pc = level ; *pc ; pc++)
1393                     *pc = ((*pc >= 'a') && (*pc <= 'z')) ? 'A' - 'a' + *pc : *pc ;
1394 
1395                 if (strncmp (level, PATIENT_LEVEL_STRING,
1396                              strlen (PATIENT_LEVEL_STRING)) == 0)
1397                     handle_->queryLevel = PATIENT_LEVEL ;
1398                 else if (strncmp (level, STUDY_LEVEL_STRING,
1399                                   strlen (STUDY_LEVEL_STRING)) == 0)
1400                     handle_->queryLevel = STUDY_LEVEL ;
1401                 else if (strncmp (level, SERIE_LEVEL_STRING,
1402                                   strlen (SERIE_LEVEL_STRING)) == 0)
1403                     handle_->queryLevel = SERIE_LEVEL ;
1404                 else if (strncmp (level, IMAGE_LEVEL_STRING,
1405                                   strlen (IMAGE_LEVEL_STRING)) == 0)
1406                     handle_->queryLevel = IMAGE_LEVEL ;
1407                 else {
1408                     if (elem. PValueField)
1409                         free (elem. PValueField) ;
1410 #ifdef DEBUG
1411                     DCMQRDB_DEBUG("DB_startFindRequest () : Illegal query level (" << level << ")");
1412 #endif
1413                     status->setStatus(STATUS_FIND_Failed_UnableToProcess);
1414                     return (QR_EC_IndexDatabaseError) ;
1415                 }
1416                 qrLevelFound = OFTrue;
1417             } else {
1418                 /** Else it is a query identifier.
1419                 ** Append it to our RequestList if it is supported
1420                 */
1421                 if (DB_TagSupported (elem. XTag)) {
1422 
1423                     plist = new DB_ElementList ;
1424                     if (plist == NULL) {
1425                         status->setStatus(STATUS_FIND_Refused_OutOfResources);
1426                         return (QR_EC_IndexDatabaseError) ;
1427                     }
1428                     DB_DuplicateElement (&elem, &(plist->elem)) ;
1429                     if (handle_->findRequestList == NULL) {
1430                         handle_->findRequestList = last = plist ;
1431                     } else {
1432                         last->next = plist ;
1433                         last = plist ;
1434                     }
1435                 }
1436             }
1437 
1438             if ( elem. PValueField ) {
1439                 free (elem. PValueField) ;
1440             }
1441         }
1442     }
1443 
1444     if (!qrLevelFound) {
1445         /* The Query/Retrieve Level is missing */
1446         status->setStatus(STATUS_FIND_Failed_IdentifierDoesNotMatchSOPClass);
1447         DCMQRDB_WARN("DB_startFindRequest(): missing Query/Retrieve Level");
1448         handle_->idxCounter = -1 ;
1449         DB_FreeElementList (handle_->findRequestList) ;
1450         handle_->findRequestList = NULL ;
1451         return (QR_EC_IndexDatabaseError) ;
1452     }
1453 
1454     switch (handle_->rootLevel)
1455     {
1456       case PATIENT_ROOT :
1457         qLevel = PATIENT_LEVEL ;
1458         lLevel = IMAGE_LEVEL ;
1459         break ;
1460       case STUDY_ROOT :
1461         qLevel = STUDY_LEVEL ;
1462         lLevel = IMAGE_LEVEL ;
1463         break ;
1464       case PATIENT_STUDY:
1465         qLevel = PATIENT_LEVEL ;
1466         lLevel = STUDY_LEVEL ;
1467         break ;
1468     }
1469 
1470     /**** Test the consistency of the request list
1471     ***/
1472 
1473     if (doCheckFindIdentifier) {
1474         cond = testFindRequestList (handle_->findRequestList, handle_->queryLevel, qLevel, lLevel) ;
1475         if (cond != EC_Normal) {
1476             handle_->idxCounter = -1 ;
1477             DB_FreeElementList (handle_->findRequestList) ;
1478             handle_->findRequestList = NULL ;
1479 #ifdef DEBUG
1480             DCMQRDB_DEBUG("DB_startFindRequest () : STATUS_FIND_Failed_IdentifierDoesNotMatchSOPClass - Invalid RequestList");
1481 #endif
1482             status->setStatus(STATUS_FIND_Failed_IdentifierDoesNotMatchSOPClass);
1483             return (cond) ;
1484         }
1485     }
1486 
1487     /**** Goto the beginning of Index File
1488     **** Then find the first matching image
1489     ***/
1490 
1491     DB_lock(OFFalse);
1492 
1493     DB_IdxInitLoop (&(handle_->idxCounter)) ;
1494     MatchFound = OFFalse ;
1495     cond = EC_Normal ;
1496 
1497     CharsetConsideringMatcher dbmatch(*handle_);
1498     while (1) {
1499 
1500         /*** Exit loop if read error (or end of file)
1501         **/
1502 
1503         if (DB_IdxGetNext (&(handle_->idxCounter), &idxRec) != EC_Normal)
1504             break ;
1505 
1506         /*** Exit loop if error or matching OK
1507         **/
1508 
1509         dbmatch.setRecord(idxRec);
1510         cond = hierarchicalCompare (handle_, &idxRec, qLevel, qLevel, &MatchFound, dbmatch) ;
1511         if (cond != EC_Normal)
1512             break ;
1513         if (MatchFound)
1514             break ;
1515     }
1516 
1517     /**** If an error occurred in Matching function
1518     ****    return a failed status
1519     ***/
1520 
1521     if (cond != EC_Normal) {
1522         handle_->idxCounter = -1 ;
1523         DB_FreeElementList (handle_->findRequestList) ;
1524         handle_->findRequestList = NULL ;
1525 #ifdef DEBUG
1526         DCMQRDB_DEBUG("DB_startFindRequest () : STATUS_FIND_Failed_UnableToProcess");
1527 #endif
1528         status->setStatus(STATUS_FIND_Failed_UnableToProcess);
1529 
1530         DB_unlock();
1531 
1532         return (cond) ;
1533     }
1534 
1535 
1536     /**** If a matching image has been found,
1537     ****         add index record to UID found list
1538     ****    prepare Response List in handle
1539     ****    return status is pending
1540     ***/
1541 
1542     if (MatchFound) {
1543         DB_UIDAddFound (handle_, &idxRec) ;
1544         makeResponseList (handle_, &idxRec) ;
1545 #ifdef DEBUG
1546         DCMQRDB_DEBUG("DB_startFindRequest () : STATUS_Pending");
1547 #endif
1548         status->setStatus(STATUS_Pending);
1549         return (EC_Normal) ;
1550     }
1551 
1552     /**** else no matching image has been found,
1553     ****    free query identifiers list
1554     ****    status is success
1555     ***/
1556 
1557     else {
1558         handle_->idxCounter = -1 ;
1559         DB_FreeElementList (handle_->findRequestList) ;
1560         handle_->findRequestList = NULL ;
1561 #ifdef DEBUG
1562         DCMQRDB_DEBUG("DB_startFindRequest () : STATUS_Success");
1563 #endif
1564         status->setStatus(STATUS_Success);
1565 
1566         DB_unlock();
1567 
1568         return (EC_Normal) ;
1569     }
1570 
1571 }
1572 
1573 /********************
1574 **      Get next find response in Database
1575  */
1576 
nextFindResponse(DcmDataset ** findResponseIdentifiers,DcmQueryRetrieveDatabaseStatus * status,const DcmQueryRetrieveCharacterSetOptions & characterSetOptions)1577 OFCondition DcmQueryRetrieveIndexDatabaseHandle::nextFindResponse (
1578                 DcmDataset      **findResponseIdentifiers,
1579                 DcmQueryRetrieveDatabaseStatus  *status,
1580                 const DcmQueryRetrieveCharacterSetOptions& characterSetOptions)
1581 {
1582 
1583     DB_ElementList      *plist = NULL;
1584     int                 MatchFound = OFFalse;
1585     IdxRecord           idxRec ;
1586     DB_LEVEL            qLevel = PATIENT_LEVEL;
1587     const char          *queryLevelString = NULL;
1588     OFCondition         cond = EC_Normal;
1589 
1590     if (handle_->findResponseList == NULL) {
1591 #ifdef DEBUG
1592         DCMQRDB_DEBUG("DB_nextFindResponse () : STATUS_Success");
1593 #endif
1594         *findResponseIdentifiers = NULL ;
1595         status->setStatus(STATUS_Success);
1596 
1597         DB_unlock();
1598 
1599         return (EC_Normal) ;
1600     }
1601 
1602     /***** Create the response (findResponseIdentifiers) using
1603     ***** the last find done and saved in handle findResponseList
1604     ****/
1605 
1606     *findResponseIdentifiers = new DcmDataset ;
1607     if ( *findResponseIdentifiers != NULL ) {
1608 
1609         /*** Put responses
1610         **/
1611 
1612         for ( plist = handle_->findResponseList ; plist != NULL ; plist = plist->next ) {
1613             DcmTag t(plist->elem.XTag);
1614             DcmElement *dce = DcmItem::newDicomElement(t);
1615             if (dce == NULL) {
1616                 status->setStatus(STATUS_FIND_Refused_OutOfResources);
1617                 return QR_EC_IndexDatabaseError;
1618             }
1619             if (plist->elem.PValueField != NULL &&
1620                 strlen(plist->elem.PValueField) > 0) {
1621                 OFCondition ec = dce->putString(plist->elem.PValueField);
1622                 if (ec != EC_Normal) {
1623                     DCMQRDB_WARN("dbfind: DB_nextFindResponse: cannot put()");
1624                     status->setStatus(STATUS_FIND_Failed_UnableToProcess);
1625                     return QR_EC_IndexDatabaseError;
1626                 }
1627             }
1628             OFCondition ec = (*findResponseIdentifiers)->insert(dce, OFTrue /*replaceOld*/);
1629             if (ec != EC_Normal) {
1630                 DCMQRDB_WARN("dbfind: DB_nextFindResponse: cannot insert()");
1631                 status->setStatus(STATUS_FIND_Failed_UnableToProcess);
1632                 return QR_EC_IndexDatabaseError;
1633             }
1634         }
1635 
1636         /*** Append the Query level
1637         **/
1638 
1639         switch (handle_->queryLevel) {
1640         case PATIENT_LEVEL :
1641             queryLevelString = PATIENT_LEVEL_STRING ;
1642             break ;
1643         case STUDY_LEVEL :
1644             queryLevelString = STUDY_LEVEL_STRING ;
1645             break ;
1646         case SERIE_LEVEL :
1647             queryLevelString = SERIE_LEVEL_STRING ;
1648             break ;
1649         case IMAGE_LEVEL :
1650             queryLevelString = IMAGE_LEVEL_STRING ;
1651             break ;
1652         }
1653         DU_putStringDOElement(*findResponseIdentifiers,
1654                               DCM_QueryRetrieveLevel, queryLevelString);
1655 
1656 #ifdef DCMTK_ENABLE_CHARSET_CONVERSION
1657         OFString specificCharacterSet;
1658         if ((*findResponseIdentifiers)->findAndGetOFStringArray(DCM_SpecificCharacterSet, specificCharacterSet).bad())
1659             specificCharacterSet.clear();
1660 
1661         const OFString* destinationCharacterSet = NULL;
1662         const OFString* fallbackCharacterSet = NULL;
1663 
1664         if (characterSetOptions.flags & DcmQueryRetrieveCharacterSetOptions::Override) {
1665             destinationCharacterSet = &characterSetOptions.characterSet;
1666             if (
1667                 (characterSetOptions.flags & DcmQueryRetrieveCharacterSetOptions::Fallback) &&
1668                 characterSetOptions.characterSet != handle_->findRequestCharacterSet
1669             ) {
1670                 fallbackCharacterSet = &handle_->findRequestCharacterSet;
1671             }
1672         } else {
1673             destinationCharacterSet = &handle_->findRequestCharacterSet;
1674             if (
1675                 (characterSetOptions.flags & DcmQueryRetrieveCharacterSetOptions::Fallback) &&
1676                 characterSetOptions.characterSet != handle_->findRequestCharacterSet
1677             ) {
1678                 fallbackCharacterSet = &characterSetOptions.characterSet;
1679             }
1680         }
1681 
1682         if (isConversionNecessary(specificCharacterSet, *destinationCharacterSet)) {
1683             OFCondition charset_status = (*findResponseIdentifiers)->convertCharacterSet(
1684                 specificCharacterSet,
1685                 *destinationCharacterSet,
1686                 characterSetOptions.conversionFlags,
1687                 OFTrue);
1688             if (charset_status.bad()) {
1689                 DCMQRDB_WARN("Converting response from character set \""
1690                     << characterSetName(specificCharacterSet)
1691                     << "\" to character set \""
1692                     << characterSetName(*destinationCharacterSet)
1693                     << "\" failed, (error message: " << charset_status.text() << ')');
1694                 if (fallbackCharacterSet && isConversionNecessary(specificCharacterSet, *fallbackCharacterSet)) {
1695                     DCMQRDB_INFO("Trying to convert response from character set \""
1696                         << characterSetName(specificCharacterSet)
1697                         << "\" to fall-back character set \""
1698                         << characterSetName(*fallbackCharacterSet) << "\" instead");
1699                     charset_status = (*findResponseIdentifiers)->convertCharacterSet(
1700                         specificCharacterSet,
1701                         *fallbackCharacterSet,
1702                         characterSetOptions.conversionFlags,
1703                         OFTrue);
1704                     if (charset_status.bad()) {
1705                         DCMQRDB_WARN("Converting response from character set \""
1706                             << characterSetName(specificCharacterSet)
1707                             << "\" to character set \""
1708                             << characterSetName(*fallbackCharacterSet)
1709                             << "\" failed, (error message: " << charset_status.text() << ')');
1710                     } else {
1711                         DCMQRDB_INFO("Successfully converted response from character set \""
1712                             << characterSetName(specificCharacterSet)
1713                             << "\" to character set \""
1714                             << characterSetName(*fallbackCharacterSet) << "\"");
1715                     }
1716                 } else if (fallbackCharacterSet) {
1717                     DCMQRDB_INFO("Conversion to fall-back character set \""
1718                         << characterSetName(*fallbackCharacterSet)
1719                         << "\" is not necessary, since the original character set is compatible");
1720                 }
1721             } else {
1722                 DCMQRDB_INFO("Successfully converted response from character set \""
1723                     << characterSetName(specificCharacterSet)
1724                     << "\" to character set \""
1725                     << characterSetName(*destinationCharacterSet)
1726                     << "\"");
1727             }
1728         }
1729 #endif
1730 
1731 #ifdef DEBUG
1732         DCMQRDB_DEBUG("DB: findResponseIdentifiers:" << OFendl
1733             << DcmObject::PrintHelper(**findResponseIdentifiers));
1734 #endif
1735     } else {
1736 
1737         DB_unlock();
1738 
1739         return (QR_EC_IndexDatabaseError) ;
1740     }
1741 
1742     switch (handle_->rootLevel) {
1743     case PATIENT_ROOT : qLevel = PATIENT_LEVEL ;        break ;
1744     case STUDY_ROOT :   qLevel = STUDY_LEVEL ;          break ;
1745     case PATIENT_STUDY: qLevel = PATIENT_LEVEL ;        break ;
1746     }
1747 
1748     /***** Free the last response...
1749     ****/
1750 
1751     DB_FreeElementList (handle_->findResponseList) ;
1752     handle_->findResponseList = NULL ;
1753 
1754     /***** ... and find the next one
1755     ****/
1756 
1757     MatchFound = OFFalse ;
1758     cond = EC_Normal ;
1759 
1760     CharsetConsideringMatcher dbmatch(*handle_);
1761     while (1) {
1762 
1763         /*** Exit loop if read error (or end of file)
1764         **/
1765 
1766         if (DB_IdxGetNext (&(handle_->idxCounter), &idxRec) != EC_Normal)
1767             break ;
1768 
1769         /*** If Response already found
1770         **/
1771 
1772         if (DB_UIDAlreadyFound (handle_, &idxRec))
1773             continue ;
1774 
1775         /*** Exit loop if error or matching OK
1776         **/
1777 
1778         dbmatch.setRecord(idxRec);
1779         cond = hierarchicalCompare (handle_, &idxRec, qLevel, qLevel, &MatchFound, dbmatch) ;
1780         if (cond != EC_Normal)
1781             break ;
1782         if (MatchFound)
1783             break ;
1784 
1785     }
1786 
1787     /**** If an error occurred in Matching function
1788     ****    return status is pending
1789     ***/
1790 
1791     if (cond != EC_Normal) {
1792         handle_->idxCounter = -1 ;
1793         DB_FreeElementList (handle_->findRequestList) ;
1794         handle_->findRequestList = NULL ;
1795 #ifdef DEBUG
1796         DCMQRDB_DEBUG("DB_nextFindResponse () : STATUS_FIND_Failed_UnableToProcess");
1797 #endif
1798         status->setStatus(STATUS_FIND_Failed_UnableToProcess);
1799 
1800         DB_unlock();
1801 
1802         return (cond) ;
1803     }
1804 
1805     /**** If a matching image has been found
1806     ****    add index records UIDs in found UID list
1807     ****    prepare Response List in handle
1808     ***/
1809 
1810     if (MatchFound) {
1811         DB_UIDAddFound (handle_, &idxRec) ;
1812         makeResponseList (handle_, &idxRec) ;
1813 #ifdef DEBUG
1814         DCMQRDB_DEBUG("DB_nextFindResponse () : STATUS_Pending");
1815 #endif
1816         status->setStatus(STATUS_Pending);
1817         return (EC_Normal) ;
1818     }
1819 
1820     /**** else no matching image has been found,
1821     ****    free query identifiers list
1822     **** Response list is null, so next call will return STATUS_Success
1823     ***/
1824 
1825     else {
1826         handle_->idxCounter = -1 ;
1827         DB_FreeElementList (handle_->findRequestList) ;
1828         handle_->findRequestList = NULL ;
1829         DB_FreeUidList (handle_->uidList) ;
1830         handle_->uidList = NULL ;
1831     }
1832 
1833 #ifdef DEBUG
1834     DCMQRDB_DEBUG("DB_nextFindResponse () : STATUS_Pending");
1835 #endif
1836     status->setStatus(STATUS_Pending);
1837     return (EC_Normal) ;
1838 }
1839 
1840 /********************
1841 **      Cancel find request
1842  */
1843 
cancelFindRequest(DcmQueryRetrieveDatabaseStatus * status)1844 OFCondition DcmQueryRetrieveIndexDatabaseHandle::cancelFindRequest (DcmQueryRetrieveDatabaseStatus *status)
1845 {
1846 
1847     handle_->idxCounter = -1 ;
1848     DB_FreeElementList (handle_->findRequestList) ;
1849     handle_->findRequestList = NULL ;
1850     DB_FreeElementList (handle_->findResponseList) ;
1851     handle_->findResponseList = NULL ;
1852     DB_FreeUidList (handle_->uidList) ;
1853     handle_->uidList = NULL ;
1854 
1855     status->setStatus(STATUS_FIND_Cancel_MatchingTerminatedDueToCancelRequest);
1856 
1857     DB_unlock();
1858 
1859     return (EC_Normal) ;
1860 }
1861 
1862 /* ========================= MOVE ========================= */
1863 
1864 /************
1865  *      Test a Move Request List
1866  *      Returns EC_Normal if OK, else returns QR_EC_IndexDatabaseError
1867  */
1868 
testMoveRequestList(DB_ElementList * findRequestList,DB_LEVEL queryLevel,DB_LEVEL infLevel,DB_LEVEL lowestLevel)1869 OFCondition DcmQueryRetrieveIndexDatabaseHandle::testMoveRequestList (
1870                 DB_ElementList  *findRequestList,
1871                 DB_LEVEL        queryLevel,
1872                 DB_LEVEL        infLevel,
1873                 DB_LEVEL        lowestLevel
1874                 )
1875 {
1876     DB_ElementList *plist ;
1877     DB_LEVEL    XTagLevel = PATIENT_LEVEL; // DB_GetTagLevel() will set this correctly
1878     DB_KEY_TYPE XTagType  = OPTIONAL_KEY;  // DB_GetTagKeyAttr() will set this
1879     int level ;
1880 
1881     /**** Query level must be at least the infLevel
1882     ***/
1883 
1884     if (queryLevel < infLevel) {
1885         DCMQRDB_DEBUG("Level incompatible with Information Model (level " << (int)queryLevel << ")");
1886         return QR_EC_IndexDatabaseError ;
1887     }
1888 
1889     if (queryLevel > lowestLevel) {
1890         DCMQRDB_DEBUG("Level incompatible with Information Model (level " << (int)queryLevel << ")");
1891         return QR_EC_IndexDatabaseError ;
1892     }
1893 
1894     for (level = PATIENT_LEVEL ; level <= IMAGE_LEVEL ; level++) {
1895 
1896         /**** Manage exception due to StudyRoot Information Model :
1897         **** In this information model, move may not include any
1898         **** Patient attributes.
1899         ***/
1900 
1901         if ((level == PATIENT_LEVEL) && (infLevel == STUDY_LEVEL)) {
1902 
1903             /** In Study Root Information Model, do not accept any
1904             ** Patient Tag
1905             */
1906 
1907             int atLeastOneKeyFound = OFFalse ;
1908             for (plist = findRequestList ; plist ; plist = plist->next) {
1909                 DB_GetTagLevel (plist->elem. XTag, &XTagLevel) ;
1910                 if (XTagLevel != level)
1911                     continue ;
1912                 atLeastOneKeyFound = OFTrue ;
1913             }
1914             if (atLeastOneKeyFound) {
1915                 DCMQRDB_DEBUG("Key found in Study Root Information Model (level " << level << ")");
1916                 return QR_EC_IndexDatabaseError ;
1917             }
1918         }
1919 
1920         /**** If current level is above or equal to the QueryLevel
1921         ***/
1922 
1923         else if (level <= queryLevel) {
1924 
1925             /** For these levels, only unique keys are allowed
1926             ** Parse the request list elements referring to
1927             ** this level.
1928             ** Check that only unique key attr are provided
1929             */
1930 
1931             int uniqueKeyFound = OFFalse ;
1932             for (plist = findRequestList ; plist ; plist = plist->next) {
1933                 DB_GetTagLevel (plist->elem. XTag, &XTagLevel) ;
1934                 if (XTagLevel != level)
1935                     continue ;
1936                 DB_GetTagKeyAttr (plist->elem. XTag, &XTagType) ;
1937                 if (XTagType != UNIQUE_KEY) {
1938                     DCMQRDB_DEBUG("Non Unique Key found (level " << level << ")");
1939                     return QR_EC_IndexDatabaseError ;
1940                 }
1941                 else if (uniqueKeyFound) {
1942                     DCMQRDB_DEBUG("More than one Unique Key found (level " << level << ")");
1943                     return QR_EC_IndexDatabaseError ;
1944                 }
1945                 else
1946                     uniqueKeyFound = OFTrue ;
1947             }
1948             if (! uniqueKeyFound) {
1949                 DCMQRDB_DEBUG("No Unique Key found (level " << level << ")");
1950                 return QR_EC_IndexDatabaseError ;
1951             }
1952         }
1953 
1954         /**** If current level beyond the QueryLevel
1955         ***/
1956 
1957         else if (level > queryLevel) {
1958 
1959             /** For this level, no key is allowed
1960             ** Parse the request list elements referring to
1961             ** this level.
1962             ** Check that no key is provided
1963             */
1964 
1965             int atLeastOneKeyFound = OFFalse ;
1966             for (plist = findRequestList ; plist ; plist = plist->next) {
1967                 DB_GetTagLevel (plist->elem. XTag, &XTagLevel) ;
1968                 if (XTagLevel != level)
1969                     continue ;
1970                 atLeastOneKeyFound = OFTrue ;
1971             }
1972             if (atLeastOneKeyFound) {
1973                 DCMQRDB_DEBUG("Key found beyond query level (level " << level << ")");
1974                 return QR_EC_IndexDatabaseError ;
1975             }
1976         }
1977 
1978     }
1979     return EC_Normal ;
1980 }
1981 
1982 
1983 
startMoveRequest(const char * SOPClassUID,DcmDataset * moveRequestIdentifiers,DcmQueryRetrieveDatabaseStatus * status)1984 OFCondition DcmQueryRetrieveIndexDatabaseHandle::startMoveRequest(
1985         const char      *SOPClassUID,
1986         DcmDataset      *moveRequestIdentifiers,
1987         DcmQueryRetrieveDatabaseStatus  *status)
1988 {
1989 
1990     DB_SmallDcmElmt     elem ;
1991     DB_ElementList      *plist = NULL;
1992     DB_ElementList      *last = NULL;
1993     DB_CounterList      *pidxlist = NULL;
1994     DB_CounterList      *lastidxlist = NULL;
1995     int                 MatchFound = OFFalse;
1996     IdxRecord           idxRec ;
1997     DB_LEVEL            qLevel = PATIENT_LEVEL; // highest legal level for a query in the current model
1998     DB_LEVEL            lLevel = IMAGE_LEVEL;   // lowest legal level for a query in the current model
1999     OFCondition         cond = EC_Normal;
2000     OFBool qrLevelFound = OFFalse;
2001 
2002     /**** Is SOPClassUID supported ?
2003     ***/
2004 
2005     if (strcmp( SOPClassUID, UID_MOVEPatientRootQueryRetrieveInformationModel) == 0)
2006         handle_->rootLevel = PATIENT_ROOT ;
2007     else if (strcmp( SOPClassUID, UID_MOVEStudyRootQueryRetrieveInformationModel) == 0)
2008         handle_->rootLevel = STUDY_ROOT ;
2009 #ifndef NO_PATIENTSTUDYONLY_SUPPORT
2010     else if (strcmp( SOPClassUID, UID_RETIRED_MOVEPatientStudyOnlyQueryRetrieveInformationModel) == 0)
2011         handle_->rootLevel = PATIENT_STUDY ;
2012 #endif
2013 #ifndef NO_GET_SUPPORT
2014     /* experimental support for GET */
2015     else if (strcmp( SOPClassUID, UID_GETPatientRootQueryRetrieveInformationModel) == 0)
2016         handle_->rootLevel = PATIENT_ROOT ;
2017     else if (strcmp( SOPClassUID, UID_GETStudyRootQueryRetrieveInformationModel) == 0)
2018         handle_->rootLevel = STUDY_ROOT ;
2019 #ifndef NO_PATIENTSTUDYONLY_SUPPORT
2020     else if (strcmp( SOPClassUID, UID_RETIRED_GETPatientStudyOnlyQueryRetrieveInformationModel) == 0)
2021         handle_->rootLevel = PATIENT_STUDY ;
2022 #endif
2023 #endif
2024 
2025     else {
2026         status->setStatus(STATUS_MOVE_Failed_SOPClassNotSupported);
2027         return (QR_EC_IndexDatabaseError) ;
2028     }
2029 
2030 
2031     /**** Parse Identifiers in the Dicom Object
2032     **** Find Query Level and construct a list
2033     **** of query identifiers
2034     ***/
2035 
2036     int elemCount = (int)(moveRequestIdentifiers->card());
2037     for (int elemIndex=0; elemIndex<elemCount; elemIndex++) {
2038 
2039         DcmElement* dcelem = moveRequestIdentifiers->getElement(elemIndex);
2040 
2041         elem.XTag = dcelem->getTag().getXTag();
2042         if (elem.XTag == DCM_QueryRetrieveLevel || DB_TagSupported(elem.XTag)) {
2043             elem.ValueLength = dcelem->getLength();
2044             if (elem.ValueLength == 0) {
2045                 elem.PValueField = NULL ;
2046             } else if ((elem.PValueField = (char*)malloc((size_t)(elem.ValueLength+1))) == NULL) {
2047                 status->setStatus(STATUS_MOVE_Failed_UnableToProcess);
2048                 return (QR_EC_IndexDatabaseError) ;
2049             } else {
2050                 /* only char string type tags are supported at the moment */
2051                 char *s = NULL;
2052                 dcelem->getString(s);
2053                 /* the available space is always elem.ValueLength+1 */
2054                 OFStandard::strlcpy(elem.PValueField, s, elem.ValueLength+1);
2055             }
2056 
2057             /** If element is the Query Level, store it in handle
2058              */
2059 
2060             if (elem. XTag == DCM_QueryRetrieveLevel && elem.PValueField) {
2061                 char *pc ;
2062                 char level [50] ;
2063 
2064                 strncpy (level, (char *) elem. PValueField, (size_t)((elem. ValueLength < 50) ? elem. ValueLength : 49)) ;
2065 
2066                 /*** Skip this two lines if you want strict comparison
2067                 **/
2068 
2069                 for (pc = level ; *pc ; pc++)
2070                     *pc = ((*pc >= 'a') && (*pc <= 'z')) ? 'A' - 'a' + *pc : *pc ;
2071 
2072                 if (strncmp (level, PATIENT_LEVEL_STRING,
2073                              strlen (PATIENT_LEVEL_STRING)) == 0)
2074                     handle_->queryLevel = PATIENT_LEVEL ;
2075                 else if (strncmp (level, STUDY_LEVEL_STRING,
2076                                   strlen (STUDY_LEVEL_STRING)) == 0)
2077                     handle_->queryLevel = STUDY_LEVEL ;
2078                 else if (strncmp (level, SERIE_LEVEL_STRING,
2079                                   strlen (SERIE_LEVEL_STRING)) == 0)
2080                     handle_->queryLevel = SERIE_LEVEL ;
2081                 else if (strncmp (level, IMAGE_LEVEL_STRING,
2082                                   strlen (IMAGE_LEVEL_STRING)) == 0)
2083                     handle_->queryLevel = IMAGE_LEVEL ;
2084                 else {
2085 #ifdef DEBUG
2086                     DCMQRDB_DEBUG("DB_startMoveRequest : STATUS_MOVE_Failed_UnableToProcess");
2087 #endif
2088                     status->setStatus(STATUS_MOVE_Failed_UnableToProcess);
2089                     return (QR_EC_IndexDatabaseError) ;
2090                 }
2091                 qrLevelFound = OFTrue;
2092             } else {
2093                 /** Else it is a query identifier
2094                 ** Append it to our RequestList
2095                 */
2096                 if (! DB_TagSupported (elem. XTag))
2097                     continue ;
2098 
2099                 plist = new DB_ElementList ;
2100                 if (plist == NULL) {
2101                     status->setStatus(STATUS_FIND_Refused_OutOfResources);
2102                     return (QR_EC_IndexDatabaseError) ;
2103                 }
2104                 DB_DuplicateElement (&elem, & (plist->elem)) ;
2105                 if (handle_->findRequestList == NULL) {
2106                     handle_->findRequestList = last = plist ;
2107                 } else {
2108                     last->next = plist ;
2109                     last = plist ;
2110                 }
2111             }
2112 
2113             if ( elem. PValueField ) {
2114                 free (elem. PValueField) ;
2115             }
2116         }
2117     }
2118 
2119     if (!qrLevelFound) {
2120         /* The Query/Retrieve Level is missing */
2121         status->setStatus(STATUS_MOVE_Failed_IdentifierDoesNotMatchSOPClass);
2122         DCMQRDB_WARN("DB_startMoveRequest(): missing Query/Retrieve Level");
2123         handle_->idxCounter = -1 ;
2124         DB_FreeElementList (handle_->findRequestList) ;
2125         handle_->findRequestList = NULL ;
2126         return (QR_EC_IndexDatabaseError) ;
2127     }
2128 
2129     switch (handle_->rootLevel)
2130     {
2131       case PATIENT_ROOT :
2132         qLevel = PATIENT_LEVEL ;
2133         lLevel = IMAGE_LEVEL ;
2134         break ;
2135       case STUDY_ROOT :
2136         qLevel = STUDY_LEVEL ;
2137         lLevel = IMAGE_LEVEL ;
2138         break ;
2139       case PATIENT_STUDY:
2140         qLevel = PATIENT_LEVEL ;
2141         lLevel = STUDY_LEVEL ;
2142         break ;
2143     }
2144 
2145     /**** Test the consistency of the request list
2146     ***/
2147 
2148     if (doCheckMoveIdentifier) {
2149         cond = testMoveRequestList (handle_->findRequestList,
2150                                     handle_->queryLevel, qLevel, lLevel) ;
2151         if (cond != EC_Normal) {
2152             handle_->idxCounter = -1 ;
2153             DB_FreeElementList (handle_->findRequestList) ;
2154             handle_->findRequestList = NULL ;
2155 #ifdef DEBUG
2156             DCMQRDB_DEBUG("DB_startMoveRequest () : STATUS_MOVE_Failed_IdentifierDoesNotMatchSOPClass - Invalid RequestList");
2157 #endif
2158             status->setStatus(STATUS_MOVE_Failed_IdentifierDoesNotMatchSOPClass);
2159             return (cond) ;
2160         }
2161     }
2162 
2163     /**** Goto the beginning of Index File
2164     **** Then find all matching images
2165     ***/
2166 
2167     MatchFound = OFFalse ;
2168     handle_->moveCounterList = NULL ;
2169     handle_->NumberRemainOperations = 0 ;
2170 
2171     /**** Find matching images
2172     ***/
2173 
2174     DB_lock(OFFalse);
2175 
2176     CharsetConsideringMatcher dbmatch(*handle_);
2177     DB_IdxInitLoop (&(handle_->idxCounter)) ;
2178     while (1) {
2179 
2180         /*** Exit loop if read error (or end of file)
2181         **/
2182 
2183         if (DB_IdxGetNext (&(handle_->idxCounter), &idxRec) != EC_Normal)
2184             break ;
2185 
2186         /*** If matching found
2187         **/
2188 
2189         dbmatch.setRecord(idxRec);
2190         cond = hierarchicalCompare (handle_, &idxRec, qLevel, qLevel, &MatchFound, dbmatch) ;
2191         if (MatchFound) {
2192             pidxlist = (DB_CounterList *) malloc (sizeof( DB_CounterList ) ) ;
2193             if (pidxlist == NULL) {
2194                 status->setStatus(STATUS_FIND_Refused_OutOfResources);
2195                 return (QR_EC_IndexDatabaseError) ;
2196             }
2197 
2198             pidxlist->next = NULL ;
2199             pidxlist->idxCounter = handle_->idxCounter ;
2200             handle_->NumberRemainOperations++ ;
2201             if ( handle_->moveCounterList == NULL )
2202                 handle_->moveCounterList = lastidxlist = pidxlist ;
2203             else {
2204                 lastidxlist->next = pidxlist ;
2205                 lastidxlist = pidxlist ;
2206             }
2207         }
2208     }
2209 
2210     DB_FreeElementList (handle_->findRequestList) ;
2211     handle_->findRequestList = NULL ;
2212 
2213     /**** If a matching image has been found,
2214     ****    status is pending
2215     ***/
2216 
2217     if ( handle_->NumberRemainOperations > 0 ) {
2218 #ifdef DEBUG
2219         DCMQRDB_DEBUG("DB_startMoveRequest : STATUS_Pending");
2220 #endif
2221         status->setStatus(STATUS_Pending);
2222         return (EC_Normal) ;
2223     }
2224 
2225     /**** else no matching image has been found,
2226     ****    free query identifiers list
2227     ****    status is success
2228     ***/
2229 
2230     else {
2231         handle_->idxCounter = -1 ;
2232 #ifdef DEBUG
2233         DCMQRDB_DEBUG("DB_startMoveRequest : STATUS_Success");
2234 #endif
2235         status->setStatus(STATUS_Success);
2236 
2237         DB_unlock();
2238 
2239         return (EC_Normal) ;
2240     }
2241 
2242 
2243 }
2244 
nextMoveResponse(char * SOPClassUID,size_t SOPClassUIDSize,char * SOPInstanceUID,size_t SOPInstanceUIDSize,char * imageFileName,size_t imageFileNameSize,unsigned short * numberOfRemainingSubOperations,DcmQueryRetrieveDatabaseStatus * status)2245 OFCondition DcmQueryRetrieveIndexDatabaseHandle::nextMoveResponse(
2246     char *SOPClassUID,
2247     size_t SOPClassUIDSize,
2248     char *SOPInstanceUID,
2249     size_t SOPInstanceUIDSize,
2250     char *imageFileName,
2251     size_t imageFileNameSize,
2252     unsigned short *numberOfRemainingSubOperations,
2253     DcmQueryRetrieveDatabaseStatus *status)
2254 {
2255     IdxRecord           idxRec ;
2256     DB_CounterList              *nextlist ;
2257 
2258     /**** If all matching images have been retrieved,
2259     ****    status is success
2260     ***/
2261 
2262     if ( handle_->NumberRemainOperations <= 0 ) {
2263         status->setStatus(STATUS_Success);
2264 
2265         DB_unlock();
2266 
2267         return (EC_Normal) ;
2268     }
2269 
2270     /**** Goto the next matching image number of Index File
2271     ***/
2272 
2273     if (DB_IdxRead (handle_->moveCounterList->idxCounter, &idxRec) != EC_Normal) {
2274 #ifdef DEBUG
2275         DCMQRDB_DEBUG("DB_nextMoveResponse : STATUS_MOVE_Failed_UnableToProcess");
2276 #endif
2277         status->setStatus(STATUS_MOVE_Failed_UnableToProcess);
2278 
2279         DB_unlock();
2280 
2281         return (QR_EC_IndexDatabaseError) ;
2282     }
2283 
2284     OFStandard::strlcpy(SOPClassUID, (char *) idxRec. SOPClassUID, SOPClassUIDSize) ;
2285     OFStandard::strlcpy(SOPInstanceUID, (char *) idxRec. SOPInstanceUID, SOPInstanceUIDSize) ;
2286     OFStandard::strlcpy(imageFileName, (char *) idxRec. filename, imageFileNameSize) ;
2287 
2288     *numberOfRemainingSubOperations = --handle_->NumberRemainOperations ;
2289 
2290     nextlist = handle_->moveCounterList->next ;
2291     free (handle_->moveCounterList) ;
2292     handle_->moveCounterList = nextlist ;
2293     status->setStatus(STATUS_Pending);
2294 #ifdef DEBUG
2295     DCMQRDB_DEBUG("DB_nextMoveResponse : STATUS_Pending");
2296 #endif
2297     return (EC_Normal) ;
2298 }
2299 
2300 
2301 
cancelMoveRequest(DcmQueryRetrieveDatabaseStatus * status)2302 OFCondition DcmQueryRetrieveIndexDatabaseHandle::cancelMoveRequest (DcmQueryRetrieveDatabaseStatus *status)
2303 {
2304     DB_CounterList *plist ;
2305 
2306     while (handle_->moveCounterList) {
2307         plist  = handle_->moveCounterList ;
2308         handle_->moveCounterList = handle_->moveCounterList->next ;
2309         free (plist) ;
2310     }
2311 
2312     status->setStatus(STATUS_MOVE_Cancel_SubOperationsTerminatedDueToCancelIndication);
2313 
2314     DB_unlock();
2315 
2316     return (EC_Normal) ;
2317 }
2318 
2319 
2320 /* ========================= STORE ========================= */
2321 
2322 
enableQuotaSystem(OFBool enable)2323 void DcmQueryRetrieveIndexDatabaseHandle::enableQuotaSystem(OFBool enable)
2324 {
2325     quotaSystemEnabled = enable;
2326 }
2327 
2328 
2329 /*
2330 ** Image file deleting
2331 */
2332 
2333 
deleteImageFile(char * imgFile)2334 OFCondition DcmQueryRetrieveIndexDatabaseHandle::deleteImageFile(char* imgFile)
2335 {
2336     if (!quotaSystemEnabled) {
2337       DCMQRDB_WARN("file delete operations are disabled, keeping file: " << imgFile << " despite duplicate SOP Instance UID");
2338       return EC_Normal;
2339     } else {
2340       DCMQRDB_WARN("Deleting file: " << imgFile << " due to quota or duplicate SOP instance UID");
2341     }
2342 
2343 #ifdef LOCK_IMAGE_FILES
2344     int lockfd;
2345 #ifdef O_BINARY
2346     lockfd = open(imgFile, O_RDWR | O_BINARY, 0666);    /* obtain file descriptor */
2347 #else
2348     lockfd = open(imgFile, O_RDWR, 0666);   /* obtain file descriptor */
2349 #endif
2350     if (lockfd < 0) {
2351       DCMQRDB_WARN("DB ERROR: cannot open image file for deleting: " << imgFile);
2352       return QR_EC_IndexDatabaseError;
2353     }
2354     if (dcmtk_flock(lockfd, LOCK_EX) < 0) { /* exclusive lock (blocking) */
2355       DCMQRDB_WARN("DB ERROR: cannot lock image file for deleting: " << imgFile);
2356       dcmtk_plockerr("DB ERROR");
2357     }
2358 #endif
2359 
2360     if (unlink(imgFile) < 0) {
2361         /* delete file */
2362         DCMQRDB_ERROR("DB ERROR: cannot delete image file: " << imgFile << OFendl
2363             << "QR_EC_IndexDatabaseError: " << OFStandard::getLastSystemErrorCode().message());
2364     }
2365 
2366 #ifdef LOCK_IMAGE_FILES
2367     if (dcmtk_flock(lockfd, LOCK_UN) < 0) { /* unlock */
2368         DCMQRDB_WARN("DB ERROR: cannot unlock image file for deleting: " << imgFile);
2369         dcmtk_plockerr("DB ERROR");
2370      }
2371     close(lockfd);              /* release file descriptor */
2372 #endif
2373 
2374     return EC_Normal;
2375 }
2376 
2377 
2378 /*************************
2379 **   Delete oldest study in database
2380  */
2381 
deleteOldestStudy(StudyDescRecord * pStudyDesc)2382 int DcmQueryRetrieveIndexDatabaseHandle::deleteOldestStudy(StudyDescRecord *pStudyDesc)
2383 {
2384 
2385     int oldestStudy ;
2386     double OldestDate ;
2387     int s ;
2388     size_t n ;
2389     int idx = 0 ;
2390     IdxRecord idxRec ;
2391 
2392     oldestStudy = 0 ;
2393     OldestDate = 0.0 ;
2394 
2395 #ifdef DEBUG
2396     DCMQRDB_DEBUG("deleteOldestStudy");
2397 #endif
2398 
2399     for ( s = 0 ; s < handle_ -> maxStudiesAllowed ; s++ ) {
2400         if ( ( pStudyDesc[s]. NumberofRegistratedImages != 0 ) &&
2401             ( ( OldestDate == 0.0 ) || ( pStudyDesc[s]. LastRecordedDate < OldestDate ) ) ) {
2402             OldestDate = pStudyDesc[s]. LastRecordedDate ;
2403             oldestStudy = s ;
2404         }
2405     }
2406 
2407 #ifdef DEBUG
2408     DCMQRDB_DEBUG("deleteOldestStudy oldestStudy = " << oldestStudy);
2409 #endif
2410 
2411     n = strlen(pStudyDesc[oldestStudy].StudyInstanceUID) ;
2412     while ( DB_IdxRead (idx, &idxRec) == EC_Normal ) {
2413 
2414     if ( ! ( strncmp(idxRec. StudyInstanceUID, pStudyDesc[oldestStudy].StudyInstanceUID, n) ) ) {
2415         DB_IdxRemove (idx) ;
2416         deleteImageFile(idxRec.filename);
2417     }
2418     idx++ ;
2419     }
2420 
2421     pStudyDesc[oldestStudy].NumberofRegistratedImages = 0 ;
2422     pStudyDesc[oldestStudy].StudySize = 0 ;
2423     return(oldestStudy) ;
2424 }
2425 
2426 
2427 
2428 
2429 /*************************
2430 **   Delete oldest images in database
2431  */
2432 
deleteOldestImages(StudyDescRecord * pStudyDesc,int StudyNum,char * StudyUID,long RequiredSize)2433 OFCondition DcmQueryRetrieveIndexDatabaseHandle::deleteOldestImages(StudyDescRecord *pStudyDesc, int StudyNum, char *StudyUID, long RequiredSize)
2434 {
2435 
2436     ImagesofStudyArray *StudyArray ;
2437     IdxRecord idxRec ;
2438     int nbimages = 0 , s = 0;
2439     size_t n ;
2440     long DeletedSize ;
2441 
2442 #ifdef DEBUG
2443     DCMQRDB_DEBUG("deleteOldestImages RequiredSize = " << RequiredSize);
2444 #endif
2445     n = strlen(StudyUID) ;
2446     StudyArray = (ImagesofStudyArray *)malloc(MAX_NUMBER_OF_IMAGES * sizeof(ImagesofStudyArray)) ;
2447 
2448     if (StudyArray == NULL) {
2449         DCMQRDB_WARN("deleteOldestImages: out of memory");
2450         return QR_EC_IndexDatabaseError;
2451     }
2452 
2453     /** Find all images having the same StudyUID
2454      */
2455 
2456     DB_IdxInitLoop (&(handle_ -> idxCounter)) ;
2457     while ( DB_IdxGetNext(&(handle_ -> idxCounter), &idxRec) == EC_Normal ) {
2458     if ( ! ( strncmp(idxRec. StudyInstanceUID, StudyUID, n) ) ) {
2459 
2460         StudyArray[nbimages]. idxCounter = handle_ -> idxCounter ;
2461         StudyArray[nbimages]. RecordedDate = idxRec. RecordedDate ;
2462         StudyArray[nbimages++]. ImageSize = idxRec. ImageSize ;
2463     }
2464     }
2465 
2466     /** Sort the StudyArray in order to have the oldest images first
2467      */
2468     qsort((char *)StudyArray, nbimages, sizeof(ImagesofStudyArray), DB_Compare) ;
2469 
2470 #ifdef DEBUG
2471     {
2472         int i ;
2473         DCMQRDB_DEBUG("deleteOldestImages : Sorted images ref array");
2474         for (i = 0 ; i < nbimages ; i++)
2475             DCMQRDB_DEBUG("[" << STD_NAMESPACE setw(2) << i << "] :   Size " << StudyArray[i].ImageSize
2476                 << "   Date " << STD_NAMESPACE setw(20) << STD_NAMESPACE setprecision(3) << StudyArray[i].RecordedDate
2477                 << "   Ref " << StudyArray[i].idxCounter);
2478         DCMQRDB_DEBUG("deleteOldestImages : end of ref array");
2479     }
2480 #endif
2481 
2482     s = 0 ;
2483     DeletedSize = 0 ;
2484 
2485     while ( DeletedSize < RequiredSize ) {
2486 
2487     IdxRecord idxRemoveRec ;
2488     DB_IdxRead (StudyArray[s]. idxCounter, &idxRemoveRec) ;
2489 #ifdef DEBUG
2490     DCMQRDB_DEBUG("Removing file : " << idxRemoveRec. filename);
2491 #endif
2492     deleteImageFile(idxRemoveRec.filename);
2493 
2494     DB_IdxRemove (StudyArray[s]. idxCounter) ;
2495     pStudyDesc[StudyNum].NumberofRegistratedImages -= 1 ;
2496     pStudyDesc[StudyNum].StudySize -= StudyArray[s]. ImageSize ;
2497     DeletedSize += StudyArray[s++]. ImageSize ;
2498     }
2499 
2500 #ifdef DEBUG
2501     DCMQRDB_DEBUG("deleteOldestImages DeletedSize = " << (int)DeletedSize);
2502 #endif
2503     free(StudyArray) ;
2504     return( EC_Normal ) ;
2505 
2506 }
2507 
2508 
2509 
2510 /*************************
2511  *   Verify if study UID already exists
2512  *   If the study UID exists, its index in the study descriptor is returned.
2513  *   If the study UID does not exist, the index of the first unused descriptor entry is returned.
2514  *   If no entries are free, maxStudiesAllowed is returned.
2515  */
2516 
matchStudyUIDInStudyDesc(StudyDescRecord * pStudyDesc,char * StudyUID,int maxStudiesAllowed)2517 int DcmQueryRetrieveIndexDatabaseHandle::matchStudyUIDInStudyDesc (StudyDescRecord *pStudyDesc, char *StudyUID, int maxStudiesAllowed)
2518 {
2519     int s = 0 ;
2520     while  (s < maxStudiesAllowed)
2521     {
2522       if ((pStudyDesc[s].NumberofRegistratedImages > 0) && (0 == strcmp(pStudyDesc[s].StudyInstanceUID, StudyUID))) break;
2523       s++ ;
2524     }
2525     if (s==maxStudiesAllowed) // study uid does not exist, look for free descriptor
2526     {
2527       s=0;
2528       while  (s < maxStudiesAllowed)
2529       {
2530         if (pStudyDesc[s].NumberofRegistratedImages == 0) break;
2531         s++ ;
2532       }
2533     }
2534     return s;
2535 }
2536 
2537 
2538 /*************************
2539 **  Check up storage rights in Study Desk record
2540  */
2541 
checkupinStudyDesc(StudyDescRecord * pStudyDesc,char * StudyUID,long imageSize)2542 OFCondition DcmQueryRetrieveIndexDatabaseHandle::checkupinStudyDesc(StudyDescRecord *pStudyDesc, char *StudyUID, long imageSize)
2543 {
2544     int         s ;
2545     long        RequiredSize ;
2546 
2547     s = matchStudyUIDInStudyDesc (pStudyDesc, StudyUID,
2548                      (int)(handle_ -> maxStudiesAllowed)) ;
2549 
2550     /** If Study already exists
2551      */
2552 
2553     if ( pStudyDesc[s]. NumberofRegistratedImages != 0 ) {
2554 
2555 #ifdef DEBUG
2556     DCMQRDB_DEBUG("checkupinStudyDesc: study already exists : " << s) ;
2557 #endif
2558     if ( OFstatic_cast(size_t, pStudyDesc[s]. StudySize) + imageSize  >
2559          OFstatic_cast(size_t, handle_ -> maxBytesPerStudy) )
2560     {
2561         if ( imageSize > handle_ -> maxBytesPerStudy ) {
2562 #ifdef DEBUG
2563             DCMQRDB_DEBUG("checkupinStudyDesc: imageSize = " << imageSize << " too large");
2564 #endif
2565             return ( QR_EC_IndexDatabaseError ) ;
2566         }
2567 
2568         RequiredSize = imageSize -
2569             ( handle_ -> maxBytesPerStudy - pStudyDesc[s]. StudySize ) ;
2570         deleteOldestImages(pStudyDesc, s, StudyUID, RequiredSize) ;
2571     }
2572 
2573 
2574     }
2575     else {
2576 #ifdef DEBUG
2577     DCMQRDB_DEBUG("checkupinStudyDesc: study doesn't already exist");
2578 #endif
2579     if ( imageSize > handle_ -> maxBytesPerStudy ) {
2580 #ifdef DEBUG
2581         DCMQRDB_DEBUG("checkupinStudyDesc: imageSize = " << imageSize << " too large");
2582 #endif
2583         return ( QR_EC_IndexDatabaseError ) ;
2584     }
2585     if ( s > ( handle_ -> maxStudiesAllowed - 1 ) )
2586         s = deleteOldestStudy(pStudyDesc) ;
2587 
2588     }
2589 
2590     pStudyDesc[s]. StudySize += imageSize ;
2591 #ifdef DEBUG
2592     DCMQRDB_DEBUG("checkupinStudyDesc: ~~~~~~~~ StudySize = " << pStudyDesc[s]. StudySize);
2593 #endif
2594 
2595     /* we only have second accuracy */
2596     pStudyDesc[s]. LastRecordedDate =  (double) time(NULL);
2597 
2598     pStudyDesc[s]. NumberofRegistratedImages++ ;
2599     OFStandard::strlcpy(pStudyDesc[s].StudyInstanceUID, StudyUID, UI_MAX_LENGTH+1) ;
2600 
2601     if ( DB_StudyDescChange (pStudyDesc) == EC_Normal)
2602         return ( EC_Normal ) ;
2603     else
2604         return ( QR_EC_IndexDatabaseError ) ;
2605 }
2606 
2607 /*
2608  * If the image is already stored remove it from the database.
2609  * hewett - Nov. 1, 93
2610  */
removeDuplicateImage(const char * SOPInstanceUID,const char * StudyInstanceUID,StudyDescRecord * pStudyDesc,const char * newImageFileName)2611 OFCondition DcmQueryRetrieveIndexDatabaseHandle::removeDuplicateImage(
2612     const char *SOPInstanceUID, const char *StudyInstanceUID,
2613     StudyDescRecord *pStudyDesc, const char *newImageFileName)
2614 {
2615 
2616     int idx = 0;
2617     IdxRecord idxRec ;
2618     int studyIdx = 0;
2619 
2620     studyIdx = matchStudyUIDInStudyDesc (pStudyDesc, (char*)StudyInstanceUID,
2621                         (int)(handle_ -> maxStudiesAllowed)) ;
2622 
2623     if ( pStudyDesc[studyIdx].NumberofRegistratedImages == 0 ) {
2624     /* no study images, cannot be any old images */
2625     return EC_Normal;
2626     }
2627 
2628     while (DB_IdxRead(idx, &idxRec) == EC_Normal) {
2629 
2630     if (strcmp(idxRec.SOPInstanceUID, SOPInstanceUID) == 0) {
2631 
2632 #ifdef DEBUG
2633         DCMQRDB_DEBUG("--- Removing Existing DB Image Record: " << idxRec.filename);
2634 #endif
2635         /* remove the idx record  */
2636         DB_IdxRemove (idx);
2637         /* only remove the image file if it is different than that
2638          * being entered into the database.
2639          */
2640         if (strcmp(idxRec.filename, newImageFileName) != 0) {
2641             deleteImageFile(idxRec.filename);
2642         }
2643         /* update the study info */
2644         pStudyDesc[studyIdx].NumberofRegistratedImages--;
2645         pStudyDesc[studyIdx].StudySize -= idxRec.ImageSize;
2646     }
2647     idx++;
2648     }
2649     /* the study record should be written to file later */
2650     return EC_Normal;
2651 }
2652 
2653 
2654 /*************************
2655 **  Add data from imageFileName to database
2656  */
2657 
storeRequest(const char * SOPClassUID,const char *,const char * imageFileName,DcmQueryRetrieveDatabaseStatus * status,OFBool isNew)2658 OFCondition DcmQueryRetrieveIndexDatabaseHandle::storeRequest (
2659     const char  *SOPClassUID,
2660     const char  * /*SOPInstanceUID*/,
2661     const char  *imageFileName,
2662     DcmQueryRetrieveDatabaseStatus *status,
2663     OFBool      isNew)
2664 {
2665     IdxRecord        idxRec ;
2666     StudyDescRecord  *pStudyDesc ;
2667     int              i ;
2668     struct stat      stat_buf ;
2669 
2670     /**** Initialize an IdxRecord
2671     ***/
2672 
2673     bzero((char*)&idxRec, sizeof(idxRec));
2674 
2675     DB_IdxInitRecord (&idxRec, 0) ;
2676 
2677     strncpy(idxRec.filename, imageFileName, DBC_MAXSTRING);
2678 #ifdef DEBUG
2679     DCMQRDB_DEBUG("DB_storeRequest () : storage request of file : " << idxRec.filename);
2680 #endif
2681     strncpy (idxRec.SOPClassUID, SOPClassUID, UI_MAX_LENGTH);
2682 
2683     /**** Get IdxRec values from ImageFile
2684     ***/
2685 
2686     DcmFileFormat dcmff;
2687     if (dcmff.loadFile(imageFileName).bad())
2688     {
2689       DCMQRDB_WARN("DB: Cannot open file: " << imageFileName << ": "
2690           << OFStandard::getLastSystemErrorCode().message());
2691       status->setStatus(STATUS_STORE_Error_CannotUnderstand);
2692       return (QR_EC_IndexDatabaseError) ;
2693     }
2694 
2695     DcmDataset *dset = dcmff.getDataset();
2696 
2697     assert(dset);
2698 
2699     OFCondition ec;
2700 
2701     for (i = 0 ; i < NBPARAMETERS ; i++ ) {
2702         DB_SmallDcmElmt *se = idxRec.param + i;
2703 
2704         const char *strPtr = NULL;
2705         ec = dset->findAndGetString(se->XTag, strPtr);
2706         if ((ec != EC_Normal) || (strPtr == NULL)) {
2707             /* not found or empty */
2708             se->PValueField[0] = '\0';
2709             se->ValueLength = 0;
2710         } else {
2711             /* found and non-empty */
2712             strncpy(se->PValueField, strPtr, (size_t)(se->ValueLength));
2713             /* important: do not change the ValueLength field before the string is copied! */
2714             se->ValueLength = OFstatic_cast(int, strlen(se->PValueField));
2715         }
2716     }
2717 
2718     /* InstanceStatus */
2719     idxRec.hstat = (isNew) ? DVIF_objectIsNew : DVIF_objectIsNotNew;
2720 
2721     /* InstanceDescription */
2722     OFBool useDescrTag = OFTrue;
2723     DcmTagKey descrTag = DCM_ImageComments;
2724     if (SOPClassUID != NULL)
2725     {
2726         /* fill in value depending on SOP class UID (content might be improved) */
2727         if (strcmp(SOPClassUID, UID_GrayscaleSoftcopyPresentationStateStorage) == 0)
2728         {
2729             descrTag = DCM_ContentDescription;
2730         } else if (strcmp(SOPClassUID, UID_RETIRED_HardcopyGrayscaleImageStorage) == 0)
2731         {
2732             OFStandard::strlcpy(idxRec.InstanceDescription, "Hardcopy Grayscale Image", DESCRIPTION_MAX_LENGTH+1);
2733             useDescrTag = OFFalse;
2734         } else if ((strcmp(SOPClassUID, UID_BasicTextSRStorage) == 0) ||
2735                    (strcmp(SOPClassUID, UID_EnhancedSRStorage) == 0) ||
2736                    (strcmp(SOPClassUID, UID_ComprehensiveSRStorage) == 0) ||
2737                    (strcmp(SOPClassUID, UID_Comprehensive3DSRStorage) == 0) ||
2738                    (strcmp(SOPClassUID, UID_ExtensibleSRStorage) == 0) ||
2739                    (strcmp(SOPClassUID, UID_ProcedureLogStorage) == 0) ||
2740                    (strcmp(SOPClassUID, UID_MammographyCADSRStorage) == 0) ||
2741                    (strcmp(SOPClassUID, UID_KeyObjectSelectionDocumentStorage) == 0) ||
2742                    (strcmp(SOPClassUID, UID_ChestCADSRStorage) == 0) ||
2743                    (strcmp(SOPClassUID, UID_ColonCADSRStorage) == 0) ||
2744                    (strcmp(SOPClassUID, UID_XRayRadiationDoseSRStorage) == 0) ||
2745                    (strcmp(SOPClassUID, UID_SpectaclePrescriptionReportStorage) == 0) ||
2746                    (strcmp(SOPClassUID, UID_MacularGridThicknessAndVolumeReportStorage) == 0) ||
2747                    (strcmp(SOPClassUID, UID_ImplantationPlanSRDocumentStorage) == 0) ||
2748                    (strcmp(SOPClassUID, UID_RadiopharmaceuticalRadiationDoseSRStorage) == 0) ||
2749                    (strcmp(SOPClassUID, UID_AcquisitionContextSRStorage) == 0) ||
2750                    (strcmp(SOPClassUID, UID_SimplifiedAdultEchoSRStorage) == 0) ||
2751                    (strcmp(SOPClassUID, UID_PatientRadiationDoseSRStorage) == 0) ||
2752                    (strcmp(SOPClassUID, UID_PerformedImagingAgentAdministrationSRStorage) == 0) ||
2753                    (strcmp(SOPClassUID, UID_PlannedImagingAgentAdministrationSRStorage) == 0))
2754         {
2755             OFString string;
2756             OFString description = "unknown SR";
2757             const char *name = dcmFindNameOfUID(SOPClassUID);
2758             if (name != NULL)
2759                 description = name;
2760             if (dset->findAndGetOFString(DCM_VerificationFlag, string) == EC_Normal)
2761             {
2762                 description += ", ";
2763                 description += string;
2764             }
2765             if (dset->findAndGetOFString(DCM_CompletionFlag, string) == EC_Normal)
2766             {
2767                 description += ", ";
2768                 description += string;
2769             }
2770             if (dset->findAndGetOFString(DCM_CompletionFlagDescription, string) == EC_Normal)
2771             {
2772                 description += ", ";
2773                 description += string;
2774             }
2775             OFStandard::strlcpy(idxRec.InstanceDescription, description.c_str(), DESCRIPTION_MAX_LENGTH+1);
2776             useDescrTag = OFFalse;
2777         } else if (strcmp(SOPClassUID, UID_RETIRED_StoredPrintStorage) == 0)
2778         {
2779             OFStandard::strlcpy(idxRec.InstanceDescription, "Stored Print", DESCRIPTION_MAX_LENGTH+1);
2780             useDescrTag = OFFalse;
2781         }
2782     }
2783     /* get description from attribute specified above */
2784     if (useDescrTag)
2785     {
2786         OFString string;
2787         /* return value is irrelevant */
2788         dset->findAndGetOFString(descrTag, string);
2789         strncpy(idxRec.InstanceDescription, string.c_str(), DESCRIPTION_MAX_LENGTH);
2790     }
2791     /* is dataset digitally signed? */
2792     if (strlen(idxRec.InstanceDescription) + 9 < DESCRIPTION_MAX_LENGTH)
2793     {
2794         DcmStack stack;
2795         if (dset->search(DCM_DigitalSignaturesSequence, stack, ESM_fromHere, OFTrue /* searchIntoSub */) == EC_Normal)
2796         {
2797             /* in principle it should be checked whether there is _any_ non-empty digital signatures sequence, but ... */
2798             if (((DcmSequenceOfItems *)stack.top())->card() > 0)
2799             {
2800                 if (strlen(idxRec.InstanceDescription) > 0)
2801                     OFStandard::strlcat(idxRec.InstanceDescription, " (Signed)", DESCRIPTION_MAX_LENGTH+1);
2802                 else
2803                     OFStandard::strlcpy(idxRec.InstanceDescription, "Signed Instance", DESCRIPTION_MAX_LENGTH+1);
2804             }
2805         }
2806     }
2807 
2808     /**** Print Elements
2809     ***/
2810 
2811 #ifdef DEBUG
2812     DCMQRDB_DEBUG("-- BEGIN Parameters to Register in DB");
2813     for (i = 0 ; i < NBPARAMETERS ; i++) {  /* new definition */
2814         DB_SmallDcmElmt *se = idxRec.param + i;
2815         const char* value = "";
2816         if (se->PValueField != NULL) value = se->PValueField;
2817         DcmTag tag(se->XTag);
2818         DCMQRDB_DEBUG(" " << tag.getTagName() << ": \"" << value << "\"");
2819     }
2820     DCMQRDB_DEBUG("-- END Parameters to Register in DB");
2821 #endif
2822 
2823     /**** Goto the end of IndexFile, and write the record
2824     ***/
2825 
2826     DB_lock(OFTrue);
2827 
2828     pStudyDesc = (StudyDescRecord *)malloc (SIZEOF_STUDYDESC) ;
2829     if (pStudyDesc == NULL) {
2830       DCMQRDB_ERROR("DB_storeRequest: out of memory");
2831       status->setStatus(STATUS_STORE_Refused_OutOfResources);
2832       DB_unlock();
2833       return (QR_EC_IndexDatabaseError) ;
2834     }
2835 
2836     bzero((char *)pStudyDesc, SIZEOF_STUDYDESC);
2837     DB_GetStudyDesc(pStudyDesc) ;
2838 
2839     stat(imageFileName, &stat_buf) ;
2840     idxRec. ImageSize = (int)(stat_buf. st_size) ;
2841 
2842     /* we only have second accuracy */
2843     idxRec. RecordedDate =  (double) time(NULL);
2844 
2845     /*
2846      * If the image is already stored remove it from the database.
2847      * hewett - Nov. 1, 93
2848      */
2849 
2850     removeDuplicateImage(idxRec.SOPInstanceUID,
2851                 idxRec.StudyInstanceUID, pStudyDesc,
2852                 imageFileName);
2853 
2854 
2855     if ( checkupinStudyDesc(pStudyDesc, idxRec. StudyInstanceUID, idxRec. ImageSize) != EC_Normal ) {
2856         free (pStudyDesc) ;
2857         status->setStatus(STATUS_STORE_Refused_OutOfResources);
2858 
2859         DB_unlock();
2860 
2861         return (QR_EC_IndexDatabaseError) ;
2862     }
2863 
2864     free (pStudyDesc) ;
2865 
2866     if (DB_IdxAdd (handle_, &i, &idxRec) == EC_Normal)
2867     {
2868         status->setStatus(STATUS_Success);
2869         DB_unlock();
2870         return (EC_Normal) ;
2871     }
2872     else
2873     {
2874         status->setStatus(STATUS_STORE_Refused_OutOfResources);
2875         DB_unlock();
2876     }
2877     return QR_EC_IndexDatabaseError;
2878 }
2879 
2880 /*
2881 ** Prune invalid DB records.
2882 */
2883 
pruneInvalidRecords()2884 OFCondition DcmQueryRetrieveIndexDatabaseHandle::pruneInvalidRecords()
2885 {
2886     int idx = 0;
2887     IdxRecord idxRec ;
2888     StudyDescRecord *pStudyDesc;
2889 
2890     DB_lock(OFTrue);
2891 
2892     pStudyDesc = (StudyDescRecord *)malloc (SIZEOF_STUDYDESC) ;
2893     if (pStudyDesc == NULL) {
2894       DCMQRDB_WARN("DB_pruneInvalidRecords: out of memory");
2895       DB_unlock();
2896       return (QR_EC_IndexDatabaseError) ;
2897     }
2898 
2899     for (int i = 0 ; i < handle_ -> maxStudiesAllowed ; i++ )
2900       pStudyDesc[i]. NumberofRegistratedImages = 0 ;
2901 
2902     DB_GetStudyDesc(pStudyDesc) ;
2903 
2904     while (DB_IdxRead(idx, &idxRec) == EC_Normal)
2905     {
2906       if (access(idxRec.filename, R_OK) < 0)
2907       {
2908 #ifdef DEBUG
2909         DCMQRDB_DEBUG("*** Pruning Invalid DB Image Record: " << idxRec.filename);
2910 #endif
2911         /* update the study info */
2912         int studyIdx = matchStudyUIDInStudyDesc(pStudyDesc, idxRec.StudyInstanceUID, (int)(handle_->maxStudiesAllowed)) ;
2913         if (studyIdx < handle_->maxStudiesAllowed)
2914         {
2915           if (pStudyDesc[studyIdx].NumberofRegistratedImages > 0)
2916           {
2917             pStudyDesc[studyIdx].NumberofRegistratedImages--;
2918           } else {
2919             pStudyDesc[studyIdx].NumberofRegistratedImages = 0;
2920             pStudyDesc[studyIdx].StudySize = 0;
2921             pStudyDesc[studyIdx].StudyInstanceUID[0] = '\0';
2922           }
2923           if (pStudyDesc[studyIdx].StudySize > idxRec.ImageSize)
2924           {
2925             pStudyDesc[studyIdx].StudySize -= idxRec.ImageSize;
2926           }
2927         }
2928 
2929         /* remove the idx record  */
2930         DB_IdxRemove (idx);
2931       }
2932       idx++;
2933     }
2934 
2935     DB_StudyDescChange (pStudyDesc);
2936     DB_unlock();
2937     free (pStudyDesc) ;
2938     return EC_Normal;
2939 }
2940 
2941 
2942 /* ========================= INDEX ========================= */
2943 
2944 
2945 /************************
2946  *      Dump an index file
2947  */
2948 
printIndexFile(char * storeArea)2949 void DcmQueryRetrieveIndexDatabaseHandle::printIndexFile (char *storeArea)
2950 {
2951     int i ;
2952     int j ;
2953     IdxRecord           idxRec ;
2954     StudyDescRecord     *pStudyDesc;
2955 
2956     OFCondition result;
2957     DcmQueryRetrieveIndexDatabaseHandle handle(storeArea, -1, -1, result);
2958     if (result.bad()) return;
2959 
2960     pStudyDesc = (StudyDescRecord *)malloc (SIZEOF_STUDYDESC) ;
2961     if (pStudyDesc == NULL) {
2962         DCMQRDB_ERROR("printIndexFile: out of memory");
2963         return;
2964     }
2965 
2966     handle.DB_lock(OFFalse);
2967 
2968     handle.DB_GetStudyDesc(pStudyDesc);
2969 
2970     for (i=0; i<handle.handle_->maxStudiesAllowed; i++) {
2971         if (pStudyDesc[i].NumberofRegistratedImages != 0 ) {
2972             COUT << "******************************************************" << OFendl
2973                 << "STUDY DESCRIPTOR: " << i << OFendl
2974                 << "  Study UID: " << pStudyDesc[i].StudyInstanceUID << OFendl
2975                 << "  StudySize: " << pStudyDesc[i].StudySize << OFendl
2976                 << "  LastRecDate: " << pStudyDesc[i].LastRecordedDate << OFendl
2977                 << "  NumOfImages: " << pStudyDesc[i].NumberofRegistratedImages << OFendl;
2978         }
2979     }
2980 
2981     handle.DB_IdxInitLoop (&j) ;
2982     while (1) {
2983         if (handle.DB_IdxGetNext(&j, &idxRec) != EC_Normal)
2984             break ;
2985 
2986         COUT << "*******************************************************" << OFendl;
2987         COUT << "RECORD NUMBER: " << j << OFendl << "  Status: ";
2988         if (idxRec.hstat == DVIF_objectIsNotNew)
2989             COUT << "is NOT new" << OFendl;
2990         else
2991             COUT << "is new" << OFendl;
2992         COUT << "  Filename: " << idxRec.filename << OFendl
2993              << "  ImageSize: " << idxRec.ImageSize << OFendl
2994              << "  RecordedDate: " << idxRec.RecordedDate << OFendl;
2995         for (i = 0 ; i < NBPARAMETERS ; i++) {  /* new definition */
2996             DB_SmallDcmElmt *se = idxRec.param + i;
2997             const char* value = "";
2998             if (se->PValueField != NULL) value = se->PValueField;
2999             DcmTag tag(se->XTag);
3000             COUT << "    " << tag.getTagName() << ": \"" << value << "\"" << OFendl;
3001         }
3002         COUT << "  InstanceDescription: \"" << idxRec.InstanceDescription << "\"" << OFendl;
3003     }
3004     COUT << "*******************************************************" << OFendl
3005          << "RECORDS IN THIS INDEXFILE: " << j << OFendl;
3006 
3007     handle.DB_unlock();
3008 
3009 }
3010 
3011 /************************
3012  *      Search in index file for SOP Class UID and SOP Instance UID. Used for the storage commitment server
3013  */
3014 
findSOPInstance(const char * storeArea,const OFString & sopClassUID,const OFString & sopInstanceUID)3015 OFBool DcmQueryRetrieveIndexDatabaseHandle::findSOPInstance(const char *storeArea, const OFString &sopClassUID,const OFString &sopInstanceUID)
3016 {
3017     int j ;
3018     IdxRecord           idxRec ;
3019 
3020     OFCondition result;
3021     OFBool Found = OFFalse;
3022 
3023     if (sopClassUID.empty() || sopInstanceUID.empty()) return Found;
3024 
3025     DcmQueryRetrieveIndexDatabaseHandle handle(storeArea, -1, -1, result);
3026     if (result.bad()) return Found;
3027 
3028     handle.DB_lock(OFFalse);
3029 
3030     handle.DB_IdxInitLoop (&j) ;
3031     while (1) {
3032         if (handle.DB_IdxGetNext(&j, &idxRec) != EC_Normal)
3033             break ;
3034         if (sopClassUID.compare(idxRec.SOPClassUID)==0 && sopInstanceUID.compare(idxRec.SOPInstanceUID)==0)
3035         {
3036             Found=OFTrue;
3037             break;
3038         }
3039     }
3040     handle.DB_unlock();
3041     return Found;
3042 }
3043 
3044 
3045 /* ========================= UTILS ========================= */
3046 
3047 
getStorageArea() const3048 const char *DcmQueryRetrieveIndexDatabaseHandle::getStorageArea() const
3049 {
3050     return handle_->storageArea;
3051 }
3052 
getIndexFilename() const3053 const char *DcmQueryRetrieveIndexDatabaseHandle::getIndexFilename() const
3054 {
3055     return handle_->indexFilename;
3056 }
3057 
setIdentifierChecking(OFBool checkFind,OFBool checkMove)3058 void DcmQueryRetrieveIndexDatabaseHandle::setIdentifierChecking(OFBool checkFind, OFBool checkMove)
3059 {
3060     doCheckFindIdentifier = checkFind;
3061     doCheckMoveIdentifier = checkMove;
3062 }
3063 
3064 
3065 /***********************
3066  *      Creates a handle
3067  */
3068 
DcmQueryRetrieveIndexDatabaseHandle(const char * storageArea,long maxStudiesPerStorageArea,long maxBytesPerStudy,OFCondition & result)3069 DcmQueryRetrieveIndexDatabaseHandle::DcmQueryRetrieveIndexDatabaseHandle(
3070     const char *storageArea,
3071     long maxStudiesPerStorageArea,
3072     long maxBytesPerStudy,
3073     OFCondition& result)
3074 : handle_(NULL)
3075 , quotaSystemEnabled(OFTrue)
3076 , doCheckFindIdentifier(OFFalse)
3077 , doCheckMoveIdentifier(OFFalse)
3078 , fnamecreator()
3079 {
3080 
3081     handle_ = new DB_Private_Handle;
3082 
3083 #ifdef DEBUG
3084     DCMQRDB_DEBUG("DB_createHandle () : Handle created for " << storageArea);
3085     DCMQRDB_DEBUG("                     maxStudiesPerStorageArea: " << maxStudiesPerStorageArea
3086             << " maxBytesPerStudy: " << maxBytesPerStudy);
3087 #endif
3088 
3089     /* check maximum number of studies for valid value */
3090     if (maxStudiesPerStorageArea < 0) {
3091         maxStudiesPerStorageArea = DB_UpperMaxStudies;
3092     }
3093     else if (maxStudiesPerStorageArea > DB_UpperMaxStudies) {
3094         DCMQRDB_WARN("maxStudiesPerStorageArea too large" << OFendl
3095             << "        setting to " << DB_UpperMaxStudies);
3096         maxStudiesPerStorageArea = DB_UpperMaxStudies;
3097     }
3098     /* check maximum study size for valid value value */
3099     if (maxBytesPerStudy < 0) {
3100         maxBytesPerStudy = DB_UpperMaxBytesPerStudy;
3101     }
3102     else if (maxBytesPerStudy > DB_UpperMaxBytesPerStudy) {
3103         DCMQRDB_WARN("maxBytesPerStudy too large" << OFendl
3104             << "        setting to " << DB_UpperMaxBytesPerStudy);
3105         maxBytesPerStudy = DB_UpperMaxBytesPerStudy;
3106     }
3107 
3108     if (handle_) {
3109         sprintf (handle_ -> storageArea,"%s", storageArea);
3110         sprintf (handle_ -> indexFilename,"%s%c%s", storageArea, PATH_SEPARATOR, DBINDEXFILE);
3111 
3112         /* create index file if it does not already exist */
3113         FILE* f = fopen(handle_->indexFilename, "ab");
3114         if (f == NULL) {
3115             DCMQRDB_ERROR(handle_->indexFilename << ": " << OFStandard::getLastSystemErrorCode().message());
3116             result = QR_EC_IndexDatabaseError;
3117             return;
3118         }
3119         fclose(f);
3120 
3121         /* open fd of index file */
3122 #ifdef O_BINARY
3123         handle_ -> pidx = open(handle_ -> indexFilename, O_RDWR | O_BINARY );
3124 #else
3125         handle_ -> pidx = open(handle_ -> indexFilename, O_RDWR );
3126 #endif
3127         if ( handle_ -> pidx == (-1) )
3128         {
3129            result = QR_EC_IndexDatabaseError;
3130            return;
3131         }
3132         else
3133         {
3134             result = DB_lock(OFTrue);
3135             if ( result.bad() )
3136                 return;
3137 
3138             // test whether the file contains more than zero bytes
3139             if ( DB_lseek( handle_ -> pidx, 0L, SEEK_END ) > 0 )
3140             {
3141                 DB_lseek( handle_ -> pidx, 0L, SEEK_SET );
3142                 // allocate HEADERSIZE + 1 bytes and fill it with zeros,
3143                 // ensuring whatever is read is terminated with a NUL byte
3144                 char header[DBHEADERSIZE+1] = {};
3145                 // 0 is an invalid version, no matter what
3146                 unsigned int version = 0;
3147                 if
3148                 (
3149                     read( handle_ -> pidx, header, DBHEADERSIZE ) != DBHEADERSIZE ||
3150                     strncmp( header, DBMAGIC, strlen(DBMAGIC) ) != 0              ||
3151                     sscanf( header + strlen(DBMAGIC), "%x", &version ) != 1       ||
3152                     version != DBVERSION
3153                 )
3154                 {
3155                     DB_unlock();
3156                     if ( version )
3157                         DCMQRDB_ERROR(handle_->indexFilename << ": invalid/unsupported QRDB database version " << version);
3158                     else
3159                         DCMQRDB_ERROR(handle_->indexFilename << ": unknown/legacy QRDB database file format");
3160                     result = QR_EC_IndexDatabaseError;
3161                     return;
3162                 }
3163             }
3164             else
3165             {
3166                 // write magic word and version number to the buffer
3167                 // then write it to the file
3168                 char header[DBHEADERSIZE + 1];
3169                 sprintf( header, DBMAGIC "%.2X", DBVERSION );
3170                 if ( write( handle_ -> pidx, header, DBHEADERSIZE ) != DBHEADERSIZE )
3171                 {
3172                     DCMQRDB_ERROR(handle_->indexFilename << ": " << OFStandard::getLastSystemErrorCode().message());
3173                     DB_unlock();
3174                     result = QR_EC_IndexDatabaseError;
3175                     return;
3176                 }
3177             }
3178 
3179             DB_unlock();
3180 
3181             handle_ -> idxCounter = -1;
3182             handle_ -> findRequestList = NULL;
3183             handle_ -> findResponseList = NULL;
3184             handle_ -> maxBytesPerStudy = maxBytesPerStudy;
3185             handle_ -> maxStudiesAllowed = maxStudiesPerStorageArea;
3186             handle_ -> uidList = NULL;
3187             result = EC_Normal;
3188             return;
3189         }
3190     }
3191     else
3192     {
3193         result = QR_EC_IndexDatabaseError;
3194         return;
3195     }
3196 }
3197 
3198 /***********************
3199  *      Destroys a handle
3200  */
3201 
~DcmQueryRetrieveIndexDatabaseHandle()3202 DcmQueryRetrieveIndexDatabaseHandle::~DcmQueryRetrieveIndexDatabaseHandle()
3203 {
3204     if (handle_)
3205     {
3206 #ifndef _WIN32
3207       /* should not be necessary because we are closing the file handle anyway.
3208        * On Unix systems this does no harm, but on Windows the unlock fails
3209        * if the file was not locked before
3210        * and this gives an unnecessary error message on stderr.
3211        */
3212       DB_unlock();
3213 #endif
3214       close( handle_ -> pidx);
3215 
3216       /* Free lists */
3217       DB_FreeElementList (handle_ -> findRequestList);
3218       DB_FreeElementList (handle_ -> findResponseList);
3219       DB_FreeUidList (handle_ -> uidList);
3220 
3221       delete handle_;
3222     }
3223 }
3224 
3225 /**********************************
3226  *      Provides a storage filename
3227  */
3228 
makeNewStoreFileName(const char * SOPClassUID,const char *,char * newImageFileName,size_t newImageFileNameLen)3229 OFCondition DcmQueryRetrieveIndexDatabaseHandle::makeNewStoreFileName(
3230                 const char      *SOPClassUID,
3231                 const char      * /* SOPInstanceUID */ ,
3232                 char            *newImageFileName,
3233                 size_t          newImageFileNameLen)
3234 {
3235 
3236     OFString filename;
3237     char prefix[12];
3238 
3239     const char *m = dcmSOPClassUIDToModality(SOPClassUID);
3240     if (m==NULL) m = "XX";
3241     sprintf(prefix, "%s_", m);
3242     // unsigned int seed = fnamecreator.hashString(SOPInstanceUID);
3243     unsigned int seed = (unsigned int)time(NULL);
3244     newImageFileName[0]=0; // return empty string in case of error
3245     if (! fnamecreator.makeFilename(seed, handle_->storageArea, prefix, ".dcm", filename))
3246         return QR_EC_IndexDatabaseError;
3247 
3248     OFStandard::strlcpy(newImageFileName, filename.c_str(), newImageFileNameLen);
3249     return EC_Normal;
3250 }
3251 
3252 
instanceReviewed(int idx)3253 OFCondition DcmQueryRetrieveIndexDatabaseHandle::instanceReviewed(int idx)
3254 {
3255     // acquire shared lock and read record at index position
3256     OFCondition result = DB_lock(OFFalse);
3257     if (result.bad()) return result;
3258     IdxRecord record;
3259     result = DB_IdxRead(idx, &record);
3260     DB_unlock();
3261 
3262     if (result.good() && (record.hstat != DVIF_objectIsNotNew))
3263     {
3264       // acquire exclusive lock and update flag
3265       result = DB_lock(OFTrue);
3266       if (result.bad()) return result;
3267 
3268       record.hstat = DVIF_objectIsNotNew;
3269       DB_lseek(handle_->pidx, OFstatic_cast(long, DBHEADERSIZE + SIZEOF_STUDYDESC + idx * SIZEOF_IDXRECORD), SEEK_SET);
3270       if (write(handle_->pidx, OFreinterpret_cast(char *, &record), SIZEOF_IDXRECORD) != SIZEOF_IDXRECORD)
3271           result = QR_EC_IndexDatabaseError;
3272       DB_lseek(handle_->pidx, OFstatic_cast(long, DBHEADERSIZE), SEEK_SET);
3273       DB_unlock();
3274     }
3275 
3276     return result;
3277 }
3278 
3279 
3280 /***********************
3281  *    Default constructors for struct IdxRecord and DB_SSmallDcmElmt
3282  */
3283 
IdxRecord()3284 IdxRecord::IdxRecord()
3285 : RecordedDate(0.0)
3286 , ImageSize(0)
3287 , hstat(DVIF_objectIsNotNew)
3288 {
3289 }
3290 
DB_SmallDcmElmt()3291 DB_SmallDcmElmt::DB_SmallDcmElmt()
3292 : PValueField(NULL)
3293 , ValueLength(0)
3294 , XTag()
3295 {
3296 }
3297 
3298 
DcmQueryRetrieveIndexDatabaseHandleFactory(const DcmQueryRetrieveConfig * config)3299 DcmQueryRetrieveIndexDatabaseHandleFactory::DcmQueryRetrieveIndexDatabaseHandleFactory(const DcmQueryRetrieveConfig *config)
3300 : DcmQueryRetrieveDatabaseHandleFactory()
3301 , config_(config)
3302 {
3303 }
3304 
~DcmQueryRetrieveIndexDatabaseHandleFactory()3305 DcmQueryRetrieveIndexDatabaseHandleFactory::~DcmQueryRetrieveIndexDatabaseHandleFactory()
3306 {
3307 }
3308 
createDBHandle(const char *,const char * calledAETitle,OFCondition & result) const3309 DcmQueryRetrieveDatabaseHandle *DcmQueryRetrieveIndexDatabaseHandleFactory::createDBHandle(
3310     const char * /* callingAETitle */,
3311     const char *calledAETitle,
3312     OFCondition& result) const
3313 {
3314   return new DcmQueryRetrieveIndexDatabaseHandle(
3315     config_->getStorageArea(calledAETitle),
3316     config_->getMaxStudies(calledAETitle),
3317     config_->getMaxBytesPerStudy(calledAETitle), result);
3318 }
3319