From 797e92a6a3cf1534f6ecb0f30019b0135d0ffacb Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Mon, 19 Jul 2010 04:12:54 -0400 Subject: Fixed error in updateRoster when the name keyword parameter is left out. The Roster stanza object builds item elements manually, and did not handle the case where the name attribute is set to None, which would crash SleekXMPP. --- sleekxmpp/stanza/roster.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'sleekxmpp/stanza') diff --git a/sleekxmpp/stanza/roster.py b/sleekxmpp/stanza/roster.py index 1fefc180..69027b6c 100644 --- a/sleekxmpp/stanza/roster.py +++ b/sleekxmpp/stanza/roster.py @@ -23,7 +23,9 @@ class Roster(ElementBase): if 'subscription' in items[jid]: item.attrib['subscription'] = items[jid]['subscription'] if 'name' in items[jid]: - item.attrib['name'] = items[jid]['name'] + name = items[jid]['name'] + if name is not None: + item.attrib['name'] = name if 'groups' in items[jid]: for group in items[jid]['groups']: groupxml = ET.Element('{jabber:iq:roster}group') -- cgit v1.2.3 From d5e42ac0e7282500583bf17f21eb2f944600ce76 Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Mon, 19 Jul 2010 13:58:53 -0400 Subject: Condensed all of the stanzaPlugin functions into a single registerStanzaPlugin function. Updated plugins and tests to use new function. --- sleekxmpp/stanza/atom.py | 2 +- sleekxmpp/stanza/error.py | 2 +- sleekxmpp/stanza/htmlim.py | 2 +- sleekxmpp/stanza/nick.py | 2 +- sleekxmpp/stanza/roster.py | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) (limited to 'sleekxmpp/stanza') diff --git a/sleekxmpp/stanza/atom.py b/sleekxmpp/stanza/atom.py index 5e82cb98..9df85a2b 100644 --- a/sleekxmpp/stanza/atom.py +++ b/sleekxmpp/stanza/atom.py @@ -1,4 +1,4 @@ -from .. xmlstream.stanzabase import ElementBase, ET, JID +from .. xmlstream.stanzabase import registerStanzaPlugin, ElementBase, ET, JID from xml.etree import cElementTree as ET class AtomEntry(ElementBase): diff --git a/sleekxmpp/stanza/error.py b/sleekxmpp/stanza/error.py index ee46722a..b9ab2676 100644 --- a/sleekxmpp/stanza/error.py +++ b/sleekxmpp/stanza/error.py @@ -5,7 +5,7 @@ See the file license.txt for copying permission. """ -from .. xmlstream.stanzabase import ElementBase, ET +from .. xmlstream.stanzabase import registerStanzaPlugin, ElementBase, ET class Error(ElementBase): namespace = 'jabber:client' diff --git a/sleekxmpp/stanza/htmlim.py b/sleekxmpp/stanza/htmlim.py index 60686e4a..14595e24 100644 --- a/sleekxmpp/stanza/htmlim.py +++ b/sleekxmpp/stanza/htmlim.py @@ -5,7 +5,7 @@ See the file license.txt for copying permission. """ -from .. xmlstream.stanzabase import ElementBase, ET +from .. xmlstream.stanzabase import registerStanzaPlugin, ElementBase, ET class HTMLIM(ElementBase): namespace = 'http://jabber.org/protocol/xhtml-im' diff --git a/sleekxmpp/stanza/nick.py b/sleekxmpp/stanza/nick.py index ac7e3604..ec290703 100644 --- a/sleekxmpp/stanza/nick.py +++ b/sleekxmpp/stanza/nick.py @@ -5,7 +5,7 @@ See the file license.txt for copying permission. """ -from .. xmlstream.stanzabase import ElementBase, ET +from .. xmlstream.stanzabase import registerStanzaPlugin, ElementBase, ET class Nick(ElementBase): namespace = 'http://jabber.org/nick/nick' diff --git a/sleekxmpp/stanza/roster.py b/sleekxmpp/stanza/roster.py index 69027b6c..708b8d40 100644 --- a/sleekxmpp/stanza/roster.py +++ b/sleekxmpp/stanza/roster.py @@ -5,7 +5,7 @@ See the file license.txt for copying permission. """ -from .. xmlstream.stanzabase import ElementBase, ET, JID +from .. xmlstream.stanzabase import registerStanzaPlugin, ElementBase, ET, JID import logging class Roster(ElementBase): -- cgit v1.2.3 From bb927c7e6ad75b190ab3aeea7fd71d8cd2118eed Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Tue, 20 Jul 2010 00:04:34 -0400 Subject: Updated presence stanza to include a 'show' interface. Presence stanza tests updated accordingly. --- sleekxmpp/stanza/presence.py | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) (limited to 'sleekxmpp/stanza') diff --git a/sleekxmpp/stanza/presence.py b/sleekxmpp/stanza/presence.py index c66246c9..0da7ffc8 100644 --- a/sleekxmpp/stanza/presence.py +++ b/sleekxmpp/stanza/presence.py @@ -5,36 +5,34 @@ See the file license.txt for copying permission. """ -from .. xmlstream.stanzabase import StanzaBase -from xml.etree import cElementTree as ET + from . error import Error from . rootstanza import RootStanza +from .. xmlstream.stanzabase import StanzaBase, ET + class Presence(RootStanza): - interfaces = set(('type', 'to', 'from', 'id', 'status', 'priority')) + interfaces = set(('type', 'to', 'from', 'id', 'show', 'status', 'priority')) types = set(('available', 'unavailable', 'error', 'probe', 'subscribe', 'subscribed', 'unsubscribe', 'unsubscribed')) showtypes = set(('dnd', 'chat', 'xa', 'away')) - sub_interfaces = set(('status', 'priority')) + sub_interfaces = set(('show', 'status', 'priority')) name = 'presence' plugin_attrib = name namespace = 'jabber:client' - def getShowElement(self): - return self.xml.find("{%s}show" % self.namespace) + def setShow(self, show): + if show in self.showtypes: + self._setSubText('show', text=show) + return self def setType(self, value): - show = self.getShowElement() if value in self.types: - if show is not None: - self.xml.remove(show) + self['show'] = None if value == 'available': value = '' self._setAttr('type', value) elif value in self.showtypes: - if show is None: - show = ET.Element("{%s}show" % self.namespace) - self.xml.append(show) - show.text = value + self['show'] = value return self def setPriority(self, value): @@ -48,9 +46,7 @@ class Presence(RootStanza): def getType(self): out = self._getAttr('type') if not out: - show = self.getShowElement() - if show is not None: - out = show.text + out = self['show'] if not out or out is None: out = 'available' return out -- cgit v1.2.3 From 690eaf8d3c3856c6242612da22e6c6d323f193ed Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Tue, 20 Jul 2010 11:19:49 -0400 Subject: Updated license notices to use the correct MIT format. Also corrected references to nonexistant license.txt to LICENSE. --- sleekxmpp/stanza/__init__.py | 2 +- sleekxmpp/stanza/error.py | 2 +- sleekxmpp/stanza/htmlim.py | 2 +- sleekxmpp/stanza/iq.py | 2 +- sleekxmpp/stanza/message.py | 2 +- sleekxmpp/stanza/nick.py | 2 +- sleekxmpp/stanza/presence.py | 2 +- sleekxmpp/stanza/rootstanza.py | 2 +- sleekxmpp/stanza/roster.py | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) (limited to 'sleekxmpp/stanza') diff --git a/sleekxmpp/stanza/__init__.py b/sleekxmpp/stanza/__init__.py index c3d8a318..b8fca228 100644 --- a/sleekxmpp/stanza/__init__.py +++ b/sleekxmpp/stanza/__init__.py @@ -3,6 +3,6 @@ Copyright (C) 2010 Nathanael C. Fritz This file is part of SleekXMPP. - See the file license.txt for copying permission. + See the file LICENSE for copying permission. """ __all__ = ['presence'] diff --git a/sleekxmpp/stanza/error.py b/sleekxmpp/stanza/error.py index b9ab2676..7771c87b 100644 --- a/sleekxmpp/stanza/error.py +++ b/sleekxmpp/stanza/error.py @@ -3,7 +3,7 @@ Copyright (C) 2010 Nathanael C. Fritz This file is part of SleekXMPP. - See the file license.txt for copying permission. + See the file LICENSE for copying permission. """ from .. xmlstream.stanzabase import registerStanzaPlugin, ElementBase, ET diff --git a/sleekxmpp/stanza/htmlim.py b/sleekxmpp/stanza/htmlim.py index 14595e24..50195b11 100644 --- a/sleekxmpp/stanza/htmlim.py +++ b/sleekxmpp/stanza/htmlim.py @@ -3,7 +3,7 @@ Copyright (C) 2010 Nathanael C. Fritz This file is part of SleekXMPP. - See the file license.txt for copying permission. + See the file LICENSE for copying permission. """ from .. xmlstream.stanzabase import registerStanzaPlugin, ElementBase, ET diff --git a/sleekxmpp/stanza/iq.py b/sleekxmpp/stanza/iq.py index ded7515f..daf05b43 100644 --- a/sleekxmpp/stanza/iq.py +++ b/sleekxmpp/stanza/iq.py @@ -3,7 +3,7 @@ Copyright (C) 2010 Nathanael C. Fritz This file is part of SleekXMPP. - See the file license.txt for copying permission. + See the file LICENSE for copying permission. """ from .. xmlstream.stanzabase import StanzaBase from xml.etree import cElementTree as ET diff --git a/sleekxmpp/stanza/message.py b/sleekxmpp/stanza/message.py index 38341809..75ecc232 100644 --- a/sleekxmpp/stanza/message.py +++ b/sleekxmpp/stanza/message.py @@ -3,7 +3,7 @@ Copyright (C) 2010 Nathanael C. Fritz This file is part of SleekXMPP. - See the file license.txt for copying permission. + See the file LICENSE for copying permission. """ from .. xmlstream.stanzabase import StanzaBase from xml.etree import cElementTree as ET diff --git a/sleekxmpp/stanza/nick.py b/sleekxmpp/stanza/nick.py index ec290703..47d4620a 100644 --- a/sleekxmpp/stanza/nick.py +++ b/sleekxmpp/stanza/nick.py @@ -3,7 +3,7 @@ Copyright (C) 2010 Nathanael C. Fritz This file is part of SleekXMPP. - See the file license.txt for copying permission. + See the file LICENSE for copying permission. """ from .. xmlstream.stanzabase import registerStanzaPlugin, ElementBase, ET diff --git a/sleekxmpp/stanza/presence.py b/sleekxmpp/stanza/presence.py index 0da7ffc8..ec681763 100644 --- a/sleekxmpp/stanza/presence.py +++ b/sleekxmpp/stanza/presence.py @@ -3,7 +3,7 @@ Copyright (C) 2010 Nathanael C. Fritz This file is part of SleekXMPP. - See the file license.txt for copying permission. + See the file LICENSE for copying permission. """ from . error import Error diff --git a/sleekxmpp/stanza/rootstanza.py b/sleekxmpp/stanza/rootstanza.py index 3b4822d8..e568b62b 100644 --- a/sleekxmpp/stanza/rootstanza.py +++ b/sleekxmpp/stanza/rootstanza.py @@ -3,7 +3,7 @@ Copyright (C) 2010 Nathanael C. Fritz This file is part of SleekXMPP. - See the file license.txt for copying permission. + See the file LICENSE for copying permission. """ from .. xmlstream.stanzabase import StanzaBase from xml.etree import cElementTree as ET diff --git a/sleekxmpp/stanza/roster.py b/sleekxmpp/stanza/roster.py index 708b8d40..eda65ec7 100644 --- a/sleekxmpp/stanza/roster.py +++ b/sleekxmpp/stanza/roster.py @@ -3,7 +3,7 @@ Copyright (C) 2010 Nathanael C. Fritz This file is part of SleekXMPP. - See the file license.txt for copying permission. + See the file LICENSE for copying permission. """ from .. xmlstream.stanzabase import registerStanzaPlugin, ElementBase, ET, JID import logging -- cgit v1.2.3 From c8989c04f3675235e3ae730cb240e2154b5d9e76 Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Mon, 26 Jul 2010 21:02:25 -0400 Subject: Replaced traceback calls to use logging.exception where applicable. --- sleekxmpp/stanza/rootstanza.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sleekxmpp/stanza') diff --git a/sleekxmpp/stanza/rootstanza.py b/sleekxmpp/stanza/rootstanza.py index e568b62b..3bae1f0d 100644 --- a/sleekxmpp/stanza/rootstanza.py +++ b/sleekxmpp/stanza/rootstanza.py @@ -9,6 +9,7 @@ from .. xmlstream.stanzabase import StanzaBase from xml.etree import cElementTree as ET from . error import Error from .. exceptions import XMPPError +import logging import traceback import sys @@ -29,6 +30,7 @@ class RootStanza(StanzaBase): self['error']['text'] = "SleekXMPP got into trouble." else: self['error']['text'] = traceback.format_tb(e.__traceback__) + logging.exception('Error handling {%s}%s stanza' % (self.namespace, self.name)) self.send() # all jabber:client root stanzas should have the error plugin -- cgit v1.2.3 From bd92ef6acfd9b1ecd390f08b28f1a3f4e7ec69a9 Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Wed, 28 Jul 2010 13:14:41 -0400 Subject: Updated RootStanza to use registerStanzaPlugin, and be PEP8 compliant. --- sleekxmpp/stanza/__init__.py | 7 +++- sleekxmpp/stanza/rootstanza.py | 76 ++++++++++++++++++++++++++++-------------- 2 files changed, 57 insertions(+), 26 deletions(-) (limited to 'sleekxmpp/stanza') diff --git a/sleekxmpp/stanza/__init__.py b/sleekxmpp/stanza/__init__.py index b8fca228..8302c43d 100644 --- a/sleekxmpp/stanza/__init__.py +++ b/sleekxmpp/stanza/__init__.py @@ -5,4 +5,9 @@ See the file LICENSE for copying permission. """ -__all__ = ['presence'] + + +from sleekxmpp.stanza.error import Error +from sleekxmpp.stanza.iq import Iq +from sleekxmpp.stanza.message import Message +from sleekxmpp.stanza.presence import Presence diff --git a/sleekxmpp/stanza/rootstanza.py b/sleekxmpp/stanza/rootstanza.py index 3bae1f0d..fb5498f5 100644 --- a/sleekxmpp/stanza/rootstanza.py +++ b/sleekxmpp/stanza/rootstanza.py @@ -5,34 +5,60 @@ See the file LICENSE for copying permission. """ -from .. xmlstream.stanzabase import StanzaBase -from xml.etree import cElementTree as ET -from . error import Error -from .. exceptions import XMPPError + import logging import traceback import sys +from sleekxmpp.exceptions import XMPPError +from sleekxmpp.stanza import Error +from sleekxmpp.xmlstream.stanzabase import ET, StanzaBase, registerStanzaPlugin + + class RootStanza(StanzaBase): - def exception(self, e): #called when a handler raises an exception - self.reply() - if isinstance(e, XMPPError): # we raised this deliberately - self['error']['condition'] = e.condition - self['error']['text'] = e.text - if e.extension is not None: # extended error tag - extxml = ET.Element("{%s}%s" % (e.extension_ns, e.extension), e.extension_args) - self['error'].xml.append(extxml) - self['error']['type'] = e.etype - else: # we probably didn't raise this on purpose, so send back a traceback - self['error']['condition'] = 'undefined-condition' - if sys.version_info < (3,0): - self['error']['text'] = "SleekXMPP got into trouble." - else: - self['error']['text'] = traceback.format_tb(e.__traceback__) - logging.exception('Error handling {%s}%s stanza' % (self.namespace, self.name)) - self.send() - -# all jabber:client root stanzas should have the error plugin -RootStanza.plugin_attrib_map['error'] = Error -RootStanza.plugin_tag_map["{%s}%s" % (Error.namespace, Error.name)] = Error + """ + A top-level XMPP stanza in an XMLStream. + + The RootStanza class provides a more XMPP specific exception + handler than provided by the generic StanzaBase class. + + Methods: + exception -- Overrides StanzaBase.exception + """ + + def exception(self, e): + """ + Create and send an error reply. + + Typically called when an event handler raises an exception. + The error's type and text content are based on the exception + object's type and content. + + Overrides StanzaBase.exception. + + Arguments: + e -- Exception object + """ + self.reply() + if isinstance(e, XMPPError): + # We raised this deliberately + self['error']['condition'] = e.condition + self['error']['text'] = e.text + if e.extension is not None: + # Extended error tag + extxml = ET.Element("{%s}%s" % (e.extension_ns, e.extension), e.extension_args) + self['error'].append(extxml) + self['error']['type'] = e.etype + else: + # We probably didn't raise this on purpose, so send back a traceback + self['error']['condition'] = 'undefined-condition' + if sys.version_info < (3,0): + self['error']['text'] = "SleekXMPP got into trouble." + else: + self['error']['text'] = traceback.format_tb(e.__traceback__) + logging.exception('Error handling {%s}%s stanza' % (self.namespace, self.name)) + self.send() + + +registerStanzaPlugin(RootStanza, Error) -- cgit v1.2.3 From e8e934fa95ea776d067d875fa67e89cc9e273e90 Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Thu, 29 Jul 2010 11:02:42 -0400 Subject: Fixed some PEP8 errors in RootStanza (trailing whitespace and line length) --- sleekxmpp/stanza/rootstanza.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'sleekxmpp/stanza') diff --git a/sleekxmpp/stanza/rootstanza.py b/sleekxmpp/stanza/rootstanza.py index fb5498f5..eafc79a2 100644 --- a/sleekxmpp/stanza/rootstanza.py +++ b/sleekxmpp/stanza/rootstanza.py @@ -41,23 +41,25 @@ class RootStanza(StanzaBase): e -- Exception object """ self.reply() - if isinstance(e, XMPPError): + if isinstance(e, XMPPError): # We raised this deliberately self['error']['condition'] = e.condition self['error']['text'] = e.text - if e.extension is not None: + if e.extension is not None: # Extended error tag - extxml = ET.Element("{%s}%s" % (e.extension_ns, e.extension), e.extension_args) + extxml = ET.Element("{%s}%s" % (e.extension_ns, e.extension), + e.extension_args) self['error'].append(extxml) self['error']['type'] = e.etype - else: - # We probably didn't raise this on purpose, so send back a traceback + else: + # We probably didn't raise this on purpose, so send a traceback self['error']['condition'] = 'undefined-condition' - if sys.version_info < (3,0): + if sys.version_info < (3, 0): self['error']['text'] = "SleekXMPP got into trouble." else: self['error']['text'] = traceback.format_tb(e.__traceback__) - logging.exception('Error handling {%s}%s stanza' % (self.namespace, self.name)) + logging.exception('Error handling {%s}%s stanza' % + (self.namespace, self.name)) self.send() -- cgit v1.2.3 From 25f43bd21906cb13df137b926dc62ca23bd97df0 Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Thu, 29 Jul 2010 11:06:10 -0400 Subject: Updated error stanza to be PEP8 compliant and include documentation. --- sleekxmpp/stanza/error.py | 175 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 122 insertions(+), 53 deletions(-) (limited to 'sleekxmpp/stanza') diff --git a/sleekxmpp/stanza/error.py b/sleekxmpp/stanza/error.py index 7771c87b..3253d9c5 100644 --- a/sleekxmpp/stanza/error.py +++ b/sleekxmpp/stanza/error.py @@ -5,58 +5,127 @@ See the file LICENSE for copying permission. """ -from .. xmlstream.stanzabase import registerStanzaPlugin, ElementBase, ET + +from sleekxmpp.xmlstream.stanzabase import registerStanzaPlugin +from sleekxmpp.xmlstream.stanzabase import ElementBase, ET + class Error(ElementBase): - namespace = 'jabber:client' - name = 'error' - plugin_attrib = 'error' - conditions = set(('bad-request', 'conflict', 'feature-not-implemented', 'forbidden', 'gone', 'internal-server-error', 'item-not-found', 'jid-malformed', 'not-acceptable', 'not-allowed', 'not-authorized', 'payment-required', 'recipient-unavailable', 'redirect', 'registration-required', 'remote-server-not-found', 'remote-server-timeout', 'resource-constraint', 'service-unavailable', 'subscription-required', 'undefined-condition', 'unexpected-request')) - interfaces = set(('code', 'condition', 'text', 'type')) - types = set(('cancel', 'continue', 'modify', 'auth', 'wait')) - sub_interfaces = set(('text',)) - condition_ns = 'urn:ietf:params:xml:ns:xmpp-stanzas' - - def setup(self, xml=None): - if ElementBase.setup(self, xml): #if we had to generate xml - self['type'] = 'cancel' - self['condition'] = 'feature-not-implemented' - if self.parent is not None: - self.parent()['type'] = 'error' - - def getCondition(self): - for child in self.xml.getchildren(): - if "{%s}" % self.condition_ns in child.tag: - return child.tag.split('}', 1)[-1] - return '' - - def setCondition(self, value): - if value in self.conditions: - for child in self.xml.getchildren(): - if "{%s}" % self.condition_ns in child.tag: - self.xml.remove(child) - condition = ET.Element("{%s}%s" % (self.condition_ns, value)) - self.xml.append(condition) - return self - - def delCondition(self): - return self - - def getText(self): - text = '' - textxml = self.xml.find("{urn:ietf:params:xml:ns:xmpp-stanzas}text") - if textxml is not None: - text = textxml.text - return text - - def setText(self, value): - self.delText() - textxml = ET.Element('{urn:ietf:params:xml:ns:xmpp-stanzas}text') - textxml.text = value - self.xml.append(textxml) - return self - - def delText(self): - textxml = self.xml.find("{urn:ietf:params:xml:ns:xmpp-stanzas}text") - if textxml is not None: - self.xml.remove(textxml) + + """ + XMPP stanzas of type 'error' should include an stanza that + describes the nature of the error and how it should be handled. + + Use the 'XEP-0086: Error Condition Mappings' plugin to include error + codes used in older XMPP versions. + + Example error stanza: + + + + The item was not found. + + + + Stanza Interface: + code -- The error code used in older XMPP versions. + condition -- The name of the condition element. + text -- Human readable description of the error. + type -- Error type indicating how the error should be handled. + + Attributes: + conditions -- The set of allowable error condition elements. + condition_ns -- The namespace for the condition element. + types -- A set of values indicating how the error + should be treated. + + Methods: + setup -- Overrides ElementBase.setup. + getCondition -- Retrieve the name of the condition element. + setCondition -- Add a condition element. + delCondition -- Remove the condition element. + getText -- Retrieve the contents of the element. + setText -- Set the contents of the element. + delText -- Remove the element. + """ + + namespace = 'jabber:client' + name = 'error' + plugin_attrib = 'error' + interfaces = set(('code', 'condition', 'text', 'type')) + sub_interfaces = set(('text',)) + conditions = set(('bad-request', 'conflict', 'feature-not-implemented', + 'forbidden', 'gone', 'internal-server-error', + 'item-not-found', 'jid-malformed', 'not-acceptable', + 'not-allowed', 'not-authorized', 'payment-required', + 'recipient-unavailable', 'redirect', + 'registration-required', 'remote-server-not-found', + 'remote-server-timeout', 'resource-constraint', + 'service-unavailable', 'subscription-required', + 'undefined-condition', 'unexpected-request')) + condition_ns = 'urn:ietf:params:xml:ns:xmpp-stanzas' + types = set(('cancel', 'continue', 'modify', 'auth', 'wait')) + + def setup(self, xml=None): + """ + Populate the stanza object using an optional XML object. + + Overrides ElementBase.setup. + + Sets a default error type and condition, and changes the + parent stanza's type to 'error'. + + Arguments: + xml -- Use an existing XML object for the stanza's values. + """ + if ElementBase.setup(self, xml): + #If we had to generate XML then set default values. + self['type'] = 'cancel' + self['condition'] = 'feature-not-implemented' + if self.parent is not None: + self.parent()['type'] = 'error' + + def getCondition(self): + """Return the condition element's name.""" + for child in self.xml.getchildren(): + if "{%s}" % self.condition_ns in child.tag: + return child.tag.split('}', 1)[-1] + return '' + + def setCondition(self, value): + """ + Set the tag name of the condition element. + + Arguments: + value -- The tag name of the condition element. + """ + if value in self.conditions: + del self['condition'] + self.xml.append(ET.Element("{%s}%s" % (self.condition_ns, value))) + return self + + def delCondition(self): + """Remove the condition element.""" + for child in self.xml.getchildren(): + if "{%s}" % self.condition_ns in child.tag: + self.xml.remove(child) + return self + + def getText(self): + """Retrieve the contents of the element.""" + return self._getSubText('{%s}text' % self.condition_ns) + + def setText(self, value): + """ + Set the contents of the element. + + Arguments: + value -- The new contents for the element. + """ + self._setSubText('{%s}text' % self.condition_ns, text=value) + return self + + def delText(self): + """Remove the element.""" + self._delSub('{%s}text' % self.condition_ns) + return self -- cgit v1.2.3 From 1da3e5b35eb59909d4d6903b1c0190a7aad98a30 Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Thu, 29 Jul 2010 23:55:13 -0400 Subject: Added unit tests for error stanzas. Corrected error in deleting conditions. --- sleekxmpp/stanza/error.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'sleekxmpp/stanza') diff --git a/sleekxmpp/stanza/error.py b/sleekxmpp/stanza/error.py index 3253d9c5..6d18c297 100644 --- a/sleekxmpp/stanza/error.py +++ b/sleekxmpp/stanza/error.py @@ -108,7 +108,9 @@ class Error(ElementBase): """Remove the condition element.""" for child in self.xml.getchildren(): if "{%s}" % self.condition_ns in child.tag: - self.xml.remove(child) + tag = child.tag.split('}', 1)[-1] + if tag in self.conditions: + self.xml.remove(child) return self def getText(self): -- cgit v1.2.3 From cbed8029bad691f8353854dc264f83303a196a09 Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Thu, 29 Jul 2010 23:58:25 -0400 Subject: Updated, cleaned, and documented Iq stanza class. Also added unit tests. --- sleekxmpp/stanza/iq.py | 233 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 165 insertions(+), 68 deletions(-) (limited to 'sleekxmpp/stanza') diff --git a/sleekxmpp/stanza/iq.py b/sleekxmpp/stanza/iq.py index daf05b43..c735ae64 100644 --- a/sleekxmpp/stanza/iq.py +++ b/sleekxmpp/stanza/iq.py @@ -5,73 +5,170 @@ See the file LICENSE for copying permission. """ -from .. xmlstream.stanzabase import StanzaBase -from xml.etree import cElementTree as ET -from . error import Error -from .. xmlstream.handler.waiter import Waiter -from .. xmlstream.matcher.id import MatcherId -from . rootstanza import RootStanza + +from sleekxmpp.stanza import Error +from sleekxmpp.stanza.rootstanza import RootStanza +from sleekxmpp.xmlstream import RESPONSE_TIMEOUT +from sleekxmpp.xmlstream.stanzabase import StanzaBase, ET +from sleekxmpp.xmlstream.handler import Waiter +from sleekxmpp.xmlstream.matcher import MatcherId + class Iq(RootStanza): - interfaces = set(('type', 'to', 'from', 'id','query')) - types = set(('get', 'result', 'set', 'error')) - name = 'iq' - plugin_attrib = name - namespace = 'jabber:client' - - def __init__(self, *args, **kwargs): - StanzaBase.__init__(self, *args, **kwargs) - if self['id'] == '': - if self.stream is not None: - self['id'] = self.stream.getNewId() - else: - self['id'] = '0' - - def unhandled(self): - if self['type'] in ('get', 'set'): - self.reply() - self['error']['condition'] = 'feature-not-implemented' - self['error']['text'] = 'No handlers registered for this request.' - self.send() - - def setPayload(self, value): - self.clear() - StanzaBase.setPayload(self, value) - return self - - def setQuery(self, value): - query = self.xml.find("{%s}query" % value) - if query is None and value: - self.clear() - query = ET.Element("{%s}query" % value) - self.xml.append(query) - return self - - def getQuery(self): - for child in self.xml.getchildren(): - if child.tag.endswith('query'): - ns =child.tag.split('}')[0] - if '{' in ns: - ns = ns[1:] - return ns - return '' - - def reply(self): - self['type'] = 'result' - StanzaBase.reply(self) - return self - - def delQuery(self): - for child in self.getchildren(): - if child.tag.endswith('query'): - self.xml.remove(child) - return self - - def send(self, block=True, timeout=10): - if block and self['type'] in ('get', 'set'): - waitfor = Waiter('IqWait_%s' % self['id'], MatcherId(self['id'])) - self.stream.registerHandler(waitfor) - StanzaBase.send(self) - return waitfor.wait(timeout) - else: - return StanzaBase.send(self) + + """ + XMPP stanzas, or info/query stanzas, are XMPP's method of + requesting and modifying information, similar to HTTP's GET and + POST methods. + + Each stanza must have an 'id' value which associates the + stanza with the response stanza. XMPP entities must always + be given a response stanza with a type of 'result' after + sending a stanza of type 'get' or 'set'. + + Most uses cases for stanzas will involve adding a + element whose namespace indicates the type of information + desired. However, some custom XMPP applications use stanzas + as a carrier stanza for an application-specific protocol instead. + + Example Stanzas: + + + + + + + + Friends + + + + + Stanza Interface: + query -- The namespace of the element if one exists. + + Methods: + __init__ -- Overrides StanzaBase.__init__. + unhandled -- Send error if there are no handlers. + setPayload -- Overrides StanzaBase.setPayload. + setQuery -- Add or modify a element. + getQuery -- Return the namespace of the element. + delQuery -- Remove the element. + reply -- Overrides StanzaBase.reply + send -- Overrides StanzaBase.send + """ + + namespace = 'jabber:client' + name = 'iq' + interfaces = set(('type', 'to', 'from', 'id', 'query')) + types = set(('get', 'result', 'set', 'error')) + plugin_attrib = name + + def __init__(self, *args, **kwargs): + """ + Initialize a new stanza with an 'id' value. + + Overrides StanzaBase.__init__. + """ + StanzaBase.__init__(self, *args, **kwargs) + if self['id'] == '': + if self.stream is not None: + self['id'] = self.stream.getNewId() + else: + self['id'] = '0' + + def unhandled(self): + """ + Send a feature-not-implemented error if the stanza is not handled. + + Overrides StanzaBase.unhandled. + """ + if self['type'] in ('get', 'set'): + self.reply() + self['error']['condition'] = 'feature-not-implemented' + self['error']['text'] = 'No handlers registered for this request.' + self.send() + + def setPayload(self, value): + """ + Set the XML contents of the stanza. + + Arguments: + value -- An XML object to use as the stanza's contents + """ + self.clear() + StanzaBase.setPayload(self, value) + return self + + def setQuery(self, value): + """ + Add or modify a element. + + Query elements are differentiated by their namespace. + + Arguments: + value -- The namespace of the element. + """ + query = self.xml.find("{%s}query" % value) + if query is None and value: + self.clear() + query = ET.Element("{%s}query" % value) + self.xml.append(query) + return self + + def getQuery(self): + """Return the namespace of the element.""" + for child in self.xml.getchildren(): + if child.tag.endswith('query'): + ns = child.tag.split('}')[0] + if '{' in ns: + ns = ns[1:] + return ns + return '' + + def delQuery(self): + """Remove the element.""" + for child in self.xml.getchildren(): + if child.tag.endswith('query'): + self.xml.remove(child) + return self + + def reply(self): + """ + Send a reply stanza. + + Overrides StanzaBase.reply + + Sets the 'type' to 'result' in addition to the default + StanzaBase.reply behavior. + """ + self['type'] = 'result' + StanzaBase.reply(self) + return self + + def send(self, block=True, timeout=RESPONSE_TIMEOUT): + """ + Send an stanza over the XML stream. + + The send call can optionally block until a response is received or + a timeout occurs. Be aware that using blocking in non-threaded event + handlers can drastically impact performance. + + Overrides StanzaBase.send + + Arguments: + block -- Specify if the send call will block until a response + is received, or a timeout occurs. Defaults to True. + timeout -- The length of time (in seconds) to wait for a response + before exiting the send call if blocking is used. + Defaults to sleekxmpp.xmlstream.RESPONSE_TIMEOUT + """ + if block and self['type'] in ('get', 'set'): + waitfor = Waiter('IqWait_%s' % self['id'], MatcherId(self['id'])) + self.stream.registerHandler(waitfor) + StanzaBase.send(self) + return waitfor.wait(timeout) + else: + return StanzaBase.send(self) -- cgit v1.2.3 From 1cedea280499c0b7f7debb6c95442038d8f2d89c Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Fri, 30 Jul 2010 14:11:24 -0400 Subject: Added optional default value to _getAttr. --- sleekxmpp/stanza/iq.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'sleekxmpp/stanza') diff --git a/sleekxmpp/stanza/iq.py b/sleekxmpp/stanza/iq.py index c735ae64..c5ef8bb4 100644 --- a/sleekxmpp/stanza/iq.py +++ b/sleekxmpp/stanza/iq.py @@ -49,6 +49,9 @@ class Iq(RootStanza): Stanza Interface: query -- The namespace of the element if one exists. + Attributes: + types -- May be one of: get, set, result, or error. + Methods: __init__ -- Overrides StanzaBase.__init__. unhandled -- Send error if there are no handlers. -- cgit v1.2.3 From 939ae298c2856f095526a9e0f52216e9dc4e7db1 Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Tue, 3 Aug 2010 12:19:45 -0400 Subject: Updated message stanzas and tests with documentation and PEP8 style. --- sleekxmpp/stanza/message.py | 188 +++++++++++++++++++++++++++++++------------- 1 file changed, 134 insertions(+), 54 deletions(-) (limited to 'sleekxmpp/stanza') diff --git a/sleekxmpp/stanza/message.py b/sleekxmpp/stanza/message.py index 75ecc232..560e1d47 100644 --- a/sleekxmpp/stanza/message.py +++ b/sleekxmpp/stanza/message.py @@ -5,59 +5,139 @@ See the file LICENSE for copying permission. """ -from .. xmlstream.stanzabase import StanzaBase -from xml.etree import cElementTree as ET -from . error import Error -from . rootstanza import RootStanza + +from sleekxmpp.stanza import Error +from sleekxmpp.stanza.rootstanza import RootStanza +from sleekxmpp.xmlstream.stanzabase import StanzaBase, ET + class Message(RootStanza): - interfaces = set(('type', 'to', 'from', 'id', 'body', 'subject', 'mucroom', 'mucnick')) - types = set((None, 'normal', 'chat', 'headline', 'error', 'groupchat')) - sub_interfaces = set(('body', 'subject')) - name = 'message' - plugin_attrib = name - namespace = 'jabber:client' - - def getType(self): - return self.xml.attrib.get('type', 'normal') - - def chat(self): - self['type'] = 'chat' - return self - - def normal(self): - self['type'] = 'normal' - return self - - def reply(self, body=None): - StanzaBase.reply(self) - if self['type'] == 'groupchat': - self['to'] = self['to'].bare - del self['id'] - if body is not None: - self['body'] = body - return self - - def getMucroom(self): - if self['type'] == 'groupchat': - return self['from'].bare - else: - return '' - - def setMucroom(self, value): - pass - - def delMucroom(self): - pass - - def getMucnick(self): - if self['type'] == 'groupchat': - return self['from'].resource - else: - return '' - - def setMucnick(self, value): - pass - - def delMucnick(self): - pass + + """ + XMPP's stanzas are a "push" mechanism to send information + to other XMPP entities without requiring a response. + + Chat clients will typically use stanzas that have a type + of either "chat" or "groupchat". + + When handling a message event, be sure to check if the message is + an error response. + + Example stanzas: + + Hi! + + + + Hi everyone! + + + Stanza Interface: + body -- The main contents of the message. + subject -- An optional description of the message's contents. + mucroom -- (Read-only) The name of the MUC room that sent the message. + mucnick -- (Read-only) The MUC nickname of message's sender. + + Attributes: + types -- May be one of: normal, chat, headline, groupchat, or error. + + Methods: + chat -- Set the message type to 'chat'. + normal -- Set the message type to 'normal'. + reply -- Overrides StanzaBase.reply + getType -- Overrides StanzaBase interface + getMucroom -- Return the name of the MUC room of the message. + setMucroom -- Dummy method to prevent assignment. + delMucroom -- Dummy method to prevent deletion. + getMucnick -- Return the MUC nickname of the message's sender. + setMucnick -- Dummy method to prevent assignment. + delMucnick -- Dummy method to prevent deletion. + """ + + namespace = 'jabber:client' + name = 'message' + interfaces = set(('type', 'to', 'from', 'id', 'body', 'subject', + 'mucroom', 'mucnick')) + sub_interfaces = set(('body', 'subject')) + plugin_attrib = name + types = set((None, 'normal', 'chat', 'headline', 'error', 'groupchat')) + + def getType(self): + """ + Return the message type. + + Overrides default stanza interface behavior. + + Returns 'normal' if no type attribute is present. + """ + return self._getAttr('type', 'normal') + + def chat(self): + """Set the message type to 'chat'.""" + self['type'] = 'chat' + return self + + def normal(self): + """Set the message type to 'chat'.""" + self['type'] = 'normal' + return self + + def reply(self, body=None): + """ + Create a message reply. + + Overrides StanzaBase.reply. + + Sets proper 'to' attribute if the message is from a MUC, and + adds a message body if one is given. + + Arguments: + body -- Optional text content for the message. + """ + StanzaBase.reply(self) + if self['type'] == 'groupchat': + self['to'] = self['to'].bare + + del self['id'] + + if body is not None: + self['body'] = body + return self + + def getMucroom(self): + """ + Return the name of the MUC room where the message originated. + + Read-only stanza interface. + """ + if self['type'] == 'groupchat': + return self['from'].bare + else: + return '' + + def getMucnick(self): + """ + Return the nickname of the MUC user that sent the message. + + Read-only stanza interface. + """ + if self['type'] == 'groupchat': + return self['from'].resource + else: + return '' + + def setMucroom(self, value): + """Dummy method to prevent modification.""" + pass + + def delMucroom(self): + """Dummy method to prevent deletion.""" + pass + + def setMucnick(self, value): + """Dummy method to prevent modification.""" + pass + + def delMucnick(self): + """Dummy method to prevent deletion.""" + pass -- cgit v1.2.3 From 41ab2b84604849d0e650ecd833554b3488733785 Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Tue, 3 Aug 2010 17:30:34 -0400 Subject: Updated presence stanza with documentation and PEP8 style. --- sleekxmpp/stanza/presence.py | 183 +++++++++++++++++++++++++++++++------------ 1 file changed, 135 insertions(+), 48 deletions(-) (limited to 'sleekxmpp/stanza') diff --git a/sleekxmpp/stanza/presence.py b/sleekxmpp/stanza/presence.py index ec681763..651bf34d 100644 --- a/sleekxmpp/stanza/presence.py +++ b/sleekxmpp/stanza/presence.py @@ -6,54 +6,141 @@ See the file LICENSE for copying permission. """ -from . error import Error -from . rootstanza import RootStanza -from .. xmlstream.stanzabase import StanzaBase, ET +from sleekxmpp.stanza import Error +from sleekxmpp.stanza.rootstanza import RootStanza +from sleekxmpp.xmlstream.stanzabase import StanzaBase, ET class Presence(RootStanza): - interfaces = set(('type', 'to', 'from', 'id', 'show', 'status', 'priority')) - types = set(('available', 'unavailable', 'error', 'probe', 'subscribe', 'subscribed', 'unsubscribe', 'unsubscribed')) - showtypes = set(('dnd', 'chat', 'xa', 'away')) - sub_interfaces = set(('show', 'status', 'priority')) - name = 'presence' - plugin_attrib = name - namespace = 'jabber:client' - - def setShow(self, show): - if show in self.showtypes: - self._setSubText('show', text=show) - return self - - def setType(self, value): - if value in self.types: - self['show'] = None - if value == 'available': - value = '' - self._setAttr('type', value) - elif value in self.showtypes: - self['show'] = value - return self - - def setPriority(self, value): - self._setSubText('priority', text = str(value)) - - def getPriority(self): - p = self._getSubText('priority') - if not p: p = 0 - return int(p) - - def getType(self): - out = self._getAttr('type') - if not out: - out = self['show'] - if not out or out is None: - out = 'available' - return out - - def reply(self): - if self['type'] == 'unsubscribe': - self['type'] = 'unsubscribed' - elif self['type'] == 'subscribe': - self['type'] = 'subscribed' - return StanzaBase.reply(self) + + """ + XMPP's stanza allows entities to know the status of other + clients and components. Since it is currently the only multi-cast + stanza in XMPP, many extensions add more information to + stanzas to broadcast to every entry in the roster, such as + capabilities, music choices, or locations (XEP-0115: Entity Capabilities + and XEP-0163: Personal Eventing Protocol). + + Since stanzas are broadcast when an XMPP entity changes + its status, the bulk of the traffic in an XMPP network will be from + stanzas. Therefore, do not include more information than + necessary in a status message or within a stanza in order + to help keep the network running smoothly. + + Example stanzas: + + + + away + Getting lunch. + 5 + + + + + + + Stanza Interface: + priority -- A value used by servers to determine message routing. + show -- The type of status, such as away or available for chat. + status -- Custom, human readable status message. + + Attributes: + types -- One of: available, unavailable, error, probe, + subscribe, subscribed, unsubscribe, + and unsubscribed. + showtypes -- One of: away, chat, dnd, and xa. + + Methods: + reply -- Overrides StanzaBase.reply + setShow -- Set the value of the element. + getType -- Get the value of the type attribute or element. + setType -- Set the value of the type attribute or element. + getPriority -- Get the value of the element. + setPriority -- Set the value of the element. + """ + + namespace = 'jabber:client' + name = 'presence' + interfaces = set(('type', 'to', 'from', 'id', 'show', + 'status', 'priority')) + sub_interfaces = set(('show', 'status', 'priority')) + plugin_attrib = name + + types = set(('available', 'unavailable', 'error', 'probe', 'subscribe', + 'subscribed', 'unsubscribe', 'unsubscribed')) + showtypes = set(('dnd', 'chat', 'xa', 'away')) + + def setShow(self, show): + """ + Set the value of the element. + + Arguments: + show -- Must be one of: away, chat, dnd, or xa. + """ + if show in self.showtypes: + self._setSubText('show', text=show) + return self + + def setType(self, value): + """ + Set the type attribute's value, and the element + if applicable. + + Arguments: + value -- Must be in either self.types or self.showtypes. + """ + if value in self.types: + self['show'] = None + if value == 'available': + value = '' + self._setAttr('type', value) + elif value in self.showtypes: + self['show'] = value + return self + + def setPriority(self, value): + """ + Set the entity's priority value. Some server use priority to + determine message routing behavior. + + Bot clients should typically use a priority of 0 if the same + JID is used elsewhere by a human-interacting client. + + Arguments: + value -- An integer value greater than or equal to 0. + """ + self._setSubText('priority', text=str(value)) + + def getPriority(self): + """ + Return the value of the element as an integer. + """ + p = self._getSubText('priority') + if not p: + p = 0 + return int(p) + + def getType(self): + """ + Return the value of the stanza's type attribute, or + the value of the element. + """ + out = self._getAttr('type') + if not out: + out = self['show'] + if not out or out is None: + out = 'available' + return out + + def reply(self): + """ + Set the appropriate presence reply type. + + Overrides StanzaBase.reply. + """ + if self['type'] == 'unsubscribe': + self['type'] = 'unsubscribed' + elif self['type'] == 'subscribe': + self['type'] = 'subscribed' + return StanzaBase.reply(self) -- cgit v1.2.3 From 183a3f1b87a8f5a6c4e174dec6de513a0bb5cb83 Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Tue, 3 Aug 2010 17:58:18 -0400 Subject: Updated XHTML-IM stanza with documentation and PEP8 style. --- sleekxmpp/stanza/htmlim.py | 97 +++++++++++++++++++++++++++++++++------------- 1 file changed, 71 insertions(+), 26 deletions(-) (limited to 'sleekxmpp/stanza') diff --git a/sleekxmpp/stanza/htmlim.py b/sleekxmpp/stanza/htmlim.py index 50195b11..c2f2f0c8 100644 --- a/sleekxmpp/stanza/htmlim.py +++ b/sleekxmpp/stanza/htmlim.py @@ -5,31 +5,76 @@ See the file LICENSE for copying permission. """ -from .. xmlstream.stanzabase import registerStanzaPlugin, ElementBase, ET + +from sleekxmpp.stanza import Message +from sleekxmpp.xmlstream.stanzabase import registerStanzaPlugin +from sleekxmpp.xmlstream.stanzabase import ElementBase, ET + class HTMLIM(ElementBase): - namespace = 'http://jabber.org/protocol/xhtml-im' - name = 'html' - plugin_attrib = 'html' - interfaces = set(('html',)) - plugin_attrib_map = set() - plugin_xml_map = set() - - def setHtml(self, html): - if isinstance(html, str): - html = ET.XML(html) - if html.tag != '{http://www.w3.org/1999/xhtml}body': - body = ET.Element('{http://www.w3.org/1999/xhtml}body') - body.append(html) - self.xml.append(body) - else: - self.xml.append(html) - - def getHtml(self): - html = self.xml.find('{http://www.w3.org/1999/xhtml}body') - if html is None: return '' - return html - - def delHtml(self): - if self.parent is not None: - self.parent().xml.remove(self.xml) + + """ + XEP-0071: XHTML-IM defines a method for embedding XHTML content + within a stanza so that lightweight markup can be used + to format the message contents and to create links. + + Only a subset of XHTML is recommended for use with XHTML-IM. + See the full spec at 'http://xmpp.org/extensions/xep-0071.html' + for more information. + + Example stanza: + + Non-html message content. + + +

