1 /*
2  *
3  *  Copyright (C) 1993-2018, OFFIS e.V.
4  *  All rights reserved.  See COPYRIGHT file for details.
5  *
6  *  This software and supporting documentation were developed by
7  *
8  *    OFFIS e.V.
9  *    R&D Division Health
10  *    Escherweg 2
11  *    D-26121 Oldenburg, Germany
12  *
13  *
14  *  Module:  dcmqrdb
15  *
16  *  Author:  Marco Eichelberg
17  *
18  *  Purpose: class DcmQueryRetrieveSCP
19  *
20  */
21 
22 #include "dcmtk/config/osconfig.h"    /* make sure OS specific configuration is included first */
23 #include "dcmtk/dcmqrdb/dcmqrsrv.h"
24 #include "dcmtk/dcmqrdb/dcmqropt.h"
25 #include "dcmtk/dcmdata/dcfilefo.h"
26 #include "dcmtk/dcmdata/dcdeftag.h"
27 #include "dcmtk/dcmdata/dcmetinf.h"
28 #include "dcmtk/dcmqrdb/dcmqrdba.h"
29 #include "dcmtk/dcmqrdb/dcmqrcbf.h"    /* for class DcmQueryRetrieveFindContext */
30 #include "dcmtk/dcmqrdb/dcmqrcbm.h"    /* for class DcmQueryRetrieveMoveContext */
31 #include "dcmtk/dcmqrdb/dcmqrcbg.h"    /* for class DcmQueryRetrieveGetContext */
32 #include "dcmtk/dcmqrdb/dcmqrcbs.h"    /* for class DcmQueryRetrieveStoreContext */
33 
34 
findCallback(void * callbackData,OFBool cancelled,T_DIMSE_C_FindRQ * request,DcmDataset * requestIdentifiers,int responseCount,T_DIMSE_C_FindRSP * response,DcmDataset ** responseIdentifiers,DcmDataset ** stDetail)35 static void findCallback(
36   /* in */
37   void *callbackData,
38   OFBool cancelled, T_DIMSE_C_FindRQ *request,
39   DcmDataset *requestIdentifiers, int responseCount,
40   /* out */
41   T_DIMSE_C_FindRSP *response,
42   DcmDataset **responseIdentifiers,
43   DcmDataset **stDetail)
44 {
45   DcmQueryRetrieveFindContext *context = OFstatic_cast(DcmQueryRetrieveFindContext *, callbackData);
46   context->callbackHandler(cancelled, request, requestIdentifiers, responseCount, response, responseIdentifiers, stDetail);
47 }
48 
49 
getCallback(void * callbackData,OFBool cancelled,T_DIMSE_C_GetRQ * request,DcmDataset * requestIdentifiers,int responseCount,T_DIMSE_C_GetRSP * response,DcmDataset ** stDetail,DcmDataset ** responseIdentifiers)50 static void getCallback(
51   /* in */
52   void *callbackData,
53   OFBool cancelled, T_DIMSE_C_GetRQ *request,
54   DcmDataset *requestIdentifiers, int responseCount,
55   /* out */
56   T_DIMSE_C_GetRSP *response, DcmDataset **stDetail,
57   DcmDataset **responseIdentifiers)
58 {
59   DcmQueryRetrieveGetContext *context = OFstatic_cast(DcmQueryRetrieveGetContext *, callbackData);
60   context->callbackHandler(cancelled, request, requestIdentifiers, responseCount, response, stDetail, responseIdentifiers);
61 }
62 
63 
moveCallback(void * callbackData,OFBool cancelled,T_DIMSE_C_MoveRQ * request,DcmDataset * requestIdentifiers,int responseCount,T_DIMSE_C_MoveRSP * response,DcmDataset ** stDetail,DcmDataset ** responseIdentifiers)64 static void moveCallback(
65   /* in */
66   void *callbackData,
67   OFBool cancelled, T_DIMSE_C_MoveRQ *request,
68   DcmDataset *requestIdentifiers, int responseCount,
69   /* out */
70   T_DIMSE_C_MoveRSP *response, DcmDataset **stDetail,
71   DcmDataset **responseIdentifiers)
72 {
73   DcmQueryRetrieveMoveContext *context = OFstatic_cast(DcmQueryRetrieveMoveContext *, callbackData);
74   context->callbackHandler(cancelled, request, requestIdentifiers, responseCount, response, stDetail, responseIdentifiers);
75 }
76 
77 
storeCallback(void * callbackData,T_DIMSE_StoreProgress * progress,T_DIMSE_C_StoreRQ * req,char * imageFileName,DcmDataset ** imageDataSet,T_DIMSE_C_StoreRSP * rsp,DcmDataset ** stDetail)78 static void storeCallback(
79   /* in */
80   void *callbackData,
81   T_DIMSE_StoreProgress *progress,    /* progress state */
82   T_DIMSE_C_StoreRQ *req,             /* original store request */
83   char *imageFileName,                /* being received into */
84   DcmDataset **imageDataSet,          /* being received into */
85   /* out */
86   T_DIMSE_C_StoreRSP *rsp,            /* final store response */
87   DcmDataset **stDetail)
88 {
89   DcmQueryRetrieveStoreContext *context = OFstatic_cast(DcmQueryRetrieveStoreContext *, callbackData);
90   context->callbackHandler(progress, req, imageFileName, imageDataSet, rsp, stDetail);
91 }
92 
93 
94 /*
95  * ============================================================================================================
96  */
97 
98 
DcmQueryRetrieveSCP(const DcmQueryRetrieveConfig & config,const DcmQueryRetrieveOptions & options,const DcmQueryRetrieveDatabaseHandleFactory & factory,const DcmAssociationConfiguration & associationConfiguration)99 DcmQueryRetrieveSCP::DcmQueryRetrieveSCP(
100   const DcmQueryRetrieveConfig& config,
101   const DcmQueryRetrieveOptions& options,
102   const DcmQueryRetrieveDatabaseHandleFactory& factory,
103   const DcmAssociationConfiguration& associationConfiguration)
104 : config_(&config)
105 , processtable_()
106 , dbCheckFindIdentifier_(OFFalse)
107 , dbCheckMoveIdentifier_(OFFalse)
108 , factory_(factory)
109 , options_(options)
110 , associationConfiguration_(associationConfiguration)
111 {
112 }
113 
114 
dispatch(T_ASC_Association * assoc,OFBool correctUIDPadding)115 OFCondition DcmQueryRetrieveSCP::dispatch(T_ASC_Association *assoc, OFBool correctUIDPadding)
116 {
117     OFCondition cond = EC_Normal;
118     T_DIMSE_Message msg;
119     T_ASC_PresentationContextID presID;
120     OFBool firstLoop = OFTrue;
121 
122     // this while loop is executed exactly once unless the "keepDBHandleDuringAssociation_"
123     // flag is not set, in which case the inner loop is executed only once and this loop
124     // repeats for each incoming DIMSE command. In this case, the DB handle is created
125     // and released for each DIMSE command.
126     while (cond.good())
127     {
128         /* Create a database handle for this association */
129         DcmQueryRetrieveDatabaseHandle *dbHandle = factory_.createDBHandle(
130               assoc->params->DULparams.callingAPTitle,
131           assoc->params->DULparams.calledAPTitle, cond);
132 
133         if (cond.bad())
134         {
135           DCMQRDB_ERROR("dispatch: cannot create DB Handle");
136           return cond;
137         }
138 
139         if (dbHandle == NULL)
140         {
141           // this should not happen, but we check it anyway
142           DCMQRDB_ERROR("dispatch: cannot create DB Handle");
143           return EC_IllegalCall;
144         }
145 
146         dbHandle->setIdentifierChecking(dbCheckFindIdentifier_, dbCheckMoveIdentifier_);
147         firstLoop = OFTrue;
148 
149         // this while loop is executed exactly once unless the "keepDBHandleDuringAssociation_"
150         // flag is set, in which case the DB handle remains open until something goes wrong
151         // or the remote peer closes the association
152         while (cond.good() && (firstLoop || options_.keepDBHandleDuringAssociation_) )
153         {
154             firstLoop = OFFalse;
155             cond = DIMSE_receiveCommand(assoc, DIMSE_BLOCKING, 0, &presID, &msg, NULL);
156 
157             /* did peer release, abort, or do we have a valid message ? */
158             if (cond.good())
159             {
160                 /* process command */
161                 switch (msg.CommandField) {
162                 case DIMSE_C_ECHO_RQ:
163                     cond = echoSCP(assoc, &msg.msg.CEchoRQ, presID);
164                     break;
165                 case DIMSE_C_STORE_RQ:
166                     cond = storeSCP(assoc, &msg.msg.CStoreRQ, presID, *dbHandle, correctUIDPadding);
167                     break;
168                 case DIMSE_C_FIND_RQ:
169                     cond = findSCP(assoc, &msg.msg.CFindRQ, presID, *dbHandle);
170                     break;
171                 case DIMSE_C_MOVE_RQ:
172                     cond = moveSCP(assoc, &msg.msg.CMoveRQ, presID, *dbHandle);
173                     break;
174                 case DIMSE_C_GET_RQ:
175                     cond = getSCP(assoc, &msg.msg.CGetRQ, presID, *dbHandle);
176                     break;
177                 case DIMSE_C_CANCEL_RQ:
178                     /* This is a late cancel request, just ignore it */
179                     DCMQRDB_INFO("dispatch: late C-CANCEL-RQ, ignoring");
180                     break;
181                 default:
182                     /* we cannot handle this kind of message */
183                     cond = DIMSE_BADCOMMANDTYPE;
184                     DCMQRDB_ERROR("Cannot handle command: 0x" << STD_NAMESPACE hex <<
185                             (unsigned)msg.CommandField);
186                     /* the condition will be returned, the caller will abort the association. */
187                 }
188             }
189             else if ((cond == DUL_PEERREQUESTEDRELEASE)||(cond == DUL_PEERABORTEDASSOCIATION))
190             {
191                 // association gone
192             }
193             else
194             {
195                 // the condition will be returned, the caller will abort the association.
196             }
197         }
198 
199         // release DB handle
200         delete dbHandle;
201     }
202 
203     // Association done
204     return cond;
205 }
206 
207 
handleAssociation(T_ASC_Association * assoc,OFBool correctUIDPadding)208 OFCondition DcmQueryRetrieveSCP::handleAssociation(T_ASC_Association * assoc, OFBool correctUIDPadding)
209 {
210     OFCondition         cond = EC_Normal;
211     DIC_NODENAME        peerHostName;
212     DIC_AE              peerAETitle;
213     DIC_AE              myAETitle;
214     OFString            temp_str;
215 
216     ASC_getPresentationAddresses(assoc->params, peerHostName, sizeof(peerHostName), NULL, 0);
217     ASC_getAPTitles(assoc->params, peerAETitle, sizeof(peerAETitle), myAETitle, sizeof(myAETitle), NULL, 0);
218 
219     /* now do the real work */
220     cond = dispatch(assoc, correctUIDPadding);
221 
222     /* clean up on association termination */
223     if (cond == DUL_PEERREQUESTEDRELEASE) {
224         DCMQRDB_INFO("Association Release");
225         cond = ASC_acknowledgeRelease(assoc);
226         ASC_dropSCPAssociation(assoc);
227     } else if (cond == DUL_PEERABORTEDASSOCIATION) {
228         DCMQRDB_INFO("Association Aborted");
229     } else {
230         DCMQRDB_ERROR("DIMSE Failure (aborting association): " << DimseCondition::dump(temp_str, cond));
231         /* some kind of error so abort the association */
232         cond = ASC_abortAssociation(assoc);
233     }
234 
235     cond = ASC_dropAssociation(assoc);
236     if (cond.bad()) {
237         DCMQRDB_ERROR("Cannot Drop Association: " << DimseCondition::dump(temp_str, cond));
238     }
239     cond = ASC_destroyAssociation(&assoc);
240     if (cond.bad()) {
241         DCMQRDB_ERROR("Cannot Destroy Association: " << DimseCondition::dump(temp_str, cond));
242     }
243 
244     return cond;
245 }
246 
247 
echoSCP(T_ASC_Association * assoc,T_DIMSE_C_EchoRQ * req,T_ASC_PresentationContextID presId)248 OFCondition DcmQueryRetrieveSCP::echoSCP(T_ASC_Association * assoc, T_DIMSE_C_EchoRQ * req,
249         T_ASC_PresentationContextID presId)
250 {
251     OFCondition cond = EC_Normal;
252 
253     DCMQRDB_INFO("Received Echo SCP RQ: MsgID " << req->MessageID);
254     /* we send an echo response back */
255     cond = DIMSE_sendEchoResponse(assoc, presId,
256         req, STATUS_Success, NULL);
257 
258     if (cond.bad()) {
259         OFString temp_str;
260         DCMQRDB_ERROR("echoSCP: Echo Response Failed: " << DimseCondition::dump(temp_str, cond));
261     }
262     return cond;
263 }
264 
265 
findSCP(T_ASC_Association * assoc,T_DIMSE_C_FindRQ * request,T_ASC_PresentationContextID presID,DcmQueryRetrieveDatabaseHandle & dbHandle)266 OFCondition DcmQueryRetrieveSCP::findSCP(T_ASC_Association * assoc, T_DIMSE_C_FindRQ * request,
267         T_ASC_PresentationContextID presID,
268         DcmQueryRetrieveDatabaseHandle& dbHandle)
269 
270 {
271     OFCondition cond = EC_Normal;
272     DcmQueryRetrieveFindContext context(dbHandle, options_, STATUS_Pending, config_->getCharacterSetOptions());
273 
274     DIC_AE aeTitle;
275     aeTitle[0] = '\0';
276     ASC_getAPTitles(assoc->params, NULL, 0, aeTitle, sizeof(aeTitle), NULL, 0);
277     context.setOurAETitle(aeTitle);
278 
279     OFString temp_str;
280     DCMQRDB_INFO("Received Find SCP:" << OFendl << DIMSE_dumpMessage(temp_str, *request, DIMSE_INCOMING));
281 
282     cond = DIMSE_findProvider(assoc, presID, request,
283         findCallback, &context, options_.blockMode_, options_.dimse_timeout_);
284     if (cond.bad()) {
285         DCMQRDB_ERROR("Find SCP Failed: " << DimseCondition::dump(temp_str, cond));
286     }
287     return cond;
288 }
289 
290 
getSCP(T_ASC_Association * assoc,T_DIMSE_C_GetRQ * request,T_ASC_PresentationContextID presID,DcmQueryRetrieveDatabaseHandle & dbHandle)291 OFCondition DcmQueryRetrieveSCP::getSCP(T_ASC_Association * assoc, T_DIMSE_C_GetRQ * request,
292         T_ASC_PresentationContextID presID, DcmQueryRetrieveDatabaseHandle& dbHandle)
293 {
294     OFCondition cond = EC_Normal;
295     DcmQueryRetrieveGetContext context(dbHandle, options_, STATUS_Pending, assoc, request->MessageID, request->Priority, presID);
296 
297     DIC_AE aeTitle;
298     aeTitle[0] = '\0';
299     ASC_getAPTitles(assoc->params, NULL, 0, aeTitle, sizeof(aeTitle), NULL, 0);
300     context.setOurAETitle(aeTitle);
301 
302     OFString temp_str;
303     DCMQRDB_INFO("Received Get SCP:" << OFendl << DIMSE_dumpMessage(temp_str, *request, DIMSE_INCOMING));
304 
305     cond = DIMSE_getProvider(assoc, presID, request,
306         getCallback, &context, options_.blockMode_, options_.dimse_timeout_);
307     if (cond.bad()) {
308         DCMQRDB_ERROR("Get SCP Failed: " << DimseCondition::dump(temp_str, cond));
309     }
310     return cond;
311 }
312 
313 
moveSCP(T_ASC_Association * assoc,T_DIMSE_C_MoveRQ * request,T_ASC_PresentationContextID presID,DcmQueryRetrieveDatabaseHandle & dbHandle)314 OFCondition DcmQueryRetrieveSCP::moveSCP(T_ASC_Association * assoc, T_DIMSE_C_MoveRQ * request,
315         T_ASC_PresentationContextID presID, DcmQueryRetrieveDatabaseHandle& dbHandle)
316 {
317     OFCondition cond = EC_Normal;
318     DcmQueryRetrieveMoveContext context(dbHandle, options_, associationConfiguration_, config_, STATUS_Pending, assoc, request->MessageID, request->Priority);
319 
320     DIC_AE aeTitle;
321     aeTitle[0] = '\0';
322     ASC_getAPTitles(assoc->params, NULL, 0, aeTitle, sizeof(aeTitle), NULL, 0);
323     context.setOurAETitle(aeTitle);
324 
325     OFString temp_str;
326     DCMQRDB_INFO("Received Move SCP:" << OFendl << DIMSE_dumpMessage(temp_str, *request, DIMSE_INCOMING));
327 
328     cond = DIMSE_moveProvider(assoc, presID, request,
329         moveCallback, &context, options_.blockMode_, options_.dimse_timeout_);
330     if (cond.bad()) {
331         DCMQRDB_ERROR("Move SCP Failed: " << DimseCondition::dump(temp_str, cond));
332     }
333     return cond;
334 }
335 
336 
storeSCP(T_ASC_Association * assoc,T_DIMSE_C_StoreRQ * request,T_ASC_PresentationContextID presId,DcmQueryRetrieveDatabaseHandle & dbHandle,OFBool correctUIDPadding)337 OFCondition DcmQueryRetrieveSCP::storeSCP(T_ASC_Association * assoc, T_DIMSE_C_StoreRQ * request,
338              T_ASC_PresentationContextID presId,
339              DcmQueryRetrieveDatabaseHandle& dbHandle,
340              OFBool correctUIDPadding)
341 {
342     OFCondition cond = EC_Normal;
343     OFCondition dbcond = EC_Normal;
344     char imageFileName[MAXPATHLEN+1];
345     DcmFileFormat dcmff;
346 
347     DcmQueryRetrieveStoreContext context(dbHandle, options_, STATUS_Success, &dcmff, correctUIDPadding);
348 
349     OFString temp_str;
350     DCMQRDB_INFO("Received Store SCP:" << OFendl << DIMSE_dumpMessage(temp_str, *request, DIMSE_INCOMING));
351 
352     if (!dcmIsaStorageSOPClassUID(request->AffectedSOPClassUID)) {
353         /* callback will send back sop class not supported status */
354         context.setStatus(STATUS_STORE_Refused_SOPClassNotSupported);
355         /* must still receive data */
356         OFStandard::strlcpy(imageFileName, NULL_DEVICE_NAME, sizeof(imageFileName));
357     } else if (options_.ignoreStoreData_) {
358         OFStandard::strlcpy(imageFileName, NULL_DEVICE_NAME, sizeof(imageFileName));
359     } else {
360         dbcond = dbHandle.makeNewStoreFileName(
361             request->AffectedSOPClassUID,
362             request->AffectedSOPInstanceUID,
363             imageFileName, sizeof(imageFileName));
364         if (dbcond.bad()) {
365             DCMQRDB_ERROR("storeSCP: Database: makeNewStoreFileName Failed");
366             /* must still receive data */
367             OFStandard::strlcpy(imageFileName, NULL_DEVICE_NAME, sizeof(imageFileName));
368             /* callback will send back out of resources status */
369             context.setStatus(STATUS_STORE_Refused_OutOfResources);
370         }
371     }
372 
373 #ifdef LOCK_IMAGE_FILES
374     /* exclusively lock image file */
375 #ifdef O_BINARY
376     int lockfd = open(imageFileName, (O_WRONLY | O_CREAT | O_TRUNC | O_BINARY), 0666);
377 #else
378     int lockfd = open(imageFileName, (O_WRONLY | O_CREAT | O_TRUNC), 0666);
379 #endif
380     if (lockfd < 0)
381     {
382         DCMQRDB_ERROR("storeSCP: file locking failed, cannot create file");
383 
384         /* must still receive data */
385         OFStandard::strlcpy(imageFileName, NULL_DEVICE_NAME, sizeof(imageFileName));
386 
387         /* callback will send back out of resources status */
388         context.setStatus(STATUS_STORE_Refused_OutOfResources);
389     }
390     else
391       dcmtk_flock(lockfd, LOCK_EX);
392 #endif
393 
394     context.setFileName(imageFileName);
395 
396     // store SourceApplicationEntityTitle in metaheader
397     if (assoc && assoc->params)
398     {
399       const char *aet = assoc->params->DULparams.callingAPTitle;
400       if (aet) dcmff.getMetaInfo()->putAndInsertString(DCM_SourceApplicationEntityTitle, aet);
401     }
402 
403     DcmDataset *dset = dcmff.getDataset();
404 
405     /* we must still retrieve the data set even if some error has occurred */
406 
407     if (options_.bitPreserving_) { /* the bypass option can be set on the command line */
408         cond = DIMSE_storeProvider(assoc, presId, request, imageFileName, (int)options_.useMetaheader_,
409                                    NULL, storeCallback,
410                                    (void*)&context, options_.blockMode_, options_.dimse_timeout_);
411     } else {
412         cond = DIMSE_storeProvider(assoc, presId, request, (char *)NULL, (int)options_.useMetaheader_,
413                                    &dset, storeCallback,
414                                    (void*)&context, options_.blockMode_, options_.dimse_timeout_);
415     }
416 
417     if (cond.bad()) {
418         DCMQRDB_ERROR("Store SCP Failed: " << DimseCondition::dump(temp_str, cond));
419     }
420     if (!options_.ignoreStoreData_ && (cond.bad() || (context.getStatus() != STATUS_Success)))
421     {
422       /* remove file */
423       if (strcmp(imageFileName, NULL_DEVICE_NAME) != 0) // don't try to delete /dev/null
424       {
425         DCMQRDB_INFO("Store SCP: Deleting Image File: %s" << imageFileName);
426         OFStandard::deleteFile(imageFileName);
427       }
428       dbHandle.pruneInvalidRecords();
429     }
430 
431 #ifdef LOCK_IMAGE_FILES
432     /* unlock image file */
433     if (lockfd >= 0)
434     {
435       dcmtk_flock(lockfd, LOCK_UN);
436       close(lockfd);
437     }
438 #endif
439 
440     return cond;
441 }
442 
443 
444 /* Association negotiation */
445 
refuseAnyStorageContexts(T_ASC_Association * assoc)446 void DcmQueryRetrieveSCP::refuseAnyStorageContexts(T_ASC_Association * assoc)
447 {
448     int i;
449     T_ASC_PresentationContextID pid;
450 
451     for (i = 0; i < numberOfDcmAllStorageSOPClassUIDs; i++) {
452         do {
453           pid = ASC_findAcceptedPresentationContextID(assoc, dcmAllStorageSOPClassUIDs[i]);
454           if (pid != 0) ASC_refusePresentationContext(assoc->params, pid, ASC_P_USERREJECTION);
455         } while (pid != 0); // repeat as long as we find presentation contexts for this SOP class - there might be multiple ones.
456     }
457 }
458 
459 
refuseAssociation(T_ASC_Association ** assoc,CTN_RefuseReason reason)460 OFCondition DcmQueryRetrieveSCP::refuseAssociation(T_ASC_Association ** assoc, CTN_RefuseReason reason)
461 {
462     OFCondition cond = EC_Normal;
463     T_ASC_RejectParameters rej;
464     OFString temp_str;
465 
466     const char *reason_string;
467     switch (reason)
468     {
469       case CTN_TooManyAssociations:
470           reason_string = "TooManyAssociations";
471           break;
472       case CTN_CannotFork:
473           reason_string = "CannotFork";
474           break;
475       case CTN_BadAppContext:
476           reason_string = "BadAppContext";
477           break;
478       case CTN_BadAEPeer:
479           reason_string = "BadAEPeer";
480           break;
481       case CTN_BadAEService:
482           reason_string = "BadAEService";
483           break;
484       case CTN_NoReason:
485           reason_string = "NoReason";
486         break;
487       default:
488           reason_string = "???";
489           break;
490     }
491     DCMQRDB_INFO("Refusing Association (" << reason_string << ")");
492 
493     switch (reason)
494     {
495       case CTN_TooManyAssociations:
496         rej.result = ASC_RESULT_REJECTEDTRANSIENT;
497         rej.source = ASC_SOURCE_SERVICEPROVIDER_PRESENTATION_RELATED;
498         rej.reason = ASC_REASON_SP_PRES_LOCALLIMITEXCEEDED;
499         break;
500       case CTN_CannotFork:
501         rej.result = ASC_RESULT_REJECTEDPERMANENT;
502         rej.source = ASC_SOURCE_SERVICEPROVIDER_PRESENTATION_RELATED;
503         rej.reason = ASC_REASON_SP_PRES_TEMPORARYCONGESTION;
504         break;
505       case CTN_BadAppContext:
506         rej.result = ASC_RESULT_REJECTEDTRANSIENT;
507         rej.source = ASC_SOURCE_SERVICEUSER;
508         rej.reason = ASC_REASON_SU_APPCONTEXTNAMENOTSUPPORTED;
509         break;
510       case CTN_BadAEPeer:
511         rej.result = ASC_RESULT_REJECTEDPERMANENT;
512         rej.source = ASC_SOURCE_SERVICEUSER;
513         rej.reason = ASC_REASON_SU_CALLINGAETITLENOTRECOGNIZED;
514         break;
515       case CTN_BadAEService:
516         rej.result = ASC_RESULT_REJECTEDPERMANENT;
517         rej.source = ASC_SOURCE_SERVICEUSER;
518         rej.reason = ASC_REASON_SU_CALLEDAETITLENOTRECOGNIZED;
519         break;
520       case CTN_NoReason:
521       default:
522         rej.result = ASC_RESULT_REJECTEDPERMANENT;
523         rej.source = ASC_SOURCE_SERVICEUSER;
524         rej.reason = ASC_REASON_SU_NOREASON;
525         break;
526     }
527 
528     cond = ASC_rejectAssociation(*assoc, &rej);
529 
530     if (cond.bad())
531     {
532       DCMQRDB_ERROR("Association Reject Failed: " << DimseCondition::dump(temp_str, cond));
533     }
534 
535     cond = ASC_dropAssociation(*assoc);
536     if (cond.bad())
537     {
538       DCMQRDB_ERROR("Cannot Drop Association: " << DimseCondition::dump(temp_str, cond));
539     }
540     cond = ASC_destroyAssociation(assoc);
541     if (cond.bad())
542     {
543       DCMQRDB_ERROR("Cannot Destroy Association: " << DimseCondition::dump(temp_str, cond));
544     }
545 
546     return cond;
547 }
548 
549 
negotiateAssociation(T_ASC_Association * assoc)550 OFCondition DcmQueryRetrieveSCP::negotiateAssociation(T_ASC_Association * assoc)
551 {
552     OFCondition cond = EC_Normal;
553     int i;
554     T_ASC_PresentationContextID movepid, findpid;
555     OFString temp_str;
556     struct { const char *moveSyntax, *findSyntax; } queryRetrievePairs[] =
557     {
558       { UID_MOVEPatientRootQueryRetrieveInformationModel,
559         UID_FINDPatientRootQueryRetrieveInformationModel },
560       { UID_MOVEStudyRootQueryRetrieveInformationModel,
561         UID_FINDStudyRootQueryRetrieveInformationModel },
562       { UID_RETIRED_MOVEPatientStudyOnlyQueryRetrieveInformationModel,
563         UID_RETIRED_FINDPatientStudyOnlyQueryRetrieveInformationModel }
564     };
565 
566     DIC_AE calledAETitle;
567     ASC_getAPTitles(assoc->params, NULL, 0, calledAETitle, sizeof(calledAETitle), NULL, 0);
568 
569     const char* transferSyntaxes[] = { NULL, NULL, NULL, NULL };
570     int numTransferSyntaxes = 0;
571 
572     switch (options_.networkTransferSyntax_)
573     {
574       case EXS_LittleEndianImplicit:
575         /* we only support Little Endian Implicit */
576         transferSyntaxes[0]  = UID_LittleEndianImplicitTransferSyntax;
577         numTransferSyntaxes = 1;
578         break;
579       case EXS_LittleEndianExplicit:
580         /* we prefer Little Endian Explicit */
581         transferSyntaxes[0] = UID_LittleEndianExplicitTransferSyntax;
582         transferSyntaxes[1] = UID_BigEndianExplicitTransferSyntax;
583         transferSyntaxes[2]  = UID_LittleEndianImplicitTransferSyntax;
584         numTransferSyntaxes = 3;
585         break;
586       case EXS_BigEndianExplicit:
587         /* we prefer Big Endian Explicit */
588         transferSyntaxes[0] = UID_BigEndianExplicitTransferSyntax;
589         transferSyntaxes[1] = UID_LittleEndianExplicitTransferSyntax;
590         transferSyntaxes[2]  = UID_LittleEndianImplicitTransferSyntax;
591         numTransferSyntaxes = 3;
592         break;
593 #ifndef DISABLE_COMPRESSION_EXTENSION
594       case EXS_JPEGProcess14SV1:
595         /* we prefer JPEGLossless:Hierarchical-1stOrderPrediction (default lossless) */
596         transferSyntaxes[0] = UID_JPEGProcess14SV1TransferSyntax;
597         transferSyntaxes[1] = UID_LittleEndianExplicitTransferSyntax;
598         transferSyntaxes[2] = UID_BigEndianExplicitTransferSyntax;
599         transferSyntaxes[3] = UID_LittleEndianImplicitTransferSyntax;
600         numTransferSyntaxes = 4;
601         break;
602       case EXS_JPEGProcess1:
603         /* we prefer JPEGBaseline (default lossy for 8 bit images) */
604         transferSyntaxes[0] = UID_JPEGProcess1TransferSyntax;
605         transferSyntaxes[1] = UID_LittleEndianExplicitTransferSyntax;
606         transferSyntaxes[2] = UID_BigEndianExplicitTransferSyntax;
607         transferSyntaxes[3] = UID_LittleEndianImplicitTransferSyntax;
608         numTransferSyntaxes = 4;
609         break;
610       case EXS_JPEGProcess2_4:
611         /* we prefer JPEGExtended (default lossy for 12 bit images) */
612         transferSyntaxes[0] = UID_JPEGProcess2_4TransferSyntax;
613         transferSyntaxes[1] = UID_LittleEndianExplicitTransferSyntax;
614         transferSyntaxes[2] = UID_BigEndianExplicitTransferSyntax;
615         transferSyntaxes[3] = UID_LittleEndianImplicitTransferSyntax;
616         numTransferSyntaxes = 4;
617         break;
618       case EXS_JPEG2000LosslessOnly:
619         /* we prefer JPEG 2000 lossless */
620         transferSyntaxes[0] = UID_JPEG2000LosslessOnlyTransferSyntax;
621         transferSyntaxes[1] = UID_LittleEndianExplicitTransferSyntax;
622         transferSyntaxes[2] = UID_BigEndianExplicitTransferSyntax;
623         transferSyntaxes[3] = UID_LittleEndianImplicitTransferSyntax;
624         numTransferSyntaxes = 4;
625         break;
626       case EXS_JPEG2000:
627         /* we prefer JPEG 2000 lossy or lossless */
628         transferSyntaxes[0] = UID_JPEG2000TransferSyntax;
629         transferSyntaxes[1] = UID_LittleEndianExplicitTransferSyntax;
630         transferSyntaxes[2] = UID_BigEndianExplicitTransferSyntax;
631         transferSyntaxes[3] = UID_LittleEndianImplicitTransferSyntax;
632         numTransferSyntaxes = 4;
633         break;
634       case EXS_JPEGLSLossless:
635         /* we prefer JPEG-LS Lossless */
636         transferSyntaxes[0] = UID_JPEGLSLosslessTransferSyntax;
637         transferSyntaxes[1] = UID_LittleEndianExplicitTransferSyntax;
638         transferSyntaxes[2] = UID_BigEndianExplicitTransferSyntax;
639         transferSyntaxes[3] = UID_LittleEndianImplicitTransferSyntax;
640         numTransferSyntaxes = 4;
641         break;
642       case EXS_JPEGLSLossy:
643         /* we prefer JPEG-LS Lossy */
644         transferSyntaxes[0] = UID_JPEGLSLossyTransferSyntax;
645         transferSyntaxes[1] = UID_LittleEndianExplicitTransferSyntax;
646         transferSyntaxes[2] = UID_BigEndianExplicitTransferSyntax;
647         transferSyntaxes[3] = UID_LittleEndianImplicitTransferSyntax;
648         numTransferSyntaxes = 4;
649         break;
650       case EXS_MPEG2MainProfileAtMainLevel:
651         /* we prefer MPEG2 MP@ML */
652         transferSyntaxes[0] = UID_MPEG2MainProfileAtMainLevelTransferSyntax;
653         transferSyntaxes[1] = UID_LittleEndianExplicitTransferSyntax;
654         transferSyntaxes[2] = UID_BigEndianExplicitTransferSyntax;
655         transferSyntaxes[3] = UID_LittleEndianImplicitTransferSyntax;
656         numTransferSyntaxes = 4;
657         break;
658       case EXS_MPEG2MainProfileAtHighLevel:
659         /* we prefer MPEG2 MP@HL */
660         transferSyntaxes[0] = UID_MPEG2MainProfileAtHighLevelTransferSyntax;
661         transferSyntaxes[1] = UID_LittleEndianExplicitTransferSyntax;
662         transferSyntaxes[2] = UID_BigEndianExplicitTransferSyntax;
663         transferSyntaxes[3] = UID_LittleEndianImplicitTransferSyntax;
664         numTransferSyntaxes = 4;
665         break;
666       case EXS_MPEG4HighProfileLevel4_1:
667         /* we prefer MPEG4 HP/L4.1 */
668         transferSyntaxes[0] = UID_MPEG4HighProfileLevel4_1TransferSyntax;
669         transferSyntaxes[1] = UID_LittleEndianExplicitTransferSyntax;
670         transferSyntaxes[2] = UID_BigEndianExplicitTransferSyntax;
671         transferSyntaxes[3] = UID_LittleEndianImplicitTransferSyntax;
672         numTransferSyntaxes = 4;
673         break;
674       case EXS_MPEG4BDcompatibleHighProfileLevel4_1:
675         /* we prefer MPEG4 BD HP/L4.1 */
676         transferSyntaxes[0] = UID_MPEG4BDcompatibleHighProfileLevel4_1TransferSyntax;
677         transferSyntaxes[1] = UID_LittleEndianExplicitTransferSyntax;
678         transferSyntaxes[2] = UID_BigEndianExplicitTransferSyntax;
679         transferSyntaxes[3] = UID_LittleEndianImplicitTransferSyntax;
680         numTransferSyntaxes = 4;
681         break;
682       case EXS_MPEG4HighProfileLevel4_2_For2DVideo:
683         /* we prefer MPEG4 HP/L4.2 for 2D Videos */
684         transferSyntaxes[0] = UID_MPEG4HighProfileLevel4_2_For2DVideoTransferSyntax;
685         transferSyntaxes[1] = UID_LittleEndianExplicitTransferSyntax;
686         transferSyntaxes[2] = UID_BigEndianExplicitTransferSyntax;
687         transferSyntaxes[3] = UID_LittleEndianImplicitTransferSyntax;
688         numTransferSyntaxes = 4;
689         break;
690       case EXS_MPEG4HighProfileLevel4_2_For3DVideo:
691         /* we prefer MPEG4 HP/L4.2 for 3D Videos */
692         transferSyntaxes[0] = UID_MPEG4HighProfileLevel4_2_For3DVideoTransferSyntax;
693         transferSyntaxes[1] = UID_LittleEndianExplicitTransferSyntax;
694         transferSyntaxes[2] = UID_BigEndianExplicitTransferSyntax;
695         transferSyntaxes[3] = UID_LittleEndianImplicitTransferSyntax;
696         numTransferSyntaxes = 4;
697         break;
698       case EXS_MPEG4StereoHighProfileLevel4_2:
699         /* we prefer MPEG4 Stereo HP/L4.2 */
700         transferSyntaxes[0] = UID_MPEG4StereoHighProfileLevel4_2TransferSyntax;
701         transferSyntaxes[1] = UID_LittleEndianExplicitTransferSyntax;
702         transferSyntaxes[2] = UID_BigEndianExplicitTransferSyntax;
703         transferSyntaxes[3] = UID_LittleEndianImplicitTransferSyntax;
704         numTransferSyntaxes = 4;
705         break;
706       case EXS_HEVCMainProfileLevel5_1:
707         /* we prefer HEVC/H.265 Main Profile/L5.1 */
708         transferSyntaxes[0] = UID_HEVCMainProfileLevel5_1TransferSyntax;
709         transferSyntaxes[1] = UID_LittleEndianExplicitTransferSyntax;
710         transferSyntaxes[2] = UID_BigEndianExplicitTransferSyntax;
711         transferSyntaxes[3] = UID_LittleEndianImplicitTransferSyntax;
712         numTransferSyntaxes = 4;
713         break;
714       case EXS_HEVCMain10ProfileLevel5_1:
715         /* we prefer HEVC/H.265 Main 10 Profile/L5.1 */
716         transferSyntaxes[0] = UID_HEVCMain10ProfileLevel5_1TransferSyntax;
717         transferSyntaxes[1] = UID_LittleEndianExplicitTransferSyntax;
718         transferSyntaxes[2] = UID_BigEndianExplicitTransferSyntax;
719         transferSyntaxes[3] = UID_LittleEndianImplicitTransferSyntax;
720         numTransferSyntaxes = 4;
721         break;
722       case EXS_RLELossless:
723         /* we prefer RLE Lossless */
724         transferSyntaxes[0] = UID_RLELosslessTransferSyntax;
725         transferSyntaxes[1] = UID_LittleEndianExplicitTransferSyntax;
726         transferSyntaxes[2] = UID_BigEndianExplicitTransferSyntax;
727         transferSyntaxes[3] = UID_LittleEndianImplicitTransferSyntax;
728         numTransferSyntaxes = 4;
729         break;
730 #ifdef WITH_ZLIB
731       case EXS_DeflatedLittleEndianExplicit:
732         /* we prefer deflated transmission */
733         transferSyntaxes[0] = UID_DeflatedExplicitVRLittleEndianTransferSyntax;
734         transferSyntaxes[1] = UID_LittleEndianExplicitTransferSyntax;
735         transferSyntaxes[2] = UID_BigEndianExplicitTransferSyntax;
736         transferSyntaxes[3] = UID_LittleEndianImplicitTransferSyntax;
737         numTransferSyntaxes = 4;
738         break;
739 #endif
740 #endif
741       default:
742         /* We prefer explicit transfer syntaxes.
743          * If we are running on a Little Endian machine we prefer
744          * LittleEndianExplicitTransferSyntax to BigEndianTransferSyntax.
745          */
746         if (gLocalByteOrder == EBO_LittleEndian)  /* defined in dcxfer.h */
747         {
748           transferSyntaxes[0] = UID_LittleEndianExplicitTransferSyntax;
749           transferSyntaxes[1] = UID_BigEndianExplicitTransferSyntax;
750         } else {
751           transferSyntaxes[0] = UID_BigEndianExplicitTransferSyntax;
752           transferSyntaxes[1] = UID_LittleEndianExplicitTransferSyntax;
753         }
754         transferSyntaxes[2] = UID_LittleEndianImplicitTransferSyntax;
755         numTransferSyntaxes = 3;
756         break;
757     }
758 
759     const char * const nonStorageSyntaxes[] =
760     {
761         UID_VerificationSOPClass,
762         UID_FINDPatientRootQueryRetrieveInformationModel,
763         UID_MOVEPatientRootQueryRetrieveInformationModel,
764         UID_GETPatientRootQueryRetrieveInformationModel,
765 #ifndef NO_PATIENTSTUDYONLY_SUPPORT
766         UID_RETIRED_FINDPatientStudyOnlyQueryRetrieveInformationModel,
767         UID_RETIRED_MOVEPatientStudyOnlyQueryRetrieveInformationModel,
768         UID_RETIRED_GETPatientStudyOnlyQueryRetrieveInformationModel,
769 #endif
770         UID_FINDStudyRootQueryRetrieveInformationModel,
771         UID_MOVEStudyRootQueryRetrieveInformationModel,
772         UID_GETStudyRootQueryRetrieveInformationModel,
773         UID_PrivateShutdownSOPClass
774     };
775 
776     const int numberOfNonStorageSyntaxes = DIM_OF(nonStorageSyntaxes);
777     const char *selectedNonStorageSyntaxes[DIM_OF(nonStorageSyntaxes)];
778     int numberOfSelectedNonStorageSyntaxes = 0;
779     for (i = 0; i < numberOfNonStorageSyntaxes; i++)
780     {
781         if (0 == strcmp(nonStorageSyntaxes[i], UID_FINDPatientRootQueryRetrieveInformationModel))
782         {
783           if (options_.supportPatientRoot_) selectedNonStorageSyntaxes[numberOfSelectedNonStorageSyntaxes++] = nonStorageSyntaxes[i];
784         }
785         else if (0 == strcmp(nonStorageSyntaxes[i], UID_MOVEPatientRootQueryRetrieveInformationModel))
786         {
787           if (options_.supportPatientRoot_) selectedNonStorageSyntaxes[numberOfSelectedNonStorageSyntaxes++] = nonStorageSyntaxes[i];
788         }
789         else if (0 == strcmp(nonStorageSyntaxes[i], UID_GETPatientRootQueryRetrieveInformationModel))
790         {
791           if (options_.supportPatientRoot_ && (! options_.disableGetSupport_)) selectedNonStorageSyntaxes[numberOfSelectedNonStorageSyntaxes++] = nonStorageSyntaxes[i];
792         }
793         else if (0 == strcmp(nonStorageSyntaxes[i], UID_RETIRED_FINDPatientStudyOnlyQueryRetrieveInformationModel))
794         {
795           if (options_.supportPatientStudyOnly_) selectedNonStorageSyntaxes[numberOfSelectedNonStorageSyntaxes++] = nonStorageSyntaxes[i];
796         }
797         else if (0 == strcmp(nonStorageSyntaxes[i], UID_RETIRED_MOVEPatientStudyOnlyQueryRetrieveInformationModel))
798         {
799           if (options_.supportPatientStudyOnly_) selectedNonStorageSyntaxes[numberOfSelectedNonStorageSyntaxes++] = nonStorageSyntaxes[i];
800         }
801         else if (0 == strcmp(nonStorageSyntaxes[i], UID_RETIRED_GETPatientStudyOnlyQueryRetrieveInformationModel))
802         {
803           if (options_.supportPatientStudyOnly_ && (! options_.disableGetSupport_)) selectedNonStorageSyntaxes[numberOfSelectedNonStorageSyntaxes++] = nonStorageSyntaxes[i];
804         }
805         else if (0 == strcmp(nonStorageSyntaxes[i], UID_FINDStudyRootQueryRetrieveInformationModel))
806         {
807           if (options_.supportStudyRoot_) selectedNonStorageSyntaxes[numberOfSelectedNonStorageSyntaxes++] = nonStorageSyntaxes[i];
808         }
809         else if (0 == strcmp(nonStorageSyntaxes[i], UID_MOVEStudyRootQueryRetrieveInformationModel))
810         {
811           if (options_.supportStudyRoot_) selectedNonStorageSyntaxes[numberOfSelectedNonStorageSyntaxes++] = nonStorageSyntaxes[i];
812         }
813         else if (0 == strcmp(nonStorageSyntaxes[i], UID_GETStudyRootQueryRetrieveInformationModel))
814         {
815           if (options_.supportStudyRoot_ && (! options_.disableGetSupport_)) selectedNonStorageSyntaxes[numberOfSelectedNonStorageSyntaxes++] = nonStorageSyntaxes[i];
816         }
817         else if (0 == strcmp(nonStorageSyntaxes[i], UID_PrivateShutdownSOPClass))
818         {
819           if (options_.allowShutdown_) selectedNonStorageSyntaxes[numberOfSelectedNonStorageSyntaxes++] = nonStorageSyntaxes[i];
820         } else {
821             selectedNonStorageSyntaxes[numberOfSelectedNonStorageSyntaxes++] = nonStorageSyntaxes[i];
822         }
823     }
824 
825     if (options_.incomingProfile.empty())
826     {
827       /*  accept any of the storage syntaxes */
828       if (options_.disableGetSupport_)
829       {
830         /* accept storage syntaxes with default role only */
831         cond = ASC_acceptContextsWithPreferredTransferSyntaxes(
832           assoc->params,
833           dcmAllStorageSOPClassUIDs, numberOfDcmAllStorageSOPClassUIDs,
834           (const char**)transferSyntaxes, numTransferSyntaxes);
835         if (cond.bad()) {
836           DCMQRDB_ERROR("Cannot accept presentation contexts: " << DimseCondition::dump(temp_str, cond));
837         }
838       } else {
839         /* accept storage syntaxes with proposed role */
840         T_ASC_PresentationContext pc;
841         T_ASC_SC_ROLE role;
842         int npc = ASC_countPresentationContexts(assoc->params);
843         for (i = 0; i < npc; i++)
844         {
845           ASC_getPresentationContext(assoc->params, i, &pc);
846           if (dcmIsaStorageSOPClassUID(pc.abstractSyntax))
847           {
848             /*
849             ** We are prepared to accept whatever role he proposes.
850             ** Normally we can be the SCP of the Storage Service Class.
851             ** When processing the C-GET operation we can be the SCU of the Storage Service Class.
852             */
853             role = pc.proposedRole;
854 
855             /*
856             ** Accept in the order "least wanted" to "most wanted" transfer
857             ** syntax.  Accepting a transfer syntax will override previously
858             ** accepted transfer syntaxes.
859             */
860             for (int k = numTransferSyntaxes - 1; k >= 0; k--)
861             {
862               for (int j = 0; j < (int)pc.transferSyntaxCount; j++)
863               {
864                 /* if the transfer syntax was proposed then we can accept it
865                  * appears in our supported list of transfer syntaxes
866                  */
867                 if (strcmp(pc.proposedTransferSyntaxes[j], transferSyntaxes[k]) == 0)
868                 {
869                   cond = ASC_acceptPresentationContext(
870                       assoc->params, pc.presentationContextID, transferSyntaxes[k], role);
871                   if (cond.bad()) return cond;
872                 }
873               }
874             }
875           }
876         } /* for */
877       } /* else */
878     }
879     else
880     {
881       cond = associationConfiguration_.evaluateAssociationParameters(options_.incomingProfile.c_str(), *assoc);
882       if (cond.bad()) return cond;
883     }
884 
885     /*  accept any of the non-storage syntaxes */
886     cond = ASC_acceptContextsWithPreferredTransferSyntaxes(
887     assoc->params,
888     (const char**)selectedNonStorageSyntaxes, numberOfSelectedNonStorageSyntaxes,
889     (const char**)transferSyntaxes, numTransferSyntaxes);
890     if (cond.bad()) {
891         DCMQRDB_ERROR("Cannot accept presentation contexts: " << DimseCondition::dump(temp_str, cond));
892     }
893 
894     /*
895      * check if we have negotiated the private "shutdown" SOP Class
896      */
897     if (0 != ASC_findAcceptedPresentationContextID(assoc, UID_PrivateShutdownSOPClass))
898     {
899       DCMQRDB_INFO("Shutting down server ... (negotiated private \"shut down\" SOP class)");
900       refuseAssociation(&assoc, CTN_NoReason);
901       return ASC_SHUTDOWNAPPLICATION;
902     }
903 
904     /*
905      * Refuse any "Storage" presentation contexts to non-writable
906      * storage areas.
907      */
908     if (!config_->writableStorageArea(calledAETitle))
909     {
910       refuseAnyStorageContexts(assoc);
911     }
912 
913     /*
914      * Enforce RSNA'93 Demonstration Requirements about only
915      * accepting a context for MOVE if a context for FIND is also present.
916      */
917 
918     for (i = 0; i < (int)DIM_OF(queryRetrievePairs); i++) {
919         movepid = ASC_findAcceptedPresentationContextID(assoc,
920         queryRetrievePairs[i].moveSyntax);
921         if (movepid != 0) {
922           findpid = ASC_findAcceptedPresentationContextID(assoc,
923               queryRetrievePairs[i].findSyntax);
924           if (findpid == 0) {
925             if (options_.requireFindForMove_) {
926               /* refuse the move */
927               ASC_refusePresentationContext(assoc->params,
928                   movepid, ASC_P_USERREJECTION);
929             } else {
930               DCMQRDB_ERROR("Move Presentation Context but no Find (accepting for now)");
931             }
932           }
933         }
934     }
935 
936     /*
937      * Enforce an Ad-Hoc rule to limit storage access.
938      * If the storage area is "writable" and some other association has
939      * already negotiated a "Storage" class presentation context,
940      * then refuse any "storage" presentation contexts.
941      */
942 
943     if (options_.refuseMultipleStorageAssociations_)
944     {
945         if (config_->writableStorageArea(calledAETitle))
946         {
947           if (processtable_.haveProcessWithWriteAccess(calledAETitle))
948           {
949             refuseAnyStorageContexts(assoc);
950           }
951         }
952     }
953 
954     return cond;
955 }
956 
957 
waitForAssociation(T_ASC_Network * theNet)958 OFCondition DcmQueryRetrieveSCP::waitForAssociation(T_ASC_Network * theNet)
959 {
960     OFCondition cond = EC_Normal;
961     OFString temp_str;
962 #ifdef HAVE_FORK
963     int                 pid;
964 #endif
965     T_ASC_Association  *assoc;
966     char                buf[BUFSIZ];
967     int timeout;
968     OFBool go_cleanup = OFFalse;
969 
970     if (options_.singleProcess_) timeout = 1000;
971     else
972     {
973       if (processtable_.countChildProcesses() > 0)
974       {
975         timeout = 1;
976       } else {
977         timeout = 1000;
978       }
979     }
980 
981     if (ASC_associationWaiting(theNet, timeout))
982     {
983         cond = ASC_receiveAssociation(theNet, &assoc, (int)options_.maxPDU_);
984         if (cond.bad())
985         {
986           DCMQRDB_INFO("Failed to receive association: " << DimseCondition::dump(temp_str, cond));
987           go_cleanup = OFTrue;
988         }
989     } else return EC_Normal;
990 
991     if (! go_cleanup)
992     {
993         DCMQRDB_INFO("Association Received (" << assoc->params->DULparams.callingPresentationAddress
994             << ":" << assoc->params->DULparams.callingAPTitle << " -> "
995             << assoc->params->DULparams.calledAPTitle << ")");
996 
997         DCMQRDB_DEBUG("Parameters:" << OFendl << ASC_dumpParameters(temp_str, assoc->params, ASC_ASSOC_RQ));
998 
999         if (options_.refuse_)
1000         {
1001             DCMQRDB_INFO("Refusing Association (forced via command line)");
1002             cond = refuseAssociation(&assoc, CTN_NoReason);
1003             go_cleanup = OFTrue;
1004         }
1005     }
1006 
1007     if (! go_cleanup)
1008     {
1009         /* Application Context Name */
1010         cond = ASC_getApplicationContextName(assoc->params, buf, sizeof(buf));
1011         if (cond.bad() || strcmp(buf, DICOM_STDAPPLICATIONCONTEXT) != 0)
1012         {
1013             /* reject: the application context name is not supported */
1014             DCMQRDB_INFO("Bad AppContextName: " << buf);
1015             cond = refuseAssociation(&assoc, CTN_BadAppContext);
1016             go_cleanup = OFTrue;
1017         }
1018     }
1019 
1020     if (! go_cleanup)
1021     {
1022         /* Implementation Class UID */
1023         if (options_.rejectWhenNoImplementationClassUID_ &&
1024             strlen(assoc->params->theirImplementationClassUID) == 0)
1025         {
1026             /* reject: no implementation Class UID provided */
1027             DCMQRDB_INFO("No implementation Class UID provided");
1028             cond = refuseAssociation(&assoc, CTN_NoReason);
1029             go_cleanup = OFTrue;
1030         }
1031     }
1032 
1033     if (! go_cleanup)
1034     {
1035         /* Does peer AE have access to required service ?? */
1036         if (! config_->peerInAETitle(assoc->params->DULparams.calledAPTitle,
1037             assoc->params->DULparams.callingAPTitle,
1038             assoc->params->DULparams.callingPresentationAddress))
1039         {
1040             DCMQRDB_DEBUG("Peer "
1041                 << assoc->params->DULparams.callingPresentationAddress << ":"
1042                 << assoc->params->DULparams.callingAPTitle << " is not not permitted to access "
1043                 << assoc->params->DULparams.calledAPTitle << " (see configuration file)");
1044             cond = refuseAssociation(&assoc, CTN_BadAEService);
1045             go_cleanup = OFTrue;
1046         }
1047     }
1048 
1049     if (! go_cleanup)
1050     {
1051         // too many concurrent associations ??
1052         if (processtable_.countChildProcesses() >= OFstatic_cast(size_t, options_.maxAssociations_))
1053         {
1054             cond = refuseAssociation(&assoc, CTN_TooManyAssociations);
1055             go_cleanup = OFTrue;
1056         }
1057     }
1058 
1059     if (! go_cleanup)
1060     {
1061         cond = negotiateAssociation(assoc);
1062         if (cond.bad()) go_cleanup = OFTrue;
1063     }
1064 
1065     if (! go_cleanup)
1066     {
1067         cond = ASC_acknowledgeAssociation(assoc);
1068         if (cond.bad())
1069         {
1070             DCMQRDB_ERROR(DimseCondition::dump(temp_str, cond));
1071             go_cleanup = OFTrue;
1072         }
1073     }
1074 
1075     if (! go_cleanup)
1076     {
1077         DCMQRDB_INFO("Association Acknowledged (Max Send PDV: " << assoc->sendPDVLength << ")");
1078         if (ASC_countAcceptedPresentationContexts(assoc->params) == 0)
1079             DCMQRDB_INFO("    (but no valid presentation contexts)");
1080         DCMQRDB_DEBUG(ASC_dumpParameters(temp_str, assoc->params, ASC_ASSOC_AC));
1081 
1082         if (options_.singleProcess_)
1083         {
1084             /* don't spawn a sub-process to handle the association */
1085             cond = handleAssociation(assoc, options_.correctUIDPadding_);
1086         }
1087 #ifdef HAVE_FORK
1088         else
1089         {
1090             /* spawn a sub-process to handle the association */
1091             pid = (int)(fork());
1092             if (pid < 0)
1093             {
1094                 DCMQRDB_ERROR("Cannot create association sub-process: "
1095                    << OFStandard::getLastSystemErrorCode().message());
1096                 cond = refuseAssociation(&assoc, CTN_CannotFork);
1097                 go_cleanup = OFTrue;
1098             }
1099             else if (pid > 0)
1100             {
1101                 /* parent process, note process in table */
1102                 processtable_.addProcessToTable(pid, assoc);
1103             }
1104             else
1105             {
1106                 /* child process, handle the association */
1107                 cond = handleAssociation(assoc, options_.correctUIDPadding_);
1108                 /* the child process is done so exit */
1109                 exit(0);
1110             }
1111         }
1112 #endif
1113     }
1114 
1115     // cleanup code
1116     OFCondition oldcond = cond;    /* store condition flag for later use */
1117     if (!options_.singleProcess_ && (cond != ASC_SHUTDOWNAPPLICATION))
1118     {
1119         /* the child will handle the association, we can drop it */
1120         cond = ASC_dropAssociation(assoc);
1121         if (cond.bad())
1122         {
1123             DCMQRDB_ERROR("Cannot Drop Association: " << DimseCondition::dump(temp_str, cond));
1124         }
1125         cond = ASC_destroyAssociation(&assoc);
1126         if (cond.bad())
1127         {
1128             DCMQRDB_ERROR("Cannot Destroy Association: " << DimseCondition::dump(temp_str, cond));
1129         }
1130     }
1131 
1132     if (oldcond == ASC_SHUTDOWNAPPLICATION) cond = oldcond; /* abort flag is reported to top-level wait loop */
1133     return cond;
1134 }
1135 
1136 
cleanChildren()1137 void DcmQueryRetrieveSCP::cleanChildren()
1138 {
1139   processtable_.cleanChildren();
1140 }
1141 
1142 
setDatabaseFlags(OFBool dbCheckFindIdentifier,OFBool dbCheckMoveIdentifier)1143 void DcmQueryRetrieveSCP::setDatabaseFlags(
1144   OFBool dbCheckFindIdentifier,
1145   OFBool dbCheckMoveIdentifier)
1146 {
1147   dbCheckFindIdentifier_ = dbCheckFindIdentifier;
1148   dbCheckMoveIdentifier_ = dbCheckMoveIdentifier;
1149 }
1150