summaryrefslogtreecommitdiff
path: root/sleekxmpp/stanza
diff options
context:
space:
mode:
Diffstat (limited to 'sleekxmpp/stanza')
-rw-r--r--sleekxmpp/stanza/atom.py25
-rw-r--r--sleekxmpp/stanza/error.py2
-rw-r--r--sleekxmpp/stanza/htmlim.py71
-rw-r--r--sleekxmpp/stanza/iq.py61
-rw-r--r--sleekxmpp/stanza/message.py11
-rw-r--r--sleekxmpp/stanza/presence.py11
-rw-r--r--sleekxmpp/stanza/rootstanza.py4
-rw-r--r--sleekxmpp/stanza/roster.py5
8 files changed, 106 insertions, 84 deletions
diff --git a/sleekxmpp/stanza/atom.py b/sleekxmpp/stanza/atom.py
index 244ef315..4e9591a5 100644
--- a/sleekxmpp/stanza/atom.py
+++ b/sleekxmpp/stanza/atom.py
@@ -6,8 +6,7 @@
See the file LICENSE for copying permission.
"""
-from sleekxmpp.xmlstream import ElementBase
-
+from sleekxmpp.xmlstream import register_stanza_plugin, ElementBase
class AtomEntry(ElementBase):
@@ -22,5 +21,23 @@ class AtomEntry(ElementBase):
namespace = 'http://www.w3.org/2005/Atom'
name = 'entry'
plugin_attrib = 'entry'
- interfaces = set(('title', 'summary'))
- sub_interfaces = set(('title', 'summary'))
+ interfaces = set(('title', 'summary', 'id', 'published', 'updated'))
+ sub_interfaces = set(('title', 'summary', 'id', 'published',
+ 'updated'))
+
+class AtomAuthor(ElementBase):
+
+ """
+ An Atom author.
+
+ Stanza Interface:
+ name -- The printable author name
+ uri -- The bare jid of the author
+ """
+
+ name = 'author'
+ plugin_attrib = 'author'
+ interfaces = set(('name', 'uri'))
+ sub_interfaces = set(('name', 'uri'))
+
+register_stanza_plugin(AtomEntry, AtomAuthor)
diff --git a/sleekxmpp/stanza/error.py b/sleekxmpp/stanza/error.py
index 60bc65bc..56558ba8 100644
--- a/sleekxmpp/stanza/error.py
+++ b/sleekxmpp/stanza/error.py
@@ -52,7 +52,7 @@ class Error(ElementBase):
name = 'error'
plugin_attrib = 'error'
interfaces = set(('code', 'condition', 'text', 'type',
- 'gone', 'redirect'))
+ 'gone', 'redirect', 'by'))
sub_interfaces = set(('text',))
plugin_attrib_map = {}
plugin_tag_map = {}
diff --git a/sleekxmpp/stanza/htmlim.py b/sleekxmpp/stanza/htmlim.py
index d21a74e1..c43178f2 100644
--- a/sleekxmpp/stanza/htmlim.py
+++ b/sleekxmpp/stanza/htmlim.py
@@ -7,78 +7,13 @@
"""
from sleekxmpp.stanza import Message
-from sleekxmpp.xmlstream import ElementBase, ET, register_stanza_plugin
-
-
-class HTMLIM(ElementBase):
-
- """
- XEP-0071: XHTML-IM defines a method for embedding XHTML content
- within a <message> 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:
- <message to="user@example.com">
- <body>Non-html message content.</body>
- <html xmlns="http://jabber.org/protocol/xhtml-im">
- <body xmlns="http://www.w3.org/1999/xhtml">
- <p><b>HTML!</b></p>
- </body>
- </html>
- </message>
-
- Stanza Interface:
- body -- The contents of the HTML body tag.
-
- Methods:
- setup -- Overrides ElementBase.setup.
- get_body -- Return the HTML body contents.
- set_body -- Set the HTML body contents.
- del_body -- Remove the HTML body contents.
- """
-
- namespace = 'http://jabber.org/protocol/xhtml-im'
- name = 'html'
- interfaces = set(('body',))
- plugin_attrib = name
-
- def set_body(self, html):
- """
- Set the contents of the HTML body.
-
- Arguments:
- html -- Either a string or XML object. If the top level
- element is not <body> 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 get_body(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 del_body(self):
- """Remove the HTML body contents."""
- if self.parent is not None:
- self.parent().xml.remove(self.xml)
+from sleekxmpp.xmlstream import register_stanza_plugin
+from sleekxmpp.plugins.xep_0071 import XHTML_IM as HTMLIM
register_stanza_plugin(Message, HTMLIM)
+
# To comply with PEP8, method names now use underscores.
# Deprecated method names are re-mapped for backwards compatibility.
HTMLIM.setBody = HTMLIM.set_body
diff --git a/sleekxmpp/stanza/iq.py b/sleekxmpp/stanza/iq.py
index f45b3c67..088de4c0 100644
--- a/sleekxmpp/stanza/iq.py
+++ b/sleekxmpp/stanza/iq.py
@@ -9,7 +9,7 @@
from sleekxmpp.stanza.rootstanza import RootStanza
from sleekxmpp.xmlstream import StanzaBase, ET
from sleekxmpp.xmlstream.handler import Waiter, Callback
-from sleekxmpp.xmlstream.matcher import MatcherId
+from sleekxmpp.xmlstream.matcher import MatchIDSender, MatcherId
from sleekxmpp.exceptions import IqTimeout, IqError
@@ -115,9 +115,13 @@ class Iq(RootStanza):
"""
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)
+ plugin = self.plugin_tag_map.get('{%s}query' % value, None)
+ if plugin:
+ self.enable(plugin.plugin_attrib)
+ else:
+ self.clear()
+ query = ET.Element("{%s}query" % value)
+ self.xml.append(query)
return self
def get_query(self):
@@ -154,7 +158,7 @@ class Iq(RootStanza):
StanzaBase.reply(self, clear)
return self
- def send(self, block=True, timeout=None, callback=None, now=False):
+ def send(self, block=True, timeout=None, callback=None, now=False, timeout_callback=None):
"""
Send an <iq> stanza over the XML stream.
@@ -181,20 +185,47 @@ class Iq(RootStanza):
now -- Indicates if the send queue should be skipped and send
the stanza immediately. Used during stream
initialization. Defaults to False.
+ timeout_callback -- Optional reference to a stream handler function.
+ Will be executed when the timeout expires before a
+ response has been received with the originally-sent IQ
+ stanza. Only called if there is a callback parameter
+ (and therefore are in async mode).
"""
if timeout is None:
timeout = self.stream.response_timeout
+
+ if self.stream.session_bind_event.is_set():
+ matcher = MatchIDSender({
+ 'id': self['id'],
+ 'self': self.stream.boundjid,
+ 'peer': self['to']
+ })
+ else:
+ matcher = MatcherId(self['id'])
+
if callback is not None and self['type'] in ('get', 'set'):
handler_name = 'IqCallback_%s' % self['id']
- handler = Callback(handler_name,
- MatcherId(self['id']),
- callback,
- once=True)
+ if timeout_callback:
+ self.callback = callback
+ self.timeout_callback = timeout_callback
+ self.stream.schedule('IqTimeout_%s' % self['id'],
+ timeout,
+ self._fire_timeout,
+ repeat=False)
+ handler = Callback(handler_name,
+ matcher,
+ self._handle_result,
+ once=True)
+ else:
+ handler = Callback(handler_name,
+ matcher,
+ callback,
+ once=True)
self.stream.register_handler(handler)
StanzaBase.send(self, now=now)
return handler_name
elif block and self['type'] in ('get', 'set'):
- waitfor = Waiter('IqWait_%s' % self['id'], MatcherId(self['id']))
+ waitfor = Waiter('IqWait_%s' % self['id'], matcher)
self.stream.register_handler(waitfor)
StanzaBase.send(self, now=now)
result = waitfor.wait(timeout)
@@ -206,6 +237,16 @@ class Iq(RootStanza):
else:
return StanzaBase.send(self, now=now)
+ def _handle_result(self, iq):
+ # we got the IQ, so don't fire the timeout
+ self.stream.scheduler.remove('IqTimeout_%s' % self['id'])
+ self.callback(iq)
+
+ def _fire_timeout(self):
+ # don't fire the handler for the IQ, if it finally does come in
+ self.stream.remove_handler('IqCallback_%s' % self['id'])
+ self.timeout_callback(self)
+
def _set_stanza_values(self, values):
"""
Set multiple stanza interface values using a dictionary.
diff --git a/sleekxmpp/stanza/message.py b/sleekxmpp/stanza/message.py
index 02133682..0bb6e587 100644
--- a/sleekxmpp/stanza/message.py
+++ b/sleekxmpp/stanza/message.py
@@ -63,6 +63,17 @@ class Message(RootStanza):
lang_interfaces = sub_interfaces
types = set(['normal', 'chat', 'headline', 'error', 'groupchat'])
+ def __init__(self, *args, **kwargs):
+ """
+ Initialize a new <message /> stanza with an optional 'id' value.
+
+ Overrides StanzaBase.__init__.
+ """
+ StanzaBase.__init__(self, *args, **kwargs)
+ if self['id'] == '':
+ if self.stream is not None and self.stream.use_message_ids:
+ self['id'] = self.stream.new_id()
+
def get_type(self):
"""
Return the message type.
diff --git a/sleekxmpp/stanza/presence.py b/sleekxmpp/stanza/presence.py
index 7951f861..84bcd122 100644
--- a/sleekxmpp/stanza/presence.py
+++ b/sleekxmpp/stanza/presence.py
@@ -72,6 +72,17 @@ class Presence(RootStanza):
'subscribed', 'unsubscribe', 'unsubscribed'])
showtypes = set(['dnd', 'chat', 'xa', 'away'])
+ def __init__(self, *args, **kwargs):
+ """
+ Initialize a new <presence /> stanza with an optional 'id' value.
+
+ Overrides StanzaBase.__init__.
+ """
+ StanzaBase.__init__(self, *args, **kwargs)
+ if self['id'] == '':
+ if self.stream is not None and self.stream.use_presence_ids:
+ self['id'] = self.stream.new_id()
+
def exception(self, e):
"""
Override exception passback for presence.
diff --git a/sleekxmpp/stanza/rootstanza.py b/sleekxmpp/stanza/rootstanza.py
index a7c2b218..52b807e5 100644
--- a/sleekxmpp/stanza/rootstanza.py
+++ b/sleekxmpp/stanza/rootstanza.py
@@ -60,7 +60,9 @@ class RootStanza(StanzaBase):
self.send()
elif isinstance(e, XMPPError):
# We raised this deliberately
+ keep_id = self['id']
self.reply(clear=e.clear)
+ self['id'] = keep_id
self['error']['condition'] = e.condition
self['error']['text'] = e.text
self['error']['type'] = e.etype
@@ -72,7 +74,9 @@ class RootStanza(StanzaBase):
self.send()
else:
# We probably didn't raise this on purpose, so send an error stanza
+ keep_id = self['id']
self.reply()
+ self['id'] = keep_id
self['error']['condition'] = 'undefined-condition'
self['error']['text'] = "SleekXMPP got into trouble."
self['error']['type'] = 'cancel'
diff --git a/sleekxmpp/stanza/roster.py b/sleekxmpp/stanza/roster.py
index a415c482..681efd4f 100644
--- a/sleekxmpp/stanza/roster.py
+++ b/sleekxmpp/stanza/roster.py
@@ -130,7 +130,10 @@ class RosterItem(ElementBase):
def get_groups(self):
groups = []
for group in self.xml.findall('{%s}group' % self.namespace):
- groups.append(group.text)
+ if group.text:
+ groups.append(group.text)
+ else:
+ groups.append('')
return groups
def set_groups(self, values):