def _recv_handshake(self, key): pending = '' while True: if pending.find('\r\n\r\n') < 0: data = self.sock.recv(_buffer_size) logger.debug('received %d bytes\n%s', len(data), data) if not data: raise Terminated() pending += data if pending.find('\r\n\r\n') >= 0: data, pending = pending.split('\r\n\r\n', 1) firstline, headers = data.split( '\r\n', 1) if data.find('\r\n') >= 0 else (data, '') protocol, code, reason = firstline.split(' ', 2) if code != '101': raise HTTPError(code + ' ' + reason) headers = MIMEMessage(StringIO(headers)) if headers.get('Upgrade', '').lower() != 'websocket': raise HTTPError('invalid Upgrade header') if headers.get('Connection', '').lower() != 'upgrade': raise HTTPError('invalid Connection header') accept = headers.get('Sec-WebSocket-Accept', '') digest = base64.b64encode( hashlib.sha1(key + _magic).hexdigest().decode('hex')) if accept.lower() != digest.lower(): raise HTTPError('invalid Sec-WebSocket-Accept header') _callit(self, 'onopen', self) break return pending
def anonymise_mime_message(msg: MIMEMessage) -> MIMEMessage: """Replace all of the sensitive fields in the MIME message with messages indicating that this message has been anonymised. The fields that get replaced are "Subject", "Thread-Topic", as well as the body of the email. Attachments are preserved. """ msg.replace_header("Subject", "The subject has been removed for anonymity") # the thread topic isn't always present if msg.get("Thread-Topic") is not None: msg.replace_header("Thread-Topic", "The topic has been removed for anonymity") for part in msg.walk(): # the only parts that contain the body are text/html and text/plain # TODO(sam): I think sometimes these can be encoded as base64? # Might need special handling... if part.get_content_type() == "text/html" \ or part.get_content_type() == "text/plain": if not is_file(part): part.set_payload("The body has been removed for anonymity") return msg
def receive_handshake(msg, verify_handshake=None, userdata=None): index1, index2 = msg.find('\n\n'), msg.find( '\n\r\n') # handle both LFLF and CRLFCRLF if index2 > 0 and index1 > 0: index = (index1 + 2) if index1 < index2 else (index2 + 3) elif index1 > 0: index = index1 + 2 elif index2 > 0: index = index2 + 3 else: logger.debug('no CRLF found') return (None, msg, '') # not enough header data yet # verify if enough data is available for content-length, if any match = re.search(r'content-length\s*:\s*(\d+)\r?\n', msg[:index].lower()) length = int(match.group(1)) if match else 0 if len(msg) < index + length: logger.debug('has more content %d < %d (%d+%d)', len(msg), index + length, index, length) return (None, msg, '') # pending further content. # extract the first HTTP request, and store remaining as pending data, body, msg = msg[:index], msg[index:index + length], msg[index + length:] try: firstline, data = data.split('\n', 1) firstline = firstline.rstrip() headers = MIMEMessage(StringIO(data)) # validate firstline and some headers method, path, protocol = firstline.split(' ', 2) if method != 'GET': raise HTTPError('405 Method Not Allowed') if protocol != "HTTP/1.1": raise HTTPError('505 HTTP Version Not Supported') if headers.get('Upgrade', None) != 'websocket': raise HTTPError('403 Forbidden', 'missing or invalid Upgrade header') if headers.get('Connection', None) != 'Upgrade': raise HTTPError('400 Bad Request', 'missing or invalid Connection header') if 'Sec-WebSocket-Key' not in headers: raise HTTPError('400 Bad Request', 'missing Sec-WebSocket-Key header') if int(headers.get('Sec-WebSocket-Version', '0')) < 13: raise HTTPError('400 Bad Request', 'missing or unsupported Sec-WebSocket-Version') result = None # invoke app below for result if needed if verify_handshake is not None and callable(verify_handshake): try: result = verify_handshake(userdata=userdata, path=path, headers=headers) except HTTPError: raise # re-raise only HTTPError, and mask all others except: logger.exception('exception in server app: verify_handshake') raise HTTPError('500 Server Error', 'exception in server app: verify_handshake') # generate the response, and append result returned by onhandshake if applicable key = headers['Sec-WebSocket-Key'] digest = base64.b64encode( hashlib.sha1(key + _magic).hexdigest().decode('hex')) response = [ 'HTTP/1.1 101 Switching Protocols', 'Upgrade: websocket', 'Connection: Upgrade', 'Sec-WebSocket-Accept: %s' % digest ] if result: response.extend(result) response = '\r\n'.join( response) + '\r\n\r\n' # we always respond with CRLF line ending return (response, msg, path) except HTTPError as e: return (str(e), msg, '') # send error response