Guest User

Untitled

a guest
May 24th, 2018
81
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 6.38 KB | None | 0 0
  1. #!/usr/bin/env python3
  2. # Copyright (c) 2018 softhyena.com
  3. #
  4. # Permission is hereby granted, free of charge, to any person obtaining a copy
  5. # of this software and associated documentation files (the "Software"), to deal
  6. # in the Software without restriction, including without limitation the rights
  7. # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. # copies of the Software, and to permit persons to whom the Software is
  9. # furnished to do so, subject to the following conditions:
  10. #
  11. # The above copyright notice and this permission notice shall be included in all
  12. # copies or substantial portions of the Software.
  13. #
  14. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  20. # SOFTWARE.
  21.  
  22. import typing
  23.  
  24. #Can this be made into a stack based encoder? --Félix
  25. def encode(input: typing.Any) -> bytes:
  26. """Encode a object to a bencode byte string"""
  27. t = type(input)
  28. if t == str:
  29. input = input.encode()
  30. return "{}:".format(len(input)).encode()+input
  31.  
  32. elif t == bytes:
  33. return "{}:".format(len(input)).encode()+input
  34.  
  35. elif t == int:
  36. return "i{}e".format(input).encode()
  37.  
  38. elif t == list:
  39. output = b"l"
  40. for item in input:
  41. output = output + encode(item)
  42.  
  43. return output+b"e"
  44.  
  45. elif t == dict:
  46. output = b"d"
  47. for k,v in input.items():
  48. output = output + encode(k) + encode(v)
  49.  
  50. return output+b"e"
  51.  
  52. else:
  53. raise TypeError("Bencode cannot encode type {}".format(str(t)))
  54.  
  55. def decode(input: bytes, tryDecodeStrings: bool = False, disableStringKeys: bool = False) -> typing.Any:
  56. """Decode a bencode byte string"""
  57. stack = []
  58.  
  59. offset = 0
  60. length = len(input)
  61. while True:
  62. try:
  63. data = None
  64.  
  65. #Parse strings
  66. if "0" <= chr(input[offset]) <= "9":
  67. tmp = chr(input[offset])
  68. while True:
  69. offset = offset + 1
  70. if "0" <= chr(input[offset]) <= "9":
  71. tmp = tmp + chr(input[offset])
  72. else:
  73. break
  74.  
  75. if chr(input[offset]) != ":":
  76. raise ValueError("Unexpected {} in bencode string decoding at {}".format(chr(input[offset]), offset))
  77.  
  78. offset = offset + 1
  79. data = input[offset:offset+int(tmp)]
  80. offset = offset + int(tmp)
  81.  
  82. if tryDecodeStrings:
  83. #Try to decode strings
  84. try:
  85. data = data.decode()
  86. except UnicodeDecodeError:
  87. pass
  88.  
  89.  
  90. #Parse integers
  91. elif chr(input[offset]) == "i":
  92. offset = offset + 1
  93.  
  94. if "0" <= chr(input[offset]) <= "9":
  95. tmp = ""
  96. while True:
  97. if chr(input[offset]) == "e":
  98. offset = offset + 1
  99. break
  100. elif "0" <= chr(input[offset]) <= "9":
  101. tmp = tmp + chr(input[offset])
  102. offset = offset + 1
  103. else:
  104. raise ValueError("Unexpected {} in bencode integer decoding at {}".format(chr(input[offset]), offset))
  105.  
  106. else:
  107. raise ValueError("Unexpected {} in bencode integer decoding at {}".format(chr(input[offset]), offset))
  108.  
  109. data = int(tmp)
  110.  
  111. #Setup lists
  112. elif chr(input[offset]) == "l":
  113. stack.append([])
  114. offset = offset + 1
  115.  
  116. #Setup dictionary
  117. elif chr(input[offset]) == "d":
  118. stack.append({})
  119. stack.append(()) #Push a KV marker
  120. offset = offset + 1
  121.  
  122. #Process stack lists or dictionaries
  123. elif chr(input[offset]) == "e":
  124. data = stack.pop()
  125. #KV should have been appended
  126. if type(data) == tuple:
  127. if len(data):
  128. raise ValueError("Stack unbalanced! Unexpected end of dict at {}!".format(offset))
  129. else:
  130. data = stack.pop()
  131.  
  132. offset = offset + 1
  133.  
  134. #Push data into lists or dictionaries
  135. if data:
  136. if len(stack) > 0:
  137. destType = type(stack[-1])
  138. if destType == list:
  139. stack[-1].append(data)
  140. elif destType == tuple:
  141. stack[-1] = tuple(list(stack[-1]) + [data])
  142. l = len(stack[-1])
  143. if l == 2:
  144. kv = stack.pop()
  145.  
  146. if type(kv[0]) == bytes and not disableStringKeys:
  147. try:
  148. stack[-1][kv[0].decode()] = kv[1]
  149. except UnicodeDecodeError:
  150. stack[-1][kv[0]] = kv[1]
  151. else:
  152. stack[-1][kv[0]] = kv[1]
  153.  
  154. stack.append(()) #Push the a new KV marker
  155.  
  156. else:
  157. raise ValueError("Stack unbalanced! Unexpected {} at {}!".format(type(stack[-1]), offset))
  158. else:
  159. return data
  160.  
  161. except IndexError as e:
  162. raise ValueError("Unexpected end of string at {}!".format(offset))
  163.  
  164. #Aliases
  165. loads = decode
  166. dumps = encode
  167.  
  168. def load(file, *args, **kwargs):
  169. return decode(file.read(), *args, **kwargs)
  170.  
  171. def dump(input, file):
  172. file.write(encode(file.read(), *args, **kwargs))
Add Comment
Please, Sign In to add comment