root/trunk/RBJabber/RBJabber/Client.py

Revision 400, 10.4 kB (checked in by sholloway, 6 years ago)

Integrated SubjectObserver? from old foundation into tree for meantime

Line 
1 #!/usr/bin/env python
2 ##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3 ##~ License
4 ##~
5 ##- The RuneBlade Foundation library is intended to ease some
6 ##- aspects of writing intricate Jabber, XML, and User Interface (wxPython, etc.)
7 ##- applications, while providing the flexibility to modularly change the
8 ##- architecture. Enjoy.
9 ##~
10 ##~ Copyright (C) 2002  TechGame Networks, LLC.
11 ##~
12 ##~ This library is free software; you can redistribute it and/or
13 ##~ modify it under the terms of the BSD style License as found in the
14 ##~ LICENSE file included with this distribution.
15 ##~
16 ##~ TechGame Networks, LLC can be reached at:
17 ##~ 3578 E. Hartsel Drive #211
18 ##~ Colorado Springs, Colorado, USA, 80920
19 ##~
20 ##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
21
22 """Modular Jabber client
23
24 Dependencies:
25     RBFoundation
26     RBJabber
27
28 The Jabber protocol can be found at http://jabber.org,
29 and at the time of writting, specifically at
30 http://docs.jabber.org/general/html/protocol.html
31
32 """
33
34 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
35 #~ Imports
36 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
37
38 from xml.sax.saxutils import escape, quoteattr
39 from RBFoundation.XMLClassBuilder import ElementFactory as EF
40 from JabberConnection import JabberConnection
41 import ClientNodes
42 import JID
43 import SubjectObserver
44
45 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
46 #~ Jabber Client Class
47 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
48
49 class Client(JabberConnection):
50     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
51     #~ Constants / Variables / Etc.
52     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
53
54     ElementFactories = JabberConnection.ElementFactories.copy()
55     ElementFactories.update({
56         # http://etherx.jabber.org/streams nodes
57         ('http://etherx.jabber.org/streams', 'stream'): EF.Static(ClientNodes.JabberStream),
58         ('http://etherx.jabber.org/streams', 'error'): EF.Static(ClientNodes.JabberStreamError),
59
60         # jabber:client nodes
61         ('jabber:client', 'presence'): EF.Static(ClientNodes.JabberClientPresence),
62         ('jabber:client', 'message'): EF.Static(ClientNodes.JabberClientMessage),
63         ('jabber:client', 'iq'): EF.Static(ClientNodes.JabberClientIQ),
64
65         None: EF.Static(ClientNodes.JabberNode), # Default if no match with a defined (namespace, node)
66         })
67
68     stream = None
69     information = None
70     SupportedNS = None
71
72     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
73     #~ Special
74     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
75
76     def __init__(self, *args, **kw):
77         self.stream = SubjectObserver.BidableCategorySubject()
78         self.stream.IncludeSend = kw.get('xmlsend', 0)
79         self.stream.IncludeRecv = kw.get('xmlrecv', 0)
80         self.stream.IncludeTick = kw.get('processtick', 0)
81         self.information = SubjectObserver.AttributedSubject.AttributedSubject()
82         self.information.jid = JID.JID()
83         self.information.authorized = 0
84         self.__super.__init__(*args, **kw)
85        
86         self.SupportedNS = []
87         self.__NextID = 0
88
89     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
90     #~ Protected Methods
91     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
92
93     def _GetNextID(self):
94         """Used by various methods to obtain a "new id" for a message."""
95         self.__NextID += 1
96         return str(self.__NextID)
97
98     def _SetSocketError(self, exc_class, exc_info, exc_traceback):
99         self.stream.UpdateObservers(socket_error=(exc_class, exc_info))
100         del exc_class
101         del exc_info
102         del exc_traceback
103
104     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
105     #~ Public Methods
106     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
107
108     def Shutdown(self):
109         """Politely disconnects the socket and parser from the jabber stream"""
110         self.__super.Shutdown()
111         self.information.jid = JID.JID()
112         self.information.authorized = 0
113
114     def Authenticate(self, username, password, resource, callback=None):
115         """Authenticates with the Jabber server. 
116         Assumes basic authentication protocol.
117
118         Returns the iqAuthQuery instance that is sent.
119
120         See also: iqAuthQuery"""
121         import iqAuthQuery
122         query = iqAuthQuery.iqAuthQuery(self, username, password, resource, callback)
123         return query
124
125     def Register(self, username, password, callback=None):
126         """Registers a new account with the Jabber server. 
127         Assumes basic registration protocol.
128
129         Returns the iqQuery instance that is sent.
130
131         See also: iqQuery"""
132         import iqQuery
133         strXML = '<username>%s</username><password>%s</password>' % (username, password)
134         query = iqQuery.iqQuery(self, callback)
135         query.SendQuery("jabber:iq:register", self.ServerJID, 'set', strXML)
136         return query
137
138     def SetPassword(self, password, callback=None):
139         """Registers a new password with the Jabber server. 
140         Assumes user is already authenticated and a basic registration protocol.
141
142         Returns the iqQuery instance that is sent.
143
144         See also: iqQuery"""
145         import iqQuery
146         strXML = '<password>%s</password>' % password
147         query = iqQuery.iqQuery(self, callback)
148         query.SendQuery("jabber:iq:register", self.ServerJID, 'set', strXML)
149         return query
150
151     def QueryRoster(self, callback=None):
152         """Requests the jabber:iq:roster for the currently authenticated JID.
153
154         Returns the iqRosterQuery instance that is sent.
155
156         See also: iqRosterQuery"""
157         import iqRosterQuery
158         return iqRosterQuery.iqRosterQuery(self, callback)
159
160     def BrowseJID(self, toJID, callback):
161         """Requests the jabber:iq:browse data for the currently authenticated JID.
162
163         Returns the iqQuery instance that is sent.
164
165         See also: iqQuery"""
166         import iqQuery
167         query = iqQuery.iqQuery(self, callback)
168         query.SendQuery("jabber:iq:browse", toJID)
169         return query
170
171     def SetPublicData(self, namespace, xml):
172         """Saves xml data into namespace.
173
174         Returns the sent XML."""
175         return self.SendXML('''<iq id='%s' type='set'><query xmlns='%s'>%s</query></iq> ''' % (self._GetNextID(), namespace, xml))
176     SetData = SetPublicData # Alias
177  
178     def SetPrivateData(self, xml):
179         """Saves xml data into the jabber:iq:private.
180
181         Returns the sent XML."""
182         return self.SetData('jabber:iq:private', xml)
183
184     def GetPublicData(self, namespace, callback=None, xml=''):
185         """Requests the data stored in namespace.
186
187         Returns the iqQuery instance that is sent.
188
189         See also: iqQuery"""
190         return self.Query(namespace, '', type='get', callback=callback, xml=xml)
191     GetData = GetPublicData # Alias
192
193     def GetPrivateData(self, callback=None, xml=''):
194         """Requests the data stored in jabber:iq:private.
195
196         Returns the iqQuery instance that is sent.
197
198         See also: iqQuery"""
199         return self.GetData('jabber:iq:private', callback=callback, xml=xml)
200
201     def Query(self, namespace, toJID='', type='get', callback=None, xml=''):
202         """Sends a jabber:client iq query to toJID.
203
204         Returns the iqQuery instance that is sent.
205
206         See also: iqQuery"""
207         import iqQuery
208         query = iqQuery.iqQuery(self, callback)
209         query.SendQuery(namespace, toJID, type=type, xml=xml)
210         return query
211
212     def Message(self, toJID, body='', subject='', type='message', id=None, xml=''):
213         """Sends a jabber:client message to toJID.
214
215         Returns the sent XML."""
216         idMessage = id or self._GetNextID()
217         strXML = '<message id=%s to=%s type=%s>' % (quoteattr(idMessage), quoteattr(toJID), quoteattr(type))
218         strXML += xml
219         if subject:
220             strXML += '<subject>%s</subject>' % escape(subject)
221         if body:
222             strXML += '<body>%s</body>'  % escape(body)
223         strXML += '</message>'
224         return self.SendXML(strXML)
225
226     def Presence(self, toJID='', status='', show='', type='available', priority=None, xml=''):
227         """Sends a jabber:client presence to toJID or everyone if not toJID.
228
229         Returns the sent XML."""
230         idPresence = self._GetNextID()
231         strXML = '<presence id=%s ' % quoteattr(idPresence)
232         if type: strXML += 'type=%s ' % quoteattr(type)
233         if toJID: strXML += 'to=%s ' % quoteattr(toJID)
234         strXML += '>'
235
236         if priority is not None: strXML += '<priority>%s</priority>'  % escape(str(priority))
237         if show: strXML += '<show>%s</show>'  % escape(show)
238         if status: strXML += '<status>%s</status>' % escape(status)
239         if xml: strXML += xml
240         strXML += '</presence>'
241         return self.SendXML(strXML)
242
243     def Subscribe(self, toJID, type='subscribe'):
244         """Sends a jabber:client presence subscribe request to toJID.
245
246         Returns the sent XML."""
247         return self.Presence(toJID=toJID, type=type)
248
249     def Unsubscribe(self, toJID, type='unsubscribe'):
250         """Sends a jabber:client presence unsubscribe request to toJID.
251
252         Returns the sent XML."""
253         return self.Presence(toJID=toJID, type=type)
254
255 #~ Make a super ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
256 Client._Client__super = super(Client)
257
258 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
259 #~ Definitions
260 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
261
262 class ExtendedClient(Client):
263     def __init__(self, *args, **kw):
264         self.__super.__init__(*args, **kw)
265         self.stream.IncludeSend = kw.get('xmlsend', 0)
266         self.stream.IncludeRecv = kw.get('xmlrecv', 0)
267         self.stream.IncludeTick = kw.get('processtick', 0)
268
269     def _SocketRecv(self, limit=8192):
270         """Overrides socket access so that stream events can be created."""
271         result = self.__super._SocketRecv(limit)
272         if self.stream.IncludeRecv and result: self.stream.UpdateObservers(xmlrecv=result)
273         return result
274
275     def _SocketSend(self, data):
276         """Overrides socket access so that stream events can be created."""
277         if self.stream.IncludeSend and data: self.stream.UpdateObservers(xmlsend=data)
278         return self.__super._SocketSend(data)
279
280     def _NeedsRead(self, *args, **kw):
281         """Overrides SmartSelect mechanism to signal stream processtick events."""
282         result = self.__super._NeedsRead(*args, **kw)
283         if self.stream.IncludeTick and result: self.stream.UpdateObservers(processtick=1)
284         return result
285
286 #~ Make a super ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
287 ExtendedClient._ExtendedClient__super = super(ExtendedClient)
288
Note: See TracBrowser for help on using the browser.