source file: p10/parser.py
file stats: 126 lines, 125 executed: 99.2% covered
1. #!/usr/bin/env python
2.
3. import base64
4.
5. class parser:
6. """ Takes a raw line from the connection, tokenises it and then passes it on to an appropriate handler """
7.
8. _handlers = dict()
9. _maxclientnum = 0
10.
11. def __init__(self, maxclientnum):
12. self._handlers = dict()
13. self._maxclientnum = maxclientnum
14.
15. def registerHandler(self, token, handler):
16. """ Add a new handler for a particular token """
17. self._handlers[token] = handler
18.
19. def _passToHandler(self, origin, token, args):
20. """ Pass something parsed to the appropriate handler """
21. try:
22. self._handlers[token].handle(origin, args)
23. except KeyError:
24. raise ParseError("Unknown command", None)
25.
26. def _checkLineIsGood(self, string):
27. """ Check the raw line meets our various standards, tidy it up and return it """
28. # The standard requires we only accept strings ending in \r\n or \n
29. if (string[-1] != "\n"):
30. raise ParseError('Line endings were not as expected', string)
31.
32. # The standard places a limit on line lengths
33. if (len(string)) > 512:
34. raise ProtocolError('Line too long to be valid', string)
35.
36. # Trim our trailing whitespace/line endings
37. return string.rstrip()
38.
39. def _parseParams(self, params):
40. """ Further break up our body into individual parameters """
41. if params[0] == ":":
42. params = [params[1:]]
43. else:
44. params = params.split(" :", 1)
45. if len(params) == 1:
46. last_arg = None
47. else:
48. last_arg = params[1]
49. params = params[0].split(None)
50. if last_arg != None:
51. params.append(last_arg)
52. return params
53.
54. def parsePreAuth(self, string, origin):
55. """ Parse strings before authentication is established """
56. # Tidy up our line
57. string = self._checkLineIsGood(string)
58.
59. # Break up into token and body
60. high_level_parts = string.split(None, 1)
61. command = high_level_parts[0]
62. if not command.isupper() and not command.isdigit():
63. raise ProtocolError('Command not in uppercase', string)
64. params = self._parseParams(high_level_parts[1])
65.
66. # If this is an invalid command, pass it upwards
67. try:
68. self._passToHandler(origin, command, params)
69. except ParseError, error:
70. raise ParseError(error.value, string)
71.
72. def parse(self, string):
73. """ Take a string, parse it into our internal form and pass it on """
74. # Tidy up our line
75. string = self._checkLineIsGood(string)
76.
77. # Break up into origin, token and body
78. high_level_parts = string.split(None, 2)
79. origin = base64.parseNumeric(high_level_parts[0], self._maxclientnum)
80. command = high_level_parts[1]
81. if not command.isupper() and not command.isdigit():
82. raise ProtocolError('Command not in uppercase', string)
83. if len(high_level_parts) > 2:
84. params = self._parseParams(high_level_parts[2])
85. else:
86. params = []
87.
88. # If this is an invalid command, pass it upwards
89. try:
90. self._passToHandler(origin, command, params)
91. except ParseError, error:
92. raise ParseError(error.value, string)
93.
94. def build(self, origin, token, args):
95. """ Build a string suitable for sending """
96. # If the last argument is "long", package it for sending
97. if len(args) > 0:
98. if args[-1].find(" ") > -1:
99. build_last_arg = ":" + args[-1]
100. build_args = args[0:-1] + build_last_arg.split(" ")
101. else:
102. build_args = args
103. else:
104. build_args = []
105. # Build the line
106. # Future compatibility - only send \n
107. ret = base64.createNumeric(origin) + " " + token + " " + " ".join(build_args) + "\n"
108.
109. # Check we're not sending things which are protocol violations
110. if len(ret) > 512:
111. raise ProtocolError('Line too long to send')
112. if not token.isupper() and not token.isdigit():
113. raise ProtocolError('Command not in uppercase during build')
114.
115. return ret
116.
117. class ParseError(Exception):
118. """ An exception thrown if a line can not be parsed """
119.
120. line = ""
121.
122. def __init__(self, value, line):
123. self.value = value
124. self.line = line
125.
126. def __str__(self):
127. return repr(self.value) + " on line " + self.line
128.
129. class ProtocolError(Exception):
130. """ An exception if a line is a protocol violation """
131. pass