diff options
Diffstat (limited to 'sleekxmpp/xmlstream/matcher')
-rw-r--r-- | sleekxmpp/xmlstream/matcher/__init__.py | 1 | ||||
-rw-r--r-- | sleekxmpp/xmlstream/matcher/idsender.py | 47 | ||||
-rw-r--r-- | sleekxmpp/xmlstream/matcher/xmlmask.py | 71 | ||||
-rw-r--r-- | sleekxmpp/xmlstream/matcher/xpath.py | 37 |
4 files changed, 68 insertions, 88 deletions
diff --git a/sleekxmpp/xmlstream/matcher/__init__.py b/sleekxmpp/xmlstream/matcher/__init__.py index 1038d1bd..aa74c434 100644 --- a/sleekxmpp/xmlstream/matcher/__init__.py +++ b/sleekxmpp/xmlstream/matcher/__init__.py @@ -7,6 +7,7 @@ """ from sleekxmpp.xmlstream.matcher.id import MatcherId +from sleekxmpp.xmlstream.matcher.idsender import MatchIDSender from sleekxmpp.xmlstream.matcher.many import MatchMany from sleekxmpp.xmlstream.matcher.stanzapath import StanzaPath from sleekxmpp.xmlstream.matcher.xmlmask import MatchXMLMask diff --git a/sleekxmpp/xmlstream/matcher/idsender.py b/sleekxmpp/xmlstream/matcher/idsender.py new file mode 100644 index 00000000..5c2c1f51 --- /dev/null +++ b/sleekxmpp/xmlstream/matcher/idsender.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- +""" + sleekxmpp.xmlstream.matcher.id + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + Part of SleekXMPP: The Sleek XMPP Library + + :copyright: (c) 2011 Nathanael C. Fritz + :license: MIT, see LICENSE for more details +""" + +from sleekxmpp.xmlstream.matcher.base import MatcherBase + + +class MatchIDSender(MatcherBase): + + """ + The IDSender matcher selects stanzas that have the same stanza 'id' + interface value as the desired ID, and that the 'from' value is one + of a set of approved entities that can respond to a request. + """ + + def match(self, xml): + """Compare the given stanza's ``'id'`` attribute to the stored + ``id`` value, and verify the sender's JID. + + :param xml: The :class:`~sleekxmpp.xmlstream.stanzabase.ElementBase` + stanza to compare against. + """ + + selfjid = self._criteria['self'] + peerjid = self._criteria['peer'] + + allowed = {} + allowed[''] = True + allowed[selfjid.bare] = True + allowed[selfjid.host] = True + allowed[peerjid.full] = True + allowed[peerjid.bare] = True + allowed[peerjid.host] = True + + _from = xml['from'] + + try: + return xml['id'] == self._criteria['id'] and allowed[_from] + except KeyError: + return False diff --git a/sleekxmpp/xmlstream/matcher/xmlmask.py b/sleekxmpp/xmlstream/matcher/xmlmask.py index a0568f08..56f728e1 100644 --- a/sleekxmpp/xmlstream/matcher/xmlmask.py +++ b/sleekxmpp/xmlstream/matcher/xmlmask.py @@ -14,12 +14,6 @@ from sleekxmpp.xmlstream.stanzabase import ET from sleekxmpp.xmlstream.matcher.base import MatcherBase -# Flag indicating if the builtin XPath matcher should be used, which -# uses namespaces, or a custom matcher that ignores namespaces. -# Changing this will affect ALL XMLMask matchers. -IGNORE_NS = False - - log = logging.getLogger(__name__) @@ -39,19 +33,15 @@ class MatchXMLMask(MatcherBase): :class:`~sleekxmpp.xmlstream.matcher.stanzapath.StanzaPath` should be used instead. - The use of namespaces in the mask comparison is controlled by - ``IGNORE_NS``. Setting ``IGNORE_NS`` to ``True`` will disable namespace - based matching for ALL XMLMask matchers. - :param criteria: Either an :class:`~xml.etree.ElementTree.Element` XML object or XML string to use as a mask. """ - def __init__(self, criteria): + def __init__(self, criteria, default_ns='jabber:client'): MatcherBase.__init__(self, criteria) if isinstance(criteria, str): self._criteria = ET.fromstring(self._criteria) - self.default_ns = 'jabber:client' + self.default_ns = default_ns def setDefaultNS(self, ns): """Set the default namespace to use during comparisons. @@ -84,8 +74,6 @@ class MatchXMLMask(MatcherBase): do not have a specified namespace. Defaults to ``"__no_ns__"``. """ - use_ns = not IGNORE_NS - if source is None: # If the element was not found. May happend during recursive calls. return False @@ -96,17 +84,10 @@ class MatchXMLMask(MatcherBase): mask = ET.fromstring(mask) except ExpatError: log.warning("Expat error: %s\nIn parsing: %s", '', mask) - if not use_ns: - # Compare the element without using namespaces. - source_tag = source.tag.split('}', 1)[-1] - mask_tag = mask.tag.split('}', 1)[-1] - if source_tag != mask_tag: - return False - else: - # Compare the element using namespaces - mask_ns_tag = "{%s}%s" % (self.default_ns, mask.tag) - if source.tag not in [mask.tag, mask_ns_tag]: - return False + + mask_ns_tag = "{%s}%s" % (self.default_ns, mask.tag) + if source.tag not in [mask.tag, mask_ns_tag]: + return False # If the mask includes text, compare it. if mask.text and source.text and \ @@ -122,37 +103,15 @@ class MatchXMLMask(MatcherBase): # Recursively check subelements. matched_elements = {} for subelement in mask: - if use_ns: - matched = False - for other in source.findall(subelement.tag): - matched_elements[other] = False - if self._mask_cmp(other, subelement, use_ns): - if not matched_elements.get(other, False): - matched_elements[other] = True - matched = True - if not matched: - return False - else: - if not self._mask_cmp(self._get_child(source, subelement.tag), - subelement, use_ns): - return False + matched = False + for other in source.findall(subelement.tag): + matched_elements[other] = False + if self._mask_cmp(other, subelement, use_ns): + if not matched_elements.get(other, False): + matched_elements[other] = True + matched = True + if not matched: + return False # Everything matches. return True - - def _get_child(self, xml, tag): - """Return a child element given its tag, ignoring namespace values. - - Returns ``None`` if the child was not found. - - :param xml: The :class:`~xml.etree.ElementTree.Element` XML object - to search for the given child tag. - :param tag: The name of the subelement to find. - """ - tag = tag.split('}')[-1] - try: - children = [c.tag.split('}')[-1] for c in xml] - index = children.index(tag) - except ValueError: - return None - return list(xml)[index] diff --git a/sleekxmpp/xmlstream/matcher/xpath.py b/sleekxmpp/xmlstream/matcher/xpath.py index 3f03e68e..f3d28429 100644 --- a/sleekxmpp/xmlstream/matcher/xpath.py +++ b/sleekxmpp/xmlstream/matcher/xpath.py @@ -9,16 +9,10 @@ :license: MIT, see LICENSE for more details """ -from sleekxmpp.xmlstream.stanzabase import ET +from sleekxmpp.xmlstream.stanzabase import ET, fix_ns from sleekxmpp.xmlstream.matcher.base import MatcherBase -# Flag indicating if the builtin XPath matcher should be used, which -# uses namespaces, or a custom matcher that ignores namespaces. -# Changing this will affect ALL XPath matchers. -IGNORE_NS = False - - class MatchXPath(MatcherBase): """ @@ -38,6 +32,9 @@ class MatchXPath(MatcherBase): expressions will be matched without using namespaces. """ + def __init__(self, criteria): + self._criteria = fix_ns(criteria) + def match(self, xml): """ Compare a stanza's XML contents to an XPath expression. @@ -59,28 +56,4 @@ class MatchXPath(MatcherBase): x = ET.Element('x') x.append(xml) - if not IGNORE_NS: - # Use builtin, namespace respecting, XPath matcher. - if x.find(self._criteria) is not None: - return True - return False - else: - # Remove namespaces from the XPath expression. - criteria = [] - for ns_block in self._criteria.split('{'): - criteria.extend(ns_block.split('}')[-1].split('/')) - - # Walk the XPath expression. - xml = x - for tag in criteria: - if not tag: - # Skip empty tag name artifacts from the cleanup phase. - continue - - children = [c.tag.split('}')[-1] for c in xml] - try: - index = children.index(tag) - except ValueError: - return False - xml = list(xml)[index] - return True + return x.find(self._criteria) is not None |