]>
code.delx.au - pymsnt/blob - 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
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():
141 xmlns
= child
.defaultUri
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")
223 ID
= el
.getAttribute("id")
224 if(itype
!= "get" and itype
!= "error"):
225 self
.pytrans
.discovery
.sendIqError(to
=fro
, fro
=config
.jid
, ID
=ID
, xmlns
="vcard-temp", etype
="cancel", condition
="feature-not-implemented")
228 LogEvent(INFO
, "", "Sending vCard")
230 toGateway
= not (to
.find('@') > 0)
233 if(not self
.pytrans
.sessions
.has_key(froj
.userhost())):
234 self
.pytrans
.discovery
.sendIqError(to
=fro
, fro
=config
.jid
, ID
=ID
, xmlns
="vcard-temp", etype
="auth", condition
="not-authorized")
236 s
= self
.pytrans
.sessions
[froj
.userhost()]
238 self
.pytrans
.discovery
.sendIqError(to
=fro
, fro
=config
.jid
, ID
=ID
, xmlns
="vcard-temp", etype
="auth", condition
="not-authorized")
241 c
= s
.contactList
.findContact(to
)
243 self
.pytrans
.discovery
.sendIqError(to
=fro
, fro
=config
.jid
, ID
=ID
, xmlns
="vcard-temp", etype
="cancel", condition
="recipient-unavailable")
247 iq
= Element((None, "iq"))
248 iq
.attributes
["to"] = fro
249 iq
.attributes
["from"] = to
251 iq
.attributes
["id"] = ID
252 iq
.attributes
["type"] = "result"
253 vCard
= iq
.addElement("vCard")
254 vCard
.attributes
["xmlns"] = "vcard-temp"
256 FN
= vCard
.addElement("FN")
257 FN
.addContent(legacy
.name
)
258 DESC
= vCard
.addElement("DESC")
259 DESC
.addContent(legacy
.name
)
260 URL
= vCard
.addElement("URL")
261 URL
.addContent(legacy
.url
)
264 NICKNAME
= vCard
.addElement("NICKNAME")
265 NICKNAME
.addContent(c
.nickname
)
267 PHOTO
= c
.avatar
.makePhotoElement()
268 vCard
.addChild(PHOTO
)
270 self
.pytrans
.send(iq
)
272 class IqAvatarFactory
:
273 def __init__(self
, pytrans
):
274 self
.pytrans
= pytrans
275 self
.pytrans
.discovery
.addFeature(disco
.IQAVATAR
, self
.incomingIq
, "USER")
276 self
.pytrans
.discovery
.addFeature(disco
.STORAGEAVATAR
, self
.incomingIq
, "USER")
278 def incomingIq(self
, el
):
279 itype
= el
.getAttribute("type")
280 fro
= el
.getAttribute("from")
281 froj
= jid
.intern(fro
)
282 to
= el
.getAttribute("to")
283 ID
= el
.getAttribute("id")
285 if(itype
!= "get" and itype
!= "error"):
286 self
.pytrans
.discovery
.sendIqError(to
=fro
, fro
=config
.jid
, ID
=ID
, xmlns
=disco
.IQAVATAR
, etype
="cancel", condition
="feature-not-implemented")
289 LogEvent(INFO
, "", "Retrieving avatar")
291 if(not self
.pytrans
.sessions
.has_key(froj
.userhost())):
292 self
.pytrans
.discovery
.sendIqError(to
=fro
, fro
=config
.jid
, ID
=ID
, xmlns
=disco
.IQAVATAR
, etype
="auth", condition
="not-authorized")
294 s
= self
.pytrans
.sessions
[froj
.userhost()]
296 self
.pytrans
.discovery
.sendIqError(to
=fro
, fro
=config
.jid
, ID
=ID
, xmlns
=disco
.IQAVATAR
, etype
="auth", condition
="not-authorized")
299 c
= s
.contactList
.findContact(to
)
301 self
.pytrans
.discovery
.sendIqError(to
=fro
, fro
=config
.jid
, ID
=ID
, xmlns
=disco
.IQAVATAR
, etype
="cancel", condition
="recipient-unavailable")
304 iq
= Element((None, "iq"))
305 iq
.attributes
["to"] = fro
306 iq
.attributes
["from"] = to
308 iq
.attributes
["id"] = ID
309 iq
.attributes
["type"] = "result"
310 query
= iq
.addElement("query")
311 query
.attributes
["xmlns"] = disco
.IQAVATAR
313 DATA
= c
.avatar
.makeDataElement()
316 self
.pytrans
.send(iq
)
321 def __init__(self
, pytrans
):
322 self
.pytrans
= pytrans
323 # self.pingCounter = 0
324 # self.pingTask = task.LoopingCall(self.pingCheck)
325 self
.pingTask
= task
.LoopingCall(self
.whitespace
)
326 # reactor.callLater(10.0, self.start)
329 # self.pingTask.start(120.0)
331 def whitespace(self
):
332 self
.pytrans
.send(" ")
334 # def pingCheck(self):
335 # if(self.pingCounter >= 2 and self.pytrans.xmlstream): # Two minutes of no response from the main server
336 # LogEvent(WARN, "", "Disconnecting because the main server has ignored our pings for too long.")
337 # self.pytrans.xmlstream.transport.loseConnection()
338 # elif(config.mainServerJID):
339 # d = self.pytrans.discovery.sendIq(self.makePingPacket())
340 # d.addCallback(self.pongReceived)
341 # d.addErrback(self.pongFailed)
342 # self.pingCounter += 1
344 # def pongReceived(self, el):
345 # self.pingCounter = 0
347 # def pongFailed(self, el):
350 # def makePingPacket(self):
351 # iq = Element((None, "iq"))
352 # iq.attributes["from"] = config.jid
353 # iq.attributes["to"] = config.mainServerJID
354 # iq.attributes["type"] = "get"
355 # query = iq.addElement("query")
356 # query.attributes["xmlns"] = disco.IQVERSION
359 class GatewayTranslator
:
360 def __init__(self
, pytrans
):
361 self
.pytrans
= pytrans
362 self
.pytrans
.discovery
.addFeature(disco
.IQGATEWAY
, self
.incomingIq
, config
.jid
)
364 def incomingIq(self
, el
):
365 fro
= el
.getAttribute("from")
366 ID
= el
.getAttribute("id")
367 itype
= el
.getAttribute("type")
369 self
.sendPrompt(fro
, ID
, utils
.getLang(el
))
370 elif(itype
== "set"):
371 self
.sendTranslation(fro
, ID
, el
)
374 def sendPrompt(self
, to
, ID
, ulang
):
377 iq
= Element((None, "iq"))
379 iq
.attributes
["type"] = "result"
380 iq
.attributes
["from"] = config
.jid
381 iq
.attributes
["to"] = to
383 iq
.attributes
["id"] = ID
384 query
= iq
.addElement("query")
385 query
.attributes
["xmlns"] = disco
.IQGATEWAY
386 desc
= query
.addElement("desc")
387 desc
.addContent(lang
.get(ulang
).gatewayTranslator
)
388 prompt
= query
.addElement("prompt")
390 self
.pytrans
.send(iq
)
392 def sendTranslation(self
, to
, ID
, el
):
395 # Find the user's legacy account
397 for query
in el
.elements():
398 if(query
.name
== "query"):
399 for child
in query
.elements():
400 if(child
.name
== "prompt"):
401 legacyaccount
= str(child
)
406 if(legacyaccount
and len(legacyaccount
) > 0):
407 LogEvent(INFO
, "", "Sending translated account.")
408 iq
= Element((None, "iq"))
409 iq
.attributes
["type"] = "result"
410 iq
.attributes
["from"] = config
.jid
411 iq
.attributes
["to"] = to
413 iq
.attributes
["id"] = ID
414 query
= iq
.addElement("query")
415 query
.attributes
["xmlns"] = disco
.IQGATEWAY
416 prompt
= query
.addElement("prompt")
417 prompt
.addContent(legacy
.translateAccount(legacyaccount
))
419 self
.pytrans
.send(iq
)
422 self
.pytrans
.discovery
.sendIqError(to
, ID
, disco
.IQGATEWAY
)
423 self
.pytrans
.discovery
.sendIqError(to
=to
, fro
=config
.jid
, ID
=ID
, xmlns
=disco
.IQGATEWAY
, etype
="retry", condition
="bad-request")
428 def __init__(self
, pytrans
):
429 self
.pytrans
= pytrans
430 self
.pytrans
.discovery
.addFeature(disco
.IQVERSION
, self
.incomingIq
, config
.jid
)
431 self
.pytrans
.discovery
.addFeature(disco
.IQVERSION
, self
.incomingIq
, "USER")
433 def incomingIq(self
, el
):
434 eltype
= el
.getAttribute("type")
435 if(eltype
!= "get"): return # Only answer "get" stanzas
439 def sendVersion(self
, el
):
441 iq
= Element((None, "iq"))
442 iq
.attributes
["type"] = "result"
443 iq
.attributes
["from"] = el
.getAttribute("to")
444 iq
.attributes
["to"] = el
.getAttribute("from")
445 if(el
.getAttribute("id")):
446 iq
.attributes
["id"] = el
.getAttribute("id")
447 query
= iq
.addElement("query")
448 query
.attributes
["xmlns"] = disco
.IQVERSION
449 name
= query
.addElement("name")
450 name
.addContent(legacy
.name
)
451 version
= query
.addElement("version")
452 version
.addContent(legacy
.version
)
453 os
= query
.addElement("os")
454 os
.addContent("Python" + ".".join([str(x
) for x
in sys
.version_info
[0:3]]) + " - " + sys
.platform
)
456 self
.pytrans
.send(iq
)
459 class FileTransferOOBSend
:
460 def __init__(self
, pytrans
):
461 self
.pytrans
= pytrans
462 self
.pytrans
.discovery
.addFeature(disco
.IQOOB
, self
.incomingOOB
, "USER")
464 def incomingOOB(self
, el
):
465 ID
= el
.getAttribute("id")
467 self
.pytrans
.discovery
.sendIqError(to
=el
.getAttribute("from"), fro
=el
.getAttribute("to"), ID
=ID
, xmlns
=disco
.IQOOB
, etype
="cancel", condition
="feature-not-implemented")
469 if el
.attributes
["type"] != "set":
471 for child
in el
.elements():
472 if child
.name
== "query":
477 for child
in query
.elements():
478 if child
.name
== "url":
479 url
= child
.__str
__()
484 froj
= jid
.intern(el
.getAttribute("from"))
485 toj
= jid
.intern(el
.getAttribute("to"))
486 session
= self
.pytrans
.sessions
.get(froj
.userhost(), None)
490 res
= utils
.getURLBits(url
, "http")
493 host
, port
, path
, filename
= res
497 iq
= Element((None, "iq"))
498 iq
.attributes
["to"] = froj
.full()
499 iq
.attributes
["from"] = toj
.full()
500 iq
.attributes
["type"] = "result"
502 iq
.attributes
["id"] = ID
503 iq
.addElement("query").attributes
["xmlns"] = "jabber:iq:oob"
504 self
.pytrans
.send(iq
)
506 def startTransfer(consumer
):
507 factory
= protocol
.ClientFactory()
508 factory
.protocol
= ft
.OOBSendConnector
512 factory
.consumer
= consumer
513 factory
.finished
= sendResult
514 reactor
.connectTCP(host
, port
, factory
)
516 def doSendFile(length
):
517 session
.legacycon
.sendFile(toj
.userhost(), ft
.FTSend(startTransfer
, errOut
, filename
, length
))
519 # Make a HEAD request to grab the length of data first
520 factory
= protocol
.ClientFactory()
521 factory
.protocol
= ft
.OOBHeaderHelper
525 factory
.gotLength
= doSendFile
526 reactor
.connectTCP(host
, port
, factory
)
530 class Socks5FileTransfer
:
531 def __init__(self
, pytrans
):
532 self
.pytrans
= pytrans
533 self
.pytrans
.discovery
.addFeature(disco
.SI
, self
.incomingSI
, "USER")
534 self
.pytrans
.discovery
.addFeature(disco
.FT
, lambda: None, "USER")
535 self
.pytrans
.discovery
.addFeature(disco
.S5B
, self
.incomingS5B
, "USER")
538 def incomingSI(self
, el
):
539 ID
= el
.getAttribute("id")
541 self
.pytrans
.discovery
.sendIqError(to
=el
.getAttribute("from"), fro
=el
.getAttribute("to"), ID
=ID
, xmlns
=disco
.SI
, etype
="cancel", condition
="bad-request")
543 toj
= jid
.intern(el
.getAttribute("to"))
544 froj
= jid
.intern(el
.getAttribute("from"))
545 session
= self
.pytrans
.sessions
.get(froj
.userhost(), None)
550 if not (si
and si
.getAttribute("profile") == disco
.FT
):
553 if not (file and file.defaultUri
== disco
.FT
):
557 filename
= file["name"]
558 filesize
= int(file["size"])
564 # Check that we can use socks5 bytestreams
566 if not (feature
and feature
.defaultUri
== disco
.FEATURE_NEG
):
569 if not (x
and x
.defaultUri
== disco
.XDATA
):
572 if not (field
and field
.getAttribute("var") == "stream-method"):
574 for option
in field
.elements():
578 value
= value
.__str
__()
579 if value
== disco
.S5B
:
582 return errOut() # Socks5 bytestreams not supported :(
585 def startTransfer(consumer
):
586 iq
= Element((None, "iq"))
587 iq
["type"] = "result"
588 iq
["to"] = froj
.full()
589 iq
["from"] = toj
.full()
591 si
= iq
.addElement("si")
592 si
["xmlns"] = disco
.SI
593 feature
= si
.addElement("feature")
594 feature
["xmlns"] = disco
.FEATURE_NEG
595 x
= feature
.addElement("x")
596 x
["xmlns"] = disco
.XDATA
598 field
= x
.addElement("field")
599 field
["var"] = "stream-method"
600 value
= field
.addElement("value")
601 value
.addContent(disco
.S5B
)
602 self
.pytrans
.send(iq
)
603 self
.sessions
[(froj
.full(), sid
)] = consumer
605 session
.legacycon
.sendFile(toj
.userhost(), ft
.FTSend(startTransfer
, errOut
, filename
, filesize
))
607 def incomingS5B(self
, el
):
608 ID
= el
.getAttribute("id")
610 self
.pytrans
.discovery
.sendIqError(to
=el
.getAttribute("from"), fro
=el
.getAttribute("to"), ID
=ID
, xmlns
=disco
.S5B
, etype
="cancel", condition
="item-not-found")
612 if el
.getAttribute("type") != "set":
615 toj
= jid
.intern(el
.getAttribute("to"))
616 froj
= jid
.intern(el
.getAttribute("from"))
619 if not (query
and query
.getAttribute("mode") == "tcp"):
621 sid
= query
.getAttribute("sid")
622 consumer
= self
.sessions
.pop((froj
.full(), sid
), None)
626 for streamhost
in query
.elements():
627 if streamhost
.name
== "streamhost":
629 JID
= streamhost
["jid"]
630 host
= streamhost
["host"]
631 port
= int(streamhost
["port"])
636 streamhosts
.append((JID
, host
, port
))
639 def gotStreamhost(host
):
640 for streamhost
in streamhosts
:
641 if streamhost
[1] == host
:
648 for connector
in factory
.connectors
:
649 # Stop any other connections
651 connector
.stopConnecting()
652 except error
.NotConnectingError
:
655 iq
= Element((None, "iq"))
656 iq
["type"] = "result"
657 iq
["from"] = toj
.full()
658 iq
["to"] = froj
.full()
660 query
= iq
.addElement("query")
661 query
["xmlns"] = disco
.S5B
662 streamhost
= query
.addElement("streamhost-used")
663 streamhost
["jid"] = jid
664 self
.pytrans
.send(iq
)
667 # Try the streamhosts
668 factory
= protocol
.ClientFactory()
669 factory
.protocol
= ft
.JEP65ConnectionSend
670 factory
.consumer
= consumer
671 factory
.hash = utils
.socks5Hash(sid
, froj
.full(), toj
.full())
672 factory
.madeConnection
= gotStreamhost
673 factory
.connectors
= []
674 for streamhost
in streamhosts
:
675 factory
.connectors
.append(reactor
.connectTCP(streamhost
[1], streamhost
[2], factory
))