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