xep_0138.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. """
  2. SleekXMPP: The Sleek XMPP Library
  3. Copyright (C) 2011 Nathanael C. Fritz
  4. This file is part of SleekXMPP.
  5. See the file LICENSE for copying permission.
  6. """
  7. import logging
  8. import socket
  9. import zlib
  10. from sleekxmpp.thirdparty.suelta.util import bytes
  11. from sleekxmpp.stanza import StreamFeatures
  12. from sleekxmpp.xmlstream import RestartStream, register_stanza_plugin, ElementBase, StanzaBase
  13. from sleekxmpp.xmlstream.matcher import *
  14. from sleekxmpp.xmlstream.handler import *
  15. from sleekxmpp.plugins import BasePlugin, register_plugin
  16. log = logging.getLogger(__name__)
  17. class Compression(ElementBase):
  18. name = 'compression'
  19. namespace = 'http://jabber.org/features/compress'
  20. interfaces = set(('methods',))
  21. plugin_attrib = 'compression'
  22. plugin_tag_map = {}
  23. plugin_attrib_map = {}
  24. def get_methods(self):
  25. methods = []
  26. for method in self.xml.findall('{%s}method' % self.namespace):
  27. methods.append(method.text)
  28. return methods
  29. class Compress(StanzaBase):
  30. name = 'compress'
  31. namespace = 'http://jabber.org/protocol/compress'
  32. interfaces = set(('method',))
  33. sub_interfaces = interfaces
  34. plugin_attrib = 'compress'
  35. plugin_tag_map = {}
  36. plugin_attrib_map = {}
  37. def setup(self, xml):
  38. StanzaBase.setup(self, xml)
  39. self.xml.tag = self.tag_name()
  40. class Compressed(StanzaBase):
  41. name = 'compressed'
  42. namespace = 'http://jabber.org/protocol/compress'
  43. interfaces = set()
  44. plugin_tag_map = {}
  45. plugin_attrib_map = {}
  46. def setup(self, xml):
  47. StanzaBase.setup(self, xml)
  48. self.xml.tag = self.tag_name()
  49. class ZlibSocket(object):
  50. def __init__(self, socketobj):
  51. self.__socket = socketobj
  52. self.compressor = zlib.compressobj()
  53. self.decompressor = zlib.decompressobj(zlib.MAX_WBITS)
  54. def __getattr__(self, name):
  55. return getattr(self.__socket, name)
  56. def send(self, data):
  57. sentlen = len(data)
  58. data = self.compressor.compress(data)
  59. data += self.compressor.flush(zlib.Z_SYNC_FLUSH)
  60. log.debug(b'>>> (compressed)' + (data.encode("hex")))
  61. #return self.__socket.send(data)
  62. sentactuallen = self.__socket.send(data)
  63. assert(sentactuallen == len(data))
  64. return sentlen
  65. def recv(self, *args, **kwargs):
  66. data = self.__socket.recv(*args, **kwargs)
  67. log.debug(b'<<< (compressed)' + data.encode("hex"))
  68. return self.decompressor.decompress(self.decompressor.unconsumed_tail + data)
  69. class XEP_0138(BasePlugin):
  70. """
  71. XEP-0138: Compression
  72. """
  73. name = "xep_0138"
  74. description = "XEP-0138: Compression"
  75. dependencies = set(["xep_0030"])
  76. def plugin_init(self):
  77. self.xep = '0138'
  78. self.description = 'Stream Compression (Generic)'
  79. self.compression_methods = {'zlib': True}
  80. register_stanza_plugin(StreamFeatures, Compression)
  81. self.xmpp.register_stanza(Compress)
  82. self.xmpp.register_stanza(Compressed)
  83. self.xmpp.register_handler(
  84. Callback('Compressed',
  85. StanzaPath('compressed'),
  86. self._handle_compressed,
  87. instream=True))
  88. self.xmpp.register_feature('compression',
  89. self._handle_compression,
  90. restart=True,
  91. order=self.config.get('order', 5))
  92. def register_compression_method(self, name, handler):
  93. self.compression_methods[name] = handler
  94. def _handle_compression(self, features):
  95. for method in features['compression']['methods']:
  96. if method in self.compression_methods:
  97. log.info('Attempting to use %s compression' % method)
  98. c = Compress(self.xmpp)
  99. c['method'] = method
  100. c.send(now=True)
  101. return True
  102. return False
  103. def _handle_compressed(self, stanza):
  104. self.xmpp.features.add('compression')
  105. log.debug('Stream Compressed!')
  106. compressed_socket = ZlibSocket(self.xmpp.socket)
  107. self.xmpp.set_socket(compressed_socket)
  108. raise RestartStream()
  109. def _handle_failure(self, stanza):
  110. pass
  111. xep_0138 = XEP_0138
  112. register_plugin(XEP_0138)