1# Copyright (c) 2017, The MITRE Corporation 2# For license information, see the LICENSE.txt file 3 4""" 5Creating, handling, and parsing TAXII 1.0 messages. 6""" 7 8import six 9 10try: 11 import simplejson as json 12except ImportError: 13 import json 14import os 15import warnings 16 17from lxml import etree 18 19from .common import (parse, parse_datetime_string, append_any_content_etree, TAXIIBase, 20 get_required, get_optional, get_optional_text, parse_xml_string, 21 stringify_content) 22from .validation import do_check, uri_regex, check_timestamp_label, message_id_regex_10 23from .constants import * 24 25 26def validate_xml(xml_string): 27 """ 28 Note that this function has been deprecated. Please see 29 libtaxii.validators.SchemaValidator. 30 31 Validate XML with the TAXII XML Schema 1.0. 32 33 Args: 34 xml_string (str): The XML to validate. 35 36 Example: 37 .. code-block:: python 38 39 is_valid = tm10.validate_xml(message.to_xml()) 40 """ 41 42 warnings.warn('Call to deprecated function: libtaxii.messages_10.validate_xml()', 43 category=DeprecationWarning) 44 45 etree_xml = parse_xml_string(xml_string) 46 package_dir, package_filename = os.path.split(__file__) 47 schema_file = os.path.join(package_dir, "xsd", "TAXII_XMLMessageBinding_Schema.xsd") 48 taxii_schema_doc = parse(schema_file, allow_file=True) 49 xml_schema = etree.XMLSchema(taxii_schema_doc) 50 valid = xml_schema.validate(etree_xml) 51 if not valid: 52 return xml_schema.error_log.last_error 53 return valid 54 55 56def get_message_from_xml(xml_string, encoding='utf_8'): 57 """Create a TAXIIMessage object from an XML string. 58 59 This function automatically detects which type of Message should be created 60 based on the XML. 61 62 Args: 63 xml_string (str): The XML to parse into a TAXII message. 64 65 Example: 66 .. code-block:: python 67 68 message_xml = message.to_xml() 69 new_message = tm10.get_message_from_xml(message_xml) 70 """ 71 if isinstance(xml_string, six.binary_type): 72 xml_string = xml_string.decode(encoding, 'replace') 73 etree_xml = parse_xml_string(xml_string) 74 qn = etree.QName(etree_xml) 75 if qn.namespace != ns_map['taxii']: 76 raise ValueError('Unsupported namespace: %s' % qn.namespace) 77 78 message_type = qn.localname 79 80 if message_type == MSG_DISCOVERY_REQUEST: 81 return DiscoveryRequest.from_etree(etree_xml) 82 if message_type == MSG_DISCOVERY_RESPONSE: 83 return DiscoveryResponse.from_etree(etree_xml) 84 if message_type == MSG_FEED_INFORMATION_REQUEST: 85 return FeedInformationRequest.from_etree(etree_xml) 86 if message_type == MSG_FEED_INFORMATION_RESPONSE: 87 return FeedInformationResponse.from_etree(etree_xml) 88 if message_type == MSG_POLL_REQUEST: 89 return PollRequest.from_etree(etree_xml) 90 if message_type == MSG_POLL_RESPONSE: 91 return PollResponse.from_etree(etree_xml) 92 if message_type == MSG_STATUS_MESSAGE: 93 return StatusMessage.from_etree(etree_xml) 94 if message_type == MSG_INBOX_MESSAGE: 95 return InboxMessage.from_etree(etree_xml) 96 if message_type == MSG_MANAGE_FEED_SUBSCRIPTION_REQUEST: 97 return ManageFeedSubscriptionRequest.from_etree(etree_xml) 98 if message_type == MSG_MANAGE_FEED_SUBSCRIPTION_RESPONSE: 99 return ManageFeedSubscriptionResponse.from_etree(etree_xml) 100 101 raise ValueError('Unknown message_type: %s' % message_type) 102 103 104def get_message_from_dict(d): 105 """Create a TAXIIMessage object from a dictonary. 106 107 This function automatically detects which type of Message should be created 108 based on the 'message_type' key in the dictionary. 109 110 Args: 111 d (dict): The dictionary to build the TAXII message from. 112 113 Example: 114 .. code-block:: python 115 116 message_dict = message.to_dict() 117 new_message = tm10.get_message_from_dict(message_dict) 118 """ 119 if 'message_type' not in d: 120 raise ValueError('message_type is a required field!') 121 122 message_type = d['message_type'] 123 if message_type == MSG_DISCOVERY_REQUEST: 124 return DiscoveryRequest.from_dict(d) 125 if message_type == MSG_DISCOVERY_RESPONSE: 126 return DiscoveryResponse.from_dict(d) 127 if message_type == MSG_FEED_INFORMATION_REQUEST: 128 return FeedInformationRequest.from_dict(d) 129 if message_type == MSG_FEED_INFORMATION_RESPONSE: 130 return FeedInformationResponse.from_dict(d) 131 if message_type == MSG_POLL_REQUEST: 132 return PollRequest.from_dict(d) 133 if message_type == MSG_POLL_RESPONSE: 134 return PollResponse.from_dict(d) 135 if message_type == MSG_STATUS_MESSAGE: 136 return StatusMessage.from_dict(d) 137 if message_type == MSG_INBOX_MESSAGE: 138 return InboxMessage.from_dict(d) 139 if message_type == MSG_MANAGE_FEED_SUBSCRIPTION_REQUEST: 140 return ManageFeedSubscriptionRequest.from_dict(d) 141 if message_type == MSG_MANAGE_FEED_SUBSCRIPTION_RESPONSE: 142 return ManageFeedSubscriptionResponse.from_dict(d) 143 144 raise ValueError('Unknown message_type: %s' % message_type) 145 146 147def get_message_from_json(json_string, encoding='utf_8'): 148 """Create a TAXIIMessage object from a JSON string. 149 150 This function automatically detects which type of Message should be created 151 based on the JSON. 152 153 Args: 154 json_string (str): The JSON to parse into a TAXII message. 155 """ 156 decoded_string = json_string.decode(encoding, 'replace') 157 return get_message_from_dict(json.loads(decoded_string)) 158 159 160class TAXIIBase10(TAXIIBase): 161 version = VID_TAXII_XML_10 162 163 164class DeliveryParameters(TAXIIBase10): 165 166 """Delivery Parameters. 167 168 Args: 169 inbox_protocol (str): identifies the protocol to be used when pushing 170 TAXII Data Feed content to a Consumer's TAXII Inbox Service 171 implementation. **Required** 172 inbox_address (str): identifies the address of the TAXII Daemon hosting 173 the Inbox Service to which the Consumer requests content for this 174 TAXII Data Feed to be delivered. **Required** 175 delivery_message_binding (str): identifies the message binding to be 176 used to send pushed content for this subscription. **Required** 177 content_bindings (list of str): contains Content Binding IDs 178 indicating which types of contents the Consumer requests to 179 receive for this TAXII Data Feed. **Optional** 180 """ 181 182 # TODO: Should the default arguments of these change? I'm not sure these are 183 # actually optional 184 185 def __init__(self, inbox_protocol=None, inbox_address=None, 186 delivery_message_binding=None, content_bindings=None): 187 self.inbox_protocol = inbox_protocol 188 self.inbox_address = inbox_address 189 self.delivery_message_binding = delivery_message_binding 190 self.content_bindings = content_bindings or [] 191 192 @property 193 def sort_key(self): 194 return self.inbox_address 195 196 @property 197 def inbox_protocol(self): 198 return self._inbox_protocol 199 200 @inbox_protocol.setter 201 def inbox_protocol(self, value): 202 do_check(value, 'inbox_protocol', regex_tuple=uri_regex) 203 self._inbox_protocol = value 204 205 @property 206 def inbox_address(self): 207 return self._inbox_address 208 209 @inbox_address.setter 210 def inbox_address(self, value): 211 # TODO: Can inbox_address be validated? 212 self._inbox_address = value 213 214 @property 215 def delivery_message_binding(self): 216 return self._delivery_message_binding 217 218 @delivery_message_binding.setter 219 def delivery_message_binding(self, value): 220 do_check(value, 'delivery_message_binding', regex_tuple=uri_regex) 221 self._delivery_message_binding = value 222 223 @property 224 def content_bindings(self): 225 return self._content_bindings 226 227 @content_bindings.setter 228 def content_bindings(self, value): 229 do_check(value, 'content_bindings', regex_tuple=uri_regex) 230 self._content_bindings = value 231 232 def to_etree(self): 233 xml = etree.Element('{%s}Push_Parameters' % ns_map['taxii']) 234 235 if self.inbox_protocol is not None: 236 pb = etree.SubElement(xml, '{%s}Protocol_Binding' % ns_map['taxii']) 237 pb.text = self.inbox_protocol 238 239 if self.inbox_address is not None: 240 a = etree.SubElement(xml, '{%s}Address' % ns_map['taxii']) 241 a.text = self.inbox_address 242 243 if self.delivery_message_binding is not None: 244 mb = etree.SubElement(xml, '{%s}Message_Binding' % ns_map['taxii']) 245 mb.text = self.delivery_message_binding 246 247 for binding in self.content_bindings: 248 cb = etree.SubElement(xml, '{%s}Content_Binding' % ns_map['taxii']) 249 cb.text = binding 250 251 return xml 252 253 def to_dict(self): 254 d = {} 255 256 if self.inbox_protocol is not None: 257 d['inbox_protocol'] = self.inbox_protocol 258 259 if self.inbox_address is not None: 260 d['inbox_address'] = self.inbox_address 261 262 if self.delivery_message_binding is not None: 263 d['delivery_message_binding'] = self.delivery_message_binding 264 265 d['content_bindings'] = [] 266 for binding in self.content_bindings: 267 d['content_bindings'].append(binding) 268 269 return d 270 271 def to_text(self, line_prepend=''): 272 s = line_prepend + "=== Push Parameters ===\n" 273 s += line_prepend + " Inbox Protocol: %s\n" % self.inbox_protocol 274 s += line_prepend + " Address: %s\n" % self.inbox_address 275 s += line_prepend + " Message Binding: %s\n" % self.delivery_message_binding 276 if len(self.content_bindings) > 0: 277 s += line_prepend + " Content Bindings: Any Content\n" 278 for cb in self.content_bindings: 279 s += line_prepend + " Content Binding: %s\n" % str(cb) 280 281 return s 282 283 @staticmethod 284 def from_etree(etree_xml): 285 286 inbox_protocol = get_optional_text(etree_xml, './taxii:Protocol_Binding', ns_map) 287 inbox_address = get_optional_text(etree_xml, './taxii:Address', ns_map) 288 delivery_message_binding = get_optional_text(etree_xml, './taxii:Message_Binding', ns_map) 289 290 content_bindings = [] 291 for binding in etree_xml.xpath('./taxii:Content_Binding', namespaces=ns_map): 292 content_bindings.append(binding.text) 293 294 return DeliveryParameters(inbox_protocol, inbox_address, delivery_message_binding, content_bindings) 295 296 @staticmethod 297 def from_dict(d): 298 return DeliveryParameters(**d) 299 300 301class TAXIIMessage(TAXIIBase10): 302 303 """Encapsulate properties common to all TAXII Messages (such as headers). 304 305 This class is extended by each Message Type (e.g., DiscoveryRequest), with 306 each subclass containing subclass-specific information 307 """ 308 309 message_type = 'TAXIIMessage' 310 311 def __init__(self, message_id, in_response_to=None, extended_headers=None): 312 """Create a new TAXIIMessage 313 314 Arguments: 315 - message_id (string) - A value identifying this message. 316 - in_response_to (string) - Contains the Message ID of the message to 317 which this is a response. 318 - extended_headers (dictionary) - A dictionary of name/value pairs for 319 use as Extended Headers 320 """ 321 self.message_id = message_id 322 self.in_response_to = in_response_to 323 if extended_headers is None: 324 self.extended_headers = {} 325 else: 326 self.extended_headers = extended_headers 327 328 @property 329 def message_id(self): 330 return self._message_id 331 332 @message_id.setter 333 def message_id(self, value): 334 do_check(value, 'message_id', regex_tuple=message_id_regex_10) 335 self._message_id = value 336 337 @property 338 def in_response_to(self): 339 return self._in_response_to 340 341 @in_response_to.setter 342 def in_response_to(self, value): 343 do_check(value, 'in_response_to', regex_tuple=message_id_regex_10, can_be_none=True) 344 self._in_response_to = value 345 346 @property 347 def extended_headers(self): 348 return self._extended_headers 349 350 @extended_headers.setter 351 def extended_headers(self, value): 352 do_check(list(value.keys()), 'extended_headers.keys()', regex_tuple=uri_regex) 353 self._extended_headers = value 354 355 def to_etree(self): 356 """Creates the base etree for the TAXII Message. 357 358 Message-specific constructs must be added by each Message class. In 359 general, when converting to XML, subclasses should call this method 360 first, then create their specific XML constructs. 361 """ 362 root_elt = etree.Element('{%s}%s' % (ns_map['taxii'], self.message_type), nsmap=ns_map) 363 root_elt.attrib['message_id'] = str(self.message_id) 364 365 if self.in_response_to is not None: 366 root_elt.attrib['in_response_to'] = str(self.in_response_to) 367 368 if len(self.extended_headers) > 0: 369 eh = etree.SubElement(root_elt, '{%s}Extended_Headers' % ns_map['taxii']) 370 371 for name, value in list(self.extended_headers.items()): 372 h = etree.SubElement(eh, '{%s}Extended_Header' % ns_map['taxii']) 373 h.attrib['name'] = name 374 append_any_content_etree(h, value) 375 # h.text = value 376 return root_elt 377 378 def to_xml(self, pretty_print=False): 379 """Convert a message to XML. 380 381 Subclasses shouldn't implement this method, as it is mainly a wrapper 382 for cls.to_etree. 383 """ 384 return etree.tostring(self.to_etree(), pretty_print=pretty_print, encoding='utf-8') 385 386 def to_dict(self): 387 """Create the base dictionary for the TAXII Message. 388 389 Message-specific constructs must be added by each Message class. In 390 general, when converting to dictionary, subclasses should call this 391 method first, then create their specific dictionary constructs. 392 """ 393 d = {} 394 d['message_type'] = self.message_type 395 d['message_id'] = self.message_id 396 if self.in_response_to is not None: 397 d['in_response_to'] = self.in_response_to 398 d['extended_headers'] = {} 399 for k, v in six.iteritems(self.extended_headers): 400 if isinstance(v, etree._Element) or isinstance(v, etree._ElementTree): 401 v = etree.tostring(v, encoding='utf-8') 402 elif not isinstance(v, six.string_types): 403 v = str(v) 404 d['extended_headers'][k] = v 405 406 return d 407 408 def to_text(self, line_prepend=''): 409 s = line_prepend + "Message Type: %s\n" % self.message_type 410 s += line_prepend + "Message ID: %s" % self.message_id 411 if self.in_response_to: 412 s += "; In Response To: %s" % self.in_response_to 413 s += "\n" 414 for k, v in six.iteritems(self.extended_headers): 415 s += line_prepend + "Extended Header: %s = %s" % (k, v) 416 417 return s 418 419 @classmethod 420 def from_etree(cls, src_etree, **kwargs): 421 """Pulls properties of a TAXII Message from an etree. 422 423 Message-specific constructs must be pulled by each Message class. In 424 general, when converting from etree, subclasses should call this method 425 first, then parse their specific XML constructs. 426 """ 427 428 # Check namespace and element name of the root element 429 expected_tag = '{%s}%s' % (ns_map['taxii'], cls.message_type) 430 tag = src_etree.tag 431 if tag != expected_tag: 432 raise ValueError('%s != %s' % (tag, expected_tag)) 433 434 # Get the message ID 435 message_id = get_required(src_etree, '/taxii:*/@message_id', ns_map) 436 437 # Get in response to, if present 438 in_response_to = get_optional(src_etree, '/taxii:*/@in_response_to', ns_map) 439 if in_response_to is not None: 440 kwargs['in_response_to'] = in_response_to 441 442 # Get the Extended headers 443 extended_header_list = src_etree.xpath('/taxii:*/taxii:Extended_Headers/taxii:Extended_Header', namespaces=ns_map) 444 extended_headers = {} 445 for header in extended_header_list: 446 eh_name = header.xpath('./@name')[0] 447 # eh_value = header.text 448 if len(header) == 0: # This has string content 449 eh_value = header.text 450 else: # This has XML content 451 eh_value = header[0] 452 453 extended_headers[eh_name] = eh_value 454 455 return cls(message_id, extended_headers=extended_headers, **kwargs) 456 457 @classmethod 458 def from_xml(cls, xml): 459 """Parse a Message from XML. 460 461 Subclasses shouldn't implemnet this method, as it is mainly a wrapper 462 for cls.from_etree. 463 """ 464 etree_xml = parse_xml_string(xml) 465 return cls.from_etree(etree_xml) 466 467 @classmethod 468 def from_dict(cls, d, **kwargs): 469 """Pulls properties of a TAXII Message from a dictionary. 470 471 Message-specific constructs must be pulled by each Message class. In 472 general, when converting from dictionary, subclasses should call this 473 method first, then parse their specific dictionary constructs. 474 """ 475 message_type = d['message_type'] 476 if message_type != cls.message_type: 477 raise ValueError('%s != %s' % (message_type, cls.message_type)) 478 message_id = d['message_id'] 479 extended_headers = {} 480 for k, v in six.iteritems(d['extended_headers']): 481 try: 482 v = parse(v, allow_file=False) 483 except etree.XMLSyntaxError: 484 pass 485 extended_headers[k] = v 486 487 in_response_to = d.get('in_response_to') 488 if in_response_to: 489 kwargs['in_response_to'] = in_response_to 490 491 return cls(message_id, extended_headers=extended_headers, **kwargs) 492 493 @classmethod 494 def from_json(cls, json_string): 495 return cls.from_dict(json.loads(json_string)) 496 497 498class ContentBlock(TAXIIBase10): 499 500 """A TAXII Content Block. 501 502 Args: 503 content_binding (str): a Content Binding ID or nesting expression 504 indicating the type of content contained in the Content field of this 505 Content Block. **Required** 506 content (string or etree): a piece of content of the type specified 507 by the Content Binding. **Required** 508 timestamp_label (datetime): the Timestamp Label associated with this 509 Content Block. **Optional** 510 padding (string): an arbitrary amount of padding for this Content 511 Block. **Optional** 512 """ 513 514 NAME = 'Content_Block' 515 516 def __init__(self, content_binding, content, timestamp_label=None, padding=None): 517 self.content_binding = content_binding 518 self.content = content 519 self.timestamp_label = timestamp_label 520 self.padding = padding 521 522 @property 523 def sort_key(self): 524 return self.content[:25] 525 526 @property 527 def content_binding(self): 528 return self._content_binding 529 530 @content_binding.setter 531 def content_binding(self, value): 532 do_check(value, 'content_binding', regex_tuple=uri_regex) 533 self._content_binding = value 534 535 @property 536 def content(self): 537 if self.content_is_xml: 538 return etree.tostring(self._content, encoding='utf-8') 539 else: 540 return self._content 541 542 @content.setter 543 def content(self, value): 544 do_check(value, 'content') # Just check for not None 545 self._content, self.content_is_xml = stringify_content(value) 546 547 @property 548 def content_is_xml(self): 549 return self._content_is_xml 550 551 @content_is_xml.setter 552 def content_is_xml(self, value): 553 do_check(value, 'content_is_xml', value_tuple=(True, False)) 554 self._content_is_xml = value 555 556 @property 557 def timestamp_label(self): 558 return self._timestamp_label 559 560 @timestamp_label.setter 561 def timestamp_label(self, value): 562 value = check_timestamp_label(value, 'timestamp_label', can_be_none=True) 563 self._timestamp_label = value 564 565 def to_etree(self): 566 block = etree.Element('{%s}Content_Block' % ns_map['taxii'], nsmap=ns_map) 567 cb = etree.SubElement(block, '{%s}Content_Binding' % ns_map['taxii']) 568 cb.text = self.content_binding 569 c = etree.SubElement(block, '{%s}Content' % ns_map['taxii']) 570 571 if self.content_is_xml: 572 c.append(self._content) 573 else: 574 c.text = self._content 575 576 if self.timestamp_label: 577 tl = etree.SubElement(block, '{%s}Timestamp_Label' % ns_map['taxii']) 578 tl.text = self.timestamp_label.isoformat() 579 580 if self.padding is not None: 581 p = etree.SubElement(block, '{%s}Padding' % ns_map['taxii']) 582 p.text = self.padding 583 584 return block 585 586 def to_dict(self): 587 block = {} 588 block['content_binding'] = self.content_binding 589 590 if self.content_is_xml: 591 block['content'] = etree.tostring(self._content, encoding='utf-8') 592 else: 593 block['content'] = self._content 594 block['content_is_xml'] = self.content_is_xml 595 596 if self.timestamp_label: 597 block['timestamp_label'] = self.timestamp_label.isoformat() 598 599 if self.padding is not None: 600 block['padding'] = self.padding 601 602 return block 603 604 def to_text(self, line_prepend=''): 605 s = line_prepend + "=== Content Block ===\n" 606 s += line_prepend + " Content Binding: %s\n" % self.content_binding 607 s += line_prepend + " Content Length: %s\n" % len(self.content) 608 s += line_prepend + " (Only content length is shown for brevity)\n" 609 if self.timestamp_label: 610 s += line_prepend + " Timestamp Label: %s\n" % self.timestamp_label.isoformat() 611 s += line_prepend + " Padding: %s\n" % self.padding 612 613 return s 614 615 @staticmethod 616 def from_etree(etree_xml): 617 kwargs = {} 618 619 kwargs['content_binding'] = get_required(etree_xml, './taxii:Content_Binding', ns_map).text 620 621 kwargs['padding'] = get_optional_text(etree_xml, './taxii:Padding', ns_map) 622 623 ts_text = get_optional_text(etree_xml, './taxii:Timestamp_Label', ns_map) 624 if ts_text: 625 kwargs['timestamp_label'] = parse_datetime_string(ts_text) 626 627 content = get_required(etree_xml, './taxii:Content', ns_map) 628 629 if len(content) == 0: # This has string content 630 kwargs['content'] = content.text 631 else: # This has XML content 632 kwargs['content'] = content[0] 633 634 return ContentBlock(**kwargs) 635 636 637 @staticmethod 638 def from_dict(d): 639 kwargs = {} 640 kwargs['content_binding'] = d['content_binding'] 641 kwargs['padding'] = d.get('padding') 642 643 if d.get('timestamp_label'): 644 kwargs['timestamp_label'] = parse_datetime_string(d['timestamp_label']) 645 646 is_xml = d.get('content_is_xml', False) 647 if is_xml: 648 #FIXME: to parse or not to parse the content - this should be configurable 649 kwargs['content'] = parse(d['content'], allow_file=False) 650 else: 651 kwargs['content'] = d['content'] 652 653 cb = ContentBlock(**kwargs) 654 return cb 655 656 @classmethod 657 def from_json(cls, json_string): 658 return cls.from_dict(json.loads(json_string)) 659 660 661# TAXII Message Classes # 662 663class DiscoveryRequest(TAXIIMessage): 664 665 """ 666 A TAXII Discovery Request message. 667 668 Args: 669 message_id (str): A value identifying this message. **Required** 670 extended_headers (dict): A dictionary of name/value pairs for 671 use as Extended Headers. **Optional** 672 """ 673 674 message_type = MSG_DISCOVERY_REQUEST 675 676 @TAXIIMessage.in_response_to.setter 677 def in_response_to(self, value): 678 if value: 679 raise ValueError('in_response_to must be None') 680 self._in_response_to = value 681 682 683class DiscoveryResponse(TAXIIMessage): 684 685 """ 686 A TAXII Discovery Response message. 687 688 Args: 689 message_id (str): A value identifying this message. **Required** 690 in_response_to (str): Contains the Message ID of the message to 691 which this is a response. **Optional** 692 extended_headers (dict): A dictionary of name/value pairs for 693 use as Extended Headers. **Optional** 694 service_instances (list of `ServiceInstance`): a list of 695 service instances that this response contains. **Optional** 696 """ 697 message_type = MSG_DISCOVERY_RESPONSE 698 699 def __init__(self, message_id, in_response_to, extended_headers=None, service_instances=None): 700 super(DiscoveryResponse, self).__init__(message_id, in_response_to, extended_headers) 701 self.service_instances = service_instances or [] 702 703 @TAXIIMessage.in_response_to.setter 704 def in_response_to(self, value): 705 do_check(value, 'in_response_to', regex_tuple=uri_regex) 706 self._in_response_to = value 707 708 @property 709 def service_instances(self): 710 return self._service_instances 711 712 @service_instances.setter 713 def service_instances(self, value): 714 do_check(value, 'service_instances', type=ServiceInstance) 715 self._service_instances = value 716 717 def to_etree(self): 718 xml = super(DiscoveryResponse, self).to_etree() 719 for service_instance in self.service_instances: 720 xml.append(service_instance.to_etree()) 721 return xml 722 723 def to_dict(self): 724 d = super(DiscoveryResponse, self).to_dict() 725 d['service_instances'] = [] 726 for service_instance in self.service_instances: 727 d['service_instances'].append(service_instance.to_dict()) 728 return d 729 730 def to_text(self, line_prepend=''): 731 s = super(DiscoveryResponse, self).to_text(line_prepend) 732 for si in self.service_instances: 733 s += si.to_text(line_prepend + STD_INDENT) 734 735 return s 736 737 @classmethod 738 def from_etree(cls, etree_xml): 739 msg = super(DiscoveryResponse, cls).from_etree(etree_xml) 740 msg.service_instances = [] 741 for service_instance in etree_xml.xpath('./taxii:Service_Instance', namespaces=ns_map): 742 si = ServiceInstance.from_etree(service_instance) 743 msg.service_instances.append(si) 744 return msg 745 746 @classmethod 747 def from_dict(cls, d): 748 msg = super(DiscoveryResponse, cls).from_dict(d) 749 msg.service_instances = [] 750 for service_instance in d['service_instances']: 751 si = ServiceInstance.from_dict(service_instance) 752 msg.service_instances.append(si) 753 return msg 754 755 756class ServiceInstance(TAXIIBase10): 757 758 """ 759 The Service Instance component of a TAXII Discovery Response Message. 760 761 Args: 762 service_type (string): identifies the Service Type of this 763 Service Instance. **Required** 764 services_version (string): identifies the TAXII Services 765 Specification to which this Service conforms. **Required** 766 protocol_binding (string): identifies the protocol binding 767 supported by this Service. **Required** 768 service_address (string): identifies the network address of the 769 TAXII Daemon that hosts this Service. **Required** 770 message_bindings (list of strings): identifies the message 771 bindings supported by this Service instance. **Required** 772 inbox_service_accepted_content (list of strings): identifies 773 content bindings that this Inbox Service is willing to accept. 774 **Optional** 775 available (boolean): indicates whether the identity of the 776 requester (authenticated or otherwise) is allowed to access this 777 TAXII Service. **Optional** 778 message (string): contains a message regarding this Service 779 instance. **Optional** 780 781 The ``message_bindings`` list must contain at least one value. 782 """ 783 784 def __init__(self, service_type, services_version, protocol_binding, 785 service_address, message_bindings, 786 inbox_service_accepted_content=None, available=None, 787 message=None): 788 self.service_type = service_type 789 self.services_version = services_version 790 self.protocol_binding = protocol_binding 791 self.service_address = service_address 792 self.message_bindings = message_bindings 793 self.inbox_service_accepted_content = inbox_service_accepted_content or [] 794 self.available = available 795 self.message = message 796 797 @property 798 def sort_key(self): 799 return self.service_address 800 801 @property 802 def service_type(self): 803 return self._service_type 804 805 @service_type.setter 806 def service_type(self, value): 807 do_check(value, 'service_type', value_tuple=SVC_TYPES) 808 self._service_type = value 809 810 @property 811 def services_version(self): 812 return self._services_version 813 814 @services_version.setter 815 def services_version(self, value): 816 do_check(value, 'services_version', regex_tuple=uri_regex) 817 self._services_version = value 818 819 @property 820 def protocol_binding(self): 821 return self._protocol_binding 822 823 @protocol_binding.setter 824 def protocol_binding(self, value): 825 do_check(value, 'protocol_binding', regex_tuple=uri_regex) 826 self._protocol_binding = value 827 828 @property 829 def service_address(self): 830 return self._service_address 831 832 @service_address.setter 833 def service_address(self, value): 834 self._service_address = value 835 836 @property 837 def message_bindings(self): 838 return self._message_bindings 839 840 @message_bindings.setter 841 def message_bindings(self, value): 842 do_check(value, 'message_bindings', regex_tuple=uri_regex) 843 self._message_bindings = value 844 845 @property 846 def inbox_service_accepted_content(self): 847 return self._inbox_service_accepted_content 848 849 @inbox_service_accepted_content.setter 850 def inbox_service_accepted_content(self, value): 851 do_check(value, 'inbox_service_accepted_content', regex_tuple=uri_regex) 852 self._inbox_service_accepted_content = value 853 854 @property 855 def available(self): 856 return self._available 857 858 @available.setter 859 def available(self, value): 860 do_check(value, 'available', value_tuple=(True, False), can_be_none=True) 861 self._available = value 862 863 def to_etree(self): 864 si = etree.Element('{%s}Service_Instance' % ns_map['taxii']) 865 si.attrib['service_type'] = self.service_type 866 si.attrib['service_version'] = self.services_version 867 if self.available: 868 si.attrib['available'] = str(self.available).lower() 869 870 protocol_binding = etree.SubElement(si, '{%s}Protocol_Binding' % ns_map['taxii']) 871 protocol_binding.text = self.protocol_binding 872 873 service_address = etree.SubElement(si, '{%s}Address' % ns_map['taxii']) 874 service_address.text = self.service_address 875 876 for mb in self.message_bindings: 877 message_binding = etree.SubElement(si, '{%s}Message_Binding' % ns_map['taxii']) 878 message_binding.text = mb 879 880 for cb in self.inbox_service_accepted_content: 881 content_binding = etree.SubElement(si, '{%s}Content_Binding' % ns_map['taxii']) 882 content_binding.text = cb 883 884 if self.message is not None: 885 message = etree.SubElement(si, '{%s}Message' % ns_map['taxii']) 886 message.text = self.message 887 888 return si 889 890 def to_dict(self): 891 d = {} 892 d['service_type'] = self.service_type 893 d['services_version'] = self.services_version 894 d['protocol_binding'] = self.protocol_binding 895 d['service_address'] = self.service_address 896 d['message_bindings'] = self.message_bindings 897 d['inbox_service_accepted_content'] = self.inbox_service_accepted_content 898 d['available'] = self.available 899 d['message'] = self.message 900 return d 901 902 def to_text(self, line_prepend=''): 903 s = line_prepend + "=== Service Instance===\n" 904 s += line_prepend + " Service Type: %s\n" % self.service_type 905 s += line_prepend + " Services Version: %s\n" % self.services_version 906 s += line_prepend + " Protocol Binding: %s\n" % self.protocol_binding 907 s += line_prepend + " Address: %s\n" % self.service_address 908 for mb in self.message_bindings: 909 s += line_prepend + " Message Binding: %s\n" % mb 910 if len(self.inbox_service_accepted_content) == 0: 911 s += line_prepend + " Inbox Service Accepts: %s\n" % None 912 for isac in self.inbox_service_accepted_content: 913 s += line_prepend + " Inbox Service Accepts: %s\n" % isac 914 s += line_prepend + " Available: %s\n" % self.available 915 s += line_prepend + " Message: %s\n" % self.message 916 917 return s 918 919 @classmethod 920 def from_etree(cls, etree_xml): # Expects a taxii:Service_Instance element 921 service_type = etree_xml.attrib['service_type'] 922 services_version = etree_xml.attrib['service_version'] 923 available = None 924 if etree_xml.attrib.get('available'): 925 tmp_available = etree_xml.attrib['available'] 926 available = tmp_available.lower() == 'true' 927 928 protocol_binding = get_required(etree_xml, './taxii:Protocol_Binding', ns_map).text 929 service_address = get_required(etree_xml, './taxii:Address', ns_map).text 930 931 message_bindings = [] 932 for mb in etree_xml.xpath('./taxii:Message_Binding', namespaces=ns_map): 933 message_bindings.append(mb.text) 934 935 inbox_service_accepted_contents = [] 936 for cb in etree_xml.xpath('./taxii:Content_Binding', namespaces=ns_map): 937 inbox_service_accepted_contents.append(cb.text) 938 939 message = get_optional_text(etree_xml, './taxii:Message', ns_map) 940 941 return ServiceInstance(service_type, services_version, protocol_binding, 942 service_address, message_bindings, inbox_service_accepted_contents, 943 available, message) 944 945 @staticmethod 946 def from_dict(d): 947 return ServiceInstance(**d) 948 949 950class FeedInformationRequest(TAXIIMessage): 951 952 """ 953 A TAXII Feed Information Request message. 954 955 Args: 956 message_id (str): A value identifying this message. **Required** 957 extended_headers (dict): A dictionary of name/value pairs for 958 use as Extended Headers. **Optional** 959 """ 960 961 message_type = MSG_FEED_INFORMATION_REQUEST 962 963 @TAXIIMessage.in_response_to.setter 964 def in_response_to(self, value): 965 if value: 966 raise ValueError('in_response_to must be None') 967 self._in_response_to = value 968 969 970class FeedInformationResponse(TAXIIMessage): 971 972 """ 973 A TAXII Feed Information Response message. 974 975 Args: 976 message_id (str): A value identifying this message. **Required** 977 in_response_to (str): Contains the Message ID of the message to 978 which this is a response. **Required** 979 extended_headers (dict): A dictionary of name/value pairs for 980 use as Extended Headers. **Optional** 981 feed_informations (list of FeedInformation): A list 982 of FeedInformation objects to be contained in this response. 983 **Optional** 984 """ 985 message_type = MSG_FEED_INFORMATION_RESPONSE 986 987 def __init__(self, message_id, in_response_to, extended_headers=None, feed_informations=None): 988 super(FeedInformationResponse, self).__init__(message_id, in_response_to, extended_headers=extended_headers) 989 self.feed_informations = feed_informations or [] 990 991 @TAXIIMessage.in_response_to.setter 992 def in_response_to(self, value): 993 do_check(value, 'in_response_to', regex_tuple=message_id_regex_10) 994 self._in_response_to = value 995 996 @property 997 def feed_informations(self): 998 return self._feed_informations 999 1000 @feed_informations.setter 1001 def feed_informations(self, value): 1002 do_check(value, 'feed_informations', type=FeedInformation) 1003 self._feed_informations = value 1004 1005 def to_etree(self): 1006 xml = super(FeedInformationResponse, self).to_etree() 1007 for feed in self.feed_informations: 1008 xml.append(feed.to_etree()) 1009 return xml 1010 1011 def to_dict(self): 1012 d = super(FeedInformationResponse, self).to_dict() 1013 d['feed_informations'] = [] 1014 for feed in self.feed_informations: 1015 d['feed_informations'].append(feed.to_dict()) 1016 return d 1017 1018 def to_text(self, line_prepend=''): 1019 s = super(FeedInformationResponse, self).to_text(line_prepend) 1020 for feed in self.feed_informations: 1021 s += feed.to_text(line_prepend + STD_INDENT) 1022 1023 return s 1024 1025 @classmethod 1026 def from_etree(cls, etree_xml): 1027 msg = super(FeedInformationResponse, cls).from_etree(etree_xml) 1028 msg.feed_informations = [] 1029 feed_informations = etree_xml.xpath('./taxii:Feed', namespaces=ns_map) 1030 for feed in feed_informations: 1031 msg.feed_informations.append(FeedInformation.from_etree(feed)) 1032 return msg 1033 1034 @classmethod 1035 def from_dict(cls, d): 1036 msg = super(FeedInformationResponse, cls).from_dict(d) 1037 msg.feed_informations = [] 1038 for feed in d['feed_informations']: 1039 msg.feed_informations.append(FeedInformation.from_dict(feed)) 1040 return msg 1041 1042 1043class FeedInformation(TAXIIBase10): 1044 1045 """ 1046 The Feed Information component of a TAXII Feed Information Response 1047 Message. 1048 1049 Arguments: 1050 feed_name (str): the name by which this TAXII Data Feed is 1051 identified. **Required** 1052 feed_description (str): a prose description of this TAXII 1053 Data Feed. **Required** 1054 supported_contents (list of str): Content Binding IDs 1055 indicating which types of content are currently expressed in this 1056 TAXII Data Feed. **Required** 1057 available (boolean): whether the identity of the requester 1058 (authenticated or otherwise) is allowed to access this TAXII 1059 Service. **Optional** Default: ``None``, indicating "unknown" 1060 push_methods (list of PushMethod objects): the protocols that 1061 can be used to push content via a subscription. **Optional** 1062 polling_service_instances (list of PollingServiceInstance objects): 1063 the bindings and address a Consumer can use to interact with a 1064 Poll Service instance that supports this TAXII Data Feed. 1065 **Optional** 1066 subscription_methods (list of SubscriptionMethod objects): the 1067 protocol and address of the TAXII Daemon hosting the Feed 1068 Management Service that can process subscriptions for this TAXII 1069 Data Feed. **Optional** 1070 1071 The absense of ``push_methods`` indicates no push methods. The absense 1072 of ``polling_service_instances`` indicates no polling services. At 1073 least one of ``push_methods`` and ``polling_service_instances`` must not 1074 be empty. The absense of ``subscription_methods`` indicates no 1075 subscription services. 1076 """ 1077 1078 def __init__(self, feed_name, feed_description, supported_contents, 1079 available=None, push_methods=None, 1080 polling_service_instances=None, subscription_methods=None): 1081 1082 self.feed_name = feed_name 1083 self.available = available 1084 self.feed_description = feed_description 1085 self.supported_contents = supported_contents 1086 self.push_methods = push_methods or [] 1087 self.polling_service_instances = polling_service_instances or [] 1088 self.subscription_methods = subscription_methods or [] 1089 1090 @property 1091 def sort_key(self): 1092 return self.feed_name 1093 1094 @property 1095 def feed_name(self): 1096 return self._feed_name 1097 1098 @feed_name.setter 1099 def feed_name(self, value): 1100 do_check(value, 'feed_name', regex_tuple=uri_regex) 1101 self._feed_name = value 1102 1103 @property 1104 def available(self): 1105 return self._available 1106 1107 @available.setter 1108 def available(self, value): 1109 do_check(value, 'available', value_tuple=(True, False), can_be_none=True) 1110 self._available = value 1111 1112 @property 1113 def supported_contents(self): 1114 return self._supported_contents 1115 1116 @supported_contents.setter 1117 def supported_contents(self, value): 1118 do_check(value, 'supported_contents', regex_tuple=uri_regex) 1119 self._supported_contents = value 1120 1121 @property 1122 def push_methods(self): 1123 return self._push_methods 1124 1125 @push_methods.setter 1126 def push_methods(self, value): 1127 do_check(value, 'push_methods', type=PushMethod) 1128 self._push_methods = value 1129 1130 @property 1131 def polling_service_instances(self): 1132 return self._polling_service_instances 1133 1134 @polling_service_instances.setter 1135 def polling_service_instances(self, value): 1136 do_check(value, 'polling_service_instances', type=PollingServiceInstance) 1137 self._polling_service_instances = value 1138 1139 @property 1140 def subscription_methods(self): 1141 return self._subscription_methods 1142 1143 @subscription_methods.setter 1144 def subscription_methods(self, value): 1145 do_check(value, 'subscription_methods', type=SubscriptionMethod) 1146 self._subscription_methods = value 1147 1148 def to_etree(self): 1149 f = etree.Element('{%s}Feed' % ns_map['taxii']) 1150 f.attrib['feed_name'] = self.feed_name 1151 if self.available: 1152 f.attrib['available'] = str(self.available).lower() 1153 feed_description = etree.SubElement(f, '{%s}Description' % ns_map['taxii']) 1154 feed_description.text = self.feed_description 1155 1156 for binding in self.supported_contents: 1157 cb = etree.SubElement(f, '{%s}Content_Binding' % ns_map['taxii']) 1158 cb.text = binding 1159 1160 for push_method in self.push_methods: 1161 f.append(push_method.to_etree()) 1162 1163 for polling_service in self.polling_service_instances: 1164 f.append(polling_service.to_etree()) 1165 1166 for subscription_method in self.subscription_methods: 1167 f.append(subscription_method.to_etree()) 1168 1169 return f 1170 1171 def to_dict(self): 1172 d = {} 1173 d['feed_name'] = self.feed_name 1174 if self.available: 1175 d['available'] = self.available 1176 d['feed_description'] = self.feed_description 1177 d['supported_contents'] = self.supported_contents 1178 d['push_methods'] = [] 1179 for push_method in self.push_methods: 1180 d['push_methods'].append(push_method.to_dict()) 1181 d['polling_service_instances'] = [] 1182 for polling_service in self.polling_service_instances: 1183 d['polling_service_instances'].append(polling_service.to_dict()) 1184 d['subscription_methods'] = [] 1185 for subscription_method in self.subscription_methods: 1186 d['subscription_methods'].append(subscription_method.to_dict()) 1187 return d 1188 1189 def to_text(self, line_prepend=''): 1190 s = line_prepend + "=== Data Feed ===\n" 1191 s += line_prepend + " Feed Name: %s\n" % self.feed_name 1192 if self.available: 1193 s += line_prepend + " Available: %s\n" % self.available 1194 s += line_prepend + " Feed Description: %s\n" % self.feed_description 1195 for sc in self.supported_contents: 1196 s += line_prepend + " Supported Content: %s\n" % sc 1197 for pm in self.push_methods: 1198 s += pm.to_text(line_prepend + STD_INDENT) 1199 for ps in self.polling_service_instances: 1200 s += ps.to_text(line_prepend + STD_INDENT) 1201 for sm in self.subscription_methods: 1202 s += sm.to_text(line_prepend + STD_INDENT) 1203 1204 return s 1205 1206 @staticmethod 1207 def from_etree(etree_xml): 1208 kwargs = {} 1209 kwargs['feed_name'] = etree_xml.attrib['feed_name'] 1210 kwargs['available'] = None 1211 if 'available' in etree_xml.attrib: 1212 tmp = etree_xml.attrib['available'] 1213 kwargs['available'] = tmp.lower() == 'true' 1214 1215 kwargs['feed_description'] = get_required(etree_xml, './taxii:Description', ns_map).text 1216 1217 kwargs['supported_contents'] = [] 1218 for binding_elt in etree_xml.xpath('./taxii:Content_Binding', namespaces=ns_map): 1219 kwargs['supported_contents'].append(binding_elt.text) 1220 1221 kwargs['push_methods'] = [] 1222 for push_method_elt in etree_xml.xpath('./taxii:Push_Method', namespaces=ns_map): 1223 kwargs['push_methods'].append(PushMethod.from_etree(push_method_elt)) 1224 1225 kwargs['polling_service_instances'] = [] 1226 for polling_elt in etree_xml.xpath('./taxii:Polling_Service', namespaces=ns_map): 1227 kwargs['polling_service_instances'].append(PollingServiceInstance.from_etree(polling_elt)) 1228 1229 kwargs['subscription_methods'] = [] 1230 for subscription_elt in etree_xml.xpath('./taxii:Subscription_Service', namespaces=ns_map): 1231 kwargs['subscription_methods'].append(SubscriptionMethod.from_etree(subscription_elt)) 1232 1233 return FeedInformation(**kwargs) 1234 1235 @staticmethod 1236 def from_dict(d): 1237 kwargs = {} 1238 kwargs['feed_name'] = d['feed_name'] 1239 kwargs['available'] = d.get('available') 1240 1241 kwargs['feed_description'] = d['feed_description'] 1242 kwargs['supported_contents'] = [] 1243 for binding in d.get('supported_contents', []): 1244 kwargs['supported_contents'].append(binding) 1245 1246 kwargs['push_methods'] = [] 1247 for push_method in d.get('push_methods', []): 1248 kwargs['push_methods'].append(PushMethod.from_dict(push_method)) 1249 1250 kwargs['polling_service_instances'] = [] 1251 for polling in d.get('polling_service_instances', []): 1252 kwargs['polling_service_instances'].append(PollingServiceInstance.from_dict(polling)) 1253 1254 kwargs['subscription_methods'] = [] 1255 for subscription_method in d.get('subscription_methods', []): 1256 kwargs['subscription_methods'].append(SubscriptionMethod.from_dict(subscription_method)) 1257 1258 return FeedInformation(**kwargs) 1259 1260 1261class PushMethod(TAXIIBase10): 1262 1263 """ 1264 The Push Method component of a TAXII Feed Information 1265 component. 1266 1267 Args: 1268 push_protocol (str): a protocol binding that can be used 1269 to push content to an Inbox Service instance. **Required** 1270 push_message_bindings (list of str): the message bindings that 1271 can be used to push content to an Inbox Service instance 1272 using the protocol identified in the Push Protocol field. 1273 **Required** 1274 """ 1275 1276 def __init__(self, push_protocol, push_message_bindings): 1277 self.push_protocol = push_protocol 1278 self.push_message_bindings = push_message_bindings 1279 1280 @property 1281 def sort_key(self): 1282 return self.push_protocol 1283 1284 @property 1285 def push_protocol(self): 1286 return self._push_protocol 1287 1288 @push_protocol.setter 1289 def push_protocol(self, value): 1290 do_check(value, 'push_protocol', regex_tuple=uri_regex) 1291 self._push_protocol = value 1292 1293 @property 1294 def push_message_bindings(self): 1295 return self._push_message_bindings 1296 1297 @push_message_bindings.setter 1298 def push_message_bindings(self, value): 1299 do_check(value, 'push_message_bindings', regex_tuple=uri_regex) 1300 self._push_message_bindings = value 1301 1302 def to_etree(self): 1303 x = etree.Element('{%s}Push_Method' % ns_map['taxii']) 1304 proto_bind = etree.SubElement(x, '{%s}Protocol_Binding' % ns_map['taxii']) 1305 proto_bind.text = self.push_protocol 1306 for binding in self.push_message_bindings: 1307 b = etree.SubElement(x, '{%s}Message_Binding' % ns_map['taxii']) 1308 b.text = binding 1309 return x 1310 1311 def to_dict(self): 1312 d = {} 1313 d['push_protocol'] = self.push_protocol 1314 d['push_message_bindings'] = [] 1315 for binding in self.push_message_bindings: 1316 d['push_message_bindings'].append(binding) 1317 return d 1318 1319 def to_text(self, line_prepend=''): 1320 s = line_prepend + "=== Push Method ===\n" 1321 s += line_prepend + " Protocol Binding: %s\n" % self.push_protocol 1322 for mb in self.push_message_bindings: 1323 s += line_prepend + " Message Binding: %s\n" % mb 1324 1325 return s 1326 1327 @staticmethod 1328 def from_etree(etree_xml): 1329 kwargs = {} 1330 kwargs['push_protocol'] = get_required(etree_xml, './taxii:Protocol_Binding', ns_map).text 1331 kwargs['push_message_bindings'] = [] 1332 for message_binding in etree_xml.xpath('./taxii:Message_Binding', namespaces=ns_map): 1333 kwargs['push_message_bindings'].append(message_binding.text) 1334 return PushMethod(**kwargs) 1335 1336 @staticmethod 1337 def from_dict(d): 1338 return PushMethod(**d) 1339 1340 1341class PollingServiceInstance(TAXIIBase10): 1342 1343 """ 1344 The Polling Service Instance component of a TAXII Feed 1345 Information component. 1346 1347 Args: 1348 poll_protocol (str): the protocol binding supported by 1349 this Poll Service instance. **Required** 1350 poll_address (str): the address of the TAXII Daemon 1351 hosting this Poll Service instance. **Required** 1352 poll_message_bindings (list of str): the message bindings 1353 supported by this Poll Service instance. **Required** 1354 """ 1355 NAME = 'Polling_Service' 1356 1357 def __init__(self, poll_protocol, poll_address, poll_message_bindings): 1358 self.poll_protocol = poll_protocol 1359 self.poll_address = poll_address 1360 self.poll_message_bindings = poll_message_bindings 1361 1362 @property 1363 def sort_key(self): 1364 return self.poll_address 1365 1366 @property 1367 def poll_protocol(self): 1368 return self._poll_protocol 1369 1370 @poll_protocol.setter 1371 def poll_protocol(self, value): 1372 do_check(value, 'poll_protocol', regex_tuple=uri_regex) 1373 self._poll_protocol = value 1374 1375 @property 1376 def poll_message_bindings(self): 1377 return self._poll_message_bindings 1378 1379 @poll_message_bindings.setter 1380 def poll_message_bindings(self, value): 1381 do_check(value, 'poll_message_bindings', regex_tuple=uri_regex) 1382 self._poll_message_bindings = value 1383 1384 def to_etree(self): 1385 x = etree.Element('{%s}Polling_Service' % ns_map['taxii']) 1386 proto_bind = etree.SubElement(x, '{%s}Protocol_Binding' % ns_map['taxii']) 1387 proto_bind.text = self.poll_protocol 1388 address = etree.SubElement(x, '{%s}Address' % ns_map['taxii']) 1389 address.text = self.poll_address 1390 for binding in self.poll_message_bindings: 1391 b = etree.SubElement(x, '{%s}Message_Binding' % ns_map['taxii']) 1392 b.text = binding 1393 return x 1394 1395 def to_dict(self): 1396 d = {} 1397 d['poll_protocol'] = self.poll_protocol 1398 d['poll_address'] = self.poll_address 1399 d['poll_message_bindings'] = [] 1400 for binding in self.poll_message_bindings: 1401 d['poll_message_bindings'].append(binding) 1402 return d 1403 1404 def to_text(self, line_prepend=''): 1405 s = line_prepend + "=== Poll Service Instance ===\n" 1406 s += line_prepend + " Protocol Binding: %s\n" % self.poll_protocol 1407 s += line_prepend + " Address: %s\n" % self.poll_address 1408 for mb in self.poll_message_bindings: 1409 s += line_prepend + " Message Binding: %s\n" % mb 1410 1411 return s 1412 1413 @classmethod 1414 def from_etree(cls, etree_xml): 1415 protocol = get_required(etree_xml, './taxii:Protocol_Binding', ns_map).text 1416 addr = get_required(etree_xml, './taxii:Address', ns_map).text 1417 1418 bindings = [] 1419 for message_binding in etree_xml.xpath('./taxii:Message_Binding', namespaces=ns_map): 1420 bindings.append(message_binding.text) 1421 return cls(protocol, addr, bindings) 1422 1423 @classmethod 1424 def from_dict(cls, d): 1425 return cls(**d) 1426 1427 1428class SubscriptionMethod(TAXIIBase10): 1429 1430 """ 1431 The Subscription Method component of a TAXII Feed Information 1432 component. 1433 1434 Args: 1435 subscription_protocol (str): the protocol binding supported by 1436 this Feed Management Service instance. **Required** 1437 subscription_address (str): the address of the TAXII Daemon 1438 hosting this Feed Management Service instance. 1439 **Required**. 1440 subscription_message_bindings (list of str): the message 1441 bindings supported by this Feed Management Service 1442 Instance. **Required** 1443 """ 1444 NAME = 'Subscription_Service' 1445 1446 def __init__(self, subscription_protocol, subscription_address, 1447 subscription_message_bindings): 1448 self.subscription_protocol = subscription_protocol 1449 self.subscription_address = subscription_address 1450 self.subscription_message_bindings = subscription_message_bindings 1451 1452 @property 1453 def sort_key(self): 1454 return self.subscription_address 1455 1456 @property 1457 def subscription_protocol(self): 1458 return self._subscription_protocol 1459 1460 @subscription_protocol.setter 1461 def subscription_protocol(self, value): 1462 do_check(value, 'subscription_protocol', regex_tuple=uri_regex) 1463 self._subscription_protocol = value 1464 1465 @property 1466 def subscription_message_bindings(self): 1467 return self._subscription_message_bindings 1468 1469 @subscription_message_bindings.setter 1470 def subscription_message_bindings(self, value): 1471 do_check(value, 'subscription_message_bindings', regex_tuple=uri_regex) 1472 self._subscription_message_bindings = value 1473 1474 def to_etree(self): 1475 x = etree.Element('{%s}%s' % (ns_map['taxii'], self.NAME)) 1476 proto_bind = etree.SubElement(x, '{%s}Protocol_Binding' % ns_map['taxii']) 1477 proto_bind.text = self.subscription_protocol 1478 address = etree.SubElement(x, '{%s}Address' % ns_map['taxii']) 1479 address.text = self.subscription_address 1480 for binding in self.subscription_message_bindings: 1481 b = etree.SubElement(x, '{%s}Message_Binding' % ns_map['taxii']) 1482 b.text = binding 1483 return x 1484 1485 def to_dict(self): 1486 d = {} 1487 d['subscription_protocol'] = self.subscription_protocol 1488 d['subscription_address'] = self.subscription_address 1489 d['subscription_message_bindings'] = [] 1490 for binding in self.subscription_message_bindings: 1491 d['subscription_message_bindings'].append(binding) 1492 return d 1493 1494 def to_text(self, line_prepend=''): 1495 s = line_prepend + "=== Subscription Method ===\n" 1496 s += line_prepend + " Protocol Binding: %s\n" % self.subscription_protocol 1497 s += line_prepend + " Address: %s\n" % self.subscription_address 1498 for mb in self.subscription_message_bindings: 1499 s += line_prepend + " Message Binding: %s\n" % mb 1500 1501 return s 1502 1503 @classmethod 1504 def from_etree(cls, etree_xml): 1505 protocol = get_required(etree_xml, './taxii:Protocol_Binding', ns_map).text 1506 addr = get_required(etree_xml, './taxii:Address', ns_map).text 1507 bindings = [] 1508 for message_binding in etree_xml.xpath('./taxii:Message_Binding', namespaces=ns_map): 1509 bindings.append(message_binding.text) 1510 return cls(protocol, addr, bindings) 1511 1512 @classmethod 1513 def from_dict(cls, d): 1514 return cls(**d) 1515 1516 1517class PollRequest(TAXIIMessage): 1518 1519 """ 1520 A TAXII Poll Request message. 1521 1522 Arguments: 1523 message_id (str): A value identifying this message. **Required** 1524 extended_headers (dict): A dictionary of name/value pairs for 1525 use as Extended Headers. **Optional** 1526 feed_name (str): the name of the TAXII Data Feed that is being 1527 polled. **Required** 1528 exclusive_begin_timestamp_label (datetime): a Timestamp Label 1529 indicating the beginning of the range of TAXII Data Feed content the 1530 requester wishes to receive. **Optional** 1531 inclusive_end_timestamp_label (datetime): a Timestamp Label 1532 indicating the end of the range of TAXII Data Feed content the 1533 requester wishes to receive. **Optional** 1534 subscription_id (str): the existing subscription the Consumer 1535 wishes to poll. **Optional** 1536 content_bindings (list of str): the type of content that is 1537 requested in the response to this poll. **Optional**, defaults to 1538 accepting all content bindings. 1539 """ 1540 message_type = MSG_POLL_REQUEST 1541 1542 def __init__(self, message_id, extended_headers=None, 1543 feed_name=None, exclusive_begin_timestamp_label=None, 1544 inclusive_end_timestamp_label=None, subscription_id=None, 1545 content_bindings=None): 1546 super(PollRequest, self).__init__(message_id, extended_headers=extended_headers) 1547 self.feed_name = feed_name 1548 self.exclusive_begin_timestamp_label = exclusive_begin_timestamp_label 1549 self.inclusive_end_timestamp_label = inclusive_end_timestamp_label 1550 self.subscription_id = subscription_id 1551 self.content_bindings = content_bindings or [] 1552 1553 @TAXIIMessage.in_response_to.setter 1554 def in_response_to(self, value): 1555 if value: 1556 raise ValueError('in_response_to must be None') 1557 self._in_response_to = value 1558 1559 @property 1560 def feed_name(self): 1561 return self._feed_name 1562 1563 @feed_name.setter 1564 def feed_name(self, value): 1565 do_check(value, 'feed_name', regex_tuple=uri_regex) 1566 self._feed_name = value 1567 1568 @property 1569 def exclusive_begin_timestamp_label(self): 1570 return self._exclusive_begin_timestamp_label 1571 1572 @exclusive_begin_timestamp_label.setter 1573 def exclusive_begin_timestamp_label(self, value): 1574 value = check_timestamp_label(value, 'exclusive_begin_timestamp_label', can_be_none=True) 1575 self._exclusive_begin_timestamp_label = value 1576 1577 @property 1578 def inclusive_end_timestamp_label(self): 1579 return self._inclusive_end_timestamp_label 1580 1581 @inclusive_end_timestamp_label.setter 1582 def inclusive_end_timestamp_label(self, value): 1583 value = check_timestamp_label(value, 'inclusive_end_timestamp_label', can_be_none=True) 1584 self._inclusive_end_timestamp_label = value 1585 1586 @property 1587 def subscription_id(self): 1588 return self._subscription_id 1589 1590 @subscription_id.setter 1591 def subscription_id(self, value): 1592 do_check(value, 'subscription_id', regex_tuple=uri_regex, can_be_none=True) 1593 self._subscription_id = value 1594 1595 @property 1596 def content_bindings(self): 1597 return self._content_bindings 1598 1599 @content_bindings.setter 1600 def content_bindings(self, value): 1601 do_check(value, 'content_bindings', regex_tuple=uri_regex) 1602 self._content_bindings = value 1603 1604 def to_etree(self): 1605 xml = super(PollRequest, self).to_etree() 1606 xml.attrib['feed_name'] = self.feed_name 1607 if self.subscription_id is not None: 1608 xml.attrib['subscription_id'] = self.subscription_id 1609 1610 if self.exclusive_begin_timestamp_label: 1611 ebt = etree.SubElement(xml, '{%s}Exclusive_Begin_Timestamp' % ns_map['taxii']) 1612 # TODO: Add TZ Info 1613 ebt.text = self.exclusive_begin_timestamp_label.isoformat() 1614 1615 if self.inclusive_end_timestamp_label: 1616 iet = etree.SubElement(xml, '{%s}Inclusive_End_Timestamp' % ns_map['taxii']) 1617 # TODO: Add TZ Info 1618 iet.text = self.inclusive_end_timestamp_label.isoformat() 1619 1620 for binding in self.content_bindings: 1621 b = etree.SubElement(xml, '{%s}Content_Binding' % ns_map['taxii']) 1622 b.text = binding 1623 1624 return xml 1625 1626 def to_dict(self): 1627 d = super(PollRequest, self).to_dict() 1628 d['feed_name'] = self.feed_name 1629 if self.subscription_id is not None: 1630 d['subscription_id'] = self.subscription_id 1631 if self.exclusive_begin_timestamp_label: # TODO: Add TZ Info 1632 d['exclusive_begin_timestamp_label'] = self.exclusive_begin_timestamp_label.isoformat() 1633 if self.inclusive_end_timestamp_label: # TODO: Add TZ Info 1634 d['inclusive_end_timestamp_label'] = self.inclusive_end_timestamp_label.isoformat() 1635 d['content_bindings'] = [] 1636 for bind in self.content_bindings: 1637 d['content_bindings'].append(bind) 1638 return d 1639 1640 def to_text(self, line_prepend=''): 1641 s = super(PollRequest, self).to_text(line_prepend) 1642 s += line_prepend + " Feed Name: %s\n" % self.feed_name 1643 if self.subscription_id: 1644 s += line_prepend + " Subscription ID: %s\n" % self.subscription_id 1645 1646 if self.exclusive_begin_timestamp_label: 1647 s += line_prepend + " Excl. Begin Timestamp Label: %s\n" % self.exclusive_begin_timestamp_label.isoformat() 1648 else: 1649 s += line_prepend + " Excl. Begin Timestamp Label: %s\n" % None 1650 1651 if self.inclusive_end_timestamp_label: 1652 s += line_prepend + " Incl. End Timestamp Label: %s\n" % self.inclusive_end_timestamp_label.isoformat() 1653 else: 1654 s += line_prepend + " Incl. End Timestamp Label: %s\n" % None 1655 1656 if len(self.content_bindings) == 0: 1657 s += line_prepend + " Content Binding: Any Content\n" 1658 1659 for cb in self.content_bindings: 1660 s += line_prepend + " Content Binding: %s\n" % cb 1661 1662 return s 1663 1664 @classmethod 1665 def from_etree(cls, etree_xml): 1666 kwargs = {} 1667 kwargs['feed_name'] = get_required(etree_xml, './@feed_name', ns_map) 1668 kwargs['subscription_id'] = get_optional(etree_xml, './@subscription_id', ns_map) 1669 1670 ebt_text = get_optional_text(etree_xml, './taxii:Exclusive_Begin_Timestamp', ns_map) 1671 if ebt_text: 1672 kwargs['exclusive_begin_timestamp_label'] = parse_datetime_string(ebt_text) 1673 1674 iet_text = get_optional_text(etree_xml, './taxii:Inclusive_End_Timestamp', ns_map) 1675 if iet_text: 1676 kwargs['inclusive_end_timestamp_label'] = parse_datetime_string(iet_text) 1677 1678 kwargs['content_bindings'] = [] 1679 for binding in etree_xml.xpath('./taxii:Content_Binding', namespaces=ns_map): 1680 kwargs['content_bindings'].append(binding.text) 1681 1682 msg = super(PollRequest, cls).from_etree(etree_xml, **kwargs) 1683 return msg 1684 1685 @classmethod 1686 def from_dict(cls, d): 1687 kwargs = {} 1688 kwargs['feed_name'] = d['feed_name'] 1689 1690 kwargs['subscription_id'] = d.get('subscription_id') 1691 1692 kwargs['exclusive_begin_timestamp_label'] = None 1693 if d.get('exclusive_begin_timestamp_label'): 1694 kwargs['exclusive_begin_timestamp_label'] = parse_datetime_string(d['exclusive_begin_timestamp_label']) 1695 1696 kwargs['inclusive_end_timestamp_label'] = None 1697 if d.get('inclusive_end_timestamp_label'): 1698 kwargs['inclusive_end_timestamp_label'] = parse_datetime_string(d['inclusive_end_timestamp_label']) 1699 1700 kwargs['content_bindings'] = d.get('content_bindings', []) 1701 1702 msg = super(PollRequest, cls).from_dict(d, **kwargs) 1703 return msg 1704 1705 1706class PollResponse(TAXIIMessage): 1707 1708 """ 1709 A TAXII Poll Response message. 1710 1711 Args: 1712 message_id (str): A value identifying this message. **Required** 1713 in_response_to (str): Contains the Message ID of the message to 1714 which this is a response. **Required** 1715 extended_headers (dict): A dictionary of name/value pairs for 1716 use as Extended Headers. **Optional** 1717 feed_name (str): the name of the TAXII Data Feed that was polled. 1718 **Required** 1719 inclusive_begin_timestamp_label (datetime): a Timestamp Label 1720 indicating the beginning of the range this response covers. 1721 **Optional** 1722 inclusive_end_timestamp_label (datetime): a Timestamp Label 1723 indicating the end of the range this response covers. **Required** 1724 subscription_id (str): the Subscription ID for which this content 1725 is being provided. **Optional** 1726 message (str): additional information for the message recipient. 1727 **Optional** 1728 content_blocks (list of ContentBlock): piece of content 1729 and additional information related to the content. **Optional** 1730 """ 1731 message_type = MSG_POLL_RESPONSE 1732 1733 def __init__(self, message_id, in_response_to, extended_headers=None, 1734 feed_name=None, inclusive_begin_timestamp_label=None, 1735 inclusive_end_timestamp_label=None, subscription_id=None, 1736 message=None, content_blocks=None): 1737 super(PollResponse, self).__init__(message_id, in_response_to, extended_headers) 1738 self.feed_name = feed_name 1739 self.inclusive_end_timestamp_label = inclusive_end_timestamp_label 1740 self.inclusive_begin_timestamp_label = inclusive_begin_timestamp_label 1741 self.subscription_id = subscription_id 1742 self.message = message 1743 self.content_blocks = content_blocks or [] 1744 1745 @TAXIIMessage.in_response_to.setter 1746 def in_response_to(self, value): 1747 do_check(value, 'in_response_to', regex_tuple=uri_regex) 1748 self._in_response_to = value 1749 1750 @property 1751 def feed_name(self): 1752 return self._feed_name 1753 1754 @feed_name.setter 1755 def feed_name(self, value): 1756 do_check(value, 'feed_name', regex_tuple=uri_regex) 1757 self._feed_name = value 1758 1759 @property 1760 def inclusive_end_timestamp_label(self): 1761 return self._inclusive_end_timestamp_label 1762 1763 @inclusive_end_timestamp_label.setter 1764 def inclusive_end_timestamp_label(self, value): 1765 value = check_timestamp_label(value, 'inclusive_end_timestamp_label') 1766 self._inclusive_end_timestamp_label = value 1767 1768 @property 1769 def inclusive_begin_timestamp_label(self): 1770 return self._inclusive_begin_timestamp_label 1771 1772 @inclusive_begin_timestamp_label.setter 1773 def inclusive_begin_timestamp_label(self, value): 1774 value = check_timestamp_label(value, 'inclusive_begin_timestamp_label', can_be_none=True) 1775 self._inclusive_begin_timestamp_label = value 1776 1777 @property 1778 def subscription_id(self): 1779 return self._subscription_id 1780 1781 @subscription_id.setter 1782 def subscription_id(self, value): 1783 do_check(value, 'subscription_id', regex_tuple=uri_regex, can_be_none=True) 1784 self._subscription_id = value 1785 1786 @property 1787 def content_blocks(self): 1788 return self._content_blocks 1789 1790 @content_blocks.setter 1791 def content_blocks(self, value): 1792 do_check(value, 'content_blocks', type=ContentBlock) 1793 self._content_blocks = value 1794 1795 def to_etree(self): 1796 xml = super(PollResponse, self).to_etree() 1797 xml.attrib['feed_name'] = self.feed_name 1798 if self.subscription_id is not None: 1799 xml.attrib['subscription_id'] = self.subscription_id 1800 1801 if self.message is not None: 1802 m = etree.SubElement(xml, '{%s}Message' % ns_map['taxii']) 1803 m.text = self.message 1804 1805 if self.inclusive_begin_timestamp_label: 1806 ibt = etree.SubElement(xml, '{%s}Inclusive_Begin_Timestamp' % ns_map['taxii']) 1807 ibt.text = self.inclusive_begin_timestamp_label.isoformat() 1808 1809 iet = etree.SubElement(xml, '{%s}Inclusive_End_Timestamp' % ns_map['taxii']) 1810 iet.text = self.inclusive_end_timestamp_label.isoformat() 1811 1812 for block in self.content_blocks: 1813 xml.append(block.to_etree()) 1814 1815 return xml 1816 1817 def to_dict(self): 1818 d = super(PollResponse, self).to_dict() 1819 1820 d['feed_name'] = self.feed_name 1821 if self.subscription_id is not None: 1822 d['subscription_id'] = self.subscription_id 1823 if self.message is not None: 1824 d['message'] = self.message 1825 if self.inclusive_begin_timestamp_label: 1826 d['inclusive_begin_timestamp_label'] = self.inclusive_begin_timestamp_label.isoformat() 1827 d['inclusive_end_timestamp_label'] = self.inclusive_end_timestamp_label.isoformat() 1828 d['content_blocks'] = [] 1829 for block in self.content_blocks: 1830 d['content_blocks'].append(block.to_dict()) 1831 1832 return d 1833 1834 def to_text(self, line_prepend=''): 1835 s = super(PollResponse, self).to_text(line_prepend) 1836 s += line_prepend + " Feed Name: %s\n" % self.feed_name 1837 if self.subscription_id: 1838 s += line_prepend + " Subscription ID: %s\n" % self.subscription_id 1839 s += line_prepend + " Message: %s\n" % self.message 1840 1841 if self.inclusive_begin_timestamp_label: 1842 s += line_prepend + " Incl. Begin Timestamp Label: %s\n" % self.inclusive_begin_timestamp_label.isoformat() 1843 else: 1844 s += line_prepend + " Incl. Begin Timestamp Label: %s\n" % None 1845 1846 s += line_prepend + " Incl. End Timestamp Label: %s\n" % self.inclusive_end_timestamp_label.isoformat() 1847 1848 for cb in self.content_blocks: 1849 s += cb.to_text(line_prepend + STD_INDENT) 1850 1851 return s 1852 1853 @classmethod 1854 def from_etree(cls, etree_xml): 1855 kwargs = {} 1856 1857 kwargs['feed_name'] = get_required(etree_xml, './@feed_name', ns_map) 1858 kwargs['subscription_id'] = get_optional(etree_xml, './@subscription_id', ns_map) 1859 kwargs['message'] = get_optional_text(etree_xml, './taxii:Message', ns_map) 1860 1861 ibts_text = get_optional_text(etree_xml, './taxii:Inclusive_Begin_Timestamp', ns_map) 1862 if ibts_text: 1863 kwargs['inclusive_begin_timestamp_label'] = parse_datetime_string(ibts_text) 1864 1865 iets_text = get_required(etree_xml, './taxii:Inclusive_End_Timestamp', ns_map).text 1866 kwargs['inclusive_end_timestamp_label'] = parse_datetime_string(iets_text) 1867 1868 kwargs['content_blocks'] = [] 1869 blocks = etree_xml.xpath('./taxii:Content_Block', namespaces=ns_map) 1870 for block in blocks: 1871 kwargs['content_blocks'].append(ContentBlock.from_etree(block)) 1872 1873 msg = super(PollResponse, cls).from_etree(etree_xml, **kwargs) 1874 return msg 1875 1876 @classmethod 1877 def from_dict(cls, d): 1878 kwargs = {} 1879 kwargs['feed_name'] = d['feed_name'] 1880 1881 kwargs['message'] = d.get('message') 1882 kwargs['subscription_id'] = d.get('subscription_id') 1883 1884 kwargs['inclusive_begin_timestamp_label'] = None 1885 if d.get('inclusive_begin_timestamp_label'): 1886 kwargs['inclusive_begin_timestamp_label'] = parse_datetime_string(d['inclusive_begin_timestamp_label']) 1887 1888 kwargs['inclusive_end_timestamp_label'] = parse_datetime_string(d['inclusive_end_timestamp_label']) 1889 1890 kwargs['content_blocks'] = [] 1891 for block in d['content_blocks']: 1892 kwargs['content_blocks'].append(ContentBlock.from_dict(block)) 1893 msg = super(PollResponse, cls).from_dict(d, **kwargs) 1894 return msg 1895 1896 1897class StatusMessage(TAXIIMessage): 1898 1899 """ 1900 A TAXII Status message. 1901 1902 Args: 1903 message_id (str): A value identifying this message. **Required** 1904 in_response_to (str): Contains the Message ID of the message to 1905 which this is a response. **Required** 1906 extended_headers (dict): A dictionary of name/value pairs for 1907 use as Extended Headers. **Optional** 1908 status_type (str): One of the defined Status Types or a third-party- 1909 defined Status Type. **Required** 1910 status_detail (str): A field for additional information about 1911 this status in a machine-readable format. **Optional or Prohibited** 1912 depending on ``status_type``. See TAXII Specification for details. 1913 message (str): Additional information for the status. There is no 1914 expectation that this field be interpretable by a machine; it is 1915 instead targeted to a human operator. **Optional** 1916 """ 1917 message_type = MSG_STATUS_MESSAGE 1918 1919 def __init__(self, message_id, in_response_to, extended_headers=None, 1920 status_type=None, status_detail=None, message=None): 1921 super(StatusMessage, self).__init__(message_id, in_response_to, extended_headers=extended_headers) 1922 self.status_type = status_type 1923 self.status_detail = status_detail 1924 self.message = message 1925 1926 @TAXIIMessage.in_response_to.setter 1927 def in_response_to(self, value): 1928 do_check(value, 'in_response_to', regex_tuple=uri_regex) 1929 self._in_response_to = value 1930 1931 @property 1932 def status_type(self): 1933 return self._status_type 1934 1935 @status_type.setter 1936 def status_type(self, value): 1937 do_check(value, 'status_type') 1938 self._status_type = value 1939 1940 # TODO: is it possible to check the status detail? 1941 1942 def to_etree(self): 1943 xml = super(StatusMessage, self).to_etree() 1944 xml.attrib['status_type'] = self.status_type 1945 1946 if self.status_detail is not None: 1947 sd = etree.SubElement(xml, '{%s}Status_Detail' % ns_map['taxii']) 1948 sd.text = self.status_detail 1949 1950 if self.message is not None: 1951 m = etree.SubElement(xml, '{%s}Message' % ns_map['taxii']) 1952 m.text = self.message 1953 1954 return xml 1955 1956 def to_dict(self): 1957 d = super(StatusMessage, self).to_dict() 1958 d['status_type'] = self.status_type 1959 if self.status_detail is not None: 1960 d['status_detail'] = self.status_detail 1961 if self.message is not None: 1962 d['message'] = self.message 1963 1964 return d 1965 1966 def to_text(self, line_prepend=''): 1967 s = super(StatusMessage, self).to_text(line_prepend) 1968 s += line_prepend + " Status Type: %s\n" % self.status_type 1969 if self.status_detail: 1970 s += line_prepend + " Status Detail: %s\n" % self.status_detail 1971 s += line_prepend + " Status Message: %s\n" % self.message 1972 return s 1973 1974 @classmethod 1975 def from_etree(cls, etree_xml): 1976 kwargs = dict( 1977 status_type=etree_xml.attrib['status_type'], 1978 status_detail=get_optional_text(etree_xml, './taxii:Status_Detail', ns_map), 1979 message=get_optional_text(etree_xml, './taxii:Message', ns_map), 1980 ) 1981 1982 msg = super(StatusMessage, cls).from_etree(etree_xml, **kwargs) 1983 return msg 1984 1985 @classmethod 1986 def from_dict(cls, d): 1987 kwargs = dict( 1988 status_type=d['status_type'], 1989 status_detail=d.get('status_detail'), 1990 message=d.get('message') 1991 ) 1992 1993 msg = super(StatusMessage, cls).from_dict(d, **kwargs) 1994 return msg 1995 1996 1997class InboxMessage(TAXIIMessage): 1998 1999 """ 2000 A TAXII Inbox message. 2001 2002 Args: 2003 message_id (str): A value identifying this message. **Required** 2004 extended_headers (dict): A dictionary of name/value pairs for 2005 use as Extended Headers. **Optional** 2006 message (str): prose information for the message recipient. **Optional** 2007 subscription_information (libtaxii.messages_10.SubscriptionInformation): This 2008 field is only present if this message is being sent to provide 2009 content in accordance with an existing TAXII Data Feed 2010 subscription. **Optional** 2011 content_blocks (list of ContentBlock): Inbox content. **Optional** 2012 """ 2013 2014 message_type = MSG_INBOX_MESSAGE 2015 2016 def __init__(self, message_id, in_response_to=None, extended_headers=None, 2017 message=None, subscription_information=None, 2018 content_blocks=None): 2019 2020 super(InboxMessage, self).__init__(message_id, extended_headers=extended_headers) 2021 self.subscription_information = subscription_information 2022 self.message = message 2023 self.content_blocks = content_blocks or [] 2024 2025 @TAXIIMessage.in_response_to.setter 2026 def in_response_to(self, value): 2027 if value: 2028 raise ValueError('in_response_to must be None') 2029 self._in_response_to = value 2030 2031 @property 2032 def subscription_information(self): 2033 return self._subscription_information 2034 2035 @subscription_information.setter 2036 def subscription_information(self, value): 2037 do_check(value, 'subscription_information', type=SubscriptionInformation, can_be_none=True) 2038 self._subscription_information = value 2039 2040 @property 2041 def content_blocks(self): 2042 return self._content_blocks 2043 2044 @content_blocks.setter 2045 def content_blocks(self, value): 2046 do_check(value, 'content_blocks', type=ContentBlock) 2047 self._content_blocks = value 2048 2049 def to_etree(self): 2050 xml = super(InboxMessage, self).to_etree() 2051 if self.message is not None: 2052 m = etree.SubElement(xml, '{%s}Message' % ns_map['taxii']) 2053 m.text = self.message 2054 2055 if self.subscription_information: 2056 xml.append(self.subscription_information.to_etree()) 2057 2058 for block in self.content_blocks: 2059 xml.append(block.to_etree()) 2060 2061 return xml 2062 2063 def to_dict(self): 2064 d = super(InboxMessage, self).to_dict() 2065 if self.message is not None: 2066 d['message'] = self.message 2067 2068 if self.subscription_information: 2069 d['subscription_information'] = self.subscription_information.to_dict() 2070 2071 d['content_blocks'] = [] 2072 for block in self.content_blocks: 2073 d['content_blocks'].append(block.to_dict()) 2074 2075 return d 2076 2077 def to_text(self, line_prepend=''): 2078 s = super(InboxMessage, self).to_text(line_prepend) 2079 s += line_prepend + " Message: %s\n" % self.message 2080 if self.subscription_information: 2081 s += self.subscription_information.to_text(line_prepend + STD_INDENT) 2082 s += line_prepend + " Message has %s Content Blocks\n" % len(self.content_blocks) 2083 for cb in self.content_blocks: 2084 s += cb.to_text(line_prepend + STD_INDENT) 2085 2086 return s 2087 2088 @classmethod 2089 def from_etree(cls, etree_xml): 2090 msg = super(InboxMessage, cls).from_etree(etree_xml) 2091 2092 msg.message = get_optional_text(etree_xml, './taxii:Message', ns_map) 2093 2094 subs_info = get_optional(etree_xml, './taxii:Source_Subscription', ns_map) 2095 if subs_info is not None: 2096 msg.subscription_information = SubscriptionInformation.from_etree(subs_info) 2097 2098 content_blocks = etree_xml.xpath('./taxii:Content_Block', namespaces=ns_map) 2099 msg.content_blocks = [] 2100 for block in content_blocks: 2101 msg.content_blocks.append(ContentBlock.from_etree(block)) 2102 2103 return msg 2104 2105 @classmethod 2106 def from_dict(cls, d): 2107 msg = super(InboxMessage, cls).from_dict(d) 2108 2109 msg.message = d.get('message') 2110 2111 msg.subscription_information = None 2112 if 'subscription_information' in d: 2113 msg.subscription_information = SubscriptionInformation.from_dict(d['subscription_information']) 2114 2115 msg.content_blocks = [] 2116 for block in d['content_blocks']: 2117 msg.content_blocks.append(ContentBlock.from_dict(block)) 2118 2119 return msg 2120 2121 2122class SubscriptionInformation(TAXIIBase10): 2123 2124 """ 2125 The Subscription Information component of a TAXII Inbox message. 2126 2127 Arguments: 2128 feed_name (str): the name of the TAXII Data Feed from 2129 which this content is being provided. **Required** 2130 subscription_id (str): the Subscription ID for which this 2131 content is being provided. **Required** 2132 inclusive_begin_timestamp_label (datetime): a Timestamp Label 2133 indicating the beginning of the time range this Inbox Message 2134 covers. **Optional** 2135 inclusive_end_timestamp_label (datetime): a Timestamp Label 2136 indicating the end of the time range this Inbox Message covers. 2137 **Optional** 2138 """ 2139 2140 def __init__(self, feed_name, subscription_id, 2141 inclusive_begin_timestamp_label, 2142 inclusive_end_timestamp_label): 2143 self.feed_name = feed_name 2144 self.subscription_id = subscription_id 2145 self.inclusive_begin_timestamp_label = inclusive_begin_timestamp_label 2146 self.inclusive_end_timestamp_label = inclusive_end_timestamp_label 2147 2148 @property 2149 def feed_name(self): 2150 return self._feed_name 2151 2152 @feed_name.setter 2153 def feed_name(self, value): 2154 do_check(value, 'feed_name', regex_tuple=uri_regex) 2155 self._feed_name = value 2156 2157 @property 2158 def subscription_id(self): 2159 return self._subscription_id 2160 2161 @subscription_id.setter 2162 def subscription_id(self, value): 2163 do_check(value, 'subscription_id', regex_tuple=uri_regex) 2164 self._subscription_id = value 2165 2166 @property 2167 def inclusive_begin_timestamp_label(self): 2168 return self._inclusive_begin_timestamp_label 2169 2170 @inclusive_begin_timestamp_label.setter 2171 def inclusive_begin_timestamp_label(self, value): 2172 value = check_timestamp_label(value, 'inclusive_begin_timestamp_label') 2173 self._inclusive_begin_timestamp_label = value 2174 2175 @property 2176 def inclusive_end_timestamp_label(self): 2177 return self._inclusive_end_timestamp_label 2178 2179 @inclusive_end_timestamp_label.setter 2180 def inclusive_end_timestamp_label(self, value): 2181 value = check_timestamp_label(value, 'inclusive_end_timestamp_label') 2182 self._inclusive_end_timestamp_label = value 2183 2184 def to_etree(self): 2185 xml = etree.Element('{%s}Source_Subscription' % ns_map['taxii']) 2186 xml.attrib['feed_name'] = self.feed_name 2187 xml.attrib['subscription_id'] = self.subscription_id 2188 2189 ibtl = etree.SubElement(xml, '{%s}Inclusive_Begin_Timestamp' % ns_map['taxii']) 2190 ibtl.text = self.inclusive_begin_timestamp_label.isoformat() 2191 2192 ietl = etree.SubElement(xml, '{%s}Inclusive_End_Timestamp' % ns_map['taxii']) 2193 ietl.text = self.inclusive_end_timestamp_label.isoformat() 2194 2195 return xml 2196 2197 def to_dict(self): 2198 d = {} 2199 d['feed_name'] = self.feed_name 2200 d['subscription_id'] = self.subscription_id 2201 d['inclusive_begin_timestamp_label'] = self.inclusive_begin_timestamp_label.isoformat() 2202 d['inclusive_end_timestamp_label'] = self.inclusive_end_timestamp_label.isoformat() 2203 return d 2204 2205 def to_text(self, line_prepend=''): 2206 s = line_prepend + "=== Subscription Information ===\n" 2207 s += line_prepend + " Feed Name: %s\n" % self.feed_name 2208 s += line_prepend + " Subscription ID: %s\n" % self.subscription_id 2209 s += line_prepend + " Incl. Begin TS Label: %s\n" % self.inclusive_begin_timestamp_label.isoformat() 2210 s += line_prepend + " Incl. End TS Label: %s\n" % self.inclusive_end_timestamp_label.isoformat() 2211 return s 2212 2213 @staticmethod 2214 def from_etree(etree_xml): 2215 feed_name = etree_xml.attrib['feed_name'] 2216 subscription_id = etree_xml.attrib['subscription_id'] 2217 2218 ibtl = parse_datetime_string(get_required(etree_xml, './taxii:Inclusive_Begin_Timestamp', ns_map).text) 2219 ietl = parse_datetime_string(get_required(etree_xml, './taxii:Inclusive_End_Timestamp', ns_map).text) 2220 2221 return SubscriptionInformation(feed_name, subscription_id, ibtl, ietl) 2222 2223 @staticmethod 2224 def from_dict(d): 2225 feed_name = d['feed_name'] 2226 subscription_id = d['subscription_id'] 2227 2228 ibtl = parse_datetime_string(d['inclusive_begin_timestamp_label']) 2229 ietl = parse_datetime_string(d['inclusive_end_timestamp_label']) 2230 2231 return SubscriptionInformation(feed_name, subscription_id, ibtl, ietl) 2232 2233 2234class ManageFeedSubscriptionRequest(TAXIIMessage): 2235 2236 """ 2237 A TAXII Manage Feed Subscription Request message. 2238 2239 Args: 2240 message_id (str): A value identifying this message. **Required** 2241 extended_headers (dict): A dictionary of name/value pairs for 2242 use as Extended Headers. **Optional** 2243 feed_name (str): the name of the TAXII Data Feed to which the 2244 action applies. **Required** 2245 action (str): the requested action to take. **Required** 2246 subscription_id (str): the ID of a previously created subscription. 2247 **Required** if ``action==``:py:data:`ACT_UNSUBSCRIBE`, else 2248 **Prohibited**. 2249 delivery_parameters (list of DeliveryParameters): the delivery parameters 2250 for this request. **Optional** Absence means delivery is not requested. 2251 """ 2252 2253 message_type = MSG_MANAGE_FEED_SUBSCRIPTION_REQUEST 2254 2255 def __init__(self, message_id, extended_headers=None, 2256 feed_name=None, action=None, subscription_id=None, 2257 delivery_parameters=None): 2258 super(ManageFeedSubscriptionRequest, self).__init__(message_id, extended_headers=extended_headers) 2259 self.feed_name = feed_name 2260 self.action = action 2261 self.subscription_id = subscription_id 2262 self.delivery_parameters = delivery_parameters 2263 2264 @TAXIIMessage.in_response_to.setter 2265 def in_response_to(self, value): 2266 if value: 2267 raise ValueError('in_response_to must be None') 2268 self._in_response_to = value 2269 2270 @property 2271 def feed_name(self): 2272 return self._feed_name 2273 2274 @feed_name.setter 2275 def feed_name(self, value): 2276 do_check(value, 'feed_name', regex_tuple=uri_regex) 2277 self._feed_name = value 2278 2279 @property 2280 def action(self): 2281 return self._action 2282 2283 @action.setter 2284 def action(self, value): 2285 do_check(value, 'action', value_tuple=ACT_TYPES) 2286 self._action = value 2287 2288 @property 2289 def subscription_id(self): 2290 return self._subscription_id 2291 2292 @subscription_id.setter 2293 def subscription_id(self, value): 2294 do_check(value, 'subscription_id', regex_tuple=uri_regex, can_be_none=True) 2295 self._subscription_id = value 2296 2297 @property 2298 def delivery_parameters(self): 2299 return self._delivery_parameters 2300 2301 @delivery_parameters.setter 2302 def delivery_parameters(self, value): 2303 do_check(value, 'delivery_parameters', type=DeliveryParameters, can_be_none=True) 2304 self._delivery_parameters = value 2305 2306 def to_etree(self): 2307 xml = super(ManageFeedSubscriptionRequest, self).to_etree() 2308 xml.attrib['feed_name'] = self.feed_name 2309 xml.attrib['action'] = self.action 2310 if self.subscription_id is not None: 2311 xml.attrib['subscription_id'] = self.subscription_id 2312 2313 if self.delivery_parameters: 2314 xml.append(self.delivery_parameters.to_etree()) 2315 return xml 2316 2317 def to_dict(self): 2318 d = super(ManageFeedSubscriptionRequest, self).to_dict() 2319 d['feed_name'] = self.feed_name 2320 d['action'] = self.action 2321 d['subscription_id'] = self.subscription_id 2322 d['delivery_parameters'] = None 2323 if self.delivery_parameters: 2324 d['delivery_parameters'] = self.delivery_parameters.to_dict() 2325 return d 2326 2327 def to_text(self, line_prepend=''): 2328 s = super(ManageFeedSubscriptionRequest, self).to_text(line_prepend) 2329 s += line_prepend + " Feed Name: %s\n" % self.feed_name 2330 s += line_prepend + " Action: %s\n" % self.action 2331 s += line_prepend + " Subscription ID: %s\n" % self.subscription_id 2332 if self.delivery_parameters: 2333 s += self.delivery_parameters.to_text(line_prepend + STD_INDENT) 2334 return s 2335 2336 @classmethod 2337 def from_etree(cls, etree_xml): 2338 kwargs = dict( 2339 feed_name=get_required(etree_xml, './@feed_name', ns_map), 2340 action=get_required(etree_xml, './@action', ns_map), 2341 2342 # subscription_id is not required for action 'SUBSCRIBE' 2343 subscription_id=get_optional(etree_xml, './@subscription_id', ns_map), 2344 ) 2345 2346 # marked as required in spec but as optional is XSD 2347 delivery = get_optional(etree_xml, './taxii:Push_Parameters', ns_map) 2348 if delivery is not None: 2349 kwargs['delivery_parameters'] = DeliveryParameters.from_etree(delivery) 2350 2351 msg = super(ManageFeedSubscriptionRequest, cls).from_etree(etree_xml, **kwargs) 2352 return msg 2353 2354 @classmethod 2355 def from_dict(cls, d): 2356 kwargs = dict( 2357 feed_name=d['feed_name'], 2358 action=d['action'], 2359 subscription_id=d['subscription_id'], 2360 delivery_parameters=DeliveryParameters.from_dict(d['delivery_parameters']) 2361 ) 2362 2363 msg = super(ManageFeedSubscriptionRequest, cls).from_dict(d, **kwargs) 2364 return msg 2365 2366 2367class ManageFeedSubscriptionResponse(TAXIIMessage): 2368 2369 """ 2370 A TAXII Manage Feed Subscription Response message. 2371 2372 Args: 2373 message_id (str): A value identifying this message. **Required** 2374 in_response_to (str): Contains the Message ID of the message to 2375 which this is a response. **Required** 2376 extended_headers (dict): A dictionary of name/value pairs for 2377 use as Extended Headers. **Optional** 2378 feed_name (str): the name of the TAXII Data Feed to which 2379 the action applies. **Required** 2380 message (str): additional information for the message recipient. 2381 **Optional** 2382 subscription_instances (list of SubscriptionInstance): **Optional** 2383 """ 2384 2385 message_type = MSG_MANAGE_FEED_SUBSCRIPTION_RESPONSE 2386 2387 def __init__(self, message_id, in_response_to, extended_headers=None, 2388 feed_name=None, message=None, subscription_instances=None): 2389 super(ManageFeedSubscriptionResponse, self).__init__(message_id, in_response_to, extended_headers=extended_headers) 2390 self.feed_name = feed_name 2391 self.message = message 2392 self.subscription_instances = subscription_instances or [] 2393 2394 @TAXIIMessage.in_response_to.setter 2395 def in_response_to(self, value): 2396 do_check(value, 'in_response_to', regex_tuple=uri_regex) 2397 self._in_response_to = value 2398 2399 @property 2400 def feed_name(self): 2401 return self._feed_name 2402 2403 @feed_name.setter 2404 def feed_name(self, value): 2405 do_check(value, 'feed_name', regex_tuple=uri_regex) 2406 self._feed_name = value 2407 2408 @property 2409 def subscription_instances(self): 2410 return self._subscription_instances 2411 2412 @subscription_instances.setter 2413 def subscription_instances(self, value): 2414 do_check(value, 'subscription_instances', type=SubscriptionInstance) 2415 self._subscription_instances = value 2416 2417 def to_etree(self): 2418 xml = super(ManageFeedSubscriptionResponse, self).to_etree() 2419 xml.attrib['feed_name'] = self.feed_name 2420 if self.message is not None: 2421 m = etree.SubElement(xml, '{%s}Message' % ns_map['taxii']) 2422 m.text = self.message 2423 2424 for subscription_instance in self.subscription_instances: 2425 xml.append(subscription_instance.to_etree()) 2426 2427 return xml 2428 2429 def to_dict(self): 2430 d = super(ManageFeedSubscriptionResponse, self).to_dict() 2431 d['feed_name'] = self.feed_name 2432 if self.message is not None: 2433 d['message'] = self.message 2434 d['subscription_instances'] = [] 2435 for subscription_instance in self.subscription_instances: 2436 d['subscription_instances'].append(subscription_instance.to_dict()) 2437 2438 return d 2439 2440 def to_text(self, line_prepend=''): 2441 s = super(ManageFeedSubscriptionResponse, self).to_text(line_prepend) 2442 s += line_prepend + " Feed Name: %s\n" % self.feed_name 2443 s += line_prepend + " Message: %s\n" % self.message 2444 for si in self.subscription_instances: 2445 s += si.to_text(line_prepend + STD_INDENT) 2446 return s 2447 2448 @classmethod 2449 def from_etree(cls, etree_xml): 2450 kwargs = {} 2451 kwargs['feed_name'] = etree_xml.attrib['feed_name'] 2452 2453 kwargs['message'] = get_optional_text(etree_xml, './taxii:Message', ns_map) 2454 2455 kwargs['subscription_instances'] = [] 2456 for si in etree_xml.xpath('./taxii:Subscription', namespaces=ns_map): 2457 kwargs['subscription_instances'].append(SubscriptionInstance.from_etree(si)) 2458 2459 msg = super(ManageFeedSubscriptionResponse, cls).from_etree(etree_xml, **kwargs) 2460 return msg 2461 2462 @classmethod 2463 def from_dict(cls, d): 2464 kwargs = {} 2465 kwargs['feed_name'] = d['feed_name'] 2466 kwargs['message'] = d.get('message') 2467 2468 kwargs['subscription_instances'] = [] 2469 for instance in d['subscription_instances']: 2470 kwargs['subscription_instances'].append(SubscriptionInstance.from_dict(instance)) 2471 2472 msg = super(ManageFeedSubscriptionResponse, cls).from_dict(d, **kwargs) 2473 return msg 2474 2475 2476class SubscriptionInstance(TAXIIBase10): 2477 2478 """ 2479 The Subscription Instance component of the Manage Feed Subscription 2480 Response message. 2481 2482 Args: 2483 subscription_id (str): the id of the subscription. **Required** 2484 delivery_parameters (libtaxii.messages_10.DeliveryParameters): the parameters 2485 for this subscription. **Required** if responding to message 2486 with ``action==``:py:data:`ACT_STATUS`, otherwise **Prohibited** 2487 poll_instances (list of PollInstance): Each Poll 2488 Instance represents an instance of a Poll Service that can be 2489 contacted to retrieve content associated with the new 2490 Subscription. **Optional** 2491 """ 2492 2493 def __init__(self, subscription_id, delivery_parameters=None, 2494 poll_instances=None): 2495 self.subscription_id = subscription_id 2496 self.delivery_parameters = delivery_parameters 2497 self.poll_instances = poll_instances or [] 2498 2499 @property 2500 def sort_key(self): 2501 return self.subscription_id 2502 2503 @property 2504 def subscription_id(self): 2505 return self._subscription_id 2506 2507 @subscription_id.setter 2508 def subscription_id(self, value): 2509 do_check(value, 'subscription_id', regex_tuple=uri_regex) 2510 self._subscription_id = value 2511 2512 @property 2513 def delivery_parameters(self): 2514 return self._delivery_parameters 2515 2516 @delivery_parameters.setter 2517 def delivery_parameters(self, value): 2518 do_check(value, 'delivery_parameters', type=DeliveryParameters, can_be_none=True) 2519 self._delivery_parameters = value 2520 2521 @property 2522 def poll_instances(self): 2523 return self._poll_instances 2524 2525 @poll_instances.setter 2526 def poll_instances(self, value): 2527 do_check(value, 'poll_instances', type=PollInstance, can_be_none=False) 2528 self._poll_instances = value 2529 2530 def to_etree(self): 2531 xml = etree.Element('{%s}Subscription' % ns_map['taxii']) 2532 xml.attrib['subscription_id'] = self.subscription_id 2533 2534 if self.delivery_parameters: 2535 xml.append(self.delivery_parameters.to_etree()) 2536 2537 for poll_instance in self.poll_instances: 2538 xml.append(poll_instance.to_etree()) 2539 2540 return xml 2541 2542 def to_dict(self): 2543 d = {} 2544 d['subscription_id'] = self.subscription_id 2545 2546 if self.delivery_parameters: 2547 d['delivery_parameters'] = self.delivery_parameters.to_dict() 2548 else: 2549 d['delivery_parameters'] = None 2550 2551 d['poll_instances'] = [] 2552 for poll_instance in self.poll_instances: 2553 d['poll_instances'].append(poll_instance.to_dict()) 2554 2555 return d 2556 2557 def to_text(self, line_indent=''): 2558 s = line_indent + "=== Subscription Instance ===\n" 2559 s += line_indent + " Subscription ID: %s\n" % self.subscription_id 2560 if self.delivery_parameters: 2561 s += self.delivery_parameters.to_text(line_indent + STD_INDENT) 2562 for pi in self.poll_instances: 2563 s += pi.to_text(line_indent + STD_INDENT) 2564 return s 2565 2566 @staticmethod 2567 def from_etree(etree_xml): 2568 subscription_id = etree_xml.attrib['subscription_id'] 2569 2570 _delivery_parameters = get_optional(etree_xml, './taxii:Push_Parameters', ns_map) 2571 if _delivery_parameters is not None: 2572 delivery_parameters = DeliveryParameters.from_etree(_delivery_parameters) 2573 else: 2574 delivery_parameters = None 2575 2576 poll_instances = [] 2577 for poll_instance in etree_xml.xpath('./taxii:Poll_Instance', namespaces=ns_map): 2578 poll_instances.append(PollInstance.from_etree(poll_instance)) 2579 2580 return SubscriptionInstance(subscription_id, delivery_parameters, poll_instances) 2581 2582 @staticmethod 2583 def from_dict(d): 2584 subscription_id = d['subscription_id'] 2585 2586 if d.get('delivery_parameters'): 2587 delivery_parameters = DeliveryParameters.from_dict(d['delivery_parameters']) 2588 else: 2589 delivery_parameters = None 2590 2591 poll_instances = [] 2592 for poll_instance in d['poll_instances']: 2593 poll_instances.append(PollInstance.from_dict(poll_instance)) 2594 2595 return SubscriptionInstance(subscription_id, delivery_parameters, poll_instances) 2596 2597 2598class PollInstance(TAXIIBase10): 2599 2600 """ 2601 The Poll Instance component of the Manage Feed Subscription 2602 Response message. 2603 2604 Args: 2605 poll_protocol (str): The protocol binding supported by this 2606 instance of a Polling Service. **Required** 2607 poll_address (str): the address of the TAXII Daemon hosting 2608 this Poll Service. **Required** 2609 poll_message_bindings (list of str): one or more message bindings 2610 that can be used when interacting with this Poll Service 2611 instance. **Required** 2612 """ 2613 2614 def __init__(self, poll_protocol, poll_address, poll_message_bindings=None): 2615 self.poll_protocol = poll_protocol 2616 self.poll_address = poll_address 2617 self._poll_message_bindings = poll_message_bindings or [] 2618 2619 @property 2620 def sort_key(self): 2621 return self.poll_address 2622 2623 @property 2624 def poll_protocol(self): 2625 return self._poll_protocol 2626 2627 @poll_protocol.setter 2628 def poll_protocol(self, value): 2629 do_check(value, 'poll_protocol', regex_tuple=uri_regex) 2630 self._poll_protocol = value 2631 2632 @property 2633 def poll_message_bindings(self): 2634 return self._poll_message_bindings 2635 2636 @poll_message_bindings.setter 2637 def poll_message_bindings(self, value): 2638 do_check(value, 'poll_message_bindings', regex_tuple=uri_regex) 2639 self._poll_message_bindings = value 2640 2641 def to_etree(self): 2642 xml = etree.Element('{%s}Poll_Instance' % ns_map['taxii']) 2643 2644 pb = etree.SubElement(xml, '{%s}Protocol_Binding' % ns_map['taxii']) 2645 pb.text = self.poll_protocol 2646 2647 a = etree.SubElement(xml, '{%s}Address' % ns_map['taxii']) 2648 a.text = self.poll_address 2649 2650 for binding in self.poll_message_bindings: 2651 b = etree.SubElement(xml, '{%s}Message_Binding' % ns_map['taxii']) 2652 b.text = binding 2653 2654 return xml 2655 2656 def to_dict(self): 2657 d = {} 2658 2659 d['poll_protocol'] = self.poll_protocol 2660 d['poll_address'] = self.poll_address 2661 d['poll_message_bindings'] = [] 2662 for binding in self.poll_message_bindings: 2663 d['poll_message_bindings'].append(binding) 2664 2665 return d 2666 2667 def to_text(self, line_prepend=''): 2668 s = line_prepend + "=== Poll Instance ===\n" 2669 s += line_prepend + " Protocol Binding: %s\n" % self.poll_protocol 2670 s += line_prepend + " Address: %s\n" % self.poll_address 2671 for mb in self.poll_message_bindings: 2672 s += line_prepend + " Message Binding: %s\n" % mb 2673 return s 2674 2675 @staticmethod 2676 def from_etree(etree_xml): 2677 poll_protocol = get_required(etree_xml, './taxii:Protocol_Binding', ns_map).text 2678 address = get_required(etree_xml, './taxii:Address', ns_map).text 2679 2680 poll_message_bindings = [] 2681 for b in etree_xml.xpath('./taxii:Message_Binding', namespaces=ns_map): 2682 poll_message_bindings.append(b.text) 2683 2684 return PollInstance(poll_protocol, address, poll_message_bindings) 2685 2686 @staticmethod 2687 def from_dict(d): 2688 return PollInstance(**d) 2689 2690######################################################## 2691# EVERYTHING BELOW HERE IS FOR BACKWARDS COMPATIBILITY # 2692######################################################## 2693 2694# Add top-level classes as nested classes for backwards compatibility 2695DiscoveryResponse.ServiceInstance = ServiceInstance 2696FeedInformationResponse.FeedInformation = FeedInformation 2697FeedInformation.PushMethod = PushMethod 2698FeedInformation.PollingServiceInstance = PollingServiceInstance 2699FeedInformation.SubscriptionMethod = SubscriptionMethod 2700ManageFeedSubscriptionResponse.PollInstance = PollInstance 2701ManageFeedSubscriptionResponse.SubscriptionInstance = SubscriptionInstance 2702InboxMessage.SubscriptionInformation = SubscriptionInformation 2703 2704# Constants not imported in `from constants import *` 2705MSG_TYPES = MSG_TYPES_10 2706ST_TYPES = ST_TYPES_10 2707ACT_TYPES = ACT_TYPES_10 2708SVC_TYPES = SVC_TYPES_10 2709 2710from .common import (generate_message_id) 2711