def do_POST(self): """Called when a new event has been received""" # make sure this is what we expect if self.path != '/event': raise RuntimeError( 'Unexpected path when parsing event: {0}'.format(self.path)) # validate our content type content_type = self.headers.get('content-type', None) if content_type != 'text/x-apple-plist+xml': raise RuntimeError( 'Unexpected Content-Type when parsing event: {0}'.format( content_type)) # and the body length content_length = int(self.headers.get('content-length', 0)) if content_length == 0: raise RuntimeError('Received an event with a zero length body.') # parse XML plist xml = self.rfile.read(content_length) if len(xml) < content_length: # sometimes the content is not complete... self.event = plist_loads(xml + "</dict></plist>") else: self.event = plist_loads(xml)
def decoded_content(self): if self._next is not None: return self._next.decoded_content() self.check_content_type() if self.content_type is None or self.content_type in ('text/plain', 'text/html'): if self.charset is not None: return self._buffer.decode(self.charset) return self._buffer.decode() try: if self.content_type in ['text/x-apple-plist+xml', 'application/x-apple-binary-plist']: return plist_loads(self._buffer) elif self.content_type == 'text/parameters': dd = {} for line in self._buffer.split(b'\n'): if line == b'': continue k, v = line.split(b':', 1) dd[k] = v.strip() return dd elif self.content_type == 'application/json': return json.loads(self._buffer.decode()) elif self.content_type == 'multipart/byteranges': boundary = self.charset.split('=', 1)[1] start = self._buffer.find(b'--') if start == -1: return [] parts = self._buffer[start:].strip().split("--{}".format(boundary).encode()) return [self.parse_range_multipart(p) for p in parts if len(p) > 2] except: return self._buffer
def decoded_content(self): if self._next is not None: return self._next.decoded_content() self.check_content_type() if self.content_type is None or self.content_type in ('text/plain', 'text/html'): if self.charset is not None: return self._buffer.decode(self.charset) return self._buffer.decode() try: if self.content_type in ['text/x-apple-plist+xml', 'application/x-apple-binary-plist']: return plist_loads(self._buffer) elif self.content_type == 'text/parameters': dd = {} for line in self._buffer.split(b'\n'): if line == b'': continue k, v = line.split(b':', 1) dd[k] = v.strip() return dd elif self.content_type == 'application/json': return json.loads(self._buffer.decode()) elif self.content_type == 'application/xml': return etree.fromstring(self._buffer.decode()) elif self.content_type == 'multipart/byteranges': boundary = self.charset.split('=', 1)[1] start = self._buffer.find(b'--') if start == -1: return [] parts = self._buffer[start:].strip().split("--{}".format(boundary).encode()) return [self.parse_range_multipart(p) for p in parts if len(p) > 2] except: return self._buffer
def do_POST(self): """Called when a new event has been received""" # make sure this is what we expect if self.path != '/event': raise RuntimeError('Unexpected path when parsing event: {0}'.format(self.path)) # validate our content type content_type = self.headers.get('content-type', None) if content_type != 'text/x-apple-plist+xml': raise RuntimeError('Unexpected Content-Type when parsing event: {0}'.format(content_type)) # and the body length content_length = int(self.headers.get('content-length', 0)) if content_length == 0: raise RuntimeError('Received an event with a zero length body.') # parse XML plist self.event = plist_loads(self.rfile.read(content_length))
def sample_airportscan(): proc = Popen(['/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport', '-xs'], stdout=PIPE, stderr=PIPE) out, err = proc.communicate() if proc.returncode: return if not out: return networks = [] for data in plist_loads(out): networks.append({ 'ssid': data['SSID_STR'], 'bssid': data['BSSID'], 'rssi': data['RSSI'], }) return {'networks': networks}
def _command(self, uri, method='GET', body='', **kwargs): """Makes an HTTP request through to an AirPlay server Args: uri(string): The URI to request method(string): The HTTP verb to use when requesting `uri`, defaults to GET body(string): If provided, will be sent witout alteration as the request body. Content-Length header will be set to len(`body`) **kwargs: If provided, Will be converted to a query string and appended to `uri` Returns: True: Request returned 200 OK, with no response body False: Request returned something other than 200 OK, with no response body Mixed: The body of the HTTP response """ # generate the request if len(kwargs): uri = uri + '?' + urlencode(kwargs) request = method + " " + uri + " HTTP/1.1\r\nContent-Length: " + str( len(body)) + "\r\n\r\n" + body try: request = bytes(request, 'UTF-8') except TypeError: pass # send it self.control_socket.send(request) # parse our response result = self.control_socket.recv(self.RECV_SIZE) resp = HTTPResponse(FakeSocket(result)) resp.begin() # if our content length is zero, then return bool based on result code if int(resp.getheader('content-length', 0)) == 0: if resp.status == 200: return True else: return False # else, parse based on provided content-type # and return the response body content_type = resp.getheader('content-type') if content_type is None: raise RuntimeError('Response returned without a content type!') if content_type == 'text/parameters': body = resp.read() try: body = str(body, 'UTF-8') except TypeError: pass return email.message_from_string(body) if content_type == 'text/x-apple-plist+xml': return plist_loads(resp.read()) raise RuntimeError( 'Response received with unknown content-type: {0}'.format( content_type))
def _command(self, uri, method='GET', body='', **kwargs): """Makes an HTTP request through to an AirPlay server Args: uri(string): The URI to request method(string): The HTTP verb to use when requesting `uri`, defaults to GET body(string): If provided, will be sent witout alteration as the request body. Content-Length header will be set to len(`body`) **kwargs: If provided, Will be converted to a query string and appended to `uri` Returns: True: Request returned 200 OK, with no response body False: Request returned something other than 200 OK, with no response body Mixed: The body of the HTTP response """ # generate the request if len(kwargs): uri = uri + '?' + urlencode(kwargs) request = method + " " + uri + " HTTP/1.1\r\nContent-Length: " + str(len(body)) + "\r\n\r\n" + body try: request = bytes(request, 'UTF-8') except TypeError: pass # send it self.control_socket.send(request) # parse our response result = self.control_socket.recv(self.RECV_SIZE) resp = HTTPResponse(FakeSocket(result)) resp.begin() # if our content length is zero, then return bool based on result code if int(resp.getheader('content-length', 0)) == 0: if resp.status == 200: return True else: return False # else, parse based on provided content-type # and return the response body content_type = resp.getheader('content-type') if content_type is None: raise RuntimeError('Response returned without a content type!') if content_type == 'text/parameters': body = resp.read() try: body = str(body, 'UTF-8') except TypeError: pass return email.message_from_string(body) if content_type == 'text/x-apple-plist+xml': return plist_loads(resp.read()) raise RuntimeError('Response received with unknown content-type: {0}'.format(content_type))
def parse_output(out): if not out: return data = plist_loads(out)[0] return dict((k, data[k]) for k in KEYS)