1# Copyright (c) 2017, The MITRE Corporation 2# For license information, see the LICENSE.txt file 3 4""" 5Creating, handling, and parsing TAXII 1.1 messages. 6""" 7 8 9import collections 10import six 11try: 12 import simplejson as json 13except ImportError: 14 import json 15import os 16import warnings 17 18from lxml import etree 19 20from .common import (parse, parse_datetime_string, append_any_content_etree, TAXIIBase, 21 get_required, get_optional, get_optional_text, parse_xml_string, 22 stringify_content) 23from .validation import do_check, uri_regex, check_timestamp_label 24from .constants import * 25 26 27def validate_xml(xml_string): 28 """ 29 Note that this function has been deprecated. Please see 30 libtaxii.validators.SchemaValidator. 31 32 Validate XML with the TAXII XML Schema 1.1. 33 34 Args: 35 xml_string (str): The XML to validate. 36 37 Example: 38 .. code-block:: python 39 40 is_valid = tm11.validate_xml(message.to_xml()) 41 """ 42 43 warnings.warn('Call to deprecated function: libtaxii.messages_11.validate_xml()', 44 category=DeprecationWarning) 45 46 etree_xml = parse_xml_string(xml_string) 47 package_dir, package_filename = os.path.split(__file__) 48 schema_file = os.path.join(package_dir, "xsd", "TAXII_XMLMessageBinding_Schema_11.xsd") 49 taxii_schema_doc = parse(schema_file, allow_file=True) 50 xml_schema = etree.XMLSchema(taxii_schema_doc) 51 valid = xml_schema.validate(etree_xml) 52 # TODO: Additionally, validate the Query stuff 53 if not valid: 54 return xml_schema.error_log.last_error 55 return valid 56 57 58def get_message_from_xml(xml_string, encoding='utf_8'): 59 """Create a TAXIIMessage object from an XML string. 60 61 This function automatically detects which type of Message should be created 62 based on the XML. 63 64 Args: 65 xml_string (str): The XML to parse into a TAXII message. 66 encoding (str): The encoding of the string; defaults to UTF-8 67 68 Example: 69 .. code-block:: python 70 71 message_xml = message.to_xml() 72 new_message = tm11.get_message_from_xml(message_xml) 73 """ 74 if isinstance(xml_string, six.binary_type): 75 xml_string = xml_string.decode(encoding, 'replace') 76 etree_xml = parse_xml_string(xml_string) 77 qn = etree.QName(etree_xml) 78 if qn.namespace != ns_map['taxii_11']: 79 raise ValueError('Unsupported namespace: %s' % qn.namespace) 80 81 message_type = qn.localname 82 83 if message_type == MSG_DISCOVERY_REQUEST: 84 return DiscoveryRequest.from_etree(etree_xml) 85 if message_type == MSG_DISCOVERY_RESPONSE: 86 return DiscoveryResponse.from_etree(etree_xml) 87 if message_type == MSG_COLLECTION_INFORMATION_REQUEST: 88 return CollectionInformationRequest.from_etree(etree_xml) 89 if message_type == MSG_COLLECTION_INFORMATION_RESPONSE: 90 return CollectionInformationResponse.from_etree(etree_xml) 91 if message_type == MSG_POLL_REQUEST: 92 return PollRequest.from_etree(etree_xml) 93 if message_type == MSG_POLL_RESPONSE: 94 return PollResponse.from_etree(etree_xml) 95 if message_type == MSG_STATUS_MESSAGE: 96 return StatusMessage.from_etree(etree_xml) 97 if message_type == MSG_INBOX_MESSAGE: 98 return InboxMessage.from_etree(etree_xml) 99 if message_type == MSG_MANAGE_COLLECTION_SUBSCRIPTION_REQUEST: 100 return ManageCollectionSubscriptionRequest.from_etree(etree_xml) 101 if message_type == MSG_MANAGE_COLLECTION_SUBSCRIPTION_RESPONSE: 102 return ManageCollectionSubscriptionResponse.from_etree(etree_xml) 103 if message_type == MSG_POLL_FULFILLMENT_REQUEST: 104 return PollFulfillmentRequest.from_etree(etree_xml) 105 106 raise ValueError('Unknown message_type: %s' % message_type) 107 108 109def get_message_from_dict(d): 110 """Create a TAXIIMessage object from a dictonary. 111 112 This function automatically detects which type of Message should be created 113 based on the 'message_type' key in the dictionary. 114 115 Args: 116 d (dict): The dictionary to build the TAXII message from. 117 118 Example: 119 .. code-block:: python 120 121 message_dict = message.to_dict() 122 new_message = tm11.get_message_from_dict(message_dict) 123 """ 124 if 'message_type' not in d: 125 raise ValueError('message_type is a required field!') 126 127 message_type = d['message_type'] 128 if message_type == MSG_DISCOVERY_REQUEST: 129 return DiscoveryRequest.from_dict(d) 130 if message_type == MSG_DISCOVERY_RESPONSE: 131 return DiscoveryResponse.from_dict(d) 132 if message_type == MSG_COLLECTION_INFORMATION_REQUEST: 133 return CollectionInformationRequest.from_dict(d) 134 if message_type == MSG_COLLECTION_INFORMATION_RESPONSE: 135 return CollectionInformationResponse.from_dict(d) 136 if message_type == MSG_POLL_REQUEST: 137 return PollRequest.from_dict(d) 138 if message_type == MSG_POLL_RESPONSE: 139 return PollResponse.from_dict(d) 140 if message_type == MSG_STATUS_MESSAGE: 141 return StatusMessage.from_dict(d) 142 if message_type == MSG_INBOX_MESSAGE: 143 return InboxMessage.from_dict(d) 144 if message_type == MSG_MANAGE_COLLECTION_SUBSCRIPTION_REQUEST: 145 return ManageCollectionSubscriptionRequest.from_dict(d) 146 if message_type == MSG_MANAGE_COLLECTION_SUBSCRIPTION_RESPONSE: 147 return ManageCollectionSubscriptionResponse.from_dict(d) 148 if message_type == MSG_POLL_FULFILLMENT_REQUEST: 149 return PollFulfillmentRequest.from_dict(d) 150 151 raise ValueError('Unknown message_type: %s' % message_type) 152 153 154def get_message_from_json(json_string, encoding='utf_8'): 155 """Create a TAXIIMessage object from a JSON string. 156 157 This function automatically detects which type of Message should be created 158 based on the JSON. 159 160 Args: 161 json_string (str): The JSON to parse into a TAXII message. 162 """ 163 decoded_string = json_string.decode(encoding, 'replace') 164 return get_message_from_dict(json.loads(decoded_string)) 165 166 167def _sanitize_content_binding(binding): 168 """ 169 Takes in one of: 170 1. ContentBinding object 171 2. string 172 3. dict 173 and returns a ContentBinding object. 174 175 This supports function calls where a string or ContentBinding can be 176 used to specify a content binding. 177 """ 178 if isinstance(binding, ContentBinding): # It's already good to go 179 return binding 180 elif isinstance(binding, six.string_types): # Convert it to a ContentBinding 181 return ContentBinding.from_string(binding) 182 elif isinstance(binding, dict): # Convert it to a ContentBinding 183 return ContentBinding.from_dict(binding) 184 else: # Don't know what to do with it. 185 raise ValueError('Type cannot be converted to ContentBinding: %s' % binding.__class__.__name__) 186 187 188def _sanitize_content_bindings(binding_list): 189 bindings = [] 190 for item in binding_list: 191 bindings.append(_sanitize_content_binding(item)) 192 193 return bindings 194 195 196class UnsupportedQueryException(Exception): 197 198 def __init__(self, value): 199 self.value = value 200 201 def __str__(self): 202 return repr(self.value) 203 204 205# Start with the 'default' deserializer 206query_deserializers = {} 207 208 209def register_query_format(format_id, query, query_info, schema=None): 210 """ 211 This function registers a query format with libtaxii.messages_11. 212 Arguments: 213 format_id (string) - The format ID of the query 214 query (messages_11.Query subclass) - the Query object associated with the format_id 215 query_info (messages_11.SupportedQuery subclass) - the SupportedQuery object associated with the format_id 216 schema (xml schema) - The XML schema for validating the query 217 """ 218 query_deserializers[format_id] = {'query': query, 'query_info': query_info, 'schema': schema} 219 220 221def get_deserializer(format_id, type): 222 do_check(type, 'type', value_tuple=('query', 'query_info')) 223 224 if format_id not in query_deserializers: 225 raise UnsupportedQueryException('A deserializer for the query format \'%s\' is not registered.' % format_id) 226 227 return query_deserializers[format_id][type] 228 229# TODO: Consider using this 230# def _create_element(name, namespace=ns_map['taxii_11'], value=None, attrs=None, parent=None): 231 # """ 232 # Helper method for appending a new element to an existing element. 233 234 # Assumes the namespace is TAXII 1.1 235 236 # Arguments: 237 # name (string) - The name of the element 238 # namespace (string) - The namespace of the element 239 # value (string) - The text value of the element 240 # attrs (dict) - A dictionary of attributes 241 # parent (Element) - The parent element 242 # """ 243 # if value is None and attrs is None: 244 # return 245 246 # if parent is None: 247 # elt = etree.Element('{%s}%s' % (namespace, name), nsmap=ns_map) 248 # else: 249 # elt = etree.SubElement(parent, '{%s}%s' % (namespace, name), nsmap=ns_map) 250 251 # if value is not None: 252 # elt.text = value 253 254 # if attrs is not None: 255 # for k, v in attrs.items(): 256 # elt.attrib[k] = v 257 258 # return elt 259 260 261class TAXIIBase11(TAXIIBase): 262 version = VID_TAXII_XML_11 263 264 265class SupportedQuery(TAXIIBase11): 266 267 """ 268 This class contains an instance of a supported query. It 269 is expected that, generally, messages_11.SupportedQuery 270 subclasses will be used in place of this class 271 to represent a query 272 """ 273 274 def __init__(self, format_id): 275 """ 276 Arguments: 277 format_id (string) - The format_id of this supported query 278 """ 279 self.format_id = format_id 280 281 @property 282 def sort_key(self): 283 return self.format_id 284 285 @property 286 def format_id(self): 287 return self._format_id 288 289 @format_id.setter 290 def format_id(self, value): 291 do_check(value, 'format_id', regex_tuple=uri_regex) 292 self._format_id = value 293 294 def to_etree(self): 295 q = etree.Element('{%s}Supported_Query' % ns_map['taxii_11'], nsmap=ns_map) 296 q.attrib['format_id'] = self.format_id 297 return q 298 299 def to_dict(self): 300 return {'format_id': self.format_id} 301 302 def to_text(self, line_prepend=''): 303 s = line_prepend + "=== Supported Query Information ===\n" 304 s += line_prepend + " Query Format: %s\n" % self.format_id 305 return s 306 307 @staticmethod 308 def from_etree(etree_xml): 309 format_id = get_required(etree_xml, './@format_id', ns_map) 310 return SupportedQuery(format_id) 311 312 @staticmethod 313 def from_dict(d): 314 return SupportedQuery(**d) 315 316 317class Query(TAXIIBase11): 318 319 """This class contains an instance of a query. 320 321 It is expected that, generally, messages_11.Query subclasses will be used 322 in place of this class to represent a query. 323 """ 324 325 def __init__(self, format_id): 326 """ 327 Arguments: 328 format_id (string) - The format_id of this query 329 """ 330 self.format_id = format_id 331 332 @property 333 def format_id(self): 334 return self._format_id 335 336 @format_id.setter 337 def format_id(self, value): 338 do_check(value, 'format_id', regex_tuple=uri_regex) 339 self._format_id = value 340 341 def to_etree(self): 342 q = etree.Element('{%s}Query' % ns_map['taxii_11'], nsmap=ns_map) 343 q.attrib['format_id'] = self.format_id 344 return q 345 346 def to_dict(self): 347 return {'format_id': self.format_id} 348 349 def to_text(self, line_prepend=''): 350 s = line_prepend + "=== Query ===\n" 351 s += line_prepend + " Query Format: %s\n" % self.format_id 352 353 return s 354 355 @classmethod 356 def from_etree(cls, etree_xml, kwargs): 357 format_id = get_required(etree_xml, './@format_id', ns_map) 358 return cls(format_id, **kwargs) 359 360 @classmethod 361 def from_dict(cls, d, kwargs): 362 return cls(d, **kwargs) 363 364 365# A value can be one of: 366# - a dictionary, where each key is a content_binding_id and each value is a list of subtypes 367# (This is the default representation) 368# - a "content_binding_id[>subtype]" structure 369# - a list of "content_binding_id[>subtype]" structures 370 371 372class ContentBinding(TAXIIBase11): 373 374 """TAXII Content Binding component 375 376 Args: 377 binding_id (str): The content binding ID. **Required** 378 subtype_ids (list of str): the subtype IDs. **Required** 379 """ 380 381 def __init__(self, binding_id, subtype_ids=None): 382 self.binding_id = binding_id 383 self.subtype_ids = subtype_ids or [] 384 385 def __str__(self): 386 s = self.binding_id 387 if len(self.subtype_ids) > 0: 388 s += '>' + ','.join(self.subtype_ids) 389 390 return s 391 392 @staticmethod 393 def from_string(s): 394 if '>' not in s: 395 return ContentBinding(s) 396 397 parts = s.split('>') 398 binding_id = parts[0] 399 subtype_ids = parts[1].split(',') 400 return ContentBinding(binding_id, subtype_ids) 401 402 @property 403 def sort_key(self): 404 return str(self) 405 406 @property 407 def binding_id(self): 408 return self._binding_id 409 410 @binding_id.setter 411 def binding_id(self, value): 412 do_check(value, 'binding_id', regex_tuple=uri_regex) 413 self._binding_id = value 414 415 @property 416 def subtype_ids(self): 417 return self._subtype_ids 418 419 @subtype_ids.setter 420 def subtype_ids(self, value): 421 do_check(value, 'subtype_ids', regex_tuple=uri_regex) 422 self._subtype_ids = value 423 424 def to_etree(self): 425 cb = etree.Element('{%s}Content_Binding' % ns_map['taxii_11'], nsmap=ns_map) 426 cb.attrib['binding_id'] = self.binding_id 427 for subtype_id in self.subtype_ids: 428 s = etree.SubElement(cb, '{%s}Subtype' % ns_map['taxii_11'], nsmap=ns_map) 429 s.attrib['subtype_id'] = subtype_id 430 return cb 431 432 def to_dict(self): 433 return {'binding_id': self.binding_id, 'subtype_ids': self.subtype_ids} 434 435 def to_text(self, line_prepend=''): 436 return line_prepend + str(self) 437 438 def __hash__(self): 439 return hash(str(self.to_dict())) 440 441 @classmethod 442 def from_etree(self, etree_xml): 443 binding_id = etree_xml.attrib['binding_id'] 444 subtype_ids = [] 445 subtype_elts = etree_xml.xpath('./taxii_11:Subtype', namespaces=ns_map) 446 for elt in subtype_elts: 447 subtype_ids.append(elt.attrib['subtype_id']) 448 return ContentBinding(binding_id, subtype_ids) 449 450 @classmethod 451 def from_dict(self, d): 452 return ContentBinding(**d) 453 454 455class RecordCount(TAXIIBase11): 456 457 """ 458 Information summarizing the number of records. 459 460 Args: 461 record_count (int): The number of records 462 partial_count (bool): Whether the number of records is a partial count 463 """ 464 465 def __init__(self, record_count, partial_count=False): 466 self.record_count = record_count 467 self.partial_count = partial_count 468 469 @property 470 def record_count(self): 471 return self._record_count 472 473 @record_count.setter 474 def record_count(self, value): 475 do_check(value, 'record_count', type=int) 476 self._record_count = value 477 478 @property 479 def partial_count(self): 480 return self._partial_count 481 482 @partial_count.setter 483 def partial_count(self, value): 484 do_check(value, 'partial_count', value_tuple=(True, False), can_be_none=True) 485 self._partial_count = value 486 487 def to_etree(self): 488 xml = etree.Element('{%s}Record_Count' % ns_map['taxii_11'], nsmap=ns_map) 489 xml.text = str(self.record_count) 490 491 if self.partial_count is not None: 492 xml.attrib['partial_count'] = str(self.partial_count).lower() 493 494 return xml 495 496 def to_dict(self): 497 d = {} 498 d['record_count'] = self.record_count 499 if self.partial_count is not None: 500 d['partial_count'] = self.partial_count 501 502 return d 503 504 def to_text(self, line_prepend=''): 505 s = line_prepend + "=== Record Count ===\n" 506 s += line_prepend + " Record Count: %s\n" % self.record_count 507 if self.partial_count: 508 s += line_prepend + " Partial Count: %s\n" % self.partial_count 509 510 return s 511 512 @staticmethod 513 def from_etree(etree_xml): 514 record_count = int(etree_xml.text) 515 partial_count = etree_xml.attrib.get('partial_count', 'false') == 'true' 516 517 return RecordCount(record_count, partial_count) 518 519 @staticmethod 520 def from_dict(d): 521 return RecordCount(**d) 522 523 524class _GenericParameters(TAXIIBase11): 525 name = 'Generic_Parameters' 526 527 def __init__(self, response_type=RT_FULL, content_bindings=None, query=None): 528 self.response_type = response_type 529 self.content_bindings = content_bindings or [] 530 self.query = query 531 532 @property 533 def response_type(self): 534 return self._response_type 535 536 @response_type.setter 537 def response_type(self, value): 538 do_check(value, 'response_type', value_tuple=(RT_FULL, RT_COUNT_ONLY), can_be_none=True) 539 self._response_type = value 540 541 @property 542 def content_bindings(self): 543 return self._content_bindings 544 545 @content_bindings.setter 546 def content_bindings(self, value): 547 value = _sanitize_content_bindings(value) 548 do_check(value, 'content_bindings', type=ContentBinding) 549 self._content_bindings = value 550 551 @property 552 def query(self): 553 return self._query 554 555 @query.setter 556 def query(self, value): 557 # TODO: Can i do more validation? 558 do_check(value, 'query', type=Query, can_be_none=True) 559 self._query = value 560 561 def to_etree(self): 562 xml = etree.Element('{%s}%s' % (ns_map['taxii_11'], self.name), nsmap=ns_map) 563 if self.response_type is not None: 564 rt = etree.SubElement(xml, '{%s}Response_Type' % ns_map['taxii_11'], nsmap=ns_map) 565 rt.text = self.response_type 566 567 for binding in self.content_bindings: 568 xml.append(binding.to_etree()) 569 570 if self.query is not None: 571 xml.append(self.query.to_etree()) 572 573 return xml 574 575 def to_dict(self): 576 d = {} 577 if self.response_type is not None: 578 d['response_type'] = self.response_type 579 580 d['content_bindings'] = [] 581 for binding in self.content_bindings: 582 d['content_bindings'].append(binding.to_dict()) 583 584 d['query'] = None 585 if self.query is not None: 586 d['query'] = self.query.to_dict() 587 588 return d 589 590 def to_text(self, line_prepend=''): 591 s = line_prepend + "=== %s ===\n" % self.name 592 for binding in self.content_bindings: 593 s += " Content Binding: %s\n" % str(binding) 594 595 if self.query: 596 s += self.query.to_text(line_prepend + STD_INDENT) 597 598 if self.response_type: 599 s += line_prepend + " Response type: %s\n" % str(self.response_type) 600 601 return s 602 603 @classmethod 604 def from_etree(cls, etree_xml, **kwargs): 605 606 response_type = get_optional_text(etree_xml, './taxii_11:Response_Type', ns_map) 607 if response_type is None: 608 response_type = RT_FULL 609 610 content_bindings = [] 611 for binding in etree_xml.xpath('./taxii_11:Content_Binding', namespaces=ns_map): 612 content_bindings.append(ContentBinding.from_etree(binding)) 613 614 query = None 615 query_el = get_optional(etree_xml, './taxii_11:Query', ns_map) 616 if query_el is not None: 617 format_id = query_el.attrib['format_id'] 618 query = get_deserializer(format_id, 'query').from_etree(query_el) 619 620 return cls(response_type, content_bindings, query, **kwargs) 621 622 @classmethod 623 def from_dict(cls, d, **kwargs): 624 response_type = d.get('response_type', RT_FULL) 625 content_bindings = [] 626 for binding in d['content_bindings']: 627 content_bindings.append(ContentBinding.from_dict(binding)) 628 629 query = None 630 if 'query' in d and d['query'] is not None: 631 format_id = d['query']['format_id'] 632 query = get_deserializer(format_id, 'query').from_dict(d['query']) 633 634 return cls(response_type, content_bindings, query, **kwargs) 635 636 637class SubscriptionParameters(_GenericParameters): 638 639 """ 640 TAXII Subscription Parameters. 641 642 Args: 643 response_type (str): The requested response type. Must be either 644 :py:data:`RT_FULL` or :py:data:`RT_COUNT_ONLY`. **Optional**, 645 defaults to :py:data:`RT_FULL` 646 content_bindings (list of ContentBinding objects): A list of Content 647 Bindings acceptable in response. **Optional** 648 query (Query): The query for this poll parameters. **Optional** 649 """ 650 name = 'Subscription_Parameters' 651 652 653class ContentBlock(TAXIIBase11): 654 655 """A TAXII Content Block. 656 657 Args: 658 content_binding (ContentBinding): a Content Binding ID or nesting expression 659 indicating the type of content contained in the Content field of this 660 Content Block. **Required** 661 content (string or etree): a piece of content of the type specified 662 by the Content Binding. **Required** 663 timestamp_label (datetime): the Timestamp Label associated with this 664 Content Block. **Optional** 665 padding (string): an arbitrary amount of padding for this Content 666 Block. **Optional** 667 message (string): a message associated with this ContentBlock. **Optional** 668 """ 669 NAME = 'Content_Block' 670 671 def __init__(self, content_binding, content, timestamp_label=None, 672 padding=None, message=None): 673 self.content_binding = content_binding 674 self.content = content 675 self.timestamp_label = timestamp_label 676 self.message = message 677 self.padding = padding 678 679 @property 680 def sort_key(self): 681 return self.content[:25] 682 683 @property 684 def content_binding(self): 685 return self._content_binding 686 687 @content_binding.setter 688 def content_binding(self, value): 689 value = _sanitize_content_binding(value) 690 do_check(value, 'content_binding', type=ContentBinding) 691 self._content_binding = value 692 693 @property 694 def content(self): 695 if self.content_is_xml: 696 return etree.tostring(self._content, encoding='utf-8') 697 else: 698 return self._content 699 700 @content.setter 701 def content(self, value): 702 do_check(value, 'content') # Just check for not None 703 self._content, self.content_is_xml = stringify_content(value) 704 705 @property 706 def content_is_xml(self): 707 return self._content_is_xml 708 709 @content_is_xml.setter 710 def content_is_xml(self, value): 711 do_check(value, 'content_is_xml', value_tuple=(True, False)) 712 self._content_is_xml = value 713 714 @property 715 def timestamp_label(self): 716 return self._timestamp_label 717 718 @timestamp_label.setter 719 def timestamp_label(self, value): 720 value = check_timestamp_label(value, 'timestamp_label', can_be_none=True) 721 self._timestamp_label = value 722 723 @property 724 def message(self): 725 return self._message 726 727 @message.setter 728 def message(self, value): 729 do_check(value, 'message', type=six.string_types, can_be_none=True) 730 self._message = value 731 732 def to_etree(self): 733 block = etree.Element('{%s}Content_Block' % ns_map['taxii_11'], nsmap=ns_map) 734 block.append(self.content_binding.to_etree()) 735 c = etree.SubElement(block, '{%s}Content' % ns_map['taxii_11']) 736 737 if self.content_is_xml: 738 c.append(self._content) 739 else: 740 c.text = self._content 741 742 if self.timestamp_label is not None: 743 tl = etree.SubElement(block, '{%s}Timestamp_Label' % ns_map['taxii_11']) 744 tl.text = self.timestamp_label.isoformat() 745 746 if self.message is not None: 747 m = etree.SubElement(block, '{%s}Message' % ns_map['taxii_11']) 748 m.text = self.message 749 750 if self.padding is not None: 751 p = etree.SubElement(block, '{%s}Padding' % ns_map['taxii_11']) 752 p.text = self.padding 753 754 return block 755 756 def to_dict(self): 757 block = {} 758 block['content_binding'] = self.content_binding.to_dict() 759 760 if self.content_is_xml: 761 block['content'] = etree.tostring(self._content, encoding='utf-8') 762 else: 763 block['content'] = self._content 764 block['content_is_xml'] = self.content_is_xml 765 766 if self.timestamp_label is not None: 767 block['timestamp_label'] = self.timestamp_label.isoformat() 768 769 if self.message is not None: 770 block['message'] = self.message 771 772 if self.padding is not None: 773 block['padding'] = self.padding 774 775 return block 776 777 def to_text(self, line_prepend=''): 778 s = line_prepend + "=== Content Block ===\n" 779 s += line_prepend + " Content Binding: %s\n" % str(self.content_binding) 780 s += line_prepend + " Content length: %s\n" % len(self.content) 781 s += line_prepend + " (Content not printed for brevity)\n" 782 if self.timestamp_label: 783 s += line_prepend + " Timestamp Label: %s\n" % self.timestamp_label 784 s += line_prepend + " Message: %s\n" % self.message 785 s += line_prepend + " Padding: %s\n" % self.padding 786 return s 787 788 @staticmethod 789 def from_etree(etree_xml): 790 kwargs = {} 791 792 kwargs['content_binding'] = ContentBinding.from_etree( 793 get_required(etree_xml, './taxii_11:Content_Binding', ns_map)) 794 795 kwargs['padding'] = get_optional_text(etree_xml, './taxii_11:Padding', ns_map) 796 797 ts_text = get_optional_text(etree_xml, './taxii_11:Timestamp_Label', ns_map) 798 if ts_text: 799 kwargs['timestamp_label'] = parse_datetime_string(ts_text) 800 801 kwargs['message'] = get_optional_text(etree_xml, './taxii_11:Message', ns_map) 802 803 content = get_required(etree_xml, './taxii_11:Content', ns_map) 804 if len(content) == 0: # This has string content 805 kwargs['content'] = content.text 806 else: # This has XML content 807 kwargs['content'] = content[0] 808 809 return ContentBlock(**kwargs) 810 811 @staticmethod 812 def from_dict(d): 813 kwargs = {} 814 kwargs['content_binding'] = ContentBinding.from_dict(d['content_binding']) 815 kwargs['padding'] = d.get('padding') 816 if 'timestamp_label' in d: 817 kwargs['timestamp_label'] = parse_datetime_string(d['timestamp_label']) 818 kwargs['message'] = d.get('message') 819 is_xml = d.get('content_is_xml', False) 820 if is_xml: 821 kwargs['content'] = parse(d['content'], allow_file=False) 822 else: 823 kwargs['content'] = d['content'] 824 825 cb = ContentBlock(**kwargs) 826 return cb 827 828 @classmethod 829 def from_json(cls, json_string): 830 return cls.from_dict(json.loads(json_string)) 831 832 833class PushParameters(TAXIIBase11): 834 835 """Set up Push Parameters. 836 837 Args: 838 inbox_protocol (str): identifies the protocol to be used when pushing 839 TAXII Data Collection content to a Consumer's TAXII Inbox Service 840 implementation. **Required** 841 inbox_address (str): identifies the address of the TAXII Daemon hosting 842 the Inbox Service to which the Consumer requests content for this 843 TAXII Data Collection to be delivered. **Required** 844 delivery_message_binding (str): identifies the message binding to be 845 used to send pushed content for this subscription. **Required** 846 """ 847 848 name = 'Push_Parameters' 849 850 def __init__(self, inbox_protocol, inbox_address, delivery_message_binding): 851 self.inbox_protocol = inbox_protocol 852 self.inbox_address = inbox_address 853 self.delivery_message_binding = delivery_message_binding 854 855 @property 856 def sort_key(self): 857 return self.inbox_address 858 859 @property 860 def inbox_protocol(self): 861 return self._inbox_protocol 862 863 @inbox_protocol.setter 864 def inbox_protocol(self, value): 865 do_check(value, 'inbox_protocol', regex_tuple=uri_regex) 866 self._inbox_protocol = value 867 868 @property 869 def inbox_address(self): 870 return self._inbox_address 871 872 @inbox_address.setter 873 def inbox_address(self, value): 874 self._inbox_address = value 875 876 @property 877 def delivery_message_binding(self): 878 return self._delivery_message_binding 879 880 @delivery_message_binding.setter 881 def delivery_message_binding(self, value): 882 do_check(value, 'delivery_message_binding', regex_tuple=uri_regex) 883 self._delivery_message_binding = value 884 885 def to_etree(self): 886 xml = etree.Element('{%s}%s' % (ns_map['taxii_11'], self.name)) 887 888 pb = etree.SubElement(xml, '{%s}Protocol_Binding' % ns_map['taxii_11']) 889 pb.text = self.inbox_protocol 890 891 a = etree.SubElement(xml, '{%s}Address' % ns_map['taxii_11']) 892 a.text = self.inbox_address 893 894 mb = etree.SubElement(xml, '{%s}Message_Binding' % ns_map['taxii_11']) 895 mb.text = self.delivery_message_binding 896 897 return xml 898 899 def to_dict(self): 900 d = {} 901 902 if self.inbox_protocol is not None: 903 d['inbox_protocol'] = self.inbox_protocol 904 905 if self.inbox_address is not None: 906 d['inbox_address'] = self.inbox_address 907 908 if self.delivery_message_binding is not None: 909 d['delivery_message_binding'] = self.delivery_message_binding 910 911 return d 912 913 def to_text(self, line_prepend=''): 914 s = line_prepend + "=== Push Parameters ===\n" 915 s += line_prepend + " Protocol Binding: %s\n" % self.inbox_protocol 916 s += line_prepend + " Inbox Address: %s\n" % self.inbox_address 917 s += line_prepend + " Message Binding: %s\n" % self.delivery_message_binding 918 return s 919 920 @classmethod 921 def from_etree(cls, etree_xml): 922 inbox_protocol = get_optional_text(etree_xml, './taxii_11:Protocol_Binding', ns_map) 923 inbox_address = get_optional_text(etree_xml, './taxii_11:Address', ns_map) 924 delivery_message_binding = get_optional_text(etree_xml, './taxii_11:Message_Binding', ns_map) 925 926 return cls(inbox_protocol, inbox_address, delivery_message_binding) 927 928 @classmethod 929 def from_dict(cls, d): 930 return cls(**d) 931 932 933# TODO: Check docstring 934class DeliveryParameters(PushParameters): 935 936 """Set up Delivery Parameters. 937 938 Args: 939 inbox_protocol (str): identifies the protocol to be used when pushing 940 TAXII Data Collection content to a Consumer's TAXII Inbox Service 941 implementation. **Required** 942 inbox_address (str): identifies the address of the TAXII Daemon hosting 943 the Inbox Service to which the Consumer requests content for this 944 TAXII Data Collection to be delivered. **Required** 945 delivery_message_binding (str): identifies the message binding to be 946 used to send pushed content for this subscription. **Required** 947 """ 948 949 name = 'Delivery_Parameters' 950 951 952class TAXIIMessage(TAXIIBase11): 953 954 """Encapsulate properties common to all TAXII Messages (such as headers). 955 956 This class is extended by each Message Type (e.g., DiscoveryRequest), with 957 each subclass containing subclass-specific information 958 """ 959 960 message_type = 'TAXIIMessage' 961 962 def __init__(self, message_id, in_response_to=None, extended_headers=None): 963 """Create a new TAXIIMessage 964 965 Args: 966 message_id (str): A value identifying this message. 967 in_response_to (str): Contains the Message ID of the message to 968 which this is a response. 969 extended_headers (dict): A dictionary of name/value pairs for 970 use as Extended Headers 971 """ 972 self.message_id = message_id 973 self.in_response_to = in_response_to 974 self.extended_headers = extended_headers or {} 975 976 @property 977 def message_id(self): 978 return self._message_id 979 980 @message_id.setter 981 def message_id(self, value): 982 do_check(value, 'message_id', regex_tuple=uri_regex) 983 self._message_id = value 984 985 @property 986 def in_response_to(self): 987 return self._in_response_to 988 989 @in_response_to.setter 990 def in_response_to(self, value): 991 do_check(value, 'in_response_to', regex_tuple=uri_regex) 992 self._in_response_to = value 993 994 @property 995 def extended_headers(self): 996 return self._extended_headers 997 998 @extended_headers.setter 999 def extended_headers(self, value): 1000 do_check(list(value.keys()), 'extended_headers.keys()', regex_tuple=uri_regex) 1001 self._extended_headers = value 1002 1003 def to_etree(self): 1004 """Creates the base etree for the TAXII Message. 1005 1006 Message-specific constructs must be added by each Message class. In 1007 general, when converting to XML, subclasses should call this method 1008 first, then create their specific XML constructs. 1009 """ 1010 root_elt = etree.Element('{%s}%s' % (ns_map['taxii_11'], self.message_type), nsmap=ns_map) 1011 root_elt.attrib['message_id'] = str(self.message_id) 1012 1013 if self.in_response_to is not None: 1014 root_elt.attrib['in_response_to'] = str(self.in_response_to) 1015 1016 if len(self.extended_headers) > 0: 1017 eh = etree.SubElement(root_elt, '{%s}Extended_Headers' % ns_map['taxii_11'], nsmap=ns_map) 1018 1019 for name, value in list(self.extended_headers.items()): 1020 h = etree.SubElement(eh, '{%s}Extended_Header' % ns_map['taxii_11'], nsmap=ns_map) 1021 h.attrib['name'] = name 1022 append_any_content_etree(h, value) 1023 # h.text = value 1024 return root_elt 1025 1026 def to_dict(self): 1027 """Create the base dictionary for the TAXII Message. 1028 1029 Message-specific constructs must be added by each Message class. In 1030 general, when converting to dictionary, subclasses should call this 1031 method first, then create their specific dictionary constructs. 1032 """ 1033 d = {} 1034 d['message_type'] = self.message_type 1035 d['message_id'] = self.message_id 1036 if self.in_response_to is not None: 1037 d['in_response_to'] = self.in_response_to 1038 d['extended_headers'] = {} 1039 for k, v in six.iteritems(self.extended_headers): 1040 if isinstance(v, etree._Element) or isinstance(v, etree._ElementTree): 1041 v = etree.tostring(v, encoding='utf-8') 1042 elif not isinstance(v, six.string_types): 1043 v = str(v) 1044 d['extended_headers'][k] = v 1045 1046 return d 1047 1048 def to_text(self, line_prepend=''): 1049 s = line_prepend + "Message Type: %s\n" % self.message_type 1050 s += line_prepend + "Message ID: %s" % self.message_id 1051 if self.in_response_to: 1052 s += "; In Response To: %s" % self.in_response_to 1053 s += "\n" 1054 for k, v in six.iteritems(self.extended_headers): 1055 s += line_prepend + "Extended Header: %s = %s\n" % (k, v) 1056 1057 return s 1058 1059 @classmethod 1060 def from_etree(cls, src_etree, **kwargs): 1061 """Pulls properties of a TAXII Message from an etree. 1062 1063 Message-specific constructs must be pulled by each Message class. In 1064 general, when converting from etree, subclasses should call this method 1065 first, then parse their specific XML constructs. 1066 """ 1067 1068 # Check namespace and element name of the root element 1069 expected_tag = '{%s}%s' % (ns_map['taxii_11'], cls.message_type) 1070 tag = src_etree.tag 1071 if tag != expected_tag: 1072 raise ValueError('%s != %s' % (tag, expected_tag)) 1073 1074 # Get the message ID 1075 message_id = get_required(src_etree, '/taxii_11:*/@message_id', ns_map) 1076 1077 # Get in response to, if present 1078 in_response_to = get_optional(src_etree, '/taxii_11:*/@in_response_to', ns_map) 1079 if in_response_to is not None: 1080 kwargs['in_response_to'] = in_response_to 1081 1082 # Get the Extended headers 1083 extended_header_list = src_etree.xpath('/taxii_11:*/taxii_11:Extended_Headers/taxii_11:Extended_Header', namespaces=ns_map) 1084 extended_headers = {} 1085 for header in extended_header_list: 1086 eh_name = header.xpath('./@name')[0] 1087 if len(header) == 0: # This has string content 1088 eh_value = header.text 1089 else: # This has XML content 1090 eh_value = header[0] 1091 1092 extended_headers[eh_name] = eh_value 1093 1094 return cls(message_id, extended_headers=extended_headers, **kwargs) 1095 1096 1097 @classmethod 1098 def from_dict(cls, d, **kwargs): 1099 """Pulls properties of a TAXII Message from a dictionary. 1100 1101 Message-specific constructs must be pulled by each Message class. In 1102 general, when converting from dictionary, subclasses should call this 1103 method first, then parse their specific dictionary constructs. 1104 """ 1105 message_type = d['message_type'] 1106 if message_type != cls.message_type: 1107 raise ValueError('%s != %s' % (message_type, cls.message_type)) 1108 message_id = d['message_id'] 1109 extended_headers = {} 1110 for k, v in six.iteritems(d['extended_headers']): 1111 try: 1112 v = parse(v, allow_file=False) 1113 except etree.XMLSyntaxError: 1114 pass 1115 extended_headers[k] = v 1116 1117 in_response_to = d.get('in_response_to') 1118 if in_response_to: 1119 kwargs['in_response_to'] = in_response_to 1120 1121 return cls(message_id, extended_headers=extended_headers, **kwargs) 1122 1123 @classmethod 1124 def from_json(cls, json_string): 1125 return cls.from_dict(json.loads(json_string)) 1126 1127 1128class TAXIIRequestMessage(TAXIIMessage): 1129 1130 @TAXIIMessage.in_response_to.setter 1131 def in_response_to(self, value): 1132 if value is not None: 1133 raise ValueError('in_response_to must be None') 1134 self._in_response_to = value 1135 1136 1137class DiscoveryRequest(TAXIIRequestMessage): 1138 1139 """ 1140 A TAXII Discovery Request message. 1141 1142 Args: 1143 message_id (str): A value identifying this message. **Required** 1144 extended_headers (dict): A dictionary of name/value pairs for 1145 use as Extended Headers. **Optional** 1146 """ 1147 1148 message_type = MSG_DISCOVERY_REQUEST 1149 1150 1151class DiscoveryResponse(TAXIIMessage): 1152 1153 """ 1154 A TAXII Discovery Response message. 1155 1156 Args: 1157 message_id (str): A value identifying this message. **Required** 1158 in_response_to (str): Contains the Message ID of the message to 1159 which this is a response. **Optional** 1160 extended_headers (dict): A dictionary of name/value pairs for 1161 use as Extended Headers. **Optional** 1162 service_instances (list of `ServiceInstance`): a list of 1163 service instances that this response contains. **Optional** 1164 """ 1165 1166 message_type = MSG_DISCOVERY_RESPONSE 1167 1168 def __init__(self, message_id, in_response_to, extended_headers=None, service_instances=None): 1169 super(DiscoveryResponse, self).__init__(message_id, in_response_to, extended_headers) 1170 self.service_instances = service_instances or [] 1171 1172 @TAXIIMessage.in_response_to.setter 1173 def in_response_to(self, value): 1174 do_check(value, 'in_response_to', regex_tuple=uri_regex) 1175 self._in_response_to = value 1176 1177 @property 1178 def service_instances(self): 1179 return self._service_instances 1180 1181 @service_instances.setter 1182 def service_instances(self, value): 1183 do_check(value, 'service_instances', type=ServiceInstance) 1184 self._service_instances = value 1185 1186 def to_etree(self): 1187 xml = super(DiscoveryResponse, self).to_etree() 1188 for service_instance in self.service_instances: 1189 xml.append(service_instance.to_etree()) 1190 return xml 1191 1192 def to_dict(self): 1193 d = super(DiscoveryResponse, self).to_dict() 1194 d['service_instances'] = [] 1195 for service_instance in self.service_instances: 1196 d['service_instances'].append(service_instance.to_dict()) 1197 return d 1198 1199 def to_text(self, line_prepend=''): 1200 s = super(DiscoveryResponse, self).to_text() 1201 for si in self.service_instances: 1202 s += si.to_text(line_prepend + STD_INDENT) 1203 1204 return s 1205 1206 @classmethod 1207 def from_etree(cls, etree_xml): 1208 kwargs = {} 1209 kwargs['service_instances'] = [] 1210 service_instance_set = etree_xml.xpath('./taxii_11:Service_Instance', namespaces=ns_map) 1211 for service_instance in service_instance_set: 1212 si = ServiceInstance.from_etree(service_instance) 1213 kwargs['service_instances'].append(si) 1214 1215 return super(DiscoveryResponse, cls).from_etree(etree_xml, **kwargs) 1216 1217 @classmethod 1218 def from_dict(cls, d): 1219 msg = super(DiscoveryResponse, cls).from_dict(d) 1220 msg.service_instances = [] 1221 service_instance_set = d['service_instances'] 1222 for service_instance in service_instance_set: 1223 si = ServiceInstance.from_dict(service_instance) 1224 msg.service_instances.append(si) 1225 return msg 1226 1227 1228class ServiceInstance(TAXIIBase11): 1229 1230 """ 1231 The Service Instance component of a TAXII Discovery Response Message. 1232 1233 Args: 1234 service_type (string): identifies the Service Type of this 1235 Service Instance. **Required** 1236 services_version (string): identifies the TAXII Services 1237 Specification to which this Service conforms. **Required** 1238 protocol_binding (string): identifies the protocol binding 1239 supported by this Service. **Required** 1240 service_address (string): identifies the network address of the 1241 TAXII Daemon that hosts this Service. **Required** 1242 message_bindings (list of strings): identifies the message 1243 bindings supported by this Service instance. **Required** 1244 inbox_service_accepted_content (list of ContentBinding objects): identifies 1245 content bindings that this Inbox Service is willing to accept. 1246 **Optional** 1247 available (boolean): indicates whether the identity of the 1248 requester (authenticated or otherwise) is allowed to access this 1249 TAXII Service. **Optional** 1250 message (string): contains a message regarding this Service 1251 instance. **Optional** 1252 supported_query (SupportedQuery): contains a structure indicating a 1253 supported query. **Optional** 1254 1255 The ``message_bindings`` list must contain at least one value. The 1256 ``supported_query`` parameter is optional when 1257 ``service_type`` is :py:data:`SVC_POLL`. 1258 """ 1259 1260 def __init__(self, service_type, services_version, protocol_binding, 1261 service_address, message_bindings, 1262 inbox_service_accepted_content=None, available=None, 1263 message=None, supported_query=None): 1264 self.service_type = service_type 1265 self.services_version = services_version 1266 self.protocol_binding = protocol_binding 1267 self.service_address = service_address 1268 self.message_bindings = message_bindings 1269 self.inbox_service_accepted_content = inbox_service_accepted_content or [] 1270 self.available = available 1271 self.message = message 1272 self.supported_query = supported_query or [] 1273 1274 @property 1275 def sort_key(self): 1276 return self.service_address 1277 1278 @property 1279 def service_type(self): 1280 return self._service_type 1281 1282 @service_type.setter 1283 def service_type(self, value): 1284 do_check(value, 'service_type', value_tuple=SVC_TYPES) 1285 self._service_type = value 1286 1287 @property 1288 def services_version(self): 1289 return self._services_version 1290 1291 @services_version.setter 1292 def services_version(self, value): 1293 do_check(value, 'services_version', regex_tuple=uri_regex) 1294 self._services_version = value 1295 1296 @property 1297 def protocol_binding(self): 1298 return self._protocol_binding 1299 1300 @protocol_binding.setter 1301 def protocol_binding(self, value): 1302 do_check(value, 'protocol_binding', regex_tuple=uri_regex) 1303 self._protocol_binding = value 1304 1305 @property 1306 def service_address(self): 1307 return self._service_address 1308 1309 @service_address.setter 1310 def service_address(self, value): 1311 self._service_address = value 1312 1313 @property 1314 def message_bindings(self): 1315 return self._message_bindings 1316 1317 @message_bindings.setter 1318 def message_bindings(self, value): 1319 do_check(value, 'message_bindings', regex_tuple=uri_regex) 1320 self._message_bindings = value 1321 1322 @property 1323 def supported_query(self): 1324 return self._supported_query 1325 1326 @supported_query.setter 1327 def supported_query(self, value): 1328 do_check(value, 'supported_query', type=SupportedQuery) 1329 self._supported_query = value 1330 1331 @property 1332 def inbox_service_accepted_content(self): 1333 return self._inbox_service_accepted_content 1334 1335 @inbox_service_accepted_content.setter 1336 def inbox_service_accepted_content(self, value): 1337 value = _sanitize_content_bindings(value) 1338 do_check(value, 'inbox_service_accepted_content', type=ContentBinding) 1339 self._inbox_service_accepted_content = value 1340 1341 @property 1342 def available(self): 1343 return self._available 1344 1345 @available.setter 1346 def available(self, value): 1347 do_check(value, 'available', value_tuple=(True, False), can_be_none=True) 1348 self._available = value 1349 1350 def to_etree(self): 1351 si = etree.Element('{%s}Service_Instance' % ns_map['taxii_11'], nsmap=ns_map) 1352 si.attrib['service_type'] = self.service_type 1353 si.attrib['service_version'] = self.services_version 1354 if self.available is not None: 1355 si.attrib['available'] = str(self.available).lower() 1356 1357 protocol_binding = etree.SubElement(si, '{%s}Protocol_Binding' % ns_map['taxii_11'], nsmap=ns_map) 1358 protocol_binding.text = self.protocol_binding 1359 1360 service_address = etree.SubElement(si, '{%s}Address' % ns_map['taxii_11'], nsmap=ns_map) 1361 service_address.text = self.service_address 1362 1363 for mb in self.message_bindings: 1364 message_binding = etree.SubElement(si, '{%s}Message_Binding' % ns_map['taxii_11'], nsmap=ns_map) 1365 message_binding.text = mb 1366 1367 for sq in self.supported_query: 1368 si.append(sq.to_etree()) 1369 1370 for cb in self.inbox_service_accepted_content: 1371 content_binding = cb.to_etree() 1372 si.append(content_binding) 1373 1374 if self.message is not None: 1375 message = etree.SubElement(si, '{%s}Message' % ns_map['taxii_11'], nsmap=ns_map) 1376 message.text = self.message 1377 1378 return si 1379 1380 def to_dict(self): 1381 d = {} 1382 d['service_type'] = self.service_type 1383 d['services_version'] = self.services_version 1384 d['protocol_binding'] = self.protocol_binding 1385 d['service_address'] = self.service_address 1386 d['message_bindings'] = self.message_bindings 1387 d['supported_query'] = [] 1388 for sq in self.supported_query: 1389 d['supported_query'].append(sq.to_dict()) 1390 d['inbox_service_accepted_content'] = self.inbox_service_accepted_content 1391 d['available'] = self.available 1392 d['message'] = self.message 1393 return d 1394 1395 def to_text(self, line_prepend=''): 1396 s = line_prepend + "=== Service Instance ===\n" 1397 s += line_prepend + " Service Type: %s\n" % self.service_type 1398 s += line_prepend + " Service Version: %s\n" % self.services_version 1399 s += line_prepend + " Protocol Binding: %s\n" % self.protocol_binding 1400 s += line_prepend + " Service Address: %s\n" % self.service_address 1401 for mb in self.message_bindings: 1402 s += line_prepend + " Message Binding: %s\n" % mb 1403 if self.service_type == SVC_INBOX: 1404 s += line_prepend + " Inbox Service AC: %s\n" % [ac.to_text() for ac in self.inbox_service_accepted_content] 1405 s += line_prepend + " Available: %s\n" % self.available 1406 s += line_prepend + " Message: %s\n" % self.message 1407 for q in self.supported_query: 1408 s += q.to_text(line_prepend + STD_INDENT) 1409 1410 return s 1411 1412 @staticmethod 1413 def from_etree(etree_xml): # Expects a taxii_11:Service_Instance element 1414 service_type = etree_xml.attrib['service_type'] 1415 services_version = etree_xml.attrib['service_version'] 1416 available = None 1417 if etree_xml.attrib.get('available'): 1418 tmp_available = etree_xml.attrib['available'] 1419 available = tmp_available == 'true' 1420 1421 protocol_binding = get_required(etree_xml, './taxii_11:Protocol_Binding', ns_map).text 1422 service_address = get_required(etree_xml, './taxii_11:Address', ns_map).text 1423 1424 message_bindings = [] 1425 message_binding_set = etree_xml.xpath('./taxii_11:Message_Binding', namespaces=ns_map) 1426 for mb in message_binding_set: 1427 message_bindings.append(mb.text) 1428 1429 inbox_service_accepted_content = [] 1430 inbox_service_accepted_content_set = etree_xml.xpath('./taxii_11:Content_Binding', namespaces=ns_map) 1431 for cb in inbox_service_accepted_content_set: 1432 inbox_service_accepted_content.append(ContentBinding.from_etree(cb)) 1433 1434 supported_query = [] 1435 supported_query_set = etree_xml.xpath('./taxii_11:Supported_Query', namespaces=ns_map) 1436 for sq in supported_query_set: 1437 format_id = sq.xpath('./@format_id')[0] 1438 query_obj = get_deserializer(format_id, 'query_info').from_etree(sq) 1439 supported_query.append(query_obj) 1440 1441 message = get_optional_text(etree_xml, './taxii_11:Message', ns_map) 1442 1443 return ServiceInstance(service_type, 1444 services_version, 1445 protocol_binding, 1446 service_address, 1447 message_bindings, 1448 inbox_service_accepted_content, 1449 available, 1450 message, 1451 supported_query) 1452 1453 @staticmethod 1454 def from_dict(d): 1455 service_type = d['service_type'] 1456 services_version = d['services_version'] 1457 protocol_binding = d['protocol_binding'] 1458 service_address = d['service_address'] 1459 message_bindings = d['message_bindings'] 1460 supported_query = [] 1461 sq_list = d.get('supported_query') 1462 if sq_list is not None: 1463 for sq in sq_list: 1464 format_id = sq['format_id'] 1465 query_obj = get_deserializer(format_id, 'query_info').from_dict(sq) 1466 supported_query.append(query_obj) 1467 inbox_service_accepted_content = d.get('inbox_service_accepted_content') 1468 available = d.get('available') 1469 message = d.get('message') 1470 1471 return ServiceInstance(service_type, 1472 services_version, 1473 protocol_binding, 1474 service_address, 1475 message_bindings, 1476 inbox_service_accepted_content, 1477 available, 1478 message, 1479 supported_query) 1480 1481 1482class CollectionInformationRequest(TAXIIRequestMessage): 1483 1484 """ 1485 A TAXII Collection Information Request message. 1486 1487 Args: 1488 message_id (str): A value identifying this message. **Required** 1489 extended_headers (dict): A dictionary of name/value pairs for 1490 use as Extended Headers. **Optional** 1491 """ 1492 1493 message_type = MSG_COLLECTION_INFORMATION_REQUEST 1494 1495 1496class CollectionInformationResponse(TAXIIMessage): 1497 1498 """ 1499 A TAXII Collection Information Response message. 1500 1501 Args: 1502 message_id (str): A value identifying this message. **Required** 1503 in_response_to (str): Contains the Message ID of the message to 1504 which this is a response. **Optional** 1505 extended_headers (dict): A dictionary of name/value pairs for 1506 use as Extended Headers. **Optional** 1507 collection_informations (list of CollectionInformation objects): A list 1508 of CollectionInformation objects to be contained in this response. 1509 **Optional** 1510 """ 1511 message_type = MSG_COLLECTION_INFORMATION_RESPONSE 1512 1513 def __init__(self, message_id, in_response_to, extended_headers=None, collection_informations=None): 1514 super(CollectionInformationResponse, self).__init__(message_id, in_response_to, extended_headers=extended_headers) 1515 self.collection_informations = collection_informations or [] 1516 1517 @TAXIIMessage.in_response_to.setter 1518 def in_response_to(self, value): 1519 do_check(value, 'in_response_to', regex_tuple=uri_regex) 1520 self._in_response_to = value 1521 1522 @property 1523 def collection_informations(self): 1524 return self._collection_informations 1525 1526 @collection_informations.setter 1527 def collection_informations(self, value): 1528 do_check(value, 'collection_informations', type=CollectionInformation) 1529 self._collection_informations = value 1530 1531 def to_etree(self): 1532 xml = super(CollectionInformationResponse, self).to_etree() 1533 for collection in self.collection_informations: 1534 xml.append(collection.to_etree()) 1535 return xml 1536 1537 def to_dict(self): 1538 d = super(CollectionInformationResponse, self).to_dict() 1539 d['collection_informations'] = [] 1540 for collection in self.collection_informations: 1541 d['collection_informations'].append(collection.to_dict()) 1542 return d 1543 1544 def to_text(self, line_prepend=''): 1545 s = super(CollectionInformationResponse, self).to_text(line_prepend) 1546 s += line_prepend + "Contains %s Collection Informations\n" % len(self.collection_informations) 1547 for collection in self.collection_informations: 1548 s += collection.to_text(line_prepend + STD_INDENT) 1549 1550 return s 1551 1552 @classmethod 1553 def from_etree(cls, etree_xml): 1554 msg = super(CollectionInformationResponse, cls).from_etree(etree_xml) 1555 msg.collection_informations = [] 1556 collection_informations = etree_xml.xpath('./taxii_11:Collection', namespaces=ns_map) 1557 for collection in collection_informations: 1558 msg.collection_informations.append(CollectionInformation.from_etree(collection)) 1559 return msg 1560 1561 @classmethod 1562 def from_dict(cls, d): 1563 msg = super(CollectionInformationResponse, cls).from_dict(d) 1564 msg.collection_informations = [] 1565 for collection in d['collection_informations']: 1566 msg.collection_informations.append(CollectionInformation.from_dict(collection)) 1567 return msg 1568 1569 1570class CollectionInformation(TAXIIBase11): 1571 1572 """ 1573 The Collection Information component of a TAXII Collection Information 1574 Response Message. 1575 1576 Arguments: 1577 collection_name (str): the name by which this TAXII Data Collection is 1578 identified. **Required** 1579 collection_description (str): a prose description of this TAXII 1580 Data Collection. **Required** 1581 supported_contents (list of str): Content Binding IDs 1582 indicating which types of content are currently expressed in this 1583 TAXII Data Collection. **Optional** 1584 available (boolean): whether the identity of the requester 1585 (authenticated or otherwise) is allowed to access this TAXII 1586 Service. **Optional** Default: ``None``, indicating "unknown" 1587 push_methods (list of PushMethod objects): the protocols that 1588 can be used to push content via a subscription. **Optional** 1589 polling_service_instances (list of PollingServiceInstance objects): 1590 the bindings and address a Consumer can use to interact with a 1591 Poll Service instance that supports this TAXII Data Collection. 1592 **Optional** 1593 subscription_methods (list of SubscriptionMethod objects): the 1594 protocol and address of the TAXII Daemon hosting the Collection 1595 Management Service that can process subscriptions for this TAXII 1596 Data Collection. **Optional** 1597 collection_volume (int): the typical number of messages per day. 1598 **Optional** 1599 collection_type (str): the type ofo this collection. **Optional**, 1600 defaults to :py:data:`CT_DATA_FEED`. 1601 receiving_inbox_services (list of ReceivingInboxService objects): 1602 TODO: FILL THIS IN. **Optional** 1603 1604 If ``supported_contents`` is omitted, then the collection supports all 1605 content bindings. The absense of ``push_methods`` indicates no push 1606 methods. The absense of ``polling_service_instances`` indicates no 1607 polling services. The absense of ``subscription_methods`` indicates no 1608 subscription services. The absense of ``receiving_inbox_services`` 1609 indicates no receiving inbox services. 1610 """ 1611 1612 def __init__(self, collection_name, collection_description, 1613 supported_contents=None, available=None, push_methods=None, 1614 polling_service_instances=None, subscription_methods=None, 1615 collection_volume=None, collection_type=CT_DATA_FEED, 1616 receiving_inbox_services=None): 1617 self.collection_name = collection_name 1618 self.available = available 1619 self.collection_description = collection_description 1620 self.supported_contents = supported_contents or [] 1621 self.push_methods = push_methods or [] 1622 self.polling_service_instances = polling_service_instances or [] 1623 self.subscription_methods = subscription_methods or [] 1624 self.receiving_inbox_services = receiving_inbox_services or [] 1625 self.collection_volume = collection_volume 1626 self.collection_type = collection_type 1627 1628 @property 1629 def sort_key(self): 1630 return self.collection_name 1631 1632 @property 1633 def collection_name(self): 1634 return self._collection_name 1635 1636 @collection_name.setter 1637 def collection_name(self, value): 1638 do_check(value, 'collection_name', regex_tuple=uri_regex) 1639 self._collection_name = value 1640 1641 @property 1642 def available(self): 1643 return self._available 1644 1645 @available.setter 1646 def available(self, value): 1647 do_check(value, 'available', value_tuple=(True, False), can_be_none=True) 1648 self._available = value 1649 1650 @property 1651 def supported_contents(self): 1652 return self._supported_contents 1653 1654 @supported_contents.setter 1655 def supported_contents(self, value): 1656 value = _sanitize_content_bindings(value) 1657 do_check(value, 'supported_contents', type=ContentBinding) 1658 self._supported_contents = value 1659 1660 @property 1661 def push_methods(self): 1662 return self._push_methods 1663 1664 @push_methods.setter 1665 def push_methods(self, value): 1666 do_check(value, 'push_methods', type=PushMethod) 1667 self._push_methods = value 1668 1669 @property 1670 def polling_service_instances(self): 1671 return self._polling_service_instances 1672 1673 @polling_service_instances.setter 1674 def polling_service_instances(self, value): 1675 do_check(value, 'polling_service_instances', type=PollingServiceInstance) 1676 self._polling_service_instances = value 1677 1678 @property 1679 def subscription_methods(self): 1680 return self._subscription_methods 1681 1682 @subscription_methods.setter 1683 def subscription_methods(self, value): 1684 do_check(value, 'subscription_methods', type=SubscriptionMethod) 1685 self._subscription_methods = value 1686 1687 @property 1688 def receiving_inbox_services(self): 1689 return self._receiving_inbox_services 1690 1691 @receiving_inbox_services.setter 1692 def receiving_inbox_services(self, value): 1693 do_check(value, 'receiving_inbox_services', type=ReceivingInboxService) 1694 self._receiving_inbox_services = value 1695 1696 @property 1697 def collection_volume(self): 1698 return self._collection_volume 1699 1700 @collection_volume.setter 1701 def collection_volume(self, value): 1702 do_check(value, 'collection_volume', type=int, can_be_none=True) 1703 self._collection_volume = value 1704 1705 @property 1706 def collection_type(self): 1707 return self._collection_type 1708 1709 @collection_type.setter 1710 def collection_type(self, value): 1711 do_check(value, 'collection_type', value_tuple=CT_TYPES, can_be_none=True) 1712 self._collection_type = value 1713 1714 def to_etree(self): 1715 c = etree.Element('{%s}Collection' % ns_map['taxii_11'], nsmap=ns_map) 1716 c.attrib['collection_name'] = self.collection_name 1717 if self.collection_type is not None: 1718 c.attrib['collection_type'] = self.collection_type 1719 if self.available is not None: 1720 c.attrib['available'] = str(self.available).lower() 1721 collection_description = etree.SubElement(c, '{%s}Description' % ns_map['taxii_11'], nsmap=ns_map) 1722 collection_description.text = self.collection_description 1723 1724 if self.collection_volume is not None: 1725 collection_volume = etree.SubElement(c, '{%s}Collection_Volume' % ns_map['taxii_11'], nsmap=ns_map) 1726 collection_volume.text = str(self.collection_volume) 1727 1728 for binding in self.supported_contents: 1729 c.append(binding.to_etree()) 1730 1731 for push_method in self.push_methods: 1732 c.append(push_method.to_etree()) 1733 1734 for polling_service in self.polling_service_instances: 1735 c.append(polling_service.to_etree()) 1736 1737 for subscription_method in self.subscription_methods: 1738 c.append(subscription_method.to_etree()) 1739 1740 for receiving_inbox_service in self.receiving_inbox_services: 1741 c.append(receiving_inbox_service.to_etree()) 1742 1743 return c 1744 1745 def to_dict(self): 1746 d = {} 1747 d['collection_name'] = self.collection_name 1748 if self.collection_type is not None: 1749 d['collection_type'] = self.collection_type 1750 if self.available is not None: 1751 d['available'] = self.available 1752 d['collection_description'] = self.collection_description 1753 if self.collection_volume is not None: 1754 d['collection_volume'] = self.collection_volume 1755 # TODO: I think this isn't a good serialization, I think a for loop is necessary 1756 # This is probably a bug 1757 d['supported_contents'] = self.supported_contents 1758 1759 d['push_methods'] = [] 1760 for push_method in self.push_methods: 1761 d['push_methods'].append(push_method.to_dict()) 1762 1763 d['polling_service_instances'] = [] 1764 for polling_service in self.polling_service_instances: 1765 d['polling_service_instances'].append(polling_service.to_dict()) 1766 1767 d['subscription_methods'] = [] 1768 for subscription_method in self.subscription_methods: 1769 d['subscription_methods'].append(subscription_method.to_dict()) 1770 1771 d['receiving_inbox_services'] = [] 1772 for receiving_inbox_service in self.receiving_inbox_services: 1773 d['receiving_inbox_services'].append(receiving_inbox_service.to_dict()) 1774 1775 return d 1776 1777 def to_text(self, line_prepend=''): 1778 s = line_prepend + "=== Data Collection Information ===\n" 1779 s += line_prepend + " Collection Name: %s\n" % self.collection_name 1780 s += line_prepend + " Collection Type: %s\n" % (self.collection_type if (None != self.collection_type) else CT_DATA_FEED) 1781 s += line_prepend + " Available: %s\n" % self.available 1782 s += line_prepend + " Collection Description: %s\n" % self.collection_description 1783 if self.collection_volume: 1784 s += line_prepend + " Volume: %s\n" % self.collection_volume 1785 if len(self.supported_contents) == 0: # All contents supported: 1786 s += line_prepend + " Supported Content: %s\n" % "All" 1787 for contents in self.supported_contents: 1788 s += line_prepend + " Supported Content: %s\n" % contents.to_text(line_prepend + STD_INDENT) 1789 for psi in self.polling_service_instances: 1790 s += psi.to_text(line_prepend + STD_INDENT) 1791 for sm in self.subscription_methods: 1792 s += sm.to_text(line_prepend + STD_INDENT) 1793 for ris in self.receiving_inbox_services: 1794 s += ris.to_text(line_prepend + STD_INDENT) 1795 s += line_prepend + "==================================\n\n" 1796 return s 1797 1798 @staticmethod 1799 def from_etree(etree_xml): 1800 kwargs = {} 1801 kwargs['collection_name'] = etree_xml.attrib['collection_name'] 1802 kwargs['collection_type'] = etree_xml.attrib.get('collection_type', None) 1803 1804 kwargs['available'] = None 1805 if 'available' in etree_xml.attrib: 1806 tmp = etree_xml.attrib['available'] 1807 kwargs['available'] = tmp.lower() == 'true' 1808 1809 kwargs['collection_description'] = get_required(etree_xml, './taxii_11:Description', ns_map).text 1810 1811 collection_volume_text = get_optional_text(etree_xml, './taxii_11:Collection_Volume', ns_map) 1812 if collection_volume_text: 1813 kwargs['collection_volume'] = int(collection_volume_text) 1814 1815 kwargs['supported_contents'] = [] 1816 supported_content_set = etree_xml.xpath('./taxii_11:Content_Binding', namespaces=ns_map) 1817 for binding_elt in supported_content_set: 1818 kwargs['supported_contents'].append(ContentBinding.from_etree(binding_elt)) 1819 1820 kwargs['push_methods'] = [] 1821 push_method_set = etree_xml.xpath('./taxii_11:Push_Method', namespaces=ns_map) 1822 for push_method_elt in push_method_set: 1823 kwargs['push_methods'].append(PushMethod.from_etree(push_method_elt)) 1824 1825 kwargs['polling_service_instances'] = [] 1826 polling_service_set = etree_xml.xpath('./taxii_11:Polling_Service', namespaces=ns_map) 1827 for polling_elt in polling_service_set: 1828 kwargs['polling_service_instances'].append(PollingServiceInstance.from_etree(polling_elt)) 1829 1830 kwargs['subscription_methods'] = [] 1831 subscription_method_set = etree_xml.xpath('./taxii_11:Subscription_Service', namespaces=ns_map) 1832 for subscription_elt in subscription_method_set: 1833 kwargs['subscription_methods'].append(SubscriptionMethod.from_etree(subscription_elt)) 1834 1835 kwargs['receiving_inbox_services'] = [] 1836 receiving_inbox_services_set = etree_xml.xpath('./taxii_11:Receiving_Inbox_Service', namespaces=ns_map) 1837 for receiving_inbox_service in receiving_inbox_services_set: 1838 kwargs['receiving_inbox_services'].append(ReceivingInboxService.from_etree(receiving_inbox_service)) 1839 1840 return CollectionInformation(**kwargs) 1841 1842 1843 @staticmethod 1844 def from_dict(d): 1845 kwargs = {} 1846 kwargs['collection_name'] = d['collection_name'] 1847 kwargs['collection_type'] = d.get('collection_type') 1848 kwargs['available'] = d.get('available') 1849 kwargs['collection_description'] = d['collection_description'] 1850 kwargs['collection_volume'] = d.get('collection_volume') 1851 1852 kwargs['supported_contents'] = d.get('supported_contents', []) 1853 1854 kwargs['push_methods'] = [] 1855 for push_method in d.get('push_methods', []): 1856 kwargs['push_methods'].append(PushMethod.from_dict(push_method)) 1857 1858 kwargs['polling_service_instances'] = [] 1859 for polling in d.get('polling_service_instances', []): 1860 kwargs['polling_service_instances'].append(PollingServiceInstance.from_dict(polling)) 1861 1862 kwargs['subscription_methods'] = [] 1863 for subscription_method in d.get('subscription_methods', []): 1864 kwargs['subscription_methods'].append(SubscriptionMethod.from_dict(subscription_method)) 1865 1866 kwargs['receiving_inbox_services'] = [] 1867 receiving_inbox_services_set = d.get('receiving_inbox_services', []) 1868 for receiving_inbox_service in receiving_inbox_services_set: 1869 kwargs['receiving_inbox_services'].append(ReceivingInboxService.from_dict(receiving_inbox_service)) 1870 1871 return CollectionInformation(**kwargs) 1872 1873 1874class PushMethod(TAXIIBase11): 1875 1876 """ 1877 The Push Method component of a TAXII Collection Information 1878 component. 1879 1880 Args: 1881 push_protocol (str): a protocol binding that can be used 1882 to push content to an Inbox Service instance. **Required** 1883 push_message_bindings (list of str): the message bindings that 1884 can be used to push content to an Inbox Service instance 1885 using the protocol identified in the Push Protocol field. 1886 **Required** 1887 """ 1888 1889 def __init__(self, push_protocol, push_message_bindings): 1890 self.push_protocol = push_protocol 1891 self.push_message_bindings = push_message_bindings 1892 1893 @property 1894 def sort_key(self): 1895 return self.push_protocol 1896 1897 @property 1898 def push_protocol(self): 1899 return self._push_protocol 1900 1901 @push_protocol.setter 1902 def push_protocol(self, value): 1903 do_check(value, 'push_protocol', regex_tuple=uri_regex) 1904 self._push_protocol = value 1905 1906 @property 1907 def push_message_bindings(self): 1908 return self._push_message_bindings 1909 1910 @push_message_bindings.setter 1911 def push_message_bindings(self, value): 1912 do_check(value, 'push_message_bindings', regex_tuple=uri_regex) 1913 self._push_message_bindings = value 1914 1915 def to_etree(self): 1916 x = etree.Element('{%s}Push_Method' % ns_map['taxii_11'], nsmap=ns_map) 1917 proto_bind = etree.SubElement(x, '{%s}Protocol_Binding' % ns_map['taxii_11'], nsmap=ns_map) 1918 proto_bind.text = self.push_protocol 1919 for binding in self.push_message_bindings: 1920 b = etree.SubElement(x, '{%s}Message_Binding' % ns_map['taxii_11'], nsmap=ns_map) 1921 b.text = binding 1922 return x 1923 1924 def to_dict(self): 1925 d = {} 1926 d['push_protocol'] = self.push_protocol 1927 d['push_message_bindings'] = [] 1928 for binding in self.push_message_bindings: 1929 d['push_message_bindings'].append(binding) 1930 return d 1931 1932 def to_text(self, line_prepend=''): 1933 s = line_prepend + "=== Push Method ===\n" 1934 s += line_prepend + " Protocol Binding: %s\n" % self.push_protocol 1935 for mb in self.push_message_bindings: 1936 s += line_prepend + " Message Binding: %s\n" % mb 1937 return s 1938 1939 @staticmethod 1940 def from_etree(etree_xml): 1941 kwargs = {} 1942 kwargs['push_protocol'] = get_required(etree_xml, './taxii_11:Protocol_Binding', ns_map).text 1943 1944 kwargs['push_message_bindings'] = [] 1945 message_binding_set = etree_xml.xpath('./taxii_11:Message_Binding', namespaces=ns_map) 1946 for message_binding in message_binding_set: 1947 kwargs['push_message_bindings'].append(message_binding.text) 1948 return PushMethod(**kwargs) 1949 1950 @staticmethod 1951 def from_dict(d): 1952 return PushMethod(**d) 1953 1954 1955class PollingServiceInstance(TAXIIBase11): 1956 1957 """ 1958 The Polling Service Instance component of a TAXII Collection 1959 Information component. 1960 1961 Args: 1962 poll_protocol (str): the protocol binding supported by 1963 this Poll Service instance. **Required** 1964 poll_address (str): the address of the TAXII Daemon 1965 hosting this Poll Service instance. **Required** 1966 poll_message_bindings (list of str): the message bindings 1967 supported by this Poll Service instance. **Required** 1968 """ 1969 NAME = 'Polling_Service' 1970 1971 def __init__(self, poll_protocol, poll_address, poll_message_bindings): 1972 self.poll_protocol = poll_protocol 1973 self.poll_address = poll_address 1974 self.poll_message_bindings = poll_message_bindings 1975 1976 @property 1977 def sort_key(self): 1978 return self.poll_address 1979 1980 @property 1981 def poll_protocol(self): 1982 return self._poll_protocol 1983 1984 @poll_protocol.setter 1985 def poll_protocol(self, value): 1986 do_check(value, 'poll_protocol', regex_tuple=uri_regex) 1987 self._poll_protocol = value 1988 1989 @property 1990 def poll_message_bindings(self): 1991 return self._poll_message_bindings 1992 1993 @poll_message_bindings.setter 1994 def poll_message_bindings(self, value): 1995 do_check(value, 'poll_message_bindings', regex_tuple=uri_regex) 1996 self._poll_message_bindings = value 1997 1998 def to_etree(self): 1999 x = etree.Element('{%s}Polling_Service' % ns_map['taxii_11'], nsmap=ns_map) 2000 proto_bind = etree.SubElement(x, '{%s}Protocol_Binding' % ns_map['taxii_11'], nsmap=ns_map) 2001 proto_bind.text = self.poll_protocol 2002 address = etree.SubElement(x, '{%s}Address' % ns_map['taxii_11'], nsmap=ns_map) 2003 address.text = self.poll_address 2004 for binding in self.poll_message_bindings: 2005 b = etree.SubElement(x, '{%s}Message_Binding' % ns_map['taxii_11'], nsmap=ns_map) 2006 b.text = binding 2007 return x 2008 2009 def to_dict(self): 2010 d = {} 2011 d['poll_protocol'] = self.poll_protocol 2012 d['poll_address'] = self.poll_address 2013 d['poll_message_bindings'] = [] 2014 for binding in self.poll_message_bindings: 2015 d['poll_message_bindings'].append(binding) 2016 return d 2017 2018 def to_text(self, line_prepend=''): 2019 s = line_prepend + "=== Polling Service Instance ===\n" 2020 s += line_prepend + " Poll Protocol: %s\n" % self.poll_protocol 2021 s += line_prepend + " Poll Address: %s\n" % self.poll_address 2022 for binding in self.poll_message_bindings: 2023 s += line_prepend + " Message Binding: %s\n" % binding 2024 return s 2025 2026 @classmethod 2027 def from_etree(cls, etree_xml): 2028 protocol = get_required(etree_xml, './taxii_11:Protocol_Binding', ns_map).text 2029 addr = get_required(etree_xml, './taxii_11:Address', ns_map).text 2030 2031 bindings = [] 2032 message_binding_set = etree_xml.xpath('./taxii_11:Message_Binding', namespaces=ns_map) 2033 for message_binding in message_binding_set: 2034 bindings.append(message_binding.text) 2035 return cls(protocol, addr, bindings) 2036 2037 @classmethod 2038 def from_dict(cls, d): 2039 return cls(**d) 2040 2041 2042class SubscriptionMethod(TAXIIBase11): 2043 2044 """ 2045 The Subscription Method component of a TAXII Collection Information 2046 component. 2047 2048 Args: 2049 subscription_protocol (str): the protocol binding supported by 2050 this Collection Management Service instance. **Required** 2051 subscription_address (str): the address of the TAXII Daemon 2052 hosting this Collection Management Service instance. 2053 **Required**. 2054 subscription_message_bindings (list of str): the message 2055 bindings supported by this Collection Management Service 2056 Instance. **Required** 2057 """ 2058 NAME = 'Subscription_Service' 2059 2060 def __init__(self, subscription_protocol, subscription_address, 2061 subscription_message_bindings): 2062 self.subscription_protocol = subscription_protocol 2063 self.subscription_address = subscription_address 2064 self.subscription_message_bindings = subscription_message_bindings 2065 2066 @property 2067 def sort_key(self): 2068 return self.subscription_address 2069 2070 @property 2071 def subscription_protocol(self): 2072 return self._subscription_protocol 2073 2074 @subscription_protocol.setter 2075 def subscription_protocol(self, value): 2076 do_check(value, 'subscription_protocol', regex_tuple=uri_regex) 2077 self._subscription_protocol = value 2078 2079 @property 2080 def subscription_message_bindings(self): 2081 return self._subscription_message_bindings 2082 2083 @subscription_message_bindings.setter 2084 def subscription_message_bindings(self, value): 2085 do_check(value, 'subscription_message_bindings', regex_tuple=uri_regex) 2086 self._subscription_message_bindings = value 2087 2088 def to_etree(self): 2089 x = etree.Element('{%s}%s' % (ns_map['taxii_11'], self.NAME)) 2090 proto_bind = etree.SubElement(x, '{%s}Protocol_Binding' % ns_map['taxii_11'], nsmap=ns_map) 2091 proto_bind.text = self.subscription_protocol 2092 address = etree.SubElement(x, '{%s}Address' % ns_map['taxii_11'], nsmap=ns_map) 2093 address.text = self.subscription_address 2094 for binding in self.subscription_message_bindings: 2095 b = etree.SubElement(x, '{%s}Message_Binding' % ns_map['taxii_11'], nsmap=ns_map) 2096 b.text = binding 2097 return x 2098 2099 def to_dict(self): 2100 d = {} 2101 d['subscription_protocol'] = self.subscription_protocol 2102 d['subscription_address'] = self.subscription_address 2103 d['subscription_message_bindings'] = [] 2104 for binding in self.subscription_message_bindings: 2105 d['subscription_message_bindings'].append(binding) 2106 return d 2107 2108 def to_text(self, line_prepend=''): 2109 s = line_prepend + "=== Subscription Service ===\n" 2110 s += line_prepend + " Protocol Binding: %s\n" % self.subscription_protocol 2111 s += line_prepend + " Address: %s\n" % self.subscription_address 2112 for mb in self.subscription_message_bindings: 2113 s += line_prepend + " Message Binding: %s\n" % mb 2114 return s 2115 2116 @classmethod 2117 def from_etree(cls, etree_xml): 2118 protocol = get_required(etree_xml, './taxii_11:Protocol_Binding', ns_map).text 2119 addr = get_required(etree_xml, './taxii_11:Address', ns_map).text 2120 bindings = [] 2121 message_binding_set = etree_xml.xpath('./taxii_11:Message_Binding', namespaces=ns_map) 2122 for message_binding in message_binding_set: 2123 bindings.append(message_binding.text) 2124 return cls(protocol, addr, bindings) 2125 2126 @classmethod 2127 def from_dict(cls, d): 2128 return cls(**d) 2129 2130 2131class ReceivingInboxService(TAXIIBase11): 2132 2133 """ 2134 The Receiving Inbox Service component of a TAXII Collection 2135 Information component. 2136 2137 Args: 2138 inbox_protocol (str): Indicates the protocol this Inbox Service 2139 uses. **Required** 2140 inbox address (str): Indicates the address of this Inbox Service. 2141 **Required** 2142 inbox_message_bindings (list of str): Each string indicates a 2143 message binding that this inbox service uses. **Required** 2144 supported_contents (list of ContentBinding objects): Each object 2145 indicates a Content Binding this inbox service can receive. 2146 **Optional**. Setting to ``None`` means that all Content 2147 Bindings are supported. 2148 """ 2149 2150 def __init__(self, inbox_protocol, inbox_address, 2151 inbox_message_bindings, supported_contents=None): 2152 self.inbox_protocol = inbox_protocol 2153 self.inbox_address = inbox_address 2154 self.inbox_message_bindings = inbox_message_bindings 2155 self.supported_contents = supported_contents or [] 2156 2157 @property 2158 def sort_key(self): 2159 return self.inbox_address 2160 2161 @property 2162 def inbox_protocol(self): 2163 return self._inbox_protocol 2164 2165 @inbox_protocol.setter 2166 def inbox_protocol(self, value): 2167 do_check(value, 'inbox_protocol', type=six.string_types, regex_tuple=uri_regex) 2168 self._inbox_protocol = value 2169 2170 @property 2171 def inbox_address(self): 2172 return self._inbox_address 2173 2174 @inbox_address.setter 2175 def inbox_address(self, value): 2176 self._inbox_address = value 2177 2178 @property 2179 def inbox_message_bindings(self): 2180 return self._inbox_message_bindings 2181 2182 @inbox_message_bindings.setter 2183 def inbox_message_bindings(self, value): 2184 do_check(value, 'inbox_message_bindings', regex_tuple=uri_regex) 2185 self._inbox_message_bindings = value 2186 2187 @property 2188 def supported_contents(self): 2189 return self._supported_contents 2190 2191 @supported_contents.setter 2192 def supported_contents(self, value): 2193 value = _sanitize_content_bindings(value) 2194 do_check(value, 'supported_contents', type=ContentBinding) 2195 self._supported_contents = value 2196 2197 def to_etree(self): 2198 xml = etree.Element('{%s}Receiving_Inbox_Service' % ns_map['taxii_11'], nsmap=ns_map) 2199 2200 pb = etree.SubElement(xml, '{%s}Protocol_Binding' % ns_map['taxii_11']) 2201 pb.text = self.inbox_protocol 2202 2203 a = etree.SubElement(xml, '{%s}Address' % ns_map['taxii_11']) 2204 a.text = self.inbox_address 2205 2206 for binding in self.inbox_message_bindings: 2207 mb = etree.SubElement(xml, '{%s}Message_Binding' % ns_map['taxii_11']) 2208 mb.text = binding 2209 2210 for binding in self.supported_contents: 2211 xml.append(binding.to_etree()) 2212 2213 return xml 2214 2215 def to_dict(self): 2216 d = {} 2217 2218 d['inbox_protocol'] = self.inbox_protocol 2219 d['inbox_address'] = self.inbox_address 2220 d['inbox_message_bindings'] = self.inbox_message_bindings 2221 d['supported_contents'] = [] 2222 for supported_content in self.supported_contents: 2223 d['supported_contents'].append(supported_content.to_dict()) 2224 2225 return d 2226 2227 def to_text(self, line_prepend=''): 2228 s = line_prepend + "=== Receiving Inbox Service ===\n" 2229 s += line_prepend + " Protocol Binding: %s\n" % self.inbox_protocol 2230 s += line_prepend + " Address: %s\n" % self.inbox_address 2231 for mb in self.inbox_message_bindings: 2232 s += line_prepend + " Message Binding: %s\n" % mb 2233 if len(self.supported_contents) == 0: 2234 s += line_prepend + " Supported Contents: All\n" 2235 for sc in self.supported_contents: 2236 s += line_prepend + " Supported Content: %s\n" % str(sc) 2237 2238 return s 2239 2240 @staticmethod 2241 def from_etree(etree_xml): 2242 proto = get_required(etree_xml, './taxii_11:Protocol_Binding', ns_map).text 2243 addr = get_required(etree_xml, './taxii_11:Address', ns_map).text 2244 2245 message_bindings = [] 2246 message_binding_set = etree_xml.xpath('./taxii_11:Message_Binding', namespaces=ns_map) 2247 for mb in message_binding_set: 2248 message_bindings.append(mb.text) 2249 2250 supported_contents = [] 2251 supported_contents_set = etree_xml.xpath('./taxii_11:Content_Binding', namespaces=ns_map) 2252 for cb in supported_contents_set: 2253 supported_contents.append(ContentBinding.from_etree(cb)) 2254 2255 return ReceivingInboxService(proto, addr, message_bindings, supported_contents) 2256 2257 @staticmethod 2258 def from_dict(d): 2259 kwargs = {} 2260 kwargs['inbox_protocol'] = d['inbox_protocol'] 2261 kwargs['inbox_address'] = d['inbox_address'] 2262 kwargs['inbox_message_bindings'] = d['inbox_message_bindings'] 2263 kwargs['supported_contents'] = [] 2264 for binding in d['supported_contents']: 2265 kwargs['supported_contents'].append(ContentBinding.from_dict(binding)) 2266 2267 return ReceivingInboxService(**kwargs) 2268 2269 2270class PollRequest(TAXIIRequestMessage): 2271 2272 """ 2273 A TAXII Poll Request message. 2274 2275 Arguments: 2276 message_id (str): A value identifying this message. **Required** 2277 extended_headers (dict): A dictionary of name/value pairs for 2278 use as Extended Headers. **Optional** 2279 collection_name (str): the name of the TAXII Data Collection that is being 2280 polled. **Required** 2281 exclusive_begin_timestamp_label (datetime): a Timestamp Label 2282 indicating the beginning of the range of TAXII Data Feed content the 2283 requester wishes to receive. **Optional for a Data Feed, Prohibited 2284 for a Data Set** 2285 inclusive_end_timestamp_label (datetime): a Timestamp Label 2286 indicating the end of the range of TAXII Data Feed content the 2287 requester wishes to receive. **Optional for a Data Feed, Probited 2288 for a Data Set** 2289 subscription_id (str): the existing subscription the Consumer 2290 wishes to poll. **Optional** 2291 poll_parameters (list of PollParameters objects): the poll parameters 2292 for this request. **Optional** 2293 2294 Exactly one of ``subscription_id`` and ``poll_parameters`` is **Required**. 2295 """ 2296 message_type = MSG_POLL_REQUEST 2297 2298 def __init__(self, message_id, extended_headers=None, 2299 collection_name=None, exclusive_begin_timestamp_label=None, 2300 inclusive_end_timestamp_label=None, subscription_id=None, 2301 poll_parameters=None): 2302 super(PollRequest, self).__init__(message_id, extended_headers=extended_headers) 2303 self.collection_name = collection_name 2304 self.exclusive_begin_timestamp_label = exclusive_begin_timestamp_label 2305 self.inclusive_end_timestamp_label = inclusive_end_timestamp_label 2306 self.subscription_id = subscription_id 2307 self.poll_parameters = poll_parameters 2308 2309 if subscription_id is None and poll_parameters is None: 2310 raise ValueError('One of subscription_id or poll_parameters must not be None') 2311 if subscription_id is not None and poll_parameters is not None: 2312 raise ValueError('Only one of subscription_id and poll_parameters can be present') 2313 2314 @TAXIIMessage.in_response_to.setter 2315 def in_response_to(self, value): 2316 do_check(value, 'in_response_to', value_tuple=(None, None), can_be_none=True) 2317 self._in_response_to = value 2318 2319 @property 2320 def collection_name(self): 2321 return self._collection_name 2322 2323 @collection_name.setter 2324 def collection_name(self, value): 2325 do_check(value, 'collection_name', regex_tuple=uri_regex) 2326 self._collection_name = value 2327 2328 @property 2329 def exclusive_begin_timestamp_label(self): 2330 return self._exclusive_begin_timestamp_label 2331 2332 @exclusive_begin_timestamp_label.setter 2333 def exclusive_begin_timestamp_label(self, value): 2334 value = check_timestamp_label(value, 'exclusive_begin_timestamp_label', can_be_none=True) 2335 self._exclusive_begin_timestamp_label = value 2336 2337 @property 2338 def inclusive_end_timestamp_label(self): 2339 return self._inclusive_end_timestamp_label 2340 2341 @inclusive_end_timestamp_label.setter 2342 def inclusive_end_timestamp_label(self, value): 2343 value = check_timestamp_label(value, 'inclusive_end_timestamp_label', can_be_none=True) 2344 self._inclusive_end_timestamp_label = value 2345 2346 @property 2347 def subscription_id(self): 2348 return self._subscription_id 2349 2350 @subscription_id.setter 2351 def subscription_id(self, value): 2352 do_check(value, 'subscription_id', regex_tuple=uri_regex, can_be_none=True) 2353 self._subscription_id = value 2354 2355 @property 2356 def poll_parameters(self): 2357 return self._poll_parameters 2358 2359 @poll_parameters.setter 2360 def poll_parameters(self, value): 2361 do_check(value, 'poll_parameters', type=PollParameters, can_be_none=True) 2362 self._poll_parameters = value 2363 2364 def to_etree(self): 2365 xml = super(PollRequest, self).to_etree() 2366 xml.attrib['collection_name'] = self.collection_name 2367 2368 if self.exclusive_begin_timestamp_label is not None: 2369 ebt = etree.SubElement(xml, '{%s}Exclusive_Begin_Timestamp' % ns_map['taxii_11'], nsmap=ns_map) 2370 # TODO: Add TZ Info 2371 ebt.text = self.exclusive_begin_timestamp_label.isoformat() 2372 2373 if self.inclusive_end_timestamp_label is not None: 2374 iet = etree.SubElement(xml, '{%s}Inclusive_End_Timestamp' % ns_map['taxii_11'], nsmap=ns_map) 2375 # TODO: Add TZ Info 2376 iet.text = self.inclusive_end_timestamp_label.isoformat() 2377 2378 if self.subscription_id is not None: 2379 si = etree.SubElement(xml, '{%s}Subscription_ID' % ns_map['taxii_11'], nsmap=ns_map) 2380 si.text = self.subscription_id 2381 2382 if self.poll_parameters is not None: 2383 xml.append(self.poll_parameters.to_etree()) 2384 2385 return xml 2386 2387 def to_dict(self): 2388 d = super(PollRequest, self).to_dict() 2389 d['collection_name'] = self.collection_name 2390 if self.subscription_id is not None: 2391 d['subscription_id'] = self.subscription_id 2392 if self.exclusive_begin_timestamp_label is not None: # TODO: Add TZ Info 2393 d['exclusive_begin_timestamp_label'] = self.exclusive_begin_timestamp_label.isoformat() 2394 if self.inclusive_end_timestamp_label is not None: # TODO: Add TZ Info 2395 d['inclusive_end_timestamp_label'] = self.inclusive_end_timestamp_label.isoformat() 2396 d['poll_parameters'] = None 2397 if self.poll_parameters is not None: 2398 d['poll_parameters'] = self.poll_parameters.to_dict() 2399 return d 2400 2401 def to_text(self, line_prepend=''): 2402 s = super(PollRequest, self).to_text(line_prepend) 2403 s += line_prepend + " Collection Name: %s\n" % self.collection_name 2404 if self.subscription_id: 2405 s += line_prepend + " Subscription ID: %s\n" % self.subscription_id 2406 s += line_prepend + " Excl. Begin TS Label: %s\n" % self.exclusive_begin_timestamp_label 2407 s += line_prepend + " Incl. End TS Label: %s\n" % self.inclusive_end_timestamp_label 2408 if self.poll_parameters: 2409 s += self.poll_parameters.to_text(line_prepend + STD_INDENT) 2410 2411 return s 2412 2413 @classmethod 2414 def from_etree(cls, etree_xml): 2415 kwargs = {} 2416 kwargs['collection_name'] = get_required(etree_xml, './@collection_name', ns_map) 2417 2418 kwargs['exclusive_begin_timestamp_label'] = None 2419 2420 begin_ts_text = get_optional_text(etree_xml, './taxii_11:Exclusive_Begin_Timestamp', ns_map) 2421 if begin_ts_text: 2422 kwargs['exclusive_begin_timestamp_label'] = parse_datetime_string(begin_ts_text) 2423 2424 end_ts_text = get_optional_text(etree_xml, './taxii_11:Inclusive_End_Timestamp', ns_map) 2425 if end_ts_text: 2426 kwargs['inclusive_end_timestamp_label'] = parse_datetime_string(end_ts_text) 2427 2428 poll_parameter_el = get_optional(etree_xml, './taxii_11:Poll_Parameters', ns_map) 2429 if poll_parameter_el is not None: 2430 kwargs['poll_parameters'] = PollParameters.from_etree(poll_parameter_el) 2431 2432 kwargs['subscription_id'] = get_optional_text(etree_xml, './taxii_11:Subscription_ID', ns_map) 2433 2434 msg = super(PollRequest, cls).from_etree(etree_xml, **kwargs) 2435 return msg 2436 2437 @classmethod 2438 def from_dict(cls, d): 2439 kwargs = {} 2440 kwargs['collection_name'] = d['collection_name'] 2441 2442 kwargs['subscription_id'] = d.get('subscription_id') 2443 2444 kwargs['exclusive_begin_timestamp_label'] = None 2445 if 'exclusive_begin_timestamp_label' in d: 2446 kwargs['exclusive_begin_timestamp_label'] = parse_datetime_string(d['exclusive_begin_timestamp_label']) 2447 2448 kwargs['inclusive_end_timestamp_label'] = None 2449 if 'inclusive_end_timestamp_label' in d: 2450 kwargs['inclusive_end_timestamp_label'] = parse_datetime_string(d['inclusive_end_timestamp_label']) 2451 2452 kwargs['poll_parameters'] = None 2453 if 'poll_parameters' in d and d['poll_parameters'] is not None: 2454 kwargs['poll_parameters'] = PollParameters.from_dict(d['poll_parameters']) 2455 2456 msg = super(PollRequest, cls).from_dict(d, **kwargs) 2457 return msg 2458 2459 2460class PollParameters(_GenericParameters): 2461 2462 """ 2463 The Poll Parameters component of a TAXII Poll Request message. 2464 2465 Args: 2466 response_type (str): The requested response type. Must be either 2467 :py:data:`RT_FULL` or :py:data:`RT_COUNT_ONLY`. **Optional**, 2468 defaults to :py:data:`RT_FULL` 2469 content_bindings (list of ContentBinding objects): A list of Content 2470 Bindings acceptable in response. **Optional** 2471 query (Query): The query for this poll parameters. **Optional** 2472 allow_asynch (bool): Indicates whether the client supports 2473 asynchronous polling. **Optional**, defaults to ``False`` 2474 delivery_parameters (libtaxii.messages_11.DeliveryParameters): The requested delivery 2475 parameters for this object. **Optional** 2476 2477 If ``content_bindings`` in not provided, this indicates that all 2478 bindings are accepted as a response. 2479 """ 2480 name = 'Poll_Parameters' 2481 2482 def __init__(self, response_type=RT_FULL, content_bindings=None, 2483 query=None, allow_asynch=False, delivery_parameters=None): 2484 super(PollParameters, self).__init__(response_type, content_bindings, query) 2485 self.allow_asynch = allow_asynch 2486 self.delivery_parameters = delivery_parameters 2487 2488 @property 2489 def delivery_parameters(self): 2490 return self._delivery_parameters 2491 2492 @delivery_parameters.setter 2493 def delivery_parameters(self, value): 2494 do_check(value, 'delivery_parameters', type=DeliveryParameters, can_be_none=True) 2495 self._delivery_parameters = value 2496 2497 @property 2498 def allow_asynch(self): 2499 return self._allow_asynch 2500 2501 @allow_asynch.setter 2502 def allow_asynch(self, value): 2503 do_check(value, 'allow_asynch', value_tuple=(True, False), can_be_none=True) 2504 self._allow_asynch = value 2505 2506 def to_etree(self): 2507 xml = super(PollParameters, self).to_etree() 2508 2509 if self.allow_asynch is not None: 2510 xml.attrib['allow_asynch'] = str(self.allow_asynch).lower() 2511 2512 if self.delivery_parameters is not None: 2513 xml.append(self.delivery_parameters.to_etree()) 2514 return xml 2515 2516 def to_dict(self): 2517 d = super(PollParameters, self).to_dict() 2518 if self.allow_asynch is not None: 2519 d['allow_asynch'] = str(self.allow_asynch).lower() 2520 d['delivery_parameters'] = None 2521 if self.delivery_parameters is not None: 2522 d['delivery_parameters'] = self.delivery_parameters.to_dict() 2523 return d 2524 2525 def to_text(self, line_prepend=''): 2526 s = super(PollParameters, self).to_text(line_prepend) 2527 if self.allow_asynch: 2528 s += line_prepend + " Allow Asynch: %s\n" % self.allow_asynch 2529 if self.delivery_parameters: 2530 s += self.delivery_parameters.to_text(line_prepend + STD_INDENT) 2531 return s 2532 2533 @classmethod 2534 def from_etree(cls, etree_xml): 2535 poll_parameters = super(PollParameters, cls).from_etree(etree_xml) 2536 2537 allow_asynch_el = get_optional(etree_xml, './@allow_asynch', ns_map) 2538 poll_parameters.allow_asynch = allow_asynch_el == 'true' 2539 2540 delivery_parameters_el = get_optional(etree_xml, './taxii_11:Delivery_Parameters', ns_map) 2541 if delivery_parameters_el is not None: 2542 poll_parameters.delivery_parameters = DeliveryParameters.from_etree(delivery_parameters_el) 2543 2544 return poll_parameters 2545 2546 @classmethod 2547 def from_dict(cls, d): 2548 poll_parameters = super(PollParameters, cls).from_dict(d) 2549 2550 aa = d.get('allow_asynch') 2551 if aa is not None: 2552 poll_parameters.allow_asynch = aa == 'true' 2553 2554 delivery_parameters = d.get('delivery_parameters') 2555 if delivery_parameters is not None: 2556 poll_parameters.delivery_parameters = DeliveryParameters.from_dict(delivery_parameters) 2557 2558 return poll_parameters 2559 2560 2561class PollResponse(TAXIIMessage): 2562 2563 """ 2564 A TAXII Poll Response message. 2565 2566 Args: 2567 message_id (str): A value identifying this message. **Required** 2568 in_response_to (str): Contains the Message ID of the message to 2569 which this is a response. **Optional** 2570 extended_headers (dict): A dictionary of name/value pairs for 2571 use as Extended Headers. **Optional** 2572 collection_name (str): the name of the TAXII Data Collection that was 2573 polled. **Required** 2574 exclusive_begin_timestamp_label (datetime): a Timestamp Label 2575 indicating the beginning of the range this response covers. 2576 **Optional for a Data Feed, Prohibited for a Data Set** 2577 inclusive_end_timestamp_label (datetime): a Timestamp Label 2578 indicating the end of the range this response covers. **Optional 2579 for a Data Feed, Prohibited for a Data Set** 2580 subscription_id (str): the Subscription ID for which this content 2581 is being provided. **Optional** 2582 message (str): additional information for the message recipient. 2583 **Optional** 2584 content_blocks (list of ContentBlock): piece of content 2585 and additional information related to the content. **Optional** 2586 more (bool): Whether there are more result parts. **Optional**, defaults 2587 to ``False`` 2588 result_id (str): The ID of this result. **Optional** 2589 result_part_number (int): The result part number of this response. 2590 **Optional** 2591 record_count (RecordCount): The number of records and whether 2592 the count is a lower bound. **Optional** 2593 """ 2594 message_type = MSG_POLL_RESPONSE 2595 2596 def __init__(self, message_id, in_response_to, extended_headers=None, 2597 collection_name=None, exclusive_begin_timestamp_label=None, 2598 inclusive_end_timestamp_label=None, subscription_id=None, 2599 message=None, content_blocks=None, more=False, result_id=None, 2600 result_part_number=1, record_count=None): 2601 super(PollResponse, self).__init__(message_id, in_response_to, extended_headers) 2602 self.collection_name = collection_name 2603 self.exclusive_begin_timestamp_label = exclusive_begin_timestamp_label 2604 self.inclusive_end_timestamp_label = inclusive_end_timestamp_label 2605 self.subscription_id = subscription_id 2606 self.message = message 2607 self.content_blocks = content_blocks or [] 2608 self.more = more 2609 self.result_part_number = result_part_number 2610 self.result_id = result_id 2611 self.record_count = record_count 2612 2613 @TAXIIMessage.in_response_to.setter 2614 def in_response_to(self, value): 2615 do_check(value, 'in_response_to', regex_tuple=uri_regex) 2616 self._in_response_to = value 2617 2618 @property 2619 def collection_name(self): 2620 return self._collection_name 2621 2622 @collection_name.setter 2623 def collection_name(self, value): 2624 do_check(value, 'collection_name', regex_tuple=uri_regex) 2625 self._collection_name = value 2626 2627 @property 2628 def inclusive_end_timestamp_label(self): 2629 return self._inclusive_end_timestamp_label 2630 2631 @inclusive_end_timestamp_label.setter 2632 def inclusive_end_timestamp_label(self, value): 2633 value = check_timestamp_label(value, 'inclusive_end_timestamp_label', can_be_none=True) 2634 self._inclusive_end_timestamp_label = value 2635 2636 @property 2637 def inclusive_begin_timestamp_label(self): 2638 return self._inclusive_begin_timestamp_label 2639 2640 @inclusive_begin_timestamp_label.setter 2641 def inclusive_begin_timestamp_label(self, value): 2642 value = check_timestamp_label(value, 'inclusive_begin_timestamp_label', can_be_none=True) 2643 self._inclusive_begin_timestamp_label = value 2644 2645 @property 2646 def subscription_id(self): 2647 return self._subscription_id 2648 2649 @subscription_id.setter 2650 def subscription_id(self, value): 2651 do_check(value, 'subscription_id', regex_tuple=uri_regex, can_be_none=True) 2652 self._subscription_id = value 2653 2654 @property 2655 def content_blocks(self): 2656 return self._content_blocks 2657 2658 @content_blocks.setter 2659 def content_blocks(self, value): 2660 do_check(value, 'content_blocks', type=ContentBlock) 2661 self._content_blocks = value 2662 2663 @property 2664 def more(self): 2665 return self._more 2666 2667 @more.setter 2668 def more(self, value): 2669 do_check(value, 'more', value_tuple=(True, False)) 2670 self._more = value 2671 2672 @property 2673 def result_id(self): 2674 return self._result_id 2675 2676 @result_id.setter 2677 def result_id(self, value): 2678 do_check(value, 'result_id', regex_tuple=uri_regex, can_be_none=True) 2679 self._result_id = value 2680 2681 @property 2682 def result_part_number(self): 2683 return self._result_part_number 2684 2685 @result_part_number.setter 2686 def result_part_number(self, value): 2687 do_check(value, 'result_part_number', type=int, can_be_none=True) 2688 self._result_part_number = value 2689 2690 @property 2691 def record_count(self): 2692 return self._record_count 2693 2694 @record_count.setter 2695 def record_count(self, value): 2696 do_check(value, 'record_count', type=RecordCount, can_be_none=True) 2697 self._record_count = value 2698 2699 def to_etree(self): 2700 xml = super(PollResponse, self).to_etree() 2701 xml.attrib['collection_name'] = self.collection_name 2702 if self.result_id is not None: 2703 xml.attrib['result_id'] = self.result_id 2704 2705 if self.more is not None: 2706 xml.attrib['more'] = str(self.more).lower() 2707 2708 if self.result_part_number is not None: 2709 xml.attrib['result_part_number'] = str(self.result_part_number) 2710 2711 if self.subscription_id is not None: 2712 si = etree.SubElement(xml, '{%s}Subscription_ID' % ns_map['taxii_11']) 2713 si.text = self.subscription_id 2714 2715 if self.exclusive_begin_timestamp_label: 2716 ibt = etree.SubElement(xml, '{%s}Exclusive_Begin_Timestamp' % ns_map['taxii_11']) 2717 ibt.text = self.exclusive_begin_timestamp_label.isoformat() 2718 2719 if self.inclusive_end_timestamp_label: 2720 iet = etree.SubElement(xml, '{%s}Inclusive_End_Timestamp' % ns_map['taxii_11']) 2721 iet.text = self.inclusive_end_timestamp_label.isoformat() 2722 2723 if self.record_count: 2724 xml.append(self.record_count.to_etree()) 2725 2726 if self.message is not None: 2727 m = etree.SubElement(xml, '{%s}Message' % ns_map['taxii_11']) 2728 m.text = self.message 2729 2730 for block in self.content_blocks: 2731 xml.append(block.to_etree()) 2732 2733 return xml 2734 2735 def to_dict(self): 2736 d = super(PollResponse, self).to_dict() 2737 2738 d['collection_name'] = self.collection_name 2739 d['more'] = self.more 2740 d['result_id'] = self.result_id 2741 d['result_part_number'] = self.result_part_number 2742 if self.record_count is not None: 2743 d['record_count'] = self.record_count.to_dict() 2744 if self.subscription_id is not None: 2745 d['subscription_id'] = self.subscription_id 2746 if self.message is not None: 2747 d['message'] = self.message 2748 if self.exclusive_begin_timestamp_label is not None: 2749 d['exclusive_begin_timestamp_label'] = self.exclusive_begin_timestamp_label.isoformat() 2750 if self.inclusive_end_timestamp_label is not None: 2751 d['inclusive_end_timestamp_label'] = self.inclusive_end_timestamp_label.isoformat() 2752 d['content_blocks'] = [] 2753 for block in self.content_blocks: 2754 d['content_blocks'].append(block.to_dict()) 2755 2756 return d 2757 2758 def to_text(self, line_prepend=''): 2759 s = super(PollResponse, self).to_text(line_prepend) 2760 s += line_prepend + " Collection Name: %s\n" % self.collection_name 2761 s += line_prepend + " More: %s\n" % self.more 2762 s += line_prepend + " Result ID: %s\n" % self.result_id 2763 if self.result_part_number: 2764 s += line_prepend + " Result Part Num: %s\n" % self.result_part_number 2765 if self.record_count: 2766 s += self.record_count.to_text(line_prepend + STD_INDENT) 2767 if self.subscription_id: 2768 s += line_prepend + " Subscription ID: %s\n" % self.subscription_id 2769 if self.message: 2770 s += line_prepend + " Message: %s\n" % self.message 2771 if self.exclusive_begin_timestamp_label: 2772 s += line_prepend + " Excl. Begin TS Label: %s\n" % self.exclusive_begin_timestamp_label.isoformat() 2773 if self.inclusive_end_timestamp_label: 2774 s += line_prepend + " Incl. End TS Label: %s\n" % self.inclusive_end_timestamp_label.isoformat() 2775 for cb in self.content_blocks: 2776 s += cb.to_text(line_prepend + STD_INDENT) 2777 return s 2778 2779 @classmethod 2780 def from_etree(cls, etree_xml): 2781 kwargs = {} 2782 2783 kwargs['collection_name'] = get_required(etree_xml, './@collection_name', ns_map) 2784 kwargs['more'] = etree_xml.attrib.get('more', 'false') == 'true' 2785 kwargs['subscription_id'] = None 2786 kwargs['result_id'] = etree_xml.attrib.get('result_id') 2787 rpn = etree_xml.attrib.get('result_part_number', None) 2788 if rpn: 2789 kwargs['result_part_number'] = int(rpn) 2790 2791 kwargs['subscription_id'] = get_optional_text(etree_xml, './taxii_11:Subscription_ID', ns_map) 2792 kwargs['message'] = get_optional_text(etree_xml, './taxii_11:Message', ns_map) 2793 2794 ebts_text = get_optional_text(etree_xml, './taxii_11:Exclusive_Begin_Timestamp', ns_map) 2795 if ebts_text: 2796 kwargs['exclusive_begin_timestamp_label'] = parse_datetime_string(ebts_text) 2797 2798 iets_text = get_optional_text(etree_xml, './taxii_11:Inclusive_End_Timestamp', ns_map) 2799 if iets_text: 2800 kwargs['inclusive_end_timestamp_label'] = parse_datetime_string(iets_text) 2801 2802 kwargs['content_blocks'] = [] 2803 for block in etree_xml.xpath('./taxii_11:Content_Block', namespaces=ns_map): 2804 kwargs['content_blocks'].append(ContentBlock.from_etree(block)) 2805 2806 record_count_el = get_optional(etree_xml, './taxii_11:Record_Count', ns_map) 2807 if record_count_el is not None: 2808 kwargs['record_count'] = RecordCount.from_etree(record_count_el) 2809 2810 msg = super(PollResponse, cls).from_etree(etree_xml, **kwargs) 2811 return msg 2812 2813 @classmethod 2814 def from_dict(cls, d): 2815 kwargs = {} 2816 kwargs['collection_name'] = d['collection_name'] 2817 kwargs['result_id'] = d.get('result_id') 2818 kwargs['result_part_number'] = d.get('result_part_number') 2819 kwargs['message'] = None 2820 if 'message' in d: 2821 kwargs['message'] = d['message'] 2822 2823 kwargs['subscription_id'] = d.get('subscription_id') 2824 kwargs['more'] = d.get('more', False) 2825 2826 kwargs['exclusive_begin_timestamp_label'] = None 2827 if 'exclusive_begin_timestamp_label' in d: 2828 kwargs['exclusive_begin_timestamp_label'] = parse_datetime_string(d['exclusive_begin_timestamp_label']) 2829 2830 kwargs['record_count'] = None 2831 if 'record_count' in d: 2832 kwargs['record_count'] = RecordCount.from_dict(d['record_count']) 2833 2834 kwargs['inclusive_end_timestamp_label'] = None 2835 if 'inclusive_end_timestamp_label' in d: 2836 kwargs['inclusive_end_timestamp_label'] = parse_datetime_string(d['inclusive_end_timestamp_label']) 2837 2838 kwargs['content_blocks'] = [] 2839 for block in d['content_blocks']: 2840 kwargs['content_blocks'].append(ContentBlock.from_dict(block)) 2841 msg = super(PollResponse, cls).from_dict(d, **kwargs) 2842 return msg 2843 2844 2845_StatusDetail = collections.namedtuple('_StatusDetail', ['name', 'required', 'type', 'multiple']) 2846_DCE_AcceptableDestination = _StatusDetail(SD_ACCEPTABLE_DESTINATION, False, str, True) 2847_IRP_MaxPartNumber = _StatusDetail(SD_MAX_PART_NUMBER, True, int, False) 2848_NF_Item = _StatusDetail(SD_ITEM, False, str, False) 2849_P_EstimatedWait = _StatusDetail(SD_ESTIMATED_WAIT, True, int, False) 2850_P_ResultId = _StatusDetail(SD_RESULT_ID, True, str, False) 2851_P_WillPush = _StatusDetail(SD_WILL_PUSH, True, bool, False) 2852_R_EstimatedWait = _StatusDetail(SD_ESTIMATED_WAIT, False, int, False) 2853_UM_SupportedBinding = _StatusDetail(SD_SUPPORTED_BINDING, False, str, True) 2854_UC_SupportedContent = _StatusDetail(SD_SUPPORTED_CONTENT, False, ContentBinding, True) 2855_UP_SupportedProtocol = _StatusDetail(SD_SUPPORTED_PROTOCOL, False, str, True) 2856_UQ_SupportedQuery = _StatusDetail(SD_SUPPORTED_QUERY, False, str, True) 2857 2858 2859status_details = { 2860 ST_ASYNCHRONOUS_POLL_ERROR: {}, 2861 ST_BAD_MESSAGE: {}, 2862 ST_DENIED: {}, 2863 ST_DESTINATION_COLLECTION_ERROR: {SD_ACCEPTABLE_DESTINATION: _DCE_AcceptableDestination}, 2864 ST_FAILURE: {}, 2865 ST_INVALID_RESPONSE_PART: {SD_MAX_PART_NUMBER: _IRP_MaxPartNumber}, 2866 ST_NETWORK_ERROR: {}, 2867 ST_NOT_FOUND: {SD_ITEM: _NF_Item}, 2868 ST_PENDING: {SD_ESTIMATED_WAIT: _P_EstimatedWait, 2869 SD_RESULT_ID: _P_ResultId, 2870 SD_WILL_PUSH: _P_WillPush}, 2871 ST_POLLING_UNSUPPORTED: {}, 2872 ST_RETRY: {SD_ESTIMATED_WAIT: _R_EstimatedWait}, 2873 ST_SUCCESS: {}, 2874 ST_UNAUTHORIZED: {}, 2875 ST_UNSUPPORTED_MESSAGE_BINDING: {SD_SUPPORTED_BINDING: _UM_SupportedBinding}, 2876 ST_UNSUPPORTED_CONTENT_BINDING: {SD_SUPPORTED_CONTENT: _UC_SupportedContent}, 2877 ST_UNSUPPORTED_PROTOCOL: {SD_SUPPORTED_PROTOCOL: _UP_SupportedProtocol}, 2878 ST_UNSUPPORTED_QUERY: {SD_SUPPORTED_QUERY: _UQ_SupportedQuery} 2879} 2880 2881 2882class StatusMessage(TAXIIMessage): 2883 2884 """ 2885 A TAXII Status message. 2886 2887 Args: 2888 message_id (str): A value identifying this message. **Required** 2889 in_response_to (str): Contains the Message ID of the message to 2890 which this is a response. **Required** 2891 extended_headers (dict): A dictionary of name/value pairs for 2892 use as Extended Headers. **Optional** 2893 status_type (str): One of the defined Status Types or a third-party- 2894 defined Status Type. **Required** 2895 status_detail (dict): A field for additional information about 2896 this status in a machine-readable format. **Required or Optional** 2897 depending on ``status_type``. See TAXII Specification for details. 2898 message (str): Additional information for the status. There is no 2899 expectation that this field be interpretable by a machine; it is 2900 instead targeted to a human operator. **Optional** 2901 """ 2902 message_type = MSG_STATUS_MESSAGE 2903 2904 def __init__(self, message_id, in_response_to, extended_headers=None, 2905 status_type=None, status_detail=None, message=None): 2906 super(StatusMessage, self).__init__(message_id, in_response_to, extended_headers=extended_headers) 2907 self.status_type = status_type 2908 self.status_detail = status_detail or {} 2909 self.message = message 2910 2911 @TAXIIMessage.in_response_to.setter 2912 def in_response_to(self, value): 2913 do_check(value, 'in_response_to', regex_tuple=uri_regex) 2914 self._in_response_to = value 2915 2916 @property 2917 def status_type(self): 2918 return self._status_type 2919 2920 @status_type.setter 2921 def status_type(self, value): 2922 do_check(value, 'status_type') 2923 self._status_type = value 2924 2925 @property 2926 def status_detail(self): 2927 return self._status_detail 2928 2929 @status_detail.setter 2930 def status_detail(self, value): 2931 do_check(list(value.keys()), 'status_detail.keys()', regex_tuple=uri_regex) 2932 detail_rules = status_details.get(self.status_type, {}) 2933 # Check defined status types for conformance 2934 for sd_name, rules in six.iteritems(detail_rules): 2935 do_check(value.get(sd_name, None), 2936 'status_detail[\'%s\']' % sd_name, 2937 type=rules.type, 2938 can_be_none=(not rules.required)) 2939 2940 self._status_detail = value 2941 2942 @property 2943 def message(self): 2944 return self._message 2945 2946 @message.setter 2947 def message(self, value): 2948 do_check(value, 'message', type=six.string_types, can_be_none=True) 2949 self._message = value 2950 2951 def to_etree(self): 2952 xml = super(StatusMessage, self).to_etree() 2953 xml.attrib['status_type'] = self.status_type 2954 2955 if len(self.status_detail) > 0: 2956 sd = etree.SubElement(xml, '{%s}Status_Detail' % ns_map['taxii_11']) 2957 for k, v in six.iteritems(self.status_detail): 2958 if not isinstance(v, list): 2959 v = [v] 2960 for item in v: 2961 d = etree.SubElement(sd, '{%s}Detail' % ns_map['taxii_11']) 2962 d.attrib['name'] = k 2963 if item in (True, False): 2964 d.text = str(item).lower() 2965 else: 2966 d.text = str(item) 2967 2968 if self.message is not None: 2969 m = etree.SubElement(xml, '{%s}Message' % ns_map['taxii_11']) 2970 m.text = self.message 2971 2972 return xml 2973 2974 def to_dict(self): 2975 d = super(StatusMessage, self).to_dict() 2976 d['status_type'] = self.status_type 2977 if self.status_detail is not None: 2978 d['status_detail'] = self.status_detail 2979 if self.message is not None: 2980 d['message'] = self.message 2981 return d 2982 2983 def to_text(self, line_prepend=''): 2984 s = super(StatusMessage, self).to_text(line_prepend) 2985 s += line_prepend + "Status Type: %s\n" % self.status_type 2986 for k, v in six.iteritems(self.status_detail): 2987 s += line_prepend + "Status Detail: %s = %s\n" % (k, v) 2988 if self.message: 2989 s += line_prepend + "Message: %s\n" % self.message 2990 return s 2991 2992 @classmethod 2993 def from_etree(cls, etree_xml): 2994 kwargs = {} 2995 2996 status_type = etree_xml.attrib['status_type'] 2997 kwargs['status_type'] = status_type 2998 2999 kwargs['status_detail'] = {} 3000 detail_set = etree_xml.xpath('./taxii_11:Status_Detail/taxii_11:Detail', namespaces=ns_map) 3001 for detail in detail_set: 3002 # TODO: This seems kind of hacky and should probably be improved 3003 name = detail.attrib['name'] 3004 3005 if status_type in status_details and name in status_details[status_type]: # We have information for this status detail 3006 detail_info = status_details[status_type][name] 3007 else: # We don't have information, so make something up 3008 detail_info = _StatusDetail('PlaceholderDetail', False, str, True) 3009 3010 if detail_info.type == bool: 3011 v = detail.text.lower() == 'true' 3012 else: 3013 v = detail_info.type(detail.text) 3014 if detail_info.multiple: # There can be multiple instances of this item 3015 if name not in kwargs['status_detail']: 3016 kwargs['status_detail'][name] = v 3017 else: # It already exists 3018 if not isinstance(kwargs['status_detail'][name], list): 3019 kwargs['status_detail'][name] = [kwargs['status_detail'][name]] # Make it a list 3020 kwargs['status_detail'][name].append(v) 3021 else: 3022 kwargs['status_detail'][name] = v 3023 3024 kwargs['message'] = None 3025 m_set = etree_xml.xpath('./taxii_11:Message', namespaces=ns_map) 3026 if len(m_set) > 0: 3027 kwargs['message'] = m_set[0].text 3028 3029 msg = super(StatusMessage, cls).from_etree(etree_xml, **kwargs) 3030 return msg 3031 3032 @classmethod 3033 def from_dict(cls, d): 3034 kwargs = {} 3035 kwargs['status_type'] = d['status_type'] 3036 kwargs['status_detail'] = d.get('status_detail') 3037 kwargs['message'] = d.get('message') 3038 3039 msg = super(StatusMessage, cls).from_dict(d, **kwargs) 3040 return msg 3041 3042 3043class InboxMessage(TAXIIMessage): 3044 3045 """ 3046 A TAXII Inbox message. 3047 3048 Args: 3049 message_id (str): A value identifying this message. **Required** 3050 extended_headers (dict): A dictionary of name/value pairs for 3051 use as Extended Headers. **Optional** 3052 message (str): prose information for the message recipient. **Optional** 3053 result_id (str): the result id. **Optional** 3054 destination_collection_names (list of str): Each string indicates a 3055 destination collection name. **Optional** 3056 subscription_information (libtaxii.messages_11.SubscriptionInformation): This 3057 field is only present if this message is being sent to provide 3058 content in accordance with an existing TAXII Data Collection 3059 subscription. **Optional** 3060 record_count (RecordCount): The number of records and whether 3061 the count is a lower bound. **Optional** 3062 content_blocks (list of ContentBlock): Inbox content. **Optional** 3063 """ 3064 message_type = MSG_INBOX_MESSAGE 3065 3066 def __init__(self, message_id, in_response_to=None, extended_headers=None, 3067 message=None, result_id=None, destination_collection_names=None, 3068 subscription_information=None, record_count=None, 3069 content_blocks=None): 3070 3071 super(InboxMessage, self).__init__(message_id, extended_headers=extended_headers) 3072 self.subscription_information = subscription_information 3073 self.message = message 3074 self.result_id = result_id 3075 self.destination_collection_names = destination_collection_names or [] 3076 self.subscription_information = subscription_information 3077 self.record_count = record_count 3078 self.content_blocks = content_blocks or [] 3079 3080 @TAXIIMessage.in_response_to.setter 3081 def in_response_to(self, value): 3082 if value is not None: 3083 raise ValueError('in_response_to must be None') 3084 self._in_response_to = value 3085 3086 @property 3087 def subscription_information(self): 3088 return self._subscription_information 3089 3090 @subscription_information.setter 3091 def subscription_information(self, value): 3092 do_check(value, 'subscription_information', type=SubscriptionInformation, can_be_none=True) 3093 self._subscription_information = value 3094 3095 @property 3096 def content_blocks(self): 3097 return self._content_blocks 3098 3099 @content_blocks.setter 3100 def content_blocks(self, value): 3101 do_check(value, 'content_blocks', type=ContentBlock) 3102 self._content_blocks = value 3103 3104 @property 3105 def result_id(self): 3106 return self._result_id 3107 3108 @result_id.setter 3109 def result_id(self, value): 3110 do_check(value, 'result_id', regex_tuple=uri_regex, can_be_none=True) 3111 self._result_id = value 3112 3113 @property 3114 def destination_collection_names(self): 3115 return self._destination_collection_names 3116 3117 @destination_collection_names.setter 3118 def destination_collection_names(self, value): 3119 do_check(value, 'destination_collection_names', regex_tuple=uri_regex) 3120 self._destination_collection_names = value 3121 3122 @property 3123 def record_count(self): 3124 return self._record_count 3125 3126 @record_count.setter 3127 def record_count(self, value): 3128 do_check(value, 'record_count', type=RecordCount, can_be_none=True) 3129 self._record_count = value 3130 3131 def to_etree(self): 3132 xml = super(InboxMessage, self).to_etree() 3133 3134 if self.result_id is not None: 3135 xml.attrib['result_id'] = self.result_id 3136 3137 for dcn in self.destination_collection_names: 3138 d = etree.SubElement(xml, '{%s}Destination_Collection_Name' % ns_map['taxii_11'], nsmap=ns_map) 3139 d.text = dcn 3140 3141 if self.message is not None: 3142 m = etree.SubElement(xml, '{%s}Message' % ns_map['taxii_11']) 3143 m.text = self.message 3144 3145 if self.subscription_information is not None: 3146 xml.append(self.subscription_information.to_etree()) 3147 3148 if self.record_count is not None: 3149 xml.append(self.record_count.to_etree()) 3150 3151 for block in self.content_blocks: 3152 xml.append(block.to_etree()) 3153 3154 return xml 3155 3156 def to_dict(self): 3157 d = super(InboxMessage, self).to_dict() 3158 3159 if self.result_id is not None: 3160 d['result_id'] = self.result_id 3161 3162 d['destination_collection_names'] = [] 3163 for dcn in self.destination_collection_names: 3164 d['destination_collection_names'].append(dcn) 3165 3166 if self.message is not None: 3167 d['message'] = self.message 3168 3169 if self.subscription_information is not None: 3170 d['subscription_information'] = self.subscription_information.to_dict() 3171 3172 if self.record_count is not None: 3173 d['record_count'] = self.record_count.to_dict() 3174 3175 d['content_blocks'] = [] 3176 for block in self.content_blocks: 3177 d['content_blocks'].append(block.to_dict()) 3178 3179 return d 3180 3181 def to_text(self, line_prepend=''): 3182 s = super(InboxMessage, self).to_text(line_prepend) 3183 if self.result_id: 3184 s += line_prepend + " Result ID: %s\n" % self.result_id 3185 for dcn in self.destination_collection_names: 3186 s += line_prepend + " Destination Collection Name: %s\n" % dcn 3187 s += line_prepend + " Message: %s\n" % self.message 3188 if self.subscription_information: 3189 s += self.subscription_information.to_text(line_prepend + STD_INDENT) 3190 if self.record_count: 3191 s += self.record_count.to_text(line_prepend + STD_INDENT) 3192 s += line_prepend + " Message has %s Content Blocks\n" % len(self.content_blocks) 3193 for cb in self.content_blocks: 3194 s += cb.to_text(line_prepend + STD_INDENT) 3195 3196 return s 3197 3198 @classmethod 3199 def from_etree(cls, etree_xml): 3200 kwargs = {} 3201 3202 result_id_set = etree_xml.xpath('./@result_id') 3203 if len(result_id_set) > 0: 3204 kwargs['result_id'] = result_id_set[0] 3205 3206 kwargs['destination_collection_names'] = [] 3207 dcn_set = etree_xml.xpath('./taxii_11:Destination_Collection_Name', namespaces=ns_map) 3208 for dcn in dcn_set: 3209 kwargs['destination_collection_names'].append(dcn.text) 3210 3211 msg_set = etree_xml.xpath('./taxii_11:Message', namespaces=ns_map) 3212 if len(msg_set) > 0: 3213 kwargs['message'] = msg_set[0].text 3214 3215 subs_infos = etree_xml.xpath('./taxii_11:Source_Subscription', namespaces=ns_map) 3216 if len(subs_infos) > 0: 3217 kwargs['subscription_information'] = SubscriptionInformation.from_etree(subs_infos[0]) 3218 3219 record_count_set = etree_xml.xpath('./taxii_11:Record_Count', namespaces=ns_map) 3220 if len(record_count_set) > 0: 3221 kwargs['record_count'] = RecordCount.from_etree(record_count_set[0]) 3222 3223 content_blocks = etree_xml.xpath('./taxii_11:Content_Block', namespaces=ns_map) 3224 kwargs['content_blocks'] = [] 3225 for block in content_blocks: 3226 kwargs['content_blocks'].append(ContentBlock.from_etree(block)) 3227 3228 msg = super(InboxMessage, cls).from_etree(etree_xml, **kwargs) 3229 return msg 3230 3231 @classmethod 3232 def from_dict(cls, d): 3233 3234 kwargs = {} 3235 3236 kwargs['result_id'] = d.get('result_id') 3237 3238 kwargs['destination_collection_names'] = [] 3239 if 'destination_collection_names' in d: 3240 for dcn in d['destination_collection_names']: 3241 kwargs['destination_collection_names'].append(dcn) 3242 3243 kwargs['message'] = d.get('message') 3244 3245 kwargs['subscription_information'] = None 3246 if 'subscription_information' in d: 3247 kwargs['subscription_information'] = SubscriptionInformation.from_dict(d['subscription_information']) 3248 3249 if 'record_count' in d: 3250 kwargs['record_count'] = RecordCount.from_dict(d['record_count']) 3251 3252 kwargs['content_blocks'] = [] 3253 for block in d['content_blocks']: 3254 kwargs['content_blocks'].append(ContentBlock.from_dict(block)) 3255 3256 msg = super(InboxMessage, cls).from_dict(d, **kwargs) 3257 return msg 3258 3259 3260class SubscriptionInformation(TAXIIBase11): 3261 3262 """ 3263 The Subscription Information component of a TAXII Inbox message. 3264 3265 Arguments: 3266 collection_name (str): the name of the TAXII Data Collection from 3267 which this content is being provided. **Required** 3268 subscription_id (str): the Subscription ID for which this 3269 content is being provided. **Required** 3270 exclusive_begin_timestamp_label (datetime): a Timestamp Label 3271 indicating the beginning of the time range this Inbox Message 3272 covers. **Optional for a Data Feed, Prohibited for a Data Set** 3273 inclusive_end_timestamp_label (datetime): a Timestamp Label 3274 indicating the end of the time range this Inbox Message covers. 3275 **Optional for a Data Feed, Prohibited for a Data Set** 3276 """ 3277 3278 def __init__(self, collection_name, subscription_id, exclusive_begin_timestamp_label=None, inclusive_end_timestamp_label=None): 3279 self.collection_name = collection_name 3280 self.subscription_id = subscription_id 3281 self.exclusive_begin_timestamp_label = exclusive_begin_timestamp_label 3282 self.inclusive_end_timestamp_label = inclusive_end_timestamp_label 3283 3284 @property 3285 def collection_name(self): 3286 return self._collection_name 3287 3288 @collection_name.setter 3289 def collection_name(self, value): 3290 do_check(value, 'collection_name', regex_tuple=uri_regex) 3291 self._collection_name = value 3292 3293 @property 3294 def subscription_id(self): 3295 return self._subscription_id 3296 3297 @subscription_id.setter 3298 def subscription_id(self, value): 3299 do_check(value, 'subscription_id', regex_tuple=uri_regex) 3300 self._subscription_id = value 3301 3302 @property 3303 def exclusive_begin_timestamp_label(self): 3304 return self._exclusive_begin_timestamp_label 3305 3306 @exclusive_begin_timestamp_label.setter 3307 def exclusive_begin_timestamp_label(self, value): 3308 value = check_timestamp_label(value, 'exclusive_begin_timestamp_label', can_be_none=True) 3309 self._exclusive_begin_timestamp_label = value 3310 3311 @property 3312 def inclusive_end_timestamp_label(self): 3313 return self._inclusive_end_timestamp_label 3314 3315 @inclusive_end_timestamp_label.setter 3316 def inclusive_end_timestamp_label(self, value): 3317 value = check_timestamp_label(value, 'inclusive_end_timestamp_label', can_be_none=True) 3318 self._inclusive_end_timestamp_label = value 3319 3320 def to_etree(self): 3321 xml = etree.Element('{%s}Source_Subscription' % ns_map['taxii_11']) 3322 xml.attrib['collection_name'] = self.collection_name 3323 si = etree.SubElement(xml, '{%s}Subscription_ID' % ns_map['taxii_11']) 3324 si.text = self.subscription_id 3325 3326 if self.exclusive_begin_timestamp_label: 3327 ebtl = etree.SubElement(xml, '{%s}Exclusive_Begin_Timestamp' % ns_map['taxii_11']) 3328 ebtl.text = self.exclusive_begin_timestamp_label.isoformat() 3329 3330 if self.inclusive_end_timestamp_label: 3331 ietl = etree.SubElement(xml, '{%s}Inclusive_End_Timestamp' % ns_map['taxii_11']) 3332 ietl.text = self.inclusive_end_timestamp_label.isoformat() 3333 3334 return xml 3335 3336 def to_dict(self): 3337 d = {} 3338 d['collection_name'] = self.collection_name 3339 d['subscription_id'] = self.subscription_id 3340 if self.exclusive_begin_timestamp_label: 3341 d['exclusive_begin_timestamp_label'] = self.exclusive_begin_timestamp_label.isoformat() 3342 if self.inclusive_end_timestamp_label: 3343 d['inclusive_end_timestamp_label'] = self.inclusive_end_timestamp_label.isoformat() 3344 return d 3345 3346 def to_text(self, line_prepend=''): 3347 s = line_prepend + "=== Source Subscription ===\n" 3348 s += line_prepend + " Collection Name: %s\n" % self.collection_name 3349 s += line_prepend + " Subscription ID: %s\n" % self.subscription_id 3350 3351 if self.exclusive_begin_timestamp_label: 3352 s += line_prepend + " Excl. Begin TS Label: %s\n" % self.exclusive_begin_timestamp_label.isoformat() 3353 else: 3354 s += line_prepend + " Excl. Begin TS Label: %s\n" % None 3355 3356 if self.inclusive_end_timestamp_label: 3357 s += line_prepend + " Incl. End TS Label: %s\n" % self.inclusive_end_timestamp_label.isoformat() 3358 else: 3359 s += line_prepend + " Incl. End TS Label: %s\n" % None 3360 return s 3361 3362 @staticmethod 3363 def from_etree(etree_xml): 3364 collection_name = etree_xml.attrib['collection_name'] 3365 subscription_id = get_required(etree_xml, './taxii_11:Subscription_ID', ns_map).text 3366 3367 begin_ts_text = get_optional_text(etree_xml, './taxii_11:Exclusive_Begin_Timestamp', ns_map) 3368 ebtl = parse_datetime_string(begin_ts_text) if begin_ts_text else None 3369 3370 end_ts_text = get_optional_text(etree_xml, './taxii_11:Inclusive_End_Timestamp', ns_map) 3371 ietl = parse_datetime_string(end_ts_text) if end_ts_text else None 3372 3373 return SubscriptionInformation(collection_name, subscription_id, ebtl, ietl) 3374 3375 @staticmethod 3376 def from_dict(d): 3377 collection_name = d['collection_name'] 3378 subscription_id = d['subscription_id'] 3379 3380 ebtl = parse_datetime_string(d.get('exclusive_begin_timestamp_label')) 3381 ietl = parse_datetime_string(d.get('inclusive_end_timestamp_label')) 3382 3383 return SubscriptionInformation(collection_name, subscription_id, ebtl, ietl) 3384 3385 3386class ManageCollectionSubscriptionRequest(TAXIIRequestMessage): 3387 3388 """ 3389 A TAXII Manage Collection Subscription Request message. 3390 3391 Args: 3392 message_id (str): A value identifying this message. **Required** 3393 extended_headers (dict): A dictionary of name/value pairs for 3394 use as Extended Headers. **Optional** 3395 collection_name (str): the name of the TAXII Data Collection to which the 3396 action applies. **Required** 3397 action (str): the requested action to take. **Required** 3398 subscription_id (str): the ID of a previously created subscription. 3399 **Probibited** if ``action==``:py:data:`ACT_SUBSCRIBE`, else 3400 **Required** 3401 subscription_parameters (SubscriptionParameters): The parameters for 3402 this subscription. **Optional** 3403 push_parameters (list of PushParameter): the push parameters for this 3404 request. **Optional** Absence means push is not requested. 3405 """ 3406 3407 message_type = MSG_MANAGE_COLLECTION_SUBSCRIPTION_REQUEST 3408 3409 def __init__(self, message_id, extended_headers=None, 3410 collection_name=None, action=None, subscription_id=None, 3411 subscription_parameters=None, push_parameters=None): 3412 3413 super(ManageCollectionSubscriptionRequest, self).__init__(message_id, extended_headers=extended_headers) 3414 self.collection_name = collection_name 3415 self.action = action 3416 self.subscription_id = subscription_id 3417 self.subscription_parameters = subscription_parameters or SubscriptionParameters() 3418 self.push_parameters = push_parameters 3419 3420 @TAXIIMessage.in_response_to.setter 3421 def in_response_to(self, value): 3422 if value is not None: 3423 raise ValueError('in_response_to must be None') 3424 self._in_response_to = value 3425 3426 @property 3427 def collection_name(self): 3428 return self._collection_name 3429 3430 @collection_name.setter 3431 def collection_name(self, value): 3432 do_check(value, 'collection_name', regex_tuple=uri_regex) 3433 self._collection_name = value 3434 3435 @property 3436 def action(self): 3437 return self._action 3438 3439 @action.setter 3440 def action(self, value): 3441 do_check(value, 'action', value_tuple=ACT_TYPES) 3442 self._action = value 3443 3444 @property 3445 def subscription_id(self): 3446 return self._subscription_id 3447 3448 @subscription_id.setter 3449 def subscription_id(self, value): 3450 do_check(value, 'subscription_id', regex_tuple=uri_regex, can_be_none=True) 3451 self._subscription_id = value 3452 3453 @property 3454 def subscription_parameters(self): 3455 return self._subscription_parameters 3456 3457 @subscription_parameters.setter 3458 def subscription_parameters(self, value): 3459 do_check(value, 'subscription_parameters', type=SubscriptionParameters, can_be_none=True) 3460 self._subscription_parameters = value 3461 3462 @property 3463 def push_parameters(self): 3464 return self._push_parameters 3465 3466 @push_parameters.setter 3467 def push_parameters(self, value): 3468 do_check(value, 'push_parameters', type=PushParameters, can_be_none=True) 3469 self._push_parameters = value 3470 3471 def to_etree(self): 3472 xml = super(ManageCollectionSubscriptionRequest, self).to_etree() 3473 xml.attrib['collection_name'] = self.collection_name 3474 xml.attrib['action'] = self.action 3475 if self.subscription_id is not None: 3476 si = etree.SubElement(xml, '{%s}Subscription_ID' % ns_map['taxii_11']) 3477 si.text = self.subscription_id 3478 3479 if self.action == ACT_SUBSCRIBE: 3480 xml.append(self.subscription_parameters.to_etree()) 3481 3482 if self.action == ACT_SUBSCRIBE and self.push_parameters: 3483 xml.append(self.push_parameters.to_etree()) 3484 return xml 3485 3486 def to_dict(self): 3487 d = super(ManageCollectionSubscriptionRequest, self).to_dict() 3488 d['collection_name'] = self.collection_name 3489 d['action'] = self.action 3490 3491 if self.subscription_id is not None: 3492 d['subscription_id'] = self.subscription_id 3493 3494 if self.action == ACT_SUBSCRIBE: 3495 d['subscription_parameters'] = self.subscription_parameters.to_dict() 3496 3497 if self.action == ACT_SUBSCRIBE and self.push_parameters: 3498 d['push_parameters'] = self.push_parameters.to_dict() 3499 3500 return d 3501 3502 def to_text(self, line_prepend=''): 3503 s = super(ManageCollectionSubscriptionRequest, self).to_text(line_prepend) 3504 s += line_prepend + " Collection Name: %s\n" % self.collection_name 3505 s += line_prepend + " Action: %s\n" % self.action 3506 s += line_prepend + " Subscription ID: %s\n" % self.subscription_id 3507 3508 if self.action == ACT_SUBSCRIBE: 3509 s += self.subscription_parameters.to_text(line_prepend + STD_INDENT) 3510 3511 if self.action == ACT_SUBSCRIBE and self.push_parameters: 3512 s += self.push_parameters.to_text(line_prepend + STD_INDENT) 3513 3514 return s 3515 3516 @classmethod 3517 def from_etree(cls, etree_xml): 3518 3519 kwargs = {} 3520 kwargs['collection_name'] = get_required(etree_xml, './@collection_name', ns_map) 3521 kwargs['action'] = get_required(etree_xml, './@action', ns_map) 3522 3523 kwargs['subscription_id'] = get_optional_text(etree_xml, './taxii_11:Subscription_ID', ns_map) 3524 3525 subscription_parameters_el = get_optional(etree_xml, './taxii_11:Subscription_Parameters', ns_map) 3526 if subscription_parameters_el is not None: 3527 kwargs['subscription_parameters'] = SubscriptionParameters.from_etree(subscription_parameters_el) 3528 3529 push_parameters_el = get_optional(etree_xml, './taxii_11:Push_Parameters', ns_map) 3530 if push_parameters_el is not None: 3531 kwargs['push_parameters'] = PushParameters.from_etree(push_parameters_el) 3532 3533 msg = super(ManageCollectionSubscriptionRequest, cls).from_etree(etree_xml, **kwargs) 3534 return msg 3535 3536 @classmethod 3537 def from_dict(cls, d): 3538 kwargs = {} 3539 kwargs['collection_name'] = d['collection_name'] 3540 kwargs['action'] = d['action'] 3541 kwargs['subscription_id'] = d.get('subscription_id') 3542 3543 kwargs['subscription_parameters'] = None 3544 if 'subscription_parameters' in d: 3545 kwargs['subscription_parameters'] = SubscriptionParameters.from_dict(d['subscription_parameters']) 3546 3547 kwargs['push_parameters'] = None 3548 if 'push_parameters' in d: 3549 kwargs['push_parameters'] = PushParameters.from_dict(d['push_parameters']) 3550 3551 msg = super(ManageCollectionSubscriptionRequest, cls).from_dict(d, **kwargs) 3552 return msg 3553 3554 3555class ManageCollectionSubscriptionResponse(TAXIIMessage): 3556 3557 """ 3558 A TAXII Manage Collection Subscription Response message. 3559 3560 Args: 3561 message_id (str): A value identifying this message. **Required** 3562 in_response_to (str): Contains the Message ID of the message to 3563 which this is a response. **Required** 3564 extended_headers (dict): A dictionary of name/value pairs for 3565 use as Extended Headers. **Optional** 3566 collection_name (str): the name of the TAXII Data Collection to which 3567 the action applies. **Required** 3568 message (str): additional information for the message recipient. 3569 **Optional** 3570 subscription_instances (list of SubscriptionInstance): **Optional** 3571 """ 3572 message_type = MSG_MANAGE_COLLECTION_SUBSCRIPTION_RESPONSE 3573 3574 def __init__(self, message_id, in_response_to, extended_headers=None, collection_name=None, message=None, subscription_instances=None): 3575 super(ManageCollectionSubscriptionResponse, self).__init__(message_id, in_response_to, extended_headers=extended_headers) 3576 self.collection_name = collection_name 3577 self.message = message 3578 self.subscription_instances = subscription_instances or [] 3579 3580 @TAXIIMessage.in_response_to.setter 3581 def in_response_to(self, value): 3582 do_check(value, 'in_response_to', regex_tuple=uri_regex) 3583 self._in_response_to = value 3584 3585 @property 3586 def collection_name(self): 3587 return self._collection_name 3588 3589 @collection_name.setter 3590 def collection_name(self, value): 3591 do_check(value, 'collection_name', regex_tuple=uri_regex) 3592 self._collection_name = value 3593 3594 @property 3595 def subscription_instances(self): 3596 return self._subscription_instances 3597 3598 @subscription_instances.setter 3599 def subscription_instances(self, value): 3600 do_check(value, 'subscription_instances', type=SubscriptionInstance) 3601 self._subscription_instances = value 3602 3603 def to_etree(self): 3604 xml = super(ManageCollectionSubscriptionResponse, self).to_etree() 3605 xml.attrib['collection_name'] = self.collection_name 3606 if self.message is not None: 3607 m = etree.SubElement(xml, '{%s}Message' % ns_map['taxii_11']) 3608 m.text = self.message 3609 3610 for subscription_instance in self.subscription_instances: 3611 xml.append(subscription_instance.to_etree()) 3612 3613 return xml 3614 3615 def to_dict(self): 3616 d = super(ManageCollectionSubscriptionResponse, self).to_dict() 3617 d['collection_name'] = self.collection_name 3618 if self.message is not None: 3619 d['message'] = self.message 3620 d['subscription_instances'] = [] 3621 for subscription_instance in self.subscription_instances: 3622 d['subscription_instances'].append(subscription_instance.to_dict()) 3623 3624 return d 3625 3626 def to_text(self, line_prepend=''): 3627 s = super(ManageCollectionSubscriptionResponse, self).to_text(line_prepend) 3628 s += line_prepend + " Collection Name: %s\n" % self.collection_name 3629 s += line_prepend + " Message: %s\n" % self.message 3630 for si in self.subscription_instances: 3631 s += si.to_text(line_prepend + STD_INDENT) 3632 3633 return s 3634 3635 @classmethod 3636 def from_etree(cls, etree_xml): 3637 kwargs = {} 3638 kwargs['collection_name'] = etree_xml.attrib['collection_name'] 3639 3640 kwargs['message'] = get_optional_text(etree_xml, './taxii_11:Message', ns_map) 3641 3642 kwargs['subscription_instances'] = [] 3643 for si in etree_xml.xpath('./taxii_11:Subscription', namespaces=ns_map): 3644 kwargs['subscription_instances'].append(SubscriptionInstance.from_etree(si)) 3645 3646 msg = super(ManageCollectionSubscriptionResponse, cls).from_etree(etree_xml, **kwargs) 3647 return msg 3648 3649 @classmethod 3650 def from_dict(cls, d): 3651 kwargs = {} 3652 kwargs['collection_name'] = d['collection_name'] 3653 3654 kwargs['message'] = d.get('message') 3655 3656 kwargs['subscription_instances'] = [] 3657 for instance in d['subscription_instances']: 3658 kwargs['subscription_instances'].append(SubscriptionInstance.from_dict(instance)) 3659 3660 msg = super(ManageCollectionSubscriptionResponse, cls).from_dict(d, **kwargs) 3661 return msg 3662 3663 3664class SubscriptionInstance(TAXIIBase11): 3665 3666 """ 3667 The Subscription Instance component of the Manage Collection Subscription 3668 Response message. 3669 3670 Args: 3671 subscription_id (str): the id of the subscription. **Required** 3672 status (str): One of :py:data:`SS_ACTIVE`, :py:data:`SS_PAUSED`, or 3673 :py:data:`SS_UNSUBSCRIBED`. **Optional**, defaults to "ACTIVE" 3674 subscription_parameters (SubscriptionParameters): the parameters 3675 for this subscription. **Optional** If provided, should match 3676 the request. 3677 push_parameters (PushParameters): the push parameters for this 3678 subscription. **Optional** If provided, should match the request. 3679 poll_instances (list of PollInstance): The Poll Services that can be 3680 polled to fulfill this subscription. **Optional** 3681 """ 3682 3683 def __init__(self, subscription_id, status=SS_ACTIVE, 3684 subscription_parameters=None, push_parameters=None, 3685 poll_instances=None): 3686 self.subscription_id = subscription_id 3687 self.status = status 3688 self.subscription_parameters = subscription_parameters 3689 self.push_parameters = push_parameters 3690 self.poll_instances = poll_instances or [] 3691 3692 @property 3693 def sort_key(self): 3694 return self.subscription_id 3695 3696 @property 3697 def subscription_id(self): 3698 return self._subscription_id 3699 3700 @subscription_id.setter 3701 def subscription_id(self, value): 3702 do_check(value, 'subscription_id', regex_tuple=uri_regex) 3703 self._subscription_id = value 3704 3705 @property 3706 def status(self): 3707 return self._status 3708 3709 @status.setter 3710 def status(self, value): 3711 do_check(value, 'status', value_tuple=SS_TYPES, can_be_none=True) 3712 self._status = value 3713 3714 @property 3715 def subscription_parameters(self): 3716 return self._subscription_parameters 3717 3718 @subscription_parameters.setter 3719 def subscription_parameters(self, value): 3720 do_check(value, 'subscription_parameters', type=SubscriptionParameters, can_be_none=True) 3721 self._subscription_parameters = value 3722 3723 @property 3724 def push_parameters(self): 3725 return self._push_parameters 3726 3727 @push_parameters.setter 3728 def push_parameters(self, value): 3729 do_check(value, 'push_parameters', type=PushParameters, can_be_none=True) 3730 self._push_parameters = value 3731 3732 @property 3733 def poll_instances(self): 3734 return self._poll_instances 3735 3736 @poll_instances.setter 3737 def poll_instances(self, value): 3738 do_check(value, 'poll_instances', type=PollInstance) 3739 self._poll_instances = value 3740 3741 def to_etree(self): 3742 si = etree.Element('{%s}Subscription' % ns_map['taxii_11'], nsmap=ns_map) 3743 if self.status is not None: 3744 si.attrib['status'] = self.status 3745 3746 subs_id = etree.SubElement(si, '{%s}Subscription_ID' % ns_map['taxii_11']) 3747 subs_id.text = self.subscription_id 3748 3749 if self.subscription_parameters is not None: 3750 si.append(self.subscription_parameters.to_etree()) 3751 3752 if self.push_parameters is not None: 3753 si.append(self.push_parameters.to_etree()) 3754 3755 for pi in self.poll_instances: 3756 si.append(pi.to_etree()) 3757 3758 return si 3759 3760 def to_dict(self): 3761 d = {} 3762 d['status'] = self.status 3763 d['subscription_id'] = self.subscription_id 3764 d['subscription_parameters'] = None 3765 if self.subscription_parameters is not None: 3766 d['subscription_parameters'] = self.subscription_parameters.to_dict() 3767 3768 d['push_parameters'] = None 3769 if self.push_parameters is not None: 3770 d['push_parameters'] = self.push_parameters.to_dict() 3771 3772 d['poll_instances'] = [] 3773 for pi in self.poll_instances: 3774 d['poll_instances'].append(pi.to_dict()) 3775 3776 return d 3777 3778 def to_text(self, line_prepend=''): 3779 s = line_prepend + "=== Subscription Instance ===\n" 3780 s += line_prepend + " Status: %s\n" % self.status 3781 s += line_prepend + " Subscription ID: %s\n" % self.subscription_id 3782 if self.subscription_parameters: 3783 s += self.subscription_parameters.to_text(line_prepend + STD_INDENT) 3784 if self.push_parameters: 3785 s += self.push_parameters.to_text(line_prepend + STD_INDENT) 3786 for pi in self.poll_instances: 3787 s += pi.to_text(line_prepend + STD_INDENT) 3788 3789 return s 3790 3791 @staticmethod 3792 def from_etree(etree_xml): 3793 3794 status = get_optional(etree_xml, './@status', ns_map) 3795 3796 subscription_id = get_required(etree_xml, './taxii_11:Subscription_ID', ns_map).text 3797 3798 subscription_parameters = None 3799 subscription_parameters_el = get_optional(etree_xml, './taxii_11:Subscription_Parameters', ns_map) 3800 if subscription_parameters_el is not None: 3801 subscription_parameters = SubscriptionParameters.from_etree(subscription_parameters_el) 3802 3803 push_parameters = None 3804 push_parameters_el = get_optional(etree_xml, './taxii_11:Push_Parameters', ns_map) 3805 if push_parameters_el is not None: 3806 push_parameters = PushParameters.from_etree(push_parameters_el) 3807 3808 poll_instances = [] 3809 for pi in etree_xml.xpath('./taxii_11:Poll_Instance', namespaces=ns_map): 3810 poll_instances.append(PollInstance.from_etree(pi)) 3811 3812 return SubscriptionInstance(subscription_id, status, subscription_parameters, push_parameters, poll_instances) 3813 3814 @staticmethod 3815 def from_dict(d): 3816 subscription_id = d['subscription_id'] 3817 status = d.get('status') 3818 3819 subscription_parameters = None 3820 if d.get('subscription_parameters') is not None: 3821 subscription_parameters = SubscriptionParameters.from_dict(d['subscription_parameters']) 3822 3823 push_parameters = None 3824 if d.get('push_parameters') is not None: 3825 push_parameters = PushParameters.from_dict(d['push_parameters']) 3826 3827 poll_instances = [] 3828 if 'poll_instances' in d: 3829 for pi in d['poll_instances']: 3830 poll_instances.append(PollInstance.from_dict(pi)) 3831 3832 return SubscriptionInstance(subscription_id, status, subscription_parameters, push_parameters, poll_instances) 3833 3834 3835class PollInstance(TAXIIBase11): 3836 3837 """ 3838 The Poll Instance component of the Manage Collection Subscription 3839 Response message. 3840 3841 Args: 3842 poll_protocol (str): The protocol binding supported by this 3843 instance of a Polling Service. **Required** 3844 poll_address (str): the address of the TAXII Daemon hosting 3845 this Poll Service. **Required** 3846 poll_message_bindings (list of str): one or more message bindings 3847 that can be used when interacting with this Poll Service 3848 instance. **Required** 3849 """ 3850 3851 def __init__(self, poll_protocol, poll_address, poll_message_bindings=None): 3852 self.poll_protocol = poll_protocol 3853 self.poll_address = poll_address 3854 self.poll_message_bindings = poll_message_bindings or [] 3855 3856 @property 3857 def sort_key(self): 3858 return self.poll_address 3859 3860 @property 3861 def poll_protocol(self): 3862 return self._poll_protocol 3863 3864 @poll_protocol.setter 3865 def poll_protocol(self, value): 3866 do_check(value, 'poll_protocol', regex_tuple=uri_regex) 3867 self._poll_protocol = value 3868 3869 @property 3870 def poll_message_bindings(self): 3871 return self._poll_message_bindings 3872 3873 @poll_message_bindings.setter 3874 def poll_message_bindings(self, value): 3875 do_check(value, 'poll_message_bindings', regex_tuple=uri_regex) 3876 self._poll_message_bindings = value 3877 3878 def to_etree(self): 3879 xml = etree.Element('{%s}Poll_Instance' % ns_map['taxii_11']) 3880 3881 pb = etree.SubElement(xml, '{%s}Protocol_Binding' % ns_map['taxii_11']) 3882 pb.text = self.poll_protocol 3883 3884 a = etree.SubElement(xml, '{%s}Address' % ns_map['taxii_11']) 3885 a.text = self.poll_address 3886 3887 for binding in self.poll_message_bindings: 3888 b = etree.SubElement(xml, '{%s}Message_Binding' % ns_map['taxii_11']) 3889 b.text = binding 3890 3891 return xml 3892 3893 def to_dict(self): 3894 d = {} 3895 3896 d['poll_protocol'] = self.poll_protocol 3897 d['poll_address'] = self.poll_address 3898 d['poll_message_bindings'] = [] 3899 for binding in self.poll_message_bindings: 3900 d['poll_message_bindings'].append(binding) 3901 3902 return d 3903 3904 def to_text(self, line_prepend=''): 3905 s = line_prepend + "=== Poll Instance ===\n" 3906 s += line_prepend + " Protocol Binding: %s\n" % self.poll_protocol 3907 s += line_prepend + " Address: %s\n" % self.poll_address 3908 for mb in self.poll_message_bindings: 3909 s += line_prepend + " Message Binding: %s\n" % mb 3910 return s 3911 3912 @staticmethod 3913 def from_etree(etree_xml): 3914 poll_protocol = get_required(etree_xml, './taxii_11:Protocol_Binding', ns_map).text 3915 address = get_required(etree_xml, './taxii_11:Address', ns_map).text 3916 3917 poll_message_bindings = [] 3918 for b in etree_xml.xpath('./taxii_11:Message_Binding', namespaces=ns_map): 3919 poll_message_bindings.append(b.text) 3920 3921 return PollInstance(poll_protocol, address, poll_message_bindings) 3922 3923 @staticmethod 3924 def from_dict(d): 3925 return PollInstance(**d) 3926 3927 3928class PollFulfillmentRequest(TAXIIRequestMessage): 3929 3930 """ 3931 A TAXII Poll Fulfillment Request message. 3932 3933 Args: 3934 message_id (str): A value identifying this message. **Required** 3935 extended_headers (dict): A dictionary of name/value pairs for 3936 use as Extended Headers. **Optional** 3937 collection_name (str): the name of the TAXII Data Collection to which the 3938 action applies. **Required** 3939 result_id (str): The result id of the requested result. **Required** 3940 result_part_number (int): The part number being requested. **Required** 3941 """ 3942 message_type = MSG_POLL_FULFILLMENT_REQUEST 3943 3944 def __init__(self, message_id, extended_headers=None, 3945 collection_name=None, result_id=None, result_part_number=None): 3946 super(PollFulfillmentRequest, self).__init__(message_id, extended_headers=extended_headers) 3947 self.collection_name = collection_name 3948 self.result_id = result_id 3949 self.result_part_number = result_part_number 3950 3951 @property 3952 def collection_name(self): 3953 return self._collection_name 3954 3955 @collection_name.setter 3956 def collection_name(self, value): 3957 do_check(value, 'collection_name', regex_tuple=uri_regex) 3958 self._collection_name = value 3959 3960 @property 3961 def result_id(self): 3962 return self._result_id 3963 3964 @result_id.setter 3965 def result_id(self, value): 3966 do_check(value, 'result_id', regex_tuple=uri_regex) 3967 self._result_id = value 3968 3969 @property 3970 def result_part_number(self): 3971 return self._result_part_number 3972 3973 @result_part_number.setter 3974 def result_part_number(self, value): 3975 do_check(value, 'result_part_number', type=int) 3976 self._result_part_number = value 3977 3978 def to_etree(self): 3979 xml = super(PollFulfillmentRequest, self).to_etree() 3980 xml.attrib['collection_name'] = self.collection_name 3981 xml.attrib['result_id'] = self.result_id 3982 xml.attrib['result_part_number'] = str(self.result_part_number) 3983 return xml 3984 3985 def to_dict(self): 3986 d = super(PollFulfillmentRequest, self).to_dict() 3987 d['collection_name'] = self.collection_name 3988 d['result_id'] = self.result_id 3989 d['result_part_number'] = self.result_part_number 3990 return d 3991 3992 def to_text(self, line_prepend=''): 3993 s = super(PollFulfillmentRequest, self).to_text(line_prepend) 3994 s += line_prepend + " Collection Name: %s\n" % self.collection_name 3995 s += line_prepend + " Result ID: %s\n" % self.result_id 3996 s += line_prepend + " Result Part Number: %s\n" % self.result_part_number 3997 return s 3998 3999 @classmethod 4000 def from_etree(cls, etree_xml): 4001 4002 kwargs = {} 4003 kwargs['collection_name'] = etree_xml.attrib['collection_name'] 4004 kwargs['result_id'] = etree_xml.attrib['result_id'] 4005 kwargs['result_part_number'] = int(etree_xml.attrib['result_part_number']) 4006 4007 return super(PollFulfillmentRequest, cls).from_etree(etree_xml, **kwargs) 4008 4009 @classmethod 4010 def from_dict(cls, d): 4011 4012 kwargs = {} 4013 kwargs['collection_name'] = d['collection_name'] 4014 kwargs['result_id'] = d['result_id'] 4015 kwargs['result_part_number'] = int(d['result_part_number']) 4016 4017 return super(PollFulfillmentRequest, cls).from_dict(d, **kwargs) 4018 4019 4020######################################################## 4021# EVERYTHING BELOW HERE IS FOR BACKWARDS COMPATIBILITY # 4022######################################################## 4023 4024# Add top-level classes as nested classes for backwards compatibility 4025DiscoveryResponse.ServiceInstance = ServiceInstance 4026CollectionInformationResponse.CollectionInformation = CollectionInformation 4027CollectionInformation.PushMethod = PushMethod 4028CollectionInformation.PollingServiceInstance = PollingServiceInstance 4029CollectionInformation.SubscriptionMethod = SubscriptionMethod 4030CollectionInformation.ReceivingInboxService = ReceivingInboxService 4031ManageCollectionSubscriptionResponse.PollInstance = PollInstance 4032ManageCollectionSubscriptionResponse.SubscriptionInstance = SubscriptionInstance 4033PollRequest.PollParameters = PollParameters 4034InboxMessage.SubscriptionInformation = SubscriptionInformation 4035 4036# Constants not imported in `from constants import *` 4037 4038MSG_TYPES = MSG_TYPES_11 4039ST_TYPES = ST_TYPES_11 4040ACT_TYPES = ACT_TYPES_11 4041SS_TYPES = SS_TYPES_11 4042RT_TYPES = RT_TYPES_11 4043CT_TYPES = CT_TYPES_11 4044SVC_TYPES = SVC_TYPES_11 4045SD_TYPES = SD_TYPES_11 4046 4047from .common import (generate_message_id) 4048