1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 """Basic Jabber client functionality implementation.
17
18 Extends `pyxmpp.client` interface with legacy authentication
19 and basic Service Discovery handling.
20
21 Normative reference:
22 - `JEP 78 <http://www.jabber.org/jeps/jep-0078.html>`__
23 - `JEP 30 <http://www.jabber.org/jeps/jep-0030.html>`__
24 """
25
26 __docformat__="restructuredtext en"
27
28 import logging
29
30 from pyxmpp.jabber.clientstream import LegacyClientStream
31 from pyxmpp.jabber.disco import DISCO_ITEMS_NS,DISCO_INFO_NS
32 from pyxmpp.jabber.disco import DiscoInfo,DiscoItems,DiscoIdentity
33 from pyxmpp.jabber import disco
34 from pyxmpp.client import Client
35 from pyxmpp.stanza import Stanza
36 from pyxmpp.cache import CacheSuite
37 from pyxmpp.utils import from_utf8
38 from pyxmpp.interfaces import IFeaturesProvider
39
41 """Base class for a Jabber client.
42
43 :Ivariables:
44 - `disco_items`: default Disco#items reply for a query to an empty node.
45 - `disco_info`: default Disco#info reply for a query to an empty node --
46 provides information about the client and its supported fetures.
47 - `disco_identity`: default identity of the default `disco_info`.
48 - `register`: when `True` than registration will be started instead of authentication.
49 :Types:
50 - `disco_items`: `DiscoItems`
51 - `disco_info`: `DiscoInfo`
52 - `register`: `bool`
53 """
54 - def __init__(self,jid=None, password=None, server=None, port=5222,
55 auth_methods=("sasl:DIGEST-MD5","digest"),
56 tls_settings=None, keepalive=0,
57 disco_name=u"pyxmpp based Jabber client", disco_category=u"client",
58 disco_type=u"pc"):
59 """Initialize a JabberClient object.
60
61 :Parameters:
62 - `jid`: user full JID for the connection.
63 - `password`: user password.
64 - `server`: server to use. If not given then address will be derived form the JID.
65 - `port`: port number to use. If not given then address will be derived form the JID.
66 - `auth_methods`: sallowed authentication methods. SASL authentication mechanisms
67 in the list should be prefixed with "sasl:" string.
68 - `tls_settings`: settings for StartTLS -- `TLSSettings` instance.
69 - `keepalive`: keepalive output interval. 0 to disable.
70 - `disco_name`: name of the client identity in the disco#info
71 replies.
72 - `disco_category`: category of the client identity in the disco#info
73 replies. The default of u'client' should be the right choice in
74 most cases.
75 - `disco_type`: type of the client identity in the disco#info
76 replies. Use `the types registered by Jabber Registrar <http://www.jabber.org/registrar/disco-categories.html>`__
77 :Types:
78 - `jid`: `pyxmpp.JID`
79 - `password`: `unicode`
80 - `server`: `unicode`
81 - `port`: `int`
82 - `auth_methods`: sequence of `str`
83 - `tls_settings`: `pyxmpp.TLSSettings`
84 - `keepalive`: `int`
85 - `disco_name`: `unicode`
86 - `disco_category`: `unicode`
87 - `disco_type`: `unicode`
88 """
89
90 Client.__init__(self,jid,password,server,port,auth_methods,tls_settings,keepalive)
91 self.stream_class = LegacyClientStream
92 self.disco_items=DiscoItems()
93 self.disco_info=DiscoInfo()
94 self.disco_identity=DiscoIdentity(self.disco_info,
95 disco_name, disco_category, disco_type)
96 self.register_feature(u"dnssrv")
97 self.register_feature(u"stringprep")
98 self.register_feature(u"urn:ietf:params:xml:ns:xmpp-sasl#c2s")
99 self.cache = CacheSuite(max_items = 1000)
100 self.__logger = logging.getLogger("pyxmpp.jabber.JabberClient")
101
102
103
104 - def connect(self, register = False):
105 """Connect to the server and set up the stream.
106
107 Set `self.stream` and notify `self.state_changed` when connection
108 succeeds. Additionally, initialize Disco items and info of the client.
109 """
110 Client.connect(self, register)
111 if register:
112 self.stream.registration_callback = self.process_registration_form
113
115 """Register a feature to be announced by Service Discovery.
116
117 :Parameters:
118 - `feature_name`: feature namespace or name.
119 :Types:
120 - `feature_name`: `unicode`"""
121 self.disco_info.add_feature(feature_name)
122
124 """Unregister a feature to be announced by Service Discovery.
125
126 :Parameters:
127 - `feature_name`: feature namespace or name.
128 :Types:
129 - `feature_name`: `unicode`"""
130 self.disco_info.remove_feature(feature_name)
131
140
141
143 """Handle a disco#info request.
144
145 `self.disco_get_info` method will be used to prepare the query response.
146
147 :Parameters:
148 - `iq`: the IQ stanza received.
149 :Types:
150 - `iq`: `pyxmpp.iq.Iq`"""
151 q=iq.get_query()
152 if q.hasProp("node"):
153 node=from_utf8(q.prop("node"))
154 else:
155 node=None
156 info=self.disco_get_info(node,iq)
157 if isinstance(info,DiscoInfo):
158 resp=iq.make_result_response()
159 self.__logger.debug("Disco-info query: %s preparing response: %s with reply: %s"
160 % (iq.serialize(),resp.serialize(),info.xmlnode.serialize()))
161 resp.set_content(info.xmlnode.copyNode(1))
162 elif isinstance(info,Stanza):
163 resp=info
164 else:
165 resp=iq.make_error_response("item-not-found")
166 self.__logger.debug("Disco-info response: %s" % (resp.serialize(),))
167 self.stream.send(resp)
168
195
206
207
208
216
218 """Return Disco#info data for a node.
219
220 :Parameters:
221 - `node`: the node queried.
222 - `iq`: the request stanza received.
223 :Types:
224 - `node`: `unicode`
225 - `iq`: `pyxmpp.iq.Iq`
226
227 :return: self.disco_info if `node` is empty or `None` otherwise.
228 :returntype: `DiscoInfo`"""
229 to=iq.get_to()
230 if to and to!=self.jid:
231 return iq.make_error_response("recipient-unavailable")
232 if not node and self.disco_info:
233 return self.disco_info
234 return None
235
237 """Return Disco#items data for a node.
238
239 :Parameters:
240 - `node`: the node queried.
241 - `iq`: the request stanza received.
242 :Types:
243 - `node`: `unicode`
244 - `iq`: `pyxmpp.iq.Iq`
245
246 :return: self.disco_info if `node` is empty or `None` otherwise.
247 :returntype: `DiscoInfo`"""
248 to=iq.get_to()
249 if to and to!=self.jid:
250 return iq.make_error_response("recipient-unavailable")
251 if not node and self.disco_items:
252 return self.disco_items
253 return None
254
285
286
287