]> code.delx.au - pymsnt/blob - src/misciq.py
Trunk is broken. Don't use!
[pymsnt] / src / misciq.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.internet import reactor, task
6 from debug import LogEvent, INFO, WARN, ERROR
7 import jabw
8 import legacy
9 import disco
10 import config
11 import lang
12 import base64
13 import sys
14
15
16 class ConnectUsers:
17 def __init__(self, pytrans):
18 self.pytrans = pytrans
19 self.pytrans.adHocCommands.addCommand("connectusers", self.incomingIq, "command_ConnectUsers")
20
21 def sendProbes(self):
22 for jid in self.pytrans.xdb.files():
23 jabw.sendPresence(self.pytrans, jid, config.jid, ptype="probe")
24
25 def incomingIq(self, el):
26 to = el.getAttribute("from")
27 ID = el.getAttribute("id")
28 ulang = utils.getLang(el)
29
30 if config.admins.count(utils.jid(to).userhost()) == 0:
31 self.pytrans.discovery.sendIqError(to=to, fro=config.jid, ID=ID, xmlns=disco.COMMANDS, etype="cancel", condition="not-authorized")
32 return
33
34
35 self.sendProbes()
36
37 iq = Element((None, "iq"))
38 iq.attributes["to"] = to
39 iq.attributes["from"] = config.jid
40 if(ID):
41 iq.attributes["id"] = ID
42 iq.attributes["type"] = "result"
43
44 command = iq.addElement("command")
45 command.attributes["sessionid"] = self.pytrans.makeMessageID()
46 command.attributes["xmlns"] = disco.COMMANDS
47 command.attributes["status"] = "completed"
48
49 x = command.addElement("x")
50 x.attributes["xmlns"] = "jabber:x:data"
51 x.attributes["type"] = "result"
52
53 title = x.addElement("title")
54 title.addContent(lang.get(ulang).command_ConnectUsers)
55
56 field = x.addElement("field")
57 field.attributes["type"] = "fixed"
58 field.addElement("value").addContent(lang.get(ulang).command_Done)
59
60 self.pytrans.send(iq)
61
62
63 class Statistics:
64 def __init__(self, pytrans):
65 self.pytrans = pytrans
66 self.pytrans.adHocCommands.addCommand("stats", self.incomingIq, "command_Statistics")
67
68 # self.stats is indexed by a unique ID, with value being the value for that statistic
69 self.stats = {}
70 self.stats["Uptime"] = 0
71 self.stats["OnlineUsers"] = 0
72 self.stats["TotalUsers"] = 0
73
74 legacy.startStats(self)
75
76 def incomingIq(self, el):
77 to = el.getAttribute("from")
78 ID = el.getAttribute("id")
79 ulang = utils.getLang(el)
80
81 iq = Element((None, "iq"))
82 iq.attributes["to"] = to
83 iq.attributes["from"] = config.jid
84 if(ID):
85 iq.attributes["id"] = ID
86 iq.attributes["type"] = "result"
87
88 command = iq.addElement("command")
89 command.attributes["sessionid"] = self.pytrans.makeMessageID()
90 command.attributes["xmlns"] = disco.COMMANDS
91 command.attributes["status"] = "completed"
92
93 x = command.addElement("x")
94 x.attributes["xmlns"] = "jabber:x:data"
95 x.attributes["type"] = "result"
96
97 title = x.addElement("title")
98 title.addContent(lang.get(ulang).command_Statistics)
99
100 for key in self.stats:
101 label = getattr(lang.get(ulang), "command_%s" % key)
102 description = getattr(lang.get(ulang), "command_%s_Desc" % key)
103 field = x.addElement("field")
104 field.attributes["var"] = key
105 field.attributes["label"] = label
106 field.attributes["type"] = "text-single"
107 field.addElement("value").addContent(str(self.stats[key]))
108 field.addElement("desc").addContent(description)
109
110 self.pytrans.send(iq)
111
112
113
114 class AdHocCommands:
115 def __init__(self, pytrans):
116 self.pytrans = pytrans
117 self.pytrans.discovery.addFeature(disco.COMMANDS, self.incomingIq, config.jid)
118 self.pytrans.discovery.addNode(disco.COMMANDS, self.sendCommandList, "command_CommandList", config.jid, True)
119
120 self.commands = {} # Dict of handlers indexed by node
121 self.commandNames = {} # Dict of names indexed by node
122
123 def addCommand(self, command, handler, name):
124 self.commands[command] = handler
125 self.commandNames[command] = name
126 self.pytrans.discovery.addNode(command, self.incomingIq, name, config.jid, False)
127
128 def incomingIq(self, el):
129 itype = el.getAttribute("type")
130 fro = el.getAttribute("from")
131 froj = utils.jid(fro)
132 to = el.getAttribute("to")
133 ID = el.getAttribute("id")
134
135 LogEvent(INFO, "", "Looking for handler")
136
137 node = None
138 for child in el.elements():
139 xmlns = child.defaultUri
140 node = child.getAttribute("node")
141
142 handled = False
143 if(child.name == "query" and xmlns == disco.DISCO_INFO):
144 if(node and self.commands.has_key(node) and (itype == "get")):
145 self.sendCommandInfoResponse(to=fro, ID=ID)
146 handled = True
147 elif(child.name == "query" and xmlns == disco.DISCO_ITEMS):
148 if(node and self.commands.has_key(node) and (itype == "get")):
149 self.sendCommandItemsResponse(to=fro, ID=ID)
150 handled = True
151 elif(child.name == "command" and xmlns == disco.COMMANDS):
152 if((node and self.commands.has_key(node)) and (itype == "set" or itype == "error")):
153 self.commands[node](el)
154 handled = True
155 if(not handled):
156 LogEvent(WARN, "", "Unknown Ad-Hoc command received.")
157 self.pytrans.discovery.sendIqError(to=fro, fro=config.jid, ID=ID, xmlns=xmlns, etype="cancel", condition="feature-not-implemented")
158
159
160 def sendCommandList(self, el):
161 to = el.getAttribute("from")
162 ID = el.getAttribute("id")
163 ulang = utils.getLang(el)
164
165 iq = Element((None, "iq"))
166 iq.attributes["to"] = to
167 iq.attributes["from"] = config.jid
168 if ID:
169 iq.attributes["id"] = ID
170 iq.attributes["type"] = "result"
171
172 query = iq.addElement("query")
173 query.attributes["xmlns"] = disco.DISCO_ITEMS
174 query.attributes["node"] = disco.COMMANDS
175
176 for command in self.commands:
177 item = query.addElement("item")
178 item.attributes["jid"] = config.jid
179 item.attributes["node"] = command
180 item.attributes["name"] = getattr(lang.get(ulang), self.commandNames[command])
181
182 self.pytrans.send(iq)
183
184 def sendCommandInfoResponse(self, to, ID):
185 LogEvent(INFO, "", "Replying to disco#info")
186 iq = Element((None, "iq"))
187 iq.attributes["type"] = "result"
188 iq.attributes["from"] = config.jid
189 iq.attributes["to"] = to
190 if(ID): iq.attributes["id"] = ID
191 query = iq.addElement("query")
192 query.attributes["xmlns"] = disco.DISCO_INFO
193
194 feature = query.addElement("feature")
195 feature.attributes["var"] = disco.COMMANDS
196 self.pytrans.send(iq)
197
198 def sendCommandItemsResponse(self, to, ID):
199 LogEvent(INFO, "", "Replying to disco#items")
200 iq = Element((None, "iq"))
201 iq.attributes["type"] = "result"
202 iq.attributes["from"] = config.jid
203 iq.attributes["to"] = to
204 if(ID): iq.attributes["id"] = ID
205 query = iq.addElement("query")
206 query.attributes["xmlns"] = disco.DISCO_ITEMS
207 self.pytrans.send(iq)
208
209
210 class VCardFactory:
211 def __init__(self, pytrans):
212 self.pytrans = pytrans
213 self.pytrans.discovery.addFeature("vcard-temp", self.incomingIq, "USER")
214 self.pytrans.discovery.addFeature("vcard-temp", self.incomingIq, config.jid)
215
216 def incomingIq(self, el):
217 itype = el.getAttribute("type")
218 fro = el.getAttribute("from")
219 froj = utils.jid(fro)
220 to = el.getAttribute("to")
221 ID = el.getAttribute("id")
222 if(itype != "get" and itype != "error"):
223 self.pytrans.discovery.sendIqError(to=fro, fro=config.jid, ID=ID, xmlns="vcard-temp", etype="cancel", condition="feature-not-implemented")
224 return
225
226 LogEvent(INFO, "", "Sending vCard")
227
228 toGateway = not (to.find('@') > 0)
229
230 if(not toGateway):
231 if(not self.pytrans.sessions.has_key(froj.userhost())):
232 self.pytrans.discovery.sendIqError(to=fro, fro=config.jid, ID=ID, xmlns="vcard-temp", etype="auth", condition="not-authorized")
233 return
234 s = self.pytrans.sessions[froj.userhost()]
235 if(not s.ready):
236 self.pytrans.discovery.sendIqError(to=fro, fro=config.jid, ID=ID, xmlns="vcard-temp", etype="auth", condition="not-authorized")
237 return
238
239 c = s.contactList.findContact(to)
240 if(not c):
241 self.pytrans.discovery.sendIqError(to=fro, fro=config.jid, ID=ID, xmlns="vcard-temp", etype="cancel", condition="recipient-unavailable")
242 return
243
244
245 iq = Element((None, "iq"))
246 iq.attributes["to"] = fro
247 iq.attributes["from"] = to
248 if ID:
249 iq.attributes["id"] = ID
250 iq.attributes["type"] = "result"
251 vCard = iq.addElement("vCard")
252 vCard.attributes["xmlns"] = "vcard-temp"
253 if(toGateway):
254 FN = vCard.addElement("FN")
255 FN.addContent(legacy.name)
256 DESC = vCard.addElement("DESC")
257 DESC.addContent(legacy.name)
258 URL = vCard.addElement("URL")
259 URL.addContent(legacy.url)
260 else:
261 if(c.nickname):
262 NICKNAME = vCard.addElement("NICKNAME")
263 NICKNAME.addContent(c.nickname)
264 if(c.avatar):
265 PHOTO = c.avatar.makePhotoElement()
266 vCard.addChild(PHOTO)
267
268 self.pytrans.send(iq)
269
270 class IqAvatarFactory:
271 def __init__(self, pytrans):
272 self.pytrans = pytrans
273 self.pytrans.discovery.addFeature(disco.IQAVATAR, self.incomingIq, "USER")
274 self.pytrans.discovery.addFeature(disco.STORAGEAVATAR, self.incomingIq, "USER")
275
276 def incomingIq(self, el):
277 itype = el.getAttribute("type")
278 fro = el.getAttribute("from")
279 froj = utils.jid(fro)
280 to = el.getAttribute("to")
281 ID = el.getAttribute("id")
282
283 if(itype != "get" and itype != "error"):
284 self.pytrans.discovery.sendIqError(to=fro, fro=config.jid, ID=ID, xmlns=disco.IQAVATAR, etype="cancel", condition="feature-not-implemented")
285 return
286
287 LogEvent(INFO, "", "Retrieving avatar")
288
289 if(not self.pytrans.sessions.has_key(froj.userhost())):
290 self.pytrans.discovery.sendIqError(to=fro, fro=config.jid, ID=ID, xmlns=disco.IQAVATAR, etype="auth", condition="not-authorized")
291 return
292 s = self.pytrans.sessions[froj.userhost()]
293 if(not s.ready):
294 self.pytrans.discovery.sendIqError(to=fro, fro=config.jid, ID=ID, xmlns=disco.IQAVATAR, etype="auth", condition="not-authorized")
295 return
296
297 c = s.contactList.findContact(to)
298 if(not c):
299 self.pytrans.discovery.sendIqError(to=fro, fro=config.jid, ID=ID, xmlns=disco.IQAVATAR, etype="cancel", condition="recipient-unavailable")
300 return
301
302 iq = Element((None, "iq"))
303 iq.attributes["to"] = fro
304 iq.attributes["from"] = to
305 if ID:
306 iq.attributes["id"] = ID
307 iq.attributes["type"] = "result"
308 query = iq.addElement("query")
309 query.attributes["xmlns"] = disco.IQAVATAR
310 if(c.avatar):
311 DATA = c.avatar.makeDataElement()
312 query.addChild(DATA)
313
314 self.pytrans.send(iq)
315
316
317
318 class PingService:
319 def __init__(self, pytrans):
320 self.pytrans = pytrans
321 # self.pingCounter = 0
322 # self.pingTask = task.LoopingCall(self.pingCheck)
323 self.pingTask = task.LoopingCall(self.whitespace)
324 # reactor.callLater(10.0, self.start)
325
326 # def start(self):
327 # self.pingTask.start(120.0)
328
329 def whitespace(self):
330 self.pytrans.send(" ")
331
332 # def pingCheck(self):
333 # if(self.pingCounter >= 2 and self.pytrans.xmlstream): # Two minutes of no response from the main server
334 # LogEvent(WARN, "", "Disconnecting because the main server has ignored our pings for too long.")
335 # self.pytrans.xmlstream.transport.loseConnection()
336 # elif(config.mainServerJID):
337 # d = self.pytrans.discovery.sendIq(self.makePingPacket())
338 # d.addCallback(self.pongReceived)
339 # d.addErrback(self.pongFailed)
340 # self.pingCounter += 1
341
342 # def pongReceived(self, el):
343 # self.pingCounter = 0
344
345 # def pongFailed(self, el):
346 # pass
347
348 # def makePingPacket(self):
349 # iq = Element((None, "iq"))
350 # iq.attributes["from"] = config.jid
351 # iq.attributes["to"] = config.mainServerJID
352 # iq.attributes["type"] = "get"
353 # query = iq.addElement("query")
354 # query.attributes["xmlns"] = disco.IQVERSION
355 # return iq
356
357 class GatewayTranslator:
358 def __init__(self, pytrans):
359 self.pytrans = pytrans
360 self.pytrans.discovery.addFeature(disco.IQGATEWAY, self.incomingIq, config.jid)
361
362 def incomingIq(self, el):
363 fro = el.getAttribute("from")
364 ID = el.getAttribute("id")
365 itype = el.getAttribute("type")
366 if(itype == "get"):
367 self.sendPrompt(fro, ID, utils.getLang(el))
368 elif(itype == "set"):
369 self.sendTranslation(fro, ID, el)
370
371
372 def sendPrompt(self, to, ID, ulang):
373 LogEvent(INFO)
374
375 iq = Element((None, "iq"))
376
377 iq.attributes["type"] = "result"
378 iq.attributes["from"] = config.jid
379 iq.attributes["to"] = to
380 if ID:
381 iq.attributes["id"] = ID
382 query = iq.addElement("query")
383 query.attributes["xmlns"] = disco.IQGATEWAY
384 desc = query.addElement("desc")
385 desc.addContent(lang.get(ulang).gatewayTranslator)
386 prompt = query.addElement("prompt")
387
388 self.pytrans.send(iq)
389
390 def sendTranslation(self, to, ID, el):
391 LogEvent(INFO)
392
393 # Find the user's legacy account
394 legacyaccount = None
395 for query in el.elements():
396 if(query.name == "query"):
397 for child in query.elements():
398 if(child.name == "prompt"):
399 legacyaccount = str(child)
400 break
401 break
402
403
404 if(legacyaccount and len(legacyaccount) > 0):
405 LogEvent(INFO, "", "Sending translated account.")
406 iq = Element((None, "iq"))
407 iq.attributes["type"] = "result"
408 iq.attributes["from"] = config.jid
409 iq.attributes["to"] = to
410 if ID:
411 iq.attributes["id"] = ID
412 query = iq.addElement("query")
413 query.attributes["xmlns"] = disco.IQGATEWAY
414 prompt = query.addElement("prompt")
415 prompt.addContent(legacy.translateAccount(legacyaccount))
416
417 self.pytrans.send(iq)
418
419 else:
420 self.pytrans.discovery.sendIqError(to, ID, disco.IQGATEWAY)
421 self.pytrans.discovery.sendIqError(to=to, fro=config.jid, ID=ID, xmlns=disco.IQGATEWAY, etype="retry", condition="bad-request")
422
423
424
425 class VersionTeller:
426 def __init__(self, pytrans):
427 self.pytrans = pytrans
428 self.pytrans.discovery.addFeature(disco.IQVERSION, self.incomingIq, config.jid)
429 self.pytrans.discovery.addFeature(disco.IQVERSION, self.incomingIq, "USER")
430
431 def incomingIq(self, el):
432 eltype = el.getAttribute("type")
433 if(eltype != "get"): return # Only answer "get" stanzas
434
435 self.sendVersion(el)
436
437 def sendVersion(self, el):
438 LogEvent(INFO)
439 iq = Element((None, "iq"))
440 iq.attributes["type"] = "result"
441 iq.attributes["from"] = el.getAttribute("to")
442 iq.attributes["to"] = el.getAttribute("from")
443 if(el.getAttribute("id")):
444 iq.attributes["id"] = el.getAttribute("id")
445 query = iq.addElement("query")
446 query.attributes["xmlns"] = disco.IQVERSION
447 name = query.addElement("name")
448 name.addContent(legacy.name)
449 version = query.addElement("version")
450 version.addContent(legacy.version)
451 os = query.addElement("os")
452 os.addContent("Python" + ".".join([str(x) for x in sys.version_info[0:3]]) + " - " + sys.platform)
453
454 self.pytrans.send(iq)
455
456
457