/ src / helper_msgcoding.py
helper_msgcoding.py
  1  """
  2  Message encoding end decoding functions
  3  """
  4  
  5  import string
  6  import zlib
  7  
  8  from . import messagetypes
  9  from .bmconfigparser import config
 10  from .debug import logger
 11  from .tr import _translate
 12  
 13  try:
 14      import msgpack
 15  except ImportError:
 16      try:
 17          import umsgpack as msgpack
 18      except ImportError:
 19          from . import fallback.umsgpack.umsgpack as msgpack
 20  
 21  BITMESSAGE_ENCODING_IGNORE = 0
 22  BITMESSAGE_ENCODING_TRIVIAL = 1
 23  BITMESSAGE_ENCODING_SIMPLE = 2
 24  BITMESSAGE_ENCODING_EXTENDED = 3
 25  
 26  
 27  class MsgEncodeException(Exception):
 28      """Exception during message encoding"""
 29      pass
 30  
 31  
 32  class MsgDecodeException(Exception):
 33      """Exception during message decoding"""
 34      pass
 35  
 36  
 37  class DecompressionSizeException(MsgDecodeException):
 38      # pylint: disable=super-init-not-called
 39      """Decompression resulted in too much data (attack protection)"""
 40      def __init__(self, size):
 41          self.size = size
 42  
 43  
 44  class MsgEncode(object):
 45      """Message encoder class"""
 46      def __init__(self, message, encoding=BITMESSAGE_ENCODING_SIMPLE):
 47          self.data = None
 48          self.encoding = encoding
 49          self.length = 0
 50          if self.encoding == BITMESSAGE_ENCODING_EXTENDED:
 51              self.encodeExtended(message)
 52          elif self.encoding == BITMESSAGE_ENCODING_SIMPLE:
 53              self.encodeSimple(message)
 54          elif self.encoding == BITMESSAGE_ENCODING_TRIVIAL:
 55              self.encodeTrivial(message)
 56          else:
 57              raise MsgEncodeException("Unknown encoding %i" % (encoding))
 58  
 59      def encodeExtended(self, message):
 60          """Handle extended encoding"""
 61          try:
 62              msgObj = messagetypes.message.Message()
 63              self.data = zlib.compress(msgpack.dumps(msgObj.encode(message)), 9)
 64          except zlib.error:
 65              logger.error("Error compressing message")
 66              raise MsgEncodeException("Error compressing message")
 67          except msgpack.exceptions.PackException:
 68              logger.error("Error msgpacking message")
 69              raise MsgEncodeException("Error msgpacking message")
 70          self.length = len(self.data)
 71  
 72      def encodeSimple(self, message):
 73          """Handle simple encoding"""
 74          self.data = 'Subject:%(subject)s\nBody:%(body)s' % message
 75          self.length = len(self.data)
 76  
 77      def encodeTrivial(self, message):
 78          """Handle trivial encoding"""
 79          self.data = message['body']
 80          self.length = len(self.data)
 81  
 82  
 83  class MsgDecode(object):
 84      """Message decoder class"""
 85      def __init__(self, encoding, data):
 86          self.encoding = encoding
 87          if self.encoding == BITMESSAGE_ENCODING_EXTENDED:
 88              self.decodeExtended(data)
 89          elif self.encoding in (
 90                  BITMESSAGE_ENCODING_SIMPLE, BITMESSAGE_ENCODING_TRIVIAL):
 91              self.decodeSimple(data)
 92          else:
 93              self.body = _translate(
 94                  "MsgDecode",
 95                  "The message has an unknown encoding.\n"
 96                  "Perhaps you should upgrade Bitmessage.")
 97              self.subject = _translate("MsgDecode", "Unknown encoding")
 98  
 99      def decodeExtended(self, data):
100          """Handle extended encoding"""
101          dc = zlib.decompressobj()
102          tmp = ""
103          while len(tmp) <= config.safeGetInt("zlib", "maxsize"):
104              try:
105                  got = dc.decompress(
106                      data, config.safeGetInt("zlib", "maxsize")
107                      + 1 - len(tmp))
108                  # EOF
109                  if got == "":
110                      break
111                  tmp += got
112                  data = dc.unconsumed_tail
113              except zlib.error:
114                  logger.error("Error decompressing message")
115                  raise MsgDecodeException("Error decompressing message")
116          else:
117              raise DecompressionSizeException(len(tmp))
118  
119          try:
120              tmp = msgpack.loads(tmp)
121          except (msgpack.exceptions.UnpackException,
122                  msgpack.exceptions.ExtraData):
123              logger.error("Error msgunpacking message")
124              raise MsgDecodeException("Error msgunpacking message")
125  
126          try:
127              msgType = tmp[""]
128          except KeyError:
129              logger.error("Message type missing")
130              raise MsgDecodeException("Message type missing")
131  
132          msgObj = messagetypes.constructObject(tmp)
133          if msgObj is None:
134              raise MsgDecodeException("Malformed message")
135          try:
136              msgObj.process()
137          except:  # noqa:E722
138              raise MsgDecodeException("Malformed message")
139          if msgType == "message":
140              self.subject = msgObj.subject
141              self.body = msgObj.body
142  
143      def decodeSimple(self, data):
144          """Handle simple encoding"""
145          bodyPositionIndex = string.find(data, '\nBody:')
146          if bodyPositionIndex > 1:
147              subject = data[8:bodyPositionIndex]
148              # Only save and show the first 500 characters of the subject.
149              # Any more is probably an attack.
150              subject = subject[:500]
151              body = data[bodyPositionIndex + 6:]
152          else:
153              subject = ''
154              body = data
155          # Throw away any extra lines (headers) after the subject.
156          if subject:
157              subject = subject.splitlines()[0]
158          self.subject = subject
159          self.body = body