예제 #1
0
def get_message_handler(service, taxii_message):
    """
    Given a service and a TAXII Message, return the
    message handler class.
    """

    # Convenience aliases
    st = service.service_type
    mt = taxii_message.message_type

    handler = None
    try:
        if st == SVC_INBOX and mt == MSG_INBOX_MESSAGE:
            handler = service.inbox_message_handler
        elif st == SVC_POLL and mt == MSG_POLL_REQUEST:
            handler = service.poll_request_handler
        elif st == SVC_POLL and mt == MSG_POLL_FULFILLMENT_REQUEST:
            handler = service.poll_fulfillment_handler
        elif st == SVC_DISCOVERY and mt == MSG_DISCOVERY_REQUEST:
            handler = service.discovery_handler
        elif st == SVC_COLLECTION_MANAGEMENT and mt in (
                MSG_COLLECTION_INFORMATION_REQUEST,
                MSG_FEED_INFORMATION_REQUEST):
            handler = service.collection_information_handler
        elif st == SVC_COLLECTION_MANAGEMENT and mt in (
                MSG_MANAGE_COLLECTION_SUBSCRIPTION_REQUEST,
                MSG_MANAGE_FEED_SUBSCRIPTION_REQUEST):
            handler = service.subscription_management_handler
    except models.MessageHandler.DoesNotExist:
        raise StatusMessageException(
            taxii_message.message_id, ST_FAILURE,
            "The MessageHandler lookup failed for service at %s" %
            service.path)

    if not handler:
        raise StatusMessageException(
            taxii_message.message_id, ST_FAILURE,
            "Message Type: %s is not supported by %s" % (mt, st))

    module_name, class_name = handler.handler.rsplit('.', 1)

    try:
        module = import_module(module_name)
        handler_class = getattr(module, class_name)
    except Exception as e:
        type_, value, traceback = sys.exc_info()
        raise type_, ("Error importing handler: %s" % handler.handler, type,
                      value), traceback

    return handler_class
    def handle_message(poll_service, poll_fulfillment_request, django_request):
        """
        Looks in the database for a matching result set part and return it.

        Workflow:
            1. Look in models.ResultSetPart for a ResultSetPart that matches the criteria of the request
            2. Update the ResultSetPart's parent (models.ResultSet) to store which ResultSetPart was most recently returned
            3. Turn the ResultSetPart into a PollResponse, and return it
        """
        try:
            rsp = models.ResultSetPart.objects.get(
                result_set__pk=poll_fulfillment_request.result_id,
                part_number=poll_fulfillment_request.result_part_number,
                result_set__data_collection__name=poll_fulfillment_request.
                collection_name)

            poll_response = rsp.to_poll_response_11(
                poll_fulfillment_request.message_id)
            rsp.result_set.last_part_returned = rsp
            rsp.save()
            return poll_response
        except models.ResultSetPart.DoesNotExist:
            raise StatusMessageException(
                poll_fulfillment_request.message_id, ST_NOT_FOUND,
                {SD_ITEM: str(poll_fulfillment_request.result_id)})
예제 #3
0
    def filter_content(cls, prp, content_blocks):
        """
        Turns the prp.query into an XPath, runs the XPath against each
        item in `content_blocks`, and returns the items in `content_blocks`
        that match the XPath.

        :param prp: A PollRequestParameters object representing the Poll Request
        :param content_blocks: A list of models.ContentBlock objects to filter
        :return: A list of models.ContentBlock objects matching the query
        """
        if prp.query.targeting_expression_id not in cls.get_supported_tevs():
            raise StatusMessageException(
                prp.message_id,
                ST_UNSUPPORTED_TARGETING_EXPRESSION_ID,
                status_detail={
                    SD_TARGETING_EXPRESSION_ID: cls.get_supported_tevs()
                })

        result_list = []
        for content_block in content_blocks:
            etree_content = parse(content_block.content)
            if cls.evaluate_criteria(prp, etree_content, prp.query.criteria):
                result_list.append(content_block)

        return result_list
