]>
code.delx.au - pymsnt/blob - src/misciq.py
66f87b95903d9849424b1f9f2fa5db3d99eb2dce
1 # Copyright 2004-2005 James Bunton <james@delx.cjb.net>
2 # Licensed for distribution under the GPL version 2, check COPYING for details
5 from twisted
.internet
import reactor
, task
, protocol
, error
6 from tlib
.xmlw
import Element
, jid
7 from debug
import LogEvent
, INFO
, WARN
, ERROR
19 def __init__(self
, pytrans
):
20 self
.pytrans
= pytrans
21 self
.pytrans
.adHocCommands
.addCommand("connectusers", self
.incomingIq
, "command_ConnectUsers")
24 for jid
in self
.pytrans
.xdb
.files():
25 jabw
.sendPresence(self
.pytrans
, jid
, config
.jid
, ptype
="probe")
27 def incomingIq(self
, el
):
28 to
= el
.getAttribute("from")
29 ID
= el
.getAttribute("id")
30 ulang
= utils
.getLang(el
)
32 if config
.admins
.count(jid
.intern(to
).userhost()) == 0:
33 self
.pytrans
.discovery
.sendIqError(to
=to
, fro
=config
.jid
, ID
=ID
, xmlns
=disco
.COMMANDS
, etype
="cancel", condition
="not-authorized")
39 iq
= Element((None, "iq"))
40 iq
.attributes
["to"] = to
41 iq
.attributes
["from"] = config
.jid
43 iq
.attributes
["id"] = ID
44 iq
.attributes
["type"] = "result"
46 command
= iq
.addElement("command")
47 command
.attributes
["sessionid"] = self
.pytrans
.makeMessageID()
48 command
.attributes
["xmlns"] = disco
.COMMANDS
49 command
.attributes
["status"] = "completed"
51 x
= command
.addElement("x")
52 x
.attributes
["xmlns"] = disco
.XDATA
53 x
.attributes
["type"] = "result"
55 title
= x
.addElement("title")
56 title
.addContent(lang
.get(ulang
).command_ConnectUsers
)
58 field
= x
.addElement("field")
59 field
.attributes
["type"] = "fixed"
60 field
.addElement("value").addContent(lang
.get(ulang
).command_Done
)
66 def __init__(self
, pytrans
):
67 self
.pytrans
= pytrans
68 self
.pytrans
.adHocCommands
.addCommand("stats", self
.incomingIq
, "command_Statistics")
70 # self.stats is indexed by a unique ID, with value being the value for that statistic
72 self
.stats
["Uptime"] = 0
73 self
.stats
["OnlineUsers"] = 0
74 self
.stats
["TotalUsers"] = 0
76 legacy
.startStats(self
)
78 def incomingIq(self
, el
):
79 to
= el
.getAttribute("from")
80 ID
= el
.getAttribute("id")
81 ulang
= utils
.getLang(el
)
83 iq
= Element((None, "iq"))
84 iq
.attributes
["to"] = to
85 iq
.attributes
["from"] = config
.jid
87 iq
.attributes
["id"] = ID
88 iq
.attributes
["type"] = "result"
90 command
= iq
.addElement("command")
91 command
.attributes
["sessionid"] = self
.pytrans
.makeMessageID()
92 command
.attributes
["xmlns"] = disco
.COMMANDS
93 command
.attributes
["status"] = "completed"
95 x
= command
.addElement("x")
96 x
.attributes
["xmlns"] = disco
.XDATA
97 x
.attributes
["type"] = "result"
99 title
= x
.addElement("title")
100 title
.addContent(lang
.get(ulang
).command_Statistics
)
102 for key
in self
.stats
:
103 label
= getattr(lang
.get(ulang
), "command_%s" % key
)
104 description
= getattr(lang
.get(ulang
), "command_%s_Desc" % key
)
105 field
= x
.addElement("field")
106 field
.attributes
["var"] = key
107 field
.attributes
["label"] = label
108 field
.attributes
["type"] = "text-single"
109 field
.addElement("value").addContent(str(self
.stats
[key
]))
110 field
.addElement("desc").addContent(description
)
112 self
.pytrans
.send(iq
)
117 def __init__(self
, pytrans
):
118 self
.pytrans
= pytrans
119 self
.pytrans
.discovery
.addFeature(disco
.COMMANDS
, self
.incomingIq
, config
.jid
)
120 self
.pytrans
.discovery
.addNode(disco
.COMMANDS
, self
.sendCommandList
, "command_CommandList", config
.jid
, True)
122 self
.commands
= {} # Dict of handlers indexed by node
123 self
.commandNames
= {} # Dict of names indexed by node
125 def addCommand(self
, command
, handler
, name
):
126 self
.commands
[command
] = handler
127 self
.commandNames
[command
] = name
128 self
.pytrans
.discovery
.addNode(command
, self
.incomingIq
, name
, config
.jid
, False)
130 def incomingIq(self
, el
):
131 itype
= el
.getAttribute("type")
132 fro
= el
.getAttribute("from")
133 froj
= jid
.intern(fro
)
134 to
= el
.getAttribute("to")
135 ID
= el
.getAttribute("id")
137 LogEvent(INFO
, "", "Looking for handler")
140 for child
in el
.elements():
142 node
= child
.getAttribute("node")
145 if(child
.name
== "query" and xmlns
== disco
.DISCO_INFO
):
146 if(node
and self
.commands
.has_key(node
) and (itype
== "get")):
147 self
.sendCommandInfoResponse(to
=fro
, ID
=ID
)
149 elif(child
.name
== "query" and xmlns
== disco
.DISCO_ITEMS
):
150 if(node
and self
.commands
.has_key(node
) and (itype
== "get")):
151 self
.sendCommandItemsResponse(to
=fro
, ID
=ID
)
153 elif(child
.name
== "command" and xmlns
== disco
.COMMANDS
):
154 if((node
and self
.commands
.has_key(node
)) and (itype
== "set" or itype
== "error")):
155 self
.commands
[node
](el
)
158 LogEvent(WARN
, "", "Unknown Ad-Hoc command received.")
159 self
.pytrans
.discovery
.sendIqError(to
=fro
, fro
=config
.jid
, ID
=ID
, xmlns
=xmlns
, etype
="cancel", condition
="feature-not-implemented")
162 def sendCommandList(self
, el
):
163 to
= el
.getAttribute("from")
164 ID
= el
.getAttribute("id")
165 ulang
= utils
.getLang(el
)
167 iq
= Element((None, "iq"))
168 iq
.attributes
["to"] = to
169 iq
.attributes
["from"] = config
.jid
171 iq
.attributes
["id"] = ID
172 iq
.attributes
["type"] = "result"
174 query
= iq
.addElement("query")
175 query
.attributes
["xmlns"] = disco
.DISCO_ITEMS
176 query
.attributes
["node"] = disco
.COMMANDS
178 for command
in self
.commands
:
179 item
= query
.addElement("item")
180 item
.attributes
["jid"] = config
.jid
181 item
.attributes
["node"] = command
182 item
.attributes
["name"] = getattr(lang
.get(ulang
), self
.commandNames
[command
])
184 self
.pytrans
.send(iq
)
186 def sendCommandInfoResponse(self
, to
, ID
):
187 LogEvent(INFO
, "", "Replying to disco#info")
188 iq
= Element((None, "iq"))
189 iq
.attributes
["type"] = "result"
190 iq
.attributes
["from"] = config
.jid
191 iq
.attributes
["to"] = to
192 if(ID
): iq
.attributes
["id"] = ID
193 query
= iq
.addElement("query")
194 query
.attributes
["xmlns"] = disco
.DISCO_INFO
196 feature
= query
.addElement("feature")
197 feature
.attributes
["var"] = disco
.COMMANDS
198 self
.pytrans
.send(iq
)
200 def sendCommandItemsResponse(self
, to
, ID
):
201 LogEvent(INFO
, "", "Replying to disco#items")
202 iq
= Element((None, "iq"))
203 iq
.attributes
["type"] = "result"
204 iq
.attributes
["from"] = config
.jid
205 iq
.attributes
["to"] = to
206 if(ID
): iq
.attributes
["id"] = ID
207 query
= iq
.addElement("query")
208 query
.attributes
["xmlns"] = disco
.DISCO_ITEMS
209 self
.pytrans
.send(iq
)
213 def __init__(self
, pytrans
):
214 self
.pytrans
= pytrans
215 self
.pytrans
.discovery
.addFeature("vcard-temp", self
.incomingIq
, "USER")
216 self
.pytrans
.discovery
.addFeature("vcard-temp", self
.incomingIq
, config
.jid
)
218 def incomingIq(self
, el
):
219 itype
= el
.getAttribute("type")
220 fro
= el
.getAttribute("from")
221 froj
= jid
.intern(fro
)
222 to
= el
.getAttribute("to")
224 ID
= el
.getAttribute("id")
225 if itype
!= "get" and itype
!= "error":
226 self
.pytrans
.discovery
.sendIqError(to
=fro
, fro
=config
.jid
, ID
=ID
, xmlns
="vcard-temp", etype
="cancel", condition
="feature-not-implemented")
229 LogEvent(INFO
, "", "Sending vCard")
231 toGateway
= not (to
.find('@') > 0)
234 if not self
.pytrans
.sessions
.has_key(froj
.userhost()):
235 self
.pytrans
.discovery
.sendIqError(to
=fro
, fro
=config
.jid
, ID
=ID
, xmlns
="vcard-temp", etype
="auth", condition
="not-authorized")
237 s
= self
.pytrans
.sessions
[froj
.userhost()]
239 self
.pytrans
.discovery
.sendIqError(to
=fro
, fro
=config
.jid
, ID
=ID
, xmlns
="vcard-temp", etype
="auth", condition
="not-authorized")
242 c
= s
.contactList
.findContact(toj
.userhost())
244 self
.pytrans
.discovery
.sendIqError(to
=fro
, fro
=config
.jid
, ID
=ID
, xmlns
="vcard-temp", etype
="cancel", condition
="recipient-unavailable")
248 iq
= Element((None, "iq"))
249 iq
.attributes
["to"] = fro
250 iq
.attributes
["from"] = to
252 iq
.attributes
["id"] = ID
253 iq
.attributes
["type"] = "result"
254 vCard
= iq
.addElement("vCard")
255 vCard
.attributes
["xmlns"] = "vcard-temp"
257 FN
= vCard
.addElement("FN")
258 FN
.addContent(config
.discoName
)
259 DESC
= vCard
.addElement("DESC")
260 DESC
.addContent(config
.discoName
)
261 URL
= vCard
.addElement("URL")
262 URL
.addContent(legacy
.url
)
265 NICKNAME
= vCard
.addElement("NICKNAME")
266 NICKNAME
.addContent(c
.nickname
)
268 PHOTO
= c
.avatar
.makePhotoElement()
269 vCard
.addChild(PHOTO
)
271 self
.pytrans
.send(iq
)
273 class IqAvatarFactory
:
274 def __init__(self
, pytrans
):
275 self
.pytrans
= pytrans
276 self
.pytrans
.discovery
.addFeature(disco
.IQAVATAR
, self
.incomingIq
, "USER")
277 self
.pytrans
.discovery
.addFeature(disco
.STORAGEAVATAR
, self
.incomingIq
, "USER")
279 def incomingIq(self
, el
):
280 itype
= el
.getAttribute("type")
281 fro
= el
.getAttribute("from")
282 froj
= jid
.intern(fro
)
283 to
= el
.getAttribute("to")
285 ID
= el
.getAttribute("id")
287 if(itype
!= "get" and itype
!= "error"):
288 self
.pytrans
.discovery
.sendIqError(to
=fro
, fro
=config
.jid
, ID
=ID
, xmlns
=disco
.IQAVATAR
, etype
="cancel", condition
="feature-not-implemented")
291 LogEvent(INFO
, "", "Retrieving avatar")
293 if(not self
.pytrans
.sessions
.has_key(froj
.userhost())):
294 self
.pytrans
.discovery
.sendIqError(to
=fro
, fro
=config
.jid
, ID
=ID
, xmlns
=disco
.IQAVATAR
, etype
="auth", condition
="not-authorized")
296 s
= self
.pytrans
.sessions
[froj
.userhost()]
298 self
.pytrans
.discovery
.sendIqError(to
=fro
, fro
=config
.jid
, ID
=ID
, xmlns
=disco
.IQAVATAR
, etype
="auth", condition
="not-authorized")
301 c
= s
.contactList
.findContact(toj
.userhost())
303 self
.pytrans
.discovery
.sendIqError(to
=fro
, fro
=config
.jid
, ID
=ID
, xmlns
=disco
.IQAVATAR
, etype
="cancel", condition
="recipient-unavailable")
306 iq
= Element((None, "iq"))
307 iq
.attributes
["to"] = fro
308 iq
.attributes
["from"] = to
310 iq
.attributes
["id"] = ID
311 iq
.attributes
["type"] = "result"
312 query
= iq
.addElement("query")
313 query
.attributes
["xmlns"] = disco
.IQAVATAR
315 DATA
= c
.avatar
.makeDataElement()
318 self
.pytrans
.send(iq
)
323 def __init__(self
, pytrans
):
324 self
.pytrans
= pytrans
325 # self.pingCounter = 0
326 # self.pingTask = task.LoopingCall(self.pingCheck)
327 self
.pingTask
= task
.LoopingCall(self
.whitespace
)
328 # reactor.callLater(10.0, self.start)
331 # self.pingTask.start(120.0)
333 def whitespace(self
):
334 self
.pytrans
.send(" ")
336 # def pingCheck(self):
337 # if(self.pingCounter >= 2 and self.pytrans.xmlstream): # Two minutes of no response from the main server
338 # LogEvent(WARN, "", "Disconnecting because the main server has ignored our pings for too long.")
339 # self.pytrans.xmlstream.transport.loseConnection()
340 # elif(config.mainServerJID):
341 # d = self.pytrans.discovery.sendIq(self.makePingPacket())
342 # d.addCallback(self.pongReceived)
343 # d.addErrback(self.pongFailed)
344 # self.pingCounter += 1
346 # def pongReceived(self, el):
347 # self.pingCounter = 0
349 # def pongFailed(self, el):
352 # def makePingPacket(self):
353 # iq = Element((None, "iq"))
354 # iq.attributes["from"] = config.jid
355 # iq.attributes["to"] = config.mainServerJID
356 # iq.attributes["type"] = "get"
357 # query = iq.addElement("query")
358 # query.attributes["xmlns"] = disco.IQVERSION
361 class GatewayTranslator
:
362 def __init__(self
, pytrans
):
363 self
.pytrans
= pytrans
364 self
.pytrans
.discovery
.addFeature(disco
.IQGATEWAY
, self
.incomingIq
, config
.jid
)
366 def incomingIq(self
, el
):
367 fro
= el
.getAttribute("from")
368 ID
= el
.getAttribute("id")
369 itype
= el
.getAttribute("type")
371 self
.sendPrompt(fro
, ID
, utils
.getLang(el
))
372 elif(itype
== "set"):
373 self
.sendTranslation(fro
, ID
, el
)
376 def sendPrompt(self
, to
, ID
, ulang
):
379 iq
= Element((None, "iq"))
381 iq
.attributes
["type"] = "result"
382 iq
.attributes
["from"] = config
.jid
383 iq
.attributes
["to"] = to
385 iq
.attributes
["id"] = ID
386 query
= iq
.addElement("query")
387 query
.attributes
["xmlns"] = disco
.IQGATEWAY
388 desc
= query
.addElement("desc")
389 desc
.addContent(lang
.get(ulang
).gatewayTranslator
)
390 prompt
= query
.addElement("prompt")
392 self
.pytrans
.send(iq
)
394 def sendTranslation(self
, to
, ID
, el
):
397 # Find the user's legacy account
399 for query
in el
.elements():
400 if(query
.name
== "query"):
401 for child
in query
.elements():
402 if(child
.name
== "prompt"):
403 legacyaccount
= str(child
)
408 if(legacyaccount
and len(legacyaccount
) > 0):
409 LogEvent(INFO
, "", "Sending translated account.")
410 iq
= Element((None, "iq"))
411 iq
.attributes
["type"] = "result"
412 iq
.attributes
["from"] = config
.jid
413 iq
.attributes
["to"] = to
415 iq
.attributes
["id"] = ID
416 query
= iq
.addElement("query")
417 query
.attributes
["xmlns"] = disco
.IQGATEWAY
418 prompt
= query
.addElement("prompt")
419 prompt
.addContent(legacy
.translateAccount(legacyaccount
))
421 self
.pytrans
.send(iq
)
424 self
.pytrans
.discovery
.sendIqError(to
, ID
, disco
.IQGATEWAY
)
425 self
.pytrans
.discovery
.sendIqError(to
=to
, fro
=config
.jid
, ID
=ID
, xmlns
=disco
.IQGATEWAY
, etype
="retry", condition
="bad-request")
430 def __init__(self
, pytrans
):
431 self
.pytrans
= pytrans
432 self
.pytrans
.discovery
.addFeature(disco
.IQVERSION
, self
.incomingIq
, config
.jid
)
433 self
.pytrans
.discovery
.addFeature(disco
.IQVERSION
, self
.incomingIq
, "USER")
435 def incomingIq(self
, el
):
436 eltype
= el
.getAttribute("type")
437 if(eltype
!= "get"): return # Only answer "get" stanzas
441 def sendVersion(self
, el
):
443 iq
= Element((None, "iq"))
444 iq
.attributes
["type"] = "result"
445 iq
.attributes
["from"] = el
.getAttribute("to")
446 iq
.attributes
["to"] = el
.getAttribute("from")
447 if(el
.getAttribute("id")):
448 iq
.attributes
["id"] = el
.getAttribute("id")
449 query
= iq
.addElement("query")
450 query
.attributes
["xmlns"] = disco
.IQVERSION
451 name
= query
.addElement("name")
452 name
.addContent(config
.discoName
)
453 version
= query
.addElement("version")
454 version
.addContent(legacy
.version
)
455 os
= query
.addElement("os")
456 os
.addContent("Python" + ".".join([str(x
) for x
in sys
.version_info
[0:3]]) + " - " + sys
.platform
)
458 self
.pytrans
.send(iq
)
461 class FileTransferOOBSend
:
462 def __init__(self
, pytrans
):
463 self
.pytrans
= pytrans
464 self
.pytrans
.discovery
.addFeature(disco
.IQOOB
, self
.incomingOOB
, "USER")
466 def incomingOOB(self
, el
):
467 ID
= el
.getAttribute("id")
469 self
.pytrans
.discovery
.sendIqError(to
=el
.getAttribute("from"), fro
=el
.getAttribute("to"), ID
=ID
, xmlns
=disco
.IQOOB
, etype
="cancel", condition
="feature-not-implemented")
471 if el
.attributes
["type"] != "set":
473 for child
in el
.elements():
474 if child
.name
== "query":
479 for child
in query
.elements():
480 if child
.name
== "url":
481 url
= child
.__str
__()
486 froj
= jid
.intern(el
.getAttribute("from"))
487 toj
= jid
.intern(el
.getAttribute("to"))
488 session
= self
.pytrans
.sessions
.get(froj
.userhost(), None)
492 res
= utils
.getURLBits(url
, "http")
495 host
, port
, path
, filename
= res
499 iq
= Element((None, "iq"))
500 iq
.attributes
["to"] = froj
.full()
501 iq
.attributes
["from"] = toj
.full()
502 iq
.attributes
["type"] = "result"
504 iq
.attributes
["id"] = ID
505 iq
.addElement("query").attributes
["xmlns"] = "jabber:iq:oob"
506 self
.pytrans
.send(iq
)
508 def startTransfer(consumer
):
509 factory
= protocol
.ClientFactory()
510 factory
.protocol
= ft
.OOBSendConnector
514 factory
.consumer
= consumer
515 factory
.finished
= sendResult
516 reactor
.connectTCP(host
, port
, factory
)
518 def doSendFile(length
):
519 ft
.FTSend(session
, toj
.userhost(), startTransfer
, errOut
, filename
, length
)
521 # Make a HEAD request to grab the length of data first
522 factory
= protocol
.ClientFactory()
523 factory
.protocol
= ft
.OOBHeaderHelper
527 factory
.gotLength
= doSendFile
528 reactor
.connectTCP(host
, port
, factory
)
532 class Socks5FileTransfer
:
533 def __init__(self
, pytrans
):
534 self
.pytrans
= pytrans
535 self
.pytrans
.discovery
.addFeature(disco
.SI
, self
.incomingSI
, "USER")
536 self
.pytrans
.discovery
.addFeature(disco
.FT
, lambda: None, "USER")
537 self
.pytrans
.discovery
.addFeature(disco
.S5B
, self
.incomingS5B
, "USER")
540 def incomingSI(self
, el
):
541 ID
= el
.getAttribute("id")
543 self
.pytrans
.discovery
.sendIqError(to
=el
.getAttribute("from"), fro
=el
.getAttribute("to"), ID
=ID
, xmlns
=disco
.SI
, etype
="cancel", condition
="bad-request")
545 toj
= jid
.intern(el
.getAttribute("to"))
546 froj
= jid
.intern(el
.getAttribute("from"))
547 session
= self
.pytrans
.sessions
.get(froj
.userhost(), None)
552 if not (si
and si
.getAttribute("profile") == disco
.FT
):
555 if not (file and file.uri
== disco
.FT
):
559 filename
= file["name"]
560 filesize
= int(file["size"])
566 # Check that we can use socks5 bytestreams
568 if not (feature
and feature
.uri
== disco
.FEATURE_NEG
):
571 if not (x
and x
.uri
== disco
.XDATA
):
574 if not (field
and field
.getAttribute("var") == "stream-method"):
576 for option
in field
.elements():
580 value
= value
.__str
__()
581 if value
== disco
.S5B
:
584 return errOut() # Socks5 bytestreams not supported :(
587 def startTransfer(consumer
):
588 iq
= Element((None, "iq"))
589 iq
["type"] = "result"
590 iq
["to"] = froj
.full()
591 iq
["from"] = toj
.full()
593 si
= iq
.addElement("si")
594 si
["xmlns"] = disco
.SI
595 feature
= si
.addElement("feature")
596 feature
["xmlns"] = disco
.FEATURE_NEG
597 x
= feature
.addElement("x")
598 x
["xmlns"] = disco
.XDATA
600 field
= x
.addElement("field")
601 field
["var"] = "stream-method"
602 value
= field
.addElement("value")
603 value
.addContent(disco
.S5B
)
604 self
.pytrans
.send(iq
)
605 self
.sessions
[(froj
.full(), sid
)] = consumer
607 ft
.FTSend(session
, toj
.userhost(), startTransfer
, errOut
, filename
, filesize
)
609 def incomingS5B(self
, el
):
610 ID
= el
.getAttribute("id")
612 self
.pytrans
.discovery
.sendIqError(to
=el
.getAttribute("from"), fro
=el
.getAttribute("to"), ID
=ID
, xmlns
=disco
.S5B
, etype
="cancel", condition
="item-not-found")
614 if el
.getAttribute("type") != "set":
617 toj
= jid
.intern(el
.getAttribute("to"))
618 froj
= jid
.intern(el
.getAttribute("from"))
621 if not (query
and query
.getAttribute("mode", "tcp") == "tcp"):
623 sid
= query
.getAttribute("sid")
624 consumer
= self
.sessions
.pop((froj
.full(), sid
), None)
628 for streamhost
in query
.elements():
629 if streamhost
.name
== "streamhost":
631 JID
= streamhost
["jid"]
632 host
= streamhost
["host"]
633 port
= int(streamhost
["port"])
638 streamhosts
.append((JID
, host
, port
))
641 def gotStreamhost(host
):
642 for streamhost
in streamhosts
:
643 if streamhost
[1] == host
:
650 for connector
in factory
.connectors
:
651 # Stop any other connections
653 connector
.stopConnecting()
654 except error
.NotConnectingError
:
657 iq
= Element((None, "iq"))
658 iq
["type"] = "result"
659 iq
["from"] = toj
.full()
660 iq
["to"] = froj
.full()
662 query
= iq
.addElement("query")
663 query
["xmlns"] = disco
.S5B
664 streamhost
= query
.addElement("streamhost-used")
665 streamhost
["jid"] = jid
666 self
.pytrans
.send(iq
)
669 # Try the streamhosts
670 factory
= protocol
.ClientFactory()
671 factory
.protocol
= ft
.JEP65ConnectionSend
672 factory
.consumer
= consumer
673 factory
.hash = utils
.socks5Hash(sid
, froj
.full(), toj
.full())
674 factory
.madeConnection
= gotStreamhost
675 factory
.connectors
= []
676 for streamhost
in streamhosts
:
677 factory
.connectors
.append(reactor
.connectTCP(streamhost
[1], streamhost
[2], factory
))