Esempio n. 1
0
 def __init__(self, address=""):
     self.address = address
     self.max_packet_length = 0xffff
     self.obex_version = OBEX_Version()
     self.request_handler = requests.RequestHandler()
     self.connected = False
     self.remote_info = None
Esempio n. 2
0
    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 __init__(self, callback=None, err_callback=None, loop=None):
	
		self.max_packet_length = 0xffff
		self.obex_version = OBEX_Version()
		self.__callback=callback
		self.__err_callback=err_callback
		self.loop = loop

		# internal state variables
		self.state = common.IDLE
		self.state_put = common.PUT_IDLE
		
		# internal holders
		self.pending_headers = []
		self.response_handler = responses.ResponseHandler()
		self.length = 0
		self.type = None
		self.invalid = False
		
		# a state flag that allow us to know if we have to send more
		# headers as soon as we get a reply		
		self.sending_headers = False
Esempio n. 4
0
    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)
Esempio n. 5
0
    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
Esempio n. 6
0
class Client:
    """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 and returns its response.
        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 = Socket()

        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()

        return response

    def disconnect(self, header_list=()):
        """disconnect(self, header_list = ())
        
        Sends a disconnection message to the server and returns its response.
        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

        return response

    def put(self, name, file_data, header_list=(), callback=None):
        """put(self, name, file_data, header_list = (), callback = None)
        
        Sends a file with the given name, containing the file_data specified,
        to the server for storage in the current directory for the session.
        
        If a callback is specified, it will be called with each response
        obtained during the put operation. If no callback is specified, the
        final response is returned when the put operation is complete or an
        error occurs.
        
        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.
        """

        for response in self._put(name, file_data, header_list):

            if isinstance(response, responses.Continue) or \
                isinstance(response, responses.Success):

                # Report successful responses if using a callback.
                if callback:
                    callback(response)

            elif callback:
                # Report failure responses using the callback, then return.
                callback(response)
                return
            else:
                # Return failure responses directly.
                return response

        # Finally, return the last response if not using a callback.
        if not callback:
            return 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=(), callback=None):
        """get(self, name = None, header_list = (), callback = None)
        
        Requests the specified file from the server's current directory for
        the session.
        
        If a callback is specified, it will be called with each response
        obtained during the get operation. If no callback is specified, a value
        is returned which depends on the success of the operation.
        
        For an operation without callback, if successful, this method returns a
        tuple containing a list of responses received during the operation and
        the file data received; if unsuccessful, a single response object is
        returned.
        
        Additional headers can be sent by passing a sequence as the
        header_list keyword argument. These will be sent after the name
        information.
        """

        returned_headers = []

        for response in self._get(name, header_list):
            if isinstance(response, responses.Continue) or \
                isinstance(response, responses.Success):

                # Report successful responses if using a callback or collect
                # them for later.
                if callback:
                    callback(response)
                else:
                    returned_headers += response.header_data

            elif callback:
                # Report failure responses using the callback, then return.
                callback(response)
                return
            else:
                # Return failure responses directly.
                return response

        # Finally, return the collected responses if not using a callback.
        if not callback:
            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) and \
            not 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, and returns the response.
        
        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)
        return 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 and returns the server's response.
        """

        header_list = [headers.Name(name)] + list(header_list)

        max_length = self.remote_info.max_packet_length
        request = requests.Put_Final()

        return self._send_headers(request, header_list, max_length)

    def abort(self, header_list=()):
        """abort(self, header_list = ())
        
        Aborts the current session and returns the server's response.
        
        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)
        return response
Esempio n. 7
0
class Server:
    def __init__(self, address=""):
        self.address = address
        self.max_packet_length = 0xffff
        self.obex_version = OBEX_Version()
        self.request_handler = requests.RequestHandler()
        self.connected = False
        self.remote_info = None

    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

    @staticmethod
    def stop_service(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, *address)

    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=None):
        # TODO: This needs to be able to split messages that are longer than
        # the maximum message length agreed with the other party.
        if header_list is None:
            header_list = []

        while header_list:
            if response.add_header(header_list[0], self._max_length()):
                header_list.pop(0)
            else:
                socket.sendall(response.encode())
                response.reset_headers()

        # Always send at least one request.
        socket.sendall(response.encode())

    def _reject(self, socket):
        self.send_response(socket, responses.Forbidden())

    # noinspection PyUnusedLocal
    @staticmethod
    def accept_connection(address, port):
        return True

    def process_request(self, connection, request, *address):
        """Processes the request from the connection.

        This method should be reimplemented in subclasses to add support for
        more request types.
        """

        if isinstance(request, requests.Connect):
            self.connect(connection, request)

        elif isinstance(request, requests.Disconnect):
            self.disconnect(connection)

        elif isinstance(request, requests.Put):
            self.put(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):
        response = responses.Success()
        self.send_response(socket, response)
        self.connected = False

    def put(self, socket, request):
        self._reject(socket)