예제 #4
0
        def _wrapped_view(request, *args, **kwargs):

            if request.method != 'POST':
                raise StatusMessageException('0', ST_BAD_MESSAGE,
                                             'Request method was not POST!')

            # If requested, attempt to validate
            # TODO: Validation has changed!
            if do_validate:
                try:
                    validate(request)
                except Exception as e:
                    raise StatusMessageException('0', ST_BAD_MESSAGE,
                                                 e.message)

            # Attempt to deserialize
            try:
                message = deserialize(request)
            except Exception as e:
                raise StatusMessageException('0', ST_BAD_MESSAGE, e.message)

            try:
                if message_types is None:
                    pass  # No checking was requested
                elif isinstance(message_types, list):
                    if message.__class__ not in message_types:
                        raise ValueError(
                            'Message type not allowed. Must be one of: %s' %
                            message_types)
                elif isinstance(message_types, object):
                    if not isinstance(message, message_types):
                        raise ValueError(
                            'Message type not allowed. Must be: %s' %
                            message_types)
                elif message_types is not None:
                    raise ValueError(
                        'Something strange happened with message_types! Was not a list or object!'
                    )
            except ValueError as e:
                msg = tm11.StatusMessage(generate_message_id(),
                                         message.message_id,
                                         status_type='FAILURE',
                                         message=e.message)
                return HttpResponseTaxii(msg.to_xml(), response_headers)

            kwargs['taxii_message'] = message
            return view_func(request, *args, **kwargs)
예제 #5
0
 def handle_message(discovery_service, discovery_request, django_request):
     """
     Passes the message off to either DiscoveryRequest10Handler or DiscoveryRequest11Handler
     """
     if isinstance(discovery_request, tm10.DiscoveryRequest):
         return DiscoveryRequest10Handler.handle_message(
             discovery_service, discovery_request, django_request)
     elif isinstance(discovery_request, tm11.DiscoveryRequest):
         return DiscoveryRequest11Handler.handle_message(
             discovery_service, discovery_request, django_request)
     else:
         raise StatusMessageException(
             discovery_request.message_id, ST_FAILURE,
             "TAXII Message not supported by Message Handler.")
 def handle_message(inbox_service, inbox_message, django_request):
     """
     Passes the request to either InboxMessage10Handler or InboxMessage11Handler
     """
     if isinstance(inbox_message, tm10.InboxMessage):
         return InboxMessage10Handler.handle_message(
             inbox_service, inbox_message, django_request)
     elif isinstance(inbox_message, tm11.InboxMessage):
         return InboxMessage11Handler.handle_message(
             inbox_service, inbox_message, django_request)
     else:
         raise StatusMessageException(
             inbox_message.message_id, ST_FAILURE,
             "TAXII Message not supported by Message Handler.")
예제 #7
0
    def create_pending_response(cls, poll_service, prp, content):
        """
        Arguments:
            poll_service (models.PollService) - The TAXII Poll Service being invoked
            prp (util.PollRequestProperties) - The Poll Request Properties of the Poll Request
            content - A list of content (nominally, models.ContentBlock objects).

        This method returns a StatusMessage with a Status Type
        of Pending OR raises a StatusMessageException
        based on the following table::

            asynch | Delivery_Params | can_push || Response Type
            ----------------------------------------------------
            True   | -               | -        || Pending - Asynch
            False  | Yes             | Yes      || Pending - Push
            False  | Yes             | No       || StatusMessageException
            False  | No              | -        || StatusMessageException
        """

        # Identify the Exception conditions first (e.g., rows #3 and #4)
        if (prp.allow_asynch is False and
            (prp.delivery_parameters is None or prp.can_push is False)):

            raise StatusMessageException(
                prp.message_id, ST_FAILURE,
                "The content was not available now and \
                                         the request had allow_asynch=False and no \
                                         Delivery Parameters were specified.")

        # Rows #1 and #2 are both Status Messages with a type of Pending
        result_set = cls.create_result_set(content, prp, poll_service)
        sm = tm11.StatusMessage(message_id=generate_message_id(),
                                in_response_to=prp.message_id,
                                status_type=ST_PENDING)
        if prp.allow_asynch:
            sm.status_details = {
                SD_ESTIMATED_WAIT: 300,
                SD_RESULT_ID: result_set.pk,
                SD_WILL_PUSH: False
            }
        else:
            # TODO: Check and see if the requested delivery parameters are supported
            sm.status_details = {
                SD_ESTIMATED_WAIT: 300,
                SD_RESULT_ID: result_set.pk,
                SD_WILL_PUSH: True
            }
            # TODO: Need to try pushing or something.
        return sm
    def validate_message_is_supported(cls, taxii_message):
        """
        Checks whether the TAXII Message is supported by this Message Handler.

        Arguments:
            taxii_message - A libtaxii.messages_11 or libtaxii.messages_10 taxii message

        Returns:
            None if the message is supported, raises a StatusMessageException otherwise.
        """
        if taxii_message.__class__ not in cls.get_supported_request_messages():
            raise StatusMessageException(taxii_message.message_id,
                                         ST_FAILURE,
                                         "TAXII Message not supported by Message Handler.")
        return
