"""Items common to both the client and server
"""
+import array as _array
import re as _re
+import socket as _socket
+from . import LOG as _LOG
from . import error as _error
_ENCODE_PATTERN = '(' + '|'.join(['%', '\r', '\n']) + ')'
_ENCODE_STR_REGEXP = _re.compile(_ENCODE_PATTERN)
_ENCODE_BYTE_REGEXP = _re.compile(_ENCODE_PATTERN.encode('ascii'))
-_DECODE_STR_REGEXP = _re.compile('(%[0-9A-F]{2})')
-_DECODE_BYTE_REGEXP = _re.compile(b'(%[0-9A-F]{2})')
+_DECODE_STR_REGEXP = _re.compile('(%[0-9A-Fa-f]{2})')
+_DECODE_BYTE_REGEXP = _re.compile(b'(%[0-9A-Fa-f]{2})')
_REQUEST_REGEXP = _re.compile('^(\w+)( *)(.*)\Z')
def from_bytes(self, line):
if len(line) > 1000: # TODO: byte-vs-str and newlines?
raise _error.AssuanError(message='Line too long')
- if line.startswith(b'D '):
- self.command = 'D'
- self.parameters = decode(line[2:])
- else:
- line = str(line, encoding='utf-8')
+ line = str(line, encoding='utf-8')
match = _REQUEST_REGEXP.match(line)
if not match:
raise _error.AssuanError(message='Invalid request')
def __bytes__(self):
if self.parameters:
if self.type == 'D':
- return b'{} {}'.format(b'D', self.parameters)
+ return b' '.join((b'D', self.parameters))
else:
return '{} {}'.format(
self.type, encode(self.parameters)).encode('utf-8')
ERR 1 General error
"""
return Response(type='ERR', parameters=str(error))
+
+
+def send_fds(socket, msg=None, fds=None, logger=_LOG):
+ """Send a file descriptor over a Unix socket using ``sendmsg``.
+
+ ``sendmsg`` suport requires Python >= 3.3.
+
+ Code from
+ http://docs.python.org/dev/library/socket.html#socket.socket.sendmsg
+
+ Assuan equivalent is
+ http://www.gnupg.org/documentation/manuals/assuan/Client-code.html#function-assuan_005fsendfd
+ """
+ if msg is None:
+ msg = b''.join(
+ [b'# descriptors in flight: ', str(fds).encode('ascii'), b'\n'])
+ if logger is not None:
+ logger.debug('sending file descriptors {} down {}'.format(fds, socket))
+ return socket.sendmsg(
+ [msg],
+ [(_socket.SOL_SOCKET, _socket.SCM_RIGHTS, _array.array('i', fds))])
+
+def receive_fds(socket, msglen=200, maxfds=10, logger=_LOG):
+ """Recieve file descriptors using ``recvmsg``.
+
+ ``recvmsg`` suport requires Python >= 3.3.
+
+ Code from http://docs.python.org/dev/library/socket.html
+
+ Assuan equivalent is
+ http://www.gnupg.org/documentation/manuals/assuan/Client-code.html#fun_002dassuan_005freceivedfd
+ """
+ fds = _array.array('i') # Array of ints
+ msg,ancdata,flags,addr = socket.recvmsg(
+ msglen, _socket.CMSG_LEN(maxfds * fds.itemsize))
+ for cmsg_level,cmsg_type,cmsg_data in ancdata:
+ if (cmsg_level == _socket.SOL_SOCKET and
+ cmsg_type == _socket.SCM_RIGHTS):
+ # Append data, ignoring any truncated integers at the end.
+ fds.fromstring(
+ cmsg_data[:len(cmsg_data) - (len(cmsg_data) % fds.itemsize)])
+ if logger is not None:
+ logger.debug('receiving file descriptors {} from {} ({})'.format(
+ fds, socket, msg))
+ return (msg, list(fds))