HTML!

+ + +
+ + Stanza Interface: + body -- The contents of the HTML body tag. + + Methods: + getBody -- Return the HTML body contents. + setBody -- Set the HTML body contents. + delBody -- Remove the HTML body contents. + """ + + namespace = 'http://jabber.org/protocol/xhtml-im' + name = 'html' + interfaces = set(('body',)) + plugin_attrib = name + + def setBody(self, html): + """ + Set the contents of the HTML body. + + Arguments: + html -- Either a string or XML object. If the top level + element is not with a namespace of + 'http://www.w3.org/1999/xhtml', it will be wrapped. + """ + if isinstance(html, str): + html = ET.XML(html) + if html.tag != '{http://www.w3.org/1999/xhtml}body': + body = ET.Element('{http://www.w3.org/1999/xhtml}body') + body.append(html) + self.xml.append(body) + else: + self.xml.append(html) + + def getBody(self): + """Return the contents of the HTML body.""" + html = self.xml.find('{http://www.w3.org/1999/xhtml}body') + if html is None: + return '' + return html + + def delBody(self): + """Remove the HTML body contents.""" + if self.parent is not None: + self.parent().xml.remove(self.xml) + + +registerStanzaPlugin(Message, HTMLIM) -- cgit v1.2.3 From fec69be7318d9ad2a8e4ac128ac57f1969a6b852 Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Tue, 3 Aug 2010 18:32:53 -0400 Subject: Update nick stanza with documentation and PEP8 style. --- sleekxmpp/stanza/nick.py | 80 ++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 63 insertions(+), 17 deletions(-) (limited to 'sleekxmpp/stanza') diff --git a/sleekxmpp/stanza/nick.py b/sleekxmpp/stanza/nick.py index 47d4620a..de54b307 100644 --- a/sleekxmpp/stanza/nick.py +++ b/sleekxmpp/stanza/nick.py @@ -5,22 +5,68 @@ See the file LICENSE for copying permission. """ -from .. xmlstream.stanzabase import registerStanzaPlugin, ElementBase, ET + +from sleekxmpp.stanza import Message, Presence +from sleekxmpp.xmlstream.stanzabase import registerStanzaPlugin +from sleekxmpp.xmlstream.stanzabase import ElementBase, ET + class Nick(ElementBase): - namespace = 'http://jabber.org/nick/nick' - name = 'nick' - plugin_attrib = 'nick' - interfaces = set(('nick')) - plugin_attrib_map = set() - plugin_xml_map = set() - - def setNick(self, nick): - self.xml.text = nick - - def getNick(self): - return self.xml.text - - def delNick(self): - if self.parent is not None: - self.parent().xml.remove(self.xml) + + """ + XEP-0172: User Nickname allows the addition of a element + in several stanza types, including and stanzas. + + The nickname contained in a should be the global, friendly or + informal name chosen by the owner of a bare JID. The element + may be included when establishing communications with new entities, + such as normal XMPP users or MUC services. + + The nickname contained in a element will not necessarily be + the same as the nickname used in a MUC. + + Example stanzas: + + The User + ... + + + + The User + + + Stanza Interface: + nick -- A global, friendly or informal name chosen by a user. + + Methods: + getNick -- Return the nickname in the element. + setNick -- Add a element with the given nickname. + delNick -- Remove the element. + """ + + namespace = 'http://jabber.org/nick/nick' + name = 'nick' + plugin_attrib = name + interfaces = set(('nick',)) + + def setNick(self, nick): + """ + Add a element with the given nickname. + + Arguments: + nick -- A human readable, informal name. + """ + self.xml.text = nick + + def getNick(self): + """Return the nickname in the element.""" + return self.xml.text + + def delNick(self): + """Remove the element.""" + if self.parent is not None: + self.parent().xml.remove(self.xml) + + +registerStanzaPlugin(Message, Nick) +registerStanzaPlugin(Presence, Nick) -- cgit v1.2.3 From b40a48979636ccb4055294427292b2decf095fea Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Wed, 11 Aug 2010 23:32:14 -0400 Subject: Updated roster stanza with docs and PEP8 style. --- sleekxmpp/stanza/roster.py | 146 +++++++++++++++++++++++++++++++-------------- 1 file changed, 100 insertions(+), 46 deletions(-) (limited to 'sleekxmpp/stanza') diff --git a/sleekxmpp/stanza/roster.py b/sleekxmpp/stanza/roster.py index eda65ec7..292c8956 100644 --- a/sleekxmpp/stanza/roster.py +++ b/sleekxmpp/stanza/roster.py @@ -5,51 +5,105 @@ See the file LICENSE for copying permission. """ -from .. xmlstream.stanzabase import registerStanzaPlugin, ElementBase, ET, JID -import logging + +from sleekxmpp.stanza import Iq +from sleekxmpp.xmlstream import JID +from sleekxmpp.xmlstream.stanzabase import registerStanzaPlugin +from sleekxmpp.xmlstream.stanzabase import ET, ElementBase + class Roster(ElementBase): - namespace = 'jabber:iq:roster' - name = 'query' - plugin_attrib = 'roster' - interfaces = set(('items',)) - sub_interfaces = set() - - def setItems(self, items): - self.delItems() - for jid in items: - ijid = str(jid) - item = ET.Element('{jabber:iq:roster}item', {'jid': ijid}) - if 'subscription' in items[jid]: - item.attrib['subscription'] = items[jid]['subscription'] - if 'name' in items[jid]: - name = items[jid]['name'] - if name is not None: - item.attrib['name'] = name - if 'groups' in items[jid]: - for group in items[jid]['groups']: - groupxml = ET.Element('{jabber:iq:roster}group') - groupxml.text = group - item.append(groupxml) - self.xml.append(item) - return self - - def getItems(self): - items = {} - itemsxml = self.xml.findall('{jabber:iq:roster}item') - if itemsxml is not None: - for itemxml in itemsxml: - item = {} - item['name'] = itemxml.get('name', '') - item['subscription'] = itemxml.get('subscription', '') - item['groups'] = [] - groupsxml = itemxml.findall('{jabber:iq:roster}group') - if groupsxml is not None: - for groupxml in groupsxml: - item['groups'].append(groupxml.text) - items[itemxml.get('jid')] = item - return items - - def delItems(self): - for child in self.xml.getchildren(): - self.xml.remove(child) + + """ + Example roster stanzas: + + + + Friends + + + + + Stanza Inteface: + items -- A dictionary of roster entries contained + in the stanza. + + Methods: + getItems -- Return a dictionary of roster entries. + setItems -- Add elements. + delItems -- Remove all elements. + """ + + namespace = 'jabber:iq:roster' + name = 'query' + plugin_attrib = 'roster' + interfaces = set(('items',)) + + def setItems(self, items): + """ + Set the roster entries in the stanza. + + Uses a dictionary using JIDs as keys, where each entry is itself + a dictionary that contains: + name -- An alias or nickname for the JID. + subscription -- The subscription type. Can be one of 'to', + 'from', 'both', 'none', or 'remove'. + groups -- A list of group names to which the JID + has been assigned. + + Arguments: + items -- A dictionary of roster entries. + """ + self.delItems() + for jid in items: + ijid = str(jid) + item = ET.Element('{jabber:iq:roster}item', {'jid': ijid}) + if 'subscription' in items[jid]: + item.attrib['subscription'] = items[jid]['subscription'] + if 'name' in items[jid]: + name = items[jid]['name'] + if name is not None: + item.attrib['name'] = name + if 'groups' in items[jid]: + for group in items[jid]['groups']: + groupxml = ET.Element('{jabber:iq:roster}group') + groupxml.text = group + item.append(groupxml) + self.xml.append(item) + return self + + def getItems(self): + """ + Return a dictionary of roster entries. + + Each item is keyed using its JID, and contains: + name -- An assigned alias or nickname for the JID. + subscription -- The subscription type. Can be one of 'to', + 'from', 'both', 'none', or 'remove'. + groups -- A list of group names to which the JID has + been assigned. + """ + items = {} + itemsxml = self.xml.findall('{jabber:iq:roster}item') + if itemsxml is not None: + for itemxml in itemsxml: + item = {} + item['name'] = itemxml.get('name', '') + item['subscription'] = itemxml.get('subscription', '') + item['groups'] = [] + groupsxml = itemxml.findall('{jabber:iq:roster}group') + if groupsxml is not None: + for groupxml in groupsxml: + item['groups'].append(groupxml.text) + items[itemxml.get('jid')] = item + return items + + def delItems(self): + """ + Remove all elements from the roster stanza. + """ + for child in self.xml.getchildren(): + self.xml.remove(child) + + +registerStanzaPlugin(Iq, Roster) -- cgit v1.2.3