class Client(object):
	def __init__(self, callback=None, err_callback=None, loop=None):
	
		self.max_packet_length = 0xffff
		self.obex_version = OBEX_Version()
		self.__callback=callback
		self.__err_callback=err_callback
		self.loop = loop

		# internal state variables
		self.state = common.IDLE
		self.state_put = common.PUT_IDLE
		
		# internal holders
		self.pending_headers = []
		self.response_handler = responses.ResponseHandler()
		self.length = 0
		self.type = None
		self.invalid = False
		
		# a state flag that allow us to know if we have to send more
		# headers as soon as we get a reply		
		self.sending_headers = False
		
	def cleanup(self):
	    self.socket.remove_callbacks()
	    self.socket.close()
	
	def callback(self, *args, **kwargs):
		if self.invalid:
			return
		print "callback", args, kwargs
		if callable(self.__callback):
			self.__callback(self, *args, **kwargs)
	
	def err_callback(self, *args, **kwargs):
		print "err_callback", args, kwargs
		self.invalid = True
		if callable(self.__err_callback):
			self.__err_callback(self, *args, **kwargs)
			
	def internal_callback(self, *args, **kwargs):
		if self.state != common.PUT:
			return self.callback(*args, **kwargs)
			
		if self.state_put == common.PUT_HEADER:
			return self.put_headers_done(*args, **kwargs)
		
		if self.state_put == common.PUT_BODY:
			return self.put_body_done(*args, **kwargs)
			
		if self.state_put == common.PUT_FINAL:
			return self.put_done(*args, **kwargs)
			
		raise Exception("Invalid state %s %s" % (
												common.STATES[self.state],
												common.STATES_PUT[self.state_put]
						))
	
	def data_ready(self, amount):
		print "data_ready", amount
		if self.length == 0:
			if amount < 3:
				return
		
			format = ">BH"
			data = self.socket.recv(3)
			amount=amount-3
			type, length = struct.unpack(format, data)
		else:
			type = self.type
			length = self.length
		
		if length-3>amount:
			self.type = type
			self.length = length
			self.data = data
			print "pending data", length
			return
		data += self.socket.recv(length - 3)
		self.type = 0
		self.length = 0
		print type, length, len(data)
			
		if isinstance(self.request, requests.Connect):
			response = self.response_handler.decode_connection(type, length, data)
			self.remote_info = response
		else:
			response = self.response_handler.decode(type, length, data)
		
		if self.sending_headers and not isinstance(response, responses.Continue):
			self.err_callback(error=common.ObexError(response))
			return

		self.internal_callback(response=response)

	def __connected_rfcomm(self, *args, **kwargs):
		self.callback(*args[1:], **kwargs)
	
	def __failed_rfcomm(self, *args, **kwargs):
		self.err_callback(error=common.ErrnoError(args[0]), *args[1:], **kwargs)	

	def connect_rfcomm(self, address, port, callback, err_callback, bind=None):
		'''
			Start up the RFcomm level connection. Once this successed
			you have to call connect_rfcomm
		'''
		print "connecting rfcomm"
		self.socket = BluetoothAsyncSocket()
		self.__callback = callback
		self.__err_callback = err_callback
		if bind:
		    self.socket._sock.bind((bind, 0))
		self.socket.connect_ex((address, port), 
							callback=self.__connected_rfcomm, 
							err_callback=self.__failed_rfcomm)
		self.state = common.CONNECTING_RFCOMM
		return common.ReplyPending()
	
	def connect_obex(self, header_list=[]):
		'''
			Start the Obex connection.
		'''
		flags = 0
		data = (self.obex_version.to_byte(), flags, self.max_packet_length)

		max_length = self.max_packet_length
		request = requests.Connect(data)

		# upgrade our state
		self.state = common.CONNECTING_OBEX
		
		# register to get informed when more data is available
		self.socket.setReadReady(self.data_ready) 

		self.pending_headers = list(header_list)
		self._send_headers(request, max_length)

	def _send_headers(self, request, max_length):
	
		"""Convenience method to add headers to a request and send one or
		more requests with those headers."""
		
		while self.pending_headers:
		
			if request.add_header(self.pending_headers[0], max_length):
				self.pending_headers.pop(0)
			else:
				print "sending on headers", request.encode()
				self.socket.sendall(request.encode())
				request.reset_headers()
				self.sending_headers = True
				self.request = request
				# now we wait for data
				return common.ReplyPending
		
		self.sending_headers = False
		# 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
		
		print "sending", len(request.encode())
		self.socket.sendall(request.encode())
		self.request = request
		return common.ReplyPending
	
	def put_headers_done(self, response):
		if not isinstance(response, responses.Continue):
			return self.err_callback(common.ObexError(response))
		self.put_body()
		
	def put_body_done(self, response):
		if not isinstance(response, responses.Continue):
			return self.err_callback(common.ObexError(response))
		
		self.put_body()
		
	def put_done(self, response):
		if not isinstance(response, responses.Success):
			return self.err_callback(common.ObexError(response))
		
		self.callback(response)
		self.state = common.CONNECTED
		self.state_put = common.PUT_IDLE
		
		
	def put_body(self):
		max_length = self.remote_info.max_packet_length
		file_data = self.file_data
		
		# 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
		
		data = file_data[:optimum_size]
		self.file_data=self.file_data[optimum_size:]
		
		if len(data) == 0:
			raise Exception("work done")
		
		if len(self.file_data) > 0:
			self.state_put = common.PUT_BODY
			request = requests.Put()
			request.add_header(headers.Body(data, False), max_length)
			self.socket.sendall(request.encode())			
		else:
			self.state_put = common.PUT_FINAL
			request = requests.Put_Final()
			request.add_header(headers.End_Of_Body(data, False), max_length)
			self.socket.sendall(request.encode())
	
	def put(self, name, file_data, header_list = ()):
		"""put(self, name, file_data, header_list = ())
		
		Sends a file with the given name, containing the file_data specified,
		to the server for storage in the current directory for the session,
		and returns the response.
		
		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.
		"""
		
		header_list = [
					headers.Name(name),
					headers.Length(len(file_data))
			] + list(header_list)
		
		max_length = self.remote_info.max_packet_length
		request = requests.Put()
		self.state = common.PUT
		self.state_put = common.PUT_HEADER
		
		self.file_data = file_data
		self.pending_headers = header_list
		
		self._send_headers(request, max_length)		
Esempio n. 9
0
    def __init__(self, address=""):

        self.address = address
        self.max_packet_length = 0xffff
        self.obex_version = OBEX_Version()
        self.request_handler = requests.RequestHandler()