1 /*
2  *
3  *  Copyright (C) 1998-2020, 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:  dcmpstat
15  *
16  *  Author:  Joerg Riesmeier, Marco Eichelberg
17  *
18  *  Purpose: DVPresentationState
19  *
20  */
21 
22 
23 #include "dcmtk/config/osconfig.h"    /* make sure OS specific configuration is included first */
24 
25 #include "dcmtk/dcmpstat/dviface.h"
26 
27 #include "dcmtk/dcmpstat/dvpsdef.h"   /* for constants */
28 #include "dcmtk/ofstd/ofstring.h"     /* for class OFString */
29 #include "dcmtk/ofstd/ofbmanip.h"     /* for OFBitmanipTemplate */
30 #include "dcmtk/ofstd/ofdatime.h"     /* for OFDateTime */
31 #include "dcmtk/ofstd/oflist.h"       /* for class OFList */
32 #include "dcmtk/ofstd/ofstream.h"
33 #include "dcmtk/ofstd/ofcast.h"
34 
35 #include "dcmtk/dcmimgle/digsdfn.h"   /* for DiGSDFunction */
36 #include "dcmtk/dcmimgle/diciefn.h"   /* for DiCIELABFunction */
37 #include "dcmtk/dcmnet/diutil.h"      /* for DU_getStringDOElement */
38 #include "dcmtk/dcmpstat/dvpssp.h"    /* for class DVPSStoredPrint */
39 #include "dcmtk/dcmpstat/dvpshlp.h"   /* for class DVPSHelper */
40 #include "dcmtk/dcmimgle/dcmimage.h"  /* for class DicomImage */
41 #include "dcmtk/dcmpstat/dvsighdl.h"  /* for class DVSignatureHandler */
42 #include "dcmtk/dcmsign/dcsignat.h"   /* for class DcmSignature */
43 #include "dcmtk/dcmsr/dsrdoc.h"       /* for class DSRDocument */
44 #include "dcmtk/dcmsr/dsrcodvl.h"     /* for class DSRCodedEntryValue */
45 #include "dcmtk/oflog/fileap.h"       /* for dcmtk::log4cplus::FileAppender */
46 
47 #include "dcmtk/dcmpstat/dvpsib.h"    /* for DVPSImageBoxContent, needed by MSVC5 with STL */
48 #include "dcmtk/dcmpstat/dvpsab.h"    /* for DVPSAnnotationContent, needed by MSVC5 with STL */
49 #include "dcmtk/dcmpstat/dvpsov.h"    /* for DVPSOverlay, needed by MSVC5 with STL */
50 #include "dcmtk/dcmpstat/dvpsgl.h"    /* for DVPSGraphicLayer, needed by MSVC5 with STL */
51 #include "dcmtk/dcmpstat/dvpsal.h"    /* for DVPSOverlayCurveActivationLayer, needed by MSVC5 with STL */
52 #include "dcmtk/dcmpstat/dvpsga.h"    /* for DVPSGraphicAnnotation, needed by MSVC5 with STL */
53 #include "dcmtk/dcmpstat/dvpscu.h"    /* for DVPSCurve, needed by MSVC5 with STL */
54 #include "dcmtk/dcmpstat/dvpsvl.h"    /* for DVPSVOILUT, needed by MSVC5 with STL */
55 #include "dcmtk/dcmpstat/dvpsvw.h"    /* for DVPSVOIWindow, needed by MSVC5 with STL */
56 #include "dcmtk/dcmpstat/dvpsda.h"    /* for DVPSDisplayedArea, needed by MSVC5 with STL */
57 #include "dcmtk/dcmpstat/dvpssv.h"    /* for DVPSSoftcopyVOI, needed by MSVC5 with STL */
58 #include "dcmtk/dcmpstat/dvpsrs.h"    /* for DVPSReferencedSeries, needed by MSVC5 with STL */
59 #include "dcmtk/dcmpstat/dvpstx.h"    /* for DVPSTextObject, needed by MSVC5 with STL */
60 #include "dcmtk/dcmpstat/dvpsgr.h"    /* for DVPSGraphicObject, needed by MSVC5 with STL */
61 #include "dcmtk/dcmpstat/dvpsri.h"    /* for DVPSReferencedImage, needed by MSVC5 with STL */
62 #include "dcmtk/dcmqrdb/dcmqrdbi.h"   /* for DB_UpperMaxBytesPerStudy */
63 #include "dcmtk/dcmqrdb/dcmqrdbs.h"   /* for DcmQueryRetrieveDatabaseStatus */
64 
65 #define INCLUDE_CSTDIO
66 #define INCLUDE_CCTYPE
67 #define INCLUDE_CMATH
68 #define INCLUDE_UNISTD
69 #include "dcmtk/ofstd/ofstdinc.h"
70 
71 BEGIN_EXTERN_C
72 #ifdef HAVE_SYS_TYPES_H
73 #include <sys/types.h>   /* for fork */
74 #endif
75 #ifdef HAVE_SYS_WAIT_H
76 #include <sys/wait.h>    /* for waitpid */
77 #endif
78 #ifdef HAVE_SYS_TIME_H
79 #include <sys/time.h>    /* for wait3 */
80 #endif
81 #ifdef HAVE_SYS_RESOURCE_H
82 #include <sys/resource.h> /* for wait3 */
83 #endif
84 #ifdef HAVE_SYS_STAT_H
85 #include <sys/stat.h>    /* for stat, fstat */
86 #endif
87 #ifdef HAVE_SYS_UTIME_H
88 #include <sys/utime.h>   /* for utime */
89 #endif
90 #ifdef HAVE_UTIME_H
91 #include <utime.h>       /* for utime */
92 #endif
93 END_EXTERN_C
94 
95 #ifdef HAVE_WINDOWS_H
96 #include <winbase.h>     /* for CreateProcess */
97 #endif
98 
99 #ifdef WITH_OPENSSL
100 #include "dcmtk/dcmtls/tlstrans.h"
101 #include "dcmtk/dcmtls/tlslayer.h"
102 
103 BEGIN_EXTERN_C
104 #include <openssl/evp.h>
105 #include <openssl/x509.h>
106 #include <openssl/pem.h>
107 #include <openssl/err.h>
108 #include <openssl/ssl.h>
109 END_EXTERN_C
110 #endif
111 
112 
DVInterface(const char * config_file,OFBool useLog)113 DVInterface::DVInterface(const char *config_file, OFBool useLog)
114 : DVConfiguration(config_file)
115 , pPrint(NULL)
116 , pState(NULL)
117 , pReport(NULL)
118 , pSignatureHandler(NULL)
119 , pStoredPState(NULL)
120 , pDicomImage(NULL)
121 , pDicomPState(NULL)
122 , pHardcopyImage(NULL)
123 , printJobIdentifier()
124 , printJobCounter(0)
125 , configPath()
126 , databaseIndexFile()
127 , referenceTime(0)
128 , pHandle(NULL)
129 , lockingMode(OFFalse)
130 , idxCache()
131 , idxRec()
132 , idxRecPos(-1)
133 , imageInDatabase(OFFalse)
134 , minimumPrintBitmapWidth(0)
135 , minimumPrintBitmapHeight(0)
136 , maximumPrintBitmapWidth(0)
137 , maximumPrintBitmapHeight(0)
138 , maximumPrintPreviewWidth(0)
139 , maximumPrintPreviewHeight(0)
140 , maximumPreviewImageWidth(0)
141 , maximumPreviewImageHeight(0)
142 , currentPrinter()
143 , displayCurrentLUTID()
144 , printCurrentLUTID()
145 , printerMediumType()
146 , printerFilmDestination()
147 , printerFilmSessionLabel()
148 , printerNumberOfCopies(0)
149 , printerPriority()
150 , printerOwnerID()
151 , activateAnnotation(OFFalse)
152 , prependDateTime(OFTrue)
153 , prependPrinterName(OFTrue)
154 , prependLighting(OFTrue)
155 , annotationText()
156 {
157 #ifdef WITH_OPENSSL
158     DcmSignature::initializeLibrary(); // initializes OpenSSL for dcmsign and dcmtls
159 #endif
160 
161     clearIndexRecord(idxRec, idxRecPos);
162     if (config_file) configPath = config_file;
163 
164     /* initialize display transform (only on low-cost systems) */
165     for (int i = DVPSD_first; i < DVPSD_max;i++)
166         displayFunction[i] = NULL;
167     if (!getGUIConfigEntryBool(L2_HIGHRESOLUTIONGRAPHICS, OFFalse))
168     {
169         const char *displayFunctionFile = getMonitorCharacteristicsFile();
170         if (displayFunctionFile && (strlen(displayFunctionFile) > 0))
171         {
172             DiDisplayFunction *df = new DiGSDFunction(displayFunctionFile);
173             if (df && (df->isValid()))
174             {
175                 displayFunction[DVPSD_GSDF] = df;
176                 df = new DiCIELABFunction(displayFunctionFile);
177                 if (df && (df->isValid()))
178                     displayFunction[DVPSD_CIELAB] = df;
179             }
180             else
181             {
182                 if (df) delete df;
183                 DCMPSTAT_WARN("Unable to load monitor characterics file '" << displayFunctionFile << "', ignoring");
184             }
185         }
186     }
187 
188     minimumPrintBitmapWidth   = getMinPrintResolutionX();
189     minimumPrintBitmapHeight  = getMinPrintResolutionY();
190     maximumPrintBitmapWidth   = getMaxPrintResolutionX();
191     maximumPrintBitmapHeight  = getMaxPrintResolutionY();
192     maximumPreviewImageWidth  = getMaxPreviewResolutionX();
193     maximumPreviewImageHeight = getMaxPreviewResolutionY();
194 
195     pPrint = new DVPSStoredPrint(getDefaultPrintIllumination(), getDefaultPrintReflection(), getNetworkAETitle());
196     pState = new DVPresentationState(OFstatic_cast(DiDisplayFunction **, displayFunction),
197       minimumPrintBitmapWidth, minimumPrintBitmapHeight, maximumPrintBitmapWidth, maximumPrintBitmapHeight,
198       maximumPreviewImageWidth, maximumPreviewImageHeight);
199     pReport = new DSRDocument();
200     pSignatureHandler = new DVSignatureHandler(*this);
201 
202     referenceTime = OFstatic_cast(unsigned long, time(NULL));
203     /* initialize printJobIdentifier with a string comprising the current time */
204     char buf[20];
205     sprintf(buf, "%lu", referenceTime);
206     printJobIdentifier = buf;
207     /* initialize reference time with "yesterday" */
208     if (referenceTime >= 86400) referenceTime -= 86400; // subtract one day
209     setCurrentPrinter(getTargetID(0, DVPSE_printAny));
210 
211     if (useLog)
212     {
213         const char *filename = getLogFile();
214         if (filename != NULL)
215         {
216             const char *directory = getLogFolder();
217             OFString filepath;
218             if (directory != NULL)
219             {
220                 filepath = directory;
221                 filepath += PATH_SEPARATOR;
222                 filepath += filename;
223             } else
224                 filepath = filename;
225 
226             // This will badly interact with oflog config files :(
227             const char *pattern = "%D, Level %p, Module DCMPSTAT%n%m%n";
228             OFunique_ptr<dcmtk::log4cplus::Layout> layout(new dcmtk::log4cplus::PatternLayout(pattern));
229             dcmtk::log4cplus::SharedAppenderPtr logfile(new dcmtk::log4cplus::FileAppender(filepath, STD_NAMESPACE ios::app));
230             // We can't use OFLog::getLogger() here because that doesn't let us
231             // configure the object
232             dcmtk::log4cplus::Logger log = dcmtk::log4cplus::Logger::getInstance("dcmtk.dcmpstat.logfile");
233 
234             logfile->setLayout(OFmove(layout));
235             log.addAppender(logfile);
236             log.setLogLevel(getLogLevel());
237 
238             // All log messages to the logger "dcmtk.dcmpstat" are now written
239             // to the log file <filepath>
240         }
241     }
242 
243     DCMPSTAT_LOGFILE("---------------------------\n--- Application started ---\n---------------------------");
244 }
245 
246 
~DVInterface()247 DVInterface::~DVInterface()
248 {
249     DCMPSTAT_INFO("Application terminated");
250     delete pPrint;
251     delete pState;
252     delete pReport;
253     delete pSignatureHandler;
254     delete pStoredPState;
255     delete pDicomImage;
256     delete pDicomPState;
257     delete pHardcopyImage;
258     for (int i = DVPSD_first; i < DVPSD_max; i++)
259       delete displayFunction[i];
260     if (pHandle) releaseDatabase();
261     // refresh database index file access time
262     if (!databaseIndexFile.empty())
263         // cast to char* required for gcc 2.5.8 on NeXTSTEP
264         utime(OFconst_cast(char *, databaseIndexFile.c_str()), NULL);
265 }
266 
267 
loadImage(const char * studyUID,const char * seriesUID,const char * instanceUID,OFBool changeStatus)268 OFCondition DVInterface::loadImage(const char *studyUID,
269                                    const char *seriesUID,
270                                    const char *instanceUID,
271                                    OFBool changeStatus)
272 {
273     OFCondition status = EC_IllegalCall;
274     if (studyUID && seriesUID && instanceUID)
275     {
276         if (lockDatabase() == EC_Normal)
277         {
278             const char *filename = getFilename(studyUID, seriesUID, instanceUID);
279             if (filename)
280             {
281                 if ((status = loadImage(filename)) == EC_Normal)
282                 {
283                     imageInDatabase = OFTrue;
284                     if (changeStatus)
285                         instanceReviewed(studyUID, seriesUID, instanceUID);
286                 }
287             } else
288                 DCMPSTAT_LOGFILE("Load image from database failed: UIDs not in index file");
289         } else
290             DCMPSTAT_LOGFILE("Load image from database failed: could not lock index file");
291     } else
292         DCMPSTAT_LOGFILE("Load image from database failed: invalid UIDs");
293    return status;
294 }
295 
296 
loadImage(const char * imgName)297 OFCondition DVInterface::loadImage(const char *imgName)
298 {
299     OFCondition status = EC_IllegalCall;
300     DcmFileFormat *image = NULL;
301     DVPresentationState *newState = new DVPresentationState(OFstatic_cast(DiDisplayFunction **, displayFunction),
302       minimumPrintBitmapWidth, minimumPrintBitmapHeight, maximumPrintBitmapWidth, maximumPrintBitmapHeight,
303       maximumPreviewImageWidth, maximumPreviewImageHeight);
304     if (newState==NULL)
305     {
306         DCMPSTAT_LOGFILE("Load image from file failed: memory exhausted");
307         return EC_MemoryExhausted;
308     }
309 
310     if ((status = DVPSHelper::loadFileFormat(imgName, image)) == EC_Normal)
311     {
312         if (image)
313         {
314             DcmDataset *dataset = image->getDataset();
315             if (dataset)
316             {
317               if (EC_Normal == (status = newState->createFromImage(*dataset)))
318                 status = newState->attachImage(image, OFFalse);
319               if (EC_Normal == status)
320               {
321                 exchangeImageAndPState(newState, image);
322                 imageInDatabase = OFFalse;
323               }
324             } else status = EC_CorruptedData;
325         } else status = EC_IllegalCall;
326         if (status != EC_Normal)
327             DCMPSTAT_LOGFILE("Load image from file failed: invalid data structures");
328     } else
329        DCMPSTAT_LOGFILE("Load image from file failed: could not read fileformat");
330     if (status != EC_Normal)
331     {
332         delete newState;
333         delete image;
334     }
335     return status;
336 }
337 
338 
loadReferencedImage(size_t idx,OFBool changeStatus)339 OFCondition DVInterface::loadReferencedImage(size_t idx, OFBool changeStatus)
340 {
341     OFCondition status = EC_IllegalCall;
342     if ((pState != NULL) && (idx < pState->numberOfImageReferences()))
343     {
344         OFString ofstudyUID, ofseriesUID, ofsopclassUID, ofinstanceUID, offrames, aetitle, mediaID, mediaUID;
345         if ((status = pState->getImageReference(idx, ofstudyUID, ofseriesUID, ofsopclassUID, ofinstanceUID,
346             offrames, aetitle, mediaID, mediaUID)) == EC_Normal)
347         {
348             if (lockDatabase() == EC_Normal)
349             {
350                 const char *filename = getFilename(ofstudyUID.c_str(), ofseriesUID.c_str(), ofinstanceUID.c_str());
351                 if (filename != NULL)
352                 {
353                     DcmFileFormat *image = NULL;
354                     if ((status = DVPSHelper::loadFileFormat(filename, image)) == EC_Normal)
355                     {
356                         status = EC_IllegalCall;
357                         if (image != NULL)
358                         {
359                             DcmDataset *dataset = image->getDataset();
360                             if (dataset != NULL)
361                             {
362                                 if ((status = pState->attachImage(image, OFFalse)) == EC_Normal)
363                                 {
364                                     exchangeImageAndPState(pState, image);      // do not exchange pState
365                                     imageInDatabase = OFTrue;
366                                     if (changeStatus)
367                                         instanceReviewed(ofstudyUID.c_str(), ofseriesUID.c_str(), ofinstanceUID.c_str());
368                                 }
369                             }
370                         }
371                         if (status != EC_Normal)
372                             DCMPSTAT_LOGFILE("Load referenced image from file failed: invalid data structures");
373                     } else {
374                         status = EC_CorruptedData;
375                         DCMPSTAT_LOGFILE("Load referenced image from file failed: could not read fileformat");
376                     }
377                     if (status != EC_Normal)
378                         delete image;
379                 } else {
380                     status = EC_IllegalCall;
381                     DCMPSTAT_LOGFILE("Load referenced image from database failed: UIDs not in index file");
382                 }
383             } else
384                 DCMPSTAT_LOGFILE("Load referenced image from database failed: could not lock index file");
385         } else
386             DCMPSTAT_LOGFILE("Load referenced image from database failed: internal error");
387     } else
388         DCMPSTAT_LOGFILE("Load referenced image from database failed: internal error");
389     return status;
390 }
391 
392 
loadPState(const char * studyUID,const char * seriesUID,const char * instanceUID,OFBool changeStatus)393 OFCondition DVInterface::loadPState(const char *studyUID,
394                                     const char *seriesUID,
395                                     const char *instanceUID,
396                                     OFBool changeStatus)
397 {
398     // determine the filename of the presentation state
399     OFCondition status = lockDatabase();
400     if (status != EC_Normal)
401     {
402         DCMPSTAT_LOGFILE("Load presentation state from database failed: could not lock index file");
403         return status;
404     }
405     const char *filename = getFilename(studyUID, seriesUID, instanceUID);
406     if (filename==NULL)
407     {
408         DCMPSTAT_LOGFILE("Load presentation state from database failed: UIDs not in index file");
409         return EC_IllegalCall;
410     }
411 
412     // load the presentation state
413     DcmFileFormat *pstate = NULL;
414     DVPresentationState *newState = new DVPresentationState(OFstatic_cast(DiDisplayFunction **, displayFunction),
415       minimumPrintBitmapWidth, minimumPrintBitmapHeight, maximumPrintBitmapWidth, maximumPrintBitmapHeight,
416       maximumPreviewImageWidth, maximumPreviewImageHeight);
417     if (newState==NULL)
418     {
419         DCMPSTAT_LOGFILE("Load presentation state from database failed: memory exhausted");
420         return EC_MemoryExhausted;
421     }
422 
423     if ((EC_Normal == (status = DVPSHelper::loadFileFormat(filename, pstate)))&&(pstate))
424     {
425         DcmDataset *dataset = pstate->getDataset();
426         if (dataset) status = newState->read(*dataset); else status = EC_CorruptedData;
427     }
428     if (status == EC_Normal)
429     {
430         // access the first image reference in the presentation state
431         OFString ofstudyUID, ofseriesUID, ofsopclassUID, ofinstanceUID, offrames, aetitle, mediaID, mediaUID;
432         status = newState->getImageReference(0, ofstudyUID, ofseriesUID, ofsopclassUID, ofinstanceUID,
433           offrames, aetitle, mediaID, mediaUID);
434 
435         // we could do something fancy with the retrieve AE title and the storage media ID here,
436         // but we just assume that the referenced image is in our local database.
437         if (EC_Normal == status)
438         {
439             // determine the filename of the referenced image
440             const char *imagefilename = NULL;
441             imagefilename = getFilename(ofstudyUID.c_str(), ofseriesUID.c_str(), ofinstanceUID.c_str());
442 
443             // load the image file and attach it
444             if (imagefilename)
445             {
446                 DcmFileFormat *fimage = NULL;
447                 if ((EC_Normal == (status = DVPSHelper::loadFileFormat(imagefilename, fimage)))&&(fimage))
448                 {
449                     DcmDataset *dataset = pstate->getDataset();
450                     if (dataset)
451                     {
452                         status = newState->attachImage(fimage, OFFalse);
453                         if (EC_Normal == status)
454                         {
455                           exchangeImageAndPState(newState, fimage, pstate);
456                           imageInDatabase = OFTrue;
457                           if (changeStatus)
458                           {
459                               // mark pstate and (first) image reviewed
460                               instanceReviewed(studyUID, seriesUID, instanceUID);
461                               instanceReviewed(ofstudyUID.c_str(), ofseriesUID.c_str(), ofinstanceUID.c_str());
462                           }
463                         }
464                     } else status = EC_CorruptedData;
465                 }
466                 if (status!=EC_Normal)
467                 {
468                     delete fimage;
469                     DCMPSTAT_LOGFILE("Load presentation state from database failed: could not read image data");
470                 }
471             } else {
472                 status = EC_IllegalCall;     // no valid image filename
473                 DCMPSTAT_LOGFILE("Load presentation state from database failed: referenced image not in index file");
474             }
475         }
476     } else
477         DCMPSTAT_LOGFILE("Load presentation state from database failed: could not read fileformat");
478     if (status!=EC_Normal)
479     {
480         delete pstate;
481         delete newState;
482     }
483     return status;
484 }
485 
486 
loadPState(const char * pstName,const char * imgName)487 OFCondition DVInterface::loadPState(const char *pstName,
488                                     const char *imgName)
489 {
490     OFCondition status = EC_IllegalCall;
491     DcmFileFormat *pstate = NULL;
492     DcmFileFormat *image = pDicomImage;     // default: do not replace image if image filename is NULL
493     DVPresentationState *newState = new DVPresentationState(OFstatic_cast(DiDisplayFunction **, displayFunction),
494       minimumPrintBitmapWidth, minimumPrintBitmapHeight, maximumPrintBitmapWidth, maximumPrintBitmapHeight,
495       maximumPreviewImageWidth, maximumPreviewImageHeight);
496     if (newState==NULL)
497     {
498         DCMPSTAT_LOGFILE("Load presentation state from file failed: memory exhausted");
499         return EC_MemoryExhausted;
500     }
501 
502     if ((status = DVPSHelper::loadFileFormat(pstName, pstate)) == EC_Normal)
503     {
504         if ((imgName == NULL) || ((status = DVPSHelper::loadFileFormat(imgName, image)) == EC_Normal))
505         {
506             if ((pstate)&&(image))
507             {
508                 DcmDataset *dataset = pstate->getDataset();
509                 if (dataset)
510                 {
511                     if (EC_Normal == (status = newState->read(*dataset)))
512                         status = newState->attachImage(image, OFFalse);
513                     if (EC_Normal == status)
514                     {
515                         exchangeImageAndPState(newState, image, pstate);
516                         imageInDatabase = OFFalse;
517                     }
518                 } else status = EC_CorruptedData;
519             } else status = EC_IllegalCall;
520             if (status != EC_Normal)
521                 DCMPSTAT_LOGFILE("Load presentation state from file failed: invalid data structures");
522         } else
523             DCMPSTAT_LOGFILE("Load presentation state from file failed: could not load image");
524     } else
525         DCMPSTAT_LOGFILE("Load presentation state from file failed: could not read fileformat");
526     if (status != EC_Normal)
527     {
528         delete newState;
529         if (image != pDicomImage)
530             delete image;
531         delete pstate;
532     }
533     return status;
534 }
535 
536 
loadStructuredReport(const char * studyUID,const char * seriesUID,const char * instanceUID,OFBool changeStatus)537 OFCondition DVInterface::loadStructuredReport(const char *studyUID,
538                                               const char *seriesUID,
539                                               const char *instanceUID,
540                                               OFBool changeStatus)
541 {
542     OFCondition status = EC_IllegalCall;
543     if (studyUID && seriesUID && instanceUID)
544     {
545         if (lockDatabase() == EC_Normal)
546         {
547             const char *filename = getFilename(studyUID, seriesUID, instanceUID);
548             if (filename)
549             {
550                 if ((status = loadStructuredReport(filename)) == EC_Normal)
551                 {
552                     if (changeStatus)
553                         instanceReviewed(studyUID, seriesUID, instanceUID);
554                 }
555             } else
556                 DCMPSTAT_LOGFILE("Load structured report from database failed: UIDs not in index file");
557         } else
558             DCMPSTAT_LOGFILE("Load structured report from database failed: could not lock index file");
559     } else
560         DCMPSTAT_LOGFILE("Load structured report from database failed: invalid UIDs");
561    return status;
562 }
563 
564 
loadStructuredReport(const char * filename)565 OFCondition DVInterface::loadStructuredReport(const char *filename)
566 {
567     OFCondition status = EC_IllegalCall;
568     DcmFileFormat *fileformat = NULL;
569     DSRDocument *newReport = new DSRDocument();
570     if (newReport == NULL)
571     {
572         DCMPSTAT_LOGFILE("Load structured report from file failed: memory exhausted");
573         return EC_MemoryExhausted;
574     }
575 
576     if ((status = DVPSHelper::loadFileFormat(filename, fileformat)) == EC_Normal)
577     {
578         if (fileformat)
579         {
580             DcmDataset *dataset = fileformat->getDataset();
581             if (dataset)
582             {
583                 if ((status = newReport->read(*dataset, DSRTypes::RF_readDigitalSignatures)) == EC_Normal)
584                 {
585                     delete pReport;
586                     pReport = newReport;
587                     if (pSignatureHandler)
588                     {
589                         pSignatureHandler->updateDigitalSignatureInformation(*dataset, DVPSS_structuredReport, OFTrue);
590                         /* check whether loaded report is 'finalized' (certain attributes are digitally signed) */
591                         DcmAttributeTag tagList(DcmTag(0, 0) /* irrelevant value */);
592                         tagList.putTagVal(DCM_SOPInstanceUID, 0);
593                         tagList.putTagVal(DCM_VerifyingObserverSequence, 1);
594                         tagList.putTagVal(DCM_InstanceCreationDate, 2);
595                         tagList.putTagVal(DCM_InstanceCreationTime, 3);
596                         tagList.putTagVal(DCM_InstanceCreatorUID, 4);
597                         if (pSignatureHandler->attributesSigned(*dataset, tagList))
598                             pReport->finalizeDocument();
599                     }
600                 }
601             } else
602                 status = EC_CorruptedData;
603         } else
604             status = EC_IllegalCall;
605         if (status != EC_Normal)
606             DCMPSTAT_LOGFILE("Load structured report from file failed: invalid data structures");
607     } else
608        DCMPSTAT_LOGFILE("Load structured report from file failed: could not read fileformat");
609     if (status != EC_Normal)
610         delete newReport;
611     delete fileformat;
612     return status;
613 }
614 
615 
loadSRTemplate(const char * reportID)616 OFCondition DVInterface::loadSRTemplate(const char *reportID)
617 {
618   OFCondition result = EC_IllegalCall;
619   if (reportID)
620   {
621      const char *srfile = getReportFilename(reportID);
622      if (srfile)
623      {
624        OFString filename = getReportFolder(); // never NULL.
625        filename += PATH_SEPARATOR;
626        filename += srfile;
627        result = loadStructuredReport(filename.c_str());
628        if (result == EC_Normal)
629        {
630          if (pReport != NULL)
631          {
632            /* date/time is filled automatically if empty */
633            pReport->setContentDate("");
634            pReport->setContentTime("");
635            /* generate new study/series/instance UID and fill date/time */
636            pReport->createNewStudy();
637          }
638        } else
639          DCMPSTAT_LOGFILE("Load structured reporting 'template' from file failed");
640      }
641   }
642   return result;
643 }
644 
645 
savePState(OFBool replaceSOPInstanceUID)646 OFCondition DVInterface::savePState(OFBool replaceSOPInstanceUID)
647 {
648     // release database lock since we are using the DB module directly
649     releaseDatabase();
650 
651     if (pState==NULL) return EC_IllegalCall;
652     const char *instanceUID = NULL;
653     if (replaceSOPInstanceUID) instanceUID=pState->createInstanceUID(); else instanceUID=pState->getInstanceUID();
654     if (instanceUID==NULL) return EC_IllegalCall;
655 
656     DcmQueryRetrieveDatabaseStatus dbStatus(STATUS_Success);
657     OFCondition result=EC_Normal;
658     char imageFileName[MAXPATHLEN+1];
659 
660     DcmQueryRetrieveIndexDatabaseHandle dbhandle(getDatabaseFolder(), PSTAT_MAXSTUDYCOUNT, PSTAT_STUDYSIZE, result);
661     if (result.bad())
662     {
663         DCMPSTAT_LOGFILE("Save presentation state to database failed: could not lock index file");
664         return EC_IllegalCall;
665     }
666 
667     if (dbhandle.makeNewStoreFileName(UID_GrayscaleSoftcopyPresentationStateStorage, instanceUID, imageFileName, sizeof(imageFileName)).good())
668     {
669         // now store presentation state as filename
670         result = savePState(imageFileName, OFFalse);
671         if (EC_Normal==result)
672         {
673             if (dbhandle.storeRequest(UID_GrayscaleSoftcopyPresentationStateStorage,
674                 instanceUID, imageFileName, &dbStatus).bad())
675             {
676                 result = EC_IllegalCall;
677                 DCMPSTAT_LOGFILE("Save presentation state to database failed: could not register in index file");
678                 DCMPSTAT_WARN("Unable to register presentation state '" << imageFileName << "' in database");
679             }
680         }
681     }
682     if (pDicomImage != NULL)
683     {
684         DcmDataset *dset = pDicomImage->getDataset();
685         if (dset != NULL)
686         {
687             DIC_UI sopClass;
688             DIC_UI instanceUID2;
689             DIC_UI seriesUID;
690             DIC_UI studyUID;
691             if (DU_getStringDOElement(dset, DCM_SOPClassUID, sopClass, sizeof(sopClass)) &&
692                     DU_getStringDOElement(dset, DCM_SOPInstanceUID, instanceUID2, sizeof(instanceUID2)) &&
693                     DU_getStringDOElement(dset, DCM_SeriesInstanceUID, seriesUID, sizeof(seriesUID)) &&
694                     DU_getStringDOElement(dset, DCM_StudyInstanceUID, studyUID, sizeof(studyUID)) &&
695                 ((!imageInDatabase) || (getSeriesStruct(studyUID, seriesUID, instanceUID2) == NULL)))
696             {
697                 releaseDatabase();   /* avoid deadlocks */
698                 if (dbhandle.makeNewStoreFileName(sopClass, instanceUID2, imageFileName, sizeof(imageFileName)).good())
699                 {
700                     // now store presentation state as filename
701                     result = saveCurrentImage(imageFileName);
702                     if (EC_Normal==result)
703                     {
704                         if (dbhandle.storeRequest(sopClass, instanceUID2, imageFileName, &dbStatus).bad())
705                         {
706                             result = EC_IllegalCall;
707                             DCMPSTAT_LOGFILE("Save presentation state to database failed: could not register image in index file");
708                             DCMPSTAT_WARN("Unable to register image '" << imageFileName << "' in database");
709                         } else {
710                             imageInDatabase = OFTrue;
711                         }
712                     }
713                 }
714             }
715         }
716     }
717     return result;
718 }
719 
720 
savePState(const char * filename,OFBool replaceSOPInstanceUID,OFBool explicitVR)721 OFCondition DVInterface::savePState(const char *filename, OFBool replaceSOPInstanceUID, OFBool explicitVR)
722 {
723     if (pState==NULL) return EC_IllegalCall;
724     if (filename==NULL) return EC_IllegalCall;
725 
726     OFCondition status = EC_IllegalCall;
727     DcmFileFormat *fileformat = new DcmFileFormat();
728     DcmDataset *dataset = NULL;
729     if (fileformat) dataset=fileformat->getDataset();
730 
731     if (dataset)
732     {
733         if ((status = pState->write(*dataset, replaceSOPInstanceUID)) == EC_Normal)
734         {
735           status = DVPSHelper::saveFileFormat(filename, fileformat, explicitVR);
736 
737           // replace the stored data for resetPresentationState()
738           delete pDicomPState;
739           pDicomPState = fileformat;
740           fileformat = NULL; // make sure we don't delete fileformat later
741           if (pSignatureHandler)
742           {
743             pSignatureHandler->updateDigitalSignatureInformation(*pDicomPState->getDataset(), DVPSS_presentationState, OFFalse);
744           }
745         }
746         if (status != EC_Normal)
747             DCMPSTAT_LOGFILE("Save presentation state to file failed: could not write fileformat");
748     } else {
749         DCMPSTAT_LOGFILE("Save presentation state to file failed: memory exhausted");
750         status = EC_MemoryExhausted;
751     }
752 
753     delete fileformat;
754     return status;
755 }
756 
757 
saveCurrentImage(const char * filename,OFBool explicitVR)758 OFCondition DVInterface::saveCurrentImage(const char *filename, OFBool explicitVR)
759 {
760     if (filename==NULL) return EC_IllegalCall;
761     if (pDicomImage==NULL) return EC_IllegalCall;
762     OFCondition result = DVPSHelper::saveFileFormat(filename, pDicomImage, explicitVR);
763     if (result != EC_Normal)
764         DCMPSTAT_LOGFILE("Save image to file failed: could not write fileformat");
765     return result;
766 }
767 
768 
saveStructuredReport()769 OFCondition DVInterface::saveStructuredReport()
770 {
771     // release database lock since we are using the DB module directly
772     releaseDatabase();
773 
774     if (pReport == NULL)
775         return EC_IllegalCall;
776     OFString sopClassUID;
777     if (pReport->getSOPClassUID(sopClassUID).bad() || sopClassUID.empty())
778         return EC_IllegalCall;
779     OFString instanceUID;
780     if (pReport->getSOPInstanceUID(instanceUID).bad() || instanceUID.empty())
781         return EC_IllegalCall;
782 
783     DcmQueryRetrieveDatabaseStatus dbStatus(STATUS_Success);
784     char filename[MAXPATHLEN + 1];
785     OFCondition result = EC_Normal;
786 
787     DcmQueryRetrieveIndexDatabaseHandle dbhandle(getDatabaseFolder(), PSTAT_MAXSTUDYCOUNT, PSTAT_STUDYSIZE, result);
788     if (result.bad())
789     {
790         DCMPSTAT_LOGFILE("Save structured report to database failed: could not lock index file");
791         return EC_IllegalCall;
792     }
793 
794     if (dbhandle.makeNewStoreFileName(sopClassUID.c_str(), instanceUID.c_str(), filename, sizeof(filename)).good())
795     {
796         // now store presentation state as filename
797         result = saveStructuredReport(filename);
798         if (EC_Normal == result)
799         {
800             if (dbhandle.storeRequest(sopClassUID.c_str(), instanceUID.c_str(), filename, &dbStatus).bad())
801             {
802                 result = EC_IllegalCall;
803                 DCMPSTAT_LOGFILE("Save structured report to database failed: could not register in index file");
804                 DCMPSTAT_WARN("Unable to register structured report '" << filename << "' in database");
805             }
806         }
807     }
808     return result;
809 }
810 
811 
saveStructuredReport(const char * filename,OFBool explicitVR)812 OFCondition DVInterface::saveStructuredReport(const char *filename, OFBool explicitVR)
813 {
814     if (pReport==NULL) return EC_IllegalCall;
815     if (filename==NULL) return EC_IllegalCall;
816 
817     OFCondition status = EC_IllegalCall;
818     DcmFileFormat *fileformat = new DcmFileFormat();
819     DcmDataset *dataset = NULL;
820     if (fileformat) dataset=fileformat->getDataset();
821 
822     if (dataset)
823     {
824         /* always add information about private OFFIS DCMTK Coding Scheme */
825         pReport->getCodingSchemeIdentification().addPrivateDcmtkCodingScheme();
826         if ((status = pReport->write(*dataset)) == EC_Normal)
827         {
828             status = DVPSHelper::saveFileFormat(filename, fileformat, explicitVR);
829             if (pSignatureHandler)
830             {
831               pSignatureHandler->updateDigitalSignatureInformation(*dataset, DVPSS_structuredReport, OFFalse);
832             }
833         }
834         if (status != EC_Normal)
835             DCMPSTAT_LOGFILE("Save structured report to file failed: could not write fileformat");
836     } else {
837         DCMPSTAT_LOGFILE("Save structured report to file failed: memory exhausted");
838         status = EC_MemoryExhausted;
839     }
840 
841     delete fileformat;
842     return status;
843 }
844 
845 
addImageReferenceToPState(const char * studyUID,const char * seriesUID,const char * instanceUID)846 OFCondition DVInterface::addImageReferenceToPState(const char *studyUID, const char *seriesUID, const char *instanceUID)
847 {
848     OFCondition status = EC_IllegalCall;
849     if (pState && studyUID && seriesUID && instanceUID)
850     {
851         OFString study = pState->getStudyUID();
852         if (study == studyUID)
853         {
854             if (lockDatabase() == EC_Normal)
855             {
856                 const char *filename = getFilename(studyUID, seriesUID, instanceUID);
857                 if (filename)
858                 {
859                     DcmFileFormat *image = NULL;
860                     if ((status = DVPSHelper::loadFileFormat(filename, image)) == EC_Normal)
861                     {
862                         status = EC_CorruptedData;
863                         if (image)
864                         {
865                             DcmDataset *dataset = image->getDataset();
866                             if (dataset)
867                                 status = pState->addImageReference(*dataset);
868                         }
869                         if (status != EC_Normal)
870                             DCMPSTAT_LOGFILE("Add image reference to presentation state failed: invalid data structures");
871                     } else
872                         DCMPSTAT_LOGFILE("Add image reference to presentation state failed: could not read fileformat");
873                     delete image;
874                 } else
875                     DCMPSTAT_LOGFILE("Add image reference to presentation state failed: UIDs not in index file");
876             } else
877                 DCMPSTAT_LOGFILE("Add image reference to presentation state failed: could not lock index file");
878         } else
879             DCMPSTAT_LOGFILE("Add image reference to presentation state failed: not the same study UID");
880     } else
881         DCMPSTAT_LOGFILE("Add image reference to presentation state failed: invalid UIDs");
882     return status;
883 }
884 
885 
getNumberOfImageReferences()886 size_t DVInterface::getNumberOfImageReferences()
887 {
888     if (pState != NULL)
889         return pState->numberOfImageReferences();
890     return 0;
891 }
892 
893 
exchangeImageAndPState(DVPresentationState * newState,DcmFileFormat * image,DcmFileFormat * state)894 OFCondition DVInterface::exchangeImageAndPState(DVPresentationState *newState, DcmFileFormat *image, DcmFileFormat *state)
895 {
896     if (newState==NULL) return EC_IllegalCall;
897     if (image==NULL) return EC_IllegalCall;
898     if (pState != newState)
899     {
900         delete pState;
901         delete pStoredPState;
902         delete pDicomPState;
903         pState = newState;
904         pStoredPState = NULL;
905         pDicomPState = state;
906         if (pSignatureHandler)
907         {
908           if (pDicomPState) pSignatureHandler->updateDigitalSignatureInformation(*pDicomPState->getDataset(), DVPSS_presentationState, OFTrue);
909           else pSignatureHandler->disableDigitalSignatureInformation(DVPSS_presentationState);
910         }
911     }
912     if (pDicomImage != image)
913     {
914         delete pDicomImage;       // delete only if different
915         pDicomImage = image;
916         if (pSignatureHandler)
917         {
918           pSignatureHandler->updateDigitalSignatureInformation(*pDicomImage->getDataset(), DVPSS_image, OFTrue);
919         }
920     }
921     return EC_Normal;
922 }
923 
924 
resetPresentationState()925 OFCondition DVInterface::resetPresentationState()
926 {
927     DVPresentationState *newState = new DVPresentationState(displayFunction,
928       minimumPrintBitmapWidth, minimumPrintBitmapHeight, maximumPrintBitmapWidth, maximumPrintBitmapHeight,
929       maximumPreviewImageWidth, maximumPreviewImageHeight);
930     if (newState==NULL) return EC_MemoryExhausted;
931 
932     OFCondition status = EC_Normal;
933     if ((pDicomImage)&&(pDicomPState))
934     {
935         // both image and presentation state are present
936         DcmDataset *dataset = pDicomPState->getDataset();
937         if (dataset)
938         {
939             if (EC_Normal == (status = newState->read(*dataset))) status = newState->attachImage(pDicomImage, OFFalse);
940             if (EC_Normal == status)
941             {
942                 if (pState) delete pState;
943                 if (pStoredPState) delete pStoredPState;
944                 pState = newState;
945                 pStoredPState = NULL;                   // return to original pstate
946             }
947         } else status = EC_IllegalCall;
948     }
949     else if (pDicomImage)
950     {
951         // only image is present
952         DcmDataset *dataset = pDicomImage->getDataset();
953         if (dataset)
954         {
955             if (EC_Normal == (status = newState->createFromImage(*dataset))) status = newState->attachImage(pDicomImage, OFFalse);
956             if (EC_Normal == status)
957             {
958                 if (pState) delete pState;
959                 if (pStoredPState) delete pStoredPState;
960                 pState = newState;
961                 pStoredPState = NULL;
962             }
963         } else status = EC_IllegalCall;
964     }
965     if (EC_Normal != status) delete newState;
966     return status;
967 }
968 
969 
saveCurrentPStateForReset()970 OFCondition DVInterface::saveCurrentPStateForReset()
971 {
972     OFCondition status = EC_IllegalCall;
973     if (pState != NULL)
974     {
975         DcmFileFormat *fileformat = new DcmFileFormat();
976         if (fileformat != NULL)
977         {
978             DcmDataset *dataset = fileformat->getDataset();
979             if (dataset)
980             {
981                 status = pState->write(*dataset, OFFalse);  // write current state to 'reset' dataset
982                 if (status == EC_Normal)
983                 {
984                     delete pDicomPState;
985                     pDicomPState = fileformat;
986                     fileformat = NULL;                      // avoid deletion of pDicomPState a few lines later
987                 }
988             } else status = EC_MemoryExhausted;
989         } else status = EC_MemoryExhausted;
990         delete fileformat;
991     }
992     return status;
993 }
994 
995 
getNumberOfPStates()996 Uint32 DVInterface::getNumberOfPStates()
997 {
998     if (createPStateCache())
999     {
1000         DVInstanceCache::ItemStruct *instance = getInstanceStruct();
1001         if ((instance != NULL) && ((instance->Type == DVPSI_image) || (instance->Type == DVPSI_hardcopyGrayscale)))
1002             return OFstatic_cast(Uint32, instance->List.size());
1003     }
1004     return 0;
1005 }
1006 
1007 
selectPState(Uint32 idx,OFBool changeStatus)1008 OFCondition DVInterface::selectPState(Uint32 idx, OFBool changeStatus)
1009 {
1010     if (createPStateCache())
1011     {
1012         DVInstanceCache::ItemStruct *instance = getInstanceStruct();
1013         if ((instance != NULL) && ((instance->Type == DVPSI_image) || (instance->Type == DVPSI_hardcopyGrayscale)))
1014         {
1015             OFListIterator(DVInstanceCache::ItemStruct *) iter = instance->List.begin();
1016             OFListIterator(DVInstanceCache::ItemStruct *) last = instance->List.end();
1017             while (iter != last)
1018             {
1019                 if (idx == 0)
1020                 {
1021                     DVInstanceCache::ItemStruct *pstate = (*iter);
1022                     if (pstate != NULL)
1023                     {
1024                         OFCondition status = EC_IllegalCall;
1025                         if (pDicomImage == NULL)
1026                             status = loadPState(pstate->Filename.c_str(), instance->Filename.c_str());
1027                         else
1028                             status = loadPState(pstate->Filename.c_str());
1029                         if ((status == EC_Normal) && changeStatus)
1030                             instanceReviewed(pstate->Pos);
1031                         return status;
1032                     }
1033                 }
1034                 idx--;
1035                 ++iter;
1036             }
1037         }
1038     }
1039     return EC_IllegalCall;
1040 }
1041 
1042 
getPStateDescription(Uint32 idx)1043 const char *DVInterface::getPStateDescription(Uint32 idx)
1044 {
1045     if (createPStateCache())
1046     {
1047         DVInstanceCache::ItemStruct *instance = getInstanceStruct();
1048         if ((instance != NULL) && ((instance->Type == DVPSI_image) || (instance->Type == DVPSI_hardcopyGrayscale)))
1049         {
1050             OFListIterator(DVInstanceCache::ItemStruct *) iter = instance->List.begin();
1051             OFListIterator(DVInstanceCache::ItemStruct *) last = instance->List.end();
1052             while (iter != last)
1053             {
1054                 if (idx == 0)
1055                 {
1056                     DVInstanceCache::ItemStruct *pstate = (*iter);
1057                     if (pstate != NULL)
1058                         return pstate->Description.c_str();
1059                 }
1060                 idx--;
1061                 ++iter;
1062             }
1063         }
1064     }
1065     return NULL;
1066 }
1067 
1068 
getPStateLabel(Uint32 idx)1069 const char *DVInterface::getPStateLabel(Uint32 idx)
1070 {
1071     if (createPStateCache())
1072     {
1073         DVInstanceCache::ItemStruct *instance = getInstanceStruct();
1074         if ((instance != NULL) && ((instance->Type == DVPSI_image) || (instance->Type == DVPSI_hardcopyGrayscale)))
1075         {
1076             OFListIterator(DVInstanceCache::ItemStruct *) iter = instance->List.begin();
1077             OFListIterator(DVInstanceCache::ItemStruct *) last = instance->List.end();
1078             while (iter != last)
1079             {
1080                 if (idx == 0)
1081                 {
1082                     DVInstanceCache::ItemStruct *pstate = (*iter);
1083                     if (pstate != NULL)
1084                         return pstate->Label.c_str();
1085                 }
1086                 idx--;
1087                 ++iter;
1088             }
1089         }
1090     }
1091     return NULL;
1092 }
1093 
1094 
disablePState()1095 OFCondition DVInterface::disablePState()
1096 {
1097   OFCondition status = EC_IllegalCall;
1098   if ((pState != NULL) && (pStoredPState == NULL))
1099   {
1100     if (pDicomImage != NULL)
1101     {
1102       DcmDataset *dataset = pDicomImage->getDataset();
1103       if (dataset != NULL)
1104       {
1105         DVPresentationState *newState = new DVPresentationState(displayFunction,
1106           minimumPrintBitmapWidth, minimumPrintBitmapHeight, maximumPrintBitmapWidth, maximumPrintBitmapHeight,
1107           maximumPreviewImageWidth, maximumPreviewImageHeight);
1108         if (newState != NULL)
1109         {
1110           if ((status = newState->createFromImage(*dataset)) == EC_Normal)
1111           {
1112             if ((status = newState->attachImage(pDicomImage, OFFalse)) == EC_Normal)
1113             {
1114               pStoredPState = pState;
1115               pState = newState;
1116               return EC_Normal;
1117             }
1118           }
1119           delete newState;
1120         }
1121       }
1122     }
1123   }
1124   return status;
1125 }
1126 
1127 
enablePState()1128 OFCondition DVInterface::enablePState()
1129 {
1130     if ((pState != NULL) && (pStoredPState != NULL))
1131     {
1132         delete pState;
1133         pState = pStoredPState;
1134         pStoredPState = NULL;
1135         return EC_Normal;
1136     }
1137     return EC_IllegalCall;
1138 }
1139 
1140 
getStudyStruct(const char * studyUID,const char * seriesUID)1141 DVStudyCache::ItemStruct *DVInterface::getStudyStruct(const char *studyUID,
1142                                                       const char *seriesUID)
1143 {
1144     if (createIndexCache())
1145     {
1146         if (studyUID)
1147         {
1148             if (idxCache.isElem(studyUID))
1149             {
1150                 DVStudyCache::ItemStruct *study = idxCache.getItem();
1151                 if ((seriesUID == NULL) || (study->List.isElem(seriesUID)))
1152                     return study;
1153             }
1154         } else
1155             return idxCache.getItem();          // current study
1156     }
1157     return NULL;
1158 }
1159 
1160 
getSeriesStruct(const char * studyUID,const char * seriesUID,const char * instanceUID)1161 DVSeriesCache::ItemStruct *DVInterface::getSeriesStruct(const char *studyUID,
1162                                                         const char *seriesUID,
1163                                                         const char *instanceUID)
1164 {
1165     if ((studyUID && seriesUID) || (!studyUID && !seriesUID))
1166     {
1167         DVStudyCache::ItemStruct *study = getStudyStruct(studyUID, seriesUID);
1168         if (study != NULL)
1169         {
1170             DVSeriesCache::ItemStruct *series = study->List.getItem();
1171             if (series != NULL)
1172             {
1173                 if ((instanceUID == NULL) || (series->List.isElem(instanceUID)))
1174                     return series;
1175             }
1176         }
1177     }
1178     return NULL;
1179 }
1180 
1181 
getInstanceStruct(const char * studyUID,const char * seriesUID,const char * instanceUID)1182 DVInstanceCache::ItemStruct *DVInterface::getInstanceStruct(const char *studyUID,
1183                                                             const char *seriesUID,
1184                                                             const char *instanceUID)
1185 {
1186     if ((studyUID && seriesUID && instanceUID) || (!studyUID && !seriesUID && !instanceUID))
1187     {
1188         DVSeriesCache::ItemStruct *series = getSeriesStruct(studyUID, seriesUID, instanceUID);
1189         if (series != NULL)
1190             return series->List.getItem();
1191     }
1192     return NULL;
1193 }
1194 
1195 
getFilename(const char * studyUID,const char * seriesUID,const char * instanceUID)1196 const char *DVInterface::getFilename(const char *studyUID,
1197                                      const char *seriesUID,
1198                                      const char *instanceUID)
1199 {
1200     DVSeriesCache::ItemStruct *series = getSeriesStruct(studyUID, seriesUID, instanceUID);
1201     if (series != NULL)
1202         return series->List.getFilename();
1203     return NULL;
1204 }
1205 
1206 
lockDatabase()1207 OFCondition DVInterface::lockDatabase()
1208 {
1209     if (pHandle) return EC_Normal; // may be called multiple times
1210 
1211     OFCondition result;
1212     pHandle = new DcmQueryRetrieveIndexDatabaseHandle(getDatabaseFolder(), PSTAT_MAXSTUDYCOUNT, PSTAT_STUDYSIZE, result);
1213     if (result.good())
1214     {
1215         lockingMode = OFFalse;
1216         if (pHandle->DB_lock(OFFalse).good())
1217         {
1218             if (databaseIndexFile.empty())
1219                 databaseIndexFile = pHandle->getIndexFilename();
1220             return EC_Normal;
1221         }
1222     }
1223     return EC_IllegalCall;
1224 }
1225 
1226 
lockExclusive()1227 OFCondition DVInterface::lockExclusive()
1228 {
1229     if (pHandle && lockingMode) return EC_Normal;
1230     OFCondition result = EC_Normal;
1231     if (pHandle == NULL) result = lockDatabase();
1232     if (result.good())
1233     {
1234         // we now have a shared lock.
1235         pHandle->DB_unlock();
1236         if (pHandle->DB_lock(OFTrue).good())
1237             lockingMode = OFTrue;
1238         else
1239             result = EC_IllegalCall;
1240     }
1241     return result;
1242 }
1243 
1244 
unlockExclusive()1245 OFCondition DVInterface::unlockExclusive()
1246 {
1247     if (pHandle && lockingMode)
1248     {
1249         if (pHandle->DB_unlock().good())
1250         {
1251             delete pHandle;
1252             pHandle=NULL;
1253             lockingMode=OFFalse;
1254             clearIndexCache();
1255             return EC_Normal;
1256         }
1257     }
1258     return EC_IllegalCall;
1259 }
1260 
1261 
releaseDatabase()1262 OFCondition DVInterface::releaseDatabase()
1263 {
1264     if (pHandle == NULL) return EC_Normal;
1265     OFCondition cond = pHandle->DB_unlock();
1266     if (cond.good())
1267     {
1268       delete pHandle;
1269       pHandle = NULL;
1270       clearIndexCache();
1271     }
1272     return cond;
1273 }
1274 
1275 
resetDatabaseReferenceTime()1276 void DVInterface::resetDatabaseReferenceTime()
1277 {
1278     // set index file modification time to "yesterday" to make sure
1279     // we notice any change even if different processes have minor
1280     // date/time differences (i.e. over NFS)
1281 #ifdef HAVE_DECLARATION_STRUCT_UTIMBUF
1282     struct utimbuf utime_buf;
1283     utime_buf.actime  = OFstatic_cast(time_t, referenceTime);
1284     utime_buf.modtime = OFstatic_cast(time_t, referenceTime);
1285     if (0 != utime(databaseIndexFile.c_str(), &utime_buf))
1286 #else
1287     // some old platforms use the prototype int utime(char *file, time_t timep[])
1288     time_t utime_buf[2];
1289     utime_buf[0]  = OFstatic_cast(time_t, referenceTime);
1290     utime_buf[1] = OFstatic_cast(time_t, referenceTime);
1291     if (0 != utime(OFconst_cast(char *, databaseIndexFile.c_str()), utime_buf))
1292 #endif
1293     {
1294       DCMPSTAT_WARN("Cannot set database index file modification time");
1295     } else {
1296       struct stat stat_buf;
1297       if (0 == stat(databaseIndexFile.c_str(), &stat_buf))
1298       {
1299         referenceTime = OFstatic_cast(unsigned long, stat_buf.st_mtime);
1300       }
1301     }
1302 }
1303 
1304 
newInstancesReceived()1305 OFBool DVInterface::newInstancesReceived()
1306 {
1307   if (databaseIndexFile.empty())
1308   {
1309     if (pHandle == NULL)
1310     {
1311       lockDatabase(); // derives databaseIndexFile
1312       releaseDatabase();
1313     }
1314   }
1315 
1316   if (!databaseIndexFile.empty())
1317   {
1318     struct stat stat_buf;
1319     if (0== stat(databaseIndexFile.c_str(), &stat_buf))
1320     {
1321       if (OFstatic_cast(unsigned long, stat_buf.st_mtime) == referenceTime) return OFFalse;
1322     }
1323 
1324     resetDatabaseReferenceTime();
1325   }
1326   return OFTrue; // default
1327 }
1328 
1329 
clearIndexCache()1330 void DVInterface::clearIndexCache()
1331 {
1332     idxCache.clear();
1333     clearIndexRecord(idxRec, idxRecPos);
1334 }
1335 
1336 
createIndexCache()1337 OFBool DVInterface::createIndexCache()
1338 {
1339     if (lockDatabase() == EC_Normal)
1340     {
1341         if (idxCache.empty())
1342         {
1343             int counter = 0;
1344             pHandle->DB_IdxInitLoop(&counter);
1345             IdxRecord record;
1346             while (pHandle->DB_IdxGetNext(&counter, &record).good())
1347             {
1348                 if (!idxCache.isElem(record.StudyInstanceUID))
1349                     idxCache.addItem(record.StudyInstanceUID);
1350                 DVStudyCache::ItemStruct *study = idxCache.getItem();
1351                 if (study != NULL)
1352                 {
1353                     if (!study->List.isElem(record.SeriesInstanceUID))
1354                         study->List.addItem(record.SeriesInstanceUID);
1355                     DVSeriesCache::ItemStruct *series = study->List.getItem();
1356                     if (series != NULL)
1357                     {
1358                         if (!series->List.isElem(record.SOPInstanceUID))
1359                         {
1360                             DVPSInstanceType type = DVPSI_image;
1361                             if (DSRTypes::sopClassUIDToDocumentType(record.SOPClassUID) != DSRTypes::DT_invalid)
1362                                 type = DVPSI_structuredReport;
1363                             else if (strcmp(record.Modality, "PR") == 0)
1364                                 type = DVPSI_presentationState;
1365                             else if (strcmp(record.Modality, "SR") == 0)
1366                                 type = DVPSI_structuredReport;
1367                             else if (strcmp(record.Modality, "HC") == 0)
1368                                 type =DVPSI_hardcopyGrayscale;
1369                             else if (strcmp(record.Modality, "STORED_PRINT") == 0)
1370                                 type = DVPSI_storedPrint;
1371                             series->List.addItem(record.SOPInstanceUID,
1372                                                  counter,
1373                                                  OFstatic_cast(DVIFhierarchyStatus, record.hstat),
1374                                                  type,
1375                                                  record.ImageSize,
1376                                                  record.filename);
1377                             if (series->Type == DVPSI_image)
1378                                 series->Type = type;                // series contains only one type of instances
1379                         }
1380                     }
1381                 }
1382             }
1383             updateStatusCache();
1384         }
1385         return OFTrue;
1386     }
1387     return OFFalse;
1388 }
1389 
1390 
createPStateCache()1391 OFBool DVInterface::createPStateCache()
1392 {
1393     DVStudyCache::ItemStruct *study = getStudyStruct();
1394     if (study != NULL)
1395     {
1396         DVSeriesCache::ItemStruct *series = study->List.getItem();
1397         if (series != NULL)
1398         {
1399             DVInstanceCache::ItemStruct *instance = series->List.getItem();
1400             if ((instance != NULL) && ((instance->Type == DVPSI_image) || (instance->Type == DVPSI_hardcopyGrayscale)))
1401             {
1402                 if (!instance->Checked)                             // is current instance already checked?
1403                 {
1404                     if (instance->List.empty())
1405                     {
1406                         OFString seriesUID = series->UID;
1407                         OFString instanceUID = instance->UID;
1408                         if (study->List.gotoFirst())
1409                         {
1410                             do { /* for all series */
1411                                 if (study->List.getType() == DVPSI_presentationState)
1412                                 {
1413                                     series = study->List.getItem();
1414                                     if (series != NULL)
1415                                     {
1416                                         if (series->List.gotoFirst())
1417                                         {
1418                                             do { /* for all instances */
1419                                                 if (series->List.getType() == DVPSI_presentationState)
1420                                                 {
1421                                                     DcmFileFormat *pstate = NULL;
1422                                                     if ((DVPSHelper::loadFileFormat(series->List.getFilename(), pstate) == EC_Normal) && pstate)
1423                                                     {
1424                                                         DcmDataset *dataset = pstate->getDataset();
1425                                                         DVPSReferencedSeries_PList plist;
1426                                                         if (dataset && (plist.read(*dataset) == EC_Normal) && plist.isValid())
1427                                                         {
1428                                                             if (plist.findImageReference(seriesUID.c_str(), instanceUID.c_str()))
1429                                                             {
1430                                                                 DVInstanceCache::ItemStruct *reference = series->List.getItem();
1431                                                                 if (reference != NULL)
1432                                                                 {
1433                                                                     DcmStack stack;
1434                                                                     if (dataset->search(DCM_ContentDescription, stack, ESM_fromHere, OFFalse) == EC_Normal)
1435                                                                     {
1436                                                                         char *value = NULL;
1437                                                                         if ((*OFstatic_cast(DcmLongString *, stack.top())).getString(value) == EC_Normal)
1438                                                                             reference->Description = value;
1439                                                                     }
1440                                                                     stack.clear();
1441                                                                     if (dataset->search(DCM_ContentLabel, stack, ESM_fromHere, OFFalse) == EC_Normal)
1442                                                                     {
1443                                                                         char *value = NULL;
1444                                                                         if ((*OFstatic_cast(DcmLongString *, stack.top())).getString(value) == EC_Normal)
1445                                                                             reference->Label = value;
1446                                                                     }
1447                                                                     instance->List.push_back(reference);
1448                                                                 }
1449                                                             }
1450                                                         }
1451                                                     }
1452                                                     delete pstate;
1453                                                 }
1454                                             } while (series->List.gotoNext());
1455                                         }
1456                                         series->List.reset();                    // set iterator to old position
1457                                     }
1458                                 }
1459                             } while (study->List.gotoNext());
1460                         }
1461                         study->List.reset();                                     // set iterator to old position
1462                     }
1463                     instance->Checked = OFTrue;                                  // do not check twice
1464                 }
1465                 return OFTrue;
1466             }
1467         }
1468     }
1469     return OFFalse;
1470 }
1471 
1472 
clearIndexRecord(IdxRecord & record,int & recpos)1473 void DVInterface::clearIndexRecord(IdxRecord &record,
1474                                    int &recpos)
1475 {
1476     OFBitmanipTemplate<Uint8>::zeroMem(OFreinterpret_cast(Uint8 *, &record), sizeof(idxRec));
1477     recpos = -1;
1478 }
1479 
1480 
readIndexRecord(const int pos,IdxRecord & record,int * oldpos)1481 OFBool DVInterface::readIndexRecord(const int pos,
1482                                     IdxRecord &record,
1483                                     int *oldpos)
1484 {
1485     if (lockDatabase() == EC_Normal)
1486     {
1487         if ((oldpos != NULL) && (pos == *oldpos))                      // record already read
1488             return OFTrue;
1489         if (pHandle->DB_IdxRead(pos, &record).good())
1490         {
1491             if (oldpos != NULL)
1492                 *oldpos = pos;
1493             return OFTrue;
1494         }
1495     }
1496     return OFFalse;
1497 }
1498 
1499 
updateStatusCache()1500 void DVInterface::updateStatusCache()
1501 {
1502     idxCache.updateStatus();
1503 }
1504 
1505 
getNumberOfStudies()1506 Uint32 DVInterface::getNumberOfStudies()
1507 {
1508     if (createIndexCache())
1509         return idxCache.getCount();
1510     return 0;
1511 }
1512 
1513 
getNumberOfSeries()1514 Uint32 DVInterface::getNumberOfSeries()
1515 {
1516     DVStudyCache::ItemStruct *study = getStudyStruct();
1517     if (study != NULL)
1518         return study->List.getCount();
1519     return 0;
1520 }
1521 
1522 
getNumberOfInstances()1523 Uint32 DVInterface::getNumberOfInstances()
1524 {
1525     DVSeriesCache::ItemStruct *series = getSeriesStruct();
1526     if (series != NULL)
1527         return series->List.getCount();
1528     return 0;
1529 }
1530 
1531 
selectStudy(Uint32 idx)1532 OFCondition DVInterface::selectStudy(Uint32 idx)
1533 {
1534     if (createIndexCache())
1535     {
1536         if (idxCache.gotoItem(idx))
1537         {
1538             DVStudyCache::ItemStruct *study = idxCache.getItem();
1539             if (study->List.gotoItem(0))
1540             {
1541                 DVSeriesCache::ItemStruct *series = study->List.getItem();
1542                 if (series != NULL)
1543                 {
1544                     if (series->List.gotoItem(0))
1545                     {
1546                         if (readIndexRecord(series->List.getPos(), idxRec, &idxRecPos))
1547                             return EC_Normal;
1548                     }
1549                 }
1550             }
1551         }
1552     }
1553     return EC_IllegalCall;
1554 }
1555 
1556 
selectStudy(const char * studyUID)1557 OFCondition DVInterface::selectStudy(const char *studyUID)
1558 {
1559     if (studyUID)
1560     {
1561         if (createIndexCache())
1562         {
1563             if (idxCache.isElem(studyUID))
1564             {
1565                 DVStudyCache::ItemStruct *study = idxCache.getItem();
1566                 if (study->List.gotoItem(0))
1567                 {
1568                     DVSeriesCache::ItemStruct *series = study->List.getItem();
1569                     if (series != NULL)
1570                     {
1571                         if (series->List.gotoItem(0))
1572                         {
1573                             if (readIndexRecord(series->List.getPos(), idxRec, &idxRecPos))
1574                                 return EC_Normal;
1575                         }
1576                     }
1577                 }
1578             }
1579         }
1580     }
1581     return EC_IllegalCall;
1582 }
1583 
1584 
selectSeries(Uint32 idx)1585 OFCondition DVInterface::selectSeries(Uint32 idx)
1586 {
1587     DVStudyCache::ItemStruct *study = getStudyStruct();
1588     if (study != NULL)
1589     {
1590         if (study->List.gotoItem(idx))
1591         {
1592             DVSeriesCache::ItemStruct *series = study->List.getItem();
1593             if (series != NULL)
1594             {
1595                 if (series->List.gotoItem(0))
1596                 {
1597                     if (readIndexRecord(series->List.getPos(), idxRec, &idxRecPos))
1598                         return EC_Normal;
1599                 }
1600             }
1601         }
1602     }
1603     return EC_IllegalCall;
1604 }
1605 
1606 
selectSeries(const char * seriesUID)1607 OFCondition DVInterface::selectSeries(const char *seriesUID)
1608 {
1609     if (seriesUID)
1610     {
1611         DVStudyCache::ItemStruct *study = getStudyStruct();
1612         if (study != NULL)
1613         {
1614             if (study->List.isElem(seriesUID))
1615             {
1616                 DVSeriesCache::ItemStruct *series = study->List.getItem();
1617                 if (series != NULL)
1618                 {
1619                     if (series->List.gotoItem(0))
1620                     {
1621                         if (readIndexRecord(series->List.getPos(), idxRec, &idxRecPos))
1622                             return EC_Normal;
1623                     }
1624                 }
1625             }
1626         }
1627     }
1628     return EC_IllegalCall;
1629 }
1630 
1631 
selectInstance(Uint32 idx)1632 OFCondition DVInterface::selectInstance(Uint32 idx)
1633 {
1634     DVSeriesCache::ItemStruct *series = getSeriesStruct();
1635     if (series != NULL)
1636     {
1637         if (series->List.gotoItem(idx))
1638         {
1639             if (readIndexRecord(series->List.getPos(), idxRec, &idxRecPos))
1640                 return EC_Normal;
1641         }
1642     }
1643     return EC_IllegalCall;
1644 }
1645 
1646 
selectInstance(const char * instanceUID)1647 OFCondition DVInterface::selectInstance(const char *instanceUID)
1648 {
1649     if (instanceUID)
1650     {
1651         DVSeriesCache::ItemStruct *series = getSeriesStruct();
1652         if (series != NULL)
1653         {
1654             if (series->List.isElem(instanceUID))
1655             {
1656                 if (readIndexRecord(series->List.getPos(), idxRec, &idxRecPos))
1657                     return EC_Normal;
1658             }
1659         }
1660     }
1661     return EC_IllegalCall;
1662 }
1663 
1664 
selectInstance(const char * instanceUID,const char * sopClassUID)1665 OFCondition DVInterface::selectInstance(const char *instanceUID, const char *sopClassUID)
1666 {
1667     if (instanceUID)
1668     {
1669         if (createIndexCache() && idxCache.gotoFirst())
1670         {
1671             DVStudyCache::ItemStruct *study = NULL;
1672             DVSeriesCache::ItemStruct *series = NULL;
1673             do {  /* for all studies */
1674                 study = idxCache.getItem();
1675                 if ((study != NULL) && study->List.gotoFirst())
1676                 {
1677                     do {  /* for all series */
1678                         series = study->List.getItem();
1679                         if ((series != NULL) && series->List.isElem(instanceUID))
1680                         {
1681                             if (readIndexRecord(series->List.getPos(), idxRec, &idxRecPos))
1682                             {
1683                                 if (sopClassUID == NULL)
1684                                     return EC_Normal;
1685                                 else if (strcmp(sopClassUID, idxRec.SOPClassUID) == 0)
1686                                     return EC_Normal;
1687                             }
1688                         }
1689                     } while (study->List.gotoNext());
1690                 }
1691             } while (idxCache.gotoNext());
1692         }
1693     }
1694     return EC_IllegalCall;
1695 }
1696 
1697 
selectInstance(const char * studyUID,const char * seriesUID,const char * instanceUID)1698 OFCondition DVInterface::selectInstance(const char *studyUID, const char *seriesUID, const char *instanceUID)
1699 {
1700     if (studyUID && seriesUID && instanceUID)
1701     {
1702         if (createIndexCache())
1703         {
1704             if (idxCache.isElem(studyUID))
1705             {
1706                 DVStudyCache::ItemStruct *study = idxCache.getItem();
1707                 if (study->List.isElem(seriesUID))
1708                 {
1709                     DVSeriesCache::ItemStruct *series = study->List.getItem();
1710                     if (series != NULL)
1711                     {
1712                         if (series->List.isElem(instanceUID))
1713                         {
1714                             if (readIndexRecord(series->List.getPos(), idxRec, &idxRecPos))
1715                                 return EC_Normal;
1716                         }
1717                     }
1718                 }
1719             }
1720         }
1721     }
1722     return EC_IllegalCall;
1723 }
1724 
1725 
getStudyStatus()1726 DVIFhierarchyStatus DVInterface::getStudyStatus()
1727 {
1728     return idxCache.getStatus();
1729 }
1730 
1731 
getSeriesStatus()1732 DVIFhierarchyStatus DVInterface::getSeriesStatus()
1733 {
1734     DVStudyCache::ItemStruct *study = idxCache.getItem();
1735     if (study != NULL)
1736         return study->List.getStatus();
1737     return DVIF_objectIsNew;
1738 }
1739 
1740 
getInstanceStatus()1741 DVIFhierarchyStatus DVInterface::getInstanceStatus()
1742 {
1743     DVStudyCache::ItemStruct *study = idxCache.getItem();
1744     if (study != NULL)
1745     {
1746         DVSeriesCache::ItemStruct *series = study->List.getItem();
1747         if (series != NULL)
1748             return series->List.getStatus();
1749     }
1750     return DVIF_objectIsNew;
1751 }
1752 
1753 
getSeriesType()1754 DVPSInstanceType DVInterface::getSeriesType()
1755 {
1756     DVStudyCache::ItemStruct *study = idxCache.getItem();
1757     if (study != NULL)
1758         return study->List.getType();
1759     return DVPSI_image;
1760 }
1761 
1762 
getInstanceType()1763 DVPSInstanceType DVInterface::getInstanceType()
1764 {
1765     DVStudyCache::ItemStruct *study = idxCache.getItem();
1766     if (study != NULL)
1767     {
1768         DVSeriesCache::ItemStruct *series = study->List.getItem();
1769         if (series != NULL)
1770             return series->List.getType();
1771     }
1772     return DVPSI_image;
1773 }
1774 
1775 
getStudyUID()1776 const char *DVInterface::getStudyUID()
1777 {
1778     return idxRec.StudyInstanceUID;
1779 }
1780 
1781 
getSeriesUID()1782 const char *DVInterface::getSeriesUID()
1783 {
1784     return idxRec.SeriesInstanceUID;
1785 }
1786 
1787 
getSOPClassUID()1788 const char *DVInterface::getSOPClassUID()
1789 {
1790     return idxRec.SOPClassUID;
1791 }
1792 
1793 
getInstanceUID()1794 const char *DVInterface::getInstanceUID()
1795 {
1796     return idxRec.SOPInstanceUID;
1797 }
1798 
1799 
getStudyDescription()1800 const char *DVInterface::getStudyDescription()
1801 {
1802     return idxRec.StudyDescription;
1803 }
1804 
1805 
getStudyDate()1806 const char *DVInterface::getStudyDate()
1807 {
1808     return idxRec.StudyDate;
1809 }
1810 
1811 
getStudyTime()1812 const char *DVInterface::getStudyTime()
1813 {
1814     return idxRec.StudyTime;
1815 }
1816 
1817 
getReferringPhysiciansName()1818 const char *DVInterface::getReferringPhysiciansName()
1819 {
1820     return idxRec.ReferringPhysicianName;
1821 }
1822 
1823 
getAccessionNumber()1824 const char *DVInterface::getAccessionNumber()
1825 {
1826     return idxRec.AccessionNumber;
1827 }
1828 
1829 
getNameOfPhysiciansReadingStudy()1830 const char *DVInterface::getNameOfPhysiciansReadingStudy()
1831 {
1832     return idxRec.NameOfPhysiciansReadingStudy;
1833 }
1834 
1835 
getPatientName()1836 const char *DVInterface::getPatientName()
1837 {
1838     return idxRec.PatientName;
1839 }
1840 
1841 
getPatientID()1842 const char *DVInterface::getPatientID()
1843 {
1844     return idxRec.PatientID;
1845 }
1846 
1847 
getPatientBirthDate()1848 const char *DVInterface::getPatientBirthDate()
1849 {
1850     return idxRec.PatientBirthDate;
1851 }
1852 
1853 
getPatientSex()1854 const char *DVInterface::getPatientSex()
1855 {
1856     return idxRec.PatientSex;
1857 }
1858 
1859 
getPatientBirthTime()1860 const char *DVInterface::getPatientBirthTime()
1861 {
1862     return idxRec.PatientBirthTime;
1863 }
1864 
1865 
getOtherPatientNames()1866 const char *DVInterface::getOtherPatientNames()
1867 {
1868     return idxRec.OtherPatientNames;
1869 }
1870 
1871 
getOtherPatientID()1872 const char *DVInterface::getOtherPatientID()
1873 {
1874     return idxRec.OtherPatientIDs;
1875 }
1876 
1877 
getEthnicGroup()1878 const char *DVInterface::getEthnicGroup()
1879 {
1880     return idxRec.EthnicGroup;
1881 }
1882 
1883 
getSeriesDescription()1884 const char *DVInterface::getSeriesDescription()
1885 {
1886     return idxRec.SeriesDescription;
1887 }
1888 
1889 
getSeriesNumber()1890 const char *DVInterface::getSeriesNumber()
1891 {
1892     return idxRec.SeriesNumber;
1893 }
1894 
1895 
getSeriesDate()1896 const char *DVInterface::getSeriesDate()
1897 {
1898     return idxRec.SeriesDate;
1899 }
1900 
1901 
getSeriesTime()1902 const char *DVInterface::getSeriesTime()
1903 {
1904     return idxRec.SeriesTime;
1905 }
1906 
1907 
getSeriesPerformingPhysiciansName()1908 const char *DVInterface::getSeriesPerformingPhysiciansName()
1909 {
1910     return idxRec.PerformingPhysicianName;
1911 }
1912 
1913 
getSeriesProtocolName()1914 const char *DVInterface::getSeriesProtocolName()
1915 {
1916     return idxRec.ProtocolName;
1917 }
1918 
1919 
getSeriesOperatorsName()1920 const char *DVInterface::getSeriesOperatorsName()
1921 {
1922     return idxRec.OperatorsName;
1923 }
1924 
1925 
getModality()1926 const char *DVInterface::getModality()
1927 {
1928     return idxRec.Modality;
1929 }
1930 
1931 
getImageNumber()1932 const char *DVInterface::getImageNumber()
1933 {
1934     return idxRec.ImageNumber;
1935 }
1936 
1937 
getFilename()1938 const char *DVInterface::getFilename()
1939 {
1940     return idxRec.filename;
1941 }
1942 
1943 
getInstanceDescription()1944 const char *DVInterface::getInstanceDescription()
1945 {
1946     return idxRec.InstanceDescription;
1947 }
1948 
1949 
getPresentationLabel()1950 const char *DVInterface::getPresentationLabel()
1951 {
1952     return idxRec.PresentationLabel;
1953 }
1954 
1955 
instanceReviewed(int pos)1956 OFCondition DVInterface::instanceReviewed(int pos)
1957 {
1958     lockDatabase();
1959     OFBool wasNew = newInstancesReceived();
1960     if (pHandle == NULL) return EC_IllegalCall;
1961     pHandle->DB_unlock();
1962     OFCondition result = pHandle->instanceReviewed(pos);
1963     pHandle->DB_lock(OFFalse);
1964     if (!wasNew) resetDatabaseReferenceTime();
1965     releaseDatabase();
1966     return result;
1967 }
1968 
1969 
instanceReviewed(const char * studyUID,const char * seriesUID,const char * instanceUID)1970 OFCondition DVInterface::instanceReviewed(const char *studyUID,
1971                                           const char *seriesUID,
1972                                           const char *instanceUID)
1973 {
1974     OFCondition result = EC_IllegalCall;
1975     DVInstanceCache::ItemStruct *instance = getInstanceStruct(studyUID, seriesUID, instanceUID);
1976     if (instance != NULL)
1977     {
1978         if (instance->Status == DVIF_objectIsNotNew)
1979             result = EC_Normal;
1980         else
1981             result = instanceReviewed(instance->Pos);
1982     }
1983     return result;
1984 }
1985 
1986 
findStudyIdx(StudyDescRecord * study,const char * uid)1987 int DVInterface::findStudyIdx(StudyDescRecord *study,
1988                               const char *uid)
1989 {
1990     if ((study != NULL) && (uid != NULL))
1991     {
1992         int i = 0;
1993         for (i = 0; i < PSTAT_MAXSTUDYCOUNT; i++)
1994         {
1995             if (strcmp(uid, study[i].StudyInstanceUID) == 0)
1996                 return i;
1997         }
1998     }
1999     return -1;
2000 }
2001 
2002 
deleteImageFile(const char * filename)2003 int DVInterface::deleteImageFile(const char *filename)
2004 {
2005     if ((filename != NULL) && (pHandle != NULL))
2006     {
2007         const char *pos;
2008         if (((pos = strrchr(filename, OFstatic_cast(int, PATH_SEPARATOR))) == NULL) ||   // check whether image file resides in index.dat directory
2009             (strncmp(filename, pHandle->getStorageArea(), pos - filename) == 0))
2010         {
2011 //            DB_deleteImageFile((/*const */char *)filename);
2012             if (unlink(filename) == 0)
2013                 return 1;                                                 // image file has been deleted
2014         }
2015         return 2;                                                         // image file has not been deleted
2016     }
2017     return 0;                                                             // given filename is invalid
2018 }
2019 
2020 
deleteStudy(const char * studyUID)2021 OFCondition DVInterface::deleteStudy(const char *studyUID)
2022 {
2023     DVStudyCache::ItemStruct *study = getStudyStruct(studyUID);
2024     if (study != NULL)
2025     {
2026         OFCondition result = EC_IllegalCall;
2027         OFBool wasNew = OFTrue;
2028         if (lockExclusive() == EC_Normal)
2029         {
2030             wasNew = newInstancesReceived();
2031             if (study->List.gotoFirst())
2032             {
2033                 StudyDescRecord *study_desc = OFstatic_cast(StudyDescRecord *, malloc(SIZEOF_STUDYDESC));
2034                 if (study_desc != NULL)
2035                 {
2036                     if (pHandle->DB_GetStudyDesc(study_desc).good())
2037                     {
2038                         int idx = findStudyIdx(study_desc, studyUID);
2039                         if (idx >= 0)
2040                         {
2041                             do /* for all series */
2042                             {
2043                                 DVSeriesCache::ItemStruct *series = study->List.getItem();
2044                                 if (series != NULL)
2045                                 {
2046                                     if (series->List.gotoFirst())
2047                                     {
2048                                         do /* for all instances */
2049                                         {
2050                                             pHandle->DB_IdxRemove(series->List.getPos());
2051                                             deleteImageFile(series->List.getFilename());
2052                                         } while (series->List.gotoNext());
2053                                     }
2054                                 }
2055                             } while (study->List.gotoNext());
2056                             study_desc[idx].NumberofRegistratedImages = 0;
2057                             study_desc[idx].StudySize = 0;
2058                             pHandle->DB_StudyDescChange(study_desc);
2059                         }
2060                     }
2061                     free(study_desc);
2062                 }
2063             }
2064         }
2065         unlockExclusive();
2066         if (!wasNew)
2067             resetDatabaseReferenceTime();
2068         return result;
2069     }
2070     return EC_IllegalCall;
2071 }
2072 
2073 
deleteSeries(const char * studyUID,const char * seriesUID)2074 OFCondition DVInterface::deleteSeries(const char *studyUID,
2075                                       const char *seriesUID)
2076 {
2077     DVSeriesCache::ItemStruct *series = getSeriesStruct(studyUID, seriesUID);
2078     if (series != NULL)
2079     {
2080         OFCondition result = EC_IllegalCall;
2081         OFBool wasNew = OFTrue;
2082         if (lockExclusive() == EC_Normal)
2083         {
2084             wasNew = newInstancesReceived();
2085             if (series->List.gotoFirst())
2086             {
2087                 StudyDescRecord *study_desc = OFstatic_cast(StudyDescRecord *, malloc(SIZEOF_STUDYDESC));
2088                 if (study_desc != NULL)
2089                 {
2090                     if (pHandle->DB_GetStudyDesc(study_desc).good())
2091                     {
2092                         int idx = findStudyIdx(study_desc, studyUID);
2093                         if (idx >= 0)
2094                         {
2095                             do /* for all images */
2096                             {
2097                                 pHandle->DB_IdxRemove(series->List.getPos());
2098                                 if (study_desc[idx].NumberofRegistratedImages > 0)
2099                                 {
2100                                     study_desc[idx].NumberofRegistratedImages--;
2101                                     study_desc[idx].StudySize -= series->List.getImageSize();
2102                                 }
2103                                 deleteImageFile(series->List.getFilename());
2104                             } while (series->List.gotoNext());
2105                             pHandle->DB_StudyDescChange(study_desc);
2106                         }
2107                     }
2108                     free(study_desc);
2109                 }
2110             }
2111         }
2112         unlockExclusive();
2113         if (!wasNew)
2114             resetDatabaseReferenceTime();
2115         return result;
2116     }
2117     return EC_IllegalCall;
2118 }
2119 
2120 
2121 
deleteInstance(const char * studyUID,const char * seriesUID,const char * instanceUID)2122 OFCondition DVInterface::deleteInstance(const char *studyUID,
2123                                         const char *seriesUID,
2124                                         const char *instanceUID)
2125 {
2126     DVSeriesCache::ItemStruct *series = getSeriesStruct(studyUID, seriesUID, instanceUID);
2127     if (series != NULL)
2128     {
2129         OFCondition result = EC_IllegalCall;
2130         OFBool wasNew = OFTrue;
2131         if (lockExclusive() == EC_Normal)
2132         {
2133             wasNew = newInstancesReceived();
2134             pHandle->DB_IdxRemove(series->List.getPos());
2135             StudyDescRecord *study_desc = OFstatic_cast(StudyDescRecord *, malloc(SIZEOF_STUDYDESC));
2136             if (study_desc != NULL)
2137             {
2138                 if (pHandle->DB_GetStudyDesc(study_desc).good())
2139                 {
2140                     int i = 0;
2141                     for (i = 0; i < PSTAT_MAXSTUDYCOUNT; i++)
2142                     {
2143                         if (strcmp(studyUID, study_desc[i].StudyInstanceUID) != 0)
2144                         {
2145                             if (study_desc[i].NumberofRegistratedImages > 0)
2146                             {
2147                                 study_desc[i].NumberofRegistratedImages--;
2148                                 study_desc[i].StudySize -= series->List.getImageSize();
2149                                 pHandle->DB_StudyDescChange(study_desc);
2150                             }
2151                             break;
2152                         }
2153                     }
2154                     free(study_desc);
2155                     result = EC_Normal;
2156                 }
2157                 deleteImageFile(series->List.getFilename());
2158             }
2159         }
2160         unlockExclusive();
2161         if (!wasNew)
2162             resetDatabaseReferenceTime();
2163         return result;
2164     }
2165     return EC_IllegalCall;
2166 }
2167 
2168 
isDisplayTransformPossible(DVPSDisplayTransform transform)2169 OFBool DVInterface::isDisplayTransformPossible(DVPSDisplayTransform transform)
2170 {
2171     if (transform == DVPSD_none)
2172         return OFFalse;
2173     return (displayFunction[transform] != NULL);
2174 }
2175 
2176 
setAmbientLightValue(double value)2177 OFCondition DVInterface::setAmbientLightValue(double value)
2178 {
2179     OFCondition result = EC_IllegalCall;
2180     for (int i = DVPSD_first; i < DVPSD_max; i++)
2181     {
2182         if ((displayFunction[i] != NULL) && (displayFunction[i]->setAmbientLightValue(value)))
2183             result = EC_Normal;         // at least one display function has been valid
2184     }
2185     return result;
2186 }
2187 
2188 
getAmbientLightValue(double & value)2189 OFCondition DVInterface::getAmbientLightValue(double &value)
2190 {
2191     if (displayFunction[DVPSD_first] != NULL)
2192     {
2193         value = displayFunction[DVPSD_first]->getAmbientLightValue();
2194         return EC_Normal;
2195     }
2196     return EC_IllegalCall;
2197 }
2198 
2199 
sendIOD(const char * targetID,const char * studyUID,const char * seriesUID,const char * instanceUID)2200 OFCondition DVInterface::sendIOD(const char * targetID,
2201                                  const char * studyUID,
2202                                  const char * seriesUID,
2203                                  const char * instanceUID)
2204 {
2205   if ((targetID==NULL)||(studyUID==NULL)) return EC_IllegalCall;
2206   const char *sender_application = getSenderName();
2207   if (sender_application==NULL) return EC_IllegalCall;
2208   if (configPath.empty()) return EC_IllegalCall;
2209 
2210   DVPSHelper::cleanChildren(); // clean up old child processes before creating new ones
2211 
2212 #ifdef HAVE_FORK
2213   // Unix version - call fork() and execl()
2214   pid_t pid = fork();
2215   if (pid < 0)
2216   {
2217     // fork failed - return error code
2218     return EC_IllegalCall;
2219   } else if (pid > 0)
2220   {
2221     // we are the parent process
2222     return EC_Normal;
2223   } else {
2224     // we are the child process
2225     if (execl(sender_application, sender_application, configPath.c_str(),
2226             targetID, studyUID, seriesUID, instanceUID, OFreinterpret_cast(char *, 0)) < 0)
2227     {
2228       DCMPSTAT_ERROR("Unable to execute '" << sender_application << "'");
2229     }
2230     // if execl succeeds, this part will not get executed.
2231     // if execl fails, there is not much we can do except bailing out.
2232     abort();
2233   }
2234 #else
2235   // Windows version - call CreateProcess()
2236 
2237   // initialize startup info
2238   PROCESS_INFORMATION procinfo;
2239   STARTUPINFOA sinfo;
2240   OFBitmanipTemplate<char>::zeroMem((char *)&sinfo, sizeof(sinfo));
2241   sinfo.cb = sizeof(sinfo);
2242   char commandline[4096];
2243   if (seriesUID && instanceUID) sprintf(commandline, "%s %s %s %s %s %s", sender_application, configPath.c_str(),
2244       targetID, studyUID, seriesUID, instanceUID);
2245   else if (seriesUID) sprintf(commandline, "%s %s %s %s %s", sender_application, configPath.c_str(), targetID,
2246       studyUID, seriesUID);
2247   else sprintf(commandline, "%s %s %s %s", sender_application, configPath.c_str(), targetID, studyUID);
2248 #ifdef DEBUG
2249   if (CreateProcessA(NULL, commandline, NULL, NULL, 0, 0, NULL, NULL, &sinfo, &procinfo))
2250 #else
2251   if (CreateProcessA(NULL, commandline, NULL, NULL, 0, DETACHED_PROCESS, NULL, NULL, &sinfo, &procinfo))
2252 #endif
2253   {
2254     return EC_Normal;
2255   } else {
2256     DCMPSTAT_ERROR("Unable to execute '" << sender_application << "'");
2257   }
2258 
2259 #endif
2260   return EC_IllegalCall;
2261 }
2262 
2263 
startReceiver()2264 OFCondition DVInterface::startReceiver()
2265 {
2266   const char *receiver_application = getReceiverName();
2267   if (receiver_application==NULL) return EC_IllegalCall;
2268   if (configPath.empty()) return EC_IllegalCall;
2269 
2270   OFCondition result = EC_Normal;
2271   DCMPSTAT_LOGFILE("Starting network receiver processes ...");
2272 
2273   Uint32 numberOfReceivers = getNumberOfTargets(DVPSE_receiver);
2274   for (Uint32 i=0; i < numberOfReceivers; i++)
2275   {
2276     DVPSHelper::cleanChildren(); // clean up old child processes before creating new ones
2277 #ifdef HAVE_FORK
2278     // Unix version - call fork() and execl()
2279     pid_t pid = fork();
2280     if (pid < 0)
2281     {
2282       // fork failed - set error code
2283       result = EC_IllegalCall;
2284     } else if (pid > 0)
2285     {
2286       // we are the parent process, continue loop
2287     } else {
2288       // we are the child process
2289       if (execl(receiver_application, receiver_application, configPath.c_str(), getTargetID(i, DVPSE_receiver), OFreinterpret_cast(char *, 0)) < 0)
2290       {
2291           DCMPSTAT_ERROR("Unable to execute '" << receiver_application << "'");
2292       }
2293       // if execl succeeds, this part will not get executed.
2294       // if execl fails, there is not much we can do except bailing out.
2295       abort();
2296     }
2297 #else
2298     // Windows version - call CreateProcess()
2299     // initialize startup info
2300     PROCESS_INFORMATION procinfo;
2301     STARTUPINFOA sinfo;
2302     OFBitmanipTemplate<char>::zeroMem((char *)&sinfo, sizeof(sinfo));
2303     sinfo.cb = sizeof(sinfo);
2304     char commandline[4096];
2305     sprintf(commandline, "%s %s %s", receiver_application, configPath.c_str(), getTargetID(i, DVPSE_receiver));
2306 #ifdef DEBUG
2307     if (CreateProcessA(NULL, commandline, NULL, NULL, 0, 0, NULL, NULL, &sinfo, &procinfo))
2308 #else
2309     if (CreateProcessA(NULL, commandline, NULL, NULL, 0, DETACHED_PROCESS, NULL, NULL, &sinfo, &procinfo))
2310 #endif
2311     {
2312       // continue loop
2313     } else {
2314         DCMPSTAT_ERROR("Unable to execute '" << receiver_application << "'");
2315         result = EC_IllegalCall;
2316     }
2317 #endif
2318   }
2319   return result;
2320 }
2321 
terminateReceiver()2322 OFCondition DVInterface::terminateReceiver()
2323 {
2324   const char *receiver_application = getReceiverName();
2325   if (receiver_application==NULL) return EC_IllegalCall;
2326   if (configPath.empty()) return EC_IllegalCall;
2327 
2328   OFCondition result = EC_Normal;
2329   DCMPSTAT_LOGFILE("Terminating network receiver processes ...");
2330 
2331   DVPSHelper::cleanChildren(); // clean up old child processes before creating new ones
2332 #ifdef HAVE_FORK
2333   // Unix version - call fork() and execl()
2334   pid_t pid = fork();
2335   if (pid < 0)
2336   {
2337     // fork failed - set error code
2338     result = EC_IllegalCall;
2339   } else if (pid > 0)
2340   {
2341     // we are the parent process, continue loop
2342   } else {
2343     // we are the child process
2344     if (execl(receiver_application, receiver_application, configPath.c_str(), "--terminate", OFreinterpret_cast(char *, 0)) < 0)
2345     {
2346         DCMPSTAT_ERROR("Unable to execute '" << receiver_application << "'");
2347     }
2348     // if execl succeeds, this part will not get executed.
2349     // if execl fails, there is not much we can do except bailing out.
2350     abort();
2351   }
2352 #else
2353   // Windows version - call CreateProcess()
2354   // initialize startup info
2355   PROCESS_INFORMATION procinfo;
2356   STARTUPINFOA sinfo;
2357   OFBitmanipTemplate<char>::zeroMem((char *)&sinfo, sizeof(sinfo));
2358   sinfo.cb = sizeof(sinfo);
2359   char commandline[4096];
2360   sprintf(commandline, "%s %s %s", receiver_application, configPath.c_str(), "--terminate");
2361 #ifdef DEBUG
2362   if (CreateProcessA(NULL, commandline, NULL, NULL, 0, 0, NULL, NULL, &sinfo, &procinfo))
2363 #else
2364   if (CreateProcessA(NULL, commandline, NULL, NULL, 0, DETACHED_PROCESS, NULL, NULL, &sinfo, &procinfo))
2365 #endif
2366   {
2367     // continue loop
2368   } else {
2369       DCMPSTAT_ERROR("Unable to execute '" << receiver_application << "'");
2370       result = EC_IllegalCall;
2371   }
2372 #endif
2373   return result;
2374 }
2375 
2376 
startQueryRetrieveServer()2377 OFCondition DVInterface::startQueryRetrieveServer()
2378 {
2379   const char *server_application = getQueryRetrieveServerName();
2380   if (server_application==NULL) return EC_IllegalCall;
2381   if (configPath.empty()) return EC_IllegalCall;
2382 
2383   OFString config_filename = getQueryRetrieveServerName();
2384   config_filename += ".cfg";
2385   if (getQueryRetrieveAutoCreateConfigFile())
2386     createQueryRetrieveServerConfigFile(config_filename.c_str());
2387 
2388   DCMPSTAT_LOGFILE("Starting query/retrieve server process ...");
2389 
2390   DVPSHelper::cleanChildren(); // clean up old child processes before creating new ones
2391 
2392   Sint32 timeout = getQueryRetrieveTimeout();
2393 
2394 #ifdef HAVE_FORK
2395   // Unix version - call fork() and execl()
2396   pid_t pid = fork();
2397   if (pid < 0)
2398   {
2399     // fork failed - return error code
2400     return EC_IllegalCall;
2401   } else if (pid > 0)
2402   {
2403     // we are the parent process
2404     return EC_Normal;
2405   } else {
2406     // we are the child process
2407     if (timeout > 0)
2408     {
2409       char str_timeout[20];
2410       sprintf(str_timeout, "%lu", OFstatic_cast(unsigned long, timeout));
2411       execl(server_application, server_application, "-c", config_filename.c_str(), "--allow-shutdown",
2412         "--timeout", str_timeout, OFreinterpret_cast(char *, 0));
2413     }
2414     else
2415     {
2416       execl(server_application, server_application, "-c", config_filename.c_str(), "--allow-shutdown", OFreinterpret_cast(char *, 0));
2417     }
2418 
2419     DCMPSTAT_ERROR("Unable to execute '" << server_application << "'");
2420 
2421     // if execl succeeds, this part will not get executed.
2422     // if execl fails, there is not much we can do except bailing out.
2423     abort();
2424   }
2425 #else
2426   // Windows version - call CreateProcess()
2427   // initialize startup info
2428   PROCESS_INFORMATION procinfo;
2429   STARTUPINFOA sinfo;
2430   OFBitmanipTemplate<char>::zeroMem((char *)&sinfo, sizeof(sinfo));
2431   sinfo.cb = sizeof(sinfo);
2432   char commandline[4096];
2433 
2434   if (timeout > 0)
2435   {
2436     sprintf(commandline, "%s -c %s --allow-shutdown --timeout %lu",
2437       server_application, config_filename.c_str(), (unsigned long) timeout);
2438   }
2439   else
2440   {
2441     sprintf(commandline, "%s -c %s --allow-shutdown", server_application, config_filename.c_str());
2442   }
2443 
2444 #ifdef DEBUG
2445   if (CreateProcessA(NULL, commandline, NULL, NULL, 0, 0, NULL, NULL, &sinfo, &procinfo))
2446 #else
2447   if (CreateProcessA(NULL, commandline, NULL, NULL, 0, DETACHED_PROCESS, NULL, NULL, &sinfo, &procinfo))
2448 #endif
2449   {
2450     return EC_Normal;
2451   } else {
2452       DCMPSTAT_ERROR("Unable to execute '" << server_application << "'");
2453   }
2454 #endif
2455   return EC_IllegalCall;
2456 }
2457 
terminateQueryRetrieveServer()2458 OFCondition DVInterface::terminateQueryRetrieveServer()
2459 {
2460   if (getQueryRetrieveServerName()==NULL) return EC_IllegalCall;
2461   if (configPath.empty()) return EC_IllegalCall;
2462 
2463   OFStandard::initializeNetwork();
2464 
2465   OFCondition result = EC_Normal;
2466   T_ASC_Network *net=NULL;
2467   T_ASC_Parameters *params=NULL;
2468   DIC_NODENAME peerHost;
2469   T_ASC_Association *assoc=NULL;
2470 
2471   DCMPSTAT_LOGFILE("Terminating query/retrieve server process ...");
2472 
2473   OFCondition cond = ASC_initializeNetwork(NET_REQUESTOR, 0, 30, &net);
2474   if (cond.good())
2475   {
2476     cond = ASC_createAssociationParameters(&params, DEFAULT_MAXPDU);
2477     if (cond.good())
2478     {
2479       ASC_setAPTitles(params, getNetworkAETitle(), getQueryRetrieveAETitle(), NULL);
2480       sprintf(peerHost, "localhost:%d", OFstatic_cast(int, getQueryRetrievePort()));
2481       ASC_setPresentationAddresses(params, OFStandard::getHostName().c_str(), peerHost);
2482 
2483       const char* transferSyntaxes[] = { UID_LittleEndianImplicitTransferSyntax };
2484       cond = ASC_addPresentationContext(params, 1, UID_PrivateShutdownSOPClass, transferSyntaxes, 1);
2485       if (cond.good())
2486       {
2487         cond = ASC_requestAssociation(net, params, &assoc);
2488         if (cond.good()) ASC_abortAssociation(assoc); // tear down association if necessary
2489         ASC_dropAssociation(assoc);
2490         ASC_destroyAssociation(&assoc);
2491       }
2492     } else result = EC_IllegalCall;
2493     ASC_dropNetwork(&net);
2494   } else result = EC_IllegalCall;
2495 
2496   OFStandard::shutdownNetwork();
2497 
2498   return result;
2499 }
2500 
createQueryRetrieveServerConfigFile(const char * filename)2501 OFCondition DVInterface::createQueryRetrieveServerConfigFile(const char *filename)
2502 {
2503   STD_NAMESPACE ofstream output(filename);
2504   if (output)
2505   {
2506     DCMPSTAT_LOGFILE("Creating configuration file for query/retrieve server");
2507     output << "# ATTENTION: This file has been created automatically and will" << OFendl;
2508     output << "#            be re-created each time the query/retrieve server" << OFendl;
2509     output << "#            is started.  To avoid that manual changes to this" << OFendl;
2510     output << "#            file are destroyed, the flag AutoCreateConfigFile" << OFendl;
2511     output << "#            in the configuration file '" << configPath << "' has to be" << OFendl;
2512     output << "#            switched off." << OFendl;
2513     output << OFendl;
2514     output << "NetworkType     = \"tcp\"" << OFendl;
2515     output << "NetworkTCPPort  = " << getQueryRetrievePort() << OFendl;
2516     output << "MaxPDUSize      = " << getQueryRetrieveMaxPDU() << OFendl;
2517     output << "MaxAssociations = " << getQueryRetrieveMaxAssociations() << OFendl;
2518     output << "Display         = \"no\"" << OFendl;
2519     output << OFendl;
2520     output << "HostTable BEGIN" << OFendl;
2521     const char *aet = NULL;
2522     const char *name = NULL;
2523     const Uint32 count = getNumberOfTargets();
2524     for (Uint32 i = 0; i < count; i++)
2525     {
2526       const char *id = getTargetID(i);
2527       if (id != NULL)
2528       {
2529           aet = getTargetAETitle(id);
2530           name = getTargetHostname(id);
2531           if ((aet != NULL) && (name != NULL))
2532             output << id << " = (" << aet << ", " << name << ", " << getTargetPort(id) << ")" << OFendl;
2533       }
2534     }
2535     output << "HostTable END" << OFendl;
2536     output << OFendl;
2537     output << "AETable BEGIN" << OFendl;
2538     output << getQueryRetrieveAETitle() << "\t" << getDatabaseFolder() << "\tR\t(";
2539     output << PSTAT_MAXSTUDYCOUNT << ", " << PSTAT_STUDYSIZE / 1024 / 1024 << "mb)\tANY" << OFendl;
2540     output << "AETable END" << OFendl;
2541     return EC_Normal;
2542   }
2543   DCMPSTAT_LOGFILE("Could not create configuration file for query/retrieve server");
2544   return EC_IllegalCall;
2545 }
2546 
saveDICOMImage(const char * filename,const void * pixelData,unsigned long width,unsigned long height,double aspectRatio,OFBool explicitVR,const char * instanceUID)2547 OFCondition DVInterface::saveDICOMImage(
2548   const char *filename,
2549   const void *pixelData,
2550   unsigned long width,
2551   unsigned long height,
2552   double aspectRatio,
2553   OFBool explicitVR,
2554   const char *instanceUID)
2555 {
2556     if ((width<1)||(width > 0xFFFF)) return EC_IllegalCall;
2557     if ((height<1)||(height > 0xFFFF)) return EC_IllegalCall;
2558     if (pixelData == NULL) return EC_IllegalCall;
2559     if (filename == NULL) return EC_IllegalCall;
2560     if (aspectRatio == 0.0) return EC_IllegalCall;
2561 
2562     Uint16 columns = OFstatic_cast(Uint16, width);
2563     Uint16 rows = OFstatic_cast(Uint16, height);
2564     OFCondition status = EC_Normal;
2565     DcmFileFormat *fileformat = new DcmFileFormat();
2566     DcmDataset *dataset = NULL;
2567     if (fileformat) dataset=fileformat->getDataset();
2568     char newuid[70];
2569 
2570     if (dataset)
2571     {
2572       if (EC_Normal==status) status = DVPSHelper::putStringValue(dataset, DCM_PatientName);
2573       if (EC_Normal==status) status = DVPSHelper::putStringValue(dataset, DCM_PatientID);
2574       if (EC_Normal==status) status = DVPSHelper::putStringValue(dataset, DCM_PatientBirthDate);
2575       if (EC_Normal==status) status = DVPSHelper::putStringValue(dataset, DCM_PatientSex);
2576       if (EC_Normal==status) status = DVPSHelper::putStringValue(dataset, DCM_StudyDate);
2577       if (EC_Normal==status) status = DVPSHelper::putStringValue(dataset, DCM_StudyTime);
2578       if (EC_Normal==status) status = DVPSHelper::putStringValue(dataset, DCM_ReferringPhysicianName);
2579       if (EC_Normal==status) status = DVPSHelper::putStringValue(dataset, DCM_StudyID);
2580       if (EC_Normal==status) status = DVPSHelper::putStringValue(dataset, DCM_AccessionNumber);
2581       if (EC_Normal==status) status = DVPSHelper::putStringValue(dataset, DCM_SeriesNumber);
2582       if (EC_Normal==status) status = DVPSHelper::putStringValue(dataset, DCM_InstanceNumber);
2583       if (EC_Normal==status) status = DVPSHelper::putStringValue(dataset, DCM_PatientOrientation);
2584       if (EC_Normal==status) status = DVPSHelper::putStringValue(dataset, DCM_SOPClassUID, UID_SecondaryCaptureImageStorage);
2585       if (EC_Normal==status) status = DVPSHelper::putStringValue(dataset, DCM_Modality, "OT");
2586       if (EC_Normal==status) status = DVPSHelper::putStringValue(dataset, DCM_ConversionType, "WSD");
2587       if (EC_Normal==status) status = DVPSHelper::putStringValue(dataset, DCM_PhotometricInterpretation, "MONOCHROME2");
2588       dcmGenerateUniqueIdentifier(newuid);
2589       if (EC_Normal==status) status = DVPSHelper::putStringValue(dataset, DCM_SOPInstanceUID, (instanceUID ? instanceUID : newuid));
2590       dcmGenerateUniqueIdentifier(newuid, SITE_SERIES_UID_ROOT);
2591       if (EC_Normal==status) status = DVPSHelper::putStringValue(dataset, DCM_SeriesInstanceUID, newuid);
2592       dcmGenerateUniqueIdentifier(newuid, SITE_STUDY_UID_ROOT);
2593       if (EC_Normal==status) status = DVPSHelper::putStringValue(dataset, DCM_StudyInstanceUID, newuid);
2594       if (EC_Normal==status) status = DVPSHelper::putUint16Value(dataset, DCM_SamplesPerPixel, 1);
2595       if (EC_Normal==status) status = DVPSHelper::putUint16Value(dataset, DCM_Rows, rows);
2596       if (EC_Normal==status) status = DVPSHelper::putUint16Value(dataset, DCM_Columns, columns);
2597       if (EC_Normal==status) status = DVPSHelper::putUint16Value(dataset, DCM_BitsAllocated, 8);
2598       if (EC_Normal==status) status = DVPSHelper::putUint16Value(dataset, DCM_BitsStored, 8);
2599       if (EC_Normal==status) status = DVPSHelper::putUint16Value(dataset, DCM_HighBit, 7);
2600       if (EC_Normal==status) status = DVPSHelper::putUint16Value(dataset, DCM_PixelRepresentation, 0);
2601       if ((EC_Normal==status)&&(aspectRatio != 1.0))
2602       {
2603         sprintf(newuid, "%ld\\%ld", 1000L, OFstatic_cast(long, aspectRatio*1000.0));
2604         status = DVPSHelper::putStringValue(dataset, DCM_PixelAspectRatio, newuid);
2605       }
2606       DcmPolymorphOBOW *pxData = new DcmPolymorphOBOW(DCM_PixelData);
2607       if (pxData)
2608       {
2609         status = pxData->putUint8Array(OFstatic_cast(Uint8 *, OFconst_cast(void *, pixelData)), OFstatic_cast(unsigned long, width*height));
2610         if (EC_Normal==status) status = dataset->insert(pxData, OFTrue /*replaceOld*/); else delete pxData;
2611       } else status = EC_MemoryExhausted;
2612 
2613       if (status != EC_Normal)
2614         DCMPSTAT_LOGFILE("Save image to file failed: invalid data structures");
2615 
2616       if (EC_Normal == status)
2617       {
2618         status = DVPSHelper::saveFileFormat(filename, fileformat, explicitVR);
2619         if (status != EC_Normal)
2620           DCMPSTAT_LOGFILE("Save image to file failed: could not write fileformat");
2621       }
2622     } else {
2623       status = EC_MemoryExhausted;
2624       DCMPSTAT_LOGFILE("Save image to file failed: memory exhausted");
2625     }
2626 
2627     delete fileformat;
2628     return status;
2629 }
2630 
2631 
saveDICOMImage(const void * pixelData,unsigned long width,unsigned long height,double aspectRatio)2632 OFCondition DVInterface::saveDICOMImage(
2633   const void *pixelData,
2634   unsigned long width,
2635   unsigned long height,
2636   double aspectRatio)
2637 {
2638   // release database lock since we are using the DB module directly
2639   releaseDatabase();
2640 
2641   char uid[100];
2642   dcmGenerateUniqueIdentifier(uid);
2643 
2644   DcmQueryRetrieveDatabaseStatus dbStatus(STATUS_Success);
2645   char imageFileName[MAXPATHLEN+1];
2646 
2647   OFCondition result = EC_Normal;
2648   DcmQueryRetrieveIndexDatabaseHandle handle(getDatabaseFolder(), PSTAT_MAXSTUDYCOUNT, PSTAT_STUDYSIZE, result);
2649   if (result.bad())
2650   {
2651     DCMPSTAT_LOGFILE("Save image to database failed: could not lock index file");
2652     return result;
2653   }
2654 
2655   if (handle.makeNewStoreFileName(UID_SecondaryCaptureImageStorage, uid, imageFileName, sizeof(imageFileName)).good())
2656   {
2657      // now store presentation state as filename
2658      result = saveDICOMImage(imageFileName, pixelData, width, height, aspectRatio, OFTrue, uid);
2659      if (EC_Normal==result)
2660      {
2661        if (handle.storeRequest(UID_SecondaryCaptureImageStorage, uid, imageFileName, &dbStatus).bad())
2662        {
2663          result = EC_IllegalCall;
2664          DCMPSTAT_LOGFILE("Save image to database failed: could not register in index file");
2665          DCMPSTAT_WARN("Unable to register secondary capture image '" << imageFileName << "' in database");
2666        }
2667      }
2668   }
2669   return result;
2670 }
2671 
2672 
saveHardcopyGrayscaleImage(const char * filename,const void * pixelData,unsigned long width,unsigned long height,double aspectRatio,OFBool explicitVR,const char * instanceUID)2673 OFCondition DVInterface::saveHardcopyGrayscaleImage(
2674   const char *filename,
2675   const void *pixelData,
2676   unsigned long width,
2677   unsigned long height,
2678   double aspectRatio,
2679   OFBool explicitVR,
2680   const char *instanceUID)
2681 {
2682     if (pState == NULL) return EC_IllegalCall;
2683     if (pPrint == NULL) return EC_IllegalCall;
2684 
2685     if ((width<1)||(width > 0xFFFF)) return EC_IllegalCall;
2686     if ((height<1)||(height > 0xFFFF)) return EC_IllegalCall;
2687     if (pixelData == NULL) return EC_IllegalCall;
2688     if (filename == NULL) return EC_IllegalCall;
2689     if (aspectRatio == 0.0) return EC_IllegalCall;
2690 
2691     Uint16 columns = OFstatic_cast(Uint16, width);
2692     Uint16 rows = OFstatic_cast(Uint16, height);
2693     OFCondition status = EC_Normal;
2694     DcmFileFormat *fileformat = new DcmFileFormat();
2695     DcmDataset *dataset = NULL;
2696     if (fileformat) dataset=fileformat->getDataset();
2697     char newuid[70];
2698     OFString aString;
2699     OFString theInstanceUID;
2700 
2701     if (dataset)
2702     {
2703       // write patient module
2704       if (EC_Normal==status) status = pState->writeHardcopyImageAttributes(*dataset);
2705       // write general study and general series module
2706       if (EC_Normal==status) status = pPrint->writeHardcopyImageAttributes(*dataset);
2707 
2708       // Hardcopy Equipment Module
2709       if (EC_Normal==status) status = DVPSHelper::putStringValue(dataset, DCM_RETIRED_HardcopyDeviceManufacturer, "OFFIS");
2710       if (EC_Normal==status) status = DVPSHelper::putStringValue(dataset, DCM_RETIRED_HardcopyDeviceSoftwareVersion, OFFIS_DTK_IMPLEMENTATION_VERSION_NAME);
2711 
2712       // General Image Module
2713       if (EC_Normal==status) status = DVPSHelper::putStringValue(dataset, DCM_InstanceNumber);
2714       if (EC_Normal==status) status = DVPSHelper::putStringValue(dataset, DCM_PatientOrientation);
2715       if (EC_Normal==status) status = DVPSHelper::putStringValue(dataset, DCM_ImageType, "DERIVED\\SECONDARY");
2716       if (EC_Normal==status) status = DVPSHelper::putStringValue(dataset, DCM_DerivationDescription, "Hardcopy rendered using Presentation State");
2717       // source image sequence is written in pState->writeHardcopyImageAttributes().
2718 
2719       // SOP Common Module
2720       if (EC_Normal==status) status = DVPSHelper::putStringValue(dataset, DCM_SOPClassUID, UID_RETIRED_HardcopyGrayscaleImageStorage);
2721       dcmGenerateUniqueIdentifier(newuid);
2722       theInstanceUID = (instanceUID ? instanceUID : newuid);
2723       if (EC_Normal==status) status = DVPSHelper::putStringValue(dataset, DCM_SOPInstanceUID, theInstanceUID.c_str());
2724       DVPSHelper::currentDate(aString);
2725       if (EC_Normal==status) status = DVPSHelper::putStringValue(dataset, DCM_InstanceCreationDate, aString.c_str());
2726       DVPSHelper::currentTime(aString);
2727       if (EC_Normal==status) status = DVPSHelper::putStringValue(dataset, DCM_InstanceCreationTime, aString.c_str());
2728 
2729       // Hardcopy Grayscale Image Module
2730       if (EC_Normal==status) status = DVPSHelper::putStringValue(dataset, DCM_PhotometricInterpretation, "MONOCHROME2");
2731       if (EC_Normal==status) status = DVPSHelper::putUint16Value(dataset, DCM_SamplesPerPixel, 1);
2732       if (EC_Normal==status) status = DVPSHelper::putUint16Value(dataset, DCM_Rows, rows);
2733       if (EC_Normal==status) status = DVPSHelper::putUint16Value(dataset, DCM_Columns, columns);
2734       if (EC_Normal==status) status = DVPSHelper::putUint16Value(dataset, DCM_BitsAllocated, 16);
2735       if (EC_Normal==status) status = DVPSHelper::putUint16Value(dataset, DCM_BitsStored, 12);
2736       if (EC_Normal==status) status = DVPSHelper::putUint16Value(dataset, DCM_HighBit, 11);
2737       if (EC_Normal==status) status = DVPSHelper::putUint16Value(dataset, DCM_PixelRepresentation, 0);
2738       if ((EC_Normal==status)&&(aspectRatio != 1.0))
2739       {
2740         sprintf(newuid, "%ld\\%ld", 1000L, OFstatic_cast(long, aspectRatio*1000.0));
2741         status = DVPSHelper::putStringValue(dataset, DCM_PixelAspectRatio, newuid);
2742       }
2743 
2744       DcmPolymorphOBOW *pxData = new DcmPolymorphOBOW(DCM_PixelData);
2745       if (pxData)
2746       {
2747         status = pxData->putUint16Array(OFstatic_cast(Uint16 *, OFconst_cast(void *, pixelData)), OFstatic_cast(unsigned long, width*height));
2748         if (EC_Normal==status) status = dataset->insert(pxData, OFTrue /*replaceOld*/); else delete pxData;
2749       } else status = EC_MemoryExhausted;
2750 
2751       // add Presentation LUT to hardcopy file if present, making it a Standard Extended SOP Class
2752       if ((EC_Normal==status)&&(pState->getPresentationLUT() == DVPSP_table))
2753       {
2754         status = pState->writePresentationLUTforPrint(*dataset);
2755       }
2756 
2757       if (status != EC_Normal)
2758         DCMPSTAT_LOGFILE("Save hardcopy grayscale image to file failed: invalid data structures");
2759 
2760       // save image file
2761       if (EC_Normal == status)
2762       {
2763         status = DVPSHelper::saveFileFormat(filename, fileformat, explicitVR);
2764         if (status != EC_Normal)
2765           DCMPSTAT_LOGFILE("Save hardcopy grayscale image to file failed: could not write fileformat");
2766       }
2767     } else {
2768       status = EC_MemoryExhausted;
2769       DCMPSTAT_LOGFILE("Save hardcopy grayscale image to file failed: memory exhausted");
2770     }
2771 
2772     if (EC_Normal == status)
2773     {
2774       OFString reqImageTmp;
2775       const char *reqImageSize = NULL;
2776       DVPSPresentationLUT *presLUT = pState->getPresentationLUTData();
2777 
2778       if (EC_Normal == pState->getPrintBitmapRequestedImageSize(reqImageTmp)) reqImageSize = reqImageTmp.c_str();
2779       /* we don't pass the patient ID (available as pState->getPatientID()) here because then
2780        * we could end up with multiple images being part of one study and one series, but having
2781        * different patient IDs. This might confuse archives using the patient root query model.
2782        */
2783       status = pPrint->addImageBox(getNetworkAETitle(), theInstanceUID.c_str(), reqImageSize, NULL, presLUT, pState->isMonochrome1Image());
2784     }
2785 
2786     delete fileformat;
2787     return status;
2788 }
2789 
2790 
saveHardcopyGrayscaleImage(const void * pixelData,unsigned long width,unsigned long height,double aspectRatio)2791 OFCondition DVInterface::saveHardcopyGrayscaleImage(
2792   const void *pixelData,
2793   unsigned long width,
2794   unsigned long height,
2795   double aspectRatio)
2796 {
2797   // release database lock since we are using the DB module directly
2798   releaseDatabase();
2799 
2800   char uid[100];
2801   dcmGenerateUniqueIdentifier(uid);
2802 
2803   DcmQueryRetrieveDatabaseStatus dbStatus(STATUS_Success);
2804   char imageFileName[MAXPATHLEN+1];
2805 
2806   OFCondition result=EC_Normal;
2807   DcmQueryRetrieveIndexDatabaseHandle handle(getDatabaseFolder(), PSTAT_MAXSTUDYCOUNT, PSTAT_STUDYSIZE, result);
2808   if (result.bad())
2809   {
2810     DCMPSTAT_LOGFILE("Save hardcopy grayscale image to database failed: could not lock index file");
2811     return result;
2812   }
2813 
2814   if (handle.makeNewStoreFileName(UID_RETIRED_HardcopyGrayscaleImageStorage, uid, imageFileName, sizeof(imageFileName)).good())
2815   {
2816      result = saveHardcopyGrayscaleImage(imageFileName, pixelData, width, height, aspectRatio, OFTrue, uid);
2817      if (EC_Normal==result)
2818      {
2819        if (handle.storeRequest(UID_RETIRED_HardcopyGrayscaleImageStorage, uid, imageFileName, &dbStatus).bad())
2820        {
2821          result = EC_IllegalCall;
2822          DCMPSTAT_LOGFILE("Save hardcopy grayscale image to database failed: could not register in index file");
2823          DCMPSTAT_WARN("Unable to register hardcopy grayscale image '" << imageFileName << "' in database");
2824        }
2825      }
2826   }
2827   return result;
2828 }
2829 
2830 
saveFileFormatToDB(DcmFileFormat & fileformat)2831 OFCondition DVInterface::saveFileFormatToDB(DcmFileFormat &fileformat)
2832 {
2833   // release database lock since we are using the DB module directly
2834   releaseDatabase();
2835 
2836   // get SOP class and instance UID from dataset
2837   char *classUID = NULL;
2838   char *instanceUID = NULL;
2839   DcmStack stack;
2840   DcmDataset *dset = fileformat.getDataset();
2841   if (dset)
2842   {
2843     if (EC_Normal == dset->search(DCM_SOPInstanceUID, stack, ESM_fromHere, OFFalse))
2844     {
2845       OFstatic_cast(DcmElement *, stack.top())->getString(instanceUID);
2846     }
2847     stack.clear();
2848     if (EC_Normal == dset->search(DCM_SOPClassUID, stack, ESM_fromHere, OFFalse))
2849     {
2850       OFstatic_cast(DcmElement *, stack.top())->getString(classUID);
2851     }
2852   }
2853   if ((instanceUID==NULL)||(classUID==NULL)) return EC_IllegalCall;
2854 
2855   DcmQueryRetrieveDatabaseStatus dbStatus(STATUS_Success);
2856   char imageFileName[MAXPATHLEN+1];
2857 
2858   OFCondition result=EC_Normal;
2859   DcmQueryRetrieveIndexDatabaseHandle handle(getDatabaseFolder(), PSTAT_MAXSTUDYCOUNT, PSTAT_STUDYSIZE, result);
2860   if (result.bad())
2861   {
2862     DCMPSTAT_LOGFILE("Save fileformat to database failed: could not lock index file");
2863     return result;
2864   }
2865 
2866   if (handle.makeNewStoreFileName(classUID, instanceUID, imageFileName, sizeof(imageFileName)).good())
2867   {
2868      // save image file
2869      result = DVPSHelper::saveFileFormat(imageFileName, &fileformat, OFTrue);
2870      if (EC_Normal==result)
2871      {
2872        if (handle.storeRequest(classUID, instanceUID, imageFileName, &dbStatus).bad())
2873        {
2874          result = EC_IllegalCall;
2875          DCMPSTAT_LOGFILE("Save fileformat to database failed: could not register in index file");
2876          DCMPSTAT_WARN("Unable to register file '" << imageFileName << "' in database");
2877        }
2878      }
2879   }
2880   return result;
2881 }
2882 
2883 
loadStoredPrint(const char * studyUID,const char * seriesUID,const char * instanceUID,OFBool changeStatus)2884 OFCondition DVInterface::loadStoredPrint(const char *studyUID, const char *seriesUID, const char *instanceUID, OFBool changeStatus)
2885 {
2886     OFCondition status = EC_IllegalCall;
2887     if (studyUID && seriesUID && instanceUID)
2888     {
2889         if (lockDatabase() == EC_Normal)
2890         {
2891             const char *filename = getFilename(studyUID, seriesUID, instanceUID);
2892             if (filename)
2893             {
2894                 if ((status = loadStoredPrint(filename)) == EC_Normal)
2895                 {
2896                     if (changeStatus)
2897                         instanceReviewed(studyUID, seriesUID, instanceUID);
2898                 }
2899             } else
2900                 DCMPSTAT_LOGFILE("Load stored print from database failed: UIDs not in index file");
2901         } else
2902             DCMPSTAT_LOGFILE("Load stored print from database failed: could not lock index file");
2903     } else
2904         DCMPSTAT_LOGFILE("Load stored print from database failed: invalid UIDs");
2905     return status;
2906 }
2907 
2908 
loadStoredPrint(const char * filename)2909 OFCondition DVInterface::loadStoredPrint(const char *filename)
2910 {
2911     OFCondition status = EC_IllegalCall;
2912     DcmFileFormat *fileformat = NULL;
2913     DVPSStoredPrint *print = new DVPSStoredPrint(getDefaultPrintIllumination(), getDefaultPrintReflection());
2914     if (print==NULL)
2915     {
2916         DCMPSTAT_LOGFILE("Load stored print from file failed: memory exhausted");
2917         return EC_MemoryExhausted;
2918     }
2919 
2920     if ((status = DVPSHelper::loadFileFormat(filename, fileformat)) == EC_Normal)
2921     {
2922         if (fileformat)
2923         {
2924             DcmDataset *dataset = fileformat->getDataset();
2925             if (dataset)
2926             {
2927                 if (EC_Normal == (status = print->read(*dataset)))
2928                 {
2929                     delete pPrint;
2930                     pPrint = print;
2931                     clearFilmSessionSettings();
2932                 }
2933             } else status = EC_CorruptedData;
2934             delete fileformat;
2935         } else status = EC_IllegalCall;
2936         if (status != EC_Normal)
2937             DCMPSTAT_LOGFILE("Load stored print from file failed: invalid data structures");
2938     } else
2939         DCMPSTAT_LOGFILE("Load stored print from file failed: could not read fileformat");
2940     if (status != EC_Normal)
2941     {
2942         delete print;
2943     }
2944     return status;
2945 }
2946 
2947 
saveStoredPrint(const char * filename,OFBool writeRequestedImageSize,OFBool explicitVR,const char * instanceUID)2948 OFCondition DVInterface::saveStoredPrint(
2949   const char *filename,
2950   OFBool writeRequestedImageSize,
2951   OFBool explicitVR,
2952   const char *instanceUID)
2953 {
2954     if (pState == NULL) return EC_IllegalCall;
2955     if (pPrint == NULL) return EC_IllegalCall;
2956     if (filename == NULL) return EC_IllegalCall;
2957 
2958     OFCondition status = EC_Normal;
2959     DcmFileFormat *fileformat = new DcmFileFormat();
2960     DcmDataset *dataset = NULL;
2961     if (fileformat)
2962         dataset = fileformat->getDataset();
2963 
2964     char newuid[70];
2965     char buf[32];
2966 
2967     /* set annotation if active */
2968     if (activateAnnotation)
2969     {
2970       OFString text;
2971       OFString dummy;
2972       if (prependDateTime)
2973       {
2974         OFDateTime::getCurrentDateTime().getISOFormattedDateTime(text, OFFalse /*showSeconds*/);
2975         text += " ";
2976       }
2977       if (prependPrinterName)
2978       {
2979         text += currentPrinter;
2980         text += " ";
2981       }
2982       if (prependLighting)
2983       {
2984         sprintf(buf, "%d/%d ", pPrint->getPrintIllumination(), pPrint->getPrintReflectedAmbientLight());
2985         text += buf;
2986       }
2987       text += annotationText;
2988       if (text.size() >64) text.erase(64); // limit to max annotation length
2989 
2990       if (getTargetPrinterSupportsAnnotationBoxSOPClass(currentPrinter.c_str()))
2991       {
2992         const char *displayformat = getTargetPrinterAnnotationDisplayFormatID(currentPrinter.c_str(), dummy);
2993         Uint16 position = getTargetPrinterAnnotationPosition(currentPrinter.c_str());
2994         pPrint->setSingleAnnotation(displayformat, text.c_str(), position);
2995       } else pPrint->deleteAnnotations();
2996       if (getTargetPrinterSessionLabelAnnotation(currentPrinter.c_str()))
2997       {
2998         status = setPrinterFilmSessionLabel(text.c_str());
2999       }
3000     } else {
3001       pPrint->deleteAnnotations();
3002     }
3003 
3004     if (dataset)
3005     {
3006       if (instanceUID) status = pPrint->setInstanceUID(instanceUID); else
3007       {
3008         dcmGenerateUniqueIdentifier(newuid);
3009         status = pPrint->setInstanceUID(newuid);
3010       }
3011       if (EC_Normal == status) status = pPrint->write(*dataset, writeRequestedImageSize, OFTrue, OFTrue, OFFalse);
3012 
3013       // save file
3014       if (EC_Normal == status) status = DVPSHelper::saveFileFormat(filename, fileformat, explicitVR);
3015 
3016       if (status != EC_Normal)
3017         DCMPSTAT_LOGFILE("Save stored print to file failed: could not write fileformat");
3018     } else {
3019       DCMPSTAT_LOGFILE("Save stored print to file failed: memory exhausted");
3020       status = EC_MemoryExhausted;
3021     }
3022 
3023     delete fileformat;
3024     return status;
3025 }
3026 
saveStoredPrint(OFBool writeRequestedImageSize)3027 OFCondition DVInterface::saveStoredPrint(OFBool writeRequestedImageSize)
3028 {
3029   // release database lock since we are using the DB module directly
3030   releaseDatabase();
3031 
3032   char uid[100];
3033   dcmGenerateUniqueIdentifier(uid);
3034 
3035   DcmQueryRetrieveDatabaseStatus dbStatus(STATUS_Success);
3036   char imageFileName[MAXPATHLEN+1];
3037   OFCondition result=EC_Normal;
3038   DcmQueryRetrieveIndexDatabaseHandle handle(getDatabaseFolder(), PSTAT_MAXSTUDYCOUNT, PSTAT_STUDYSIZE, result);
3039   if (result.bad())
3040   {
3041     DCMPSTAT_LOGFILE("Save stored print to database failed: could not lock index file");
3042     return result;
3043   }
3044 
3045   if (handle.makeNewStoreFileName(UID_RETIRED_StoredPrintStorage, uid, imageFileName, sizeof(imageFileName)).good())
3046   {
3047      // now store stored print object as filename
3048      result = saveStoredPrint(imageFileName, writeRequestedImageSize, OFTrue, uid);
3049      if (EC_Normal==result)
3050      {
3051        if (handle.storeRequest(UID_RETIRED_StoredPrintStorage, uid, imageFileName, &dbStatus).bad())
3052        {
3053          result = EC_IllegalCall;
3054          DCMPSTAT_LOGFILE("Save stored print to database failed: could not register in index file");
3055          DCMPSTAT_WARN("Unable to register stored print object '" << imageFileName << "' in database");
3056        }
3057      }
3058   }
3059   return result;
3060 }
3061 
getNumberOfPrintPreviews()3062 size_t DVInterface::getNumberOfPrintPreviews()
3063 {
3064   if (pPrint != NULL)
3065     return pPrint->getNumberOfImages();
3066   return 0;
3067 }
3068 
loadPrintPreview(size_t idx,OFBool printLUT,OFBool changeStatus)3069 OFCondition DVInterface::loadPrintPreview(size_t idx, OFBool printLUT, OFBool changeStatus)
3070 {
3071   OFCondition status = EC_IllegalCall;
3072   if ((pPrint != NULL) && (maximumPrintPreviewWidth > 0) && (maximumPrintPreviewHeight > 0))
3073   {
3074     const char *studyUID;
3075     const char *seriesUID;
3076     const char *instanceUID;
3077     if ((status = pPrint->getImageReference(idx, studyUID, seriesUID, instanceUID)) == EC_Normal)
3078     {
3079       status = EC_IllegalCall;
3080       const char *filename = getFilename(studyUID, seriesUID, instanceUID);
3081       if (filename)
3082       {
3083         DicomImage *image = new DicomImage(filename);
3084         if (image != NULL)
3085         {
3086           if (image->getStatus() == EIS_Normal)
3087           {
3088             unsigned long width = maximumPrintPreviewWidth;
3089             unsigned long height = maximumPrintPreviewHeight;
3090             /* consider aspect ratio of the image and the display */
3091             double ratio = image->getWidthHeightRatio();
3092             const double mpWidth = getMonitorPixelWidth();
3093             const double mpHeight = getMonitorPixelHeight();
3094             if ((mpWidth > 0) && (mpHeight > 0))
3095               ratio *= (mpWidth / mpHeight);
3096             if (ratio == 0.0)
3097               ratio = 1.0;
3098             if (OFstatic_cast(double, image->getWidth()) / OFstatic_cast(double, width * ratio) <
3099                 OFstatic_cast(double, image->getHeight()) / OFstatic_cast(double, height))
3100             {
3101               width = 0;
3102             } else
3103               height = 0;
3104             image->setWidthHeightRatio(ratio);
3105             pHardcopyImage = image->createScaledImage(width, height, 0 /*interpolate*/, 1 /*aspect ratio*/);
3106             if (pHardcopyImage != NULL)
3107             {
3108               if (pHardcopyImage->getStatus() == EIS_Normal)
3109               {
3110                 /* set display function for calibrated output */
3111                 if (displayFunction[DVPSD_GSDF] != NULL)
3112                   pHardcopyImage->setDisplayFunction(displayFunction[DVPSD_GSDF]);
3113                 /* adapt polarity if necessary */
3114                 const char *polarity = pPrint->getImagePolarity(idx);
3115                 if ((polarity != NULL) && (strcmp(polarity, "REVERSE") == 0))
3116                   pHardcopyImage->setPolarity(EPP_Reverse);
3117                 /* set (print/display) presentation LUT */
3118                 DVPSPresentationLUT *plut = pPrint->getPresentationLUT();   // first check whether there's a global one
3119                 if (plut == NULL)
3120                   plut = pPrint->getImagePresentationLUT(idx);              // ... then check for an image box specific
3121                 if (plut != NULL)
3122                 {
3123                   pHardcopyImage->setHardcopyParameters(pPrint->getMinDensityValue(), pPrint->getMaxDensityValue(),
3124                       pPrint->getPrintReflectedAmbientLight(), pPrint->getPrintIllumination());
3125                   plut->activate(pHardcopyImage, printLUT);
3126                 }
3127                 status = EC_Normal;
3128                 if (changeStatus)
3129                     instanceReviewed(studyUID, seriesUID, instanceUID);
3130               } else
3131                 unloadPrintPreview();
3132             } else
3133               DCMPSTAT_LOGFILE("Load hardcopy grayscale image for print preview failed: memory exhausted");
3134           } else
3135             DCMPSTAT_LOGFILE("Load hardcopy grayscale image for print preview failed: could not read image");
3136           delete image;
3137         } else
3138           DCMPSTAT_LOGFILE("Load hardcopy grayscale image for print preview failed: memory exhausted");
3139       } else
3140         DCMPSTAT_LOGFILE("Load hardcopy grayscale image for print preview failed: UIDs not in index file");
3141     }
3142   }
3143   return status;
3144 }
3145 
unloadPrintPreview()3146 void DVInterface::unloadPrintPreview()
3147 {
3148   delete pHardcopyImage;
3149   pHardcopyImage = NULL;
3150 }
3151 
getPrintPreviewSize()3152 unsigned long DVInterface::getPrintPreviewSize()
3153 {
3154   unsigned long result = 0;
3155   unsigned long width;
3156   unsigned long height;
3157   if (getPrintPreviewWidthHeight(width, height) == EC_Normal)
3158     result = width * height;
3159   return result;
3160 }
3161 
setMaxPrintPreviewWidthHeight(unsigned long width,unsigned long height)3162 void DVInterface::setMaxPrintPreviewWidthHeight(unsigned long width, unsigned long height)
3163 {
3164   if ((width != maximumPrintPreviewWidth) || (height != maximumPrintPreviewHeight))
3165   {
3166     unloadPrintPreview();
3167     maximumPrintPreviewWidth = width;
3168     maximumPrintPreviewHeight = height;
3169   }
3170 }
3171 
getPrintPreviewWidthHeight(unsigned long & width,unsigned long & height)3172 OFCondition DVInterface::getPrintPreviewWidthHeight(unsigned long &width, unsigned long &height)
3173 {
3174   OFCondition result = EC_IllegalCall;
3175   if (pHardcopyImage != NULL)
3176   {
3177     width = pHardcopyImage->getWidth();
3178     height = pHardcopyImage->getHeight();
3179     if ((width > 0) && (height > 0))
3180       result = EC_Normal;
3181   } else {
3182     width = 0;
3183     height = 0;
3184   }
3185   return result;
3186 }
3187 
getPrintPreviewBitmap(void * bitmap,unsigned long size)3188 OFCondition DVInterface::getPrintPreviewBitmap(void *bitmap, unsigned long size)
3189 {
3190   OFCondition status = EC_IllegalCall;
3191   if ((pHardcopyImage != NULL) && (bitmap != NULL) && (size > 0))
3192   {
3193     if (pHardcopyImage->getOutputData(bitmap, size, 8 /*bits*/))
3194       status = EC_Normal;
3195   }
3196   return status;
3197 }
3198 
setCurrentPrinter(const char * targetID)3199 OFCondition DVInterface::setCurrentPrinter(const char *targetID)
3200 {
3201   if (targetID == NULL) return EC_IllegalCall;
3202   if (getTargetHostname(targetID) == NULL) return EC_IllegalCall; // Printer seems to be unknown
3203   activateAnnotation = getTargetPrinterSupportsAnnotation(targetID);
3204   if (pPrint != NULL)
3205   {
3206     pPrint->setPrinterName(targetID);
3207     pPrint->setDestination(getTargetAETitle(targetID));
3208   }
3209   currentPrinter = targetID;
3210   return EC_Normal;
3211 }
3212 
getCurrentPrinter()3213 const char *DVInterface::getCurrentPrinter()
3214 {
3215   return currentPrinter.c_str();
3216 }
3217 
setPrinterMediumType(const char * value)3218 OFCondition DVInterface::setPrinterMediumType(const char *value)
3219 {
3220   if (value) printerMediumType = value; else printerMediumType.clear();
3221   return EC_Normal;
3222 }
3223 
getPrinterMediumType()3224 const char *DVInterface::getPrinterMediumType()
3225 {
3226   return printerMediumType.c_str();
3227 }
3228 
setPrinterFilmDestination(const char * value)3229 OFCondition DVInterface::setPrinterFilmDestination(const char *value)
3230 {
3231   if (value) printerFilmDestination = value; else printerFilmDestination.clear();
3232   return EC_Normal;
3233 }
3234 
getPrinterFilmDestination()3235 const char *DVInterface::getPrinterFilmDestination()
3236 {
3237   return printerFilmDestination.c_str();
3238 }
3239 
setPrinterFilmSessionLabel(const char * value)3240 OFCondition DVInterface::setPrinterFilmSessionLabel(const char *value)
3241 {
3242   if (value) printerFilmSessionLabel = value; else printerFilmSessionLabel.clear();
3243   return EC_Normal;
3244 }
3245 
getPrinterFilmSessionLabel()3246 const char *DVInterface::getPrinterFilmSessionLabel()
3247 {
3248   return printerFilmSessionLabel.c_str();
3249 }
3250 
setPrinterPriority(const char * value)3251 OFCondition DVInterface::setPrinterPriority(const char *value)
3252 {
3253   if (value) printerPriority = value; else printerPriority.clear();
3254   return EC_Normal;
3255 }
3256 
getPrinterPriority()3257 const char *DVInterface::getPrinterPriority()
3258 {
3259   return printerPriority.c_str();
3260 }
3261 
setPrinterOwnerID(const char * value)3262 OFCondition DVInterface::setPrinterOwnerID(const char *value)
3263 {
3264   if (value) printerOwnerID = value; else printerOwnerID.clear();
3265   return EC_Normal;
3266 }
3267 
getPrinterOwnerID()3268 const char *DVInterface::getPrinterOwnerID()
3269 {
3270   return printerOwnerID.c_str();
3271 }
3272 
setPrinterNumberOfCopies(unsigned long value)3273 OFCondition DVInterface::setPrinterNumberOfCopies(unsigned long value)
3274 {
3275   printerNumberOfCopies = value;
3276   return EC_Normal;
3277 }
3278 
getPrinterNumberOfCopies()3279 unsigned long DVInterface::getPrinterNumberOfCopies()
3280 {
3281   return printerNumberOfCopies;
3282 }
3283 
selectDisplayPresentationLUT(const char * lutID)3284 OFCondition DVInterface::selectDisplayPresentationLUT(const char *lutID)
3285 {
3286   OFCondition result = EC_IllegalCall;
3287   if (lutID && pState)
3288   {
3289      const char *lutfile = getLUTFilename(lutID);
3290      if (lutfile)
3291      {
3292        OFString filename = getLUTFolder(); // never NULL.
3293        filename += PATH_SEPARATOR;
3294        filename += lutfile;
3295        DcmFileFormat *fileformat = NULL;
3296        if ((result = DVPSHelper::loadFileFormat(filename.c_str(), fileformat)) == EC_Normal)
3297        {
3298          if (fileformat)
3299          {
3300            DcmDataset *dataset = fileformat->getDataset();
3301            if (dataset)
3302              result = pState->setPresentationLookupTable(*dataset);
3303            else
3304              result = EC_IllegalCall;
3305            if (EC_Normal == result)
3306              displayCurrentLUTID = lutID;
3307            else
3308              displayCurrentLUTID.clear();
3309          } else result = EC_IllegalCall;
3310          if (result != EC_Normal)
3311            DCMPSTAT_LOGFILE("Load display presentation LUT from file: invalid data structures");
3312        } else
3313          DCMPSTAT_LOGFILE("Load display presentation LUT from file: could not read fileformat");
3314        delete fileformat;
3315      } else
3316        DCMPSTAT_LOGFILE("Load display presentation LUT from file: not specified in config file");
3317   }
3318   return result;
3319 }
3320 
getDisplayPresentationLUTID()3321 const char *DVInterface::getDisplayPresentationLUTID()
3322 {
3323   return displayCurrentLUTID.c_str();
3324 }
3325 
selectPrintPresentationLUT(const char * lutID)3326 OFCondition DVInterface::selectPrintPresentationLUT(const char *lutID)
3327 {
3328   OFCondition result = EC_IllegalCall;
3329   if (lutID && pPrint)
3330   {
3331      const char *lutfile = getLUTFilename(lutID);
3332      if (lutfile)
3333      {
3334        OFString filename = getLUTFolder(); // never NULL.
3335        filename += PATH_SEPARATOR;
3336        filename += lutfile;
3337        DcmFileFormat *fileformat = NULL;
3338        if ((result = DVPSHelper::loadFileFormat(filename.c_str(), fileformat)) == EC_Normal)
3339        {
3340          if (fileformat)
3341          {
3342            DcmDataset *dataset = fileformat->getDataset();
3343            if (dataset)
3344              result = pPrint->setPresentationLookupTable(*dataset);
3345            else
3346              result = EC_IllegalCall;
3347            if (EC_Normal == result)
3348              printCurrentLUTID = lutID;
3349            else
3350              printCurrentLUTID.clear();
3351          } else result = EC_IllegalCall;
3352          if (result != EC_Normal)
3353            DCMPSTAT_LOGFILE("Load print presentation LUT from file: invalid data structures");
3354        } else
3355          DCMPSTAT_LOGFILE("Load print presentation LUT from file: could not read fileformat");
3356        delete fileformat;
3357      } else
3358        DCMPSTAT_LOGFILE("Load print presentation LUT from file: not specified in config file");
3359   }
3360   return result;
3361 }
3362 
getPrintPresentationLUTID()3363 const char *DVInterface::getPrintPresentationLUTID()
3364 {
3365   return printCurrentLUTID.c_str();
3366 }
3367 
spoolPrintJob(OFBool deletePrintedImages)3368 OFCondition DVInterface::spoolPrintJob(OFBool deletePrintedImages)
3369 {
3370   if (pPrint==NULL) return EC_IllegalCall;
3371   if (currentPrinter.size()==0) return EC_IllegalCall;
3372 
3373   OFCondition result = saveStoredPrint(getTargetPrinterSupportsRequestedImageSize(currentPrinter.c_str()));
3374   if (EC_Normal == result)
3375   {
3376     result = spoolStoredPrintFromDB(pPrint->getStudyInstanceUID(), pPrint->getSeriesInstanceUID(), pPrint->getSOPInstanceUID());
3377   }
3378   if ((EC_Normal == result) && deletePrintedImages) result = pPrint->deleteSpooledImages();
3379   return result;
3380 }
3381 
startPrintSpooler()3382 OFCondition DVInterface::startPrintSpooler()
3383 {
3384   const char *spooler_application = getSpoolerName();
3385   if (spooler_application==NULL) return EC_IllegalCall;
3386   if (configPath.empty()) return EC_IllegalCall;
3387 
3388   const char *printer = NULL;
3389   unsigned long sleepingTime = getSpoolerSleep();
3390   if (sleepingTime==0) sleepingTime=1; // default
3391   char sleepStr[30];
3392   sprintf(sleepStr, "%lu", sleepingTime);
3393   OFBool detailedLog = getDetailedLog();
3394 
3395   OFCondition result = EC_Normal;
3396   DCMPSTAT_LOGFILE("Starting print spooler process ...");
3397 
3398   DVPSHelper::cleanChildren(); // clean up old child processes before creating new ones
3399 
3400   Uint32 numberOfPrinters = getNumberOfTargets(DVPSE_printAny);
3401   if (numberOfPrinters > 0) for (Uint32 i=0; i < numberOfPrinters; i++)
3402   {
3403         printer = getTargetID(i, DVPSE_printAny);
3404 
3405 #ifdef HAVE_FORK
3406     // Unix version - call fork() and execl()
3407     pid_t pid = fork();
3408     if (pid < 0) result = EC_IllegalCall; // fork failed - return error code
3409     else if (pid==0)
3410     {
3411       // we are the child process
3412 
3413       if (detailedLog)
3414       {
3415         if (execl(spooler_application, spooler_application, "--verbose", "--dump", "--spool", printJobIdentifier.c_str(),
3416           "--printer", printer, "--config", configPath.c_str(), "--sleep", sleepStr, OFreinterpret_cast(char *, 0)) < 0)
3417         {
3418           DCMPSTAT_ERROR("Unable to execute '" << spooler_application << "'");
3419         }
3420       } else {
3421         if (execl(spooler_application, spooler_application, "--spool", printJobIdentifier.c_str(),
3422           "--printer", printer, "--config", configPath.c_str(), "--sleep", sleepStr, OFreinterpret_cast(char *, 0)) < 0)
3423         {
3424           DCMPSTAT_ERROR("Unable to execute '" << spooler_application << "'");
3425         }
3426       }
3427 
3428       // if execl succeeds, this part will not get executed.
3429       // if execl fails, there is not much we can do except bailing out.
3430       abort();
3431     }
3432 #else
3433     // Windows version - call CreateProcess()
3434     // initialize startup info
3435     PROCESS_INFORMATION procinfo;
3436     STARTUPINFOA sinfo;
3437     OFBitmanipTemplate<char>::zeroMem((char *)&sinfo, sizeof(sinfo));
3438     sinfo.cb = sizeof(sinfo);
3439     char commandline[4096];
3440     if (detailedLog)
3441     {
3442       sprintf(commandline, "%s --verbose --dump --spool %s --printer %s --config %s --sleep %s", spooler_application,
3443         printJobIdentifier.c_str(), printer, configPath.c_str(), sleepStr);
3444     } else {
3445       sprintf(commandline, "%s --spool %s --printer %s --config %s --sleep %s", spooler_application,
3446         printJobIdentifier.c_str(), printer, configPath.c_str(), sleepStr);
3447     }
3448 #ifdef DEBUG
3449     if (0 == CreateProcessA(NULL, commandline, NULL, NULL, 0, 0, NULL, NULL, &sinfo, &procinfo))
3450 #else
3451     if (0 == CreateProcessA(NULL, commandline, NULL, NULL, 0, DETACHED_PROCESS, NULL, NULL, &sinfo, &procinfo))
3452 #endif
3453     {
3454       DCMPSTAT_ERROR("Unable to execute '" << spooler_application << "'");
3455       result = EC_IllegalCall;
3456     }
3457 #endif
3458   }
3459   return result;
3460 }
3461 
createPrintJobFilenames(const char * printer,OFString & tempname,OFString & jobname)3462 OFCondition DVInterface::createPrintJobFilenames(const char *printer, OFString& tempname, OFString& jobname)
3463 {
3464   tempname.clear();
3465   jobname.clear();
3466   if (printer==NULL) return EC_IllegalCall;
3467   char buf[20];
3468 
3469   sprintf(buf, "%04lu", printJobCounter++);
3470   jobname =  getSpoolFolder();
3471   jobname += PATH_SEPARATOR;
3472   jobname += printJobIdentifier;
3473   jobname += '_';
3474   jobname += printer;
3475   jobname += '_';
3476   jobname += buf;
3477   tempname = jobname;
3478   jobname += PRINTJOB_SUFFIX;
3479   tempname += PRINTJOB_TEMP_SUFFIX;
3480   return EC_Normal;
3481 }
3482 
terminatePrintSpooler()3483 OFCondition DVInterface::terminatePrintSpooler()
3484 {
3485   if (getSpoolerName()==NULL) return EC_IllegalCall;
3486   if (configPath.empty()) return EC_IllegalCall;
3487 
3488   DVPSHelper::cleanChildren(); // clean up old child processes before creating new ones
3489   OFString spoolFilename;
3490   OFString tempFilename;
3491   const char *prt = NULL;
3492 
3493   DCMPSTAT_LOGFILE("Terminating print spooler process ...");
3494 
3495   Uint32 numberOfPrinters = getNumberOfTargets(DVPSE_printAny);
3496   if (numberOfPrinters > 0) for (Uint32 i=0; i < numberOfPrinters; i++)
3497   {
3498     prt = getTargetID(i, DVPSE_printAny);
3499     if (EC_Normal != createPrintJobFilenames(prt, tempFilename, spoolFilename)) return EC_IllegalCall;
3500     FILE *outf = fopen(tempFilename.c_str(),"wb");
3501     if (outf)
3502     {
3503       OFString timeString;
3504       OFDateTime::getCurrentDateTime().getISOFormattedDateTime(timeString);
3505       fprintf(outf,"#\n# print job created %s\n", timeString.c_str());
3506       fprintf(outf,"# target printer: [%s]\n#\n", (prt ? prt : "none"));
3507       fprintf(outf,"terminate\n");
3508       fclose(outf);
3509       if (0 != rename(tempFilename.c_str(), spoolFilename.c_str()))
3510       {
3511         DCMPSTAT_ERROR("Unable to activate spooler termination request '" << spoolFilename.c_str() << "'");
3512         return EC_IllegalCall;
3513       }
3514     } else {
3515       DCMPSTAT_ERROR("Unable to create spooler termination request '" << tempFilename.c_str() << "'");
3516       return EC_IllegalCall;
3517     }
3518   }
3519   return EC_Normal;
3520 }
3521 
startPrintServer()3522 OFCondition DVInterface::startPrintServer()
3523 {
3524   const char *application = getPrintServerName();
3525   if (application==NULL) return EC_IllegalCall;
3526   if (configPath.empty()) return EC_IllegalCall;
3527 
3528   const char *printer = NULL;
3529   OFBool detailedLog = getDetailedLog();
3530 
3531   OFCondition result = EC_Normal;
3532   DCMPSTAT_LOGFILE("Starting print server process ...");
3533 
3534   DVPSHelper::cleanChildren(); // clean up old child processes before creating new ones
3535 
3536   Uint32 numberOfPrinters = getNumberOfTargets(DVPSE_printLocal);
3537   if (numberOfPrinters > 0) for (Uint32 i=0; i < numberOfPrinters; i++)
3538   {
3539     printer = getTargetID(i, DVPSE_printLocal);
3540 
3541 #ifdef HAVE_FORK
3542     // Unix version - call fork() and execl()
3543     pid_t pid = fork();
3544     if (pid < 0) result = EC_IllegalCall; // fork failed - return error code
3545     else if (pid==0)
3546     {
3547       // we are the child process
3548 
3549       if (detailedLog)
3550       {
3551         if (execl(application, application, "--logfile", "--verbose", "--dump", "--printer", printer, "--config",
3552             configPath.c_str(), OFreinterpret_cast(char *, 0)) < 0)
3553         {
3554           DCMPSTAT_ERROR("Unable to execute '" << application << "'");
3555         }
3556       } else {
3557         if (execl(application, application, "--logfile", "--printer", printer, "--config", configPath.c_str(), OFreinterpret_cast(char *, 0)) < 0)
3558         {
3559           DCMPSTAT_ERROR("Unable to execute '" << application << "'");
3560         }
3561       }
3562 
3563       // if execl succeeds, this part will not get executed.
3564       // if execl fails, there is not much we can do except bailing out.
3565       abort();
3566     }
3567 #else
3568     // Windows version - call CreateProcess()
3569     // initialize startup info
3570     PROCESS_INFORMATION procinfo;
3571     STARTUPINFOA sinfo;
3572     OFBitmanipTemplate<char>::zeroMem((char *)&sinfo, sizeof(sinfo));
3573     sinfo.cb = sizeof(sinfo);
3574     char commandline[4096];
3575     if (detailedLog)
3576     {
3577       sprintf(commandline, "%s --logfile --verbose --dump --printer %s --config %s", application, printer, configPath.c_str());
3578     } else {
3579       sprintf(commandline, "%s --logfile --printer %s --config %s", application, printer, configPath.c_str());
3580     }
3581 #ifdef DEBUG
3582     if (0 == CreateProcessA(NULL, commandline, NULL, NULL, 0, 0, NULL, NULL, &sinfo, &procinfo))
3583 #else
3584     if (0 == CreateProcessA(NULL, commandline, NULL, NULL, 0, DETACHED_PROCESS, NULL, NULL, &sinfo, &procinfo))
3585 #endif
3586     {
3587       DCMPSTAT_ERROR("Unable to execute '" << application << "'");
3588       result = EC_IllegalCall;
3589     }
3590 #endif
3591   }
3592   return result;    // result of last process only
3593 }
3594 
terminatePrintServer()3595 OFCondition DVInterface::terminatePrintServer()
3596 {
3597   if (getPrintServerName()==NULL) return EC_IllegalCall;
3598   if (configPath.empty()) return EC_IllegalCall;
3599 
3600   OFStandard::initializeNetwork();
3601 
3602   OFCondition result = EC_Normal;
3603   T_ASC_Network *net=NULL;
3604   T_ASC_Parameters *params=NULL;
3605   DIC_NODENAME peerHost;
3606   T_ASC_Association *assoc=NULL;
3607   const char *target = NULL;
3608   OFBool useTLS = OFFalse;
3609 
3610   DCMPSTAT_LOGFILE("Terminating print server process ...");
3611 
3612 #ifdef WITH_OPENSSL
3613   /* TLS directory */
3614   const char *current = NULL;
3615   const char *tlsFolder = getTLSFolder();
3616   if (tlsFolder==NULL) tlsFolder = ".";
3617 
3618   /* key file format */
3619   DcmKeyFileFormat keyFileFormat = DCF_Filetype_PEM;
3620   if (! getTLSPEMFormat()) keyFileFormat = DCF_Filetype_ASN1;
3621 #endif
3622 
3623   Uint32 numberOfPrinters = getNumberOfTargets(DVPSE_printLocal);
3624   if (numberOfPrinters > 0) for (Uint32 i=0; i < numberOfPrinters; i++)
3625   {
3626     target = getTargetID(i, DVPSE_printLocal);
3627     useTLS = getTargetUseTLS(target);
3628 
3629     OFCondition cond = ASC_initializeNetwork(NET_REQUESTOR, 0, 30, &net);
3630     if (cond.good())
3631     {
3632       cond = ASC_createAssociationParameters(&params, DEFAULT_MAXPDU);
3633       if (cond.good())
3634       {
3635         if (useTLS)
3636         {
3637 #ifdef WITH_OPENSSL
3638           /* certificate file */
3639           OFString tlsCertificateFile(tlsFolder);
3640           tlsCertificateFile += PATH_SEPARATOR;
3641           current = getTargetCertificate(target);
3642           if (current) tlsCertificateFile += current; else tlsCertificateFile += "sitecert.pem";
3643 
3644           /* private key file */
3645           OFString tlsPrivateKeyFile(tlsFolder);
3646           tlsPrivateKeyFile += PATH_SEPARATOR;
3647           current = getTargetPrivateKey(target);
3648           if (current) tlsPrivateKeyFile += current; else tlsPrivateKeyFile += "sitekey.pem";
3649 
3650           /* private key password */
3651           const char *tlsPrivateKeyPassword = getTargetPrivateKeyPassword(target);
3652 
3653           /* DH parameter file */
3654           OFString tlsDHParametersFile;
3655           current = getTargetDiffieHellmanParameters(target);
3656           if (current)
3657           {
3658             tlsDHParametersFile = tlsFolder;
3659             tlsDHParametersFile += PATH_SEPARATOR;
3660             tlsDHParametersFile += current;
3661           }
3662 
3663           /* random seed file */
3664           OFString tlsRandomSeedFile(tlsFolder);
3665           tlsRandomSeedFile += PATH_SEPARATOR;
3666           current = getTargetRandomSeed(target);
3667           if (current) tlsRandomSeedFile += current; else tlsRandomSeedFile += "siteseed.bin";
3668 
3669           /* CA certificate directory */
3670           const char *tlsCACertificateFolder = getTLSCACertificateFolder();
3671           if (tlsCACertificateFolder==NULL) tlsCACertificateFolder = ".";
3672 
3673 
3674           DcmTLSTransportLayer *tLayer = new DcmTLSTransportLayer(NET_REQUESTOR, tlsRandomSeedFile.c_str(), OFTrue);
3675           if (tLayer)
3676           {
3677             if (tlsCACertificateFolder) tLayer->addTrustedCertificateDir(tlsCACertificateFolder, keyFileFormat);
3678             if (tlsDHParametersFile.size() > 0) tLayer->setTempDHParameters(tlsDHParametersFile.c_str());
3679             tLayer->setPrivateKeyPasswd(tlsPrivateKeyPassword); // never prompt on console
3680             tLayer->setPrivateKeyFile(tlsPrivateKeyFile.c_str(), keyFileFormat);
3681             tLayer->setCertificateFile(tlsCertificateFile.c_str(), keyFileFormat);
3682             tLayer->setCertificateVerification(DCV_ignoreCertificate);
3683 
3684            // determine TLS profile
3685              OFString profileName;
3686             const char *profileNamePtr = getTargetTLSProfile(target);
3687             if (profileNamePtr) profileName = profileNamePtr;
3688             DcmTLSSecurityProfile tlsProfile = TSP_Profile_BCP195;  // default
3689             if (profileName == "BCP195-ND") tlsProfile = TSP_Profile_BCP195_ND;
3690             else if (profileName == "BCP195-EX") tlsProfile = TSP_Profile_BCP195_Extended;
3691             else if (profileName == "BCP195") tlsProfile = TSP_Profile_BCP195;
3692             else if (profileName == "AES") tlsProfile = TSP_Profile_AES;
3693             else if (profileName == "BASIC") tlsProfile = TSP_Profile_Basic;
3694             else if (profileName == "NULL") tlsProfile = TSP_Profile_IHE_ATNA_Unencrypted;
3695 
3696             // set TLS profile
3697             (void) tLayer->setTLSProfile(tlsProfile);
3698 
3699             // activate cipher suites
3700             (void) tLayer->activateCipherSuites();
3701 
3702             ASC_setTransportLayer(net, tLayer, 1);
3703           }
3704 #else
3705           // we cannot shutdown a TLS process since we're compiled without OpenSSL support
3706           cond = EC_IllegalCall;
3707 #endif
3708         }
3709 
3710         ASC_setAPTitles(params, getNetworkAETitle(), getTargetAETitle(target), NULL);
3711         sprintf(peerHost, "%s:%d", getTargetHostname(target), OFstatic_cast(int, getTargetPort(target)));
3712         ASC_setPresentationAddresses(params, OFStandard::getHostName().c_str(), peerHost);
3713 
3714         if (cond.good()) cond = ASC_setTransportLayerType(params, useTLS);
3715 
3716         const char* transferSyntaxes[] = { UID_LittleEndianImplicitTransferSyntax };
3717         if (cond.good()) cond = ASC_addPresentationContext(params, 1, UID_PrivateShutdownSOPClass, transferSyntaxes, 1);
3718         if (cond.good())
3719         {
3720           cond = ASC_requestAssociation(net, params, &assoc);
3721           if (cond.good()) ASC_abortAssociation(assoc); // tear down association if necessary
3722           ASC_dropAssociation(assoc);
3723           ASC_destroyAssociation(&assoc);
3724         }
3725       } else result = EC_IllegalCall;
3726       ASC_dropNetwork(&net);
3727     } else result = EC_IllegalCall;
3728   }
3729 
3730   OFStandard::shutdownNetwork();
3731 
3732   return result;    // result of last process only
3733 }
3734 
addToPrintHardcopyFromDB(const char * studyUID,const char * seriesUID,const char * instanceUID)3735 OFCondition DVInterface::addToPrintHardcopyFromDB(const char *studyUID, const char *seriesUID, const char *instanceUID)
3736 {
3737   OFCondition result = EC_IllegalCall;
3738 
3739   if (pPrint)
3740   {
3741     if (studyUID && seriesUID && instanceUID)
3742     {
3743       if (EC_Normal == (result = lockDatabase()))
3744       {
3745         DcmUniqueIdentifier sopclassuid(DCM_SOPClassUID);
3746         const char *filename = getFilename(studyUID, seriesUID, instanceUID);
3747         if (filename)
3748         {
3749           DcmFileFormat *ff = NULL;
3750           if ((result = DVPSHelper::loadFileFormat(filename, ff)) == EC_Normal)
3751           {
3752             if (ff)
3753             {
3754               DcmDataset *dataset = ff->getDataset();
3755               if (dataset)
3756               {
3757                 DcmStack stack;
3758                 DVPSPresentationLUT presentationLUT;
3759                 if (EC_Normal != presentationLUT.read(*dataset, OFFalse)) presentationLUT.setType(DVPSP_identity);
3760                     result = dataset->search(sopclassuid.getTag(), stack, ESM_fromHere, OFFalse);
3761                 if (EC_Normal == result)
3762                 {
3763                   char *sopclass = NULL;
3764                   sopclassuid = *OFstatic_cast(DcmUniqueIdentifier *, stack.top());
3765                   if (EC_Normal == sopclassuid.getString(sopclass))
3766                     result = pPrint->addImageBox(getNetworkAETitle(), studyUID, seriesUID,
3767                       sopclass, instanceUID, NULL, NULL, &presentationLUT, OFFalse);
3768                   else
3769                     result = EC_IllegalCall;
3770                 }
3771               } else result = EC_CorruptedData;
3772             } else result = EC_IllegalCall;
3773             if (result != EC_Normal)
3774                 DCMPSTAT_LOGFILE("Load hardcopy grayscale image from file failed: invalid data structures");
3775           } else
3776             DCMPSTAT_LOGFILE("Load hardcopy grayscale image from file failed: could not read fileformat");
3777           if (ff) delete ff;
3778         } else {
3779           result = EC_IllegalCall;
3780           DCMPSTAT_LOGFILE("Load hardcopy grayscale image from database failed: UIDs not in index file");
3781         }
3782       } else
3783         DCMPSTAT_LOGFILE("Load hardcopy grayscale image from database failed: could not lock index file");
3784     } else
3785       DCMPSTAT_LOGFILE("Load hardcopy grayscale image from database failed: invalid UIDs");
3786   }
3787 
3788   releaseDatabase();
3789   return result;
3790 }
3791 
spoolStoredPrintFromDB(const char * studyUID,const char * seriesUID,const char * instanceUID)3792 OFCondition DVInterface::spoolStoredPrintFromDB(const char *studyUID, const char *seriesUID, const char *instanceUID)
3793 {
3794   if ((studyUID==NULL)||(seriesUID==NULL)||(instanceUID==NULL)||configPath.empty()) return EC_IllegalCall;
3795   OFString spoolFilename;
3796   OFString tempFilename;
3797   const char *prt = getCurrentPrinter();
3798   if (EC_Normal != createPrintJobFilenames(prt, tempFilename, spoolFilename)) return EC_IllegalCall;
3799 
3800   FILE *outf = fopen(tempFilename.c_str(),"wb");
3801   if (outf)
3802   {
3803     OFString timeString;
3804     OFDateTime::getCurrentDateTime().getISOFormattedDateTime(timeString);
3805     fprintf(outf, "#\n# print job created %s\n", timeString.c_str());
3806     fprintf(outf, "# target printer: [%s]\n#\n", (prt ? prt : "none"));
3807     fprintf(outf, "study        %s\nseries       %s\ninstance     %s\n", studyUID, seriesUID, instanceUID);
3808     if (printerMediumType.size() >0)       fprintf(outf,"mediumtype   %s\n", printerMediumType.c_str());
3809     if (printerFilmDestination.size() >0)  fprintf(outf,"destination  %s\n", printerFilmDestination.c_str());
3810     if (printerFilmSessionLabel.size() >0) fprintf(outf,"label        %s\n", printerFilmSessionLabel.c_str());
3811     if (printerPriority.size() >0)         fprintf(outf,"priority     %s\n", printerPriority.c_str());
3812     if (printerOwnerID.size() >0)          fprintf(outf,"owner_id     %s\n", printerOwnerID.c_str());
3813     if (printerNumberOfCopies >0)          fprintf(outf,"copies       %lu\n", printerNumberOfCopies);
3814 
3815     fclose(outf);
3816     if (0 != rename(tempFilename.c_str(), spoolFilename.c_str()))
3817     {
3818       DCMPSTAT_ERROR("Unable to activate print job '" << spoolFilename.c_str() << "'");
3819       return EC_IllegalCall;
3820     }
3821   } else {
3822     DCMPSTAT_ERROR("Unable to create print job '" << tempFilename.c_str() << "'");
3823     return EC_IllegalCall;
3824   }
3825   return EC_Normal;
3826 }
3827 
printSCUcreateBasicFilmSession(DVPSPrintMessageHandler & printHandler,OFBool plutInSession)3828 OFCondition DVInterface::printSCUcreateBasicFilmSession(DVPSPrintMessageHandler& printHandler, OFBool plutInSession)
3829 {
3830   if (!pPrint) return EC_IllegalCall;
3831   OFCondition result = EC_Normal;
3832   DcmDataset dset;
3833   DcmElement *delem = NULL;
3834   char buf[30];
3835 
3836   if ((EC_Normal==result)&&(printerMediumType.size() > 0))
3837   {
3838     delem = new DcmCodeString(DCM_MediumType);
3839     if (delem) result = delem->putString(printerMediumType.c_str()); else result=EC_IllegalCall;
3840     if (EC_Normal==result) result = dset.insert(delem, OFTrue /*replaceOld*/);
3841   }
3842 
3843   if ((EC_Normal==result)&&(printerFilmDestination.size() > 0))
3844   {
3845     delem = new DcmCodeString(DCM_FilmDestination);
3846     if (delem) result = delem->putString(printerFilmDestination.c_str()); else result=EC_IllegalCall;
3847     if (EC_Normal==result) result = dset.insert(delem, OFTrue /*replaceOld*/);
3848   }
3849 
3850   if ((EC_Normal==result)&&(printerFilmSessionLabel.size() > 0))
3851   {
3852     delem = new DcmLongString(DCM_FilmSessionLabel);
3853     if (delem) result = delem->putString(printerFilmSessionLabel.c_str()); else result=EC_IllegalCall;
3854     if (EC_Normal==result) result = dset.insert(delem, OFTrue /*replaceOld*/);
3855   }
3856 
3857   if ((EC_Normal==result)&&(printerPriority.size() > 0))
3858   {
3859     delem = new DcmCodeString(DCM_PrintPriority);
3860     if (delem) result = delem->putString(printerPriority.c_str()); else result=EC_IllegalCall;
3861     if (EC_Normal==result) result = dset.insert(delem, OFTrue /*replaceOld*/);
3862   }
3863 
3864   if ((EC_Normal==result)&&(printerOwnerID.size() > 0))
3865   {
3866     delem = new DcmShortString(DCM_OwnerID);
3867     if (delem) result = delem->putString(printerOwnerID.c_str()); else result=EC_IllegalCall;
3868     if (EC_Normal==result) result = dset.insert(delem, OFTrue /*replaceOld*/);
3869   }
3870 
3871   if ((EC_Normal==result)&&(printerNumberOfCopies > 0))
3872   {
3873     sprintf(buf, "%lu", printerNumberOfCopies);
3874     delem = new DcmIntegerString(DCM_NumberOfCopies);
3875     if (delem) result = delem->putString(buf); else result=EC_IllegalCall;
3876     if (EC_Normal==result) result = dset.insert(delem, OFTrue /*replaceOld*/);
3877   }
3878 
3879   if (EC_Normal==result) result = pPrint->printSCUcreateBasicFilmSession(printHandler, dset, plutInSession);
3880   return result;
3881 }
3882 
clearFilmSessionSettings()3883 void DVInterface::clearFilmSessionSettings()
3884 {
3885   printerMediumType.clear();
3886   printerFilmDestination.clear();
3887   printerFilmSessionLabel.clear();
3888   printerPriority.clear();
3889   printerOwnerID.clear();
3890   printerNumberOfCopies = 0;
3891   return;
3892 }
3893 
setAnnotationText(const char * value)3894 void DVInterface::setAnnotationText(const char *value)
3895 {
3896   if (value) annotationText=value; else annotationText.clear();
3897   return;
3898 }
3899 
startExternalApplication(const char * application,const char * filename)3900 OFCondition DVInterface::startExternalApplication(const char *application, const char *filename)
3901 {
3902   if ((filename==NULL)||(application==NULL)) return EC_IllegalCall;
3903   DVPSHelper::cleanChildren(); // clean up old child processes before creating new ones
3904 
3905 #ifdef HAVE_FORK
3906   // Unix version - call fork() and execl()
3907   pid_t pid = fork();
3908   if (pid < 0) return EC_IllegalCall; // fork failed - return error code
3909   else if (pid > 0) return EC_Normal; // we are the parent process
3910   else
3911   {
3912     // we are the child process
3913     if (execl(application, application, filename, OFreinterpret_cast(char *, 0)) < 0)
3914     {
3915       DCMPSTAT_ERROR("Unable to execute '" << application << "'");
3916     }
3917     // if execl succeeds, this part will not get executed.
3918     // if execl fails, there is not much we can do except bailing out.
3919     abort();
3920   }
3921 #else
3922   // Windows version - call CreateProcess()
3923 
3924   // initialize startup info
3925   PROCESS_INFORMATION procinfo;
3926   STARTUPINFOA sinfo;
3927   OFBitmanipTemplate<char>::zeroMem((char *)&sinfo, sizeof(sinfo));
3928   sinfo.cb = sizeof(sinfo);
3929   char commandline[4096];
3930   sprintf(commandline, "%s %s", application, filename);
3931 #ifdef DEBUG
3932   if (CreateProcessA(NULL, commandline, NULL, NULL, 0, 0, NULL, NULL, &sinfo, &procinfo))
3933 #else
3934   if (CreateProcessA(NULL, commandline, NULL, NULL, 0, DETACHED_PROCESS, NULL, NULL, &sinfo, &procinfo))
3935 #endif
3936   {
3937     return EC_Normal;
3938   } else {
3939     DCMPSTAT_ERROR("Unable to execute '" << application << "'");
3940   }
3941 #endif
3942   return EC_IllegalCall;
3943 }
3944 
dumpIOD(const char * filename)3945 OFCondition DVInterface::dumpIOD(const char *filename)
3946 {
3947   OFCondition result = startExternalApplication(getDumpToolName(), filename);
3948   if (result != EC_Normal)
3949     DCMPSTAT_LOGFILE("Dump IOD failed: could not start dump application");
3950   return result;
3951 }
3952 
dumpIOD(const char * studyUID,const char * seriesUID,const char * instanceUID)3953 OFCondition DVInterface::dumpIOD(const char *studyUID, const char *seriesUID, const char *instanceUID)
3954 {
3955   OFCondition result = EC_IllegalCall;
3956   if (studyUID && seriesUID && instanceUID)
3957   {
3958     if (EC_Normal == (result = lockDatabase()))
3959     {
3960       const char *filename = getFilename(studyUID, seriesUID, instanceUID);
3961       if (filename)
3962         result = dumpIOD(filename);
3963       else
3964       {
3965         result = EC_IllegalCall;
3966         DCMPSTAT_LOGFILE("Dump IOD from database failed: could not lock index file");
3967       }
3968     } else
3969       DCMPSTAT_LOGFILE("Dump IOD from database failed: UIDs not in index file");
3970   } else
3971     DCMPSTAT_LOGFILE("Dump IOD from database failed: invalid UIDs");
3972   return result;
3973 }
3974 
checkIOD(const char * filename)3975 OFCondition DVInterface::checkIOD(const char *filename)
3976 {
3977   OFCondition result = startExternalApplication(getCheckToolName(), filename);
3978   if (result != EC_Normal)
3979     DCMPSTAT_LOGFILE("Check IOD failed: could not start evaluator application");
3980   return result;
3981 }
3982 
checkIOD(const char * studyUID,const char * seriesUID,const char * instanceUID)3983 OFCondition DVInterface::checkIOD(const char *studyUID, const char *seriesUID, const char *instanceUID)
3984 {
3985   OFCondition result = EC_IllegalCall;
3986   if (studyUID && seriesUID && instanceUID)
3987   {
3988     if (EC_Normal == (result = lockDatabase()))
3989     {
3990       const char *filename = getFilename(studyUID, seriesUID, instanceUID);
3991       if (filename)
3992         result = checkIOD(filename);
3993       else
3994       {
3995         result = EC_IllegalCall;
3996         DCMPSTAT_LOGFILE("Check IOD from database failed: could not lock index file");
3997       }
3998     } else
3999       DCMPSTAT_LOGFILE("Check IOD from database failed: UIDs not in index file");
4000   } else
4001     DCMPSTAT_LOGFILE("Check IOD from database failed: invalid UIDs");
4002   return result;
4003 }
4004 
4005 #ifdef WITH_OPENSSL
4006 
4007 /* buf     : buffer to write password into
4008  * size    : length of buffer in bytes
4009  * rwflag  : nonzero if the password will be used as a new password, i.e. user should be asked to repeat the password
4010  * userdata: arbitrary pointer that can be set with SSL_CTX_set_default_passwd_cb_userdata()
4011  * returns : number of bytes written to password buffer, -1 upon error
4012  */
4013 extern "C" int DVInterfacePasswordCallback(char *buf, int size, int rwflag, void *userdata);
4014 
DVInterfacePasswordCallback(char * buf,int size,int,void * userdata)4015 int DVInterfacePasswordCallback(char *buf, int size, int /* rwflag */, void *userdata)
4016 {
4017   if (userdata == NULL) return -1;
4018   OFString *password = OFstatic_cast(OFString *, userdata);
4019   int passwordSize = OFstatic_cast(int, password->length());
4020   if (passwordSize > size) passwordSize = size;
4021   strncpy(buf, password->c_str(), passwordSize);
4022   return passwordSize;
4023 }
4024 
4025 #endif
4026 
4027 
4028 #ifdef WITH_OPENSSL
verifyUserPassword(const char * userID,const char * passwd)4029 OFBool DVInterface::verifyUserPassword(const char *userID, const char *passwd)
4030 #else
4031 OFBool DVInterface::verifyUserPassword(const char * /*userID*/, const char * /*passwd*/)
4032 #endif
4033 {
4034   OFBool result = OFFalse;
4035 #ifdef WITH_OPENSSL
4036   OFString filename;
4037   OFString privateKeyPasswd;
4038   if (passwd) privateKeyPasswd = passwd;
4039   OFBool isPEMFormat = getTLSPEMFormat();
4040   const char *userKey = getUserPrivateKey(userID);
4041   if (userKey == NULL)
4042     DCMPSTAT_LOGFILE("Cannot verify user password: unknown user or undefined private key file");
4043   else
4044   {
4045     const char *userDir = getUserCertificateFolder();
4046     if (userDir)
4047     {
4048       filename = userDir;
4049       filename += PATH_SEPARATOR;
4050     }
4051     filename += userKey;
4052 
4053     /* attempt to load the private key with the given password*/
4054     EVP_PKEY *pkey = NULL;
4055     BIO *in = BIO_new(BIO_s_file());
4056     if (in)
4057     {
4058       if (BIO_read_filename(in, filename.c_str()) > 0)
4059       {
4060         if (isPEMFormat)
4061         {
4062           pkey = PEM_read_bio_PrivateKey(in, NULL, DVInterfacePasswordCallback, &privateKeyPasswd);
4063           if (pkey) result = OFTrue;
4064         } else {
4065           // ASN.1/DER encoded keys are never encrypted, thus no callback here.
4066           pkey = d2i_PrivateKey_bio(in, NULL);
4067           if (pkey) result = OFTrue;
4068         }
4069       } else
4070         DCMPSTAT_LOGFILE("Cannot verify user password: private key file not found");
4071       BIO_free(in);
4072     }
4073     if (pkey) EVP_PKEY_free(pkey);
4074   }
4075 #else
4076   DCMPSTAT_LOGFILE("Cannot verify user password: not compiled with OpenSSL support");
4077 #endif
4078   return result;
4079 }
4080 
4081 #ifdef WITH_OPENSSL
verifyAndSignStructuredReport(const char * userID,const char * passwd,DVPSVerifyAndSignMode mode)4082 OFCondition DVInterface::verifyAndSignStructuredReport(const char *userID, const char *passwd, DVPSVerifyAndSignMode mode)
4083 #else
4084 OFCondition DVInterface::verifyAndSignStructuredReport(const char *userID, const char * /*passwd*/, DVPSVerifyAndSignMode mode)
4085 #endif
4086 {
4087   OFCondition result = EC_IllegalCall;
4088   if ((pReport != NULL) && (userID != NULL))
4089   {
4090     OFString userName(getUserDICOMName(userID));
4091     OFString userOrg(getUserOrganization(userID));
4092     OFString userCV, userCSD, userCSV, userCM;
4093     DSRCodedEntryValue userCode(getUserCodeValue(userID, userCV), getUserCodingSchemeDesignator(userID, userCSD),
4094                                 getUserCodingSchemeVersion(userID, userCSV), getUserCodeMeaning(userID, userCM));
4095     /* verify document */
4096     if (pReport->verifyDocument(userName, userCode, userOrg) == EC_Normal)
4097     {
4098       if ((mode == DVPSY_verifyAndSign) || (mode == DVPSY_verifyAndSign_finalize))
4099       {
4100 #ifdef WITH_OPENSSL
4101         if (pSignatureHandler)
4102         {
4103           DcmStack stack;
4104           DcmItem dataset;
4105           if (pReport->write(dataset, &stack) == EC_Normal)
4106           {
4107             DcmAttributeTag tagList(DcmTag(0, 0) /* irrelevant value */);
4108             if (mode == DVPSY_verifyAndSign)
4109             {
4110               /* do not sign particular attributes */
4111               tagList.putTagVal(DCM_SOPInstanceUID, 0);
4112               tagList.putTagVal(DCM_VerifyingObserverSequence, 1);
4113               tagList.putTagVal(DCM_InstanceCreationDate, 2);
4114               tagList.putTagVal(DCM_InstanceCreationTime, 3);
4115               tagList.putTagVal(DCM_InstanceCreatorUID, 4);
4116             }
4117             else if (mode == DVPSY_verifyAndSign_finalize)
4118             {
4119               /* always sign the entire document */
4120               stack.clear();
4121             }
4122             /* if no item is marked, sign entire dataset */
4123             if (stack.empty())
4124               stack.push(&dataset);
4125             /* digitally sign document */
4126             if (pSignatureHandler->createSignature(dataset, stack, tagList, userID, passwd) == EC_Normal)
4127             {
4128               DSRDocument *newReport = new DSRDocument();
4129               if (newReport != NULL)
4130               {
4131                 if (newReport->read(dataset, DSRTypes::RF_readDigitalSignatures) == EC_Normal)
4132                 {
4133                   /* replace report in memory */
4134                   delete pReport;
4135                   pReport = newReport;
4136                   pSignatureHandler->updateDigitalSignatureInformation(dataset, DVPSS_structuredReport, OFFalse /* onRead? */);
4137                   if (mode == DVPSY_verifyAndSign_finalize)
4138                     result = pReport->finalizeDocument();
4139                   else
4140                     result = EC_Normal;
4141                 }
4142               } else
4143                 result = EC_MemoryExhausted;
4144             }
4145           }
4146         }
4147 #else
4148         DCMPSTAT_LOGFILE("Cannot sign structured report: not compiled with OpenSSL support");
4149 #endif
4150       } else
4151         result= EC_Normal;
4152     }
4153   }
4154   return result;
4155 }
4156 
getCurrentSignatureValidationHTML(DVPSObjectType objtype) const4157 const char *DVInterface::getCurrentSignatureValidationHTML(DVPSObjectType objtype) const
4158 {
4159   return pSignatureHandler->getCurrentSignatureValidationHTML(objtype);
4160 }
4161 
getCurrentSignatureValidationOverview() const4162 const char *DVInterface::getCurrentSignatureValidationOverview() const
4163 {
4164   return pSignatureHandler->getCurrentSignatureValidationOverview();
4165 }
4166 
getCurrentSignatureStatus(DVPSObjectType objtype) const4167 DVPSSignatureStatus DVInterface::getCurrentSignatureStatus(DVPSObjectType objtype) const
4168 {
4169   return pSignatureHandler->getCurrentSignatureStatus(objtype);
4170 }
4171 
getCombinedImagePStateSignatureStatus() const4172 DVPSSignatureStatus DVInterface::getCombinedImagePStateSignatureStatus() const
4173 {
4174   return pSignatureHandler->getCombinedImagePStateSignatureStatus();
4175 }
4176 
getNumberOfCorrectSignatures(DVPSObjectType objtype) const4177 unsigned long DVInterface::getNumberOfCorrectSignatures(DVPSObjectType objtype) const
4178 {
4179   return pSignatureHandler->getNumberOfCorrectSignatures(objtype);
4180 }
4181 
getNumberOfUntrustworthySignatures(DVPSObjectType objtype) const4182 unsigned long DVInterface::getNumberOfUntrustworthySignatures(DVPSObjectType objtype) const
4183 {
4184   return pSignatureHandler->getNumberOfUntrustworthySignatures(objtype);
4185 }
4186 
getNumberOfCorruptSignatures(DVPSObjectType objtype) const4187 unsigned long DVInterface::getNumberOfCorruptSignatures(DVPSObjectType objtype) const
4188 {
4189   return pSignatureHandler->getNumberOfCorruptSignatures(objtype);
4190 }
4191 
disableImageAndPState()4192 void DVInterface::disableImageAndPState()
4193 {
4194   pSignatureHandler->disableImageAndPState();
4195 }
4196