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