예제 #9
0
 def handle_message(poll_service, poll_request, django_request):
     """
     Passes the request to either PollRequest10Handler or PollRequest11Handler
     """
     if isinstance(poll_request, tm10.PollRequest):
         return PollRequest10Handler.handle_message(poll_service,
                                                    poll_request,
                                                    django_request)
     elif isinstance(poll_request, tm11.PollRequest):
         return PollRequest11Handler.handle_message(poll_service,
                                                    poll_request,
                                                    django_request)
     else:
         raise StatusMessageException(
             poll_request.message_id, ST_FAILURE,
             "TAXII Message not supported by Message Handler.")
    def handle_message(collection_management_service,
                       manage_collection_subscription_request, django_request):
        """
        Passes the request to either SubscriptionRequest10Handler
        or SubscriptionRequest11Handler.
        """
        # aliases because names are long
        cms = collection_management_service
        mcsr = manage_collection_subscription_request
        dr = django_request

        if isinstance(mcsr, tm10.ManageFeedSubscriptionRequest):
            return SubscriptionRequest10Handler.handle_message(cms, mcsr, dr)
        elif isinstance(mcsr, tm11.ManageCollectionSubscriptionRequest):
            return SubscriptionRequest11Handler.handle_message(cms, mcsr, dr)
        else:
            raise StatusMessageException(
                mcsr.message_id, ST_FAILURE,
                "TAXII Message not supported by Message Handler.")
    def handle_message(cls, collection_management_service,
                       manage_collection_subscription_request, django_request):
        """
        Workflow:
        (Kinda big)
        #. Validate the Data Collection that the request identifies
        #. Validate a variety of aspects of the request message
        #. If there's a subscription_id in the request message, attempt to identify that \
           subscription in the database
        #. If Action == Subscribe, call `subscribe(service, data_collection, request)`
        #. If Action == Unsubscribe, call `unsubscribe(service, request, subscription)`
        #. If Action == Pause, call `pause(service, request, subscription)`
        #. If Action == Resume, call `resume(service, request, subscription)`
        #. If Action == Status and there is a `subscription_id, call single_status(service, request, subscription)`
        #. If Action == Status and there is not a `subscription_id, call `multi_status(service, request)`
        #. Return a CollectionManageSubscriptionResponse

        """
        # Create an alias because the name is long as fiddlesticks
        smr = manage_collection_subscription_request
        cms = collection_management_service

        # This code follows the guidance in the TAXII Services Spec 1.1 Section 4.4.6.
        # This code could probably be optimized, but it exists in part to be readable.

        # 1. This code doesn't do authentication, so this step is skipped

        # 2. If the Collection Name does not exist, respond with a Status Message
        data_collection = cms.validate_collection_name(smr.collection_name,
                                                       smr.message_id)

        # The following code executes this truth table:
        # |Action      | Subscription ID | Subscription ID   |
        # |            |   in message?   |   DB match?       |
        # |--------------------------------------------------|
        # |Subscribe   |   Prohibited    |       N/A         |
        # |Unsubscribe |    Required     |    Not Needed     |
        # |Pause       |    Required     |     Needed        |
        # |Resume      |    Required     |     Needed        |
        # |Status      |    Optional     | Yes, if specified |

        if smr.action not in ACT_TYPES_11:
            raise StatusMessageException(
                smr.message_id,
                ST_BAD_MESSAGE,
                message="The specified value of Action was invalid.")

        # "For messages where the Action field is UNSUBSCRIBE, PAUSE, or RESUME, [subscription id] MUST be present"
        if smr.action in (ACT_UNSUBSCRIBE, ACT_PAUSE,
                          ACT_RESUME) and not smr.subscription_id:
            raise StatusMessageException(
                smr.message_id,
                ST_BAD_MESSAGE,
                message="The %s action requires a subscription id." %
                smr.action)

        # Attempt to identify a subscription in the database
        subscription = None
        if smr.subscription_id:
            try:
                subscription = models.Subscription.objects.get(
                    subscription_id=smr.subscription_id)
            except models.Subscription.DoesNotExist:
                subscription = None  # This is OK for certain circumstances

        # If subscription is None for Unsubscribe, that's OK, but it's not OK for Pause/Resume
        if subscription is None and smr.action in (ACT_PAUSE, ACT_RESUME):
            raise StatusMessageException(
                smr.message_id,
                ST_NOT_FOUND,
                status_detail={SD_ITEM: smr.subscription_id})

        # Create a stub ManageCollectionSubscriptionResponse
        response = tm11.ManageCollectionSubscriptionResponse(
            message_id=generate_message_id(),
            in_response_to=smr.message_id,
            collection_name=data_collection.name)

        # This code can probably be optimized
        if smr.action == ACT_SUBSCRIBE:
            subs_instance = cls.subscribe(smr, data_collection)
            response.subscription_instances.append(subs_instance)
        elif smr.action == ACT_UNSUBSCRIBE:
            subs_instance = cls.unsubscribe(smr, subscription)
            response.subscription_instances.append(subs_instance)
        elif smr.action == ACT_PAUSE:
            subs_instance = cls.pause(smr, subscription)
            response.subscription_instances.append(subs_instance)
        elif smr.action == ACT_RESUME:
            subs_instance = cls.resume(smr, subscription)
            response.subscription_instances.append(subs_instance)
        elif smr.action == ACT_STATUS and subscription is not None:
            subs_instance = cls.single_status(smr, subscription)
            response.subscription_instances.append(subs_instance)
        elif smr.action == ACT_STATUS and subscription is None:
            subs_instances = cls.multi_status(smr)
            for subs_instance in subs_instances:
                response.subscription_instances.append(subs_instance)
        else:
            raise ValueError("Unknown Action!")
        # print response.to_xml(pretty_print=True)
        return response
