Exemple #1
0
    def unbind(self, event, handler):
        """Unbind a callable `handler` from an `event`.

        Parameters
        ----------
        event : 3-tuple
            The event to unbind the function from.
        handler : callable
            The function that will no longer be called if the event occurs.
        """
        if event not in self._handlers:
            return

        # Make sure no access to `_handlers` while its being changed
        with threading.Lock():
            # Notification events
            if event.is_notification and handler in self._handlers[event]:
                self._handlers[event].remove(handler)

                if not self._handlers[event]:
                    del self._handlers[event]

            # Intervention events - unbind and replace with default
            if event.is_intervention and self._handlers[event] == handler:
                self._handlers[event] = evt.get_default_handler(event)

            # Unbind from our child Association events
            for assoc in self.active_associations:
                assoc.unbind(event, handler)
Exemple #2
0
    def unbind(self, event, handler):
        """Unbind a callable `handler` from an `event`.

        .. versionadded:: 1.3

        Parameters
        ----------
        event : 3-tuple
            The event to unbind the function from.
        handler : callable
            The function that will no longer be called if the event occurs.
        """
        if event not in self._handlers:
            return

        # Notification events
        if event.is_notification:
            handlers = [hh[0] for hh in self._handlers[event]]
            try:
                ii = handlers.index(handler)
                del self._handlers[event][ii]
            except ValueError:
                pass

            if not self._handlers[event]:
                del self._handlers[event]

        # Intervention events - unbind and replace with default
        if event.is_intervention and handler in self._handlers[event]:
            self._handlers[event] = (evt.get_default_handler(event), None)

        # Unbind from our child Association events
        for assoc in self.active_associations:
            assoc.unbind(event, handler)
Exemple #3
0
    def _check_sop_class_extended(self):
        """Check the user's response to a SOP Class Extended request.

        Returns
        -------
        list of pdu_primitives.SOPClassExtendedNegotiation
            The SOP Class Extended Negotiation items to be sent in response
        """
        # pylint: disable=broad-except
        try:
            default = evt.get_default_handler(evt.EVT_SOP_EXTENDED)
            if self.assoc.get_handlers(evt.EVT_SOP_EXTENDED) != default:
                user_response = evt.trigger(
                    self.assoc,
                    evt.EVT_SOP_EXTENDED,
                    {'app_info' : self.assoc.requestor.sop_class_extended}
                )
            else:
                user_response = self.assoc.ae.on_sop_class_extended(
                    self.assoc.requestor.sop_class_extended
                )
        except Exception as exc:
            user_response = {}
            LOGGER.error(
                "Exception raised in user's 'on_sop_class_extended' "
                "implementation"
            )
            LOGGER.exception(exc)

        if not isinstance(user_response, (type(None), dict)):
            LOGGER.error(
                "Invalid type returned by user's 'on_sop_class_extended' "
                "implementation"
            )
            user_response = {}

        if not user_response:
            return []

        items = []
        for sop_class, app_info in user_response.items():
            try:
                item = SOPClassExtendedNegotiation()
                item.sop_class_uid = sop_class
                item.service_class_application_information = app_info
                items.append(item)
            except Exception as exc:
                LOGGER.error(
                    "Unable to set the SOP Class Extended Negotiation "
                    "response values for the SOP Class UID {}"
                    .format(sop_class)
                )
                LOGGER.exception(exc)

        return items
Exemple #4
0
    def _bind_defaults(self) -> None:
        """Bind the default event handlers."""
        # Intervention event handlers
        for event in evt._INTERVENTION_EVENTS:
            handler = evt.get_default_handler(event)
            self.bind(event, handler)

        # Notification event handlers
        if _config.LOG_HANDLER_LEVEL == "standard":
            self.bind(evt.EVT_DIMSE_RECV, standard_dimse_recv_handler)
            self.bind(evt.EVT_DIMSE_SENT, standard_dimse_sent_handler)
            self.bind(evt.EVT_PDU_RECV, standard_pdu_recv_handler)
            self.bind(evt.EVT_PDU_SENT, standard_pdu_sent_handler)
