summaryrefslogtreecommitdiff
path: root/sleekxmpp/basexmpp.py
diff options
context:
space:
mode:
Diffstat (limited to 'sleekxmpp/basexmpp.py')
-rw-r--r--sleekxmpp/basexmpp.py133
1 files changed, 75 insertions, 58 deletions
diff --git a/sleekxmpp/basexmpp.py b/sleekxmpp/basexmpp.py
index da5b3e41..8cd61b63 100644
--- a/sleekxmpp/basexmpp.py
+++ b/sleekxmpp/basexmpp.py
@@ -18,15 +18,13 @@ import sys
import logging
import threading
-import sleekxmpp
-from sleekxmpp import plugins, features, roster
+from sleekxmpp import plugins, roster, stanza
from sleekxmpp.api import APIRegistry
from sleekxmpp.exceptions import IqError, IqTimeout
from sleekxmpp.stanza import Message, Presence, Iq, StreamError
from sleekxmpp.stanza.roster import Roster
from sleekxmpp.stanza.nick import Nick
-from sleekxmpp.stanza.htmlim import HTMLIM
from sleekxmpp.xmlstream import XMLStream, JID
from sleekxmpp.xmlstream import ET, register_stanza_plugin
@@ -34,8 +32,7 @@ from sleekxmpp.xmlstream.matcher import MatchXPath
from sleekxmpp.xmlstream.handler import Callback
from sleekxmpp.xmlstream.stanzabase import XML_NS
-from sleekxmpp.features import *
-from sleekxmpp.plugins import PluginManager, register_plugin, load_plugin
+from sleekxmpp.plugins import PluginManager, load_plugin
log = logging.getLogger(__name__)
@@ -43,8 +40,8 @@ log = logging.getLogger(__name__)
# In order to make sure that Unicode is handled properly
# in Python 2.x, reset the default encoding.
if sys.version_info < (3, 0):
- reload(sys)
- sys.setdefaultencoding('utf8')
+ from sleekxmpp.util.misc_ops import setdefaultencoding
+ setdefaultencoding('utf8')
class BaseXMPP(XMLStream):
@@ -68,10 +65,20 @@ class BaseXMPP(XMLStream):
#: An identifier for the stream as given by the server.
self.stream_id = None
- #: The JabberID (JID) used by this connection.
- self.boundjid = JID(jid)
+ #: The JabberID (JID) requested for this connection.
+ self.requested_jid = JID(jid, cache_lock=True)
+
+ #: The JabberID (JID) used by this connection,
+ #: as set after session binding. This may even be a
+ #: different bare JID than what was requested.
+ self.boundjid = JID(jid, cache_lock=True)
self._expected_server_name = self.boundjid.host
+ self._redirect_attempts = 0
+
+ #: The maximum number of consecutive see-other-host
+ #: redirections that will be followed before quitting.
+ self.max_redirects = 5
self.session_bind_event = threading.Event()
@@ -91,19 +98,30 @@ class BaseXMPP(XMLStream):
#: owner JIDs, as in the case for components. For clients
#: which only have a single JID, see :attr:`client_roster`.
self.roster = roster.Roster(self)
- self.roster.add(self.boundjid.bare)
+ self.roster.add(self.boundjid)
#: The single roster for the bound JID. This is the
#: equivalent of::
#:
#: self.roster[self.boundjid.bare]
- self.client_roster = self.roster[self.boundjid.bare]
+ self.client_roster = self.roster[self.boundjid]
#: The distinction between clients and components can be
#: important, primarily for choosing how to handle the
#: ``'to'`` and ``'from'`` JIDs of stanzas.
self.is_component = False
+ #: Messages may optionally be tagged with ID values. Setting
+ #: :attr:`use_message_ids` to `True` will assign all outgoing
+ #: messages an ID. Some plugin features require enabling
+ #: this option.
+ self.use_message_ids = False
+
+ #: Presence updates may optionally be tagged with ID values.
+ #: Setting :attr:`use_message_ids` to `True` will assign all
+ #: outgoing messages an ID.
+ self.use_presence_ids = False
+
#: The API registry is a way to process callbacks based on
#: JID+node combinations. Each callback in the registry is
#: marked with:
@@ -127,7 +145,7 @@ class BaseXMPP(XMLStream):
#: A reference to :mod:`sleekxmpp.stanza` to make accessing
#: stanza classes easier.
- self.stanza = sleekxmpp.stanza
+ self.stanza = stanza
self.register_handler(
Callback('IM',
@@ -144,6 +162,8 @@ class BaseXMPP(XMLStream):
MatchXPath("{%s}error" % self.stream_ns),
self._handle_stream_error))
+ self.add_event_handler('session_start',
+ self._handle_session_start)
self.add_event_handler('disconnected',
self._handle_disconnected)
self.add_event_handler('presence_available',
@@ -178,7 +198,6 @@ class BaseXMPP(XMLStream):
# Initialize a few default stanza plugins.
register_stanza_plugin(Iq, Roster)
register_stanza_plugin(Message, Nick)
- register_stanza_plugin(Message, HTMLIM)
def start_stream_handler(self, xml):
"""Save the stream ID once the streams have been established.
@@ -189,6 +208,10 @@ class BaseXMPP(XMLStream):
self.stream_version = xml.get('version', '')
self.peer_default_lang = xml.get('{%s}lang' % XML_NS, None)
+ if not self.is_component and not self.stream_version:
+ log.warning('Legacy XMPP 0.9 protocol detected.')
+ self.event('legacy_protocol')
+
def process(self, *args, **kwargs):
"""Initialize plugins and begin processing the XML stream.
@@ -214,13 +237,6 @@ class BaseXMPP(XMLStream):
- The send queue processor
- The scheduler
"""
- if 'xep_0115' in self.plugin:
- name = 'xep_0115'
- if not hasattr(self.plugin[name], 'post_inited'):
- if hasattr(self.plugin[name], 'post_init'):
- self.plugin[name].post_init()
- self.plugin[name].post_inited = True
-
for name in self.plugin:
if not hasattr(self.plugin[name], 'post_inited'):
if hasattr(self.plugin[name], 'post_init'):
@@ -228,7 +244,7 @@ class BaseXMPP(XMLStream):
self.plugin[name].post_inited = True
return XMLStream.process(self, *args, **kwargs)
- def register_plugin(self, plugin, pconfig={}, module=None):
+ def register_plugin(self, plugin, pconfig=None, module=None):
"""Register and configure a plugin for use in this stream.
:param plugin: The name of the plugin class. Plugin names must
@@ -591,7 +607,7 @@ class BaseXMPP(XMLStream):
@resource.setter
def resource(self, value):
- log.warning("fulljid property deprecated. Use boundjid.full")
+ log.warning("fulljid property deprecated. Use boundjid.resource")
self.boundjid.resource = value
@property
@@ -645,7 +661,7 @@ class BaseXMPP(XMLStream):
def set_jid(self, jid):
"""Rip a JID apart and claim it as our own."""
log.debug("setting jid to %s", jid)
- self.boundjid.full = jid
+ self.boundjid = JID(jid, cache_lock=True)
def getjidresource(self, fulljid):
if '/' in fulljid:
@@ -656,6 +672,10 @@ class BaseXMPP(XMLStream):
def getjidbare(self, fulljid):
return fulljid.split('/', 1)[0]
+ def _handle_session_start(self, event):
+ """Reset redirection attempt count."""
+ self._redirect_attempts = 0
+
def _handle_disconnected(self, event):
"""When disconnected, reset the roster"""
self.roster.reset()
@@ -666,6 +686,15 @@ class BaseXMPP(XMLStream):
if error['condition'] == 'see-other-host':
other_host = error['see_other_host']
+ if not other_host:
+ log.warning("No other host specified.")
+ return
+
+ if self._redirect_attempts > self.max_redirects:
+ log.error("Exceeded maximum number of redirection attempts.")
+ return
+
+ self._redirect_attempts += 1
host = other_host
port = 5222
@@ -691,17 +720,13 @@ class BaseXMPP(XMLStream):
msg['to'] = self.boundjid
self.event('message', msg)
- def _handle_available(self, presence):
- pto = presence['to'].bare
- pfrom = presence['from'].bare
- self.roster[pto][pfrom].handle_available(presence)
+ def _handle_available(self, pres):
+ self.roster[pres['to']][pres['from']].handle_available(pres)
- def _handle_unavailable(self, presence):
- pto = presence['to'].bare
- pfrom = presence['from'].bare
- self.roster[pto][pfrom].handle_unavailable(presence)
+ def _handle_unavailable(self, pres):
+ self.roster[pres['to']][pres['from']].handle_unavailable(pres)
- def _handle_new_subscription(self, stanza):
+ def _handle_new_subscription(self, pres):
"""Attempt to automatically handle subscription requests.
Subscriptions will be approved if the request is from
@@ -713,10 +738,12 @@ class BaseXMPP(XMLStream):
If a subscription is accepted, a request for a mutual
subscription will be sent if :attr:`auto_subscribe` is ``True``.
"""
- roster = self.roster[stanza['to'].bare]
- item = self.roster[stanza['to'].bare][stanza['from'].bare]
+ roster = self.roster[pres['to']]
+ item = self.roster[pres['to']][pres['from']]
if item['whitelisted']:
item.authorize()
+ if roster.auto_subscribe:
+ item.subscribe()
elif roster.auto_authorize:
item.authorize()
if roster.auto_subscribe:
@@ -724,30 +751,20 @@ class BaseXMPP(XMLStream):
elif roster.auto_authorize == False:
item.unauthorize()
- def _handle_removed_subscription(self, presence):
- pto = presence['to'].bare
- pfrom = presence['from'].bare
- self.roster[pto][pfrom].unauthorize()
-
- def _handle_subscribe(self, presence):
- pto = presence['to'].bare
- pfrom = presence['from'].bare
- self.roster[pto][pfrom].handle_subscribe(presence)
-
- def _handle_subscribed(self, presence):
- pto = presence['to'].bare
- pfrom = presence['from'].bare
- self.roster[pto][pfrom].handle_subscribed(presence)
-
- def _handle_unsubscribe(self, presence):
- pto = presence['to'].bare
- pfrom = presence['from'].bare
- self.roster[pto][pfrom].handle_unsubscribe(presence)
-
- def _handle_unsubscribed(self, presence):
- pto = presence['to'].bare
- pfrom = presence['from'].bare
- self.roster[pto][pfrom].handle_unsubscribed(presence)
+ def _handle_removed_subscription(self, pres):
+ self.roster[pres['to']][pres['from']].handle_unauthorize(pres)
+
+ def _handle_subscribe(self, pres):
+ self.roster[pres['to']][pres['from']].handle_subscribe(pres)
+
+ def _handle_subscribed(self, pres):
+ self.roster[pres['to']][pres['from']].handle_subscribed(pres)
+
+ def _handle_unsubscribe(self, pres):
+ self.roster[pres['to']][pres['from']].handle_unsubscribe(pres)
+
+ def _handle_unsubscribed(self, pres):
+ self.roster[pres['to']][pres['from']].handle_unsubscribed(pres)
def _handle_presence(self, presence):
"""Process incoming presence stanzas.