def __init__(self, address=None): if address is None: address = bluez_helper.BDADDR_ANY self.address = address self.max_packet_length = 0xffff self.obex_version = OBEX_Version() self.request_handler = requests.RequestHandler()
def __init__(self, address, port): self.address = address self.port = port self.max_packet_length = 0xffff self.obex_version = OBEX_Version() self.response_handler = responses.ResponseHandler() self.socket = None self._external_socket = False self.connection_id = None
def read_data(self, data): # Extract the connection data from the complete data. extra_data = data[self.length(Message.format):self.minimum_length] obex_version, flags, max_packet_length = struct.unpack( ">" + self.format, extra_data) self.obex_version = OBEX_Version().from_byte(obex_version) self.flags = flags self.max_packet_length = max_packet_length Request.read_data(self, data)
def decode_connection(self, socket): code, length, data = self._read_packet(socket) if code == ConnectSuccess.code: message = ConnectSuccess() elif code in self.message_dict: message = self.message_dict[code]() else: return self.UnknownMessageClass(code, length, data) obex_version, flags, max_packet_length = struct.unpack( ">BBH", data[3:7]) message.obex_version = OBEX_Version() message.obex_version.from_byte(obex_version) message.flags = flags message.max_packet_length = max_packet_length message.read_data(data) return message
def __init__(self, address=""): self.address = address self.max_packet_length = 0xffff self.obex_version = OBEX_Version() self.request_handler = requests.RequestHandler()
class Server(object): def __init__(self, address=""): self.address = address self.max_packet_length = 0xffff self.obex_version = OBEX_Version() self.request_handler = requests.RequestHandler() def start_service(self, port, name, uuid, service_classes, service_profiles, provider, description, protocols): socket = BluetoothSocket(RFCOMM) socket.bind((self.address, port)) socket.listen(1) advertise_service(socket, name, uuid, service_classes, service_profiles, provider, description, protocols) print("Starting server for %s on port %i" % socket.getsockname()) #self.serve(socket) return socket def stop_service(self, socket): stop_advertising(socket) def serve(self, socket): while True: connection, address = socket.accept() if not self.accept_connection(*address): connection.close() continue self.connected = True while self.connected: request = self.request_handler.decode(connection) self.process_request(connection, request) def _max_length(self): if hasattr(self, "remote_info"): return self.remote_info.max_packet_length else: return self.max_packet_length def send_response(self, socket, response, header_list=[]): # response encoding will handle making sure we split it # appropriately. we just need to send each chunk for h in header_list: response.add_header(h) chunks = response.encode(self._max_length(), True) while len(chunks) > 1: socket.sendall(chunks.pop(0)) gf_request = self.request_handler.decode(socket) if not isinstance(gf_request, requests.Get_Final): raise IOError( "didn't receive get final request for continuation") socket.sendall(chunks.pop(0)) def _reject(self, socket): self.send_response(socket, responses.Forbidden()) def accept_connection(self, address, port): return True def process_request(self, connection, request): """Processes the request from the connection. This method should be reimplemented in subclasses to add support for more request types. """ #print(request) if isinstance(request, requests.Connect): self.connect(connection, request) elif isinstance(request, requests.Disconnect): self.disconnect(connection, request) elif isinstance(request, requests.Get): self.get(connection, request) elif isinstance(request, requests.Put): self.put(connection, request) elif isinstance(request, requests.Set_Path): self.set_path(connection, request) else: self._reject(connection) def connect(self, socket, request): if request.obex_version > self.obex_version: self._reject(socket) self.remote_info = request max_length = self.remote_info.max_packet_length flags = 0 data = (self.obex_version.to_byte(), flags, max_length) response = responses.ConnectSuccess(data) self.send_response(socket, response) def disconnect(self, socket, request): response = responses.Success() self.send_response(socket, response) self.connected = False def get(self, socket, request): self._reject(socket) def put(self, socket, request): self._reject(socket) def set_path(self, socket, request): self._reject(socket)
class Client(object): """Client client = Client(address, port) Provides common functionality for OBEX clients, including methods for connecting to and disconnecting from a server, sending and receiving headers, and methods for higher level actions such as "get", "put", "set path" and "abort". The address used is a standard six-field bluetooth address, and the port should correspond to the port providing the service you wish to access. """ def __init__(self, address, port): self.address = address self.port = port self.max_packet_length = 0xffff self.obex_version = OBEX_Version() self.response_handler = responses.ResponseHandler() self.socket = None self._external_socket = False self.connection_id = None def _send_headers(self, request, header_list, max_length): """Convenience method to add headers to a request and send one or more requests with those headers.""" # Ensure that any Connection ID information is sent first. if self.connection_id: header_list.insert(0, self.connection_id) while header_list: if request.add_header(header_list[0], max_length): header_list.pop(0) else: self.socket.sendall(request.encode()) if isinstance(request, requests.Connect): response = self.response_handler.decode_connection( self.socket) else: response = self.response_handler.decode(self.socket) if not isinstance(response, responses.Continue): return response request.reset_headers() # Always send at least one request. if isinstance(request, requests.Get): # Turn the last Get request containing the headers into a # Get_Final request. request.code = requests.Get_Final.code self.socket.sendall(request.encode()) if isinstance(request, requests.Connect): response = self.response_handler.decode_connection(self.socket) else: response = self.response_handler.decode(self.socket) return response def _collect_parts(self, header_list): body = [] new_headers = [] for header in header_list: if isinstance(header, headers.Body): body.append(header.data) elif isinstance(header, headers.End_Of_Body): body.append(header.data) else: new_headers.append(header) return new_headers, b"".join(body) def set_socket(self, socket): """set_socket(self, socket) Sets the socket to be used for communication to the socket specified. If socket is None, the client will create a socket for internal use when a connection is made. This is the default behaviour. This method must not be called once a connection has been opened. Only after an existing connection has been disconnected is it safe to set a new socket. """ self.socket = socket if socket is None: self._external_socket = False else: self._external_socket = True def connect(self, header_list=()): """connect(self, header_list = ()) Sends a connection message to the server. Raises an exception with an error response if it fails. Typically, the response is either Success or a subclass of FailureResponse. Specific headers can be sent by passing a sequence as the header_list keyword argument. """ if not self._external_socket: self.socket = BluetoothSocket() self.socket.connect((self.address, self.port)) flags = 0 data = (self.obex_version.to_byte(), flags, self.max_packet_length) max_length = self.max_packet_length request = requests.Connect(data) header_list = list(header_list) response = self._send_headers(request, header_list, max_length) if isinstance(response, responses.ConnectSuccess): self.remote_info = response for header in response.header_data: if isinstance(header, headers.Connection_ID): # Recycle the Connection ID data to create a new header # for future use. self.connection_id = headers.Connection_ID(header.decode()) elif not self._external_socket: self.socket.close() if not isinstance(response, responses.ConnectSuccess): raise OBEXError(response) def disconnect(self, header_list=()): """disconnect(self, header_list = ()) Sends a disconnection message to the server. Raises an exception with the response if it fails. Typically, the response is either Success or a subclass of FailureResponse. Specific headers can be sent by passing a sequence as the header_list keyword argument. If the socket was not supplied using set_socket(), it will be closed. """ max_length = self.remote_info.max_packet_length request = requests.Disconnect() header_list = list(header_list) response = self._send_headers(request, header_list, max_length) if not self._external_socket: self.socket.close() self.connection_id = None if not isinstance(response, responses.Success): raise OBEXError(response) def put(self, name, file_data, header_list=()): """put(self, name, file_data, header_list = ()) Performs an OBEX PUT request to send a file with the given name, containing the file_data specified, to the server for storage in the current directory for the session. Additional headers can be sent by passing a sequence as the header_list keyword argument. These will be sent after the name and file length information associated with the name and file_data supplied. This function does not return anything. If a failure response is received, an OBEXError will be raised with the error response as its argument. """ for response in self._put(name, file_data, header_list): if isinstance(response, responses.Continue) or \ isinstance(response, responses.Success): continue else: raise OBEXError(response) def _put(self, name, file_data, header_list=()): header_list = [headers.Name(name), headers.Length(len(file_data))] + list(header_list) max_length = self.remote_info.max_packet_length request = requests.Put() response = self._send_headers(request, header_list, max_length) yield response if not isinstance(response, responses.Continue): return # Send the file data. # The optimum size is the maximum packet length accepted by the # remote device minus three bytes for the header ID and length # minus three bytes for the request. optimum_size = max_length - 3 - 3 i = 0 while i < len(file_data): data = file_data[i:i + optimum_size] i += len(data) if i < len(file_data): request = requests.Put() request.add_header(headers.Body(data, False), max_length) self.socket.sendall(request.encode()) response = self.response_handler.decode(self.socket) yield response if not isinstance(response, responses.Continue): return else: request = requests.Put_Final() request.add_header(headers.End_Of_Body(data, False), max_length) self.socket.sendall(request.encode()) response = self.response_handler.decode(self.socket) yield response if not isinstance(response, responses.Success): return def get(self, name=None, header_list=()): """get(self, name = None, header_list = (), callback = None) Performs an OBEX GET request to retrieve a file with the given name from the server's current directory for the session. Additional headers can be sent by passing a sequence as the header_list keyword argument. These will be sent after the name information. This method returns a tuple of the form (resp_header_list, body) where resp_header_list is a list of all non-body response headers, and body is a reconstructed byte string of the response body. """ returned_headers = [] for response in self._get(name, header_list): if isinstance(response, responses.Continue) or \ isinstance(response, responses.Success): # collect responses for processing at end returned_headers += response.header_data else: # Raise an exception for the failure raise OBEXError(response) # Finally, return the collected responses return self._collect_parts(returned_headers) def _get(self, name=None, header_list=()): header_list = list(header_list) if name is not None: header_list = [headers.Name(name)] + header_list max_length = self.remote_info.max_packet_length request = requests.Get() response = self._send_headers(request, header_list, max_length) yield response if not (isinstance(response, responses.Continue) or isinstance(response, responses.Success)): return # Retrieve the file data. file_data = [] request = requests.Get_Final() while isinstance(response, responses.Continue): self.socket.sendall(request.encode()) response = self.response_handler.decode(self.socket) yield response def setpath(self, name="", create_dir=False, to_parent=False, header_list=()): """setpath(self, name = "", create_dir = False, to_parent = False, header_list = ()) Requests a change to the server's current directory for the session to the directory with the specified name. Raises an exception with the response if the response was unexpected. This method is also used to perform other actions, such as navigating to the parent directory (set to_parent to True) and creating a new directory (set create_dir to True). Additional headers can be sent by passing a sequence as the header_list keyword argument. These will be sent after the name information. """ header_list = list(header_list) if name is not None: header_list = [headers.Name(name)] + header_list max_length = self.remote_info.max_packet_length flags = 0 if not create_dir: flags |= requests.Set_Path.DontCreateDir if to_parent: flags |= requests.Set_Path.NavigateToParent request = requests.Set_Path((flags, 0)) response = self._send_headers(request, header_list, max_length) if not isinstance(response, responses.Success): raise OBEXError(response) def delete(self, name, header_list=()): """delete(self, name, header_list = ()) Requests the deletion of the file with the specified name from the current directory. Raises an OBEXError with the response if there is an error. """ header_list = [headers.Name(name)] + list(header_list) max_length = self.remote_info.max_packet_length request = requests.Put_Final() response = self._send_headers(request, header_list, max_length) if not isinstance(response, responses.Success): raise OBEXError(response) def abort(self, header_list=()): """abort(self, header_list = ()) Aborts the current session. Raises an OBEXError with the response if there is an error. Specific headers can be sent by passing a sequence as the header_list keyword argument. Warning: This method should only be called to terminate a running operation and is therefore only useful for developers who want to reimplementing existing operations. """ header_list = list(header_list) max_length = self.remote_info.max_packet_length request = requests.Abort() response = self._send_headers(request, header_list, max_length) if not isinstance(response, responses.Success): raise OBEXError(response) def listdir(self, name="", xml=False): """listdir(self, name = "") Requests information about the contents of the directory with the specified name relative to the current directory for the session. If the name is omitted or an empty string is supplied, the contents of the current directory are typically listed by the server. If successful, the server will provide an XML folder listing. If the xml argument is true, the XML listing will be returned directly. Else, this function will parse the XML and return a tuple of two lists, the first list being the folder names, and the second list being file names. """ hdrs, data = self.get( name, header_list=[headers.Type(b"x-obex/folder-listing", False)]) if xml: return data tree = parse_xml(data) folders = [] files = [] for e in tree: if e.tag == "folder": folders.append(e.attrib["name"]) elif e.tag == "file": files.append(e.attrib["name"]) elif e.tag == "parent-folder": pass # ignore it else: sys.stderr.write("Unknown listing element %s\n" % e.tag) return folders, files