Exemple #5
0
    def _check_sop_class_common_extended(self):
        """Check the user's response to a SOP Class Common Extended request.

        Returns
        -------
        dict
            The {SOP Class UID : SOPClassCommonExtendedNegotiation} items for
            the accepted SOP Class Common Extended negotiation items.
        """
        # pylint: disable=broad-except
        try:
            default = evt.get_default_handler(evt.EVT_SOP_COMMON)
            if self.assoc.get_handlers(evt.EVT_SOP_COMMON) != default:
                rsp = evt.trigger(
                    self.assoc,
                    evt.EVT_SOP_COMMON,
                    {'items' : self.assoc.requestor.sop_class_common_extended}
                )
            else:
                rsp = self.assoc.ae.on_sop_class_common_extended(
                    self.assoc.requestor.sop_class_common_extended
                )
        except Exception as exc:
            LOGGER.error(
                "Exception raised in user's 'on_sop_class_common_extended' "
                "implementation"
            )
            LOGGER.exception(exc)
            return {}

        try:
            rsp = {
                uid:ii for uid, ii in rsp.items()
                if isinstance(ii, SOPClassCommonExtendedNegotiation)
            }
        except Exception as exc:
            LOGGER.error(
                "Invalid type returned by user's 'evt.EVT_SOP_COMMON' handler"
            )
            LOGGER.exception(exc)
            return {}

        return rsp
Exemple #6
0
    def _check_async_ops(self):
        """Check the user's response to an Asynchronous Operations request.

        Returns
        -------
        pdu_primitives.AsynchronousOperationsWindowNegotiation or None
            If the `AE.on_async_ops_window` callback hasn't been implemented
            then returns None, otherwise returns an
            AsynchronousOperationsWindowNegotiation item with the default
            values for the number of operations invoked/performed (1, 1).
        """
        # pylint: disable=broad-except
        try:
            # TODO: refactor in v1.4
            # Response is always ignored as async ops is not supported
            default = evt.get_default_handler(evt.EVT_ASYNC_OPS)
            if self.assoc.get_handlers(evt.EVT_ASYNC_OPS) != default:
                inv, perf = self.assoc.requestor.asynchronous_operations
                _ = evt.trigger(
                    self.assoc,
                    evt.EVT_ASYNC_OPS,
                    {'nr_invoked' : inv, 'nr_performed' : perf}
                )
            else:
                _ = self.assoc.ae.on_async_ops_window(
                    *self.assoc.requestor.asynchronous_operations
                )
        except NotImplementedError:
            return None
        except Exception as exc:
            LOGGER.error(
                "Exception raised in user's 'on_async_ops_window' "
                "implementation"
            )
            LOGGER.exception(exc)

        item = AsynchronousOperationsWindowNegotiation()
        item.maximum_number_operations_invoked = 1
        item.maximum_number_operations_performed = 1

        return item
