3 import logging as _logging
6 from . import LOG as _LOG
7 from . import common as _common
8 from . import error as _error
11 class AssuanClient (object):
12 """A single-threaded Assuan client based on the `devolpment suggestions`_
14 .. _development suggestions:
15 http://www.gnupg.org/documentation/manuals/assuan/Client-code.html
17 def __init__(self, name, logger=_LOG, use_sublogger=True,
18 close_on_disconnect=False):
21 logger = _logging.getLogger('{}.{}'.format(logger.name, self.name))
23 self.close_on_disconnect = close_on_disconnect
24 self.input = self.output = None
28 self.logger.info('read from stdin')
29 self.input = _sys.stdin
31 self.logger.info('write to stdout')
32 self.output = _sys.stdout
35 if self.close_on_disconnect:
36 self.logger.info('disconnecting')
40 def raise_error(self, error):
41 self.logger.error(str(error))
44 def read_response(self):
45 line = self.input.readline()
48 _error.AssuanError(message='IPC accept call failed'))
49 if not line.endswith('\n'):
51 _error.AssuanError(message='Invalid response'))
52 line = line[:-1] # remove trailing newline
54 response = _common.Response()
56 response.from_string(line)
57 except _error.AssuanError as e:
58 self.logger.error(str(e))
60 self.logger.info('S: {}'.format(response))
63 def _write_request(self, request):
64 rstring = str(request)
65 self.logger.info('C: {}'.format(rstring))
66 self.output.write(rstring)
67 self.output.write('\n')
73 def make_request(self, request, response=True, expect=['OK']):
74 self._write_request(request=request)
76 return self.get_responses(requests=[request], expect=expect)
78 def get_responses(self, requests=None, expect=['OK']):
79 responses = list(self.responses())
80 if responses[-1].type == 'ERR':
81 eresponse = responses[-1]
82 fields = eresponse.parameters.split(' ', 1)
85 message = fields[1].strip()
88 error = _error.AssuanError(code=code, message=message)
89 if requests is not None:
90 error.requests = requests
91 error.responses = responses
94 assert responses[-1].type in expect, [str(r) for r in responses]
96 for response in responses:
97 if response.type == 'D':
98 data.append(response.parameters)
103 return (responses, data)
107 response = self.read_response()
109 if response.type not in ['S', '#', 'D']:
112 def send_data(self, data=None, response=True, expect=['OK']):
113 """Iterate through requests necessary to send ``data`` to a server.
115 http://www.gnupg.org/documentation/manuals/assuan/Client-requests.html
119 encoded_data = _common.encode(data)
121 stop = min(_common.LINE_LENGTH-4, len(encoded_data)) # 'D ', CR, CL
122 self.logger.debug('sending {} bytes of encoded data'.format(
125 d = encoded_data[start:stop]
126 request = _common.Request(
127 command='D', parameters=encoded_data[start:stop],
129 requests.append(request)
130 self.logger.debug('send {} byte chunk'.format(stop-start))
131 self._write_request(request=request)
133 stop = start + min(_common.LINE_LENGTH-4,
134 len(encoded_data) - start)
135 request = _common.Request('END')
136 requests.append(request)
137 self._write_request(request=request)
139 return self.get_responses(requests=requests, expect=expect)