]> code.delx.au - pymsnt/blob - src/jabw.py
Fixed bug in sending files and avatars.
[pymsnt] / src / jabw.py
1 # Copyright 2004-2005 James Bunton <james@delx.cjb.net>
2 # Licensed for distribution under the GPL version 2, check COPYING for details
3
4 import utils
5 from tlib.xmlw import Element, jid
6 from debug import LogEvent, INFO, WARN, ERROR
7 import disco
8
9
10 def sendMessage(pytrans, to, fro, body, mtype=None, delay=None):
11 """ Sends a Jabber message """
12 LogEvent(INFO)
13 el = Element((None, "message"))
14 el.attributes["to"] = to
15 el.attributes["from"] = fro
16 el.attributes["id"] = pytrans.makeMessageID()
17 if(mtype):
18 el.attributes["type"] = mtype
19
20 if(delay):
21 x = el.addElement("x")
22 x.attributes["xmlns"] = disco.XDELAY
23 x.attributes["from"] = fro
24 x.attributes["stamp"] = delay
25
26 b = el.addElement("body")
27 b.addContent(body)
28 x = el.addElement("x")
29 x.attributes["xmlns"] = disco.XEVENT
30 composing = x.addElement("composing")
31 pytrans.send(el)
32
33 def sendPresence(pytrans, to, fro, show=None, status=None, priority=None, ptype=None, avatarHash=None, nickname=None, payload=[]):
34 # Strip the resource off any presence subscribes (as per XMPP RFC 3921 Section 5.1.6)
35 # Makes eJabberd behave :)
36 if ptype == "subscribe":
37 to = jid.intern(to).userhost()
38
39 el = Element((None, "presence"))
40 el.attributes["to"] = to
41 el.attributes["from"] = fro
42 if(ptype):
43 el.attributes["type"] = ptype
44 if(show):
45 s = el.addElement("show")
46 s.addContent(show)
47 if(status):
48 s = el.addElement("status")
49 s.addContent(status)
50 if(priority):
51 s = el.addElement("priority")
52 s.addContent(priority)
53
54 if(not ptype):
55 x = el.addElement("x")
56 x.attributes["xmlns"] = disco.XVCARDUPDATE
57 if(avatarHash):
58 xx = el.addElement("x")
59 xx.attributes["xmlns"] = disco.XAVATAR
60 h = xx.addElement("hash")
61 h.addContent(avatarHash)
62 h = x.addElement("photo")
63 h.addContent(avatarHash)
64 if(nickname):
65 n = x.addElement("nickname")
66 n.addContent(nickname)
67
68 if(payload):
69 for p in payload:
70 el.addChild(p)
71
72 pytrans.send(el)
73
74
75 def sendErrorMessage(pytrans, to, fro, etype, condition, explanation, body=None):
76 el = Element((None, "message"))
77 el.attributes["to"] = to
78 el.attributes["from"] = fro
79 el.attributes["type"] = "error"
80 error = el.addElement("error")
81 error.attributes["type"] = etype
82 error.attributes["code"] = str(utils.errorCodeMap[condition])
83 desc = error.addElement(condition)
84 desc.attributes["xmlns"] = "urn:ietf:params:xml:ns:xmpp-stanzas"
85 text = error.addElement("text")
86 text.attributes["xmlns"] = "urn:ietf:params:xml:ns:xmpp-stanzas"
87 text.addContent(explanation)
88 if(body and len(body) > 0):
89 b = el.addElement("body")
90 b.addContent(body)
91 pytrans.send(el)
92
93
94
95
96 class JabberConnection:
97 """ A class to handle a Jabber "Connection", ie, the Jabber side of the gateway.
98 If you want to send a Jabber event, this is the place, and this is where incoming
99 Jabber events for a session come to. """
100
101 def __init__(self, pytrans, jabberID):
102 self.pytrans = pytrans
103 self.jabberID = jabberID
104
105 self.typingUser = False # Whether this user can accept typing notifications
106 self.messageIDs = dict() # The ID of the last message the user sent to a particular contact. Indexed by contact JID
107
108 LogEvent(INFO, self.jabberID)
109
110 def removeMe(self):
111 """ Cleanly deletes the object """
112 LogEvent(INFO, self.jabberID)
113
114 def sendMessage(self, to, fro, body, mtype=None, delay=None):
115 """ Sends a Jabber message
116 For this message to have a <x xmlns="jabber:x:delay"/> you must pass a correctly formatted timestamp (See JEP0091)
117 """
118 LogEvent(INFO, self.jabberID)
119 sendMessage(self.pytrans, to, fro, body, mtype, delay)
120
121 def sendTypingNotification(self, to, fro, typing):
122 """ Sends the user the contact's current typing notification status """
123 if(self.typingUser):
124 LogEvent(INFO, self.jabberID)
125 el = Element((None, "message"))
126 el.attributes["to"] = to
127 el.attributes["from"] = fro
128 x = el.addElement("x")
129 x.attributes["xmlns"] = disco.XEVENT
130 if(typing):
131 composing = x.addElement("composing")
132 id = x.addElement("id")
133 if(self.messageIDs.has_key(fro) and self.messageIDs[fro]):
134 id.addContent(self.messageIDs[fro])
135 self.pytrans.send(el)
136
137 def sendVCardRequest(self, to, fro):
138 """ Requests the the vCard of 'to'
139 Returns a Deferred which fires when the vCard has been received.
140 First argument an Element object of the vCard
141 """
142 el = Element((None, "iq"))
143 el.attributes["to"] = to
144 el.attributes["from"] = fro
145 el.attributes["type"] = "get"
146 el.attributes["id"] = self.pytrans.makeMessageID()
147 vCard = el.addElement("vCard")
148 vCard.attributes["xmlns"] = "vcard-temp"
149 return self.pytrans.discovery.sendIq(el)
150
151 def sendErrorMessage(self, to, fro, etype, condition, explanation, body=None):
152 LogEvent(INFO, self.jabberID)
153 sendErrorMessage(self.pytrans, to, fro, etype, condition, explanation, body)
154
155 def sendPresence(self, to, fro, show=None, status=None, priority=None, ptype=None, avatarHash=None, nickname=None, payload=[]):
156 """ Sends a Jabber presence packet """
157 LogEvent(INFO, self.jabberID)
158 sendPresence(self.pytrans, to, fro, show, status, priority, ptype, avatarHash, nickname, payload)
159
160 def sendRosterImport(self, jid, ptype, sub, name="", groups=[]):
161 """ Sends a special presence packet. This will work with all clients, but clients that support roster-import will give a better user experience
162 IMPORTANT - Only ever use this for contacts that have already been authorised on the legacy service """
163 el = Element((None, "presence"))
164 el.attributes["to"] = self.jabberID
165 el.attributes["from"] = jid
166 el.attributes["type"] = ptype
167 r = el.addElement("x")
168 r.attributes["xmlns"] = disco.SUBSYNC
169 item = r.addElement("item")
170 item.attributes["subscription"] = sub
171 if(name):
172 item.attributes["name"] = unicode(name)
173 for group in groups:
174 g = item.addElement("group")
175 g.addContent(group)
176
177 self.pytrans.send(el)
178
179 def onMessage(self, el):
180 """ Handles incoming message packets """
181 #LogEvent(INFO, self.jabberID)
182 fro = el.getAttribute("from")
183 to = el.getAttribute("to")
184 try:
185 froj = jid.intern(fro)
186 toj = jid.intern(to)
187 except Exception, e:
188 LogEvent(WARN, self.jabberID)
189 return
190
191 mID = el.getAttribute("id")
192 mtype = el.getAttribute("type")
193 body = ""
194 inviteTo = ""
195 inviteRoom = ""
196 messageEvent = False
197 noerror = False
198 composing = None
199 for child in el.elements():
200 if(child.name == "body"):
201 body = child.__str__()
202 elif(child.name == "noerror" and child.uri == "sapo:noerror"):
203 noerror = True
204 elif(child.name == "x"):
205 if(child.uri == disco.XCONFERENCE):
206 inviteTo = to
207 inviteRoom = child.getAttribute("jid") # The room the contact is being invited to
208 elif(child.uri == disco.MUC_USER):
209 for child2 in child.elements():
210 if(child2.name == "invite"):
211 inviteTo = child2.getAttribute("to")
212 break
213 inviteRoom = to
214 elif(child.uri == disco.XEVENT):
215 messageEvent = True
216 composing = False
217 for child2 in child.elements():
218 if(child2.name == "composing"):
219 composing = True
220 break
221
222 if(inviteTo and inviteRoom):
223 LogEvent(INFO, self.jabberID, "Message groupchat invite packet")
224 self.inviteReceived(source=froj.userhost(), resource=froj.resource, dest=inviteTo, destr="", roomjid=inviteRoom)
225 return
226
227 # Check message event stuff
228 if(body and messageEvent):
229 self.typingUser = True
230 elif(body and not messageEvent):
231 self.typingUser = False
232 elif(not body and messageEvent):
233 LogEvent(INFO, self.jabberID, "Message typing notification packet")
234 self.typingNotificationReceived(toj.userhost(), toj.resource, composing)
235
236
237 if(body):
238 # Save the message ID for later
239 self.messageIDs[to] = mID
240 LogEvent(INFO, self.jabberID, "Message packet")
241 self.messageReceived(froj.userhost(), froj.resource, toj.userhost(), toj.resource, mtype, body, noerror)
242
243 def onPresence(self, el):
244 """ Handles incoming presence packets """
245 #LogEvent(INFO, self.jabberID)
246 fro = el.getAttribute("from")
247 froj = jid.intern(fro)
248 to = el.getAttribute("to")
249 toj = jid.intern(to)
250
251 # Grab the contents of the <presence/> packet
252 ptype = el.getAttribute("type")
253 if ptype and (ptype.startswith("subscribe") or ptype.startswith("unsubscribe")):
254 LogEvent(INFO, self.jabberID, "Parsed subscription presence packet")
255 self.subscriptionReceived(toj.userhost(), ptype)
256 else:
257 status = None
258 show = None
259 priority = None
260 avatarHash = ""
261 nickname = ""
262 for child in el.elements():
263 if(child.name == "status"):
264 status = child.__str__()
265 elif(child.name == "show"):
266 show = child.__str__()
267 elif(child.name == "priority"):
268 priority = child.__str__()
269 elif(child.defaultUri == disco.XVCARDUPDATE):
270 avatarHash = " "
271 for child2 in child.elements():
272 if(child2.name == "photo"):
273 avatarHash = child2.__str__()
274 elif(child2.name == "nickname"):
275 nickname = child2.__str__()
276
277 if not ptype:
278 # available presence
279 if(avatarHash):
280 self.avatarHashReceived(froj.userhost(), toj.userhost(), avatarHash)
281 if(nickname):
282 self.nicknameReceived(froj.userhost(), toj.userhost(), nickname)
283
284 LogEvent(INFO, self.jabberID, "Parsed presence packet")
285 self.presenceReceived(froj.userhost(), froj.resource, toj.userhost(), toj.resource, priority, ptype, show, status)
286
287
288
289 def messageReceived(self, source, resource, dest, destr, mtype, body, noerror):
290 """ Override this method to be notified when a message is received """
291 pass
292
293 def inviteReceived(self, source, resource, dest, destr, roomjid):
294 """ Override this method to be notified when an invitation is received """
295 pass
296
297 def presenceReceived(self, source, resource, to, tor, priority, ptype, show, status):
298 """ Override this method to be notified when presence is received """
299 pass
300
301 def subscriptionReceived(self, source, subtype):
302 """ Override this method to be notified when a subscription packet is received """
303 pass
304
305 def nicknameReceived(self, source, dest, nickname):
306 """ Override this method to be notified when a nickname has been received """
307 pass
308
309 def avatarHashReceieved(self, source, dest, avatarHash):
310 """ Override this method to be notified when an avatar hash is received """
311 pass
312
313