Exemple #7
0
    def SCP(self, req, context, info):
        """The implementation for the DIMSE N-GET service.

        Parameters
        ----------
        req : dimse_primitives.C_ECHO
            The N-GET request primitive sent by the peer.
        context : presentation.PresentationContext
            The presentation context that the service is operating under.
        info : dict
            A dict containing details about the association.

        See Also
        --------
        ae.ApplicationEntity.on_n_get
        association.Association.send_n_get

        Notes
        -----
        **N-GET Request**

        *Parameters*

        | (M) Message ID
        | (M) Requested SOP Class UID
        | (M) Requested SOP Instance UID
        | (U) Attribute Identifier List

        *Attribute Identifier List*

        An element with VR AT, VM 1-n, containing an attribute tag for each
        of the attributes applicable to the N-GET operation.

        **N-GET Response**

        *Parameters*

        | (M) Message ID Being Responded To
        | (U) Affected SOP Class UID
        | (U) Affected SOP Instance UID
        | (C) Attribute List
        | (M) Status

        *Attribute List*

        A dataset containing the values of the requested attributes.

        References
        ----------

        * DICOM Standard, Part 4, `Annex EE <http://dicom.nema.org/medical/dicom/current/output/html/part04.html#chapter_EE>`_
        * DICOM Standard, Part 7, Sections
          `10.1.2 <http://dicom.nema.org/medical/dicom/current/output/html/part07.html#sect_10.1.2>`_,
          `10.3.2 <http://dicom.nema.org/medical/dicom/current/output/html/part07.html#sect_10.3.2>`_
          and `Annex C <http://dicom.nema.org/medical/dicom/current/output/html/part07.html#chapter_C>`_
        """
        # Build N-GET response primitive
        rsp = N_GET()
        rsp.MessageIDBeingRespondedTo = req.MessageID
        rsp.AffectedSOPClassUID = req.RequestedSOPClassUID
        rsp.AffectedSOPInstanceUID = req.RequestedSOPInstanceUID

        default_handler = evt.get_default_handler(evt.EVT_N_GET)
        if self.assoc.get_handlers(evt.EVT_N_GET) != default_handler:
            try:
                (rsp_status, ds) = evt.trigger(self.assoc, evt.EVT_N_GET, {
                    'request': req,
                    'context': context.as_tuple,
                })
            except Exception as exc:
                LOGGER.error(
                    "Exception in the handler bound to 'evt.EVT_N_GET'")
                LOGGER.exception(exc)
                # Processing failure - Error in on_n_get callback
                rsp_status = 0x0110
        else:
            info['parameters'] = {
                'message_id': req.MessageID,
                'requested_sop_class_uid': req.RequestedSOPClassUID,
                'requested_sop_instance_uid': req.RequestedSOPInstanceUID,
            }

            # Attempt to run the ApplicationEntity's on_n_get callback
            # pylint: disable=broad-except
            try:
                # Send the value rather than the element
                (rsp_status,
                 ds) = self.ae.on_n_get(req.AttributeIdentifierList,
                                        context.as_tuple, info)
            except Exception as exc:
                LOGGER.error(
                    "Exception in the ApplicationEntity.on_n_get() callback")
                LOGGER.exception(exc)
                # Processing failure - Error in on_n_get callback
                rsp_status = 0x0110

        # Validate rsp_status and set rsp.Status accordingly
        rsp = self.validate_status(rsp_status, rsp)

        # Success or Warning, must return AttributeList dataset
        if code_to_category(rsp.Status) in [STATUS_SUCCESS, STATUS_WARNING]:
            # Encode the `dataset` using the agreed transfer syntax
            #   Will return None if failed to encode
            transfer_syntax = context.transfer_syntax[0]
            bytestream = encode(ds, transfer_syntax.is_implicit_VR,
                                transfer_syntax.is_little_endian)

            if bytestream is not None:
                rsp.AttributeList = BytesIO(bytestream)
            else:
                LOGGER.error("Failed to encode the supplied Dataset")
                # Processing failure - Failed to encode dataset
                rsp.Status = 0x0110

        self.dimse.send_msg(rsp, context.context_id)
Exemple #8
0
    def _check_user_identity(self):
        """Check the user's response to a User Identity request.

        Returns
        -------
        bool
            True if the user identity has been confirmed, False otherwise.
        pdu_primitives.UserIdentityNegotiation or None
            The negotiation response, if a positive response is requested,
            otherwise None.
        """
        # pylint: disable=broad-except
        # The UserIdentityNegotiation (request) item
        req = self.assoc.requestor.user_identity
        try:
            default = evt.get_default_handler(evt.EVT_USER_ID)
            if self.assoc.get_handlers(evt.EVT_USER_ID) != default:
                identity_verified, response = evt.trigger(
                    self.assoc,
                    evt.EVT_USER_ID,
                    {
                        'user_id_type' : req.user_identity_type,
                        'primary_field' : req.primary_field,
                        'secondary_field' : req.secondary_field,
                    }
                )
            else:
                identity_verified, response = self.assoc.ae.on_user_identity(
                    req.user_identity_type,
                    req.primary_field,
                    req.secondary_field,
                    {
                        'requestor' : self.assoc.requestor.info,
                    }
                )
        except NotImplementedError:
            # If the user hasn't implemented identity negotiation then
            #   default to accepting the association
            return True, None
        except Exception as exc:
            # If the user has implemented identity negotiation but an exception
            #   occurred then reject the association
            LOGGER.error("Exception in handling user identity negotiation")
            LOGGER.exception(exc)
            return False, None

        if not identity_verified:
            # Reject association as the user isn't authorised
            return False, None

        if req.user_identity_type in [3, 4, 5]:
            if req.positive_response_requested and response is not None:
                try:
                    rsp = UserIdentityNegotiation()
                    rsp.server_response = response
                    return True, rsp
                except Exception as exc:
                    # > If the acceptor doesn't support user identification it
                    # > will accept the association without making a positive
                    # > response
                    LOGGER.error(
                        "Unable to set the User Identity Negotiation's "
                        "'server_response'"
                    )
                    LOGGER.exception(exc)
                    return True, None

        return True, None