예제 #12
0
    def from_poll_request_10(poll_service, poll_request):
        prp = PollRequestProperties()
        prp.poll_request = poll_request
        prp.message_id = poll_request.message_id
        prp.collection = poll_service.validate_collection_name(
            poll_request.feed_name, poll_request.message_id)

        if poll_request.subscription_id:
            try:
                s = models.Subscription.objects.get(
                    subscription_id=poll_request.subscription_id)
                prp.subscription = s
            except models.Subscription.DoesNotExist:
                raise StatusMessageException(
                    poll_request.message_id,
                    ST_NOT_FOUND,
                    status_detail={SD_ITEM: poll_request.subscription_id})
            prp.response_type = None
            prp.content_bindings = s.content_binding.all()
            prp.allow_asynch = None
            prp.delivery_parameters = s.delivery_parameters
        else:
            prp.response_type = None
            prp.content_bindings = prp.collection.get_binding_intersection_10(
                poll_request.content_bindings, prp.message_id)
            prp.delivery_parameters = None

        if prp.collection.type != CT_DATA_FEED:  # Only Data Feeds existed in TAXII 1.0
            raise StatusMessageException(
                poll_request.message_id, ST_NOT_FOUND,
                "The Named Data Collection is not a Data Feed, it is a Data Set. "
                "Only Data Feeds can be"
                "Polled in TAXII 1.0", {SD_ITEM: poll_request.feed_name})

        current_datetime = datetime.datetime.now(tzutc())
        # If the request specifies a timestamp label in an acceptable range, use it.
        # Otherwise, don't use a begin timestamp label
        if poll_request.exclusive_begin_timestamp_label:
            pr_ebtl = poll_request.exclusive_begin_timestamp_label
            if pr_ebtl < current_datetime:
                prp.exclusive_begin_timestamp_label = poll_request.exclusive_begin_timestamp_label

        # Use either the specified end timestamp label;
        # or the current time iff the specified end timestmap label is after the current time
        prp.inclusive_end_timestamp_label = current_datetime
        if poll_request.inclusive_end_timestamp_label:
            pr_ietl = poll_request.inclusive_end_timestamp_label
            if pr_ietl < current_datetime:
                prp.inclusive_end_timestamp_label = poll_request.inclusive_end_timestamp_label

        if ((prp.inclusive_end_timestamp_label is not None
             and prp.exclusive_begin_timestamp_label is not None)
                and prp.inclusive_end_timestamp_label <
                prp.exclusive_begin_timestamp_label):
            raise StatusMessageException(
                prp.message_id,
                ST_FAILURE,
                message="Invalid Timestamp Labels: End TS Label is earlier "
                "than Begin TS Label")

        return prp
