source file: p10/base64.py
file stats: 149 lines, 148 executed: 99.3% covered
1. #!/usr/bin/env python
2.
3. """ A mapping of base 64 characters to the relevant values """
4. _map = {"A": 0,
5. "B": 1,
6. "C": 2,
7. "D": 3,
8. "E": 4,
9. "F": 5,
10. "G": 6,
11. "H": 7,
12. "I": 8,
13. "J": 9,
14. "K": 10,
15. "L": 11,
16. "M": 12,
17. "N": 13,
18. "O": 14,
19. "P": 15,
20. "Q": 16,
21. "R": 17,
22. "S": 18,
23. "T": 19,
24. "U": 20,
25. "V": 21,
26. "W": 22,
27. "X": 23,
28. "Y": 24,
29. "Z": 25,
30. "a": 26,
31. "b": 27,
32. "c": 28,
33. "d": 29,
34. "e": 30,
35. "f": 31,
36. "g": 32,
37. "h": 33,
38. "i": 34,
39. "j": 35,
40. "k": 36,
41. "l": 37,
42. "m": 38,
43. "n": 39,
44. "o": 40,
45. "p": 41,
46. "q": 42,
47. "r": 43,
48. "s": 44,
49. "t": 45,
50. "u": 46,
51. "v": 47,
52. "w": 48,
53. "x": 49,
54. "y": 50,
55. "z": 51,
56. "0": 52,
57. "1": 53,
58. "2": 54,
59. "3": 55,
60. "4": 56,
61. "5": 57,
62. "6": 58,
63. "7": 59,
64. "8": 60,
65. "9": 61,
66. "[": 62,
67. "]": 63}
68.
69. _revmap = dict(zip(_map.values(), _map.keys()))
70.
71. def toInt(chars):
72. """ Convert a base 64 string to its appropriate base-10 integer """
73. accum = 0
74. chars = list(chars)
75. chars.reverse()
76. chars = ''.join(chars)
77. power = 0
78. for char in chars:
79. try:
80. accum = accum + (_map[char] * (64 ** power))
81. except KeyError:
82. raise Base64Error('Invalid base64 character encountered', chars)
83. power = power + 1
84. return accum
85.
86. def toBase64(num, pad):
87. """ Convert a base-10 integer to a base-64 string """
88. # Build a list of all the appropriate characters
89. parts = list()
90. power = 0
91. while num > (64 ** power):
92. power = power + 1
93. while power >= 0:
94. part = num / (64 ** power)
95. parts.append(_revmap[part])
96. num = num - (part * (64 ** power))
97. power = power - 1
98. parts = ''.join(parts)
99. if len(parts) > 1:
100. # Trim the valueless characters off the front, apart from if the value is a literal 0
101. parts = parts.lstrip('A')
102.
103. # Do padding
104. while len(parts) < pad:
105. parts = 'A' + parts
106.
107. return parts
108.
109. def parseNumeric(numeric, maxclient):
110. """ Take a numeric and return a tuple of integers, the first representing the server numeric, the second the client numeric.
111. If the numeric is server-only, then the second element in the pair is set to None
112. The maxclient is used to return unique numerics """
113.
114. # Short and extended server only numerics
115. if len(numeric) == 1 or len(numeric) == 2:
116. return (toInt(numeric), None)
117. # Short server/client numerics
118. elif len(numeric) == 3:
119. server = toInt(numeric[0])
120. return (server, toInt(numeric[1:3]) & maxclient[server])
121. # Universal IRCU server/client numerics
122. elif len(numeric) == 4:
123. server = toInt(numeric[0])
124. return (server, toInt(numeric[1:4]) & maxclient[server])
125. # Extended server/client numerics
126. elif len(numeric) == 5:
127. server = toInt(numeric[0:2])
128. return (server, toInt(numeric[2:5]) & maxclient[server])
129. else:
130. raise Base64Error("Bad length for numeric", numeric)
131.
132. def createNumeric((server, client)):
133. """ Create a numeric from a pair of integers - with the first representing the server numeric, the second the client.
134. This only generates extended (5 character) numerics for maximum compatibility """
135.
136. # Generate the server half
137. servernum = toBase64(server, 2)
138.
139. # Handle server-only numerics
140. clientnum = ""
141. if client != None:
142. # Generate client half
143. clientnum = toBase64(client, 3)
144.
145. return servernum + clientnum
146.
147. class Base64Error(Exception):
148. """ An exception that is raised if there is an error generating or parsing the base 64 """
149.
150. numeric = ""
151.
152. def __init__(self, value, numeric):
153. self.value = value
154. self.numeric = numeric
155.
156. def __str__(self):
157. return repr(self.value) + " with numeric " + self.numeric