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