예제 #13
0
    def from_poll_request_11(poll_service, poll_request):
        prp = PollRequestProperties()
        prp.poll_request = poll_request
        prp.message_id = poll_request.message_id
        prp.collection = poll_service.validate_collection_name(
            poll_request.collection_name, poll_request.message_id)

        if poll_request.subscription_id:
            try:
                s = models.Subscription.objects.get(
                    subscription_id=poll_request.subscription_id)
                prp.subscription = s
            except models.Subscription.DoesNotExist:
                raise StatusMessageException(
                    poll_request.message_id, ST_NOT_FOUND,
                    "The subscription was not found",
                    {SD_ITEM: poll_request.subscription_id})
            prp.response_type = s.response_type
            prp.content_bindings = s.content_binding.all()
            prp.allow_asynch = False
            prp.query = s.query
            if prp.query:
                prp.supported_query = poll_service.get_supported_query(
                    prp.query, prp.message_id)
            else:
                prp.supported_query = None
            prp.delivery_parameters = s.delivery_parameters
        else:
            pp = poll_request.poll_parameters
            prp.response_type = pp.response_type
            prp.content_bindings = prp.collection.get_binding_intersection_11(
                pp.content_bindings, prp.message_id)
            prp.allow_asynch = pp.allow_asynch
            prp.query = pp.query
            if prp.query:
                prp.supported_query = poll_service.get_supported_query(
                    prp.query, prp.message_id)
            else:
                prp.supported_query = None
            prp.delivery_parameters = pp.delivery_parameters

        if prp.collection.type == CT_DATA_FEED:  # Only data feeds care about timestamp labels
            current_datetime = datetime.datetime.now(tzutc())

            # If the request specifies a timestamp label in an acceptable range, use it.
            # Otherwise, don't use a begin timestamp label
            if poll_request.exclusive_begin_timestamp_label:
                pr_ebtl = poll_request.exclusive_begin_timestamp_label
                if pr_ebtl < current_datetime:
                    prp.exclusive_begin_timestamp_label = poll_request.exclusive_begin_timestamp_label

            # Use either the specified end timestamp label;
            # or the current time iff the specified end timestmap label is after the current time
            prp.inclusive_end_timestamp_label = current_datetime
            if poll_request.inclusive_end_timestamp_label:
                pr_ietl = poll_request.inclusive_end_timestamp_label
                if pr_ietl < current_datetime:
                    prp.inclusive_end_timestamp_label = poll_request.inclusive_end_timestamp_label

            if ((prp.inclusive_end_timestamp_label is not None
                 and prp.exclusive_begin_timestamp_label is not None)
                    and prp.inclusive_end_timestamp_label <
                    prp.exclusive_begin_timestamp_label):
                raise StatusMessageException(
                    prp.message_id,
                    ST_FAILURE,
                    message="Invalid Timestamp Labels: End TS Label is earlier "
                    "than Begin TS Label")

        return prp
