oob.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. """
  2. SleekXMPP: The Sleek XMPP Library
  3. Copyright (C) 2011 Nathanael C. Fritz, Lance J.T. Stout
  4. This file is part of SleekXMPP.
  5. See the file LICENSE for copying permission.
  6. """
  7. import logging
  8. from sleekxmpp.stanza import Message, Presence, Iq
  9. from sleekxmpp.exceptions import XMPPError
  10. from sleekxmpp.xmlstream import register_stanza_plugin
  11. from sleekxmpp.xmlstream.handler import Callback
  12. from sleekxmpp.xmlstream.matcher import StanzaPath
  13. from sleekxmpp.plugins import BasePlugin
  14. from sleekxmpp.plugins.xep_0066 import stanza
  15. log = logging.getLogger(__name__)
  16. class XEP_0066(BasePlugin):
  17. """
  18. XEP-0066: Out of Band Data
  19. Out of Band Data is a basic method for transferring files between
  20. XMPP agents. The URL of the resource in question is sent to the receiving
  21. entity, which then downloads the resource before responding to the OOB
  22. request. OOB is also used as a generic means to transmit URLs in other
  23. stanzas to indicate where to find additional information.
  24. Also see <http://www.xmpp.org/extensions/xep-0066.html>.
  25. Events:
  26. oob_transfer -- Raised when a request to download a resource
  27. has been received.
  28. Methods:
  29. send_oob -- Send a request to another entity to download a file
  30. or other addressable resource.
  31. """
  32. name = 'xep_0066'
  33. description = 'XEP-0066: Out of Band Data'
  34. dependencies = set(['xep_0030'])
  35. stanza = stanza
  36. def plugin_init(self):
  37. """Start the XEP-0066 plugin."""
  38. self.url_handlers = {'global': self._default_handler,
  39. 'jid': {}}
  40. register_stanza_plugin(Iq, stanza.OOBTransfer)
  41. register_stanza_plugin(Message, stanza.OOB)
  42. register_stanza_plugin(Presence, stanza.OOB)
  43. self.xmpp.register_handler(
  44. Callback('OOB Transfer',
  45. StanzaPath('iq@type=set/oob_transfer'),
  46. self._handle_transfer))
  47. def plugin_end(self):
  48. self.xmpp.remove_handler('OOB Transfer')
  49. self.xmpp['xep_0030'].del_feature(feature=stanza.OOBTransfer.namespace)
  50. self.xmpp['xep_0030'].del_feature(feature=stanza.OOB.namespace)
  51. def session_bind(self, jid):
  52. self.xmpp['xep_0030'].add_feature(stanza.OOBTransfer.namespace)
  53. self.xmpp['xep_0030'].add_feature(stanza.OOB.namespace)
  54. def register_url_handler(self, jid=None, handler=None):
  55. """
  56. Register a handler to process download requests, either for all
  57. JIDs or a single JID.
  58. Arguments:
  59. jid -- If None, then set the handler as a global default.
  60. handler -- If None, then remove the existing handler for the
  61. given JID, or reset the global handler if the JID
  62. is None.
  63. """
  64. if jid is None:
  65. if handler is not None:
  66. self.url_handlers['global'] = handler
  67. else:
  68. self.url_handlers['global'] = self._default_handler
  69. else:
  70. if handler is not None:
  71. self.url_handlers['jid'][jid] = handler
  72. else:
  73. del self.url_handlers['jid'][jid]
  74. def send_oob(self, to, url, desc=None, ifrom=None, **iqargs):
  75. """
  76. Initiate a basic file transfer by sending the URL of
  77. a file or other resource.
  78. Arguments:
  79. url -- The URL of the resource to transfer.
  80. desc -- An optional human readable description of the item
  81. that is to be transferred.
  82. ifrom -- Specifiy the sender's JID.
  83. block -- If true, block and wait for the stanzas' reply.
  84. timeout -- The time in seconds to block while waiting for
  85. a reply. If None, then wait indefinitely.
  86. callback -- Optional callback to execute when a reply is
  87. received instead of blocking and waiting for
  88. the reply.
  89. """
  90. iq = self.xmpp.Iq()
  91. iq['type'] = 'set'
  92. iq['to'] = to
  93. iq['from'] = ifrom
  94. iq['oob_transfer']['url'] = url
  95. iq['oob_transfer']['desc'] = desc
  96. return iq.send(**iqargs)
  97. def _run_url_handler(self, iq):
  98. """
  99. Execute the appropriate handler for a transfer request.
  100. Arguments:
  101. iq -- The Iq stanza containing the OOB transfer request.
  102. """
  103. if iq['to'] in self.url_handlers['jid']:
  104. return self.url_handlers['jid'][iq['to']](iq)
  105. else:
  106. if self.url_handlers['global']:
  107. self.url_handlers['global'](iq)
  108. else:
  109. raise XMPPError('service-unavailable')
  110. def _default_handler(self, iq):
  111. """
  112. As a safe default, don't actually download files.
  113. Register a new handler using self.register_url_handler to
  114. screen requests and download files.
  115. Arguments:
  116. iq -- The Iq stanza containing the OOB transfer request.
  117. """
  118. raise XMPPError('service-unavailable')
  119. def _handle_transfer(self, iq):
  120. """
  121. Handle receiving an out-of-band transfer request.
  122. Arguments:
  123. iq -- An Iq stanza containing an OOB transfer request.
  124. """
  125. log.debug('Received out-of-band data request for %s from %s:' % (
  126. iq['oob_transfer']['url'], iq['from']))
  127. self._run_url_handler(iq)
  128. iq.reply().send()