]> code.delx.au - pymsnt/blob - src/jabw.py
fa7f4f44cda807a45b795cdc2fece7fc027733ff
[pymsnt] / src / jabw.py
1 # Copyright 2004 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 import debug
12
13
14 def sendMessage(pytrans, to, fro, body, mtype=None, delay=None):
15 """ Sends a Jabber message """
16 debug.log("jabw: Sending a Jabber message \"%s\" \"%s\" \"%s\" \"%s\"" % (to, fro, utils.latin1(body), mtype))
17 el = Element((None, "message"))
18 el.attributes["to"] = to
19 el.attributes["from"] = fro
20 el.attributes["id"] = pytrans.makeMessageID()
21 if(mtype):
22 el.attributes["type"] = mtype
23
24 if(delay):
25 x = el.addElement("x")
26 x.attributes["xmlns"] = "jabber:x:delay"
27 x.attributes["from"] = fro
28 x.attributes["stamp"] = delay
29
30 b = el.addElement("body")
31 b.addContent(body)
32 x = el.addElement("x")
33 x.attributes["xmlns"] = "jabber:x:event"
34 composing = x.addElement("composing")
35 pytrans.send(el)
36
37 def sendPresence(pytrans, to, fro, show=None, status=None, priority=None, ptype=None):
38 # Strip the resource off any presence subscribes (as per XMPP RFC 3921 Section 5.1.6)
39 # Makes eJabberd behave :)
40 if(ptype == "subscribe"):
41 (user,host,res) = jid.parse(to)
42 to = "%s@%s" % (user, host)
43
44 el = Element((None, "presence"))
45 el.attributes["to"] = to
46 el.attributes["from"] = fro
47 if(ptype):
48 el.attributes["type"] = ptype
49 if(show):
50 s = el.addElement("show")
51 s.addContent(show)
52 if(status):
53 s = el.addElement("status")
54 s.addContent(status)
55 if(priority):
56 s = el.addElement("priority")
57 s.addContent(priority)
58 pytrans.send(el)
59
60
61 def sendErrorMessage(pytrans, to, fro, etype, condition, explanation, body=None):
62 el = Element((None, "message"))
63 el.attributes["to"] = to
64 el.attributes["from"] = fro
65 el.attributes["type"] = "error"
66 error = el.addElement("error")
67 error.attributes["type"] = etype
68 error.attributes["code"] = str(utils.errorCodeMap[condition])
69 desc = error.addElement(condition)
70 desc.attributes["xmlns"] = "urn:ietf:params:xml:ns:xmpp-stanzas"
71 text = error.addElement("text")
72 text.attributes["xmlns"] = "urn:ietf:params:xml:ns:xmpp-stanzas"
73 text.addContent(explanation)
74 if(body and len(body) > 0):
75 b = el.addElement("body")
76 b.addContent(body)
77 pytrans.send(el)
78
79
80
81
82 class JabberConnection:
83 """ A class to handle a Jabber "Connection", ie, the Jabber side of the gateway.
84 If you want to send a Jabber event, this is the place, and this is where incoming
85 Jabber events for a session come to. """
86
87 def __init__(self, pytrans, jabberID):
88 self.pytrans = pytrans
89 self.jabberID = jabberID
90
91 self.typingUser = False # Whether this user can accept typing notifications
92 self.messageIDs = dict() # The ID of the last message the user sent to a particular contact. Indexed by contact JID
93
94 debug.log("User: %s - JabberConnection constructed" % (self.jabberID))
95
96 def removeMe(self):
97 """ Cleanly deletes the object """
98 debug.log("User: %s - JabberConnection removed" % (self.jabberID))
99
100 def checkFrom(self, el):
101 """ Checks to see that this packet was intended for this object """
102 fro = el.getAttribute("from")
103 froj = jid.JID(fro)
104
105 return (froj.userhost() == self.jabberID) # Compare with the Jabber ID that we're looking at
106
107 def sendMessage(self, to, fro, body, mtype=None, delay=None):
108 """ Sends a Jabber message
109 For this message to have a <x xmlns="jabber:x:delay"/> you must pass a correctly formatted timestamp (See JEP0091)
110 """
111 debug.log("User: %s - JabberConnection sending message \"%s\" \"%s\" \"%s\" \"%s\"" % (self.jabberID, to, fro, utils.latin1(body), mtype))
112 sendMessage(self.pytrans, to, fro, body, mtype, delay)
113
114 def sendTypingNotification(self, to, fro, typing):
115 """ Sends the user the contact's current typing notification status """
116 if(self.typingUser):
117 debug.log("jabw: Sending a Jabber typing notification message \"%s\" \"%s\" \"%s\"" % (to, fro, typing))
118 el = Element((None, "message"))
119 el.attributes["to"] = to
120 el.attributes["from"] = fro
121 x = el.addElement("x")
122 x.attributes["xmlns"] = "jabber:x:event"
123 if(typing):
124 composing = x.addElement("composing")
125 id = x.addElement("id")
126 if(self.messageIDs.has_key(fro) and self.messageIDs[fro]):
127 id.addContent(self.messageIDs[fro])
128 self.pytrans.send(el)
129
130 def sendErrorMessage(self, to, fro, etype, condition, explanation, body=None):
131 debug.log("User: %s - JabberConnection sending error response." % (self.jabberID))
132 sendErrorMessage(self.pytrans, to, fro, etype, condition, explanation, body)
133
134 def sendPresence(self, to, fro, show=None, status=None, priority=None, ptype=None):
135 """ Sends a Jabber presence packet """
136 debug.log("User: %s - JabberConnection sending presence \"%s\" \"%s\" \"%s\" \"%s\" \"%s\" \"%s\"" % (self.jabberID, to, fro, show, utils.latin1(status), priority, ptype))
137 sendPresence(self.pytrans, to, fro, show, status, priority, ptype)
138
139 def sendRosterImport(self, jid, ptype, sub, name="", groups=[]):
140 """ Sends a special presence packet. This will work with all clients, but clients that support roster-import will give a better user experience
141 IMPORTANT - Only ever use this for contacts that have already been authorised on the legacy service """
142 el = Element((None, "presence"))
143 el.attributes["to"] = self.jabberID
144 el.attributes["from"] = jid
145 el.attributes["type"] = ptype
146 r = el.addElement("x")
147 r.attributes["xmlns"] = "http://jabber.org/protocol/roster-subsync"
148 item = r.addElement("item")
149 item.attributes["subscription"] = sub
150 if(name):
151 item.attributes["name"] = unicode(name)
152 for group in groups:
153 g = item.addElement("group")
154 g.addContent(group)
155
156 self.pytrans.send(el)
157
158 def onMessage(self, el):
159 """ Handles incoming message packets """
160 if(not self.checkFrom(el)): return
161 debug.log("User: %s - JabberConnection received message packet" % (self.jabberID))
162 fro = el.getAttribute("from")
163 froj = jid.JID(fro)
164 to = el.getAttribute("to")
165 toj = jid.JID(to)
166 mID = el.getAttribute("id")
167
168 mtype = el.getAttribute("type")
169 body = ""
170 invite = ""
171 messageEvent = False
172 noerror = False
173 composing = None
174 for child in el.elements():
175 if(child.name == "body"):
176 body = child.__str__()
177 if(child.name == "noerror" and child.uri == "sapo:noerror"):
178 noerror = True
179 if(child.name == "x"):
180 if(child.uri == "jabber:x:conference"):
181 invite = child.getAttribute("jid") # The room the contact is being invited to
182 if(child.uri == "jabber:x:event"):
183 messageEvent = True
184 composing = False
185 for deepchild in child.elements():
186 if(deepchild.name == "composing"):
187 composing = True
188
189 if(invite):
190 debug.log("User: %s - JabberConnection parsed message groupchat invite packet \"%s\" \"%s\" \"%s\" \"%s\"" % (self.jabberID, froj.userhost(), to, froj.resource, utils.latin1(invite)))
191 self.inviteReceived(froj.userhost(), froj.resource, toj.userhost(), toj.resource, invite)
192 return
193
194 # Check message event stuff
195 if(body and messageEvent):
196 self.typingUser = True
197 elif(body and not messageEvent):
198 self.typingUser = False
199 elif(not body and messageEvent):
200 debug.log("User: %s - JabberConnection parsed typing notification \"%s\" \"%s\"" % (self.jabberID, toj.userhost(), composing))
201 self.typingNotificationReceived(toj.userhost(), toj.resource, composing)
202
203
204 if(body):
205 # body = utils.utf8(body)
206 # Save the message ID for later
207 self.messageIDs[to] = mID
208 debug.log("User: %s - JabberConnection parsed message packet \"%s\" \"%s\" \"%s\" \"%s\" \"%s\"" % (self.jabberID, froj.userhost(), to, froj.resource, mtype, utils.latin1(body)))
209 self.messageReceived(froj.userhost(), froj.resource, toj.userhost(), toj.resource, mtype, body, noerror)
210
211 def onPresence(self, el):
212 """ Handles incoming presence packets """
213 if(not self.checkFrom(el)): return
214 debug.log("User: %s - JabberConnection received presence packet" % (self.jabberID))
215 fro = el.getAttribute("from")
216 froj = jid.JID(fro)
217 to = el.getAttribute("to")
218 toj = jid.JID(to)
219
220 # Grab the contents of the <presence/> packet
221 ptype = el.getAttribute("type")
222 if(ptype in ["subscribe", "subscribed", "unsubscribe", "unsubscribed"]):
223 debug.log("User: %s - JabberConnection parsed subscription presence packet \"%s\" \"%s\"" % (self.jabberID, toj.userhost(), ptype))
224 self.subscriptionReceived(toj.userhost(), ptype)
225 else:
226 status = None
227 show = None
228 priority = None
229 for child in el.elements():
230 if(child.name == "status"):
231 status = child.__str__()
232 elif(child.name == "show"):
233 show = child.__str__()
234 elif(child.name == "priority"):
235 priority = child.__str__()
236
237 debug.log("User: %s - JabberConnection parsed presence packet \"%s\" \"%s\" \"%s\" \"%s\" \"%s\" \"%s\"" % (self.jabberID, froj.userhost(), froj.resource, priority, ptype, show, utils.latin1(status)))
238 self.presenceReceived(froj.userhost(), froj.resource, toj.userhost(), toj.resource, priority, ptype, show, status)
239
240
241
242 def messageReceived(self, source, resource, dest, destr, mtype, body, noerror):
243 """ Override this method to be notified when a message is received """
244 pass
245
246 def inviteReceived(self, source, resource, dest, destr, roomjid):
247 """ Override this method to be notified when an invitation is received """
248 pass
249
250 def presenceReceived(self, source, resource, to, tor, priority, ptype, show, status):
251 """ Override this method to be notified when presence is received """
252 pass
253
254 def subscriptionReceived(self, source, subtype):
255 """ Override this method to be notified when a subscription packet is received """
256 pass
257
258
259