]> code.delx.au - pymsnt/blob - src/jabw.py
Major changes. Moved everything away from twistd and .tac files. Its nicer this way...
[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 elif ptype == "probe":
257 LogEvent(INFO, self.jabberID, "Parsed presence probe")
258 self.contactList.getContact(toj.userhost()).sendPresence(fro)
259 else:
260 status = None
261 show = None
262 priority = None
263 avatarHash = ""
264 nickname = ""
265 for child in el.elements():
266 if(child.name == "status"):
267 status = child.__str__()
268 elif(child.name == "show"):
269 show = child.__str__()
270 elif(child.name == "priority"):
271 priority = child.__str__()
272 elif(child.defaultUri == disco.XVCARDUPDATE):
273 avatarHash = " "
274 for child2 in child.elements():
275 if(child2.name == "photo"):
276 avatarHash = child2.__str__()
277 elif(child2.name == "nickname"):
278 nickname = child2.__str__()
279
280 if not ptype:
281 # available presence
282 if(avatarHash):
283 self.avatarHashReceived(froj.userhost(), toj.userhost(), avatarHash)
284 if(nickname):
285 self.nicknameReceived(froj.userhost(), toj.userhost(), nickname)
286
287 LogEvent(INFO, self.jabberID, "Parsed presence packet")
288 self.presenceReceived(froj.userhost(), froj.resource, toj.userhost(), toj.resource, priority, ptype, show, status)
289
290
291
292 def messageReceived(self, source, resource, dest, destr, mtype, body, noerror):
293 """ Override this method to be notified when a message is received """
294 pass
295
296 def inviteReceived(self, source, resource, dest, destr, roomjid):
297 """ Override this method to be notified when an invitation is received """
298 pass
299
300 def presenceReceived(self, source, resource, to, tor, priority, ptype, show, status):
301 """ Override this method to be notified when presence is received """
302 pass
303
304 def subscriptionReceived(self, source, subtype):
305 """ Override this method to be notified when a subscription packet is received """
306 pass
307
308 def nicknameReceived(self, source, dest, nickname):
309 """ Override this method to be notified when a nickname has been received """
310 pass
311
312 def avatarHashReceieved(self, source, dest, avatarHash):
313 """ Override this method to be notified when an avatar hash is received """
314 pass
315
316