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