diff options
Diffstat (limited to 'sleekxmpp/clientxmpp.py')
-rw-r--r-- | sleekxmpp/clientxmpp.py | 69 |
1 files changed, 48 insertions, 21 deletions
diff --git a/sleekxmpp/clientxmpp.py b/sleekxmpp/clientxmpp.py index 20012b5f..590192db 100644 --- a/sleekxmpp/clientxmpp.py +++ b/sleekxmpp/clientxmpp.py @@ -15,22 +15,13 @@ from __future__ import absolute_import, unicode_literals import logging -import base64 -import sys -import hashlib -import random -import threading - -import sleekxmpp -from sleekxmpp import plugins -from sleekxmpp import stanza -from sleekxmpp import features + +from sleekxmpp.stanza import StreamFeatures from sleekxmpp.basexmpp import BaseXMPP -from sleekxmpp.stanza import * -from sleekxmpp.xmlstream import XMLStream, RestartStream -from sleekxmpp.xmlstream import StanzaBase, ET, register_stanza_plugin -from sleekxmpp.xmlstream.matcher import * -from sleekxmpp.xmlstream.handler import * +from sleekxmpp.exceptions import XMPPError +from sleekxmpp.xmlstream import XMLStream +from sleekxmpp.xmlstream.matcher import MatchXPath +from sleekxmpp.xmlstream.handler import Callback # Flag indicating if DNS SRV records are available for use. try: @@ -74,12 +65,15 @@ class ClientXMPP(BaseXMPP): BaseXMPP.__init__(self, jid, 'jabber:client') self.set_jid(jid) - self.password = password self.escape_quotes = escape_quotes self.plugin_config = plugin_config self.plugin_whitelist = plugin_whitelist self.default_port = 5222 + self.credentials = {} + + self.password = password + self.stream_header = "<stream:stream to='%s' %s %s version='1.0'>" % ( self.boundjid.host, "xmlns:stream='%s'" % self.stream_ns, @@ -97,6 +91,7 @@ class ClientXMPP(BaseXMPP): self.bindfail = False self.add_event_handler('connected', self._handle_connected) + self.add_event_handler('session_bind', self._handle_session_bind) self.register_stanza(StreamFeatures) @@ -117,6 +112,15 @@ class ClientXMPP(BaseXMPP): self.register_plugin('feature_session') self.register_plugin('feature_mechanisms', pconfig={'use_mech': sasl_mech} if sasl_mech else None) + self.register_plugin('feature_rosterver') + + @property + def password(self): + return self.credentials.get('password', '') + + @password.setter + def password(self, value): + self.credentials['password'] = value def connect(self, address=tuple(), reattempt=True, use_tls=True, use_ssl=False): @@ -154,8 +158,10 @@ class ClientXMPP(BaseXMPP): try: record = "_xmpp-client._tcp.%s" % domain answers = [] + log.debug("Querying SRV records for %s" % domain) for answer in dns.resolver.query(record, dns.rdatatype.SRV): address = (answer.target.to_text()[:-1], answer.port) + log.debug("Found SRV record: %s", address) answers.append((address, answer.priority, answer.weight)) except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer): log.warning("No SRV records for %s", domain) @@ -167,7 +173,7 @@ class ClientXMPP(BaseXMPP): return answers else: log.warning("dnspython is not installed -- " + \ - "relying on OS A record resolution") + "relying on OS A/AAAA record resolution") return [((domain, port), 0, 0)] def register_feature(self, name, handler, restart=False, order=5000): @@ -236,10 +242,17 @@ class ClientXMPP(BaseXMPP): iq = self.Iq() iq['type'] = 'get' iq.enable('roster') + if 'rosterver' in self.features: + iq['roster']['ver'] = self.client_roster.version + + if not block and callback is None: + callback = lambda resp: self._handle_roster(resp, request=True) + response = iq.send(block, timeout, callback) - if callback is None: - return self._handle_roster(response, request=True) + if block: + self._handle_roster(response, request=True) + return response def _handle_connected(self, event=None): #TODO: Use stream state here @@ -270,15 +283,22 @@ class ClientXMPP(BaseXMPP): to a request for the roster, and not an empty acknowledgement from the server. """ + if iq['from'].bare and iq['from'].bare != self.boundjid.bare: + raise XMPPError(condition='service-unavailable') if iq['type'] == 'set' or (iq['type'] == 'result' and request): + roster = self.client_roster + if iq['roster']['ver']: + roster.version = iq['roster']['ver'] for jid in iq['roster']['items']: item = iq['roster']['items'][jid] - roster = self.roster[iq['to'].bare] roster[jid]['name'] = item['name'] roster[jid]['groups'] = item['groups'] roster[jid]['from'] = item['subscription'] in ['from', 'both'] roster[jid]['to'] = item['subscription'] in ['to', 'both'] roster[jid]['pending_out'] = (item['ask'] == 'subscribe') + + roster[jid].save(remove=(item['subscription'] == 'remove')) + self.event('roster_received', iq) self.event("roster_update", iq) @@ -286,7 +306,14 @@ class ClientXMPP(BaseXMPP): iq.reply() iq.enable('roster') iq.send() - return True + + def _handle_session_bind(self, jid): + """Set the client roster to the JID set by the server. + + :param :class:`sleekxmpp.xmlstream.jid.JID` jid: The bound JID as + dictated by the server. The same as :attr:`boundjid`. + """ + self.client_roster = self.roster[jid] # To comply with PEP8, method names now use underscores. |