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