예제 #14
0
def service_router(request, path, do_validate=True):
    """
    Takes in a request, path, and TAXII Message,
    and routes the taxii_message to the Service Handler.
    """

    if request.method != 'POST':
        raise StatusMessageException('0', ST_BAD_MESSAGE,
                                     'Request method was not POST!')

    xtct = request.META.get('HTTP_X_TAXII_CONTENT_TYPE', None)
    if not xtct:
        raise StatusMessageException(
            '0', ST_BAD_MESSAGE,
            'The X-TAXII-Content-Type Header was not present.')

    parse_tuple = xtct_map.get(xtct)
    if not parse_tuple:
        raise StatusMessageException(
            '0', ST_BAD_MESSAGE,
            'The X-TAXII-Content-Type Header is not supported.')

    if do_validate:
        msg = None  # None means no error, a non-None value means an error happened
        try:
            result = parse_tuple.validator.validate_string(request.body)
            if not result.valid:
                if settings.DEBUG is True:
                    msg = 'Request was not schema valid: %s' % [
                        err for err in result.error_log
                    ]
                else:
                    msg = PV_ERR
        except XMLSyntaxError as e:
            if settings.DEBUG is True:
                msg = 'Request was not well-formed XML: %s' % str(e)
            else:
                msg = PV_ERR

        if msg is not None:
            raise StatusMessageException('0', ST_BAD_MESSAGE, msg)

    try:
        taxii_message = parse_tuple.parser(request.body)
    except tm11.UnsupportedQueryException as e:
        # TODO: Is it possible to give the real message id?
        # TODO: Is it possible to indicate which query aspects are supported?
        # This might require a change in how libtaxii works
        raise StatusMessageException('0', ST_UNSUPPORTED_QUERY)

    service = handlers.get_service_from_path(request.path)
    handler = service.get_message_handler(taxii_message)
    module_name, class_name = handler.handler.rsplit('.', 1)

    try:
        module = import_module(module_name)
        handler_class = getattr(module, class_name)
    except Exception as e:
        type, value, tb = sys.exc_info()
        raise type, ("Error importing handler: %s" % handler.handler, type,
                     value), tb

    handler_class.validate_headers(request, taxii_message.message_id)
    handler_class.validate_message_is_supported(taxii_message)

    try:
        response_message = handler_class.handle_message(
            service, taxii_message, request)
    except StatusMessageException:
        raise  # The handler_class has intentionally raised this
    except Exception as e:  # Something else happened
        msg = "There was a failure while executing the message handler"
        if settings.DEBUG:  # Add the stacktrace
            msg += "\r\n" + traceback.format_exc()

        raise StatusMessageException(taxii_message.message_id, ST_FAILURE, msg)

    try:
        response_message.message_type
    except AttributeError as e:
        msg = "The message handler (%s) did not return a TAXII Message!" % handler_class
        if settings.DEBUG:
            msg += ("\r\n The returned value was: %s (class=%s)" %
                    (response_message, response_message.__class__.__name__))

        raise StatusMessageException(taxii_message.message_id, ST_FAILURE, msg)

    if response_message.__module__ == 'libtaxii.messages_11':
        vid = VID_TAXII_SERVICES_11
    elif response_message.__module__ == 'libtaxii.messages_10':
        vid = VID_TAXII_SERVICES_10
    else:
        raise ValueError("Unknown response message module")

    response_headers = handlers.get_headers(vid, request.is_secure())

    return handlers.HttpResponseTaxii(
        response_message.to_xml(pretty_print=True), response_headers)
