comparison OrthancFramework/Sources/DicomNetworking/Internals/CommandDispatcher.cpp @ 4468:9c070a34de18

IApplicationEntityFilter::GetAcceptedTransferSyntaxes()
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 25 Jan 2021 15:18:34 +0100
parents f4dbdb2dcba6
children 68f52897c119
comparison
equal deleted inserted replaced
4467:c92ec129698a 4468:9c070a34de18
407 } 407 }
408 408
409 409
410 { 410 {
411 /* accept the abstract syntaxes for C-STORE, if presented */ 411 /* accept the abstract syntaxes for C-STORE, if presented */
412 412
413 std::vector<const char*> storageTransferSyntaxes; 413 std::set<DicomTransferSyntax> storageTransferSyntaxes;
414 414
415 // This is the list of the transfer syntaxes that were supported up to Orthanc 0.7.1 415 if (server.HasApplicationEntityFilter())
416 storageTransferSyntaxes.push_back(UID_LittleEndianExplicitTransferSyntax); 416 {
417 storageTransferSyntaxes.push_back(UID_BigEndianExplicitTransferSyntax); 417 server.GetApplicationEntityFilter().GetAcceptedTransferSyntaxes(
418 storageTransferSyntaxes.push_back(UID_LittleEndianImplicitTransferSyntax); 418 storageTransferSyntaxes, remoteIp, remoteAet, calledAet);
419 419 }
420 // New transfer syntaxes supported since Orthanc 0.7.2 420 else
421 if (!server.HasApplicationEntityFilter() || 421 {
422 server.GetApplicationEntityFilter().IsAllowedTransferSyntax(remoteIp, remoteAet, calledAet, TransferSyntax_Deflated)) 422 /**
423 { 423 * In the absence of filter, accept all the known transfer
424 storageTransferSyntaxes.push_back(UID_DeflatedExplicitVRLittleEndianTransferSyntax); 424 * syntaxes. Note that this is different from Orthanc
425 } 425 * framework <= 1.8.2, where only the uncompressed transfer
426 426 * syntaxes are accepted by default.
427 if (!server.HasApplicationEntityFilter() || 427 **/
428 server.GetApplicationEntityFilter().IsAllowedTransferSyntax(remoteIp, remoteAet, calledAet, TransferSyntax_Jpeg)) 428 GetAllTransferSyntaxes(storageTransferSyntaxes);
429 { 429 }
430 storageTransferSyntaxes.push_back(UID_JPEGProcess1TransferSyntax); 430
431 storageTransferSyntaxes.push_back(UID_JPEGProcess2_4TransferSyntax); 431 if (storageTransferSyntaxes.empty())
432 storageTransferSyntaxes.push_back(UID_JPEGProcess3_5TransferSyntax); 432 {
433 storageTransferSyntaxes.push_back(UID_JPEGProcess6_8TransferSyntax); 433 LOG(WARNING) << "The DICOM server accepts no transfer syntax, thus C-STORE SCP is disabled";
434 storageTransferSyntaxes.push_back(UID_JPEGProcess7_9TransferSyntax); 434 }
435 storageTransferSyntaxes.push_back(UID_JPEGProcess10_12TransferSyntax); 435 else
436 storageTransferSyntaxes.push_back(UID_JPEGProcess11_13TransferSyntax); 436 {
437 storageTransferSyntaxes.push_back(UID_JPEGProcess14TransferSyntax); 437 /**
438 storageTransferSyntaxes.push_back(UID_JPEGProcess15TransferSyntax); 438 * If accepted, put "Little Endian Explicit" at the first
439 storageTransferSyntaxes.push_back(UID_JPEGProcess16_18TransferSyntax); 439 * place in the accepted transfer syntaxes. This first place
440 storageTransferSyntaxes.push_back(UID_JPEGProcess17_19TransferSyntax); 440 * has an impact on the result of "getscu" (cf. integration
441 storageTransferSyntaxes.push_back(UID_JPEGProcess20_22TransferSyntax); 441 * test "test_getscu"). We choose "Little Endian Explicit",
442 storageTransferSyntaxes.push_back(UID_JPEGProcess21_23TransferSyntax); 442 * as this preserves the VR of the private tags, even if the
443 storageTransferSyntaxes.push_back(UID_JPEGProcess24_26TransferSyntax); 443 * remote modality doesn't have the dictionary of private tags.
444 storageTransferSyntaxes.push_back(UID_JPEGProcess25_27TransferSyntax); 444 *
445 storageTransferSyntaxes.push_back(UID_JPEGProcess28TransferSyntax); 445 * TODO - Should "PREFERRED_TRANSFER_SYNTAX" be an option of
446 storageTransferSyntaxes.push_back(UID_JPEGProcess29TransferSyntax); 446 * class "DicomServer"?
447 storageTransferSyntaxes.push_back(UID_JPEGProcess14SV1TransferSyntax); 447 **/
448 } 448 const DicomTransferSyntax PREFERRED_TRANSFER_SYNTAX = DicomTransferSyntax_LittleEndianExplicit;
449 449
450 if (!server.HasApplicationEntityFilter() || 450 std::vector<const char*> storageTransferSyntaxesC;
451 server.GetApplicationEntityFilter().IsAllowedTransferSyntax(remoteIp, remoteAet, calledAet, TransferSyntax_Jpeg2000)) 451 storageTransferSyntaxesC.reserve(storageTransferSyntaxes.size());
452 { 452
453 storageTransferSyntaxes.push_back(UID_JPEG2000LosslessOnlyTransferSyntax); 453 if (storageTransferSyntaxes.find(PREFERRED_TRANSFER_SYNTAX) != storageTransferSyntaxes.end())
454 storageTransferSyntaxes.push_back(UID_JPEG2000TransferSyntax); 454 {
455 storageTransferSyntaxes.push_back(UID_JPEG2000LosslessOnlyTransferSyntax); 455 storageTransferSyntaxesC.push_back(GetTransferSyntaxUid(PREFERRED_TRANSFER_SYNTAX));
456 storageTransferSyntaxes.push_back(UID_JPEG2000TransferSyntax); 456 }
457 storageTransferSyntaxes.push_back(UID_JPEG2000Part2MulticomponentImageCompressionLosslessOnlyTransferSyntax); 457
458 storageTransferSyntaxes.push_back(UID_JPEG2000Part2MulticomponentImageCompressionTransferSyntax); 458 for (std::set<DicomTransferSyntax>::const_iterator
459 } 459 it = storageTransferSyntaxes.begin(); it != storageTransferSyntaxes.end(); ++it)
460 460 {
461 if (!server.HasApplicationEntityFilter() || 461 if (*it != PREFERRED_TRANSFER_SYNTAX) // Don't add the preferred transfer syntax twice
462 server.GetApplicationEntityFilter().IsAllowedTransferSyntax(remoteIp, remoteAet, calledAet, TransferSyntax_JpegLossless)) 462 {
463 { 463 storageTransferSyntaxesC.push_back(GetTransferSyntaxUid(*it));
464 storageTransferSyntaxes.push_back(UID_JPEGLSLosslessTransferSyntax); 464 }
465 storageTransferSyntaxes.push_back(UID_JPEGLSLossyTransferSyntax); 465 }
466 } 466
467 467 /* the array of Storage SOP Class UIDs that is defined within "dcmdata/libsrc/dcuid.cc" */
468 if (!server.HasApplicationEntityFilter() || 468 size_t count = 0;
469 server.GetApplicationEntityFilter().IsAllowedTransferSyntax(remoteIp, remoteAet, calledAet, TransferSyntax_Jpip)) 469 while (dcmAllStorageSOPClassUIDs[count] != NULL)
470 { 470 {
471 storageTransferSyntaxes.push_back(UID_JPIPReferencedTransferSyntax); 471 count++;
472 storageTransferSyntaxes.push_back(UID_JPIPReferencedDeflateTransferSyntax); 472 }
473 }
474
475 if (!server.HasApplicationEntityFilter() ||
476 server.GetApplicationEntityFilter().IsAllowedTransferSyntax(remoteIp, remoteAet, calledAet, TransferSyntax_Mpeg2))
477 {
478 storageTransferSyntaxes.push_back(UID_MPEG2MainProfileAtMainLevelTransferSyntax);
479 storageTransferSyntaxes.push_back(UID_MPEG2MainProfileAtHighLevelTransferSyntax);
480 }
481
482 #if DCMTK_VERSION_NUMBER >= 361
483 // New in Orthanc 1.6.0
484 if (!server.HasApplicationEntityFilter() ||
485 server.GetApplicationEntityFilter().IsAllowedTransferSyntax(remoteIp, remoteAet, calledAet, TransferSyntax_Mpeg4))
486 {
487 storageTransferSyntaxes.push_back(UID_MPEG4BDcompatibleHighProfileLevel4_1TransferSyntax);
488 storageTransferSyntaxes.push_back(UID_MPEG4HighProfileLevel4_1TransferSyntax);
489 storageTransferSyntaxes.push_back(UID_MPEG4HighProfileLevel4_2_For2DVideoTransferSyntax);
490 storageTransferSyntaxes.push_back(UID_MPEG4HighProfileLevel4_2_For3DVideoTransferSyntax);
491 storageTransferSyntaxes.push_back(UID_MPEG4StereoHighProfileLevel4_2TransferSyntax);
492 }
493 #endif
494
495 if (!server.HasApplicationEntityFilter() ||
496 server.GetApplicationEntityFilter().IsAllowedTransferSyntax(remoteIp, remoteAet, calledAet, TransferSyntax_Rle))
497 {
498 storageTransferSyntaxes.push_back(UID_RLELosslessTransferSyntax);
499 }
500
501 /* the array of Storage SOP Class UIDs that is defined within "dcmdata/libsrc/dcuid.cc" */
502 size_t count = 0;
503 while (dcmAllStorageSOPClassUIDs[count] != NULL)
504 {
505 count++;
506 }
507 473
508 #if DCMTK_VERSION_NUMBER >= 362 474 #if DCMTK_VERSION_NUMBER >= 362
509 // The global variable "numberOfDcmAllStorageSOPClassUIDs" is 475 // The global variable "numberOfDcmAllStorageSOPClassUIDs" is
510 // only published if DCMTK >= 3.6.2: 476 // only published if DCMTK >= 3.6.2:
511 // https://bitbucket.org/sjodogne/orthanc/issues/137 477 // https://bitbucket.org/sjodogne/orthanc/issues/137
512 assert(static_cast<int>(count) == numberOfDcmAllStorageSOPClassUIDs); 478 assert(static_cast<int>(count) == numberOfDcmAllStorageSOPClassUIDs);
513 #endif 479 #endif
514 480
515 if (!server.HasGetRequestHandlerFactory()) // dcmqrsrv.cc line 828 481 if (!server.HasGetRequestHandlerFactory()) // dcmqrsrv.cc line 828
516 {
517 // This branch exactly corresponds to Orthanc <= 1.6.1 (in
518 // which C-GET SCP was not supported)
519 cond = ASC_acceptContextsWithPreferredTransferSyntaxes(
520 assoc->params, dcmAllStorageSOPClassUIDs, count,
521 &storageTransferSyntaxes[0], storageTransferSyntaxes.size());
522 if (cond.bad())
523 { 482 {
524 CLOG(INFO, DICOM) << cond.text(); 483 // This branch exactly corresponds to Orthanc <= 1.6.1 (in
525 AssociationCleanup(assoc); 484 // which C-GET SCP was not supported)
526 return NULL; 485 cond = ASC_acceptContextsWithPreferredTransferSyntaxes(
486 assoc->params, dcmAllStorageSOPClassUIDs, count,
487 &storageTransferSyntaxesC[0], storageTransferSyntaxesC.size());
488 if (cond.bad())
489 {
490 CLOG(INFO, DICOM) << cond.text();
491 AssociationCleanup(assoc);
492 return NULL;
493 }
527 } 494 }
528 } 495 else // see dcmqrsrv.cc lines 839 - 876
529 else // see dcmqrsrv.cc lines 839 - 876
530 {
531 /* accept storage syntaxes with proposed role */
532 int npc = ASC_countPresentationContexts(assoc->params);
533 for (int i = 0; i < npc; i++)
534 { 496 {
535 T_ASC_PresentationContext pc; 497 /* accept storage syntaxes with proposed role */
536 ASC_getPresentationContext(assoc->params, i, &pc); 498 int npc = ASC_countPresentationContexts(assoc->params);
537 if (dcmIsaStorageSOPClassUID(pc.abstractSyntax)) 499 for (int i = 0; i < npc; i++)
538 { 500 {
539 /** 501 T_ASC_PresentationContext pc;
540 * We are prepared to accept whatever role the caller 502 ASC_getPresentationContext(assoc->params, i, &pc);
541 * proposes. Normally we can be the SCP of the Storage 503 if (dcmIsaStorageSOPClassUID(pc.abstractSyntax))
542 * Service Class. When processing the C-GET operation 504 {
543 * we can be the SCU of the Storage Service Class. 505 /**
544 **/ 506 * We are prepared to accept whatever role the caller
545 const T_ASC_SC_ROLE role = pc.proposedRole; 507 * proposes. Normally we can be the SCP of the Storage
508 * Service Class. When processing the C-GET operation
509 * we can be the SCU of the Storage Service Class.
510 **/
511 const T_ASC_SC_ROLE role = pc.proposedRole;
546 512
547 /** 513 /**
548 * Accept in the order "least wanted" to "most wanted" 514 * Accept in the order "least wanted" to "most wanted"
549 * transfer syntax. Accepting a transfer syntax will 515 * transfer syntax. Accepting a transfer syntax will
550 * override previously accepted transfer syntaxes. 516 * override previously accepted transfer syntaxes.
551 **/ 517 **/
552 for (int k = static_cast<int>(storageTransferSyntaxes.size()) - 1; k >= 0; k--) 518 for (int k = static_cast<int>(storageTransferSyntaxesC.size()) - 1; k >= 0; k--)
553 {
554 for (int j = 0; j < static_cast<int>(pc.transferSyntaxCount); j++)
555 { 519 {
556 /** 520 for (int j = 0; j < static_cast<int>(pc.transferSyntaxCount); j++)
557 * If the transfer syntax was proposed then we can accept it
558 * appears in our supported list of transfer syntaxes
559 **/
560 if (strcmp(pc.proposedTransferSyntaxes[j], storageTransferSyntaxes[k]) == 0)
561 { 521 {
562 cond = ASC_acceptPresentationContext( 522 /**
563 assoc->params, pc.presentationContextID, storageTransferSyntaxes[k], role); 523 * If the transfer syntax was proposed then we can accept it
564 if (cond.bad()) 524 * appears in our supported list of transfer syntaxes
525 **/
526 if (strcmp(pc.proposedTransferSyntaxes[j], storageTransferSyntaxesC[k]) == 0)
565 { 527 {
566 CLOG(INFO, DICOM) << cond.text(); 528 cond = ASC_acceptPresentationContext(
567 AssociationCleanup(assoc); 529 assoc->params, pc.presentationContextID, storageTransferSyntaxesC[k], role);
568 return NULL; 530 if (cond.bad())
531 {
532 CLOG(INFO, DICOM) << cond.text();
533 AssociationCleanup(assoc);
534 return NULL;
535 }
569 } 536 }
570 } 537 }
571 } 538 }
572 } 539 }
540 } /* for */
541 }
542
543 if (!server.HasApplicationEntityFilter() ||
544 server.GetApplicationEntityFilter().IsUnknownSopClassAccepted(remoteIp, remoteAet, calledAet))
545 {
546 /*
547 * Promiscous mode is enabled: Accept everything not known not
548 * to be a storage SOP class.
549 **/
550 cond = acceptUnknownContextsWithPreferredTransferSyntaxes(
551 assoc->params, &storageTransferSyntaxesC[0], storageTransferSyntaxesC.size(), ASC_SC_ROLE_DEFAULT);
552 if (cond.bad())
553 {
554 CLOG(INFO, DICOM) << cond.text();
555 AssociationCleanup(assoc);
556 return NULL;
573 } 557 }
574 } /* for */
575 }
576
577 if (!server.HasApplicationEntityFilter() ||
578 server.GetApplicationEntityFilter().IsUnknownSopClassAccepted(remoteIp, remoteAet, calledAet))
579 {
580 /*
581 * Promiscous mode is enabled: Accept everything not known not
582 * to be a storage SOP class.
583 **/
584 cond = acceptUnknownContextsWithPreferredTransferSyntaxes(
585 assoc->params, &storageTransferSyntaxes[0], storageTransferSyntaxes.size(), ASC_SC_ROLE_DEFAULT);
586 if (cond.bad())
587 {
588 CLOG(INFO, DICOM) << cond.text();
589 AssociationCleanup(assoc);
590 return NULL;
591 } 558 }
592 } 559 }
593 } 560 }
594 561
595 /* set our app title */ 562 /* set our app title */