1 # Copyright (C) 2012 W. Trevor King <wking@drexel.edu>
3 # This file is part of pyassuan.
5 # pyassuan is free software: you can redistribute it and/or modify it under the
6 # terms of the GNU General Public License as published by the Free Software
7 # Foundation, either version 3 of the License, or (at your option) any later
10 # pyassuan is distributed in the hope that it will be useful, but WITHOUT ANY
11 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12 # A PARTICULAR PURPOSE. See the GNU General Public License for more details.
14 # You should have received a copy of the GNU General Public License along with
15 # pyassuan. If not, see <http://www.gnu.org/licenses/>.
17 """Items common to both the client and server
22 from . import error as _error
25 LINE_LENGTH = 1002 # 1000 + [CR,]LF
26 _ENCODE_REGEXP = _re.compile(
27 '(' + '|'.join(['%', '\r', '\n']) + ')')
28 _DECODE_REGEXP = _re.compile('(%[0-9A-F]{2})')
29 _REQUEST_REGEXP = _re.compile('^(\w+)( *)(.*)\Z')
35 >>> encode('It grew by 5%!\n')
38 return _ENCODE_REGEXP.sub(
39 lambda x : to_hex(x.group()), string)
44 >>> decode('%22Look out!%22%0AWhere%3F')
47 return _DECODE_REGEXP.sub(
48 lambda x : from_hex(x.group()), string)
58 return chr(int(code[1:], 16))
68 return '%{:02X}'.format(ord(char))
71 class Request (object):
74 http://www.gnupg.org/documentation/manuals/assuan/Client-requests.html
76 >>> r = Request(command='BYE')
79 >>> r = Request(command='OPTION', parameters='testing at 5%')
81 'OPTION testing at 5%25'
82 >>> r.from_string('BYE')
85 >>> print(r.parameters)
87 >>> r.from_string('OPTION testing at 5%25')
90 >>> print(r.parameters)
92 >>> r.from_string(' invalid')
93 Traceback (most recent call last):
95 pyassuan.error.AssuanError: 170 Invalid request
96 >>> r.from_string('in-valid')
97 Traceback (most recent call last):
99 pyassuan.error.AssuanError: 170 Invalid request
101 def __init__(self, command=None, parameters=None, encoded=False):
102 self.command = command
103 self.parameters = parameters
104 self.encoded = encoded
109 encoded_parameters = self.parameters
111 encoded_parameters = encode(self.parameters)
112 return '{} {}'.format(self.command, encoded_parameters)
115 def from_string(self, string):
116 if len(string) > 1000: # TODO: byte-vs-str and newlines?
117 raise _error.AssuanError(message='Line too long')
118 match = _REQUEST_REGEXP.match(string)
120 raise _error.AssuanError(message='Invalid request')
121 self.command = match.group(1)
124 self.parameters = decode(match.group(3))
126 raise _error.AssuanError(message='Invalid request')
128 self.parameters = None
131 class Response (object):
134 http://www.gnupg.org/documentation/manuals/assuan/Server-responses.html
136 >>> r = Response(type='OK')
139 >>> r = Response(type='ERR', parameters='1 General error')
141 'ERR 1 General error'
142 >>> r.from_string('OK')
145 >>> print(r.parameters)
147 >>> r.from_string('ERR 1 General error')
150 >>> print(r.parameters)
152 >>> r.from_string(' invalid')
153 Traceback (most recent call last):
155 pyassuan.error.AssuanError: 76 Invalid response
156 >>> r.from_string('in-valid')
157 Traceback (most recent call last):
159 pyassuan.error.AssuanError: 76 Invalid response
170 def __init__(self, type=None, parameters=None):
172 self.parameters = parameters
176 return '{} {}'.format(self.type, encode(self.parameters))
179 def from_string(self, string):
180 if len(string) > 1000: # TODO: byte-vs-str and newlines?
181 raise _error.AssuanError(message='Line too long')
183 type = self.types[string[0]]
185 raise _error.AssuanError(message='Invalid response')
187 if type == 'D': # data
188 self.parameters = decode(string[2:])
189 elif type == '#': # comment
190 self.parameters = decode(string[2:])
192 match = _REQUEST_REGEXP.match(string)
194 raise _error.AssuanError(message='Invalid request')
197 self.parameters = decode(match.group(3))
199 raise _error.AssuanError(message='Invalid request')
201 self.parameters = None
204 def error_response(error):
207 >>> from pyassuan.error import AssuanError
208 >>> error = AssuanError(1)
209 >>> response = error_response(error)
213 return Response(type='ERR', parameters=str(error))