您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 

172 行
6.0 KiB

  1. import base64
  2. import binascii
  3. from .exceptions import Base64Error, DecodeError
  4. class Base64Decoder(object):
  5. """This object provides an interface to decode a stream of Base64 data. It
  6. is instantiated with an "underlying object", and whenever a write()
  7. operation is performed, it will decode the incoming data as Base64, and
  8. call write() on the underlying object. This is primarily used for decoding
  9. form data encoded as Base64, but can be used for other purposes::
  10. from multipart.decoders import Base64Decoder
  11. fd = open("notb64.txt", "wb")
  12. decoder = Base64Decoder(fd)
  13. try:
  14. decoder.write("Zm9vYmFy") # "foobar" in Base64
  15. decoder.finalize()
  16. finally:
  17. decoder.close()
  18. # The contents of "notb64.txt" should be "foobar".
  19. This object will also pass all finalize() and close() calls to the
  20. underlying object, if the underlying object supports them.
  21. Note that this class maintains a cache of base64 chunks, so that a write of
  22. arbitrary size can be performed. You must call :meth:`finalize` on this
  23. object after all writes are completed to ensure that all data is flushed
  24. to the underlying object.
  25. :param underlying: the underlying object to pass writes to
  26. """
  27. def __init__(self, underlying):
  28. self.cache = bytearray()
  29. self.underlying = underlying
  30. def write(self, data):
  31. """Takes any input data provided, decodes it as base64, and passes it
  32. on to the underlying object. If the data provided is invalid base64
  33. data, then this method will raise
  34. a :class:`multipart.exceptions.DecodeError`
  35. :param data: base64 data to decode
  36. """
  37. # Prepend any cache info to our data.
  38. if len(self.cache) > 0:
  39. data = self.cache + data
  40. # Slice off a string that's a multiple of 4.
  41. decode_len = (len(data) // 4) * 4
  42. val = data[:decode_len]
  43. # Decode and write, if we have any.
  44. if len(val) > 0:
  45. try:
  46. decoded = base64.b64decode(val)
  47. except Base64Error:
  48. raise DecodeError('There was an error raised while decoding '
  49. 'base64-encoded data.')
  50. self.underlying.write(decoded)
  51. # Get the remaining bytes and save in our cache.
  52. remaining_len = len(data) % 4
  53. if remaining_len > 0:
  54. self.cache = data[-remaining_len:]
  55. else:
  56. self.cache = b''
  57. # Return the length of the data to indicate no error.
  58. return len(data)
  59. def close(self):
  60. """Close this decoder. If the underlying object has a `close()`
  61. method, this function will call it.
  62. """
  63. if hasattr(self.underlying, 'close'):
  64. self.underlying.close()
  65. def finalize(self):
  66. """Finalize this object. This should be called when no more data
  67. should be written to the stream. This function can raise a
  68. :class:`multipart.exceptions.DecodeError` if there is some remaining
  69. data in the cache.
  70. If the underlying object has a `finalize()` method, this function will
  71. call it.
  72. """
  73. if len(self.cache) > 0:
  74. raise DecodeError('There are %d bytes remaining in the '
  75. 'Base64Decoder cache when finalize() is called'
  76. % len(self.cache))
  77. if hasattr(self.underlying, 'finalize'):
  78. self.underlying.finalize()
  79. def __repr__(self):
  80. return "%s(underlying=%r)" % (self.__class__.__name__, self.underlying)
  81. class QuotedPrintableDecoder(object):
  82. """This object provides an interface to decode a stream of quoted-printable
  83. data. It is instantiated with an "underlying object", in the same manner
  84. as the :class:`multipart.decoders.Base64Decoder` class. This class behaves
  85. in exactly the same way, including maintaining a cache of quoted-printable
  86. chunks.
  87. :param underlying: the underlying object to pass writes to
  88. """
  89. def __init__(self, underlying):
  90. self.cache = b''
  91. self.underlying = underlying
  92. def write(self, data):
  93. """Takes any input data provided, decodes it as quoted-printable, and
  94. passes it on to the underlying object.
  95. :param data: quoted-printable data to decode
  96. """
  97. # Prepend any cache info to our data.
  98. if len(self.cache) > 0:
  99. data = self.cache + data
  100. # If the last 2 characters have an '=' sign in it, then we won't be
  101. # able to decode the encoded value and we'll need to save it for the
  102. # next decoding step.
  103. if data[-2:].find(b'=') != -1:
  104. enc, rest = data[:-2], data[-2:]
  105. else:
  106. enc = data
  107. rest = b''
  108. # Encode and write, if we have data.
  109. if len(enc) > 0:
  110. self.underlying.write(binascii.a2b_qp(enc))
  111. # Save remaining in cache.
  112. self.cache = rest
  113. return len(data)
  114. def close(self):
  115. """Close this decoder. If the underlying object has a `close()`
  116. method, this function will call it.
  117. """
  118. if hasattr(self.underlying, 'close'):
  119. self.underlying.close()
  120. def finalize(self):
  121. """Finalize this object. This should be called when no more data
  122. should be written to the stream. This function will not raise any
  123. exceptions, but it may write more data to the underlying object if
  124. there is data remaining in the cache.
  125. If the underlying object has a `finalize()` method, this function will
  126. call it.
  127. """
  128. # If we have a cache, write and then remove it.
  129. if len(self.cache) > 0:
  130. self.underlying.write(binascii.a2b_qp(self.cache))
  131. self.cache = b''
  132. # Finalize our underlying stream.
  133. if hasattr(self.underlying, 'finalize'):
  134. self.underlying.finalize()
  135. def __repr__(self):
  136. return "%s(underlying=%r)" % (self.__class__.__name__, self.underlying)