예제 #15
0
    def validate_headers(cls, django_request, in_response_to='0'):
        """
        Validates the headers of a django request
        based on the properties of this MessageHandler.

        Specifically, the supported_request_messages property is used to
        infer which version(s) of TAXII this message handler supports and
        from there infer which headers are valid/invalid.

        Arguments:
            django_request - The Django request to validate
            in_response_to - If a StatusMessageException is raised as a result \
                of header validation (e.g., the headers are invalid), \
                in_response_to will be used as the in_response_to \
                field of the Status Message.

        Returns:
            None if all headers are valid. Raises a StatusMessageException otherwise.
        """

        # First, make sure required headers exist
        svcs = django_request.META.get('HTTP_X_TAXII_SERVICES', None)
        if not svcs:
            msg = "The X-TAXII-Services header was not specified"
            if settings.DEBUG:
                msg += "\r\nHeaders: %s " % str(django_request.META)
            raise StatusMessageException(in_response_to,
                                         ST_FAILURE,
                                         msg)

        ct = django_request.META.get('CONTENT_TYPE', None)
        if not ct:
            raise StatusMessageException(in_response_to,
                                         ST_FAILURE,
                                         "Content-Type header was not specified")

        xtct = django_request.META.get('HTTP_X_TAXII_CONTENT_TYPE', None)
        if not xtct:
            raise StatusMessageException(in_response_to,
                                         ST_FAILURE,
                                         "The X-TAXII-Content-Type header was not specified")

        xtp = django_request.META.get('HTTP_X_TAXII_PROTOCOL', None)
        if not xtp:
            raise StatusMessageException(in_response_to,
                                         ST_FAILURE,
                                         "The X-TAXII-Protocol header was not specified")

        # These headers are optional
        accept = django_request.META.get('HTTP_ACCEPT', None)
        xta = django_request.META.get('HTTP_X_TAXII_ACCEPT', None)
        # for k, v in django_request.META.iteritems():
        # print '%s: %s' % (k, v)

        # Identify which TAXII versions the message handler supports
        supports_taxii_11 = False
        supports_taxii_10 = False
        for message in cls.get_supported_request_messages():
            if message.__module__ == 'libtaxii.messages_11':
                supports_taxii_11 = True
            elif message.__module__ == 'libtaxii.messages_10':
                supports_taxii_10 = True
            else:
                raise ValueError(("The variable \'supported_request_messages\' "
                                 "contained a non-libtaxii message module: %s") %
                                 message.__module__)

        # Next, determine whether the MessageHandler supports the headers
        # Validate the X-TAXII-Services header
        if svcs not in (VID_TAXII_SERVICES_11, VID_TAXII_SERVICES_10):
            raise StatusMessageException(in_response_to,
                                         ST_FAILURE,
                                         "The value of X-TAXII-Services was not recognized.")

        if ((svcs == VID_TAXII_SERVICES_11 and not supports_taxii_11) or
            (svcs == VID_TAXII_SERVICES_10 and not supports_taxii_10)):
            raise StatusMessageException(in_response_to,
                                         ST_FAILURE,
                                         ("The specified value of X-TAXII-Services (%s) "
                                          "is not supported by this TAXII Service.") % svcs)

        # Validate the Content-Type header
        if ct.lower() != 'application/xml':
            raise StatusMessageException(in_response_to,
                                         ST_FAILURE,
                                         "The specified value of Content-Type is not supported.")

        # Validate the X-TAXII-Content-Type header
        if xtct not in (VID_TAXII_XML_11, VID_TAXII_XML_10):
            raise StatusMessageException(in_response_to,
                                         ST_FAILURE,
                                         "The value of X-TAXII-Content-Type was not recognized.")

        if ((xtct == VID_TAXII_XML_11 and not supports_taxii_11) or
            (xtct == VID_TAXII_XML_10 and not supports_taxii_10)):
            raise StatusMessageException(in_response_to,
                                         ST_FAILURE,
                                         "The specified value of X-TAXII-Content-Type is not supported")

        # Validate the X-TAXII-Protocol header
        # TODO: Look into the service properties instead of assuming both are supported
        if xtp not in (VID_TAXII_HTTP_10, VID_TAXII_HTTPS_10):
            raise StatusMessageException(in_response_to,
                                         ST_FAILURE,
                                         "The specified value of X-TAXII-Protocol is not supported")

        # Validate the accept header
        if accept and accept.lower() != 'application/xml':
            raise StatusMessageException(in_response_to,
                                         ST_FAILURE,
                                         "The specified value of Accept is not supported")

        # Validate the X-TAXII-Accept header
        # TODO: Accept more "complex" accept headers (e.g., ones that specify more
        # than one value)
        if xta not in (VID_TAXII_XML_11, VID_TAXII_XML_10, None):
            raise StatusMessageException(in_response_to,
                                         ST_FAILURE,
                                         "The specified value of X-TAXII-Accept is not recognized")

        if not xta:  # X-TAXII-Accept not specified, we can pick whatever we want
            xta = VID_TAXII_XML_11

        if ((xta == VID_TAXII_XML_11 and not supports_taxii_11) or
            (xta == VID_TAXII_XML_10 and not supports_taxii_10)):
            raise StatusMessageException(in_response_to,
                                         ST_FAILURE,
                                         "The specified value of X-TAXII-Accept is not supported")

        # Headers are valid
        return