lightblue-0.3.2/0000755000076500007650000000000010770565177013024 5ustar beabea00000000000000lightblue-0.3.2/CHANGELOG0000644000076500007650000000557510770564765014254 0ustar beabea00000000000000Version 0.3.2 ============= + Fixed so doesn't raise exception when using 'from lightblue import *'. + For Linux: fixed bug where lightblue.obex.recvfile() was often refusing requests. + For Mac: fixed bug where NSInconsistencyException was being raised while blocking/processing events. Version 0.3.1 ============= + This fixes a bug in findservices() on Python for Series 60. Version 0.3 =========== + A new OBEXClient class for Mac OS X and Linux. It can send all the usual OBEX requests, with any type of headers (including custom headers). There is a new obex_ftp_client.py example that uses OBEXClient to implement an OBEX File Transfer client. + The library now works on Mac OS 10.5. Currently there are some deprecation warnings when you import lightblue, but there shouldn't be any issues otherwise. + The BTUtil framework has been renamed to "LightAquaBlue", and its OBEX-related classes have been completely rewritten; it now has a much better, more flexible API, that makes it possible to use it to build higher-level OBEX implementations. + The Linux version's internal OBEX code has been completely rewritten, and it's now much easier to customise and tweak the OBEX client and server implementations, if necessary. + The lightblue.obex sendfile() and recvfile() functions now accept any old file-like objects, instead of only accepting built-in file objects with proper file descriptors. + Fixed various unicode-related bugs. Version 0.2.3 ============= + Fixed Linux version to work with newer versions of PyBluez Version 0.2.2 ============= + The PyS60 3rd Edition binaries have (really) been fixed, and gethostaddr() and gethostclass() should also be fixed for PyS60 2nd Edition FP2 and FP3. + Since it's getting more difficult to build for PyS60 1st Edition, this build has been dropped for this version -- which isn't an issue for this release since there are no new features -- but there won't be any further builds for this edition. If you need to compile LightBlue for 1st Edition, feel free to email me with any issues. + Fixed functions on Mac OS X build that wait (e.g. finddevices(), recv() for sockets) so that they don't busy-wait and hog the CPU. Version 0.2.1 ============= + Hopefully fixed problems with PyS60 3rd Edition sisx binary + Added unsigned sis for PyS60 3rd Edition with maximum free dev cert capabilities Version 0.2 =========== + L2CAP client sockets for Mac OS X and Linux + finddevicename() now takes usecache argument to specify whether to do a remote name request if name is in local cache + stopadvertising() is now automatically called for a socket when close() is called on the socket + Added SIS build for Series 60 3rd Edition + If some of the SIS files for PyS60 didn't work before, they should now + Improved buffering of received data for sockets on Mac OS X + Fixed recv() for mac sockets if other side has closed connection + Fixed mac sockets to receive binary data lightblue-0.3.2/COPYING0000644000076500007650000000524210533016742014044 0ustar beabea00000000000000LightBlue is licensed under the MIT license, as reproduced below, and in each individual source code file. The LightBlue implementation for Python for Series 60 uses some source files from the PDIS project from the Helsinki Institute for Information Technology (HIIT). The license for these sources is also reproduced below. -------------------------------------------------------------------- LightBlue is: Copyright (c) 2006 Bea Lam. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------- The PDIS source code is: Copyright 2002-2005 Helsinki Institute for Information Technology (HIIT) and the authors. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. lightblue-0.3.2/doc/0000755000076500007650000000000010770565176013570 5ustar beabea00000000000000lightblue-0.3.2/doc/._index.html0000644000076500000000000000012210750353372016317 0ustar beawheel00000000000000Mac OS X  2 RTEXTlightblue-0.3.2/doc/index.html0000644000076500007650000002315210750353372015557 0ustar beabea00000000000000 package lightblue

package lightblue

LightBlue - a simple bluetooth library.

Sub-modules

module lightblue.obex

Provides an OBEX client class and convenience functions for sending and receiving files over OBEX.

Classes & Types

Socket objects

A Bluetooth socket object has the same interface as a socket object from the Python standard library <socket> module.

exception BluetoothError (exceptions.IOError)

Generic exception raised for Bluetooth errors.

Functions

finddevices(getnames=True, length=10)

    Performs a device discovery and returns the found devices as a list of 
    (address, name, class-of-device) tuples. Raises BluetoothError if an error
    occurs.
    
    Arguments:
        - getnames=True: True if device names should be retrieved during 
          discovery. If false, None will be returned instead of the device
          name.
        - length=10: the number of seconds to spend discovering devices 
          (this argument has no effect on Python for Series 60)
            
    Do not invoke a new discovery before a previous discovery has finished.
    Also, to minimise interference with other wireless and bluetooth traffic, 
    and to conserve battery power on the local device, discoveries should not 
    be invoked too frequently (an interval of at least 20 seconds is 
    recommended).

findservices(addr=None, name=None, servicetype=None)

    Performs a service discovery and returns the found services as a list of 
    (device-address, service-port, service-name) tuples. Raises BluetoothError 
    if an error occurs.
    
    Arguments:
        - addr=None: a device address, to search only for services on a 
          specific device
        - name=None: a service name string, to search only for a service with a
          specific name
        - servicetype=None: can be RFCOMM or OBEX to search only for RFCOMM or
          OBEX-type services. (OBEX services are not returned from an RFCOMM
          search)
          
    If more than one criteria is specified, this returns services that match 
    all criteria.
    
    Currently the Python for Series 60 implementation will only find RFCOMM and 
    OBEX services.

finddevicename(address, usecache=True)

    Returns the name of the device with the given bluetooth address.
    finddevicename(gethostaddr()) returns the local device name.
    
    Arguments:
        - address: the address of the device to look up
        - usecache=True: if True, the device name will be fetched from a local
          cache if possible. If False, or if the device name is not in the 
          cache, the remote device will be contacted to request its name.
    
    Raise BluetoothError if the name cannot be retrieved.

selectdevice()

    Displays a GUI which allows the end user to select a device from a list of 
    discovered devices. 
    
    Returns the selected device as an (address, name, class-of-device) tuple. 
    Returns None if the selection was cancelled.
    
    (On Python For Series 60, the device selection will fail if there are any 
    open bluetooth connections.)

selectservice()

    Displays a GUI which allows the end user to select a service from a list of 
    discovered devices and their services.
    
    Returns the selected service as a (device-address, service-port, service-
    name) tuple. Returns None if the selection was cancelled.
    
    (On Python For Series 60, the device selection will fail if there are any 
    open bluetooth connections.)
    
    Currently the Python for Series 60 implementation will only find RFCOMM and 
    OBEX services.

gethostaddr()

    Returns the address of the local bluetooth device. 

    Raise BluetoothError if the local device is not available.

gethostclass()

    Returns the class of device of the local bluetooth device. 
    
    These values indicate the device's major services and the type of the 
    device (e.g. mobile phone, laptop, etc.). If you google for 
    "assigned numbers bluetooth baseband" you might find some documents
    that discuss how to extract this information from the class of device.

    Raise BluetoothError if the local device is not available.

socket(proto=RFCOMM)

    socket(proto=RFCOMM) -> socket object
    
    Returns a new socket object.
    
    Arguments:
        - proto=RFCOMM: the type of socket to be created - either L2CAP or
          RFCOMM. 
          
    Note that L2CAP sockets are not available on Python For Series 60, and
    only L2CAP client sockets are supported on Mac OS X and Linux (i.e. you can
    connect() the socket but not bind(), accept(), etc.).

advertise(name, sock, servicetype)

    Starts advertising a service with the given name, using the given server
    socket. Raises BluetoothError if the service cannot be advertised.
    
    Arguments:
        - name: name of the service to be advertised
        - sock: the socket object that will serve this service. The socket must 
          be already bound to a channel. If a RFCOMM service is being 
          advertised, the socket should also be listening.
        - servicetype: the type of service to advertise - either RFCOMM or 
          OBEX. (L2CAP services are not currently supported.)
          
    (If the servicetype is RFCOMM, the service will be advertised with the
    Serial Port Profile; if the servicetype is OBEX, the service will be
    advertised with the OBEX Object Push Profile.)

stopadvertise(sock)

    Stops advertising the service on the given socket. Raises BluetoothError if 
    no service is advertised on the socket.
    
    This will error if the given socket is already closed.

splitclass(classofdevice)

    Splits the given class of device to return a 3-item tuple with the 
    major service class, major device class and minor device class values.

    These values indicate the device's major services and the type of the 
    device (e.g. mobile phone, laptop, etc.). If you google for 
    "assigned numbers bluetooth baseband" you might find some documents
    that discuss how to extract this information from the class of device.

    Example:
        >>> splitclass(1057036)
        (129, 1, 3)
        >>>

Data

L2CAP = 10

RFCOMM = 11

OBEX = 12

lightblue-0.3.2/doc/lightblue-BluetoothError.html0000644000076500007650000000160410750353372021402 0ustar beabea00000000000000 BluetoothError

package lightblue:

exception BluetoothError (exceptions.IOError)

    Generic exception raised for Bluetooth errors. This is not raised for
    socket-related errors; socket objects raise the socket.error and 
    socket.timeout exceptions from the standard library socket module.
    
    Note that error codes are currently platform-independent. In particular, 
    the Mac OS X implementation returns IOReturn error values from the IOKit 
    framework, and OBEXError codes from <IOBluetooth/OBEX.h> for OBEX operations.
    
lightblue-0.3.2/doc/._lightblue-Socket_objects.html0000644000076500000000000000012210750353372022126 0ustar beawheel00000000000000Mac OS X  2 RTEXTlightblue-0.3.2/doc/lightblue-Socket_objects.html0000644000076500007650000003254410750353372021373 0ustar beabea00000000000000 Socket objects

package lightblue:

Socket objects

    A Bluetooth socket object has the same interface as a socket object from 
    the Python standard library <socket> module. It also uses the same 
    exceptions, raising socket.error for general errors and socket.timeout for 
    timeout errors.

    Note that L2CAP sockets are not available on Python For Series 60, and
    only L2CAP client sockets are supported on Mac OS X and Linux.

    A simple client socket example:
        >>> from lightblue import *
        >>> s = socket()        # or socket(L2CAP) to create an L2CAP socket
        >>> s.connect(("00:12:2c:45:8a:7b", 5))
        >>> s.send("hello")
        5
        >>> s.close()
A simple server socket example:
        >>> from lightblue import *
        >>> s = socket()
        >>> s.bind(("", 0))
        >>> s.listen(1)
        >>> advertise("My RFCOMM Service", s, RFCOMM)
        >>> conn, addr = s.accept()
        >>> print "Connected by", addr
        Connected by ('00:0D:93:19:C8:68', 5)
        >>> conn.recv(1024)
        "hello"
        >>> conn.close()
        >>> s.close()

Methods

accept()

    accept() -> (socket object, address info)
    
    Wait for an incoming connection. Return a new socket representing the
    connection, and the address of the client. For RFCOMM sockets, the address
    info is a pair (hostaddr, channel).
    
    The socket must be bound and listening before calling this method.

bind(address)

    bind(address)
    
    Bind the socket to a local address. For RFCOMM sockets, the address is a
    pair (host, channel); the host must refer to the local host. 
    
    A port value of 0 binds the socket to a dynamically assigned port.
    (Note that on Mac OS X, the port value must always be 0.)
    
    The socket must not already be bound.

close()

    close()
    
    Close the socket.  It cannot be used after this call.

connect(address)

    connect(address)
    
    Connect the socket to a remote address. The address should be a 
    (host, channel) pair for RFCOMM sockets, and a (host, PSM) pair for L2CAP
    sockets.
    
    The socket must not be already connected.

connect_ex(address)

    connect_ex(address) -> errno
    
    This is like connect(address), but returns an error code instead of raising 
    an exception when an error occurs.

dup()

    dup() -> socket object
    
    Returns a new socket object connected to the same system resource.

fileno()

    fileno() -> integer
    
    Return the integer file descriptor of the socket.
    
    Raises NotImplementedError on Mac OS X and Python For Series 60.

getpeername()

    getpeername() -> address info 
    
    Return the address of the remote endpoint. The address info is a
    (host, channel) pair for RFCOMM sockets, and a (host, PSM) pair for L2CAP
    sockets.
    
    If the socket has not been connected, socket.error will be raised.

getsockname()

    getsockname() -> address info
    
    Return the address of the local endpoint. The address info is a
    (host, channel) pair for RFCOMM sockets, and a (host, PSM) pair for L2CAP
    sockets.
    
    If the socket has not been connected nor bound, this returns the tuple
    ("00:00:00:00:00:00", 0).

getsockopt(level, option[, bufsize])

    getsockopt(level, option[, bufsize]) -> value
    
    Get a socket option.  See the Unix manual for level and option.
    If a nonzero buffersize argument is given, the return value is a
    string of that length; otherwise it is an integer.
    
    Currently support for socket options are platform independent -- i.e. 
    depends on the underlying Series 60 or BlueZ socket options support. 
    The Mac OS X implementation currently does not support any options at
    all and automatically raises socket.error.

gettimeout()

    gettimeout() -> timeout
     
    Returns the timeout in floating seconds associated with socket 
    operations. A timeout of None indicates that timeouts on socket 
    operations are disabled.
    
    Currently not supported on Python For Series 60 implementation, which 
    will always return None.

listen(backlog)

    listen(backlog)
    
    Enable a server to accept connections. The backlog argument must be at
    least 1; it specifies the number of unaccepted connection that the system
    will allow before refusing new connections.
        
    The socket must not be already listening.
    
    Currently not implemented on Mac OS X.

makefile([mode[, bufsize]])

    makefile([mode[, bufsize]]) -> file object

    Returns a regular file object corresponding to the socket.  The mode
    and bufsize arguments are as for the built-in open() function.

recv(bufsize[, flags])

    recv(bufsize[, flags]) -> data
    
    Receive up to bufsize bytes from the socket.  For the optional flags
    argument, see the Unix manual.  When no data is available, block until
    at least one byte is available or until the remote end is closed.  When
    the remote end is closed and all data is read, return the empty string.
    
    Currently the flags argument has no effect on Mac OS X.

recvfrom(bufsize[, flags])

    recvfrom(bufsize[, flags]) -> (data, address info)
    
    Like recv(buffersize, flags) but also return the sender's address info.

send(data[, flags])

    send(data[, flags]) -> count
    
    Send a data string to the socket.  For the optional flags
    argument, see the Unix manual.  Return the number of bytes
    sent.
    
    The socket must be connected to a remote socket.
    
    Currently the flags argument has no effect on Mac OS X.

sendall(data[, flags])

    sendall(data[, flags])
 
    Send a data string to the socket.  For the optional flags
    argument, see the Unix manual.  This calls send() repeatedly
    until all data is sent.  If an error occurs, it's impossible
    to tell how much data has been sent.

sendto(data[, flags], address)

    sendto(data[, flags], address) -> count
    
    Like send(data, flags) but allows specifying the destination address.
    For RFCOMM sockets, the address is a pair (hostaddr, channel).

setblocking(flag)

    setblocking(flag)
     
    Set the socket to blocking (flag is true) or non-blocking (false).
    setblocking(True) is equivalent to settimeout(None);
    setblocking(False) is equivalent to settimeout(0.0).
    
    Initially a socket is in blocking mode. In non-blocking mode, if a 
    socket operation cannot be performed immediately, socket.error is raised.

setsockopt(level, option, value)

    setsockopt(level, option, value)
     
    Set a socket option.  See the Unix manual for level and option.
    The value argument can either be an integer or a string.
    
    Currently support for socket options are platform independent -- i.e. 
    depends on the underlying Series 60 or BlueZ socket options support. 
    The Mac OS X implementation currently does not support any options at
    all and automatically raise socket.error.

settimeout(timeout)

    settimeout(timeout)
     
    Set a timeout on socket operations.  'timeout' can be a float,
    giving in seconds, or None.  Setting a timeout of None disables
    the timeout feature and is equivalent to setblocking(1).
    Setting a timeout of zero is the same as setblocking(0).    
    
    If a timeout is set, the connect, accept, send and receive operations will 
    raise socket.timeout if a timeout occurs.
    
    Raises NotImplementedError on Python For Series 60.

shutdown(how)

    shutdown(how)
     
    Shut down the reading side of the socket (flag == socket.SHUT_RD), the 
    writing side of the socket (flag == socket.SHUT_WR), or both ends 
    (flag == socket.SHUT_RDWR).
lightblue-0.3.2/doc/._lightblue.obex-OBEXClient.html0000644000076500000000000000012210750555770022023 0ustar beawheel00000000000000Mac OS X  2 RTEXTlightblue-0.3.2/doc/lightblue.obex-OBEXClient.html0000644000076500007650000003756510750555770021300 0ustar beabea00000000000000 OBEXClient

module lightblue.obex:

class OBEXClient

    An OBEX client class. (Note this is not available on Python for Series 60.)

    For example, to connect to an OBEX server and send a file:
        >>> import lightblue
        >>> client = lightblue.obex.OBEXClient("aa:bb:cc:dd:ee:ff", 10)
        >>> client.connect()
        <OBEXResponse reason='OK' code=0x20 (0xa0) headers={}>
        >>> client.put({"name": "photo.jpg"}, file("photo.jpg", "rb"))
        <OBEXResponse reason='OK' code=0x20 (0xa0) headers={}>
        >>> client.disconnect()
        <OBEXResponse reason='OK' code=0x20 (0xa0) headers={}>
        >>>
A client must call connect() to establish a connection before it can send any other requests. The connect(), disconnect(), put(), delete(), get() and setpath() methods all accept the request headers as a dictionary of header-value mappings. The request headers are used to provide the server with additional information for the request. For example, this sends a Put request that includes Name, Type and Length headers in the request headers, to provide details about the transferred file:
        >>> f = file("file.txt")
        >>> client.put({"name": "file.txt", "type": "text/plain",
        ...         "length": 5192}, f)
        >>>
Here is a list of all the different string header keys that you can use in the request headers, and the expected type of the value for each header: - "name" -> a string - "type" -> a string - "length" -> an int - "time" -> a datetime object from the datetime module - "description" -> a string - "target" -> a string or buffer - "http" -> a string or buffer - "who" -> a string or buffer - "connection-id" -> an int - "application-parameters" -> a string or buffer - "authentication-challenge" -> a string or buffer - "authentication-response" -> a string or buffer - "creator-id" -> an int - "wan-uuid" -> a string or buffer - "object-class" -> a string or buffer - "session-parameters" -> a string or buffer - "session-sequence-number" -> an int less than 256 (The string header keys are not case-sensitive.) Alternatively, you can use raw header ID values instead of the above convenience strings. So, the previous example can be rewritten as:
        >>> client.put({0x01: "file.txt", 0x42: "text/plain", 0xC3: 5192},
        ...     fileobject)
        >>>
This is also useful for inserting custom headers. For example, a PutImage request for a Basic Imaging client requires the Img-Descriptor (0x71) header:
        >>> client.put({"type": "x-bt/img-img",
        ...     "name": "photo.jpg",
        ...     0x71: '<image-descriptor version="1.0"><image encoding="JPEG" pixel="160*120" size="37600"/></image-descriptor>'},
        ...     file('photo.jpg', 'rb'))
        >>>
Notice that the connection-id header is not sent, because this is automatically included by OBEXClient in the request headers if a connection-id was received in a previous Connect response. See the included src/examples/obex_ftp_client.py for an example of using OBEXClient to implement a File Transfer client for browsing the files on a remote device.

Methods

__init__(address, channel)

    Creates an OBEX client.

    Arguments:
        - address: the address of the remote device
        - channel: the RFCOMM channel of the remote OBEX service

connect(headers={})

    Establishes the Bluetooth connection to the remote OBEX server and sends
    a Connect request to open the OBEX session. Returns an OBEXResponse 
    instance containing the server response.
    
    Raises lightblue.obex.OBEXError if the session is already connected, or if
    an error occurs during the request.
    
    If the server refuses the Connect request (i.e. if it sends a response code
    other than OK/Success), the Bluetooth connection will be closed.

    Arguments:
        - headers={}: the headers to send for the Connect request

disconnect(headers={})

    Sends a Disconnect request to end the OBEX session and closes the Bluetooth
    connection to the remote OBEX server. Returns an OBEXResponse 
    instance containing the server response.
    
    Raises lightblue.obex.OBEXError if connect() has not been called, or if an
    error occurs during the request.

    Note that you don't need to send any connection-id headers - this is
    automatically included if the client received one in a Connect response.

    Arguments:
        - headers={}: the headers to send for the request

put(headers, fileobj)

    Sends a Put request. Returns an OBEXResponse instance containing the
    server response.

    Raises lightblue.obex.OBEXError if connect() has not been called, or if an
    error occurs during the request.

    Note that you don't need to send any connection-id headers - this is
    automatically included if the client received one in a Connect response.

    Arguments:
        - headers: the headers to send for the request
        - fileobj: a file-like object containing the file data to be sent for
          the request

    For example, to send a file named 'photo.jpg', using the request headers 
    to notify the server of the file's name, MIME type and length:
        >>> client = lightblue.obex.OBEXClient("aa:bb:cc:dd:ee:ff", 10)
        >>> client.connect()
        <OBEXResponse reason='OK' code=0x20 (0xa0) headers={}>
        >>> client.put({"name": "photo.jpg", "type": "image/jpeg",
                "length": 28566}, file("photo.jpg", "rb"))
        <OBEXResponse reason='OK' code=0x20 (0xa0) headers={}>
        >>>

delete(headers)

    Sends a Put-Delete request in order to delete a file or folder on the remote
    server. Returns an OBEXResponse instance containing the server response.

    Raises lightblue.obex.OBEXError if connect() has not been called, or if an
    error occurs during the request.

    Note that you don't need to send any connection-id headers - this is
    automatically included if the client received one in a Connect response.

    Arguments:
        - headers: the headers to send for the request - you should use the
          'name' header to specify the file you want to delete

    If the file on the server can't be deleted because it's a read-only file,
    you might get an 'Unauthorized' response, like this:
        >>> client = lightblue.obex.OBEXClient("aa:bb:cc:dd:ee:ff", 10)
        >>> client.connect()
        <OBEXResponse reason='OK' code=0x20 (0xa0) headers={}>
        >>> client.delete({"name": "random_file.txt"})
        <OBEXResponse reason='Unauthorized' code=0x41 (0xc1) headers={}>
        >>>

get(headers, fileobj)

    Sends a Get request. Returns an OBEXResponse instance containing the server 
    response.

    Raises lightblue.obex.OBEXError if connect() has not been called, or if an
    error occurs during the request.

    Note that you don't need to send any connection-id headers - this is
    automatically included if the client received one in a Connect response.

    Arguments:
        - headers: the headers to send for the request - you should use these
          to specify the file you want to retrieve
        - fileobj: a file-like object, to which the received data will be
          written

    An example:
        >>> client.connect()
        <OBEXResponse reason='OK' code=0x20 (0xa0) headers={}>
        >>> f = file("received_file.txt", "w+")
        >>> client.get({"name": "testfile.txt"}, f)
        <OBEXResponse reason='OK' code=0x20 (0xa0) headers={'length':9}>
        >>> f.seek(0)
        >>> f.read()
        'test file'
        >>>

setpath(headers, cdtoparent=False, createdirs=False)

    Sends a SetPath request in order to set the "current path" on the remote
    server for file transfers. Returns an OBEXResponse instance containing the 
    server response.

    Raises lightblue.obex.OBEXError if connect() has not been called, or if an
    error occurs during the request.

    Note that you don't need to send any connection-id headers - this is
    automatically included if the client received one in a Connect response.

    Arguments:
        - headers: the headers to send for the request - you should use the
          'name' header to specify the directory you want to change to
        - cdtoparent=False: True if the remote server should move up one
          directory before applying the specified directory (i.e. 'cd
          ../dirname')
        - createdirs=False: True if the specified directory should be created
          if it doesn't exist (if False, the server will return an error
          response if the directory doesn't exist)

    For example:
        # change to the "images" subdirectory
        >>> client.setpath({"name": "images"})
        <OBEXResponse reason='OK' code=0x20 (0xa0) headers={}>
        >>>
        # change to the parent directory
        >>> client.setpath({}, cdtoparent=True)
        <OBEXResponse reason='OK' code=0x20 (0xa0) headers={}>
        >>>
        # make a directory "My_Files"
        >>> client.setpath({"name": "My_Files"}, createdirs=True)
        <OBEXResponse reason='OK' code=0x20 (0xa0) headers={}>
        >>>
        # change to the root directory - you can use an empty "name" header
        # to specify this
        >>> client.setpath({"name": ""})
        <OBEXResponse reason='OK' code=0x20 (0xa0) headers={}>
        >>>
lightblue-0.3.2/doc/lightblue.obex-OBEXError.html0000644000076500007650000000101410750353372021121 0ustar beabea00000000000000 OBEXError

module lightblue.obex:

exception OBEXError (lightblue.BluetoothError)

    Generic exception raised for OBEX-related errors.
    
lightblue-0.3.2/doc/lightblue.obex-OBEXResponse.html0000644000076500007650000001013410750353372021631 0ustar beabea00000000000000 OBEXResponse

module lightblue.obex:

class OBEXResponse

    Contains the OBEX response received from an OBEX server.

    When an OBEX client sends a request, the OBEX server sends back a response
    code (to indicate whether the request was successful) and a set of response
    headers (to provide other useful information).

    For example, if a client sends a 'Get' request to retrieve a file, the
    client might get a response like this:
        >>> import lightblue
        >>> client = lightblue.obex.OBEXClient("aa:bb:cc:dd:ee:ff", 10)
        >>> response = client.get({"name": "file.txt"}, file("file.txt", "w"))
        >>> print response
        <OBEXResponse reason='OK' code=0x20 (0xa0) headers={'length': 35288}>
You can get the response code and response headers in different formats:
        >>> print response.reason
        'OK'    # a string description of the response code
        >>> print response.code
        32      # the response code
        >>> print response.headers
        {'length': 35288}   # the headers, with string keys
        >>> print response.rawheaders
        {195: 35288}        # the headers, with raw header ID keys
        >>>
Note how the 'code' attribute does not have the final bit set - e.g. for OK/Success, the response code is 0x20, not 0xA0. The lightblue.obex module defines constants for response code values (e.g. lightblue.obex.OK, lightblue.obex.FORBIDDEN, etc.).

Properties

code

The response code, without the final bit set.

headers

The response headers, as a dictionary with string keys.

rawheaders

The response headers, as a dictionary with header ID (unsigned byte) keys.

reason

A string description of the response code.

Methods

getheader(header, default=None)

        Returns the response header value for the given header, which may
        either be a string (not case-sensitive) or the raw byte
        value of the header ID.

        Returns the specified default value if the header is not present.
lightblue-0.3.2/doc/lightblue.obex.html0000644000076500007650000001611410750353372017363 0ustar beabea00000000000000 module lightblue.obex

module lightblue.obex

Provides an OBEX client class and convenience functions for sending and
receiving files over OBEX.

This module also defines constants for response code values (without the final
bit set). For example:
    >>> import lightblue
    >>> lightblue.obex.OK
    32      # the OK/Success response 0x20 (i.e. 0xA0 without the final bit)
    >>> lightblue.obex.FORBIDDEN
    67      # the Forbidden response 0x43 (i.e. 0xC3 without the final bit)

Classes & Types

class OBEXClient

An OBEX client class.

class OBEXResponse

Contains the OBEX response received from an OBEX server.

exception OBEXError (lightblue.BluetoothError)

Generic exception raised for OBEX-related errors.

Functions

sendfile(address, channel, source)

    Sends a file to a remote device.

    Raises lightblue.obex.OBEXError if an error occurred during the request, or
    if the request was refused by the remote device.

    Arguments:
        - address: the address of the remote device
        - channel: the RFCOMM channel of the remote OBEX service
        - source: a filename or file-like object, containing the data to be
          sent. If a file object is given, it must be opened for reading.

    Note you can achieve the same thing using OBEXClient with something like
    this:
        >>> import lightblue
        >>> client = lightblue.obex.OBEXClient(address, channel)
        >>> client.connect()
        <OBEXResponse reason='OK' code=0x20 (0xa0) headers={}>
        >>> putresponse = client.put({"name": "MyFile.txt"}, file("MyFile.txt", 'rb'))
        >>> client.disconnect()
        <OBEXResponse reason='OK' code=0x20 (0xa0) headers={}>
        >>> if putresponse.code != lightblue.obex.OK:
        ...     raise lightblue.obex.OBEXError("request denied")
        >>>

recvfile(sock, dest)

    Receives a file through an OBEX service.

    Arguments:
        - sock: the server socket on which the file is to be received. Note
          this socket must *not* be listening. Also, an OBEX service should
          have been advertised on this socket.
        - dest: a filename or file-like object, to which the received data will
          be written. If a filename is given, any existing file will be
          overwritten. If a file object is given, it must be opened for writing.

    For example, to receive a file and save it as "MyFile.txt":
        >>> from lightblue import *
        >>> s = socket()
        >>> s.bind(("", 0))
        >>> advertise("My OBEX Service", s, OBEX)
        >>> obex.recvfile(s, "MyFile.txt")

Data

CONTINUE = 16

OK = 32

CREATED = 33

ACCEPTED = 34

NON_AUTHORITATIVE_INFORMATION = 35

NO_CONTENT = 36

RESET_CONTENT = 37

PARTIAL_CONTENT = 38

MULTIPLE_CHOICES = 48

MOVED_PERMANENTLY = 49

MOVED_TEMPORARILY = 50

SEE_OTHER = 51

NOT_MODIFIED = 52

USE_PROXY = 53

BAD_REQUEST = 64

UNAUTHORIZED = 65

PAYMENT_REQUIRED = 66

FORBIDDEN = 67

NOT_FOUND = 68

METHOD_NOT_ALLOWED = 69

NOT_ACCEPTABLE = 70

PROXY_AUTHENTICATION_REQUIRED = 71

REQUEST_TIME_OUT = 72

CONFLICT = 73

GONE = 74

LENGTH_REQUIRED = 75

PRECONDITION_FAILED = 76

REQUESTED_ENTITY_TOO_LARGE = 77

REQUEST_URL_TOO_LARGE = 78

UNSUPPORTED_MEDIA_TYPE = 79

INTERNAL_SERVER_ERROR = 80

NOT_IMPLEMENTED = 81

BAD_GATEWAY = 82

SERVICE_UNAVAILABLE = 83

GATEWAY_TIMEOUT = 84

HTTP_VERSION_NOT_SUPPORTED = 85

DATABASE_FULL = 96

DATABASE_LOCKED = 97

lightblue-0.3.2/doc/._shortdoc.css0000644000076500000000000000012210750353372016661 0ustar beawheel00000000000000Mac OS X  2 RTEXTlightblue-0.3.2/doc/shortdoc.css0000644000076500007650000000153410750353372016121 0ustar beabea00000000000000body { margin:30px } .section { border: 1px solid #3333ff; padding: 5px 20px 20px 20px; margin-top: 10px; } .section .moduletitle { font-size: 100% } .sectiontitle { font-variant:small-caps; font-size:125%; } h3 { margin-bottom: 11px } h4 { font-family:monospace; } .sig { font-weight:normal; font-style:italic; font-family:Verdana } #functionlinks .sig { font-size: 12px } .doc { margin:0; font-family:Verdana; font-size:11px; line-height:150%; } pre.doc { margin:0 0 20px 0; } .classname { font-family:Monospace } .modulename { font-family:Monospace } #functionlinks { font-family:Monospace; line-height: 125% } pre .comment { color:green; } pre .number { color:navy; } pre .string { color:#c00; } pre .interpreter { color:#666; } #footer { text-align: right; } lightblue-0.3.2/examples/0000755000076500007650000000000010770565176014641 5ustar beabea00000000000000lightblue-0.3.2/examples/file_receive.py0000644000076500007650000000412610572753736017640 0ustar beabea00000000000000""" Shows how to receive a file over OBEX. """ import lightblue # bind the socket, and advertise an OBEX service sock = lightblue.socket() try: sock.bind(("", 0)) # bind to 0 to bind to a dynamically assigned channel lightblue.advertise("LightBlue example OBEX service", sock, lightblue.OBEX) # Receive a file and save it as MyFile.txt. # This will wait and block until a file is received. print "Waiting to receive file on channel %d..." % sock.getsockname()[1] lightblue.obex.recvfile(sock, "MyFile.txt") finally: sock.close() print "Saved received file to MyFile.txt!" # Please note: # # To use a file through this example, the other device must send the file to # the correct channel. E.g. if this example prints "Waiting to receive file on # channel 5..." the remote device must send the file specifically to channel 5. # # * But what if you can't specify a channel or service? # # If you can send a file to a specific channel - e.g. by using # lightblue.obex.sendfile(), as the send_file.py example does - then you # should be fine. # # But, if you're just using the system's default OBEX file-sending tool on # the other device (e.g. "Send file..." from the Bluetooth drop-down menu on # Mac OS X, or "Send ... Via Bluetooth" on Series 60 phones), it may only # allow you to choose a device to send the file to, without choosing a # specific channel or service on the device. In this case, the tool is # probably just choosing the first available OBEX service on the device. # # So if you switch off all other related services, this example's service # should automatically receive all OBEX files. E.g. if you're running this # example on Mac OS X, go to the System Preferences' Bluetooth panel: on # Mac OS X 10.4, go to the "Sharing" tab, and uncheck the "On" checkboxes for # the "Bluetooth File Transfer" and "Bluetooth File Exchange" services. # On Mac OS X 10.3, go to the "File Exchange" tab, and for "When receiving # items", select "Refuse all", and uncheck "Allow other devices to browse # files on this computer". lightblue-0.3.2/examples/._file_send.py0000644000076500000000000000012210747233410017671 0ustar beawheel00000000000000Mac OS X  2 RTEXTlightblue-0.3.2/examples/file_send.py0000644000076500007650000000163110747233410017127 0ustar beabea00000000000000""" Shows how to send a file over OBEX. """ import lightblue import sys if len(sys.argv) == 1: print "Usage: file_send.py [filename]" sys.exit(1) sourcefile = sys.argv[1] # Ask user to choose the device and service to send the file to. address, serviceport, servicename = lightblue.selectservice() # Send the file lightblue.obex.sendfile(address, serviceport, sourcefile) print "Done!" # Note: # Instead of calling selectservice(), you could do: # # services = lightblue.findservices(addr=lightblue.selectdevice()[0], # servicetype=lightblue.OBEX) # address, serviceport, servicename = services[0] # lightblue.obex.sendfile(address, serviceport, sourcefile) # # This will ask the user to select a device, and then just send the file to the # first OBEX service found on that device. Then you don't have to ask the # user to select a particular service. #lightblue-0.3.2/examples/LightAquaBlue/0000755000076500007650000000000010770565176017330 5ustar beabea00000000000000lightblue-0.3.2/examples/LightAquaBlue/SimpleOBEXClient/0000755000076500007650000000000010770565176022376 5ustar beabea00000000000000lightblue-0.3.2/examples/LightAquaBlue/SimpleOBEXClient/English.lproj/0000755000076500007650000000000010770565176025114 5ustar beabea00000000000000lightblue-0.3.2/examples/LightAquaBlue/SimpleOBEXClient/English.lproj/InfoPlist.strings0000644000076500007650000000031210744050101030404 0ustar beabea00000000000000þÿ/* Localized versions of Info.plist keys */ NSHumanReadableCopyright = "© __MyCompanyName__, 2008";lightblue-0.3.2/examples/LightAquaBlue/SimpleOBEXClient/English.lproj/MainMenu.nib/0000755000076500007650000000000010770565176027374 5ustar beabea00000000000000lightblue-0.3.2/examples/LightAquaBlue/SimpleOBEXClient/English.lproj/MainMenu.nib/classes.nib0000644000076500007650000000152210744050101031475 0ustar beabea00000000000000{ IBClasses = ( {CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; }, { ACTIONS = { cancelFileTransfer = id; chooseFile = id; connectOrDisconnect = id; findService = id; sendFile = id; }; CLASS = SimpleOBEXClient; LANGUAGE = ObjC; OUTLETS = { addressField = id; cancelButton = id; channelField = id; connectButton = id; connectionProgress = id; filePathField = id; logTextView = id; sendButton = id; transferProgress = id; }; SUPERCLASS = NSObject; } ); IBVersion = 1; }lightblue-0.3.2/examples/LightAquaBlue/SimpleOBEXClient/English.lproj/MainMenu.nib/info.nib0000644000076500007650000000111210744050101030766 0ustar beabea00000000000000 IBDocumentLocation 68 68 356 240 0 0 1024 746 IBEditorPositions 29 68 251 338 44 0 0 1024 746 IBFramework Version 446.1 IBOpenObjects 29 21 IBSystem Version 8R218 lightblue-0.3.2/examples/LightAquaBlue/SimpleOBEXClient/English.lproj/MainMenu.nib/keyedobjects.nib0000644000076500007650000005726110744050101032526 0ustar beabea00000000000000bplist00Ô Y$archiverX$versionT$topX$objects_NSKeyedArchiver† Ñ ]IB.objectdata€¯¡ 159@CFKcdefiq~’–œ¥°±ÃÄÌÍÐÚÛÜàâçèëïöýþ&*/02678>?BELMUVXdfijmtu~‚7ƒ‰Š‘ ¦¶ºËÌÕÝáãçëíïþ²Ñ  !Ñ "&)*+/0568;FGHKTGUVY`aijopuv}~†‡Ž–—žŸ©´·Ùëìíîïðñò¬ôòùýŽÃ  &'04/97:>@BIJRSU\^_`abcfj™Ÿ¯µ¶·¼½¾ÂÆÇÊÍÑÕÜÝàäëïðñôøÿ#(),29>?@CHOPSX`abchoapqv}~ƒŠŽ“—žŸ £§®¯°³·¾¿ÀÃÇÎÏÐÓ×Þßàãçîòóô÷û  !"&-./3:;<@HLMNOSZ[N\`ghNimuvNw{‚ƒ„…‰‘’“˜›œ ¥¦«¬±²·¸½¾ÃÄÉÊÏÐÕÖÛÜáâçèíîóôNXYZ\`achknoz{ˆ‘šY›¥®µ¶·ÀÃÒÛYÛÜâëzYð÷øÿ  Y*3iY<=FYGIW^_`ghir{ÁY„YŽ— ¡¢¤þB†‡ˆñ‹Ž“ ”•ô—˜ÉÉ™š›žŸ ›Îô¡¢š£¦§¨©«®¯˜²Ÿ‡³ ´‘žµ¶Éñôɹ¼¾† ÀÃÆÉ P × Ø Ù Ú Û Ü Ý Þ ß à á â ã ä å­ æ ç è é ê ë ì í î ï ð ñ ò ó ô õ ö ÷ ø ùX ú û ü ý þ ÿ                           ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W Z ] `U$nullß  !"#$%&'()*+,-./0_NSObjectsValues_NSAccessibilityConnectors_NSClassesValuesZNSOidsKeys[NSNamesKeys]NSClassesKeys_NSAccessibilityOidsValues\NSOidsValues_NSVisibleWindowsV$class]NSConnections]NSNamesValues]NSObjectsKeys_NSAccessibilityOidsKeys[NSFramework]NSFontManagerYNSNextOidVNSRootÖן€ €Ç؃ž€€€Ò234[NSClassName€€Ò678YNS.string]NSApplication€Ò:;<=X$classesZ$classname£=>?_NSMutableStringXNSStringXNSObjectÒ:;AB¢B?^NSCustomObjectÒ6D8_IBCocoaFramework€ÒGHJZNS.objects¡I€€ÆÜLMNOPQRSTUVWXYZ[\]^_`ab_NSWindowStyleMask_NSWindowBackingYNSMinSize]NSWindowTitle]NSWindowClass\NSWindowRect\NSScreenRectYNSMaxSize\NSWindowViewYNSWTFlags[NSViewClass€Ã€ € € €Â€Ä€ px€Å€ _{{270, 201}, {432, 427}}_Simple OBEX ClientXNSWindowÒ6g8TView€Ôjklm.opZNSSubviews_NSNextResponderWNSFrame€€€Á€/ÒGr}ªstuvwxyz{|€€M€|€„€ˆ€Œ€¬€°€¹€¾€0Üjk€‚lƒ„…†‡__ŠX‹ŒŽ‘[NSSuperview\NSBorderType_NSTitlePosition[NSTitleCellYNSOffsets]NSTransparent]NSContentViewYNSBoxType€€ € €I€G€H€€6ÒG“}¡€€0Õjkl—ssšp€€€€F€/ÒG}¦žŸ ¡¢£€€$€+€7€<€A€0×kl¦§¨«¬­®¯YNSEnabledXNSvFlagsVNSCell€€€ €#€_{{20, 60}, {239, 22}}Ù²³´µ¶·¸¹º»¼½ž¬ÀÁÂ_NSBackgroundColor[NSTextColorYNSSupportZNSContents]NSControlView_NSDrawsBackground[NSCellFlags\NSCellFlags2€€€€€ €"ÿÿÿÿ”qþA@PÔÅÆÇÈÉÊËVNSSizeVNSNameXNSfFlags"AP€€\LucidaGrandeÒ:;ÎÏ¢Ï?VNSFontÕÑÒÓÔÕÖרÙWNSColor[NSColorName\NSColorSpace]NSCatalogName€€€€VSystem_textBackgroundColorÓÝÓÞŠÙWNSWhiteB1€Ò:;áÑ¢Ñ?ÕÑÒÓÔãä×ØÙ€!€ €€YtextColorÓÝÓéŠÙB0€Ò:;ìí¤íî¨?_NSTextFieldCell\NSActionCellÒ:;ðñ¦ñòóôõ?[NSTextField\%NSTextFieldYNSControlVNSView[NSResponder×kl¦§¨ù¬­ûü€€€% €*€&_{{261, 54}, {123, 32}}Ýÿ´µ¶¸¹¼¼ Ÿ   ½_NSKeyEquivalent_NSAlternateImage^NSButtonFlags2_NSPeriodicInterval]NSButtonFlags_NSPeriodicDelay_NSAlternateContents€(€€€'€$ÿÿÿÿ†‚@ÿ€)Èþ€^Choose file...Ò6Ã8€Ò:;¥î¨?\NSButtonCell]%NSButtonCellÒ:;¥óôõ?XNSButtonÛjk€‚lƒ„†Š!"#Ž‘X€,€€€3€1€2€6ÒG'}¡(€-€0Ôkl  -p€+€+€.€/_{{2, 2}, {125, 1}}Ò:;1ô£ôõ?Ò:;34£45?^NSMutableArrayWNSArray_{{20, 49}, {358, 5}}V{0, 0}ײ³´µ¸¹º:¼<À€€5€€4€"SBoxÓÝÓ@ŠÙM0 0.80000001€Ò:;CD¤Dôõ?UNSBox×kl¦§¨H¬­ûK€€€8 €*€9_{{14, 12}, {71, 32}}Ýÿ´µ¶¸¹N¼¼Q¡   ½€;€€€:€7€)€TSendÒ6Ã8€ØkYlZ§[^_`abcYNSpiFlagsZNSMaxValue\NSDrawMatrix€€ €?#@Y€=€@Ñe€>Ò:;gh¢h?ZNSPSMatrix_{{176, 18}, {204, 20}}Ò:;kl¤lôõ?_NSProgressIndicator×kl¦§¨p¬­ûs€€€B €*€C_{{85, 12}, {82, 32}}Ýÿ´µ¶¸¹v¼¼y£   |½€E€€€D€A€)$þ€VCancelÒ6Ã8€_{{2, 2}, {394, 102}}_{{17, 163}, {398, 119}}ײ³´µ¸¹º…†‡À€€L€K€J€"[Send a fileÔÅÆÇ‹ÉÊŽ"A0€€ ÓÝÓ@ŠÙ€Ûjk’l“§”•…–__™š›­œ™Ÿ]NSNextKeyView[NSHScrollerXNSsFlags[NSVScroller€N€ € €O€z€w€s€O€{ÒG¡}£™›€O€s€w€0Ûjk’§¨©ªl§«tt®Õ®±²³´µYNSBGColorYNSDocViewYNScvFlagsXNSCursor€P€M€M€Q€€Q€o€n €rÒG·}¡®€Q€0Û»k¼½S¾¿§ÀÁ™™ÄÅÆ.×ÈÉÊ[NSFrameSizeXNSMinize\NSSharedDataZNSDelegateYNSTVFlags_NSTextContainer€R€O€O€l€[€k€ €m€SY{375, 14}ÕÍÎÏÐÑ®Ó ÔWNSWidthZNSTextView_NSLayoutManagerYNSTCFlags"C»€€Q€T€ZÕÖ×¾ØÙ×.ÛÜ]NSTextStorageYNSLMFlags_NSTextContainers€U€€Y€XÓ¾>.ßà€€W€VÒ6Ã8€Ò:;äÖ¤Öåæ?_NSMutableAttributedString_NSAttributedStringÒGè}¡Ê€S€0Ò:;ìÏ¢Ï?Ò:;îÀ¢À?Øðñòóôõ²ö÷.ãúû.Õ_NSSelectedAttributesWNSFlags_NSDefaultParagraphStyle_NSInsertionColor_NSLinkAttributes_NSMarkedAttributes€\+瀀!€e€j€€ÓGÿWNS.keys¢€_€b¢€]€^€dÕÑÒÓÔ ×ØÙ€a€`€€_selectedTextBackgroundColorÓÝÓŠÙK0.66666669€ÕÑÒÓÔã×ØÙ€!€c€€_selectedTextColorÒ:;¢?\NSDictionaryÓGÿ¢€h€i¢€f€g€d[NSUnderlineÓ#Ó$ ÙUNSRGBF0 0 1€Ò:;'(¢(?_NSTextViewSharedData\{404, 1e+07}X{114, 0}Ò:;,ΦÎ-.ôõ?[%NSTextViewVNSText_{{1, 1}, {375, 125}}Ó123 4YNSHotSpot\NSCursorType€p€qW{4, -5}Ò:;7ª¢ª?Ò:;9:¤:ôõ?ZNSClipViewØk<=l§>ttAtC­DEZNSCurValueXNSTargetXNSAction€M€M"?€€M€t€v€u_{{376, 1}, {15, 125}}\_doScroller:Ò:;IJ¥Jóôõ?ZNSScrollerÚk<=l§”L>ttAtP­ DRSYNSPercent€M€M€M€x€v"?rC€y_{{-100, -100}, {87, 18}}_{{20, 20}, {392, 127}}Ò:;WX¤Xôõ?\NSScrollView×kl¦§¨__\¬­®_€ € €} €#€~_{{17, 387}, {131, 17}}ز³´µ¶¸¹bc¼euÀh€€€‚€€€|€"@_Connect to address:ÕÑÒÓÔl×ØÙ€a€€€\controlColorÕÑÒÓÔãr×ØÙ€!€ƒ€€_controlTextColor×kl¦§¨__y¬­®|€ € €… €#€†_{{153, 385}, {145, 22}}Ù²³´µ¶·¸¹º»¼‚v¬ÀÁ€€€€‡€„ €"_00:00:00:00:00:00×kl¦§¨__Ь­®€ € €‰ €#€Š_{{311, 387}, {60, 17}}ز³´µ¶¸¹bc¼“wÀh€€€‚€€‹€ˆ€"XChannel:×kl¦§¨__𬭮€ € € €#€Ž_{{376, 385}, {36, 22}}Ú²³ ´µ¶·¸¹º»£¼¥x¬ÀÁÂ[NSFormatter€€€‘€€€Œ €"ת«¬­®¯X°Ž²Ž^NS.mantissa.bo[NS.mantissa[NS.exponentYNS.length[NS.negativeZNS.compactO@{@$€Ò:;µ¶¢¶?_NSDecimalNumberPlaceholder߸¹º»¼½¾¿ÀÁÂÃÄÅÆÇȬÊËÌÍ.Ž.Í.ÓÔÕÖ׎VNS.nil_NS.allowsfloatsWNS.zero_NS.positiveformatVNS.nanVNS.max_NS.positiveattrs\NS.localized_NS.negativeattrsVNS.min[NS.roundingZNS.decimal]NS.attributes[NS.thousand_NS.negativeformat_NS.hasthousands€¥ €œ€š€§€©€€€©€€ª€«€’€£€¡ÓGÿÚâê§ËÜÊÞ×àÖ€š€›€œ€ €¡€¢€£§ãäåæçè逓€”€•€–€—€˜€™€¤^positiveFormat\allowsFloats_attributedStringForZero_usesGroupingSeparator^negativeFormat_formatterBehavior_groupingSeparatorQ0 Óõ>ö÷ø\NSAttributes€ž€Ÿ€ÓGÿúû  €dÒ:;þæ¢æ?R-0èQ,Ò:;£?_NSMutableDictionaryÒ>÷€Ÿ€¦Óõ>ö÷ €ž€Ÿ€¨SNaNת«¬­®¯X°¬²Ž €Q.Ò:;£ ?_NSNumberFormatter×kl¦§¨__¬­û€ € €­ €*€®_{{14, 299}, {110, 32}}Ýÿ´µ¶¸¹½¼¼"y   ½€€€€¯€¬€)€WConnectÛjk€‚lƒ„†(__Š+,-Ž‘X€±€ € €¶€´€µ€6ÒG1}¡2€²€0Ôklzz7p€°€°€³€/_{{20, 338}, {392, 5}}ײ³´µ¸¹º<¼>À€€¸€€·€"ÓÝÓ@ŠÙ€×kl¦§¨__E¬­ûH€ € €º €*€»_{{14, 346}, {128, 32}}Ýÿ´µ¶¸¹K¼¼N{   ½€½€€€¼€¹€)€_Find service...Ò6Ã8€ØkYlZ§[__XY`aZc€ €  €À€¿€@Ñe€>_{{126, 307}, {19, 19}}_{{1, 9}, {432, 427}}_{{0, 0}, {1024, 746}}Z{213, 129}_{3.40282e+38, 3.40282e+38}Ò:;de¢e?_NSWindowTemplateÒ:;gh£hi?\NSMutableSetUNSSetÒGk}¯,lmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—€È€Ö€Ú€à€å€ë€ð€ö€ú€ÿ"'-26;?CINSX]bgikmoqsuwy{}€0Óš›œžWNSLabelXNSSource€Ô€Õ€ÉØ ¡¢£¤¥¦§¨©ª«¬­®VNSMenu]NSMnemonicLoc_NSKeyEquivModMaskWNSTitleYNSOnImageZNSKeyEquiv\NSMixedImage€Êÿÿÿ€Ë€Í€Ì€Ñ€Óԣư±²³´[NSMenuItemsÐÓŠÑXMinimizeQmÓ¸2¹º»^NSResourceName€Ï€Ð€ÎWNSImage_NSMenuCheckmarkÒ:;¿À£ÀÁ?_NSCustomResource_%NSCustomResourceÓ¸2ú»€Ò€Ð€Î_NSMenuMixedStateÒ:;ÈÉ¢É?ZNSMenuItemÒ6Ë8_performMiniaturize:€Ò:;ÎÏ£ÏÐ?_NSNibControlConnector^NSNibConnectorÓš›ÒÔ€Ù€Õ€×Ø ¡¢£¤¥¦§¨©×«½­®€Ê€Ø€Í€€Ñ€Ó_Bring All to FrontÒ6Þ8_arrangeInFront:€Óš›áã€ß€Õ€ÛØ ¡¢£¤¥¦å¨©æ«è­®€Ü€Ý€Í€Þ€Ñ€ÓÓ£°ì³îÀŠÁfPrint &QpÒ6ò8Vprint:€Óš›õ÷€ä€Õ€áØ ¡¢£¤¥¦å¨©ú«ü­®€Ü€â€Í€ã€Ñ€ÓkPage Setup &QPÒ68^runPageLayout:€Óš›€ê€Õ€æØ ¡¢£¤¥¦ ¨© « ­®€ç€è€Í€é€Ñ€ÓÓ£°³Š‘_NewApplication HelpQ?Ò68YshowHelp:€Óš›€ï€Õ€ìØ ¡¢£¤¥¦¨©«½­®€í€î€Í€€Ñ€Óԣư$%³'‡‰ŠˆZClear MenuÒ6*8_clearRecentDocuments:€Ô-š›0/1]NSDestination€€õ€Õ€ñØ ¡¢£¤¥¦3¨©4«6­®€ò€ó€Í€ô€Ñ€Óԣư:;³=¶¼Š·_Quit NewApplicationQqÒ6A8Zterminate:€Ô-š›0EG€€ù€Õ€÷× ¡£¤¥¦3¨J«½­®€ò€ø€Í€€Ñ€Ó_About NewApplicationÒ6Q8_orderFrontStandardAboutPanel:€Ô-š›0UW€€þ€Õ€ûØ ¡¢£¤¥¦3¨Z[«]­®€ò€ü€Í€ý€Ñ€Ó[Hide OthersQh_hideOtherApplications:Ô-š›0eg€€ÕØ ¡¢£¤¥¦3¨©j«l­®€ò€Í€Ñ€Ó_Hide NewApplicationUhide:Ô-š›0su€€ÕØ ¡¢£¤¥¦3¨©x«½­®€ò€Í€€Ñ€ÓXShow All_unhideAllApplications:Óš›€‚ €Õ Ø ¡¢£¤¥¦„¨©…«‡­®  €Í €Ñ€ÓÓ£°‹³ Š¡SCutQxÒ6‘8Tcut:€Óš›”–€ÕØ ¡¢£¤¥¦„¨©™«›­® €Í€Ñ€ÓUPasteQvÒ6¡8Vpaste:€Ó𛤦€ÕØ ¡¢£¤¥¦„¨©©««­® €Í€Ñ€ÓTRedoQZÒ6±8Uredo:€Óš›´¶€ÕØ ¡¢£¤¥¦„¨©¹«»­® €Í€Ñ€ÓZSelect AllQaÒ6Á8ZselectAll:€Óš›ÄÆ!€ÕØ ¡¢£¤¥¦„¨©É«Ë­® €Í €Ñ€ÓTUndoQzÒ6Ñ8Uundo:€Óš›ÔÖ&€Õ#Ø ¡¢£¤¥¦„¨©Ù«Û­® $€Í%€Ñ€ÓTCopyQcÒ6á8Ucopy:€Óš›äæ,€Õ(Ø ¡¢£¤¥¦è¨©é«ë­®)*€Í+€Ñ€ÓÓ£°ï³ñ¥Š¦iSpelling &Q:Ò6õ8_showGuessPanel:€Óš›øú1€Õ.Ø ¡¢£¤¥¦è¨©ý«ÿ­®)/€Í0€Ñ€Ó^Check SpellingQ;Ò68^checkSpelling:€Óš› 5€Õ3Ø ¡¢£¤¥¦è¨© «½­®)4€Í€€Ñ€Ó_Check Spelling as You Type_toggleContinuousSpellChecking:Óš›:€Õ7Ø ¡¢£¤¥¦å¨©«­®€Ü8€Í9€Ñ€ÓUCloseQw]performClose:Óš›#%>€Õ<Ø ¡¢£¤¥¦„¨©(«½­® =€Í€€Ñ€ÓVDeleteWdelete:Óš›02B€Õ@Ø ¡¢£¤¥¦§¨©5«½­®€ÊA€Í€€Ñ€ÓTZoom\performZoom:Óš›=?H€ÕDÙ ¡¢£¤¥¦AB¨©C«E­® UNSTagEF€ÍG€Ñ€ÓÓ£°I³K™ŠšeFind &Qf_performFindPanelAction:Óš›PRM€ÕJÙ ¡¢£¤¥¦AB¨©U«W­®XEK€ÍL€Ñ€ÓYFind NextQgÓš›]_R€ÕOÙ ¡¢£¤¥¦AB¨©b«d­®ŠEP€ÍQ€Ñ€Ó]Find PreviousQGÓš›jlW€ÕTÙ ¡¢£¤¥¦AB¨©o«q­®tEU€ÍV€Ñ€Ó_Use Selection for FindQeÓš›xz\€ÕYØ ¡¢£¤¥¦B¨©}«­®EZ€Í[€Ñ€Ó_Jump to SelectionQj_centerSelectionInVisibleArea:Óš›†ˆa€Õ^Ø ¡¢£¤¥¦„¨Z‹«­® _€Í`€Ñ€Ó_Paste and Match StyleQV_pasteAsPlainText:Ô-š›®•–—€QefcÒ23š€d_SimpleOBEXClient[logTextViewÒ:;žŸ£ŸÐ?_NSNibOutletConnectorÔ-š›y¢–—€¬hfc]connectButtonÔ-š›v¨–—€„jfc\addressFieldÔ-š›x®–—€Œlfc\channelFieldÔ-𛡴–—€7nfcZsendButtonÔ-š›¢º–—€<pfc_transferProgressÔ-š›|À–—€¾rfc_connectionProgressÔ-š›—ÆŸct€Õ€$[chooseFile:Ô-š›—Ìycv€Õ€¬_connectOrDisconnect:Ô-š›—Ò{cx€Õ€¹\findService:Ô-š›—Ø¡cz€Õ€7YsendFile:Ô-š›—Þ£c|€Õ€A_cancelFileTransfer:Ô-š›{ä–I€¹~f€_initialFirstResponderÔ-š›£ê–—€A€fc\cancelButtonÔ-š›žð–—€‚fc]filePathFieldÒGõM¯Wv÷_ùuû ýÔÿÖæ¡lG?y ú2B–ãŸ(„zžx‚sˆ£  "#Iå—|tw3+,%gž012Æ¢5÷7£R§<¶>uW2_Dz¦{è®1 L€„„O‹€ç’€×“#(–€æ€7€í›T€÷D€¬œ.€²ŸE€Û€ì€$€- Y€€Œ§ €^«€A®°3¨³€€Üc€¾€M€ˆ€òÂÉ<€É¸½È€<¢€áÅ€‘J7€Ê¹Í€|€û€@€ Ê€°€¹)€Q€ñ€+ÒÕÚ ¡¢O£¤¥¦>娩R«½­®WYNSSubmenu€Ü€í…€Í€€Ñ€Ó†[Open Recent^submenuAction:Ò6X8€ÒG]}¡€ì€0__NSRecentDocumentsMenuÒ:;b ¢ ?ԣưde³gŒŽŠÒ6i8XServices€ÒGl} €0__NSServicesMenuÚ ¡¢£¤p¥¦q„¨©½«¬½­®¬\NSIsDisabled]NSIsSeparator €€Í €€Ñ€Ó THelpÒG|}¡€æ€0Ú ¡¢£¤p¥¦q„¨©½«¬½­®¬ €€Í €€Ñ€Ó Ø ¡¢£¤¥¦å¨©Š«Œ­®€Ü”€Í•€Ñ€ÓTSaveQsÚ ¡¢O£¤¥¦>„¨©B”«½­®™ E—€Í€€Ñ€Ó˜TFindÒ6š8€ÒGž}¥?R_lzDJOTY€0Ú ¡¢£¤p¥¦q3¨©½«¬½­®¬€ò€€Í €€Ñ€Ó Ø ¡¢£¤¥¦å¨©°«²­®€Ü€Íž€Ñ€ÓhSave As &QSÚ ¡¢£¤p¥¦q3¨©½«¬½­®¬€ò€€Í €€Ñ€Ó Ò6Á8TEdit€ÒGÄ}¬Æ¦ý‚Ö–ˆ%¶û5’ #^<–¢€0Ú ¡¢O£¤¥¦>„¨©èÕ«½­®Ú )£€Í€€Ñ€Ó¤XSpellingÒGÝ}£æú (.3€0Ú ¡¢O£¤¥¦>"¨© 嫽­®ê¨€ç©€Í€€Ñ€ÓªÔ£Æ°ìí³ï±ÔŠ²Ø ¡¢£¤¥¦3¨©ò«ô­®€ò¬€Í­€Ñ€ÓlPreferences &× ¡£¤¥¦å¨ú«½­®€Ü¯€Í€€Ñ€ÓVRevertÚ ¡¢£¤p¥¦q3¨©½«¬½­®¬€ò€€Í €€Ñ€Ó XMainMenuÒG }¥#1D>³½Êͧ€0Ú ¡¢O£¤¥¦>"¨©3«½­®¨€ò´€Í€€Ñ€Óµ^NewApplicationÒG}«G0< gWu1€÷Ÿ«¸¹°€û›€ñ€0Ú ¡¢£¤p¥¦q3¨©½«¬½­®¬€ò€€Í €€Ñ€Ó Ú ¡¢O£¤¥¦>3¨©ù6«½­®;€ò‹º€Í€€Ñ€Ó»\_NSAppleMenuÚ ¡¢O£¤¥¦>"¨©å@«½­®E¨€Ü¾€Í€€Ñ€Ó¿TFileÒ6F8€ÒGJ}«+7÷2ÿ ,÷ãÂÅ„È7“œ®É€á€Û€0Ø ¡¢£¤¥¦å¨©Y«[­®€ÜÀÍĀрÓSNewQnØ ¡¢£¤¥¦å¨©b«d­®€ÜÆ€ÍǀрÓWOpen...QoÚ ¡¢£¤p¥¦q娩½«¬½­®¬€Ü€€Í €€Ñ€Ó Ú ¡¢£¤p¥¦q娩½«¬½­®¬€Ü€€Í €€Ñ€Ó Ú ¡¢O£¤¥¦>"¨©„~«½­®ƒ¨ ˀ̀€Ñ€ÓÌÚ ¡¢O£¤¥¦>"¨©§‡«½­®Œ¨€Ê΀̀€Ñ€ÓÏVWindowÒ68€ÒG‘}¤ž2LÔ€É@Ò€×€0Ú ¡¢£¤p¥¦q§¨©½«¬½­®¬€Ê€€Í €€Ñ€Ó ^_NSWindowsMenu[_NSMainMenuÒ:;£5¢5?ÒG¥M¯W_åB<3„„§å„è„ ÷3B3B_åèz3„å DB_"„_„3å3è0"010___#åå„3§3"å„„ååxBå>3„"_3s§I"_„_5t3§€ €ÜE¹€ò § €Ê€Ü ) €ç€„€òE€òE€ €Ü)€°€ò– €Ü€í€€+ÊE€€ ¨ €  €ò€€Ü€ò)€¨€½€€ € € ³€Ü€Ü €ò€Ê€ò¨€Ü € €Ü€Ü€ŒE€ÜÍ€ò ¨€ €ò€€Ê€¨€  € ¢€M€ò€€ÊÕÒGÿM¯A%z v+t£xBwå2¶sD?æ{_Æ7y–—1®(z|¡ãŸú¦,01ÿI 5lÖû„u¢‚÷Lýž 2"Rè <Y€ç€„€M€A7€ŒE€ˆ€Ü€²€ÊD(€¹€æ®OÅ€¬c½€Q€-€°€¾€7€Û€$«.É€€ñ“€€+¢T# €|€< –€áÒ’€€3ȧ¨J)œÕÒGCM¯ADEFGHIJKLMNOPQRSTUVOXYZO\]^O`abcdefghijklmnopqrstuvwxyO{|}~€‚ƒ„ÙÚÛÜÝÞßàáâãäåæçèéêëäìíîäïðñäòóôõö÷øùúûüýþÿ   ä  Õ[NSMenuItem4[NSMenuItem1Ò6‰8Q2€Ò6Œ8Q9€]NSScrollView2YNSButton2Ò6‘8Q1€\NSTextField1]NSTextField21Ò6Ã8€\NSMenuItem10VNSBox1[NSMenuItem6YNSButton1Ò6œ8R10€[NSMenuItem3[NSMenuItem2[NSMenuItem9WNSBox11R11Ò6¤8Q6€S121[NSMenuItem8\NSMenuItem11Ò6‰8€Ò6¬8\File's Owner€T1111Ò6°8Q3€\NSMenuItem13\NSMenuItem12\NSTextField2[NSMenuItem7Ò6·8Q5€Ò6º8Q7€Ò6‘8€Ò6 8€Ò6Á8Q8€ÒGÄM ÕÒGÇM ÕÒGÊM¯„ ¶–2„zŒD… gr‡ˆ({†lýžnt{úqLã‘”åuwƃ_Š#èu1oB÷7x£mv„‰G—•€ù IRyŸ—¢lzûž®wW‹¡‚>Ô?%02ÿ |v2Ö0¦§“ ‚1sæ"p£x–}s|÷3tŽ5ˆ+’<~_yu,z€+@IkÊNœ€ðX^«€-€¹S€È’€€Ú€Mm.€ë2Ò€Ûu{€Ü€ÿ€ˆC€ g³)€|½€àE€áÅ€Œ€í€A€Ö b›€÷}6‹€ç€J€$c€<Tq€°€É€Q€ûi€7 Í€×®D<¸€²“°"s€„È#€€Êy3?€ñ€Ÿ€€ì(¨€å€‘'€ö€æ€¾„€ò–7€úo¢]Âw¹§-;O€¬ÉYÕÒG QM¯„ R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~  € ‚ ƒ „ … † ‡ ˆ ‰ Š ‹ Œ Ž ‘ ’ “ ” • – — ˜ ™ š › œ ž Ÿ   ¡ ¢ £ ¤ ¥ ¦ § ¨ © ª « ¬ ­ ¯ ° ± ² ³ ´ µ ¶ · ¸ ¹ º » ¼ ½ ¾ ¿ À Á Â Ã Ä Å Æ Ç È É Ê Ë Ì Í Î Ï Ð Ñ Ò Ó Ô Õ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstu€hvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œÕã¬Å³£ÉP†‹ËÌäÊ%œáVé ´½À\N Q˜úžÇ8¹øSWŸMHû}è'™©•:Á‚j§²âç¡ þ®ê¯‘æ pš¤ÿKµ ùO­¿ÆˆØÄÙ~»zü°«¼Žo|9¨I’ ¸ÍRƒg¾Ã¢ý–J›ÒG X} €0ÒG [M ÕÒG ^M ÕÒ:; a b¢ b?^NSIBObjectData#,1:LQVdf¬²ý+=HTb~‹ž¥³ÁÏéõ  #&),.1369<>@CENZ\^gqŠ“ž¥·ÀÉÒ׿ï Pdv€Žœ©¶ÀÍ×ãåçéëíïñóõúüþ  . 7 @ E G X c u }  ƒ … Ž £ ¥ § © « ­ ¯ ± ³ µ · ¹ ê ö   ! + 9 G Q S U W Y [ ] _ ` b d f o r t v ‹ ‘ “ • ž « ­ ¯ ± ³ µ · ¹ Ö à é ð ò ô ö ÷ ú ü þ  ; O [ e p ~ ’ ž « ­ ¯ ± ³ µ ¶ ¸ Á Æ Ç Ø ß æ ï ô ö ø û     2 : F S a c e g i k r ˆ •   ¢ « ° Å Ç É Ë Í × ä ç é ò û  # 0 < I S Z f ƒ … ‡ ‰ Š Œ Ž § Ü î%3E[]_acegirtv{}‚‘šœ¥°½ËÔßè!"$-024EGIKMbkr{‚‘™°·ÔÖØÚÜÞâïýÿ468:;=?V‹‘“•—™ž§©ÊÔßìîðòôý  $=FOe‚„†ˆ‰‹¤ÙÛÝßáãåêìóüþ/LNPRTVbsxz|ŒŽ»ÉÕÞêìîðòôöøúüþCMWajlnprtvxz|Š‘¾ÊÓàëõ   &;CN`joqsuŠ˜¢µ·¹»½ÊÌÎÐÙÛäí '*,.7<EJk‚Ф·Êßáäæèêìîðý  .0246Tamo„†ˆŠŒ ©®»ÈÍÏÑÖØÚÜèõû )6?HUahŒ–£¥§¯¸½ÆÏÚû!#%')ANWbm– ¢¤¦¨ª¯±Ìåî÷!#%'(*,Efhjlnprw¢¤¦¨ª·ÌÎÐÒÔç   )NPRTVXY[oŒŽ’“•—°ÑÓÕ×ÙÛÝæ   'P\^`bdfhikˆ—£¯¹ÅÐãäæçðõY`rzŽ•œ¯¼ÏÖâíû-/02468:;=?ACEGIKLYhjlnprtv…‡‰‹‘“•¤±Ëãò*79;=JKLNW\]`cenu‹”–˜¥§©«¯ÌÍÏÐÒÛâö7lnprtvxz‚¯±³µ·¹»¼¾ÇÊÌÎßáãåçÿ   " $ & 3 5 R T V X Y [ ] v « ­ ¯ ± ³ µ · ¹ Ë Ô Ö ÷ ù û þ!!!! ! !$!;!S!^!{!„!‰!œ!¥!¬!¹!¿!È"#"%"'")"+"-"/"1"3"5"7":"="@"C"F"I"L"O"R"U"X"["^"a"d"g"j"m"p"s"v"y"|""‚"…"ˆ"‹"Ž"‘"”"—"š""Ÿ"¬"´"½"¿"Á"Ã"ä"ë"ù# ###*#7#9#>#C#E#G#I#K#M#^#j#m#p#s#v###Ž##Ÿ#¡#£#«#½#Æ#Í#à#ô$$$$$$#$($3$<$R$T$]$d$|$‹$˜$š$œ$ž$¿$Á$Ã$Å$Ç$É$Ë$à$é$û$ý% % %%%1%3%5%7%9%;%=%J%M%P%S%`%b%k%r%t%%ƒ%…%‡%¨%ª%¬%®%°%²%´%Ë%Í%Ö%å%ç%ô%ö%ø%ú&&&&!&#&%&'&4&7&:&=&S&U&^&h&j&w&y&{&}&ž& &¢&¤&¦&¨&ª&»&¾&Á&Ä&Ç&Ò&Û&ó&õ'''''''='?'A'C'E'G'I'Z']'`'c'f'|'~'‡'’'”'¥'§'©'«'­'Ê'Ì'Î'Ð'Ò'Ô'Ö'í'ö((()(+(-(/(1(R(T(Y([(](_(a(c(o(q(Š(›(( (¢(¥(Æ(È(Ë(Í(Ð(Ò(Ô(ê(ð))))) ),).)1)3)5)7)9)B)[)h)k)m)p)‘)”)—)™)œ)ž) )­)°)³)¶)º)¼)Å)Ê)Ì)Ù)Ü)Þ)á**** * *****"*)*+*8*;*=*@*a*d*g*i*l*n*p*u*w*€*†*ˆ*•*˜*š**¾*Á*Ä*Æ*É*Ë*Í*Ø*Ú*ã*î*ð*ý++++&+)+,+.+1+3+5+:+<+E+K+M+Z+]+_+b+ƒ+†+‰+‹+Ž++’+—+™+¢+¨+ª+·+º+¼+¿+à+ã+æ+è+ë+í+ï+ü+ÿ,,,,,#,5,7,D,G,I,L,m,p,s,u,x,z,|,‹,,–,¥,§,´,·,¹,¼,Ý,à,ã,å,ç,é,ë--)-6-9-;->-_-a-d-f-i-k-m-s-u-ƒ--“-•-˜-¹-¼-¿-Á-Ã-Å-Ç-Î-Ö-ã-æ-è-ë. ........+.8.;.=.@.e.k.n.q.s.v.x.z.‡.Š...›..·.Ä.Ç.É.Ì.ñ.ô.÷.ù.ü.þ// / ////!/F/I/L/N/Q/S/U/c/e/r/u/w/z/Ÿ/¢/¥/§/ª/¬/®/°/É/Ë/Ø/Û/Ý/à0000 0 000$0&0F0S0V0X0[0|00‚0„0‡0‰0‹0£0¥0¹0Ê0Ì0Ï0Ò0Õ0Þ0à0ã0ö11 11)1:1<1?1B1E1S1d1f1i1l1o1|111’1•1˜1¥1¶1¸1»1¾1Á1Ì1Ý1ß1â1å1è1û2 22222,2=2@2C2E2G2S2d2g2j2l2n2…2–2™2œ2ž2 2­2¾2Á2Ä2Æ2È2Ò2ã2æ2é2ë2í333333363G3I3L3O3R3_3p3r3u3x3{3‰3’4C4E4H4K4N4Q4T4V4Y4[4^4a4d4g4i4k4m4p4s4u4x4z4}4€4‚4…4ˆ4‹444‘4“4–4™4›44 4£4¥4¨4«4­4°4³4¶4¹4¼4¾4À4Ã4Å4Ç4É4Ë4Î4Ñ4Ô4×4Ù4Ü4ß4â4å4ç4ê4ì4ï4ñ4ô4÷4ù4ü4ÿ55555 5 55555555 5#5&5O5Y5[5]5`5b5d5f5h5k5w5†55‘5š55Ÿ5¡5º5Ã5È5Ù5Ü5ß5â5å5î5÷5ù66666@6M6[6^6`6b6c6e6g6i6j6o6x6{6}66¨6«6­6¯6°6²6´6¶6·6Ø6Ú6Ý6ß6â6ä6æ6ë6í77777!7#7%7'7*7/787:7C7N7Q7T7W7Z7]7_7ˆ7Š7Œ7Ž77‘7“7•7–7·7¹7¼7¾7Á7Ã7Å7Ö7Ø888888 8 888888(8A8D8G8J8M8P8S8V8Y8\8_8b8e8g88“8–8™8›88Ÿ8¡8¤8­8¶8½8À8Ã8Æ8È8ñ8ô8ö8ù8û8ý8ÿ9999999!9B9D9G9I9L9N9P9i9†9ˆ9‹999‘9“9š9Ã9Å9Ç9É9Ê9Ì9Î9Ð9Ñ9Ú9ã9î9ñ9ô9÷9ú9ý9ÿ:(:+:-:0:2:4:6:8:;:J:S:j:l:o:r:u:x:{:~:€:ƒ:†:ˆ:Š:³:µ:·:¹:º:¼:¾:À:Á:ê:ì:ï:ò:ô:ö:ø:ú:ý; ;3;6;8;;;=;?;A;C;F;K;T;V;_;v;y;|;;‚;…;ˆ;‹;Ž;‘;“;•;—;¸;º;½;¿;Â;Ä;Æ;Ê;Ì;í;ï;ò;ô;÷;ù;û<<<.<0<2<4<5<7<9<;<<6>8>:>=>@>B>E>H>K>M>O>R>U>X>Z>\>_>a>d>f>i>k>m>p>r>t>w>z>|>~>€>‚>…>ˆ>Š>Œ>>’>”>—>™>›>>Ÿ>¢>¤>§>©>¬>®>°>²>´>·>¹>»>¾>À>Â>Ä>Ç>É>Ì>Î>Ñ>Ó>Õ>×>Ú>Ü>ß>á>ä>ç>é>ë>í>ï>ñ>ô>ö>ù>û>þ????? ??—?š??Ÿ?¡?¤?¦?¨?«?­?°?²?´?¶?¹?»?¾?Á?Ä?Æ?È?Ë?Î?Ñ?Ô?Ö?Ù?Ü?ß?á?ã?å?ç?é?ë?í?ð?ó?ö?ù?û?ý@@@@@ @ @@@@@@@@"@%@'@)@,@/@2@5@8@;@>@A@J@Ï@Ò@Õ@Ø@Û@Þ@á@ä@ç@ê@í@ð@ó@ö@ù@ü@ÿAAAA AAAAAAA A#A&A)A,A/A2A5A8A;A>AAADAGAJAMAPASAVAYA\A_AbAeAhAkAnAqAtAwAzA}A€AƒA†A‰AŒAA’A•A¡A­A¶A¸AºAÃAÅAÇAÕAßAèAêAìAùBBBBB&B2BEAEDEFEIELEOERETEWEZE]E`EbEeEhEjEmEoErEuExEzE|EEEƒE†E‰E‹EEE“E–E˜EšEœEŸE¡E¤E§E©E¬E¯E²EµE¸E»E¾EÁEÄEÇEÉEÌEÏEÒEÕEÞFéFìFïFòFõFøFûFþGGGG G GGGGGGG"G%G(G+G.G1G4G7G:G=G@GCGFGIGLGOGRGUGXG[G^GaGdGgGjGmGpGsGvGyG|GG‚G…GˆG‹GŽG‘G”G—GšGG G£G¦G©G¬G¯G²GµG¸G»G¾GÁGÄGÇGÊGÍGÐGÓGÖGÙGÜGßGâGåGèGëGîGñGôG÷GúGýGÿHHHH HHHHHHH H#H&H)H,H/H2H5H8H;H>HAHDHGHJHMHPHSHVHYH\H_HbHeHhHkHnHqHtHwHyH{H~H€H‚H…H‡H‰H‹HHH‘H“H•H—H™H›HHŸH¡H£H¦H¨HªH¬H®H°H²HµH¸HºH¼H¾HÀHÂHÅHÇHÉHËHÍHÏHÑHÓHÕH×HÙHÛHÝHßHáHäHæHèHëHîHðHòHôHöHøHúHüHÿIIIII I IIIIIIIIII!I#I%I'I)I+I-I0I2I4I6I8I:I=I?IAICIEIGIIIKIMIOIQISIUIWIYI[I]I`IbIdIfIhIjImIoIqIsIvIxIzI|I~I€I‚I„I†IˆI‘I’I”IIžI¡IªI«I®I·I¼ cIËlightblue-0.3.2/examples/LightAquaBlue/SimpleOBEXClient/Info.plist0000644000076500007650000000152610744050101024324 0ustar beabea00000000000000 CFBundleDevelopmentRegion English CFBundleExecutable ${EXECUTABLE_NAME} CFBundleIconFile CFBundleIdentifier com.yourcompany.SimpleOBEXClient CFBundleInfoDictionaryVersion 6.0 CFBundleName ${PRODUCT_NAME} CFBundlePackageType APPL CFBundleSignature ???? CFBundleVersion 1.0 NSMainNibFile MainMenu NSPrincipalClass NSApplication lightblue-0.3.2/examples/LightAquaBlue/SimpleOBEXClient/main.m0000644000076500007650000000037510744050101023457 0ustar beabea00000000000000// // main.m // SimpleOBEXClient // // Created by Bea on 6/01/08. // Copyright __MyCompanyName__ 2008. All rights reserved. // #import int main(int argc, char *argv[]) { return NSApplicationMain(argc, (const char **) argv); } lightblue-0.3.2/examples/LightAquaBlue/SimpleOBEXClient/SimpleOBEXClient.h0000644000076500007650000000117710744050101025575 0ustar beabea00000000000000/* SimpleOBEXClient */ #import @class BBBluetoothOBEXClient; @interface SimpleOBEXClient : NSObject { BBBluetoothOBEXClient *mClient; IBOutlet id addressField; IBOutlet id cancelButton; IBOutlet id channelField; IBOutlet id connectButton; IBOutlet id connectionProgress; IBOutlet id filePathField; IBOutlet id logTextView; IBOutlet id sendButton; IBOutlet id transferProgress; } - (IBAction)findService:(id)sender; - (IBAction)connectOrDisconnect:(id)sender; - (IBAction)chooseFile:(id)sender; - (IBAction)sendFile:(id)sender; - (IBAction)cancelFileTransfer:(id)sender; @end lightblue-0.3.2/examples/LightAquaBlue/SimpleOBEXClient/._SimpleOBEXClient.m0000644000076500000000000000012210747223324026353 0ustar beawheel00000000000000Mac OS X  2 RTEXTlightblue-0.3.2/examples/LightAquaBlue/SimpleOBEXClient/SimpleOBEXClient.m0000644000076500007650000002300410747223324025607 0ustar beabea00000000000000#import "SimpleOBEXClient.h" #import #import #import @implementation SimpleOBEXClient - (void)awakeFromNib { [[connectButton window] center]; [[connectButton window] performSelector:@selector(makeFirstResponder:) withObject:connectButton afterDelay:0.0]; } - (void)log:(NSString *)text { [logTextView insertText:text]; [logTextView insertText:@"\n"]; } - (NSNumber *)getSizeOfFile:(NSString *)filePath { NSFileManager *fileManager = [NSFileManager defaultManager]; NSDictionary *fileAttributes = [fileManager fileAttributesAtPath:filePath traverseLink:YES]; if (fileAttributes != nil) return [fileAttributes objectForKey:NSFileSize]; return nil; } - (IBAction)findService:(id)sender { IOBluetoothServiceBrowserController *browser = [IOBluetoothServiceBrowserController serviceBrowserController:0]; if ([browser runModal] == kIOBluetoothUISuccess) { IOBluetoothSDPServiceRecord *service = [[browser getResults] objectAtIndex:0]; [addressField setStringValue:[[service getDevice] getAddressString]]; BluetoothRFCOMMChannelID channelID; if ([service getRFCOMMChannelID:&channelID] == kIOReturnSuccess) [channelField setIntValue:channelID]; } } - (IBAction)connectOrDisconnect:(id)sender { if (!mClient) { if (![BBLocalDevice isPoweredOn]) { [self log:@"Bluetooth device is not available!"]; return; } NSString *deviceAddressString = [addressField stringValue]; BluetoothDeviceAddress deviceAddress; if (IOBluetoothNSStringToDeviceAddress(deviceAddressString, &deviceAddress) != kIOReturnSuccess) { [self log:[NSString stringWithFormat:@"%@ is not a valid Bluetooth device address!", deviceAddressString]]; return; } // Create a BBBluetoothOBEXClient that will connect to the OBEX // server on the specified address and channel. mClient = [[BBBluetoothOBEXClient alloc] initWithRemoteDeviceAddress:&deviceAddress channelID:[channelField intValue] delegate:self]; } if (![mClient isConnected]) { [self log:[NSString stringWithFormat:@"Connecting to %@ on channel %d...", [addressField stringValue], [channelField intValue]]]; // Send a Connect request to start the OBEX session. // You must send a Connect request before you send any other types of // requests. OBEXError status = [mClient sendConnectRequestWithHeaders:nil]; if (status == kOBEXSuccess) { [self log:@"Sent 'Connect' request, waiting for response..."]; [connectionProgress startAnimation:nil]; [connectButton setEnabled:NO]; } else { [self log:[NSString stringWithFormat:@"Connection error! (%d)", status]]; } } else { [self log:@"Disconnecting..."]; // Send a Disconnect requst to close the OBEX session. OBEXError status = [mClient sendDisconnectRequestWithHeaders:nil]; if (status == kOBEXSuccess) { [self log:@"Sent 'Disconnect' request, waiting for response..."]; [connectionProgress startAnimation:nil]; [connectButton setEnabled:NO]; } else { [self log:[NSString stringWithFormat:@"Disconnection error! (%d)", status]]; } } } - (IBAction)chooseFile:(id)sender { NSOpenPanel *openPanel = [NSOpenPanel openPanel]; if ([openPanel runModalForTypes:nil] == NSOKButton) [filePathField setStringValue:[[openPanel filenames] objectAtIndex:0]]; } - (IBAction)sendFile:(id)sender { NSString *filePath = [filePathField stringValue]; // Open a stream that will read from the local file. It doesn't have to be // retained because the client will retain it for the duration of the // request. NSInputStream *fileInputStream = [NSInputStream inputStreamWithFileAtPath:filePath]; [fileInputStream open]; // must be opened // Attach some request headers that tell the server the name and size of // the file that we are sending. BBMutableOBEXHeaderSet *headerSet = [BBMutableOBEXHeaderSet headerSet]; [headerSet setValueForNameHeader:[filePath lastPathComponent]]; NSNumber *fileSize = [self getSizeOfFile:filePath]; if (fileSize) { [headerSet setValueForLengthHeader:[fileSize unsignedIntValue]]; [transferProgress setIndeterminate:NO]; [transferProgress setMaxValue:[fileSize unsignedIntValue]]; [self log:[NSString stringWithFormat:@"Sending file of size: %d", [fileSize unsignedIntValue]]]; } else { [transferProgress setIndeterminate:YES]; } [self log:[NSString stringWithFormat:@"Sending Put request with file <%@>", filePath]]; // send the request OBEXError status = [mClient sendPutRequestWithHeaders:headerSet readFromStream:fileInputStream]; if (status == kOBEXSuccess) { [self log:@"Sending file..."]; [sendButton setEnabled:NO]; [cancelButton setEnabled:YES]; [transferProgress startAnimation:nil]; } else { [self log:[NSString stringWithFormat:@"Put request error! (%d)", status]]; } } - (IBAction)cancelFileTransfer:(id)sender { [self log:@"Attempting to cancel transfer..."]; [mClient abortCurrentRequest]; } #pragma mark - #pragma mark BBBluetoothOBEXClient delegate methods // The following delegate methods allow the BBBluetoothOBEXClient to notify // you when an OBEX event has occurred. You must wait for a notification // that a request has finished before start the next request! For example, // if you send a Connect request, you must wait for // client:didFinishConnectRequestWithError:response: to // be called until you send another request. Otherwise, the OBEX server on the // other end cannot process your requests correctly. - (void)client:(BBBluetoothOBEXClient *)client didFinishConnectRequestWithError:(OBEXError)error response:(BBOBEXResponse *)response { if (error == kOBEXSuccess) { if ([response responseCode] == kOBEXResponseCodeSuccessWithFinalBit) { [self log:@"Connected."]; [connectButton setTitle:@"Disconnect"]; } else { [self log:[NSString stringWithFormat:@"Connect request refused (%@)", [response responseCodeDescription]]]; } } else { [self log:[NSString stringWithFormat:@"Connect error! (%d)", error]]; } [connectButton setEnabled:YES]; [connectionProgress stopAnimation:nil]; } - (void)client:(BBBluetoothOBEXClient *)client didFinishDisconnectRequestWithError:(OBEXError)error response:(BBOBEXResponse *)response { if (error == kOBEXSuccess) { if ([response responseCode] == kOBEXResponseCodeSuccessWithFinalBit) { [self log:@"Disconnected."]; } else { [self log:[NSString stringWithFormat:@"Disconnect request refused (%@)", [response responseCodeDescription]]]; } } else { [self log:[NSString stringWithFormat:@"Disconnect error! (%d)", error]]; } // close the baseband connection to the remote device [[[client RFCOMMChannel] getDevice] closeConnection]; [connectButton setTitle:@"Connect"]; [connectButton setEnabled:YES]; [connectionProgress stopAnimation:nil]; } - (void)client:(BBBluetoothOBEXClient *)client didFinishPutRequestForStream:(NSInputStream *)inputStream error:(OBEXError)error response:(BBOBEXResponse *)response { if (error == kOBEXSuccess) { if ([response responseCode] == kOBEXResponseCodeSuccessWithFinalBit) { [self log:@"File sent successfully."]; } else { [self log:[NSString stringWithFormat:@"Put request refused (%@)", [response responseCodeDescription]]]; } } else { [self log:[NSString stringWithFormat:@"Put error! (%d)", error]]; } [inputStream close]; [transferProgress stopAnimation:nil]; [sendButton setEnabled:YES]; [cancelButton setEnabled:NO]; } - (void)client:(BBBluetoothOBEXClient *)client didSendDataOfLength:(unsigned)length { [self log:[NSString stringWithFormat:@"Sent another %d bytes...", length]]; [transferProgress incrementBy:length]; } - (void)client:(BBBluetoothOBEXClient *)session didAbortRequestWithStream:(NSStream *)stream error:(OBEXError)error response:(BBOBEXResponse *)response { if (error == kOBEXSuccess) { if ([response responseCode] == kOBEXResponseCodeSuccessWithFinalBit) { [self log:@"File transfer cancelled."]; } else { [self log:[NSString stringWithFormat:@"Abort request refused (%@)", [response responseCodeDescription]]]; } } else { [self log:[NSString stringWithFormat:@"Abort error! (%d)", error]]; } [stream close]; [transferProgress stopAnimation:nil]; [transferProgress setDoubleValue:0]; [sendButton setEnabled:YES]; [cancelButton setEnabled:NO]; } - (void)dealloc { [mClient release]; [super dealloc]; } @end lightblue-0.3.2/examples/LightAquaBlue/SimpleOBEXClient/SimpleOBEXClient.xcodeproj/0000755000076500007650000000000010770565176027440 5ustar beabea00000000000000lightblue-0.3.2/examples/LightAquaBlue/SimpleOBEXClient/SimpleOBEXClient.xcodeproj/project.pbxproj0000644000076500007650000002767110750623354032520 0ustar beabea00000000000000// !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 42; objects = { /* Begin PBXBuildFile section */ 817D221B0D3DAA1C00469B5D /* IOBluetooth.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 817D221A0D3DAA1C00469B5D /* IOBluetooth.framework */; }; 81D36BF10D320BED00C2AF19 /* SimpleOBEXClient.m in Sources */ = {isa = PBXBuildFile; fileRef = 81D36BF00D320BED00C2AF19 /* SimpleOBEXClient.m */; }; 81D36C1A0D320E1000C2AF19 /* LightAquaBlue.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 81D36C190D320E1000C2AF19 /* LightAquaBlue.framework */; }; 81D36C660D32573B00C2AF19 /* IOBluetoothUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 81D36C650D32573B00C2AF19 /* IOBluetoothUI.framework */; }; 8D11072A0486CEB800E47090 /* MainMenu.nib in Resources */ = {isa = PBXBuildFile; fileRef = 29B97318FDCFA39411CA2CEA /* MainMenu.nib */; }; 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */; }; 8D11072D0486CEB800E47090 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; settings = {ATTRIBUTES = (); }; }; 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ 089C165DFE840E0CC02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = ""; }; 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; 13E42FB307B3F0F600E4EEF1 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = /System/Library/Frameworks/CoreData.framework; sourceTree = ""; }; 29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 29B97319FDCFA39411CA2CEA /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = English.lproj/MainMenu.nib; sourceTree = ""; }; 29B97324FDCFA39411CA2CEA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; }; 29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; 32CA4F630368D1EE00C91783 /* SimpleOBEXClient_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SimpleOBEXClient_Prefix.pch; sourceTree = ""; }; 817D221A0D3DAA1C00469B5D /* IOBluetooth.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOBluetooth.framework; path = /System/Library/Frameworks/IOBluetooth.framework; sourceTree = ""; }; 81D36BEF0D320BED00C2AF19 /* SimpleOBEXClient.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = SimpleOBEXClient.h; sourceTree = ""; }; 81D36BF00D320BED00C2AF19 /* SimpleOBEXClient.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = SimpleOBEXClient.m; sourceTree = ""; }; 81D36C190D320E1000C2AF19 /* LightAquaBlue.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = LightAquaBlue.framework; path = /Library/Frameworks/LightAquaBlue.framework; sourceTree = ""; }; 81D36C650D32573B00C2AF19 /* IOBluetoothUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOBluetoothUI.framework; path = /System/Library/Frameworks/IOBluetoothUI.framework; sourceTree = ""; }; 8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; 8D1107320486CEB800E47090 /* SimpleOBEXClient.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SimpleOBEXClient.app; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 8D11072E0486CEB800E47090 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */, 81D36C1A0D320E1000C2AF19 /* LightAquaBlue.framework in Frameworks */, 81D36C660D32573B00C2AF19 /* IOBluetoothUI.framework in Frameworks */, 817D221B0D3DAA1C00469B5D /* IOBluetooth.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 080E96DDFE201D6D7F000001 /* Classes */ = { isa = PBXGroup; children = ( ); name = Classes; sourceTree = ""; }; 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */ = { isa = PBXGroup; children = ( 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */, ); name = "Linked Frameworks"; sourceTree = ""; }; 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */ = { isa = PBXGroup; children = ( 29B97324FDCFA39411CA2CEA /* AppKit.framework */, 13E42FB307B3F0F600E4EEF1 /* CoreData.framework */, 29B97325FDCFA39411CA2CEA /* Foundation.framework */, ); name = "Other Frameworks"; sourceTree = ""; }; 19C28FACFE9D520D11CA2CBB /* Products */ = { isa = PBXGroup; children = ( 8D1107320486CEB800E47090 /* SimpleOBEXClient.app */, ); name = Products; sourceTree = ""; }; 29B97314FDCFA39411CA2CEA /* SimpleOBEXClient */ = { isa = PBXGroup; children = ( 080E96DDFE201D6D7F000001 /* Classes */, 29B97315FDCFA39411CA2CEA /* Other Sources */, 29B97317FDCFA39411CA2CEA /* Resources */, 29B97323FDCFA39411CA2CEA /* Frameworks */, 19C28FACFE9D520D11CA2CBB /* Products */, ); name = SimpleOBEXClient; sourceTree = ""; }; 29B97315FDCFA39411CA2CEA /* Other Sources */ = { isa = PBXGroup; children = ( 32CA4F630368D1EE00C91783 /* SimpleOBEXClient_Prefix.pch */, 29B97316FDCFA39411CA2CEA /* main.m */, 81D36BEF0D320BED00C2AF19 /* SimpleOBEXClient.h */, 81D36BF00D320BED00C2AF19 /* SimpleOBEXClient.m */, ); name = "Other Sources"; sourceTree = ""; }; 29B97317FDCFA39411CA2CEA /* Resources */ = { isa = PBXGroup; children = ( 8D1107310486CEB800E47090 /* Info.plist */, 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */, 29B97318FDCFA39411CA2CEA /* MainMenu.nib */, ); name = Resources; sourceTree = ""; }; 29B97323FDCFA39411CA2CEA /* Frameworks */ = { isa = PBXGroup; children = ( 817D221A0D3DAA1C00469B5D /* IOBluetooth.framework */, 81D36C650D32573B00C2AF19 /* IOBluetoothUI.framework */, 81D36C190D320E1000C2AF19 /* LightAquaBlue.framework */, 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */, 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */, ); name = Frameworks; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ 8D1107260486CEB800E47090 /* SimpleOBEXClient */ = { isa = PBXNativeTarget; buildConfigurationList = C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "SimpleOBEXClient" */; buildPhases = ( 8D1107290486CEB800E47090 /* Resources */, 8D11072C0486CEB800E47090 /* Sources */, 8D11072E0486CEB800E47090 /* Frameworks */, ); buildRules = ( ); dependencies = ( ); name = SimpleOBEXClient; productInstallPath = "$(HOME)/Applications"; productName = SimpleOBEXClient; productReference = 8D1107320486CEB800E47090 /* SimpleOBEXClient.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 29B97313FDCFA39411CA2CEA /* Project object */ = { isa = PBXProject; buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "SimpleOBEXClient" */; hasScannedForEncodings = 1; mainGroup = 29B97314FDCFA39411CA2CEA /* SimpleOBEXClient */; projectDirPath = ""; targets = ( 8D1107260486CEB800E47090 /* SimpleOBEXClient */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ 8D1107290486CEB800E47090 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 8D11072A0486CEB800E47090 /* MainMenu.nib in Resources */, 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ 8D11072C0486CEB800E47090 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 8D11072D0486CEB800E47090 /* main.m in Sources */, 81D36BF10D320BED00C2AF19 /* SimpleOBEXClient.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXVariantGroup section */ 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */ = { isa = PBXVariantGroup; children = ( 089C165DFE840E0CC02AAC07 /* English */, ); name = InfoPlist.strings; sourceTree = ""; }; 29B97318FDCFA39411CA2CEA /* MainMenu.nib */ = { isa = PBXVariantGroup; children = ( 29B97319FDCFA39411CA2CEA /* English */, ); name = MainMenu.nib; sourceTree = ""; }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ C01FCF4B08A954540054247B /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { COPY_PHASE_STRIP = NO; GCC_DYNAMIC_NO_PIC = NO; GCC_ENABLE_FIX_AND_CONTINUE = YES; GCC_MODEL_TUNING = G5; GCC_OPTIMIZATION_LEVEL = 0; INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Applications"; PRODUCT_NAME = SimpleOBEXClient; WRAPPER_EXTENSION = app; ZERO_LINK = YES; }; name = Debug; }; C01FCF4C08A954540054247B /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ARCHS = ( ppc, i386, ); GCC_GENERATE_DEBUGGING_SYMBOLS = NO; GCC_MODEL_TUNING = G5; INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Applications"; PRODUCT_NAME = SimpleOBEXClient; WRAPPER_EXTENSION = app; }; name = Release; }; C01FCF4F08A954540054247B /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNUSED_VARIABLE = YES; PREBINDING = NO; SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk; }; name = Debug; }; C01FCF5008A954540054247B /* Release */ = { isa = XCBuildConfiguration; buildSettings = { GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNUSED_VARIABLE = YES; PREBINDING = NO; SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "SimpleOBEXClient" */ = { isa = XCConfigurationList; buildConfigurations = ( C01FCF4B08A954540054247B /* Debug */, C01FCF4C08A954540054247B /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; C01FCF4E08A954540054247B /* Build configuration list for PBXProject "SimpleOBEXClient" */ = { isa = XCConfigurationList; buildConfigurations = ( C01FCF4F08A954540054247B /* Debug */, C01FCF5008A954540054247B /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = 29B97313FDCFA39411CA2CEA /* Project object */; } lightblue-0.3.2/examples/LightAquaBlue/SimpleOBEXClient/SimpleOBEXClient_Prefix.pch0000644000076500007650000000024310744050101027426 0ustar beabea00000000000000// // Prefix header for all source files of the 'SimpleOBEXClient' target in the 'SimpleOBEXClient' project // #ifdef __OBJC__ #import #endif lightblue-0.3.2/examples/LightAquaBlue/SimpleOBEXServer/0000755000076500007650000000000010770565176022426 5ustar beabea00000000000000lightblue-0.3.2/examples/LightAquaBlue/SimpleOBEXServer/English.lproj/0000755000076500007650000000000010770565176025144 5ustar beabea00000000000000lightblue-0.3.2/examples/LightAquaBlue/SimpleOBEXServer/English.lproj/InfoPlist.strings0000644000076500007650000000031210744005727030452 0ustar beabea00000000000000þÿ/* Localized versions of Info.plist keys */ NSHumanReadableCopyright = "© __MyCompanyName__, 2008";lightblue-0.3.2/examples/LightAquaBlue/SimpleOBEXServer/English.lproj/MainMenu.nib/0000755000076500007650000000000010770565176027424 5ustar beabea00000000000000lightblue-0.3.2/examples/LightAquaBlue/SimpleOBEXServer/English.lproj/MainMenu.nib/classes.nib0000644000076500007650000000103210744005727031537 0ustar beabea00000000000000{ IBClasses = ( {CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; }, { ACTIONS = {startOrStopServer = id; }; CLASS = SimpleOBEXServer; LANGUAGE = ObjC; OUTLETS = { logView = id; mConnectionsController = id; serverDirectoryField = id; serviceNameField = id; startButton = id; }; SUPERCLASS = NSObject; } ); IBVersion = 1; }lightblue-0.3.2/examples/LightAquaBlue/SimpleOBEXServer/English.lproj/MainMenu.nib/info.nib0000644000076500007650000000111210744005727031034 0ustar beabea00000000000000 IBDocumentLocation 68 68 356 240 0 0 1024 746 IBEditorPositions 29 68 251 338 44 0 0 1024 746 IBFramework Version 446.1 IBOpenObjects 21 29 IBSystem Version 8R218 lightblue-0.3.2/examples/LightAquaBlue/SimpleOBEXServer/English.lproj/MainMenu.nib/keyedobjects.nib0000644000076500007650000006002210744005727032561 0ustar beabea00000000000000bplist00Ô Y$archiverX$versionT$topX$objects_NSKeyedArchiver† Ñ ]IB.objectdata€¯… 159@CFKcdefiqxŒ–Ÿª«»¼ÄÅÈÒÓÔØÚßàãçîõö !")*12;?DEGKLMSTWZabvwx|€L‡ŒŽ£µ¹ÇÈÑÙÝßãçéëú¬É Éo"#'()./14?@ADM@NORYZabgx€‰¨©°¹º½ÂÃÆËÜãäçìíðøýþ !"*@+4@59:;<=>?BFpv†ŒŽ“”•™ž¡¤¨¬³´·»ÂÆÇÈËÏÖרÛßæêëìïóúÿ &'*/789:?F8GHMTUVZaefgjnuvwz~…†‡ŠŽ•–—šž¥¦§ª®µ¶·º¾ÅÉÊËÎÒÙÚÛÞâéêëïö÷øùý #$%&*12%37>?%@DLM%NRYZ[\`ghijorstw|}‚ƒˆšžŸ¡£¨©³´µ¶¹¾¿ÈÉÊËÕÖ׈ÙÞßäåêëAHIT[\]glmnp~…†‡m‘“—˜š£ª«¬³´µ¾ÇÈÖßæçèñmò÷ùüýÇmm )2m35<EFOmOPT]^fomprŠm‹•–˜î/p~s2éuw vy|}~wr€ª|ƒ„…ì†‰ŠŽ  ¥‘”—Y˜™ œ £¤œ¥¥¦¨©ì«wʬ¯² 0 ® ¯ ° ± ² ³ ´ µ ¶ · ¸ ¹ º » ¼ ½ ¾ ¿ À Á Â Ã Ä Å Æ Ç È É Ê Ë Ì Í Î Ï Ð Ñ Ò Ó Ô Õ Ö × Ø Ù Ú Û Ü Ý Þ ß à á â ã äX å æ ç è é ê ë ì í î ï ð ñ ò ó ô õ ö ÷ ø ù ú û ü ýs þ ÿ      s                  §   ! " # $ ' * -U$nullß  !"#$%&'()*+,-./0_NSObjectsValues_NSAccessibilityConnectors_NSClassesValuesZNSOidsKeys[NSNamesKeys]NSClassesKeys_NSAccessibilityOidsValues\NSOidsValues_NSVisibleWindowsV$class]NSConnections]NSNamesValues]NSObjectsKeys_NSAccessibilityOidsKeys[NSFramework]NSFontManagerYNSNextOidVNSRootÆÇƒ€„€±Ès‚€€ €Ò234[NSClassName€€Ò678YNS.string]NSApplication€Ò:;<=X$classesZ$classname£=>?_NSMutableStringXNSStringXNSObjectÒ:;AB¢B?^NSCustomObjectÒ6D8_IBCocoaFramework€ÒGHJZNS.objects¡I€€°ÜLMNOPQRSTUVWXYZ[\]^_`ab_NSWindowStyleMask_NSWindowBackingYNSMinSize]NSWindowTitle]NSWindowClass\NSWindowRect\NSScreenRectYNSMaxSize\NSWindowViewYNSWTFlags[NSViewClass€­€ € € €¬€®€ px€¯€ _{{258, 143}, {499, 532}}_OBEX Object Push ServerXNSWindowÒ6g8TView€Ôjklm.opZNSSubviews_NSNextResponderWNSFrame€€€«€9ÒGrw¤stuv€€N€|€€:Üjkyz{|l}~€__„X…†‡ˆ‰Š‹[NSSuperview\NSBorderType_NSTitlePosition[NSTitleCellYNSOffsets]NSTransparent]NSContentViewYNSBoxType€€ € €K€I€J€€@ÒGw¡‰€€:Õjkyl‘ss”p€€€€H€9ÒG—w¦˜™š›œ€€$€-€1€5€A€:×kyl ¡¢‰‰¥¦§¨©YNSEnabledXNSvFlagsVNSCell€€€ €#€_{{16, 83}, {128, 17}}ج­®¯°±²³´µ¶˜¸¹º_NSBackgroundColor[NSTextColorYNSSupportZNSContents]NSControlView[NSCellFlags\NSCellFlags2€€€€€€"þ@_OBEX service name:Ô½¾¿ÀÁÂÃVNSSizeVNSNameXNSfFlags"AP€€\LucidaGrandeÒ:;ÆÇ¢Ç?VNSFontÕÉÊËÌÍÎÏÐÑWNSColor[NSColorName\NSColorSpace]NSCatalogName€€€€VSystem\controlColorÓÕËÖ„ÑWNSWhiteK0.66666669€Ò:;ÙÉ¢É?ÕÉÊËÌÛÜÏÐÑ€!€ €€_controlTextColorÓÕËá„ÑB0€Ò:;äå¤åæ¢?_NSTextFieldCell\NSActionCellÒ:;èé¦éêëìí?[NSTextField\%NSTextFieldYNSControlVNSView[NSResponder×kyl ¡¢‰‰ñ¦§¨ô€€€% €#€&_{{149, 81}, {292, 22}}Ù¬­®¯°÷±²øùµû™¦¸ÿ_NSDrawsBackground€(€+€€'€$ €"ÿÿÿÿ”qþA@_My OBEX ServiceÕÉÊËÌÏÐÑ€*€)€€_textBackgroundColorÓÕË „ÑB1€ÕÉÊËÌÛ ÏÐÑ€!€,€€YtextColor×kyl ¡¢‰‰¦§¨€€€. €#€/_{{149, 49}, {292, 22}}Ù¬­®¯°÷±²øùµš¦¸ÿ€(€+€€0€- €"P×kyl ¡¢‰‰%¦§¨(€€€2 €#€3_{{13, 51}, {131, 17}}ج­®¯°±²³´µ.›¸¹º€€€€4€1€"_Server file directory:Ûjkyz{|l}~€3‰‰„Š678ˆ‹X€6€€€=€;€<€@ÒG<w¡=€7€:ÔkylœœBp€5€5€8€9_{{2, 2}, {125, 1}}Ò:;Fì£ìí?Ò:;HI£IJ?^NSMutableArrayWNSArray_{{16, 38}, {425, 5}}V{0, 0}׬­®¯±²øOµQ¸¹Š€(€?€€>€"SBoxÓÕËU„ÑM0 0.80000001€Ò:;XY¤Yìí?UNSBox×kyl ¡¢‰‰]¦§_`€€€B €G€C_{{10, 3}, {112, 32}}Ýcd®¯°efgh±i²jµµmopqrs¹u_NSKeyEquivalent_NSAlternateImage^NSButtonFlags2_NSPeriodicInterval]NSButtonFlags_NSPeriodicDelay_NSAlternateContents€E€€€D€Aÿÿÿÿ†‚@ÿ€FÈ€0\Start serverQ Ò:;yz¥z{æ¢?\NSButtonCell]%NSButtonCellÒ:;}~¥~ëìí?XNSButton_{{2, 2}, {461, 123}}_{{17, 392}, {465, 140}}׬­®¯±²øƒ„¸¹Š€(€M€L€0€"Ô½¾¿ˆÁ‹"A0€€ ÓÕËU„Ñ€Ûjkyl¡‘’“__–—˜§™š–œ]NSNextKeyView[NSHScrollerXNSsFlags[NSVScroller€O€ € €P€z€w€s€P€{ÒGžw£–š˜€P€s€w€:Üjk¤y¥¦§¨l¡©t«t­­°±²³´XNSBoundsYNSDocViewYNSBGColorXNSCursorYNScvFlags€Q€N€n€N€R€R€*€o€m €rÒG¶w¡­€R€:ÚkyºlS»¼¡½––ÀÁÂ.ÏÄÅÆ\NSSharedDataZNSDelegateYNSTVFlags_NSTextContainer€P€P€\€S€k€ €l€T_{{0, -472}, {383, 186}}ÕÉÊËÌÍ­ÏoÐWNSWidthZNSTextView_NSLayoutManagerYNSTCFlags"C¿€€R€U€[ÕÒÓ»ÔÕÏ.ר]NSTextStorageYNSLMFlags_NSTextContainers€V€€Z€YÓ»>.ÛÜ€€X€WÒ6!8€Ò:;àÒ¤Òáâ?_NSMutableAttributedString_NSAttributedStringÒGäw¡Æ€T€:Ò:;èË¢Ë?Ò:;ê½¢½?Øìíîïðñ¬òó.Ûö÷._NSSelectedAttributesWNSFlags_NSDefaultParagraphStyle_NSInsertionColor_NSLinkAttributes_NSMarkedAttributes€]+瀀!€e€j€€*ÓGûüÿWNS.keys¢ýþ€`€b¢€^€_€dÕÉÊËÌÍÏÐÑ€€a€€_selectedTextBackgroundColorÕÉÊËÌÛ ÏÐÑ€!€c€€_selectedTextColorÒ:;¢?\NSDictionaryÓGû¢€h€i¢€f€g€d[NSUnderlineÓËoÑUNSRGBF0 0 1€Ò:; !¢!?_NSTextViewSharedData\{527, 1e+07}Ò:;$ʦÊ%&ìí?[%NSTextViewVNSText_{{1, 1}, {442, 201}}_{{0, -472}, {442, 201}}Ó*+,o-YNSHotSpot\NSCursorType€p€qW{4, -5}Ò:;0§¢§?Ò:;23¤3ìí?ZNSClipViewØky56l¡7tt:t<§=>ZNSCurValueXNSTargetXNSAction€N€N"?€€N€t€v€u_{{443, 1}, {15, 201}}\_doScroller:Ò:;BC¥Cëìí?ZNSScrollerÚky56l¡‘E7tt:tI§o=KLYNSPercent€N€N€N€x€v"?rC€y_{{-100, -100}, {87, 18}}_{{20, 185}, {459, 203}}Ò:;PQ¤Qìí?\NSScrollView×kyl ¡¢__U¦§¨X€ € €} €#€~_{{20, 20}, {459, 65}}ج­®¯°±²³´]^u¸¹º€€€€€€|€"_ZTip: To receive files through this server, make sure the files are sent to the correct channel. Or, switch off all other OBEX services. See SimpleOBEXServer.m for details. Also, you may get "transport lost" (-21880) errors even if a file transfer was successful, if the other device terminates the connection immediately after the file transfer.Ô½¾¿cÁÂf"A €€ Þjkyhil¡‘’jk__nopqr§stnœw\NSCornerView_NSHeaderClipView\NSScrollAmts€‚€ € €ƒ€‹€ˆ€ª€¥’€¢€ƒ€{OA A A˜A˜ÒGyw¥ntrpo€ƒ€¢€¥€ˆ€‹€:Újky¦¥¨l¡vv„…„±‡³´€„€€€…€—€…€¡€rÒGŠw¡„€…€:ߎk‘’ “y”•h–—˜¬¡™š±nœ¦Ÿn¡¢Šo¤¥¦§_NSIntercellSpacingWidth\NSHeaderView_NSColumnAutoresizingStyle[NSRowHeight_NSDraggingSourceMaskForLocalYNSTvFlags_NSIntercellSpacingHeight_NSDraggingSourceMaskForNonLocal[NSFrameSize[NSGridColor^NSTableColumns"@@€‡€ƒ"Aˆ ÿÿÿÿÖà€ƒ"@€ €‹€†€€Ž€*Y{455, 66}Öª–ky¡„¬pp§¯[NSTableView€…€‰€ˆ€ˆ€ŠÚjky¦¥¨l¡±vvš…š±·³´€¨€€€‡€—€‡€©€rY{455, 17}Ò:;»¼¤¼ìí?_NSTableHeaderViewÕkyl¡vvÀ§Á€€€Œ€_{{443, 0}, {16, 17}}Ò:;ÄŤÅìí?]_NSCornerViewÒGÇw¢ÈÉ€€š€:Ú̪ÍÎÉÏÐÑÒ„„ÔÕÖ¦¦ÙÚÛ^NSResizingMask\NSHeaderCellZNSMinWidth^NSIsResizeable\NSIsEditableZNSDataCellZNSMaxWidth€…€"B "CÛ€ €–"Dz€™×¬­®¯±²ÝÞ„àá⊀’€“€L€‘€•þ_Current connectionsÓÕËå„ÑK0.33333299€ÕÉÊËÌÛéÏÐÑ€!€”€€_headerTextColorÒ:;îï¥ï忢?_NSTableHeaderCell׬­®°±²…´µ„¸ö÷€—€€€…€"!þ@ÕÉÊËÌÍúÏÐÑ€€˜€€_controlBackgroundColorÒ:;ÿ¢?]NSTableColumnÚ̪ÍÎÉÏÐÑÒ„„c¦¦ÚÛ€…€›"A €œ€™×¬­®¯±²ÝÞ„á⊀’€“€L€0€•׬­®°±²…´µ„¸ö÷€—€€€…€"ÕÉÊËÌÏÐÑ€Ÿ€ž€€YgridColorÓÕË„ÑD0.5€Ò:;ª¦ª ëìí?\%NSTableView_{{1, 17}, {442, 66}}Øky6l¡E7vvv&§=()€€€€£€v">±ÚF€¤_{{443, 17}, {15, 66}}Úky6l ¡‘E7vvv/¦§o=23€€€€¦ €v"?x¯‹€§_{{-100, -100}, {442, 15}}ÒG6w¡š€‡€:_{{1, 0}, {442, 17}}_{{20, 93}, {459, 84}}_{{1, 9}, {499, 532}}_{{0, 0}, {1024, 746}}Z{213, 129}_{3.40282e+38, 3.40282e+38}Ò:;@A¢A?_NSWindowTemplateÒ:;CD£DE?\NSMutableSetUNSSetÒGGw¯'HIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmn€²€À€Ä€Ê€Ï€Õ€Ú€à€ä€é€î€ò€ø€ý  %)-38=BGLQSU]bdmoq€:ÓqrstuWNSLabelXNSSource€¾€¿€³Øwxyz{|}~€‚ƒ„…VNSMenu]NSMnemonicLoc_NSKeyEquivModMaskWNSTitleYNSOnImageZNSKeyEquiv\NSMixedImage€´ÿÿÿ€µ€·€¶€»€½Ôz¾‡ˆ‰Š‹[NSMenuItems®±‰¯XMinimizeQmÓ2‘’^NSResourceName€¹€º€¸WNSImage_NSMenuCheckmarkÒ:;–—£—˜?_NSCustomResource_%NSCustomResourceÓ2š‘’€¼€º€¸_NSMenuMixedStateÒ:;Ÿ ¢ ?ZNSMenuItemÒ6¢8_performMiniaturize:€Ò:;¥¦£¦§?_NSNibControlConnector^NSNibConnectorÓqr©t«€Ã€¿€ÁØwxyz{|}~€®‚„…€´€Â€·€0€»€½_Bring All to FrontÒ6µ8_arrangeInFront:€Óqr¸tº€É€¿€ÅØwxyz{|}¼€½‚¿„…€Æ€Ç€·€È€»€½Óz‡ÃŠÅ~‰fPrint &QpÒ6É8Vprint:€ÓqrÌt΀΀¿€ËØwxyz{|}¼€Ñ‚Ó„…€Æ€Ì€·€Í€»€½kPage Setup &QPÒ6Ù8^runPageLayout:€ÓqrÜtÞ€Ô€¿€ÐØwxyz{|}à€á‚ã„…€Ñ€Ò€·€Ó€»€½Óz‡çŠéµ‰¶_NewApplication HelpQ?Ò6í8YshowHelp:€Óqrðtò€Ù€¿€ÖØwxyz{|}ô€õ‚„…€×€Ø€·€0€»€½Ôz¾‡ûüŠþ†ˆ‰‡ZClear MenuÒ68_clearRecentDocuments:€Ôqr0t]NSDestination€€ß€¿€ÛØwxyz{|} € ‚ „…€Ü€Ý€·€Þ€»€½Ôz¾‡Š“¡‰”_Quit NewApplicationQqÒ68Zterminate:€Ôqr0t€€ã€¿€á×wxz{|} !‚„…€Ü€â€·€0€»€½_About NewApplicationÒ6(8_orderFrontStandardAboutPanel:€Ôqr0,t.€€è€¿€åØwxyz{|} 12‚4„…€Ü€æ€·€ç€»€½[Hide OthersQh_hideOtherApplications:Ôqr0<t>€€í€¿€êØwxyz{|} €A‚C„…€Ü€ë€·€ì€»€½_Hide NewApplicationUhide:Ôqr0JtL€€ñ€¿€ïØwxyz{|} €O‚„…€Ü€ð€·€0€»€½XShow All_unhideAllApplications:ÓqrWtY€÷€¿€óØwxyz{|}[€\‚^„…€ô€õ€·€ö€»€½Óz‡bŠd½‰¾SCutQxÒ6h8Tcut:€Óqrktm€ü€¿€ùØwxyz{|}[€p‚r„…€ô€ú€·€û€»€½UPasteQvÒ6x8Vpaste:€Óqr{t}€¿€þØwxyz{|}[€€‚‚„…€ô€ÿ€·€»€½TRedoQZÒ6ˆ8Uredo:€Óqr‹t€¿Øwxyz{|}[€‚’„…€ô€·€»€½ZSelect AllQaÒ6˜8ZselectAll:€Óqr›t €¿Øwxyz{|}[€ ‚¢„…€ô €· €»€½TUndoQzÒ6¨8Uundo:€Óqr«t­€¿ Øwxyz{|}[€°‚²„…€ô€·€»€½TCopyQcÒ6¸8Ucopy:€Óqr»t½€¿Øwxyz{|}¿€À‚„…€·€»€½Óz‡ÆŠÈ¨‰©iSpelling &Q:Ò6Ì8_showGuessPanel:€ÓqrÏtÑ€¿Øwxyz{|}¿€Ô‚Ö„…€·€»€½^Check SpellingQ;Ò6Ü8^checkSpelling:€ÓqrßtဿØwxyz{|}¿€ä‚„…€·€0€»€½_Check Spelling as You Type_toggleContinuousSpellChecking:Óqrìtî$€¿!Øwxyz{|}¼€ñ‚ó„…€Æ"€·#€»€½UCloseQw]performClose:Óqrútü(€¿&Øwxyz{|}[€ÿ‚„…€ô'€·€0€»€½VDeleteWdelete:Óqrt ,€¿*Øwxyz{|}~€ ‚„…€´+€·€0€»€½TZoom\performZoom:Óqrt2€¿.Ùwxyz{|}€‚„…oUNSTag/0€·1€»€½Óz‡ Š"‰ÃeFind &Qf_performFindPanelAction:Óqr't)7€¿4Ùwxyz{|}€,‚.„…X/5€·6€»€½YFind NextQgÓqr4t6<€¿9Ùwxyz{|}€9‚;„…„/:€·;€»€½]Find PreviousQGÓqrAtCA€¿>Ùwxyz{|}€F‚H„…K/?€·@€»€½_Use Selection for FindQeÓqrOtQF€¿CØwxyz{|}€T‚V„…/D€·E€»€½_Jump to SelectionQj_centerSelectionInVisibleArea:Óqr]t_K€¿HØwxyz{|}[1b‚d„…€ôI€·J€»€½_Paste and Match StyleQV_pasteAsPlainText:Ôqršlmn€-OPMÒ23q€N_SimpleOBEXServer_serverDirectoryFieldÒ:;uv£v§?_NSNibOutletConnectorÔqr™ymn€$RPM_serviceNameFieldÔqrmn€ATPM[startButtonÔqrn…m‡M\PVÙ‰Š‹ŒŽ¦¦¦¦¦–—¦™_NSPreservesSelection_"NSClearsFilterPredicateOnInsertion_NSFilterRestrictsInsertion_NSSelectsInsertedObjects_NSAvoidsEmptySelection__NSManagedProxyZNSEditable^NSDeclaredKeys Y[ WÒG›w¡œX€:_capitalizedStringÑ ZÒ:;¢Ž¢Ž?Ò:;¤¥¤¥¦§?_NSArrayController_NSObjectController\NSControllerWcontentתq«¬rn®¯X°±‡YNSKeyPath_NSNibBindingConnectorVersionYNSBindingM`^_aV_contentArray: mConnections\contentArray\mConnectionsÒ:;·¸£¸§?_NSNibBindingConnectorÔqr‡»mnVcPM_mConnectionsControllerتq«¬Àr‡ÂÃXÄűÈYNSOptionsVgefha€_(value: arrangedObjects.capitalizedStringUvalue_!arrangedObjects.capitalizedStringÓGûÌУÍÍÍlll£ÑÒÓijk€d_NSRaisesForNotApplicableKeys_NSConditionallySetsEditable_&NSAllowsEditingMultipleValuesSelectionÔqrÛmI€AnP€_initialFirstResponderÔqr­ámn€RpPMWlogViewÔqrnçtMr€¿€A_startOrStopServer:ÒGì@¯Síîá«Ñòó™n˜­ùsü>ýþÿI ›}=Îî)‡. Éu6L~à‰È_ô­  ¼#$Þu„º[+CYm/01œtvò½7šQ¿>_tv€Áwz€$M€€R‘€&€ê€’Š€€Ü€1€þ€7€Ë!4™V€å.ƒ€Û¢¥ª«€š€³²9œ€ï€´€Ñ·€€€ €× ‹*€Æ{¿€Ð€|€á€…€Å€ô >€ó€ùŽº•€5€N€€Ö°/€-€AC–HÅ×wxz{|}¼C‚„…€Æu€·€0€»€½VRevertÚwxyz{J|}K €‚¦„…¦\NSIsDisabled]NSIsSeparator€Ü€0€· €0€»€½ Øwxyz{|}¼€V‚X„…€Æx€·y€»€½SNewQnÚwxy^z{|}7#€¼a‚„…fYNSSubmenu{€Æ|€·€0€»€½}Ôz¾‡hiŠk¸Ä‰¹TFile^submenuAction:Ò6l8€ÒGqw«òý ÿî /íùκw€ƒŠ!‹Žt‘€Ë€Å€:Øwxyz{|}¼€€‚‚„…€Æ€·‚€»€½WOpen...QoÚwxy^z{|}7¼€ôŠ‚„…€Æ€×„€·€0€»€½…[Open RecentÒ68€ÒG”w¡ò€Ö€:__NSRecentDocumentsMenuÒ:;™w¢w?Úwxyz{J|}K¼€‚¦„…¦€Æ€0€· €0€»€½ Øwxyz{|}¼€¥‚§„…€ÆŒ€·€»€½TSaveQsØwxyz{|}¼€®‚°„…€Æ€·€»€½hSave As &QSÚwxyz{J|}K¼€‚¦„…¦€Æ€0€· €0€»€½ Úwxyz{J|}K €‚¦„…¦€Ü€0€· €0€»€½ ^NewApplicationÒGÉw«1>þî>.L+€á•–’™v€ê€å€ï €Û€:Úwxyz{J|}K €‚¦„…¦€Ü€0€· €0€»€½ Øwxyz{|} €á‚ã„…€Ü—€·˜€»€½lPreferences &Q,Úwxy^z{|}7 €ë‚„…ð€Üœš€·€0€»€½›XServicesÔz¾‡óôŠöŸ‰žÒ6ñ8€ÒGúw €:__NSServicesMenuÚwxyz{J|}K €‚¦„…¦€Ü€0€· €0€»€½ \_NSAppleMenuÚwxy^z{|}7#€  ‚„…{€Ü£€·€0€»€½¤Úwxy^z{|}7[€¿‚„…€ô¦€·€0€»€½§XSpellingÒGw£½Ñá€:Úwxyz{J|}K[€‚¦„…¦€ô€0€· €0€»€½ Úwxy^z{|}7#€~,‚„…1{€´¬€·€0€»€½­VWindowÒ628€ÒG6w¤u 7«€³*°€Á€:Úwxyz{J|}K~€‚¦„…¦€´€0€· €0€»€½ ^_NSWindowsMenuÚwxy^z{|}7#€àI‚„…N{€Ñ³€·€0€»€½´THelpÒGQw¡ހЀ:Úwxyz{J|}K[€‚¦„…¦€ô€0€· €0€»€½ XMainMenuÒG_w¥ó0¢zº«²€:Úwxy^z{|}7#€[i‚„…n{€ô»€·€0€»€½¼TEditÒ6o8€ÒGsw¬}Y­m_ü$€þª€ó €ùH&·¿¥€:Úwxy^z{|}7[€„‚„…‰€ô/À€·€0€»€½ÁTFindÒ6Š8€ÒGŽw¥)6CQ.49>C€:[_NSMainMenuÒ:;—J¢J?ÒG™@¯S¼ ¿~¿¼#‰0‰t[¼_[ ¼ ¼0‰[œ¼¼ 0 ¼ #[[#„~# [s„I [¼~ó0[à_ v¼0 [[¼# ‰__ô¿~$‰‰[ [€Æ€Ü€´€Æ{€€€€N€ô€Æ€ €ô€Ü€Æ€Ü€Æ€¢€€ô€5€Æ€Æ/€Ü€€Ü/€Æ€Ü{€ô€ô{€…€´{/™€Ü«²€ô€€…€ƒ€ô€Æ€´z€€ô€Ñ€ €Ü€€Æº€Ü/€ô€ô€Æ{€Ü€€ € €×€´¿€€/¥€ô€Ü€ôÅÒGï@¯>0ó¼I™m¿íº6›œ}[nÑ#„Y>˜‰ù C½v07‡Îò$sÈtáü/ÿu)­ÞQàÉî=ýš­€€Az€Æ€€$€ù.t€Å9€1€5€þ€ôM{€…€ó–€Û€€‘·‹>€ºª°V€Ëw¿€€€N²&ŽŠ€|¥4 €ÐC€Ñ€š!€7€€-/€RÅÒG0@¯>123356789:;<=>?@ABCDEFGHIJKLMNOPQR3TUVWXYZ[\]^_`abc3efghi3klmnÉÊËËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéËêëìíîïðñòóôõö÷øùËúûüýþËÿÅÒ6q8\File's Owner€Ò6!8€[NSMenuItem9Ò6w8R10€Ò6z8Q6€[NSMenuItem3]NSTextField21VNSBox1\NSMenuItem11[NSMenuItem8Ò6]8€S121T1111\NSTextField2Ò6‡8Q2€\NSMenuItem12Ò6‹8Q3€[NSMenuItem2[NSMenuItem6]NSScrollView1\NSMenuItem10Ò6’8Q5€Ò6•8Q9€[NSMenuItem7]NSScrollView2Ò6š8Q1€[NSMenuItem4Ò6ž8Q8€Ò6¡8Q7€_NSTextField111111\NSMenuItem13[NSMenuItem1Ò6‡8€^NSTableColumn1Ò6š8€\NSTextField1ÒG­@ ÅÒG°@ ÅÒG³@¯{Uîfºbg$ZRM[Q6ùLQ#œýhôt>«JS/>cT‡_nW=Y^H­˜ò L u[_­dó_Þ„KN~ÎVl0½}kmXe+Ià)`CnO¿.› PíšÈüþ™v îIòm0Yÿ]a1É7áÑijs‰¼u\ª€ý!Q€Å=S¿¢€î€Õ™€ô€é9‘€ïC{€5€U€×€N–€Á€Ä€òŽ€êB€øVHq.€7€ó)€² €€Ö€Ü€Ï*€|-€RGz€ €Ð€…/€Ê²€Ú€´€Ëmº¥€þdo€á L €«œ€Ñ43>M€à€å€1ƒ·€ät€-€&’€$€‹v€Àw€Û€ù€Š%€A8•€š°]b€€€Æ€³ ÅÒG 1@¯{ 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~  € ‚ ƒ „ … † ‡ ˆ ‰ Š ‹ Œ Ž ‘ ’ “ ” • – — ˜ ™ š œ ž Ÿ   ¡ ¢ £ ¤ ¥ ¦ § ¨ © ª « ¬      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmno€hpqrstuvwxyz{|}~€Åœ²IéNÊ꨾8™ƒ©˜¢J–›ÝžHý}àV¯P†Ë°üÌ š´Þ Æ%Õ~9zÅâÀÇáÍSoíŸWg‹M³ £¸»­ :µè•‚j§¡æŽ¹¬‘Ø|®p×ï¤ÖìK'Rˆ«¼OÃßÉÄî\¿½ÎÏQÁÒG %w €:ÒG (@ ÅÒG +@ ÅÒ:; . /¢ /?^NSIBObjectData#,1:LQVdftzÅ×ó*FSfm{‰—±½ËÕÜßâåèëîñôöùûþ  "$&/9GIR[fmˆ‘šŸ®·ÊÌÕàãåç,>HVdq~ˆ•Ÿ«­¯±³µ·¹»½ÂÄÆáû    % 0 B J L N P R [ d f h j l n Ÿ « ¸ Ê Ö à î ü           $ ' ) + @ B D F H J S ` b d f h j l n ‹ • ž ¥ § © « ¬ ¯ ± ³ Ë ì  ! / ; H J L N P R T Y ^ s „ ‹ ’ ›   ¢ ¤ § ´ ½ Â É Þ æ ò ÿ       + 8 @ L N W \ q s u w y Œ ™ œ ž § ° Â Ï Ø å ñ þ    8 : < > ? A C \ • — ™ › Ÿ   ¢ « ° Â × Ù Û Ý ß õ "$.KMOQRTVo”–˜šœžŸ¡¢¿ÁÃÅÆÈÊâ   (UWY[]_abdmprt…‡‰‹¢«²»ÂÑÙð÷"/=?HQWtvxz{}–ËÝðÿ"4JLNPRTVXacegly{„œª³¾ÇÞø0579<IKx†’›§©«­¯±³µ·¹»ÄËÍÏÑÓ !*468:<>@BDFHKMVY[]†“ž¨º¼¾ÀÂÄÆÉËÍçü!+0246KYcvxz|~‹‘šœ¥®Êßèëíïøý ,CKex‹ ¢¥§©«­¯±¾ÆËÍÏÔÖØÚïñóõ÷*,.02FOTansuw|~€‚Ž›¡¨ª³¸ÏÜåòþ6CMZ\^fot}†‘²½ÆÏÑÓØÚÜÞàø$MWY[]_afhƒ¦¯¼ÙÛÝßàâäü!#%')‡˜Ÿ¡¤Ýêý   "5>IKMOQSU~€‚„†ˆŠŒŽ—šœžé,8Wa|žª¶ÅÊÌÎÓÕÖßáæèêìîðòü!#%')+TVXZ\^`bdnw€”©«­¯±ÈÑÚèñöøúü%4AL[hs~€‚‡ŒŽ•—´¶¸º¼¾ÃÙæòô   #,7Khjlnprwz‘“•—°¹¾Ìõ÷ùþÿ   ! # % ' ) + H J L N P R g i k m o y † ‹ – £ ° Ç è ê ì î ð ò ÷ ù!!:!!@!B!C!E!J!L!h!q!t!v!x!Ž!¦!½!Õ!à!ý"" ""'".";"A"J"›""Ÿ"¡"£"¥"§"©"«"­"¯"±"³"µ"·"º"½"À"Ã"Æ"É"Ì"Ï"Ò"Õ"Ø"Û"Þ"á"ä"ç"ê"í"ð"ó"ö"ù"ü"ÿ#####"#$#&#(#I#P#^#r#z#„##œ#ž#£#¨#ª#¬#®#°#²#Ã#Ï#Ò#Õ#Ø#Û#ä#æ#ó$$$$$$"$+$2$E$Y$f$h$j$l$$ˆ$$˜$¡$·$¹$Â$É$á$ð$ý$ÿ%%%$%&%(%*%,%.%0%E%N%`%b%o%q%s%u%–%˜%š%œ%ž% %¢%¯%²%µ%¸%Å%Ç%Ð%×%Ù%æ%è%ê%ì& &&&&&&&0&2&;&J&L&Y&[&]&_&€&‚&„&†&ˆ&Š&Œ&™&œ&Ÿ&¢&¸&º&Ã&Í&Ï&Ü&Þ&à&â'''' ' ' '' '#'&')','7'@'X'Z'k'y'{'}'''¢'¤'¦'¨'ª'¬'®'¿'Â'Å'È'Ë'á'ã'ì'÷'ù( ( ((((/(1(3(5(7(9(;(R([({(}(Ž((’(”(–(·(¹(¾(À(Â(Ä(Æ(È(Ô(Ö(ï))))))))+)-)/)1)3)5)K)Q)b)d)f)h)j)‹)))‘)“)•)—) )¹)Æ)È)Ê)Ì)í)ï)ñ)ó)õ)÷)ù** * *****#*%*2*4*6*8*Y*[*]*_*a*c*e*k*m*v*}**Œ**‘*“*´*¶*¸*º*½*¿*Á*Æ*È*Ñ*×*Ù*æ*é*ë*î++++++++(+*+3+>+@+M+P+R+U+v+x+{+}+€+‚+„+‰+‹+”+š+œ+©+¬+®+±+Ò+Ô+×+Ù+Ü+Þ+à+å+ç+ð+ö+ø,,, , ,.,1,4,6,9,;,=,J,M,P,S,f,h,q,ƒ,…,’,•,—,š,»,¾,Á,Ã,Æ,È,Ê,Ù,Û,ä,ó,õ---- -+-.-1-3-5-7-9-V-w-„-‡-‰-Œ-­-¯-²-´-·-¹-»-Á-Ã-Ñ-Þ-á-ã-æ.. . ......#.0.3.5.8.Y.[.^.`.b.d.f.k.x.….ˆ.Š..².¸.».¾.À.Ã.Å.Ç.Ô.×.Ú.Ý.è.ê//////>/A/D/F/I/K/M/W/Y/f/i/k/n/“/–/™/›/ž/ /¢/°/²/¿/Â/Ä/Ç/ì/ï/ò/ô/÷/ù/û/ý000%0(0*0-0N0Q0T0V0Y0[0]0q0s0“0 0£0¥0¨0É0Ë0Î0Ð0Ó0Õ0×0ï0ñ111111!1*1,1/1B1Y1b1i1€1‘1“1–1™1œ1¯1À1Â1Å1È1Ë1×1è1ë1î1ñ1ô2202U2r22¦2¸2Ã2Ò2Ó2Ô2Õ2Ö2×2Ú2Ý2Þ2á2ê2í2ð2ò33 3333%3.3B3W3d3l3‰3“3²3¼3¿3Â3Å3È3Ë3Î3ë3ø4444-4>4A4D4G4J4c4„4Ž4‘4”4—4š44 4¢4Í4Ó4÷55 555555!5$5&5E5c5Œ55ž5 5£5¦5¨5À5Ñ5Ó5Ö5Ù5Ü5ä5õ5ø5û5ý5ÿ666Æ6É6Ì6Ï6Ñ6Ô6×6Ú6Ü6ß6á6ã6æ6é6ë6î6ð6ó6ö6ù6û6ý6ÿ77777 77777777!7$7'7)7+7.717476787:7=7?7A7C7E7H7K7N7P7S7V7X7Z7\7^7`7b7e7h7j7l7o7r7u7w7y7{7}7€7ƒ7†7ˆ7Š777“7–7™7œ7¹7»7¾7À7Â7Ä7Æ7Í7ö88888888888@8B8E8G8J8L8N8R8T8}8‡8Š8Œ88‘8“8•8—8š8«8®8±8´8·8¼8Ë8Ô8Ö8ß8ö8ù8ü8ÿ9999 99999989:9=9?9B9D9F9N9P9y9{9}9€9‚9„9†9ˆ9‹9—9 9¢9«9®9°9²9Ë9Ô9Ù::::: : : :::1:3:6:8:;:=:?:D:F:g:i:l:n:q:s:u:†:ˆ:±:³:µ:·:¸:º:¼:¾:¿:è:ê:ì:î:ï:ñ:ó:õ:ö;;;%;';*;-;0;3;6;8;:;<;?;A;C;l;n;p;r;s;u;w;y;z;›;; ;¢;¥;§;©;Â;Ä;í;ï;ò;õ;÷;ù;û;ý<< <<< <#<&>>>>> >>A>D>F>I>K>M>O>Q>T>Y>b>e>g>i>’>”>–>˜>™>›>>Ÿ> >©>²>½>À>Ã>Æ>É>Ì>Î>÷>ú>ü>ÿ????? ????#?A@ACAEAHAJALAOARATAVAXA[AdAãAåAçAêAìAîAðAòAõAøAûAýBBBBBB BBBBBBBBB!B$B'B*B-B/B2B5B8B;B>B@BCBFBHBJBLBOBRBUBXB[B]B`BcBfBhBkBmBoBrBtBwBzB|BBB„BC CCCCCCC!C$C'C*C-C0C3C6C9CK@KBKEKGKIKKKMKOKXKYK[KdKeKhKqKrKuK~Kƒ 0K’lightblue-0.3.2/examples/LightAquaBlue/SimpleOBEXServer/Info.plist0000644000076500007650000000152610744005727024372 0ustar beabea00000000000000 CFBundleDevelopmentRegion English CFBundleExecutable ${EXECUTABLE_NAME} CFBundleIconFile CFBundleIdentifier com.yourcompany.SimpleOBEXServer CFBundleInfoDictionaryVersion 6.0 CFBundleName ${PRODUCT_NAME} CFBundlePackageType APPL CFBundleSignature ???? CFBundleVersion 1.0 NSMainNibFile MainMenu NSPrincipalClass NSApplication lightblue-0.3.2/examples/LightAquaBlue/SimpleOBEXServer/main.m0000644000076500007650000000037510744005727023525 0ustar beabea00000000000000// // main.m // SimpleOBEXServer // // Created by Bea on 8/01/08. // Copyright __MyCompanyName__ 2008. All rights reserved. // #import int main(int argc, char *argv[]) { return NSApplicationMain(argc, (const char **) argv); } lightblue-0.3.2/examples/LightAquaBlue/SimpleOBEXServer/SimpleOBEXServer.h0000644000076500007650000000113610744005727025666 0ustar beabea00000000000000/* SimpleOBEXServer */ #import #import @class BBBluetoothOBEXServer; @class IOBluetoothUserNotification; @interface SimpleOBEXServer : NSObject { BluetoothSDPServiceRecordHandle mServiceRecordHandle; IOBluetoothUserNotification *mChannelNotification; NSMutableDictionary *mOBEXServers; IBOutlet id logView; IBOutlet id serverDirectoryField; IBOutlet id serviceNameField; IBOutlet id startButton; NSMutableArray *mConnections; IBOutlet id mConnectionsController; } - (IBAction)startOrStopServer:(id)sender; @end lightblue-0.3.2/examples/LightAquaBlue/SimpleOBEXServer/._SimpleOBEXServer.m0000644000076500000000000000012210750623106026427 0ustar beawheel00000000000000Mac OS X  2 RTEXTlightblue-0.3.2/examples/LightAquaBlue/SimpleOBEXServer/SimpleOBEXServer.m0000644000076500007650000003316510750623106025674 0ustar beabea00000000000000// // SimpleOBEXServer.m // // This shows how BBBluetoothOBEXServer can be used to run a simple OBEX // server that allows clients to send and retrieve files. // // To receive files through this server, you'll need to get the remote device // to specifically send them to the channel that's being used by this OBEX // service -- that is, the channel number that's displayed when this example // prints the "Listening for connections on channel X" message. // // If you can't specify a channel when sending files from the remote device -- // for example, the Mac OS X "Send File..." Bluetooth utility only lets you // select a device, and not the channel -- this probably means the device is // just sending it to the first appropriate OBEX service that it finds. So if // you switch off all other OBEX services, e.g. the Mac OS X built-in OBEX // services, this demo's OBEX service should automatically receive the files. // // To switch off the built-in OBEX services on Mac OS 10.5 go to // System Preferences -> Sharing and uncheck the "Bluetooth Sharing" checkbox. // This disables the built-in OBEX Object Push and OBEX File Transfer services. // // On Mac OS 10.4, go to System Preferences -> Bluetooth and click the // "Sharing" tab. Uncheck the "On" checkboxes for the "Bluetooth File Transfer" // and "Bluetooth File Exchange" services. // // On Mac OS 10.3, go to System Preferences -> Bluetooth, and go to the "File // Exchange" tab. For "When receiving items", select "Refuse all", and also // uncheck "Allow other devices to browse files on this computer". // // If the device that's sending files is a Mac, then you can can specify the // channel ID programmatically: the included SimpleOBEXClient example, as well // as Apple's OBEXSample (in /Developer/Examples/Bluetooth) and the // IOBluetooth OBEXFileTransferServices class all let you connect to a // specific channel when connecting to an OBEX service. // #import "SimpleOBEXServer.h" #include #include #include @implementation SimpleOBEXServer - (id)init { self = [super init]; mOBEXServers = [[NSMutableDictionary alloc] initWithCapacity:0]; mConnections = [[NSMutableArray alloc] initWithCapacity:0]; mServiceRecordHandle = 0; return self; } - (void)awakeFromNib { [serverDirectoryField setStringValue:[[NSFileManager defaultManager] currentDirectoryPath]]; [[logView window] center]; //[BBBluetoothOBEXServer setDebug:YES]; } - (void)log:(NSString *)text { [logView insertText:text]; [logView insertText:@"\n"]; } - (NSNumber *)getSizeOfFile:(NSString *)filePath { NSFileManager *fileManager = [NSFileManager defaultManager]; NSDictionary *fileAttributes = [fileManager fileAttributesAtPath:filePath traverseLink:YES]; if (fileAttributes != nil) return [fileAttributes objectForKey:NSFileSize]; return nil; } // // Starts or stops the server. // - (IBAction)startOrStopServer:(id)sender { if (!mChannelNotification) { [self log:@"Starting server..."]; // Advertise an OBEX service so that other devices can find us when // they look for available services on our local Bluetooth device. BluetoothRFCOMMChannelID serverChannelID; BluetoothSDPServiceRecordHandle serviceRecordHandle; IOReturn result = [BBServiceAdvertiser addRFCOMMServiceDictionary:[BBServiceAdvertiser objectPushProfileDictionary] withName:[serviceNameField stringValue] UUID:nil channelID:&serverChannelID serviceRecordHandle:&serviceRecordHandle]; if (result != kOBEXSuccess) { [self log:[NSString stringWithFormat:@"Error advertising service (%d)", result]]; return; } // keep the service record handle so the service can be unregistered // later mServiceRecordHandle = serviceRecordHandle; // Tell IOBluetoothRFCOMMChannel to call channelOpened:channel: // when a client connects on , which is the channel // ID on which the OBEX service was advertised. mChannelNotification = [IOBluetoothRFCOMMChannel registerForChannelOpenNotifications:self selector:@selector(channelOpened:channel:) withChannelID:serverChannelID direction:kIOBluetoothUserNotificationChannelDirectionIncoming]; [mChannelNotification retain]; [self log:[NSString stringWithFormat:@"Listening for connections on channel %d", serverChannelID]]; [startButton setTitle:@"Stop server"]; } else { [self log:@"Stopping server..."]; // Stop listening for RFCOMM client connections, and remove the // service that was previously advertised. [mChannelNotification unregister]; [mChannelNotification release]; mChannelNotification = nil; [BBServiceAdvertiser removeService:mServiceRecordHandle]; mServiceRecordHandle = 0; [self log:@"Server stopped."]; [startButton setTitle:@"Start server"]; } } // // Called by IOBluetoothRFCOMMChannel when a RFCOMM client has connected. // The provided is the newly opened RFCOMM channel. // - (void)channelOpened:(IOBluetoothUserNotification *)notification channel:(IOBluetoothRFCOMMChannel *)channel { NSString *remoteAddress = [[channel getDevice] getAddressString]; [mConnectionsController addObject:remoteAddress]; [self log:[NSString stringWithFormat:@"=> Client connected from %@", remoteAddress]]; // create a BBBluetoothOBEXServer that will run on this new RFCOMM channel BBBluetoothOBEXServer *server = [BBBluetoothOBEXServer serverWithIncomingRFCOMMChannel:channel delegate:self]; // Tell IOBluetoothRFCOMMChannel to call channelClosed:channel: when this // channel is closed, and add the new server to a dictionary so the object // can be retained until the channel is closed. [channel registerForChannelCloseNotification:self selector:@selector(channelClosed:channel:)]; [mOBEXServers setObject:server forKey:channel]; // start the server [server run]; } // // Called by IOBluetoothRFCOMMChannel when a RFCOMM channel is closed (i.e when // a client has disconnected). // - (void)channelClosed:(IOBluetoothUserNotification *)notification channel:(IOBluetoothRFCOMMChannel *)channel { NSString *remoteAddress = [[channel getDevice] getAddressString]; [mConnectionsController removeObject:remoteAddress]; [self log:[NSString stringWithFormat:@"=> Connection closed from %@", remoteAddress]]; // the BBBluetoothOBEXServer running on this particular channel can now // be released [mOBEXServers removeObjectForKey:channel]; } #pragma mark - #pragma mark BBBluetoothOBEXServer delegate methods // The following delegate methods are called by the BBBluetoothOBEXServer object // when an OBEX server event occurs. This is where you can actually respond and // perform operations when a client connects, sends files, etc. // // For the sake of simplicity, the delegate methods relating to SetPath and // Put-Delete requests have not been implemented here, which means that this // server won't support these particular requests. // // Called when an error occurs on the server. // - (void)server:(BBBluetoothOBEXServer *)server errorOccurred:(OBEXError)error description:(NSString *)description { [self log:@"----------"]; [self log:[NSString stringWithFormat:@"Server error: %@ (%d)", description, error]]; [self log:@"----------"]; } // // Called when a Connect request is received. // - (BOOL)server:(BBBluetoothOBEXServer *)server shouldHandleConnectRequest:(BBOBEXHeaderSet *)requestHeaders { [self log:@"Got CONNECT request"]; return YES; } // // Called when a Connect request is finished. // - (void)serverDidHandleConnectRequest:(BBBluetoothOBEXServer *)server { [self log:@"Finished handling CONNECT request"]; } // // Called when a Disconnect request is received. // - (BOOL)server:(BBBluetoothOBEXServer *)server shouldHandleDisconnectRequest:(BBOBEXHeaderSet *)requestHeaders { [self log:@"Got DISCONNECT request"]; return YES; } // // Called when a Disconnect request is finished. // - (void)serverDidHandleDisconnectRequest:(BBBluetoothOBEXServer *)server { [self log:@"Finished handling DISCONNECT request"]; } // // Called when a Put request is received. // - (NSOutputStream *)server:(BBBluetoothOBEXServer *)server shouldHandlePutRequest:(BBOBEXHeaderSet *)requestHeaders { // The client should have sent some information about the file, such as // its name and length. NSString *incomingFileName = [requestHeaders valueForNameHeader]; if (!incomingFileName) incomingFileName = @"unnamed_file"; [self log:[NSString stringWithFormat:@"Got PUT request, client wants to send file '%@'", incomingFileName]]; // See if the client told us how big the file is. // kOBEXHeaderIDLength and other header IDs are defined in OBEX.h in // IOBluetooth.framework. if ([requestHeaders containsValueForHeader:kOBEXHeaderIDLength]) { [self log:[NSString stringWithFormat:@"Incoming file is %d bytes", [requestHeaders valueForLengthHeader]]]; } // IOBluetoothGetUniqueFileNameAndPath() is a useful function in // for creating a unique file name. NSString *filePath = IOBluetoothGetUniqueFileNameAndPath(incomingFileName, [serverDirectoryField stringValue]); [self log:[NSString stringWithFormat:@"File will be saved to <%@>", filePath]]; // Now open a stream that will write to the file. It doesn't have to be // retained because the server will retain it for the duration of the // request. NSOutputStream *stream = [NSOutputStream outputStreamToFileAtPath:filePath append:NO]; [stream open]; // must open stream or else it can't be used! return stream; } // // Called each time data is received during a Put request. // - (BOOL)server:(BBBluetoothOBEXServer *)server didReceiveDataOfLength:(unsigned)length isLastPacket:(BOOL)isLastPacket { [self log:[NSString stringWithFormat:@"Got another %d bytes", length]]; return YES; } // // Called when a Put request is finished. // - (void)server:(BBBluetoothOBEXServer *)server didHandlePutRequestForStream:(NSOutputStream *)outputStream requestWasAborted:(BOOL)aborted { [self log:@"Finished PUT request"]; [outputStream close]; } // // Called when a Get request is received. // - (NSInputStream *)server:(BBBluetoothOBEXServer *)server shouldHandleGetRequest:(BBOBEXHeaderSet *)requestHeaders { // See what file the client wants to retrieve. NSString *fileName = [requestHeaders valueForNameHeader]; if (!fileName) { [self log:@"Got GET request but client didn't send a filename, refusing request..."]; return NO; } [self log:[NSString stringWithFormat:@"Got GET request, client wants to get file '%@'", fileName]]; NSString *filePath = [[serverDirectoryField stringValue] stringByAppendingPathComponent:fileName]; if (![[NSFileManager defaultManager] fileExistsAtPath:filePath]) { [self log:[NSString stringWithFormat:@"Cannot find file <%@>, refusing request...", filePath]]; // You can use setResponseCodeForCurrentRequest: to set a more specific // response; otherwise if you return NO, the default response will // be 'Forbidden'. [server setResponseCodeForCurrentRequest:kOBEXResponseCodeNotFoundWithFinalBit]; return NO; } // Use the response headers to inform the client of the file's size so it // knows how much data is expected. NSNumber *fileSize = [self getSizeOfFile:filePath]; if (fileSize) { BBMutableOBEXHeaderSet *responseHeaders = [BBMutableOBEXHeaderSet headerSet]; [responseHeaders setValueForLengthHeader:[fileSize unsignedIntValue]]; [server addResponseHeadersForCurrentRequest:responseHeaders]; } // Now open a stream that will read from the file. It doesn't have to be // retained because the server will retain it for the duration of the // request. NSInputStream *stream = [NSInputStream inputStreamWithFileAtPath:filePath]; [stream open]; // must open stream or it can't be used! return stream; } // // Called each time data is sent for a Get request. // - (void)server:(BBBluetoothOBEXServer *)server didSendDataOfLength:(unsigned)length { [self log:[NSString stringWithFormat:@"Sent another %d bytes", length]]; } // // Called when a Get request is finished. // - (void)server:(BBBluetoothOBEXServer *)server didHandleGetRequestForStream:(NSInputStream *)inputStream requestWasAborted:(BOOL)aborted { [self log:@"Finished GET request"]; [inputStream close]; } - (void)dealloc { if (mServiceRecordHandle != 0) [BBServiceAdvertiser removeService:mServiceRecordHandle]; [mChannelNotification release]; [mOBEXServers release]; [super dealloc]; } @end lightblue-0.3.2/examples/LightAquaBlue/SimpleOBEXServer/SimpleOBEXServer.xcodeproj/0000755000076500007650000000000010770565176027520 5ustar beabea00000000000000lightblue-0.3.2/examples/LightAquaBlue/SimpleOBEXServer/SimpleOBEXServer.xcodeproj/project.pbxproj0000644000076500007650000002663410750622660032575 0ustar beabea00000000000000// !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 42; objects = { /* Begin PBXBuildFile section */ 817D19590D34C76C00469B5D /* SimpleOBEXServer.m in Sources */ = {isa = PBXBuildFile; fileRef = 817D19580D34C76C00469B5D /* SimpleOBEXServer.m */; }; 817D19660D34C8BF00469B5D /* LightAquaBlue.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 817D19650D34C8BF00469B5D /* LightAquaBlue.framework */; }; 817D220E0D3DA9F700469B5D /* IOBluetooth.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 817D220D0D3DA9F700469B5D /* IOBluetooth.framework */; }; 8D11072A0486CEB800E47090 /* MainMenu.nib in Resources */ = {isa = PBXBuildFile; fileRef = 29B97318FDCFA39411CA2CEA /* MainMenu.nib */; }; 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */; }; 8D11072D0486CEB800E47090 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; settings = {ATTRIBUTES = (); }; }; 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ 089C165DFE840E0CC02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = ""; }; 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; 13E42FB307B3F0F600E4EEF1 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = /System/Library/Frameworks/CoreData.framework; sourceTree = ""; }; 29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 29B97319FDCFA39411CA2CEA /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = English.lproj/MainMenu.nib; sourceTree = ""; }; 29B97324FDCFA39411CA2CEA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; }; 29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; 32CA4F630368D1EE00C91783 /* SimpleOBEXServer_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SimpleOBEXServer_Prefix.pch; sourceTree = ""; }; 817D19570D34C76C00469B5D /* SimpleOBEXServer.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = SimpleOBEXServer.h; sourceTree = ""; }; 817D19580D34C76C00469B5D /* SimpleOBEXServer.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = SimpleOBEXServer.m; sourceTree = ""; }; 817D19650D34C8BF00469B5D /* LightAquaBlue.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = LightAquaBlue.framework; path = /Library/Frameworks/LightAquaBlue.framework; sourceTree = ""; }; 817D220D0D3DA9F700469B5D /* IOBluetooth.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOBluetooth.framework; path = /System/Library/Frameworks/IOBluetooth.framework; sourceTree = ""; }; 8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; 8D1107320486CEB800E47090 /* SimpleOBEXServer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SimpleOBEXServer.app; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 8D11072E0486CEB800E47090 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */, 817D19660D34C8BF00469B5D /* LightAquaBlue.framework in Frameworks */, 817D220E0D3DA9F700469B5D /* IOBluetooth.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 080E96DDFE201D6D7F000001 /* Classes */ = { isa = PBXGroup; children = ( ); name = Classes; sourceTree = ""; }; 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */ = { isa = PBXGroup; children = ( 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */, ); name = "Linked Frameworks"; sourceTree = ""; }; 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */ = { isa = PBXGroup; children = ( 29B97324FDCFA39411CA2CEA /* AppKit.framework */, 13E42FB307B3F0F600E4EEF1 /* CoreData.framework */, 29B97325FDCFA39411CA2CEA /* Foundation.framework */, ); name = "Other Frameworks"; sourceTree = ""; }; 19C28FACFE9D520D11CA2CBB /* Products */ = { isa = PBXGroup; children = ( 8D1107320486CEB800E47090 /* SimpleOBEXServer.app */, ); name = Products; sourceTree = ""; }; 29B97314FDCFA39411CA2CEA /* SimpleOBEXServer */ = { isa = PBXGroup; children = ( 080E96DDFE201D6D7F000001 /* Classes */, 29B97315FDCFA39411CA2CEA /* Other Sources */, 29B97317FDCFA39411CA2CEA /* Resources */, 29B97323FDCFA39411CA2CEA /* Frameworks */, 19C28FACFE9D520D11CA2CBB /* Products */, ); name = SimpleOBEXServer; sourceTree = ""; }; 29B97315FDCFA39411CA2CEA /* Other Sources */ = { isa = PBXGroup; children = ( 32CA4F630368D1EE00C91783 /* SimpleOBEXServer_Prefix.pch */, 29B97316FDCFA39411CA2CEA /* main.m */, 817D19570D34C76C00469B5D /* SimpleOBEXServer.h */, 817D19580D34C76C00469B5D /* SimpleOBEXServer.m */, ); name = "Other Sources"; sourceTree = ""; }; 29B97317FDCFA39411CA2CEA /* Resources */ = { isa = PBXGroup; children = ( 8D1107310486CEB800E47090 /* Info.plist */, 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */, 29B97318FDCFA39411CA2CEA /* MainMenu.nib */, ); name = Resources; sourceTree = ""; }; 29B97323FDCFA39411CA2CEA /* Frameworks */ = { isa = PBXGroup; children = ( 817D220D0D3DA9F700469B5D /* IOBluetooth.framework */, 817D19650D34C8BF00469B5D /* LightAquaBlue.framework */, 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */, 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */, ); name = Frameworks; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ 8D1107260486CEB800E47090 /* SimpleOBEXServer */ = { isa = PBXNativeTarget; buildConfigurationList = C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "SimpleOBEXServer" */; buildPhases = ( 8D1107290486CEB800E47090 /* Resources */, 8D11072C0486CEB800E47090 /* Sources */, 8D11072E0486CEB800E47090 /* Frameworks */, ); buildRules = ( ); dependencies = ( ); name = SimpleOBEXServer; productInstallPath = "$(HOME)/Applications"; productName = SimpleOBEXServer; productReference = 8D1107320486CEB800E47090 /* SimpleOBEXServer.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 29B97313FDCFA39411CA2CEA /* Project object */ = { isa = PBXProject; buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "SimpleOBEXServer" */; hasScannedForEncodings = 1; mainGroup = 29B97314FDCFA39411CA2CEA /* SimpleOBEXServer */; projectDirPath = ""; targets = ( 8D1107260486CEB800E47090 /* SimpleOBEXServer */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ 8D1107290486CEB800E47090 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 8D11072A0486CEB800E47090 /* MainMenu.nib in Resources */, 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ 8D11072C0486CEB800E47090 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 8D11072D0486CEB800E47090 /* main.m in Sources */, 817D19590D34C76C00469B5D /* SimpleOBEXServer.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXVariantGroup section */ 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */ = { isa = PBXVariantGroup; children = ( 089C165DFE840E0CC02AAC07 /* English */, ); name = InfoPlist.strings; sourceTree = ""; }; 29B97318FDCFA39411CA2CEA /* MainMenu.nib */ = { isa = PBXVariantGroup; children = ( 29B97319FDCFA39411CA2CEA /* English */, ); name = MainMenu.nib; sourceTree = ""; }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ C01FCF4B08A954540054247B /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { COPY_PHASE_STRIP = NO; GCC_DYNAMIC_NO_PIC = NO; GCC_ENABLE_FIX_AND_CONTINUE = YES; GCC_MODEL_TUNING = G5; GCC_OPTIMIZATION_LEVEL = 0; INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Applications"; PRODUCT_NAME = SimpleOBEXServer; WRAPPER_EXTENSION = app; ZERO_LINK = YES; }; name = Debug; }; C01FCF4C08A954540054247B /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ARCHS = ( ppc, i386, ); GCC_GENERATE_DEBUGGING_SYMBOLS = NO; GCC_MODEL_TUNING = G5; INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Applications"; PRODUCT_NAME = SimpleOBEXServer; WRAPPER_EXTENSION = app; }; name = Release; }; C01FCF4F08A954540054247B /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNUSED_VARIABLE = YES; PREBINDING = NO; SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk; }; name = Debug; }; C01FCF5008A954540054247B /* Release */ = { isa = XCBuildConfiguration; buildSettings = { GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNUSED_VARIABLE = YES; PREBINDING = NO; SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "SimpleOBEXServer" */ = { isa = XCConfigurationList; buildConfigurations = ( C01FCF4B08A954540054247B /* Debug */, C01FCF4C08A954540054247B /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; C01FCF4E08A954540054247B /* Build configuration list for PBXProject "SimpleOBEXServer" */ = { isa = XCConfigurationList; buildConfigurations = ( C01FCF4F08A954540054247B /* Debug */, C01FCF5008A954540054247B /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = 29B97313FDCFA39411CA2CEA /* Project object */; } lightblue-0.3.2/examples/LightAquaBlue/SimpleOBEXServer/SimpleOBEXServer_Prefix.pch0000644000076500007650000000024310744005727027524 0ustar beabea00000000000000// // Prefix header for all source files of the 'SimpleOBEXServer' target in the 'SimpleOBEXServer' project // #ifdef __OBJC__ #import #endif lightblue-0.3.2/examples/._obex_ftp_client.py0000755000076500000000000000012210750770302021110 0ustar beawheel00000000000000Mac OS X  2 RTEXT!Rchlightblue-0.3.2/examples/obex_ftp_client.py0000755000076500000000000001627410750770302020712 0ustar beawheel00000000000000''' This example shows how to use the lightblue.obex.OBEXClient class to implement a basic client for the File Transfer Profile, which is a profile implemented on top of OBEX. This profile allows clients to: - send files - retrieve files - create and remove directories - change the current working directory You can find a copy of the profile specification at . ''' import sys import os import lightblue # This is the special Target UUID (F9EC7BC4-953C-11D2-984E-525400DC9E09) for the # File Transfer Profile, in byte form. You can get this in Python 2.5 using the # uuid module: # >>> print uuid.UUID('{F9EC7BC4-953C-11D2-984E-525400DC9E09}').bytes FTP_TARGET_UUID = '\xf9\xec{\xc4\x95<\x11\xd2\x98NRT\x00\xdc\x9e\t' # A note about Connection ID headers: # Notice that the FTPClient does not send the Connection ID in any of the # request headers, even though this is required by the File Transfer Profile # specs. This is because the OBEXClient class automatically sends the Connection # ID with each request if it received one from the server in the initial Connect # response headers, so you do not have to add it yourself. class FTPClient(object): def __init__(self, address, port): self.client = lightblue.obex.OBEXClient(address, port) def connect(self): response = self.client.connect({'target': FTP_TARGET_UUID}) if response.code != lightblue.obex.OK: raise Exception('OBEX server refused Connect request (server \ response was "%s")' % response.reason) def disconnect(self): print "Disconnecting..." response = self.client.disconnect() print 'Server response:', response.reason def ls(self): import StringIO dirlist = StringIO.StringIO() response = self.client.get({'type': 'x-obex/folder-listing'}, dirlist) print 'Server response:', response.reason if response.code == lightblue.obex.OK: files = self._parsefolderlisting(dirlist.getvalue()) if len(files) == 0: print 'No files found' else: print 'Found files:' for f in files: print '\t', f def cd(self, dirname): if dirname == os.sep: # change to root dir response = self.client.setpath({'name': ''}) elif dirname == '..': # change to parent directory response = self.client.setpath({}, cdtoparent=True) else: # change to subdirectory response = self.client.setpath({'name': dirname}) print 'Server response:', response.reason def put(self, filename): print 'Sending %s...' % filename try: f = file(filename, 'rb') except Exception, e: print "Cannot open file %s" % filename return response = self.client.put({'name': os.path.basename(filename)}, f) f.close() print 'Server response:', response.reason def get(self, filename): if os.path.isfile(filename): if raw_input("Overwrite local file %s?" % filename).lower() != "y": return print 'Retrieving %s...' % filename f = file(filename, 'wb') response = self.client.get({'name': filename}, f) f.close() print 'Server response:', response.reason def rm(self, filename): response = self.client.delete({'name': filename}) print 'Server response:', response.reason def mkdir(self, dirname): response = self.client.setpath({'name': dirname}, createdirs=True) print 'Server response:', response.reason def rmdir(self, dirname): response = self.client.delete({'name': dirname}) print 'Server response:', response.reason if response.code == lightblue.obex.PRECONDITION_FAILED: print 'Directory contents must be deleted first' def _parsefolderlisting(self, xmldata): """ Returns a list of basic details for the files and folders contained in the given XML folder-listing data. (The complete folder-listing XML DTD is documented in the IrOBEX specification.) """ if len(xmldata) == 0: print "Error parsing folder-listing XML: no xml data" return [] entries = [] import xml.dom.minidom import xml.parsers.expat try: dom = xml.dom.minidom.parseString(xmldata) except xml.parsers.expat.ExpatError, e: print "Error parsing folder-listing XML (%s): '%s'" % \ (str(e), xmldata) return [] parent = dom.getElementsByTagName('parent-folder') if len(parent) != 0: entries.append('..') folders = dom.getElementsByTagName('folder') for f in folders: entries.append('%s/\t%s' % (f.getAttribute('name'), f.getAttribute('size'))) files = dom.getElementsByTagName('file') for f in files: entries.append('%s\t%s' % (f.getAttribute('name'), f.getAttribute('size'))) return entries def processcommands(ftpclient): while True: input = raw_input('\nEnter command: ') cmd = input.split(" ")[0].lower() if not cmd: continue if cmd == 'exit': break try: method = getattr(ftpclient, cmd) except AttributeError: print 'Unknown command "%s".' % cmd print main.__doc__ continue if cmd == 'ls': if " " in input.strip(): print "(Ignoring path, can only list contents of current dir)" method() else: name = input[len(cmd)+1:] # file or directory name required method(name) def main(): """ Usage: python obex_ftp_client.py [address channel] If the address and channel are not provided, the user will be prompted to choose a service. Once the client is connected, you can enter one of these commands: ls cd (use '..' to change to parent, or '/' to change to root) put get rm mkdir rmdir exit Some servers accept "/" path separators within the or arguments. Otherwise, you will have to just send either a single directory or filename, without any paths. """ if len(sys.argv) > 1: address = sys.argv[1] channel = int(sys.argv[2]) else: # ask user to choose a service # a FTP service is usually called 'FTP', 'OBEX File Transfer', etc. address, channel, servicename = lightblue.selectservice() print 'Connecting to %s on channel %d...' % (address, channel) ftpclient = FTPClient(address, channel) ftpclient.connect() print 'Connected.' try: processcommands(ftpclient) finally: try: ftpclient.disconnect() except Exception, e: print "Error while disconnecting:", e pass if __name__ == "__main__": main() lightblue-0.3.2/examples/._simple_client.py0000644000076500000000000000012210743257062020575 0ustar beawheel00000000000000Mac OS X  2 RTEXTlightblue-0.3.2/examples/simple_client.py0000644000076500007650000000136210743257062020034 0ustar beabea00000000000000""" Shows how to send "Hello world" over a RFCOMM client socket. """ import lightblue # ask user to choose the device to connect to hostaddr = lightblue.selectdevice()[0] # find the EchoService advertised by the simple_server.py example echoservice = lightblue.findservices(addr=hostaddr, name="EchoService")[0] serviceport = echoservice[1] s = lightblue.socket() s.connect((hostaddr, serviceport)) s.send("Hello world!") print "Sent data, waiting for echo..." data = s.recv(1024) print "Got data:", data s.close() # Note: # Instead of calling selectdevice() and findservices(), you could do: # hostaddr, serviceport, servicename = lightblue.selectservice() # to ask the user to choose a service (instead of just choosing the device). lightblue-0.3.2/examples/._simple_server.py0000644000076500000000000000012210743263235020624 0ustar beawheel00000000000000Mac OS X  2 RTEXTlightblue-0.3.2/examples/simple_server.py0000644000076500007650000000122010743263235020054 0ustar beabea00000000000000""" Shows how to run a RFCOMM server socket. """ import lightblue # create and set up server socket sock = lightblue.socket() sock.bind(("", 0)) # bind to 0 to bind to a dynamically assigned channel sock.listen(1) lightblue.advertise("EchoService", sock, lightblue.RFCOMM) print "Advertised and listening on channel %d..." % sock.getsockname()[1] conn, addr = sock.accept() print "Connected by", addr data = conn.recv(1024) print "Echoing received data:", data conn.send(data) # sometimes the data isn't sent if the connection is closed immediately after # the call to send(), so wait a second import time time.sleep(1) conn.close() sock.close() lightblue-0.3.2/PKG-INFO0000644000076500007650000000211710770565177014122 0ustar beabea00000000000000Metadata-Version: 1.0 Name: lightblue Version: 0.3.2 Summary: Cross-platform Python Bluetooth library for Mac OS X, GNU/Linux and Python for Series 60. Home-page: http://lightblue.sourceforge.net Author: Bea Lam Author-email: blammit@gmail.com License: MIT Description: LightBlue is a cross-platform Python Bluetooth library for Mac OS X, GNU/Linux and Python for Series 60. It provides support for device and service discovery (with and without end-user GUIs), a standard socket interface for RFCOMM sockets, sending and receiving of files over OBEX, advertising of RFCOMM and OBEX services, and access to local device information. Platform: UNKNOWN Classifier: Development Status :: 3 - Alpha Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Programming Language :: Python Classifier: Topic :: Software Development :: Libraries Classifier: Topic :: System :: Networking Classifier: Topic :: Communications Classifier: Operating System :: MacOS :: MacOS X Classifier: Operating System :: POSIX :: Linux Classifier: Operating System :: Other OS lightblue-0.3.2/README.txt0000644000076500007650000000676710747233770014535 0ustar beabea00000000000000LightBlue http://lightblue.sourceforge.net Bea Lam LightBlue is a cross-platform Bluetooth API for Python which provides simple access to Bluetooth operations. It is available for Mac OS X, GNU/Linux and Nokia's Python for Series 60 platform for mobile phones. LightBlue provides simple access to: * Device and service discovery (with and without end-user GUIs) * Standard socket interface for RFCOMM and L2CAP sockets (currently L2CAP client sockets only, and not on PyS60) * Sending and receiving files over OBEX * Advertising of RFCOMM and OBEX services * Local device information LightBlue is released under the MIT License. See the home page at http://lightblue.sourceforge.net for more information. Requirements ============ Mac OS X: Python 2.3 or later PyObjC (http://pyobjc.sourceforge.net) Xcode 2.1 or later to build LightAquaBlue framework (but you could build from a separate .xcode project for older versions) (Mac OS X 10.4 or later is required to do device discovery without a GUI) GNU/Linux: Python 2.3 or later (with Tkinter if using selectdevice() or selectservice()) PyBluez 0.9 or later (http://org.csail.mit.edu/pybluez) OpenOBEX 1.0.1 or later (http://openobex.sourceforge.net) Python for Series 60: Python for Series 60 1.3.1 or later (http://sourceforge.net/projects/pys60) Installation ============ Mac OS X and GNU/Linux: Just open up a shell/terminal and run the command: python setup.py install Or you might need to run with root access, i.e. sudo python setup.py install On Mac OS X, the setup.py script also installs the LightAquaBlue framework into /Library/Frameworks. Python for Series 60: Download the appropriate SIS file for your phone from the LightBlue home page (http://lightblue.sourceforge.net). Send the file to your phone, and open and install. Or, use the Nokia PC Suite to install the SIS file. Installation for Xcode 1.5 / Mac OS X 10.3 ------------------------------------------ The LightAquaBlue framework for the Mac OS X installation is in a .xcodeproj package which can only be opened by Xcode 2.1 and later, and Xcode 2.1 does not run on Mac OS X 10.3. So to build LightBlue on Mac OS X 10.3, just create a .xcode package yourself: - Open Xcode and choose File -> New Project. Choose "Cocoa Framework" (under the "Frameworks" drop-down list) and save the project as "LightAquaBlue". Save the project anywhere as long as it's not replacing the existing LightBlue src/mac/LightAquaBlue directory. - Go to Project -> Add files... and add all the .h and .m files from LightBlue's src/mac/LightAquaBlue folder. Also add the OBEXFileTransferDictionary.plist, OBEXObjectPushDictionary.plist and SerialPortDictionary.plist files. - Go to Project -> Add framework... and add IOBluetooth.framework (found at /System/Library/Frameworks/IOBluetooth.framework). - Click on the "Targets" item in the left-hand column of the Xcode window. This should show all the .h and .m files you've added as well as a few other files. In the "Role" column, all the .h files currently have "project" roles. Click on each drop-down menu item to change all of them to "public". - Now go to the Finder and locate the xcode project you've just created. Copy the LightAquaBlue.xcode file for the project and paste it in LightBlue's src/mac/LightAquaBlue directory. Now go to LightBlue's root directory and run the command sudo python setup.py install You should see the output of the build of the xcode project. lightblue-0.3.2/._setup.py0000644000076500000000000000012210767440751015275 0ustar beawheel00000000000000Mac OS X  2 RTEXTlightblue-0.3.2/setup.py0000644000076500007650000000522010767440751014531 0ustar beabea00000000000000# On Python for Series 60, use the SIS files instead. from distutils.core import setup, Extension import sys LINUX = sys.platform.startswith("linux") MAC = sys.platform.startswith("darwin") def getpackagedir(): if MAC: return "src/mac" elif LINUX: return "src/linux" else: raise Exception("Unsupported platform") def getextensions(): if LINUX: linux_ext = Extension("_lightblueutil", libraries=["bluetooth"], # C libraries sources=["src/linux/lightblue_util.c"] ) linux_obex_ext = Extension("_lightblueobex", define_macros=[('LIGHTBLUE_DEBUG', '0')], # set to '1' to print debug messges libraries=["bluetooth", "openobex"], # C libraries sources=["src/linux/lightblueobex_client.c", "src/linux/lightblueobex_server.c", "src/linux/lightblueobex_main.c"], ) return [linux_ext, linux_obex_ext] return [] # install the main library setup(name="lightblue", version="0.3.2", author="Bea Lam", author_email="blammit@gmail.com", url="http://lightblue.sourceforge.net", description="Cross-platform Python Bluetooth library for Mac OS X, GNU/Linux and Python for Series 60.", long_description="LightBlue is a cross-platform Python Bluetooth library for Mac OS X, GNU/Linux and Python for Series 60. It provides support for device and service discovery (with and without end-user GUIs), a standard socket interface for RFCOMM sockets, sending and receiving of files over OBEX, advertising of RFCOMM and OBEX services, and access to local device information.", license="MIT", packages=["lightblue"], package_dir={"lightblue":getpackagedir()}, ext_modules=getextensions(), classifiers = [ "Development Status :: 3 - Alpha", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Programming Language :: Python", "Topic :: Software Development :: Libraries", "Topic :: System :: Networking", "Topic :: Communications", "Operating System :: MacOS :: MacOS X", "Operating System :: POSIX :: Linux", "Operating System :: Other OS" ] ) # On Mac, install LightAquaBlue framework # if you want to install the framework somewhere other than /Library/Frameworks # make sure the path is also changed in LightAquaBlue.py (in src/mac) if MAC: if "install" in sys.argv: import os os.chdir("src/mac/LightAquaBlue") os.system("xcodebuild install -target LightAquaBlue -configuration Release DSTROOT=/ INSTALL_PATH=/Library/Frameworks DEPLOYMENT_LOCATION=YES") lightblue-0.3.2/src/0000755000076500007650000000000010770565176013612 5ustar beabea00000000000000lightblue-0.3.2/src/linux/0000755000076500007650000000000010770565177014752 5ustar beabea00000000000000lightblue-0.3.2/src/linux/.___init__.py0000755000076500000000000000012210767432712017623 0ustar beawheel00000000000000Mac OS X  2 RTEXTlightblue-0.3.2/src/linux/__init__.py0000755000076500007650000001515210767432712017064 0ustar beabea00000000000000# Copyright (c) 2006 Bea Lam. All rights reserved. # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files # (the "Software"), to deal in the Software without restriction, # including without limitation the rights to use, copy, modify, merge, # publish, distribute, sublicense, and/or sell copies of the Software, # and to permit persons to whom the Software is furnished to do so, # subject to the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. "LightBlue - a simple bluetooth library." # Docstrings for attributes in this module. _docstrings = { "finddevices": """ Performs a device discovery and returns the found devices as a list of (address, name, class-of-device) tuples. Raises BluetoothError if an error occurs. Arguments: - getnames=True: True if device names should be retrieved during discovery. If false, None will be returned instead of the device name. - length=10: the number of seconds to spend discovering devices (this argument has no effect on Python for Series 60) Do not invoke a new discovery before a previous discovery has finished. Also, to minimise interference with other wireless and bluetooth traffic, and to conserve battery power on the local device, discoveries should not be invoked too frequently (an interval of at least 20 seconds is recommended). """, "findservices": """ Performs a service discovery and returns the found services as a list of (device-address, service-port, service-name) tuples. Raises BluetoothError if an error occurs. Arguments: - addr=None: a device address, to search only for services on a specific device - name=None: a service name string, to search only for a service with a specific name - servicetype=None: can be RFCOMM or OBEX to search only for RFCOMM or OBEX-type services. (OBEX services are not returned from an RFCOMM search) If more than one criteria is specified, this returns services that match all criteria. Currently the Python for Series 60 implementation will only find RFCOMM and OBEX services. """, "finddevicename": """ Returns the name of the device with the given bluetooth address. finddevicename(gethostaddr()) returns the local device name. Arguments: - address: the address of the device to look up - usecache=True: if True, the device name will be fetched from a local cache if possible. If False, or if the device name is not in the cache, the remote device will be contacted to request its name. Raise BluetoothError if the name cannot be retrieved. """, "gethostaddr": """ Returns the address of the local bluetooth device. Raise BluetoothError if the local device is not available. """, "gethostclass": """ Returns the class of device of the local bluetooth device. These values indicate the device's major services and the type of the device (e.g. mobile phone, laptop, etc.). If you google for "assigned numbers bluetooth baseband" you might find some documents that discuss how to extract this information from the class of device. Raise BluetoothError if the local device is not available. """, "socket": """ socket(proto=RFCOMM) -> socket object Returns a new socket object. Arguments: - proto=RFCOMM: the type of socket to be created - either L2CAP or RFCOMM. Note that L2CAP sockets are not available on Python For Series 60, and only L2CAP client sockets are supported on Mac OS X and Linux (i.e. you can connect() the socket but not bind(), accept(), etc.). """, "advertise": """ Starts advertising a service with the given name, using the given server socket. Raises BluetoothError if the service cannot be advertised. Arguments: - name: name of the service to be advertised - sock: the socket object that will serve this service. The socket must be already bound to a channel. If a RFCOMM service is being advertised, the socket should also be listening. - servicetype: the type of service to advertise - either RFCOMM or OBEX. (L2CAP services are not currently supported.) (If the servicetype is RFCOMM, the service will be advertised with the Serial Port Profile; if the servicetype is OBEX, the service will be advertised with the OBEX Object Push Profile.) """, "stopadvertise": """ Stops advertising the service on the given socket. Raises BluetoothError if no service is advertised on the socket. This will error if the given socket is already closed. """, "selectdevice": """ Displays a GUI which allows the end user to select a device from a list of discovered devices. Returns the selected device as an (address, name, class-of-device) tuple. Returns None if the selection was cancelled. (On Python For Series 60, the device selection will fail if there are any open bluetooth connections.) """, "selectservice": """ Displays a GUI which allows the end user to select a service from a list of discovered devices and their services. Returns the selected service as a (device-address, service-port, service- name) tuple. Returns None if the selection was cancelled. (On Python For Series 60, the device selection will fail if there are any open bluetooth connections.) Currently the Python for Series 60 implementation will only find RFCOMM and OBEX services. """ } # import implementation modules from _lightblue import * from _lightbluecommon import * import obex # plus submodule # set docstrings import _lightblue localattrs = locals() for attr in _lightblue.__all__: try: localattrs[attr].__doc__ = _docstrings[attr] except KeyError: pass del attr, localattrs lightblue-0.3.2/src/linux/_discoveryui.py0000644000076500007650000004341210533016740020014 0ustar beabea00000000000000# Copyright (c) 2006 Bea Lam. All rights reserved. # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files # (the "Software"), to deal in the Software without restriction, # including without limitation the rights to use, copy, modify, merge, # publish, distribute, sublicense, and/or sell copies of the Software, # and to permit persons to whom the Software is furnished to do so, # subject to the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. try: from Tkinter import * except ImportError, e: raise ImportError("Error loading GUIs for selectdevice() and selectservice(), Tkinter not found: " + str(e)) # Provides services for controlling a listbox, tracking selections, etc. class ListboxController(object): def __init__(self, listbox, cb_chosen): """ Arguments: - cb_chosen: called when a listbox item is chosen -- i.e. when an item is double-clicked or the key is pressed while an item is selected. """ self.setlistbox(listbox) self.cb_chosen = cb_chosen self.__alarmIDs = {} def setlistbox(self, listbox): self.listbox = listbox self.listbox.bind("", lambda evt: self._chosen()) self.listbox.bind("", lambda evt: lambda evt: self._chosen()) # adds an item to the list def add(self, *items): for item in items: self.listbox.insert(END, item) # clears items in listbox & refreshes UI def clear(self): self.listbox.delete(0, END) # selects an item in the list. # pass index=None to deselect. def select(self, index): self._deselect() if index is not None: self.listbox.selection_set(index) self.listbox.focus() def _deselect(self): selected = self.selectedindex() if selected != -1: self.listbox.selection_clear(selected) def selectedindex(self): sel = self.listbox.curselection() if len(sel) > 0: return int(sel[0]) return -1 # starts polling the listbox for a user selection and calls cb_selected # when an item is selected. def track(self, cb_selected, interval=100): self._track(interval, -1, cb_selected) def _track(self, interval, lastindex, callback): index = self.selectedindex() if index != -1 and index != lastindex: callback(index) # recursively keep tracking self.__alarmIDs[id(self.listbox)] = self.listbox.after( interval, self._track, interval, index, callback) def stoptracking(self): for x in self.__alarmIDs.values(): self.listbox.after_cancel(x) def focus(self): self.listbox.focus() def update(self): self.listbox.update() # called when a selection has been chosen (i.e. pressed return / dbl-click) def _chosen(self): index = self.selectedindex() if index != -1: self.cb_chosen(index) # A frame which contains a listbox and has a title above the listbox. class StandardListboxFrame(Frame): def __init__(self, parent, title, boxwidth=28): Frame.__init__(self, parent) self.pack() self.buildUI(parent, title, boxwidth) def buildUI(self, parent, title, boxwidth): bigframe = Frame(parent) bigframe.pack(side=LEFT, fill=BOTH, expand=1) self.titlelabel = Label(bigframe, text=title) self.titlelabel.pack(side=TOP) mainframe = Frame(bigframe, bd=1, relief=SUNKEN) mainframe.pack(side=BOTTOM, fill=BOTH, expand=1) scrollbar = Scrollbar(mainframe) scrollbar.pack(side=RIGHT, fill=Y) self.listbox = Listbox(mainframe, bd=1, exportselection=0) self.listbox.pack(fill=BOTH, expand=1) self.listbox.config(background="white", width=boxwidth) # attach listbox to scrollbar self.listbox.config(yscrollcommand=scrollbar.set) scrollbar.config(command=self.listbox.yview) def settitle(self, title): self.titlelabel.config(text=title) class StatusBar(object): def __init__(self, parent, side=TOP, text=""): self.label = Label(parent, text=text, bd=0, pady=8) self.label.pack(side=side, fill=BOTH, expand=1) def settext(self, text): self.label.config(text=text) # makes UI with top pane, status bar below top pane, and bottom pane. # Probably should use a grid geometry manager instead, might be easier. class LayoutFrame(Frame): def __init__(self, parent): Frame.__init__(self, parent, padx=10, pady=5) # inner padding self.topframe = Frame(self) self.topframe.pack(side=TOP, fill=BOTH, expand=1) self.statusbar = StatusBar(self) self.lineframe = Frame(self, height=1, bg="#999999") self.lineframe.pack(side=TOP, fill=BOTH, expand=1) self.bottomframe = Frame(self, pady=5) self.bottomframe.pack(side=BOTTOM, fill=BOTH, expand=1) # Abstract class for controlling and tracking selections for a listbox. class ItemSelectionController(object): def __init__(self, listbox, cb_chosen): self.cb_chosen = cb_chosen self._controller = ListboxController(listbox, self._chosen) self._closed = False def getselection(self): index = self._controller.selectedindex() if index != -1: return self._getitem(index) return None # set callback=None to switch off tracking def trackselections(self, callback, interval=100): if callback is not None: self.cb_selected = callback self._controller.track(self._selected, interval) else: self._controller.stoptracking() def close(self): self._controller.stoptracking() self._closed = True def closed(self): return self._closed # called when an item is chosen (e.g. dbl-clicked, not just selected) def _chosen(self, index): if self.cb_chosen: self.cb_chosen(self._getitem(index)) def _selected(self, index): if self.cb_selected: self.cb_selected(self._getitem(index)) # move focus to this listbox self._controller.focus() def getitemcount(self): raise NotImplementedError def _getitem(self, index): raise NotImplementedError class DeviceSelectionController(ItemSelectionController): # keep cache across instances (and across different sessions) _cache = [] def __init__(self, listbox, cb_chosen): super(DeviceSelectionController, self).__init__(listbox, cb_chosen) self._discoverer = None self.__items = [] self._loadcache() def close(self): self._stopdiscovery() DeviceSelectionController._cache = self.__items[:] super(DeviceSelectionController, self).close() def refreshdevices(self): self.__items = [] self._controller.clear() self._controller.update() self._stopdiscovery() self._discoverer = _DeviceDiscoverer(self._founddevice, None) self._discoverer.find_devices(duration=10) #self._test("device", 0, 5) def _additem(self, deviceinfo): self.__items.append(deviceinfo) self._controller.add(deviceinfo[1]) # add name def getitemcount(self): return len(self.__items) def _getitem(self, index): return self.__items[index] def _founddevice(self, address, deviceclass, name): self._additem((address, name, deviceclass)) # push updates to ensure names are progressively added to the display self._controller.listbox.update() def _loadcache(self): for item in DeviceSelectionController._cache: self._additem(item) def _stopdiscovery(self): if self._discoverer is not None: self._discoverer.cancel_inquiry() def _test(self, desc, n, max): import threading if n < max: dummy = ("00:00:00:00:00:"+str(n), "Device-" + str(n), 0) self._additem(dummy) threading.Timer(1.0, self._test, [desc, n+1, max]).start() class ServiceSelectionController(ItemSelectionController): def __init__(self, listbox, cb_chosen): super(ServiceSelectionController, self).__init__(listbox, cb_chosen) self.__items = [] # keep cache for each session (i.e. each time window is opened) self._sessioncache = {} def _additem(self, service): self.__items.append(service) self._controller.add(self._getservicedesc(service)) def getitemcount(self): return len(self.__items) # show services for given device address # pass address=None to clear display def showservices(self, address): self.__items = [] self._controller.clear() if address is None: return services = self._sessioncache.get(address) if not services: import lightblue services = lightblue.findservices(address) #services = [("", 1, "one"), ("", 2, "two"), ("", 3, "three")] self._sessioncache[address] = services if len(services) > 0: for service in services: self._additem(service) def _getitem(self, index): return self.__items[index] def _getservicedesc(self, service): address, port, name = service return "(%s) %s" % (str(port), name) class DeviceSelector(Frame): title = "Select Bluetooth device" def __init__(self, parent=None): Frame.__init__(self, parent) self.pack() self._buildUI() self._selection = None self._closed = False self.master.bind("", lambda evt: self._clickedcancel()) def _buildUI(self): mainframe = LayoutFrame(self) mainframe.pack() self._statusbar = mainframe.statusbar self._buildlistdisplay(mainframe.topframe) self._buildbuttons(mainframe.bottomframe) def _buildlistdisplay(self, parent): self.devicesframe = StandardListboxFrame(parent, "Devices", boxwidth=38) self.devicesframe.pack(side=LEFT, fill=BOTH, expand=1) self._devicemanager = DeviceSelectionController( self.devicesframe.listbox, self._chosedevice) def _buildbuttons(self, parent): self._searchbutton = Button(parent, text="Search for devices", command=self._clickedsearch) self._searchbutton.pack(side=LEFT) self._selectbutton = Button(parent, text="Select", command=self._clickedselect) self._selectbutton.pack(side=RIGHT) self._selectbutton.config(state=DISABLED) self._cancelbutton = Button(parent, text="Cancel", command=self._clickedcancel) self._cancelbutton.pack(side=RIGHT) def run(self): try: self._trackselections(True) # run gui event loop self.mainloop() except Exception, e: print "Warning: error during device selection:", e def _trackselections(self, track): if track: self._devicemanager.trackselections(self._selecteddevice) else: self._devicemanager.trackselections(None) def getresult(self): return self._selection def _selecteddevice(self, device): self._selectbutton.config(state=NORMAL) def _chosedevice(self, device): self._clickedselect() def _clickedsearch(self): self._statusbar.settext("Searching for nearby devices...") self._searchbutton.config(state=DISABLED) self._selectbutton.config(state=DISABLED) self.update() self._devicemanager.refreshdevices() if not self._closed: self._statusbar.settext( "Found %d devices." % self._devicemanager.getitemcount()) self._searchbutton.config(state=NORMAL) def _clickedcancel(self): self._quit() def _clickedselect(self): self._selection = self._devicemanager.getselection() self._quit() def _quit(self): self._closed = True self._devicemanager.close() #Frame.quit(self) # doesn't close the window self.master.destroy() class ServiceSelector(DeviceSelector): title = "Select Bluetooth service" def _buildlistdisplay(self, parent): self.devicesframe = StandardListboxFrame(parent, "Devices") self.devicesframe.pack(side=LEFT, fill=BOTH, expand=1) self._devicemanager = DeviceSelectionController( self.devicesframe.listbox, self._pickeddevice) # hack some space in between the 2 lists spacerframe = Frame(parent, width=10) spacerframe.pack(side=LEFT, fill=BOTH, expand=1) self.servicesframe = StandardListboxFrame(parent, "Services") self.servicesframe.pack(side=LEFT, fill=BOTH, expand=1) self._servicemanager = ServiceSelectionController( self.servicesframe.listbox, self._choseservice) def _trackselections(self, track): if track: self._devicemanager.trackselections(self._pickeddevice) self._servicemanager.trackselections(self._selectedservice) else: self._devicemanager.trackselections(None) self._servicemanager.trackselections(None) def _clearservices(self): self.servicesframe.settitle("Services") self._servicemanager.showservices(None) # clear services list # called when a device is selected, or chosen def _pickeddevice(self, deviceinfo): self._clearservices() self._statusbar.settext("Finding services for %s..." % deviceinfo[1]) self._selectbutton.config(state=DISABLED) self._searchbutton.config(state=DISABLED) self.update() self._servicemanager.showservices(deviceinfo[0]) if not self._closed: # user might have clicked 'cancel' self.servicesframe.settitle("%s's services" % deviceinfo[1]) self._statusbar.settext("Found %d services for %s." % ( self._servicemanager.getitemcount(), deviceinfo[1])) self._searchbutton.config(state=NORMAL) def _selectedservice(self, service): self._selectbutton.config(state=NORMAL) def _choseservice(self, service): self._clickedselect() def _clickedsearch(self): self._clearservices() self._trackselections(False) # don't track selections while searching # do the search DeviceSelector._clickedsearch(self) # re-enable selection tracking if not self._closed: self._trackselections(True) def _clickedselect(self): self._selection = self._servicemanager.getselection() self._quit() def _quit(self): self._closed = True self._devicemanager.close() self._servicemanager.close() self.master.destroy() # ----------------------------------- import select import bluetooth class _DeviceDiscoverer(bluetooth.DeviceDiscoverer): def __init__(self, cb_found, cb_complete): bluetooth.DeviceDiscoverer.__init__(self) # old-style superclass self.cb_found = cb_found self.cb_complete = cb_complete def find_devices(self, lookup_names=True, duration=8, flush_cache=True): bluetooth.DeviceDiscoverer.find_devices(self, lookup_names, duration, flush_cache) # process until inquiry is complete self._done = False self._cancelled = False while not self._done and not self._cancelled: #print "Processed" readfiles = [self,] rfds = select.select(readfiles, [], [])[0] if self in rfds: self.process_event() # cancel_inquiry() doesn't like getting stopped in the middle of # process_event() maybe? so just use flag instead. if self._cancelled: bluetooth.DeviceDiscoverer.cancel_inquiry(self) def cancel_inquiry(self): self._cancelled = True def device_discovered(self, address, deviceclass, name): #print "device_discovered", address, deviceclass, name if self.cb_found: self.cb_found(address, deviceclass, name) def inquiry_complete(self): #print "inquiry_complete" self._done = True if self.cb_complete: self.cb_complete() # ----------------------------------- # Centres a tkinter window def centrewindow(win): win.update_idletasks() xmax = win.winfo_screenwidth() ymax = win.winfo_screenheight() x0 = (xmax - win.winfo_reqwidth()) / 2 y0 = (ymax - win.winfo_reqheight()) / 2 win.geometry("+%d+%d" % (x0, y0)) def setupwin(rootwin, title): # set window title rootwin.title(title) # place window at centre rootwin.after_idle(centrewindow, rootwin) rootwin.update() # ----------------------------------- def selectdevice(): rootwin = Tk() selector = DeviceSelector(rootwin) setupwin(rootwin, DeviceSelector.title) selector.run() return selector.getresult() def selectservice(): rootwin = Tk() selector = ServiceSelector(rootwin) setupwin(rootwin, ServiceSelector.title) selector.run() return selector.getresult() if __name__ == "__main__": print selectservice()lightblue-0.3.2/src/linux/.__lightblue.py0000644000076500000000000000012210747232045020172 0ustar beawheel00000000000000Mac OS X  2 RTEXTlightblue-0.3.2/src/linux/_lightblue.py0000644000076500007650000003112110747232045017425 0ustar beabea00000000000000# Copyright (c) 2006 Bea Lam. All rights reserved. # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files # (the "Software"), to deal in the Software without restriction, # including without limitation the rights to use, copy, modify, merge, # publish, distribute, sublicense, and/or sell copies of the Software, # and to permit persons to whom the Software is furnished to do so, # subject to the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. import socket as _socket try: import bluetooth # pybluez module try: import _bluetooth # pybluez internal implementation module except: import bluetooth._bluetooth as _bluetooth except ImportError, e: raise ImportError("LightBlue requires PyBluez to be installed, " + \ "cannot find PyBluez 'bluetooth' module: " + str(e)) import _lightbluecommon import _lightblueutil # public attributes __all__ = ("finddevices", "findservices", "finddevicename", "gethostaddr", "gethostclass", "socket", "advertise", "stopadvertise", "selectdevice", "selectservice") # device name cache _devicenames = {} # map lightblue protocol values to pybluez ones _PROTOCOLS = { _lightbluecommon.RFCOMM: bluetooth.RFCOMM, _lightbluecommon.L2CAP: bluetooth.L2CAP } def finddevices(getnames=True, length=10): return _SyncDeviceInquiry().run(getnames, length) def findservices(addr=None, name=None, servicetype=None): # This always passes a uuid, to force PyBluez to use BlueZ 'search' instead # of 'browse', otherwise some services won't get found. If you use BlueZ's # or sometimes you'll get services that # aren't returned through -- I think 'browse' only returns # services with recognised protocols or profiles or something. if servicetype is None: uuid = "0100" # L2CAP -- i.e. pretty much all services elif servicetype == _lightbluecommon.RFCOMM: uuid = "0003" elif servicetype == _lightbluecommon.OBEX: uuid = "0008" else: raise ValueError("servicetype must be RFCOMM, OBEX or None, was %s" % \ servicetype) try: services = bluetooth.find_service(name=name, uuid=uuid, address=addr) except bluetooth.BluetoothError, e: raise _lightbluecommon.BluetoothError(str(e)) if servicetype == _lightbluecommon.RFCOMM: # OBEX services will be included with RFCOMM services (since OBEX is # built on top of RFCOMM), so filter out the OBEX services return [_getservicetuple(s) for s in services if not _isobexservice(s)] else: return [_getservicetuple(s) for s in services] def finddevicename(address, usecache=True): if not _lightbluecommon._isbtaddr(address): raise ValueError("%s is not a valid bluetooth address" % str(address)) if address == gethostaddr(): return _gethostname() if usecache: name = _devicenames.get(address) if name is not None: return name name = bluetooth.lookup_name(address) if name is None: raise _lightbluecommon.BluetoothError( "Could not find device name for %s" % address) _devicenames[address] = name return name ### local device ### def gethostaddr(): sock = _gethcisock() try: try: addr = _lightblueutil.hci_read_bd_addr(sock.fileno(), 1000) except IOError, e: raise _lightbluecommon.BluetoothError(str(e)) finally: sock.close() return addr def gethostclass(): sock = _gethcisock() try: try: cod = _lightblueutil.hci_read_class_of_dev(sock.fileno(), 1000) except IOError, e: raise _lightbluecommon.BluetoothError(str(e)) finally: sock.close() return _lightbluecommon._joinclass(cod) def _gethostname(): sock = _gethcisock() try: try: name = _lightblueutil.hci_read_local_name(sock.fileno(), 1000) except IOError, e: raise _lightbluecommon.BluetoothError(str(e)) finally: sock.close() return name ### socket ### class _SocketWrapper(object): def __init__(self, sock): self.__dict__["_sock"] = sock self.__dict__["_advertised"] = False self.__dict__["_listening"] = False # must implement accept() to return _SocketWrapper objects def accept(self): try: # access _sock._sock (i.e. pybluez socket's internal sock) # this is so we can raise timeout errors with a different exception conn, addr = self._sock._sock.accept() except _bluetooth.error, e: raise _socket.error(str(e)) except _bluetooth.timeout, te: raise _socket.timeout(str(te)) # return new _SocketWrapper that wraps a new BluetoothSocket newsock = bluetooth.BluetoothSocket(_sock=conn) return (_SocketWrapper(newsock), addr) accept.__doc__ = _lightbluecommon._socketdocs["accept"] def listen(self, backlog): if not self._listening: self._sock.listen(backlog) self._listening = True # must implement dup() to return _SocketWrapper objects def dup(self): return _SocketWrapper(self._sock.dup()) dup.__doc__ = _lightbluecommon._socketdocs["dup"] def getsockname(self): sockname = self._sock.getsockname() if sockname[1] != 0: return (gethostaddr(), sockname[1]) return sockname # not connected, should be ("00:00:00:00:00:00", 0) getsockname.__doc__ = _lightbluecommon._socketdocs["getsockname"] # redefine methods that can raise timeout errors, to access _sock._sock # in order to raise timeout errors with a different exception # (otherwise they are raised as generic BluetoothException) _methoddef = """def %s(self, *args, **kwargs): try: return self._sock._sock.%s(*args, **kwargs) except _bluetooth.error, e: raise _socket.error(str(e)) except _bluetooth.timeout, te: raise _socket.timeout(str(te)) %s.__doc__ = _lightbluecommon._socketdocs['%s']\n""" for _m in ("connect", "send", "recv"): exec _methoddef % (_m, _m, _m, _m) del _m, _methoddef # wrap all other socket methods, to set LightBlue-specific docstrings _othermethods = [_m for _m in _lightbluecommon._socketdocs.keys() \ if _m not in locals()] # methods other than those already defined _methoddef = """def %s(self, *args, **kwargs): try: return self._sock.%s(*args, **kwargs) except _bluetooth.error, e: raise _socket.error(str(e)) %s.__doc__ = _lightbluecommon._socketdocs['%s']\n""" for _m in _othermethods: exec _methoddef % (_m, _m, _m, _m) del _m, _methoddef def socket(proto=_lightbluecommon.RFCOMM): # return a wrapped BluetoothSocket sock = bluetooth.BluetoothSocket(_PROTOCOLS[proto]) return _SocketWrapper(sock) ### advertising services ### def advertise(servicename, sock, serviceclass): try: if serviceclass == _lightbluecommon.RFCOMM: bluetooth.advertise_service(sock._sock, servicename, service_classes=[bluetooth.SERIAL_PORT_CLASS], profiles=[bluetooth.SERIAL_PORT_PROFILE]) elif serviceclass == _lightbluecommon.OBEX: # for pybluez, socket do need to be listening in order to # advertise a service, so we'll call listen() here. This should be # safe since user shouldn't have called listen() already, because # obex.recvfile() docs state that an obex server socket should # *not* be listening before recvfile() is called (due to Series60's # particular implementation) if not sock._listening: sock.listen(1) # advertise Object Push Profile not File Transfer Profile because # obex.recvfile() implementations run OBEX servers which only # advertise Object Push operations bluetooth.advertise_service(sock._sock, servicename, service_classes=[bluetooth.OBEX_OBJPUSH_CLASS], profiles=[bluetooth.OBEX_OBJPUSH_PROFILE], protocols=["0008"]) # OBEX protocol else: raise ValueError("Unknown serviceclass, " + \ "should be either RFCOMM or OBEX constants") # set flag sock._advertised = True except bluetooth.BluetoothError, e: raise _lightbluecommon.BluetoothError(str(e)) def stopadvertise(sock): if not sock._advertised: raise _lightbluecommon.BluetoothError("no service advertised") try: bluetooth.stop_advertising(sock._sock) except bluetooth.BluetoothError, e: raise _lightbluecommon.BluetoothError(str(e)) sock._advertised = False ### GUI ### def selectdevice(): import _discoveryui return _discoveryui.selectdevice() def selectservice(): import _discoveryui return _discoveryui.selectservice() ### classes ### class _SyncDeviceInquiry(object): def __init__(self): super(_SyncDeviceInquiry, self).__init__() self._inquiry = None def run(self, getnames=True, length=10): self._founddevices = [] self._inquiry = _MyDiscoverer(self._founddevice, self._inquirycomplete) try: self._inquiry.find_devices(lookup_names=getnames, duration=length) # block until inquiry finishes self._inquiry.process_inquiry() except bluetooth.BluetoothError, e: try: self._inquiry.cancel_inquiry() finally: raise _lightbluecommon.BluetoothError(e) return self._founddevices def _founddevice(self, address, deviceclass, name): self._founddevices.append(_getdevicetuple(address, deviceclass, name)) def _inquirycomplete(self): pass # subclass of PyBluez DeviceDiscoverer class for customised async discovery class _MyDiscoverer(bluetooth.DeviceDiscoverer): # _MyDiscoverer inherits 2 major methods for discovery: # - find_devices(lookup_names=True, duration=8, flush_cache=True) # - cancel_inquiry() def __init__(self, founddevicecallback, completedcallback): bluetooth.DeviceDiscoverer.__init__(self) # old-style superclass, no super() self.founddevicecallback = founddevicecallback self.completedcallback = completedcallback def device_discovered(self, address, deviceclass, name): self.founddevicecallback(address, deviceclass, name) def inquiry_complete(self): self.completedcallback() ### utility methods ### def _getdevicetuple(address, deviceclass, name): # Return as (addr, name, cod) tuple. return (address, name, deviceclass) def _getservicetuple(service): """ Returns a (addr, port, name) tuple from a PyBluez service dictionary, which should have at least: - "name": service name - "port": service channel/PSM etc. - "host": address of host device """ return (service["host"], service["port"], service["name"]) # assume a service is an OBEX-type service if it advertises Object Push, File # Transfer or Synchronization classes, since their respective profiles are # listed as using the OBEX protocol in the bluetooth specs _obexserviceclasses = ( bluetooth.OBEX_OBJPUSH_CLASS, bluetooth.OBEX_FILETRANS_CLASS, bluetooth.IRMC_SYNC_CMD_CLASS ) def _isobexservice(service): for sc in service["service-classes"]: if sc in _obexserviceclasses: return True return False # Gets HCI socket thru PyBluez. Remember to close the returned socket. def _gethcisock(devid=-1): try: sock = _bluetooth.hci_open_dev(devid) except Exception, e: raise _lightbluecommon.BluetoothError( "Cannot access local device: " + str(e)) return sock lightblue-0.3.2/src/linux/_lightblue.pyc0000644000076500007650000003512510737617203017602 0ustar beabea00000000000000;ò ÛGc @sÎdkZy0dkZy dkZWndkiZnXWn/ej o#Zeddeeƒƒ‚nXdkZdk Z ddddddd d d d f Z hZ hei ei <ei ei  for OBEX operations. """ pass def splitclass(classofdevice): """ Splits the given class of device to return a 3-item tuple with the major service class, major device class and minor device class values. These values indicate the device's major services and the type of the device (e.g. mobile phone, laptop, etc.). If you google for "assigned numbers bluetooth baseband" you might find some documents that discuss how to extract this information from the class of device. Example: >>> splitclass(1057036) (129, 1, 3) >>> """ if not isinstance(classofdevice, int): try: classofdevice = int(classofdevice) except (TypeError, ValueError): raise TypeError("Given device class '%s' cannot be split" % \ str(classofdevice)) data = classofdevice >> 2 # skip over the 2 "format" bits service = data >> 11 major = (data >> 6) & 0x1F minor = data & 0x3F return (service, major, minor) _validbtaddr = None def _isbtaddr(address): """ Returns whether the given address is a valid bluetooth address. For example, "00:0e:6d:7b:a2:0a" is a valid address. Returns False if the argument is None or is not a string. """ # Define validity regex. Accept either ":" or "-" as separators. global _validbtaddr if _validbtaddr is None: import re _validbtaddr = re.compile("((\d|[a-f]){2}(:|-)){5}(\d|[a-f]){2}", re.IGNORECASE) import types if not isinstance(address, types.StringTypes): return False return _validbtaddr.match(address) is not None # --------- other attributes --------- def _joinclass(codtuple): """ The opposite of splitclass(). Joins a (service, major, minor) class-of- device tuple into a whole class of device value. """ if not isinstance(codtuple, tuple): raise TypeError("argument must be tuple, was %s" % type(codtuple)) if len(codtuple) != 3: raise ValueError("tuple must have 3 items, has %d" % len(codtuple)) serviceclass = codtuple[0] << 2 << 11 majorclass = codtuple[1] << 2 << 6 minorclass = codtuple[2] << 2 return (serviceclass | majorclass | minorclass) # Docstrings for socket objects. # Based on std lib socket docs. _socketdocs = { "accept": """ accept() -> (socket object, address info) Wait for an incoming connection. Return a new socket representing the connection, and the address of the client. For RFCOMM sockets, the address info is a pair (hostaddr, channel). The socket must be bound and listening before calling this method. """, "bind": """ bind(address) Bind the socket to a local address. For RFCOMM sockets, the address is a pair (host, channel); the host must refer to the local host. A port value of 0 binds the socket to a dynamically assigned port. (Note that on Mac OS X, the port value must always be 0.) The socket must not already be bound. """, "close": """ close() Close the socket. It cannot be used after this call. """, "connect": """ connect(address) Connect the socket to a remote address. The address should be a (host, channel) pair for RFCOMM sockets, and a (host, PSM) pair for L2CAP sockets. The socket must not be already connected. """, "connect_ex": """ connect_ex(address) -> errno This is like connect(address), but returns an error code instead of raising an exception when an error occurs. """, "dup": """ dup() -> socket object Returns a new socket object connected to the same system resource. """, "fileno": """ fileno() -> integer Return the integer file descriptor of the socket. Raises NotImplementedError on Mac OS X and Python For Series 60. """, "getpeername": """ getpeername() -> address info Return the address of the remote endpoint. The address info is a (host, channel) pair for RFCOMM sockets, and a (host, PSM) pair for L2CAP sockets. If the socket has not been connected, socket.error will be raised. """, "getsockname": """ getsockname() -> address info Return the address of the local endpoint. The address info is a (host, channel) pair for RFCOMM sockets, and a (host, PSM) pair for L2CAP sockets. If the socket has not been connected nor bound, this returns the tuple ("00:00:00:00:00:00", 0). """, "getsockopt": """ getsockopt(level, option[, bufsize]) -> value Get a socket option. See the Unix manual for level and option. If a nonzero buffersize argument is given, the return value is a string of that length; otherwise it is an integer. Currently support for socket options are platform independent -- i.e. depends on the underlying Series 60 or BlueZ socket options support. The Mac OS X implementation currently does not support any options at all and automatically raises socket.error. """, "gettimeout": """ gettimeout() -> timeout Returns the timeout in floating seconds associated with socket operations. A timeout of None indicates that timeouts on socket operations are disabled. Currently not supported on Python For Series 60 implementation, which will always return None. """, "listen": """ listen(backlog) Enable a server to accept connections. The backlog argument must be at least 1; it specifies the number of unaccepted connection that the system will allow before refusing new connections. The socket must not be already listening. Currently not implemented on Mac OS X. """, "makefile": """ makefile([mode[, bufsize]]) -> file object Returns a regular file object corresponding to the socket. The mode and bufsize arguments are as for the built-in open() function. """, "recv": """ recv(bufsize[, flags]) -> data Receive up to bufsize bytes from the socket. For the optional flags argument, see the Unix manual. When no data is available, block until at least one byte is available or until the remote end is closed. When the remote end is closed and all data is read, return the empty string. Currently the flags argument has no effect on Mac OS X. """, "recvfrom": """ recvfrom(bufsize[, flags]) -> (data, address info) Like recv(buffersize, flags) but also return the sender's address info. """, "send": """ send(data[, flags]) -> count Send a data string to the socket. For the optional flags argument, see the Unix manual. Return the number of bytes sent. The socket must be connected to a remote socket. Currently the flags argument has no effect on Mac OS X. """, "sendall": """ sendall(data[, flags]) Send a data string to the socket. For the optional flags argument, see the Unix manual. This calls send() repeatedly until all data is sent. If an error occurs, it's impossible to tell how much data has been sent. """, "sendto": """ sendto(data[, flags], address) -> count Like send(data, flags) but allows specifying the destination address. For RFCOMM sockets, the address is a pair (hostaddr, channel). """, "setblocking": """ setblocking(flag) Set the socket to blocking (flag is true) or non-blocking (false). setblocking(True) is equivalent to settimeout(None); setblocking(False) is equivalent to settimeout(0.0). Initially a socket is in blocking mode. In non-blocking mode, if a socket operation cannot be performed immediately, socket.error is raised. The underlying implementation on Python for Series 60 only supports non-blocking mode for send() and recv(), and ignores it for connect() and accept(). """, "setsockopt": """ setsockopt(level, option, value) Set a socket option. See the Unix manual for level and option. The value argument can either be an integer or a string. Currently support for socket options are platform independent -- i.e. depends on the underlying Series 60 or BlueZ socket options support. The Mac OS X implementation currently does not support any options at all and automatically raise socket.error. """, "settimeout": """ settimeout(timeout) Set a timeout on socket operations. 'timeout' can be a float, giving in seconds, or None. Setting a timeout of None disables the timeout feature and is equivalent to setblocking(1). Setting a timeout of zero is the same as setblocking(0). If a timeout is set, the connect, accept, send and receive operations will raise socket.timeout if a timeout occurs. Raises NotImplementedError on Python For Series 60. """, "shutdown": """ shutdown(how) Shut down the reading side of the socket (flag == socket.SHUT_RD), the writing side of the socket (flag == socket.SHUT_WR), or both ends (flag == socket.SHUT_RDWR). """ } lightblue-0.3.2/src/linux/_lightbluecommon.pyc0000644000076500007650000002447010737617240021015 0ustar beabea00000000000000;ò lxÍEc@s4dddddfZdddf\ZZZdefd„ƒYZd „Zead „Z d „Z hd d <dd<dd<dd<dd<dd<dd<dd<dd<dd<d d!<d"d#<d$d%<d&d'<d(d)<d*d+<d,d-<d.d/<d0d1<d2d3<d4d5<d6d7 for OBEX operations. (s__name__s __module__s__doc__(((sP/Users/bea/bluetooth-api/lightblue/lightblue/trunk/src/linux/_lightbluecommon.pysBluetoothError!s cCs”t|tƒ oFyt|ƒ}WqWttfj otdt|ƒƒ‚qWXn|d?}|d?}|d?d@}|d@}|||fSdS(sº Splits the given class of device to return a 3-item tuple with the major service class, major device class and minor device class values. These values indicate the device's major services and the type of the device (e.g. mobile phone, laptop, etc.). See http://www.bluetooth.org/assigned-numbers/baseband.php for details. Example: >>> splitclass(1057036) (129, 1, 3) >>> s'Given device class '%s' cannot be splitii iii?N( s isinstances classofdevicesints TypeErrors ValueErrorsstrsdatasservicesmajorsminor(s classofdevicesmajorsservicesdatasminor((sP/Users/bea/bluetooth-api/lightblue/lightblue/trunk/src/linux/_lightbluecommon.pys splitclass.s    cCskttjo"dk}|id|iƒandk}t||iƒ ot Snti |ƒtj SdS(sÅ Returns whether the given address is a valid bluetooth address. For example, "00:0e:6d:7b:a2:0a" is a valid address. Returns False if the argument is None or is not a string. Ns$((\d|[a-f]){2}(:|-)){5}(\d|[a-f]){2}( s _validbtaddrsNonesrescompiles IGNORECASEstypess isinstancesaddresss StringTypessFalsesmatch(saddresssrestypes((sP/Users/bea/bluetooth-api/lightblue/lightblue/trunk/src/linux/_lightbluecommon.pys _isbtaddrKs    cCsšt|tƒ otdt|ƒƒ‚nt|ƒdjotdt|ƒƒ‚n|dd>d>}|dd>d>}|dd>}||B|BSd S( s† The opposite of splitclass(). Joins a (service, major, minor) class-of- device tuple into a whole class of device value. sargument must be tuple, was %sistuple must have 3 items, has %diii iiN( s isinstancescodtuplestuples TypeErrorstypeslens ValueErrors serviceclasss majorclasss minorclass(scodtuples serviceclasss majorclasss minorclass((sP/Users/bea/bluetooth-api/lightblue/lightblue/trunk/src/linux/_lightbluecommon.pys _joinclass`ssacceptsE accept() -> (socket object, address info) Wait for an incoming connection. Return a new socket representing the connection, and the address of the client. For RFCOMM sockets, the address info is a pair (hostaddr, channel). The socket must be bound and listening before calling this method. sbinds* bind(address) Bind the socket to a local address. For RFCOMM sockets, the address is a pair (host, channel); the host must refer to the local host. The socket must not already be bound. A port value of 0 binds the socket to a dynamically assigned port. sclosesP close() Close the socket. It cannot be used after this call. sconnectsò connect(address) Connect the socket to a remote address. The address should be a (host, channel) pair for RFCOMM sockets, and a (host, PSM) pair for L2CAP sockets. The socket must not be already connected. s connect_exs£ connect_ex(address) -> errno This is like connect(address), but returns an error code instead of raising an exception when an error occurs. sdupsl dup() -> socket object Returns a new socket object connected to the same system resource. sfilenos¢ fileno() -> integer Return the integer file descriptor of the socket. Raises NotImplementedError on Mac OS X and Python For Series 60. s getpeernames getpeername() -> address info Return the address of the remote endpoint. The address info is a (host, channel) pair for RFCOMM sockets, and a (host, PSM) pair for L2CAP sockets. If the socket has not been connected, socket.error will be raised. s getsocknames9 getsockname() -> address info Return the address of the local endpoint. The address info is a (host, channel) pair for RFCOMM sockets, and a (host, PSM) pair for L2CAP sockets. If the socket has not been connected nor bound, this returns the tuple ("00:00:00:00:00:00", 0). s getsockopts getsockopt(level, option[, bufsize]) -> value Get a socket option. See the Unix manual for level and option. If a nonzero buffersize argument is given, the return value is a string of that length; otherwise it is an integer. Currently support for socket options are platform independent -- i.e. depends on the underlying Series 60 or BlueZ socket options support. The Mac OS X implementation currently does not support any options at all and automatically raises socket.error. s gettimeouts: gettimeout() -> timeout Returns the timeout in floating seconds associated with socket operations. A timeout of None indicates that timeouts on socket operations are disabled. Currently not supported on Python For Series 60 implementation, which will always return None. slistensN listen(backlog) Enable a server to accept connections. The backlog argument must be at least 1; it specifies the number of unaccepted connection that the system will allow before refusing new connections. The socket must not be already listening. Currently not implemented on Mac OS X. smakefilesÁ makefile([mode[, bufsize]]) -> file object Returns a regular file object corresponding to the socket. The mode and bufsize arguments are as for the built-in open() function. srecvsš recv(bufsize[, flags]) -> data Receive up to bufsize bytes from the socket. For the optional flags argument, see the Unix manual. When no data is available, block until at least one byte is available or until the remote end is closed. When the remote end is closed and all data is read, return the empty string. Currently the flags argument has no effect on Mac OS X. srecvfroms recvfrom(bufsize[, flags]) -> (data, address info) Like recv(buffersize, flags) but also return the sender's address info. ssends- send(data[, flags]) -> count Send a data string to the socket. For the optional flags argument, see the Unix manual. Return the number of bytes sent. The socket must be connected to a remote socket. Currently the flags argument has no effect on Mac OS X. ssendalls  sendall(data[, flags]) Send a data string to the socket. For the optional flags argument, see the Unix manual. This calls send() repeatedly until all data is sent. If an error occurs, it's impossible to tell how much data has been sent. ssendtosà sendto(data[, flags], address) -> count Like send(data, flags) but allows specifying the destination address. For RFCOMM sockets, the address is a pair (hostaddr, channel). s setblockingsv setblocking(flag) Set the socket to blocking (flag is true) or non-blocking (false). setblocking(True) is equivalent to settimeout(None); setblocking(False) is equivalent to settimeout(0.0). Initially a socket is in blocking mode. In non-blocking mode, if a socket operation cannot be performed immediately, socket.error is raised. s setsockoptsà setsockopt(level, option, value) Set a socket option. See the Unix manual for level and option. The value argument can either be an integer or a string. Currently support for socket options are platform independent -- i.e. depends on the underlying Series 60 or BlueZ socket options support. The Mac OS X implementation currently does not support any options at all and automatically raise socket.error. s settimeoutsè settimeout(timeout) Set a timeout on socket operations. 'timeout' can be a float, giving in seconds, or None. Setting a timeout of None disables the timeout feature and is equivalent to setblocking(1). Setting a timeout of zero is the same as setblocking(0). If a timeout is set, the connect, accept, send and receive operations will raise socket.timeout if a timeout occurs. Raises NotImplementedError on Python For Series 60. sshutdownsÐ shutdown(how) Shut down the reading side of the socket (flag == socket.SHUT_RD), the writing side of the socket (flag == socket.SHUT_WR), or both ends (flag == socket.SHUT_RDWR). N( s__all__sL2CAPsRFCOMMsOBEXsIOErrorsBluetoothErrors splitclasssNones _validbtaddrs _isbtaddrs _joinclasss _socketdocs( sBluetoothErrorsOBEXs__all__sRFCOMMs _joinclasss splitclasss _socketdocssL2CAPs _isbtaddr((sP/Users/bea/bluetooth-api/lightblue/lightblue/trunk/src/linux/_lightbluecommon.pys?s   lightblue-0.3.2/src/linux/_obex.py0000644000076500000000000003155010751225376016750 0ustar beawheel00000000000000# Copyright (c) 2006 Bea Lam. All rights reserved. # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files # (the "Software"), to deal in the Software without restriction, # including without limitation the rights to use, copy, modify, merge, # publish, distribute, sublicense, and/or sell copies of the Software, # and to permit persons to whom the Software is furnished to do so, # subject to the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. import types import datetime import _lightbluecommon import _obexcommon import _lightblueobex # python extension from _obexcommon import OBEXError _HEADER_MASK = 0xc0 _HEADER_UNICODE = 0x00 _HEADER_BYTE_SEQ = 0x40 _HEADER_1BYTE = 0x80 _HEADER_4BYTE = 0xc0 # public attributes __all__ = ("sendfile", "recvfile", "OBEXClient") class OBEXClient(object): __doc__ = _obexcommon._obexclientclassdoc def __init__(self, address, channel): if not isinstance(address, types.StringTypes): raise TypeError("address must be string, was %s" % type(address)) if not type(channel) == int: raise TypeError("channel must be int, was %s" % type(channel)) self.__sock = None self.__client = None self.__serveraddr = (address, channel) self.__connectionid = None def connect(self, headers={}): if self.__client is not None: raise OBEXError("session is already connected") self.__setUp() try: resp = self.__client.request(_lightblueobex.CONNECT, self.__convertheaders(headers), None) except IOError, e: raise OBEXError(str(e)) result = self.__createresponse(resp) if result.code == _obexcommon.OK: self.__connectionid = result.headers.get("connection-id", None) else: self.__closetransport() return result def disconnect(self, headers={}): self.__checkconnected() try: try: resp = self.__client.request(_lightblueobex.DISCONNECT, self.__convertheaders(headers), None) except IOError, e: raise OBEXError(str(e)) finally: # close bt connection regardless of disconnect response self.__closetransport() return self.__createresponse(resp) def put(self, headers, fileobj): if not hasattr(fileobj, "read"): raise TypeError("file-like object must have read() method") self.__checkconnected() try: resp = self.__client.request(_lightblueobex.PUT, self.__convertheaders(headers), None, fileobj) except IOError, e: raise OBEXError(str(e)) return self.__createresponse(resp) def delete(self, headers): self.__checkconnected() try: resp = self.__client.request(_lightblueobex.PUT, self.__convertheaders(headers), None) except IOError, e: raise OBEXError(str(e)) return self.__createresponse(resp) def get(self, headers, fileobj): if not hasattr(fileobj, "write"): raise TypeError("file-like must have write() method") self.__checkconnected() try: resp = self.__client.request(_lightblueobex.GET, self.__convertheaders(headers), None, fileobj) except IOError, e: raise OBEXError(str(e)) return self.__createresponse(resp) def setpath(self, headers, cdtoparent=False, createdirs=False): self.__checkconnected() flags = 0 if cdtoparent: flags |= 1 if not createdirs: flags |= 2 import array setpathdata = array.array('B', (flags, 0)) # zero for constants byte try: resp = self.__client.request(_lightblueobex.SETPATH, self.__convertheaders(headers), buffer(setpathdata)) except IOError, e: raise OBEXError(str(e)) return self.__createresponse(resp) def __setUp(self): if self.__client is None: import bluetooth self.__sock = bluetooth.BluetoothSocket(bluetooth.RFCOMM) try: self.__sock.connect((self.__serveraddr[0], self.__serveraddr[1])) except bluetooth.BluetoothError, e: raise OBEXError(str(e)) try: self.__client = _lightblueobex.OBEXClient(self.__sock.fileno()) except IOError, e: raise OBEXError(str(e)) def __closetransport(self): try: self.__sock.close() except: pass self.__connectionid = None self.__client = None def __checkconnected(self): if self.__client is None: raise OBEXError(_kOBEXSessionNotConnectedError, "must connect() before sending other requests") def __createresponse(self, resp): headers = resp[1] for hid, value in headers.items(): if hid == 0x44: headers[hid] = _obexcommon._datetimefromstring(value[:]) elif hid == 0xC4: headers[hid] = datetime.datetime.fromtimestamp(value) elif type(value) == buffer: headers[hid] = value[:] return _obexcommon.OBEXResponse(resp[0], headers) def __convertheaders(self, headers): result = {} for header, value in headers.items(): if isinstance(header, types.StringTypes): hid = \ _obexcommon._HEADER_STRINGS_TO_IDS.get(header.lower()) else: hid = header if hid is None: raise ValueError("unknown header '%s'" % header) if isinstance(value, datetime.datetime): value = value.strftime("%Y%m%dT%H%M%S") self.__checkheadervalue(header, hid, value) result[hid] = value if self.__connectionid is not None: result[_lightblueobex.CONNECTION_ID] = self.__connectionid return result def __checkheadervalue(self, header, hid, value): mask = hid & _HEADER_MASK if mask == _HEADER_UNICODE: if not isinstance(value, types.StringTypes): raise TypeError("value for '%s' must be string, was %s" % (str(header), type(value))) elif mask == _HEADER_BYTE_SEQ: try: buffer(value) except: raise TypeError("value for '%s' must be string, array or other buffer type, was %s" % (str(header), type(value))) elif mask == _HEADER_1BYTE: if not isinstance(value, int): raise TypeError("value for '%s' must be int, was %s" % (str(header), type(value))) elif mask == _HEADER_4BYTE: if not isinstance(value, int) and not isinstance(value, long): raise TypeError("value for '%s' must be int, was %s" % (str(header), type(value))) # set method docstrings definedmethods = locals() # i.e. defined methods in OBEXClient for name, doc in _obexcommon._obexclientdocs.items(): try: definedmethods[name].__doc__ = doc except KeyError: pass # --------------------------------------------------------------------- def sendfile(address, channel, source): if not _lightbluecommon._isbtaddr(address): raise TypeError("address '%s' is not a valid bluetooth address" \ % address) if not isinstance(channel, int): raise TypeError("channel must be int, was %s" % type(channel)) if not isinstance(source, types.StringTypes) and \ not hasattr(source, "read"): raise TypeError("source must be string or file-like object with read() method") if isinstance(source, types.StringTypes): headers = {"name": source} fileobj = file(source, "rb") closefileobj = True else: if hasattr(source, "name"): headers = {"name": source.name} fileobj = source closefileobj = False client = OBEXClient(address, channel) client.connect() try: resp = client.put(headers, fileobj) finally: if closefileobj: fileobj.close() try: client.disconnect() except: pass # always ignore disconnection errors if resp.code != _obexcommon.OK: raise OBEXError("server denied the Put request") # --------------------------------------------------------------------- # This OBEXObjectPushServer class provides an Object Push server for the # recvfile() function, and accepts Connect, Disconnect and Put requests. # It uses the OBEXServer class in the _lightblueobex extension, which provides # a generic OBEX server class that can handle any type of requests. You can # use that class to implement other types of OBEX servers, e.g. for the File # Transfer Profile. class OBEXObjectPushServer(object): def __init__(self, fileno, fileobject): if not hasattr(fileobject, "write"): raise TypeError("fileobject must be file-like object with write() method") self.__fileobject = fileobject self.__server = _lightblueobex.OBEXServer(fileno, self.error, self.newrequest, self.requestdone) def run(self): timeout = 60 self.__gotfile = False self.__disconnected = False self.__error = None while True: result = self.__server.process(timeout) if result < 0: #print "-> error during process()" if self.__error is None: self.__error = (OBEXError, "error while running server") break if result == 0: #print "-> process() timed out" break if self.__gotfile: #print "-> got file!" break if self.__error is not None and not self.__busy: #print "-> server error detected..." break if self.__gotfile: # wait briefly for disconnect request while not self.__disconnected: if self.__server.process(3) <= 0: break if not self.__gotfile: if self.__error is not None: exc, msg = self.__error if exc == IOError: exc = OBEXError raise exc(msg) raise OBEXError("client did not send a file") def newrequest(self, opcode, reqheaders, nonheaderdata, hasbody): #print "-> newrequest", opcode, reqheaders, nonheaderdata, hasbody #print "-> incoming file name:", reqheaders.get(0x01) self.__busy = True if opcode == _lightblueobex.PUT: return (_lightblueobex.SUCCESS, {}, self.__fileobject) elif opcode in (_lightblueobex.CONNECT, _lightblueobex.DISCONNECT): return (_lightblueobex.SUCCESS, {}, None) else: return (_lightblueobex.NOT_IMPLEMENTED, {}, None) def requestdone(self, opcode): #print "-> requestdone", opcode if opcode == _lightblueobex.DISCONNECT: self.__disconnected = True elif opcode == _lightblueobex.PUT: self.__gotfile = True self.__busy = False def error(self, exc, msg): #print "-> error:", exc, msg if self.__error is not None: #print "-> (keeping previous error)" return self.__error = (exc, msg) # --------------------------------------------------------------------- def recvfile(sock, dest): if sock is None: raise TypeError("Given socket is None") if not isinstance(dest, (types.StringTypes, types.FileType)): raise TypeError("dest must be string or file-like object with write() method") if isinstance(dest, types.StringTypes): fileobj = open(dest, "wb") closefileobj = True else: fileobj = dest closefileobj = False try: conn, addr = sock.accept() # print "A client connected:", addr server = OBEXObjectPushServer(conn.fileno(), fileobj) server.run() conn.close() finally: if closefileobj: fileobj.close() lightblue-0.3.2/src/linux/_obex.pyc0000644000076500007650000002711410737641161016557 0ustar beabea00000000000000;ò BGc@sldkZdkZdkZdddfZdefd„ƒYZd„Zdefd„ƒYZd„ZdS( Nssendfilesrecvfiles OBEXClientcBsntZd„Zd„Zhd„Zhd„Zd„Zd„Zd„Ze e d„Z d „Z d „Z RS( NcCsrt|tiƒ otdt|ƒƒ‚nt|ƒtj otdt|ƒƒ‚n||_ ||_ dS(Nsaddress must be string, was %sschannel must be int, was %s( s isinstancesaddressstypess StringTypess TypeErrorstypeschannelsintsselfs_OBEXClient__addresss_OBEXClient__channel(sselfsaddressschannel((sE/Users/bea/bluetooth-api/lightblue/lightblue/trunk/src/linux/_obex.pys__init__#s  cCsd|itjoPdk}|itiƒ}|i|i |i fƒt i |i ƒƒ|_ndS(N(sselfs_OBEXClient__clientsNones bluetoothsBluetoothSockets_lightbluecommonsRFCOMMssocksconnects_OBEXClient__addresss_OBEXClient__channels_lightblueobexs OBEXClientsfileno(sselfssocks bluetooth((sE/Users/bea/bluetooth-api/lightblue/lightblue/trunk/src/linux/_obex.pys__setUp-s  cCsS|itjo|iƒn|iiti|i|ƒtƒ}|i |ƒSdS(N( sselfs_OBEXClient__clientsNones_OBEXClient__setUpsrequests_lightblueobexsCONNECTs_OBEXClient__torawheaderssheaderssresps_OBEXClient__createresponse(sselfsheaderssresp((sE/Users/bea/bluetooth-api/lightblue/lightblue/trunk/src/linux/_obex.pysconnect5s cCsS|itjo|iƒn|iiti|i|ƒtƒ}|i |ƒSdS(N( sselfs_OBEXClient__clientsNones_OBEXClient__setUpsrequests_lightblueobexs DISCONNECTs_OBEXClient__torawheaderssheaderssresps_OBEXClient__createresponse(sselfsheaderssresp((sE/Users/bea/bluetooth-api/lightblue/lightblue/trunk/src/linux/_obex.pys disconnect=s cCsàt|tiƒ ot|dƒ otdƒ‚n|itjo|i ƒnt|tiƒot |dƒ}t }n |}t}z+|iiti|i|ƒt|ƒ}Wd|o|iƒnX|i|ƒSdS(Nsreads<source must be string or file-like object with read() methodsrb(s isinstancessourcestypess StringTypesshasattrs TypeErrorsselfs_OBEXClient__clientsNones_OBEXClient__setUpsopensfileobjsTrues closefileobjsFalsesrequests_lightblueobexsPUTs_OBEXClient__torawheaderssheaderssrespscloses_OBEXClient__createresponse(sselfsheadersssources closefileobjsfileobjsresp((sE/Users/bea/bluetooth-api/lightblue/lightblue/trunk/src/linux/_obex.pysputEs % cCsS|itjo|iƒn|iiti|i|ƒtƒ}|i |ƒSdS(N( sselfs_OBEXClient__clientsNones_OBEXClient__setUpsrequests_lightblueobexsPUTs_OBEXClient__torawheaderssheaderssresps_OBEXClient__createresponse(sselfsheaderssresp((sE/Users/bea/bluetooth-api/lightblue/lightblue/trunk/src/linux/_obex.pysdelete[s cCsàt|tiƒ ot|dƒ otdƒ‚n|itjo|i ƒnt|tiƒot |dƒ}t }n |}t}z+|iiti|i|ƒt|ƒ}Wd|o|iƒnX|i|ƒSdS(Nswrites;dest must be string or file-like object with write() methodswb(s isinstancesdeststypess StringTypesshasattrs TypeErrorsselfs_OBEXClient__clientsNones_OBEXClient__setUpsopensfileobjsTrues closefileobjsFalsesrequests_lightblueobexsGETs_OBEXClient__torawheaderssheaderssrespscloses_OBEXClient__createresponse(sselfsheaderssdests closefileobjsfileobjsresp((sE/Users/bea/bluetooth-api/lightblue/lightblue/trunk/src/linux/_obex.pysgetcs % cCs«|itjo|iƒnd}to|dO}n| o|dO}ndk}|id|dfƒ}|ii t i |i |ƒt|ƒƒ}|i|ƒSdS(NiiisB(sselfs_OBEXClient__clientsNones_OBEXClient__setUpsflagss backuplevels createdirssarrays setpathdatasrequests_lightblueobexsSETPATHs_OBEXClient__torawheaderssheaderssbuffersresps_OBEXClient__createresponse(sselfsheaderss cdtoparents createdirssflagssarraysresps setpathdata((sE/Users/bea/bluetooth-api/lightblue/lightblue/trunk/src/linux/_obex.pyssetpathys cCsti|d|dƒSdS(Nii(s_lightbluecommons OBEXResponsesresp(sselfsresp((sE/Users/bea/bluetooth-api/lightblue/lightblue/trunk/src/linux/_obex.pys__createresponseˆscCs‹h}xz|iƒD]l\}}t|tiƒoti i |i ƒƒ}n|}|t jotd|ƒ‚n||| error during process()serror while running servers-> process() timed outs -> got file!s-> server error detected...i(stimeoutsFalsesselfs_OBEXObjectPushServer__gotfiles#_OBEXObjectPushServer__disconnectedsNones_OBEXObjectPushServer__errorsTrues_OBEXObjectPushServer__serversprocesssresultsIOErrors_OBEXObjectPushServer__busysexcsmsg(sselfsexcsresultstimeoutsmsg((sE/Users/bea/bluetooth-api/lightblue/lightblue/trunk/src/linux/_obex.pysrunÀs<         cCs†dG|G|G|G|GHt|_|tijoti h|i fSn>|ti ti fjoti ht fSntiht fSdS(Ns -> newrequest(sopcodes reqheaderss nonheaderdatashasbodysTruesselfs_OBEXObjectPushServer__busys_lightblueobexsPUTsSUCCESSs!_OBEXObjectPushServer__fileobjectsCONNECTs DISCONNECTsNonesNOT_IMPLEMENTED(sselfsopcodes reqheaderss nonheaderdatashasbody((sE/Users/bea/bluetooth-api/lightblue/lightblue/trunk/src/linux/_obex.pys newrequestãs cCsPdG|GH|tijo t|_n|tijo t|_nt|_ dS(Ns-> requestdone( sopcodes_lightblueobexs DISCONNECTsTruesselfs#_OBEXObjectPushServer__disconnectedsPUTs_OBEXObjectPushServer__gotfilesFalses_OBEXObjectPushServer__busy(sselfsopcode((sE/Users/bea/bluetooth-api/lightblue/lightblue/trunk/src/linux/_obex.pys requestdoneîs    cCs=dG|G|GH|itj o dGHdSn||f|_dS(Ns -> error:s-> (keeping previous error)(sexcsmsgsselfs_OBEXObjectPushServer__errorsNone(sselfsexcsmsg((sE/Users/bea/bluetooth-api/lightblue/lightblue/trunk/src/linux/_obex.pyserrorös  (s__name__s __module__s__init__sruns newrequests requestdoneserror(((sE/Users/bea/bluetooth-api/lightblue/lightblue/trunk/src/linux/_obex.pysOBEXObjectPushServer·s   # cCsÞ|tjotdƒ‚nt|titifƒ otdƒ‚nt|tiƒot|dƒ}t }n |}t }z?|i ƒ\}}t|iƒ|ƒ}|iƒ|iƒWd|o|iƒnXdS(NsGiven socket is Nones;dest must be string or file-like object with write() methodswb(ssocksNones TypeErrors isinstancesdeststypess StringTypessFileTypesopensfileobjsTrues closefileobjsFalsesacceptsconnsaddrsOBEXObjectPushServersfilenosserversrunsclose(ssocksdestsaddrs closefileobjsserversconnsfileobj((sE/Users/bea/bluetooth-api/lightblue/lightblue/trunk/src/linux/_obex.pysrecvfiles"   ( stypess_lightbluecommons_lightblueobexs__all__sobjects OBEXClientssendfilesOBEXObjectPushServersrecvfile(ssendfilesrecvfiles_lightblueobexsOBEXObjectPushServers__all__s_lightbluecommonstypess OBEXClient((sE/Users/bea/bluetooth-api/lightblue/lightblue/trunk/src/linux/_obex.pys?s   z Klightblue-0.3.2/src/linux/.__obexcommon.py0000755000076500000000000000012210750556342020367 0ustar beawheel00000000000000Mac OS X  2 RTEXTlightblue-0.3.2/src/linux/_obexcommon.py0000755000076500007650000004434310750556342017634 0ustar beabea00000000000000# Copyright (c) 2006 Bea Lam. All rights reserved. # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files # (the "Software"), to deal in the Software without restriction, # including without limitation the rights to use, copy, modify, merge, # publish, distribute, sublicense, and/or sell copies of the Software, # and to permit persons to whom the Software is furnished to do so, # subject to the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. import _lightbluecommon __all__ = ('OBEXResponse', 'OBEXError', 'CONTINUE', 'OK', 'CREATED', 'ACCEPTED', 'NON_AUTHORITATIVE_INFORMATION', 'NO_CONTENT', 'RESET_CONTENT', 'PARTIAL_CONTENT', 'MULTIPLE_CHOICES', 'MOVED_PERMANENTLY', 'MOVED_TEMPORARILY', 'SEE_OTHER', 'NOT_MODIFIED', 'USE_PROXY', 'BAD_REQUEST', 'UNAUTHORIZED', 'PAYMENT_REQUIRED', 'FORBIDDEN', 'NOT_FOUND', 'METHOD_NOT_ALLOWED', 'NOT_ACCEPTABLE', 'PROXY_AUTHENTICATION_REQUIRED', 'REQUEST_TIME_OUT', 'CONFLICT', 'GONE', 'LENGTH_REQUIRED', 'PRECONDITION_FAILED', 'REQUESTED_ENTITY_TOO_LARGE', 'REQUEST_URL_TOO_LARGE', 'UNSUPPORTED_MEDIA_TYPE', 'INTERNAL_SERVER_ERROR', 'NOT_IMPLEMENTED', 'BAD_GATEWAY', 'SERVICE_UNAVAILABLE', 'GATEWAY_TIMEOUT', 'HTTP_VERSION_NOT_SUPPORTED', 'DATABASE_FULL', 'DATABASE_LOCKED') class OBEXError(_lightbluecommon.BluetoothError): """ Generic exception raised for OBEX-related errors. """ pass class OBEXResponse: """ Contains the OBEX response received from an OBEX server. When an OBEX client sends a request, the OBEX server sends back a response code (to indicate whether the request was successful) and a set of response headers (to provide other useful information). For example, if a client sends a 'Get' request to retrieve a file, the client might get a response like this: >>> import lightblue >>> client = lightblue.obex.OBEXClient("aa:bb:cc:dd:ee:ff", 10) >>> response = client.get({"name": "file.txt"}, file("file.txt", "w")) >>> print response You can get the response code and response headers in different formats: >>> print response.reason 'OK' # a string description of the response code >>> print response.code 32 # the response code (e.g. this is 0x20) >>> print response.headers {'length': 35288} # the headers, with string keys >>> print response.rawheaders {195: 35288} # the headers, with raw header ID keys >>> Note how the 'code' attribute does not have the final bit set - e.g. for OK/Success, the response code is 0x20, not 0xA0. The lightblue.obex module defines constants for response code values (e.g. lightblue.obex.OK, lightblue.obex.FORBIDDEN, etc.). """ def __init__(self, code, rawheaders): self.__code = code self.__reason = _OBEX_RESPONSES.get(code, "Unknown response code") self.__rawheaders = rawheaders self.__headers = None code = property(lambda self: self.__code, doc='The response code, without the final bit set.') reason = property(lambda self: self.__reason, doc='A string description of the response code.') rawheaders = property(lambda self: self.__rawheaders, doc='The response headers, as a dictionary with header ID (unsigned byte) keys.') def getheader(self, header, default=None): ''' Returns the response header value for the given header, which may either be a string (not case-sensitive) or the raw byte value of the header ID. Returns the specified default value if the header is not present. ''' if isinstance(header, types.StringTypes): return self.headers.get(header.lower(), default) return self.__rawheaders.get(header, default) def __getheaders(self): if self.__headers is None: self.__headers = {} for headerid, value in self.__rawheaders.items(): if headerid in _HEADER_IDS_TO_STRINGS: self.__headers[_HEADER_IDS_TO_STRINGS[headerid]] = value else: self.__headers["0x%02x" % headerid] = value return self.__headers headers = property(__getheaders, doc='The response headers, as a dictionary with string keys.') def __repr__(self): return "" % \ (self.__reason, self.__code, (self.__code | 0x80), str(self.headers)) try: import datetime # as from python docs example class UTC(datetime.tzinfo): """UTC""" def utcoffset(self, dt): return datetime.timedelta(0) def tzname(self, dt): return "UTC" def dst(self, dt): return datetime.timedelta(0) except: pass # no datetime on pys60 _LOCAL_TIME_FORMAT = "%Y%m%dT%H%M%S" _UTC_TIME_FORMAT = _LOCAL_TIME_FORMAT + "Z" def _datetimefromstring(s): import time if s[-1:] == "Z": # add UTC() instance as tzinfo args = (time.strptime(s, _UTC_TIME_FORMAT)[0:6]) + (0, UTC()) return datetime.datetime(*args) else: return datetime.datetime(*(time.strptime(s, _LOCAL_TIME_FORMAT)[0:6])) _HEADER_STRINGS_TO_IDS = { "count": 0xc0, "name": 0x01, "type": 0x42, "length": 0xc3, "time": 0x44, "description": 0x05, "target": 0x46, "http": 0x47, "who": 0x4a, "connection-id": 0xcb, "application-parameters": 0x4c, "authentication-challenge": 0x4d, "authentication-response": 0x4e, "creator-id": 0xcf, "wan-uuid": 0x50, "object-class": 0x51, "session-parameters": 0x52, "session-sequence-number": 0x93 } _HEADER_IDS_TO_STRINGS = {} for key, value in _HEADER_STRINGS_TO_IDS.items(): _HEADER_IDS_TO_STRINGS[value] = key assert len(_HEADER_IDS_TO_STRINGS) == len(_HEADER_STRINGS_TO_IDS) # These match the associated strings in httplib.responses, since OBEX response # codes are matched to HTTP status codes (except for 0x60 and 0x61). # Note these are the responses *without* the final bit set. _OBEX_RESPONSES = { 0x10: "Continue", 0x20: "OK", 0x21: "Created", 0x22: "Accepted", 0x23: "Non-Authoritative Information", 0x24: "No Content", 0x25: "Reset Content", 0x26: "Partial Content", 0x30: "Multiple Choices", 0x31: "Moved Permanently", 0x32: "Moved Temporarily", # but is 'Found' (302) in httplib.response??? 0x33: "See Other", 0x34: "Not Modified", 0x35: "Use Proxy", 0x40: "Bad Request", 0x41: "Unauthorized", 0x42: "Payment Required", 0x43: "Forbidden", 0x44: "Not Found", 0x45: "Method Not Allowed", 0x46: "Not Acceptable", 0x47: "Proxy Authentication Required", 0x48: "Request Timeout", 0x49: "Conflict", 0x4A: "Gone", 0x48: "Length Required", 0x4C: "Precondition Failed", 0x4D: "Request Entity Too Large", 0x4E: "Request-URI Too Long", 0x4F: "Unsupported Media Type", 0x50: "Internal Server Error", 0x51: "Not Implemented", 0x52: "Bad Gateway", 0x53: "Service Unavailable", 0x54: "Gateway Timeout", 0x55: "HTTP Version Not Supported", 0x60: "Database Full", 0x61: "Database Locked" } _obexclientclassdoc = \ """ An OBEX client class. (Note this is not available on Python for Series 60.) For example, to connect to an OBEX server and send a file: >>> import lightblue >>> client = lightblue.obex.OBEXClient("aa:bb:cc:dd:ee:ff", 10) >>> client.connect() >>> client.put({"name": "photo.jpg"}, file("photo.jpg", "rb")) >>> client.disconnect() >>> A client must call connect() to establish a connection before it can send any other requests. The connect(), disconnect(), put(), delete(), get() and setpath() methods all accept the request headers as a dictionary of header-value mappings. The request headers are used to provide the server with additional information for the request. For example, this sends a Put request that includes Name, Type and Length headers in the request headers, to provide details about the transferred file: >>> f = file("file.txt") >>> client.put({"name": "file.txt", "type": "text/plain", ... "length": 5192}, f) >>> Here is a list of all the different string header keys that you can use in the request headers, and the expected type of the value for each header: - "name" -> a string - "type" -> a string - "length" -> an int - "time" -> a datetime object from the datetime module - "description" -> a string - "target" -> a string or buffer - "http" -> a string or buffer - "who" -> a string or buffer - "connection-id" -> an int - "application-parameters" -> a string or buffer - "authentication-challenge" -> a string or buffer - "authentication-response" -> a string or buffer - "creator-id" -> an int - "wan-uuid" -> a string or buffer - "object-class" -> a string or buffer - "session-parameters" -> a string or buffer - "session-sequence-number" -> an int less than 256 (The string header keys are not case-sensitive.) Alternatively, you can use raw header ID values instead of the above convenience strings. So, the previous example can be rewritten as: >>> client.put({0x01: "file.txt", 0x42: "text/plain", 0xC3: 5192}, ... fileobject) >>> This is also useful for inserting custom headers. For example, a PutImage request for a Basic Imaging client requires the Img-Descriptor (0x71) header: >>> client.put({"type": "x-bt/img-img", ... "name": "photo.jpg", ... 0x71: ''}, ... file('photo.jpg', 'rb')) >>> Notice that the connection-id header is not sent, because this is automatically included by OBEXClient in the request headers if a connection-id was received in a previous Connect response. See the included src/examples/obex_ftp_client.py for an example of using OBEXClient to implement a File Transfer client for browsing the files on a remote device. """ _obexclientdocs = { "__init__": """ Creates an OBEX client. Arguments: - address: the address of the remote device - channel: the RFCOMM channel of the remote OBEX service """, "connect": """ Establishes the Bluetooth connection to the remote OBEX server and sends a Connect request to open the OBEX session. Returns an OBEXResponse instance containing the server response. Raises lightblue.obex.OBEXError if the session is already connected, or if an error occurs during the request. If the server refuses the Connect request (i.e. if it sends a response code other than OK/Success), the Bluetooth connection will be closed. Arguments: - headers={}: the headers to send for the Connect request """, "disconnect": """ Sends a Disconnect request to end the OBEX session and closes the Bluetooth connection to the remote OBEX server. Returns an OBEXResponse instance containing the server response. Raises lightblue.obex.OBEXError if connect() has not been called, or if an error occurs during the request. Note that you don't need to send any connection-id headers - this is automatically included if the client received one in a Connect response. Arguments: - headers={}: the headers to send for the request """, "put": """ Sends a Put request. Returns an OBEXResponse instance containing the server response. Raises lightblue.obex.OBEXError if connect() has not been called, or if an error occurs during the request. Note that you don't need to send any connection-id headers - this is automatically included if the client received one in a Connect response. Arguments: - headers: the headers to send for the request - fileobj: a file-like object containing the file data to be sent for the request For example, to send a file named 'photo.jpg', using the request headers to notify the server of the file's name, MIME type and length: >>> client = lightblue.obex.OBEXClient("aa:bb:cc:dd:ee:ff", 10) >>> client.connect() >>> client.put({"name": "photo.jpg", "type": "image/jpeg", "length": 28566}, file("photo.jpg", "rb")) >>> """, "delete": """ Sends a Put-Delete request in order to delete a file or folder on the remote server. Returns an OBEXResponse instance containing the server response. Raises lightblue.obex.OBEXError if connect() has not been called, or if an error occurs during the request. Note that you don't need to send any connection-id headers - this is automatically included if the client received one in a Connect response. Arguments: - headers: the headers to send for the request - you should use the 'name' header to specify the file you want to delete If the file on the server can't be deleted because it's a read-only file, you might get an 'Unauthorized' response, like this: >>> client = lightblue.obex.OBEXClient("aa:bb:cc:dd:ee:ff", 10) >>> client.connect() >>> client.delete({"name": "random_file.txt"}) >>> """, "get": """ Sends a Get request. Returns an OBEXResponse instance containing the server response. Raises lightblue.obex.OBEXError if connect() has not been called, or if an error occurs during the request. Note that you don't need to send any connection-id headers - this is automatically included if the client received one in a Connect response. Arguments: - headers: the headers to send for the request - you should use these to specify the file you want to retrieve - fileobj: a file-like object, to which the received data will be written An example: >>> client = lightblue.obex.OBEXClient("aa:bb:cc:dd:ee:ff", 10) >>> client.connect() >>> f = file("received_file.txt", "w+") >>> client.get({"name": "testfile.txt"}, f) >>> f.seek(0) >>> f.read() 'test file' >>> """, "setpath": """ Sends a SetPath request in order to set the "current path" on the remote server for file transfers. Returns an OBEXResponse instance containing the server response. Raises lightblue.obex.OBEXError if connect() has not been called, or if an error occurs during the request. Note that you don't need to send any connection-id headers - this is automatically included if the client received one in a Connect response. Arguments: - headers: the headers to send for the request - you should use the 'name' header to specify the directory you want to change to - cdtoparent=False: True if the remote server should move up one directory before applying the specified directory (i.e. 'cd ../dirname') - createdirs=False: True if the specified directory should be created if it doesn't exist (if False, the server will return an error response if the directory doesn't exist) For example: # change to the "images" subdirectory >>> client.setpath({"name": "images"}) >>> # change to the parent directory >>> client.setpath({}, cdtoparent=True) >>> # create a subdirectory "My_Files" >>> client.setpath({"name": "My_Files"}, createdirs=True) >>> # change to the root directory - you can use an empty "name" header # to specify this >>> client.setpath({"name": ""}) >>> """ } # response constants CONTINUE = 0x10 OK = 0x20 CREATED = 0x21 ACCEPTED = 0x22 NON_AUTHORITATIVE_INFORMATION = 0x23 NO_CONTENT = 0x24 RESET_CONTENT = 0x25 PARTIAL_CONTENT = 0x26 MULTIPLE_CHOICES = 0x30 MOVED_PERMANENTLY = 0x31 MOVED_TEMPORARILY = 0x32 SEE_OTHER = 0x33 NOT_MODIFIED = 0x34 USE_PROXY = 0x35 BAD_REQUEST = 0x40 UNAUTHORIZED = 0x41 PAYMENT_REQUIRED = 0x42 FORBIDDEN = 0x43 NOT_FOUND = 0x44 METHOD_NOT_ALLOWED = 0x45 NOT_ACCEPTABLE = 0x46 PROXY_AUTHENTICATION_REQUIRED = 0x47 REQUEST_TIME_OUT = 0x48 CONFLICT = 0x49 GONE = 0x4A LENGTH_REQUIRED = 0x4B PRECONDITION_FAILED = 0x4C REQUESTED_ENTITY_TOO_LARGE = 0x4D REQUEST_URL_TOO_LARGE = 0x4E UNSUPPORTED_MEDIA_TYPE = 0x4F INTERNAL_SERVER_ERROR = 0x50 NOT_IMPLEMENTED = 0x51 BAD_GATEWAY = 0x52 SERVICE_UNAVAILABLE = 0x53 GATEWAY_TIMEOUT = 0x54 HTTP_VERSION_NOT_SUPPORTED = 0x55 DATABASE_FULL = 0x60 DATABASE_LOCKED = 0x61 lightblue-0.3.2/src/linux/lightblue_util.c0000644000076500007650000000647610533016740020127 0ustar beabea00000000000000/* * Copyright (c) 2006 Bea Lam. All rights reserved. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation files * (the "Software"), to deal in the Software without restriction, * including without limitation the rights to use, copy, modify, merge, * publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* * Extension module to access BlueZ operations not provided in PyBluez. */ #include "Python.h" #include #include #include /* * Returns name of local device */ static PyObject* lb_hci_read_local_name(PyObject *self, PyObject *args) { int err = 0; int timeout = 0; int fd = 0; char name[249]; if (!PyArg_ParseTuple(args, "ii", &fd, &timeout)) return NULL; Py_BEGIN_ALLOW_THREADS err = hci_read_local_name(fd, sizeof(name)-1, name, timeout); Py_END_ALLOW_THREADS if (err != 0) return PyErr_SetFromErrno(PyExc_IOError); return PyString_FromString(name); } /* * Returns address of local device */ static PyObject* lb_hci_read_bd_addr(PyObject *self, PyObject *args) { int err = 0; int timeout = 0; int fd = 0; bdaddr_t ba; char addrstr[19] = {0}; if (!PyArg_ParseTuple(args, "ii", &fd, &timeout)) return NULL; Py_BEGIN_ALLOW_THREADS err = hci_read_bd_addr(fd, &ba, timeout); Py_END_ALLOW_THREADS if (err != 0) return PyErr_SetFromErrno(PyExc_IOError); ba2str(&ba, addrstr); return PyString_FromString(addrstr); } /* * Returns class of device of local device as a * (service, major, minor) tuple */ static PyObject* lb_hci_read_class_of_dev(PyObject *self, PyObject *args) { int err = 0; int timeout = 0; int fd = 0; uint8_t cod[3]; if (!PyArg_ParseTuple(args, "ii", &fd, &timeout)) return NULL; Py_BEGIN_ALLOW_THREADS err = hci_read_class_of_dev(fd, cod, timeout); Py_END_ALLOW_THREADS if (err != 0) return PyErr_SetFromErrno(PyExc_IOError); return Py_BuildValue("(B,B,B)", cod[2] << 3, cod[1] & 0x1f, cod[0] >> 2); } /* list of all functions in this module */ static PyMethodDef utilmethods[] = { {"hci_read_local_name", lb_hci_read_local_name, METH_VARARGS }, {"hci_read_bd_addr", lb_hci_read_bd_addr, METH_VARARGS}, {"hci_read_class_of_dev", lb_hci_read_class_of_dev, METH_VARARGS}, { NULL, NULL } /* sentinel */ }; /* module initialization functions */ void init_lightblueutil(void) { Py_InitModule("_lightblueutil", utilmethods); } lightblue-0.3.2/src/linux/lightblueobex_client.c0000644000076500000000000003115210751225135021630 0ustar beawheel00000000000000#include "lightblueobex_client.h" #include "lightblueobex_main.h" #include "structmember.h" typedef struct { PyObject_HEAD obex_t *obex; int busy; int timeout; int sendbufsize; int resp; PyObject *resp_headers; PyObject *error; PyObject *error_msg; PyObject *fileobj; PyObject *tempbuf; } OBEXClient; static void obexclient_requestcleanup(OBEXClient *self) { DEBUG("%s()\n", __func__); Py_XDECREF(self->fileobj); self->fileobj = NULL; Py_XDECREF(self->tempbuf); self->tempbuf = NULL; } static void obexclient_seterror(OBEXClient *self, PyObject *exc, char *message) { DEBUG("%s()\n", __func__); if (exc == NULL) DEBUG("\t(resetting error)\n"); else DEBUG("\tError: %s\n", (message == NULL ? "(unknown)" : message)); if (self->error != NULL) { DEBUG("\tIgnore new error, error already set!\n"); return; } Py_XDECREF(self->error); Py_XINCREF(exc); self->error = exc; Py_XDECREF(self->error_msg); self->error_msg = ( message == NULL ? PyString_FromString("error") : PyString_FromString(message) ); } static void obexclient_feedstream(OBEXClient *self, obex_object_t *obj) { /* self->fileobj shouldn't be NULL since startrequest() doesn't set OBEX_FL_STREAM_START (and so this shouldn't be called) if fileobj is null */ Py_XDECREF(self->tempbuf); self->tempbuf = lightblueobex_filetostream(self->obex, obj, self->fileobj, self->sendbufsize); if (self->tempbuf == NULL) { obexclient_seterror(self, PyExc_IOError, "error reading file object"); } } static void obexclient_readstream(OBEXClient *self, obex_object_t *obj) { int result; result = lightblueobex_streamtofile(self->obex, obj, self->fileobj); if (result < 0) { obexclient_seterror(self, PyExc_IOError, "error writing to file object"); } } static void obexclient_requestdone(OBEXClient *self, obex_object_t *obj, int obex_cmd, int obex_rsp) { DEBUG("%s()\n", __func__); DEBUG("\tCommand: %d Response: 0x%02x\n", obex_cmd, obex_rsp); self->resp = obex_rsp; Py_XDECREF(self->resp_headers); self->resp_headers = lightblueobex_readheaders(self->obex, obj); if (self->resp_headers == NULL) PyErr_SetString(PyExc_IOError, "error reading response headers"); obexclient_requestcleanup(self); self->busy = 0; } /* can this be static? */ void obexclient_event(obex_t *handle, obex_object_t *obj, int mode, int event, int obex_cmd, int obex_rsp) { DEBUG("%s()\n", __func__); DEBUG("\tEvent: %d Command: %d\n", event, obex_cmd); OBEXClient *self = (OBEXClient *)OBEX_GetUserData(handle); switch (event) { case OBEX_EV_LINKERR: case OBEX_EV_PARSEERR: obexclient_seterror(self, PyExc_IOError, (event == OBEX_EV_LINKERR ? "connection error" : "parse error")); if (obj != NULL) { /* check a request is in progress */ obexclient_requestdone(self, obj, obex_cmd, obex_rsp); } break; case OBEX_EV_STREAMEMPTY: obexclient_feedstream(self, obj); break; case OBEX_EV_STREAMAVAIL: obexclient_readstream(self, obj); break; case OBEX_EV_REQDONE: obexclient_requestdone(self, obj, obex_cmd, obex_rsp); break; default: /* ignore abort for now - can't be done with synchronous requests */ break; } } static int obexclient_startrequest(OBEXClient *self, int cmd, PyObject *headers, PyObject *nonhdrdata) { const uint8_t *nonhdrdata_raw; Py_ssize_t nonhdrdata_len; DEBUG("%s()\n", __func__); if (!PyDict_Check(headers)) { PyErr_Format(PyExc_TypeError, "headers must be dict, was %s", headers->ob_type->tp_name); return -1; } if (nonhdrdata != Py_None && !PyBuffer_Check(nonhdrdata)) { PyErr_Format(PyExc_TypeError, "nonhdrdata must be buffer or None, was %s", nonhdrdata->ob_type->tp_name); return -1; } if (self->busy) { PyErr_SetString(PyExc_IOError, "another request is in progress"); return -1; } obex_object_t *obj = OBEX_ObjectNew(self->obex, cmd); if (obj == NULL) { PyErr_SetString(PyExc_IOError, "error starting new request"); return -1; } if (lightblueobex_addheaders(self->obex, headers, obj) < 0) { OBEX_ObjectDelete(self->obex, obj); PyErr_SetString(PyExc_IOError, "error setting request headers"); return -1; } if (nonhdrdata != Py_None) { if (PyObject_AsReadBuffer(nonhdrdata, (const void **)&nonhdrdata_raw, &nonhdrdata_len) < 0) { /* PyObject_AsReadBuffer() sets exception if error */ OBEX_ObjectDelete(self->obex, obj); return -1; } if (OBEX_ObjectSetNonHdrData(obj, nonhdrdata_raw, nonhdrdata_len) < 0) { OBEX_ObjectDelete(self->obex, obj); PyErr_SetString(PyExc_IOError, "error setting request non-header data"); return -1; } } if (self->fileobj != NULL) { if (cmd == OBEX_CMD_PUT) { /* don't stream data if there is no fileobj (i.e. a Put-Delete) */ obex_headerdata_t hv; if (OBEX_ObjectAddHeader(self->obex, obj, OBEX_HDR_BODY, hv, 0, OBEX_FL_STREAM_START) < 0) { PyErr_SetString(PyExc_IOError, "error setting streaming mode for Put"); return -1; } } else if (cmd == OBEX_CMD_GET) { if (OBEX_ObjectReadStream(self->obex, obj, NULL) < 0) { PyErr_SetString(PyExc_IOError, "error setting streaming mode for Get"); return -1; } } } /* reset data for the new request */ self->busy = 1; obexclient_seterror(self, NULL, NULL); self->resp = 0x20; Py_XDECREF(self->resp_headers); self->resp_headers = NULL; if (OBEX_Request(self->obex, obj) < 0) { PyErr_SetString(PyExc_IOError, "error sending request"); return -1; } return 1; } static PyObject * OBEXClient_request(OBEXClient *self, PyObject *args) { PyObject *tmp; int result; int cmd; PyObject *headers; PyObject *nonhdrdata; PyObject *fileobj = NULL; /* optional */ DEBUG("%s()\n", __func__); if (!PyArg_ParseTuple(args, "iO!O|O", &cmd, &PyDict_Type, &headers, &nonhdrdata, &fileobj)) { return NULL; } if (fileobj != NULL) { char *method; method = ( cmd == OBEX_CMD_PUT ? "read" : "write" ); if (!PyObject_HasAttrString(fileobj, method)) { PyErr_Format(PyExc_AttributeError, "file-like object must have %s() method", method); return NULL; } tmp = self->fileobj; Py_INCREF(fileobj); self->fileobj = fileobj; Py_XDECREF(tmp); } if (obexclient_startrequest(self, cmd, headers, nonhdrdata) < 0) { obexclient_requestcleanup(self); return NULL; } /* wait until request is complete */ while (self->busy) { result = OBEX_HandleInput(self->obex, self->timeout); if (result < 0) { obexclient_requestcleanup(self); obexclient_seterror(self, PyExc_IOError, "error processing input"); break; } else if (result == 0) { obexclient_requestcleanup(self); obexclient_seterror(self, PyExc_IOError, "input processing timed out"); break; } } /* request is now complete, obexclient_requestcleanup() will have been called */ if (self->error) { PyErr_SetObject(self->error, self->error_msg); return NULL; } /* Don't throw exceptions for non-success responses. The caller can decide whether that should be done. */ return Py_BuildValue("(iO)", self->resp, self->resp_headers); } PyDoc_STRVAR(OBEXClient_request__doc__, "request(opcode, headers, nonheaderdata [, fileobj]) -> response\n\n\ Sends an OBEX request and returns the server response code. \ Provide a file-like object if performing a Put or Get request. \ The nonheaderdata is really only useful for specifying the flags for SetPath \ requests. For other requests, set this value to None."); static PyObject * OBEXClient_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { OBEXClient *self; self = (OBEXClient *)type->tp_alloc(type, 0); if (self != NULL) { self->obex = NULL; self->busy = 0; self->timeout = 10; /* seconds */ self->sendbufsize = 4096; self->resp = 0; self->resp_headers = NULL; self->error = NULL; self->error_msg = PyString_FromString(""); if (self->error_msg == NULL) { Py_DECREF(self); return NULL; } self->fileobj = NULL; self->tempbuf = NULL; } return (PyObject *)self; } static int OBEXClient_init(OBEXClient *self, PyObject *args, PyObject *kwds) { int fd = -1; int writefd = -1; int mtu = 1024; unsigned int flags = 0; static char *kwlist[] = { "fd", "writefd", "mtu", "flags", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "i|iiI", kwlist, &fd, &writefd, &mtu, &flags)) { return -1; } if (self->obex == NULL) { self->obex = OBEX_Init(OBEX_TRANS_FD, obexclient_event, flags); if (self->obex == NULL) { PyErr_SetString(PyExc_IOError, "error creating OBEX object"); return -1; } if (writefd == -1) writefd = fd; if (FdOBEX_TransportSetup(self->obex, fd, writefd, mtu) < 0) { PyErr_SetString(PyExc_IOError, "error initialising transport"); return -1; } } OBEX_SetUserData(self->obex, self); return 0; } static void OBEXClient_dealloc(OBEXClient *self) { DEBUG("%s()\n", __func__); if (self->obex) OBEX_Cleanup(self->obex); Py_XDECREF(self->error); Py_XDECREF(self->error_msg); Py_XDECREF(self->fileobj); Py_XDECREF(self->tempbuf); self->ob_type->tp_free((PyObject *)self); } static PyMemberDef OBEXClient_members[] = { {"timeout", T_INT, offsetof(OBEXClient, timeout), 0, "timeout for each request"}, {"sendbufsize", T_INT, offsetof(OBEXClient, sendbufsize), 0, "size of each data chunk to read from the file object for a Put request"}, {NULL} /* Sentinel */ }; static PyMethodDef OBEXClient_methods[] = { { "request", (PyCFunction)OBEXClient_request, METH_VARARGS, OBEXClient_request__doc__, }, {NULL} /* Sentinel */ }; static PyTypeObject OBEXClientType = { PyObject_HEAD_INIT(NULL) 0, /*ob_size*/ "obexclient.OBEXClient", /*tp_name*/ sizeof(OBEXClient), /*tp_basicsize*/ 0, /*tp_itemsize*/ (destructor)OBEXClient_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ 0, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash */ 0, /*tp_call*/ 0, /*tp_str*/ 0, /*tp_getattro*/ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ "An OBEX client class.", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ OBEXClient_methods, /* tp_methods */ OBEXClient_members, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc)OBEXClient_init, /* tp_init */ 0, /* tp_alloc */ OBEXClient_new, /* tp_new */ }; PyTypeObject *lightblueobex_getclienttype(void) { return &OBEXClientType; } lightblue-0.3.2/src/linux/._lightblueobex_client.h0000644000076500000000000000012210717030255022042 0ustar beawheel00000000000000Mac OS X  2 RTEXT!Rchlightblue-0.3.2/src/linux/lightblueobex_client.h0000644000076500007650000000021610717030255021276 0ustar beabea00000000000000#ifndef LIGHTBLUEOBEX_CLIENT_H #define LIGHTBLUEOBEX_CLIENT_H #include "Python.h" PyTypeObject *lightblueobex_getclienttype(void); #endif lightblue-0.3.2/src/linux/lightblueobex_main.c0000644000076500000000000004132010765211704021276 0ustar beawheel00000000000000#include "lightblueobex_main.h" #include "lightblueobex_client.h" #include "lightblueobex_server.h" #include #include "Python.h" #define OBEX_HI_MASK 0xc0 #define OBEX_UNICODE 0x00 #define OBEX_BYTE_STREAM 0x40 #define OBEX_BYTE 0x80 #define OBEX_INT 0xc0 /* OBEX unicode strings are UTF-16, big-endian. */ #define OBEX_BIG_ENDIAN 1 /* for encoding/decoding unicode strings */ PyObject *lightblueobex_readheaders(obex_t *obex, obex_object_t *obj) { PyObject *headers; uint8_t hi; obex_headerdata_t hv; uint32_t hv_size; int r; PyObject *value = NULL; DEBUG("%s()\n", __func__); headers = PyDict_New(); if (headers == NULL) return NULL; if (obex == NULL || obj == NULL || headers == NULL) { DEBUG("\treadheaders() got null argument\n"); return NULL; } if (!PyDict_Check(headers)) { DEBUG("\treadheaders() arg must be dict\n"); return NULL; } while (OBEX_ObjectGetNextHeader(obex, obj, &hi, &hv, &hv_size)) { DEBUG("\tread header: 0x%02x\n", hi); switch (hi & OBEX_HI_MASK) { case OBEX_UNICODE: { if (hv_size < 2) { value = PyUnicode_FromUnicode(NULL, 0); } else { /* hv_size-2 for 2-byte null terminator */ int byteorder = OBEX_BIG_ENDIAN; value = PyUnicode_DecodeUTF16((const char*)hv.bs, hv_size-2, NULL, &byteorder); if (value == NULL) { DEBUG("\terror reading unicode header 0x%02x\n", hi); if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); /* let caller set exception */ } return NULL; } } break; } case OBEX_BYTE_STREAM: { value = PyBuffer_New(hv_size); if (value != NULL) { void *buf; Py_ssize_t buflen; if (PyObject_AsWriteBuffer(value, &buf, &buflen) < 0) { Py_DECREF(value); DEBUG("\terror writing to buffer for header 0x%02x\n", hi); return NULL; } memcpy(buf, hv.bs, buflen); } break; } case OBEX_BYTE: { value = PyInt_FromLong(hv.bq1); break; } case OBEX_INT: { value = PyLong_FromUnsignedLong(hv.bq4); break; } default: DEBUG("\tunknown header id encoding %d\n", (hi & OBEX_HI_MASK)); return NULL; } if (value == NULL) { if (PyErr_Occurred() == NULL) DEBUG("\terror reading headers\n"); return NULL; } r = PyDict_SetItem(headers, PyInt_FromLong((long)hi), value); Py_DECREF(value); if (r < 0) { DEBUG("\tPyDict_SetItem() error\n"); if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); /* let caller set exception */ } return NULL; } } return headers; } static int lightblueobex_add4byteheader(obex_t *obex, obex_object_t *obj, uint8_t hi, PyObject *value) { obex_headerdata_t hv; uint32_t intvalue; DEBUG("%s()\n", __func__); if (value == NULL) return -1; if (PyInt_Check(value)) { intvalue = PyInt_AsLong(value); if (PyErr_Occurred()) { DEBUG("\tcan't convert header 0x%02x int value to long", hi); PyErr_Clear(); return -1; } } else if (PyLong_Check(value)) { intvalue = PyLong_AsUnsignedLong(value); if (PyErr_Occurred()) { DEBUG("\tcan't convert header 0x%02x long value to long", hi); PyErr_Clear(); return -1; } } else { DEBUG("\theader value for id 0x%02x must be int or long, was %s\n", hi, value->ob_type->tp_name); return -1; } hv.bq4 = intvalue; return OBEX_ObjectAddHeader(obex, obj, hi, hv, 4, OBEX_FL_FIT_ONE_PACKET); } static int lightblueobex_addbytestreamheader(obex_t *obex, obex_object_t *obj, uint8_t hi, PyObject *bufObject) { obex_headerdata_t hv; uint32_t hv_size; DEBUG("%s()\n", __func__); if (PyObject_AsReadBuffer(bufObject, (const void**)&hv.bs, (Py_ssize_t*)&hv_size) < 0) { DEBUG("\theader value for id 0x%02x must be readable buffer\n", hi); return -1; } return OBEX_ObjectAddHeader(obex, obj, hi, hv, hv_size, OBEX_FL_FIT_ONE_PACKET); } static int lightblueobex_addunicodeheader(obex_t *obex, obex_object_t *obj, uint8_t hi, PyObject *utf16string) { obex_headerdata_t hv; Py_ssize_t len = PyUnicode_GET_SIZE(utf16string); uint8_t bytes[len+2]; DEBUG("%s()\n", __func__); /* need to add 2-byte null terminator */ memcpy(bytes, (const uint8_t*)PyString_AsString(utf16string), len); bytes[len] = 0x00; bytes[len+1] = 0x00; hv.bs = bytes; return OBEX_ObjectAddHeader(obex, obj, (uint8_t)hi, hv, len+2, OBEX_FL_FIT_ONE_PACKET); } int lightblueobex_addheaders(obex_t *obex, PyObject *headers, obex_object_t *obj) { uint8_t hi; obex_headerdata_t hv; PyObject *key, *value; Py_ssize_t pos = 0; int r = -1; DEBUG("%s()\n", __func__); if (headers == NULL || !PyDict_Check(headers)) { DEBUG("\taddheaders() arg must be dict\n"); return -1; } /* add connection-id first */ key = PyInt_FromLong(OBEX_HDR_CONNECTION); if (key != NULL) { value = PyDict_GetItem(headers, key); /* don't decref! */ Py_DECREF(key); key = NULL; if (value != NULL) { DEBUG("\tadding connection-id\n"); r = lightblueobex_add4byteheader(obex, obj, OBEX_HDR_CONNECTION, value); if (r < 0) { DEBUG("\terror adding connection-id header\n"); return -1; } } } /* add target header first (shouldn't have both conn-id and target) */ key = PyInt_FromLong(OBEX_HDR_TARGET); if (key != NULL) { value = PyDict_GetItem(headers, key); /* don't decref! */ Py_DECREF(key); key = NULL; if (value != NULL) { DEBUG("\tadding target\n"); r = lightblueobex_addbytestreamheader(obex, obj, OBEX_HDR_TARGET, value); if (r < 0) { DEBUG("\terror adding target header\n"); return -1; } } } while (PyDict_Next(headers, &pos, &key, &value)) { if (key == NULL || value == NULL) { DEBUG("\terror reading headers dict\n"); return -1; } if (!PyInt_Check(key)) { DEBUG("\theader id must be int, was %s\n", key->ob_type->tp_name); return -1; } hi = (uint8_t)PyInt_AsUnsignedLongMask(key); if (hi == OBEX_HDR_CONNECTION || hi == OBEX_HDR_TARGET) { /* these are already added */ continue; } DEBUG("\tadding header: 0x%02x\n", hi); switch (hi & OBEX_HI_MASK) { case OBEX_UNICODE: { PyObject *encoded = NULL; if (PyUnicode_Check(value)) { encoded = PyUnicode_EncodeUTF16(PyUnicode_AS_UNICODE(value), PyUnicode_GET_SIZE(value), NULL, OBEX_BIG_ENDIAN); } else { /* try converting to unicode */ PyObject *tmp = NULL; tmp = PyUnicode_FromObject(value); if (tmp == NULL) { if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); /* let caller set exception */ } DEBUG("\tfailed to convert header value for id 0x%02x to unicode\n", hi); return -1; } encoded = PyUnicode_EncodeUTF16(PyUnicode_AS_UNICODE(tmp), PyUnicode_GET_SIZE(tmp), NULL, OBEX_BIG_ENDIAN); Py_DECREF(tmp); } if (encoded == NULL) { if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); /* let caller set exception */ } DEBUG("\tfailed to encode value for header 0x%02x to UTF-16 big endian\n", hi); return -1; } r = lightblueobex_addunicodeheader(obex, obj, hi, encoded); Py_DECREF(encoded); break; } case OBEX_BYTE_STREAM: { r = lightblueobex_addbytestreamheader(obex, obj, hi, value); break; } case OBEX_BYTE: { long intvalue; if (!PyInt_Check(value)) { DEBUG("\theader value for id 0x%02x must be int, was %s\n", hi, value->ob_type->tp_name); return -1; } intvalue = PyInt_AsLong(value); if (PyErr_Occurred()) { DEBUG("\terror reading int value for 0x%02x\n", hi); PyErr_Clear(); return -1; } hv.bq1 = (uint8_t)intvalue; r = OBEX_ObjectAddHeader(obex, obj, hi, hv, 1, OBEX_FL_FIT_ONE_PACKET); break; } case OBEX_INT: { r = lightblueobex_add4byteheader(obex, obj, hi, value); break; } default: DEBUG("\tunknown header id encoding %d\n", (hi & OBEX_HI_MASK)); return -1; } if (r < 0) { DEBUG("\terror adding header 0x%02x\n", hi); return -1; } } return 1; } PyObject *lightblueobex_filetostream(obex_t *obex, obex_object_t *obj, PyObject *fileobj, int bufsize) { const void *data; Py_ssize_t datalen; /* or unsigned int? */ obex_headerdata_t hv; PyObject *buf; DEBUG("%s()\n", __func__); if (fileobj == NULL) { DEBUG("\tgiven file object is NULL\n"); hv.bs = NULL; OBEX_ObjectAddHeader(obex, obj, OBEX_HDR_BODY, hv, 0, OBEX_FL_STREAM_DATAEND); return NULL; } buf = PyObject_CallMethod(fileobj, "read", "i", bufsize); if (buf == NULL) { if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); /* let caller set exception */ } DEBUG("\terror calling file object read()\n"); } if (buf != NULL && !PyObject_CheckReadBuffer(buf)) { DEBUG("\tfile object read() returned non-buffer object\n"); Py_DECREF(buf); buf = NULL; } if (buf != NULL && PyObject_AsReadBuffer(buf, &data, &datalen) < 0) { DEBUG("\terror reading file object contents\n"); Py_DECREF(buf); buf = NULL; } if (buf == NULL) { hv.bs = NULL; OBEX_ObjectAddHeader(obex, obj, OBEX_HDR_BODY, hv, 0, OBEX_FL_STREAM_DATAEND); return NULL; } hv.bs = (uint8_t*)data; if (OBEX_ObjectAddHeader(obex, obj, OBEX_HDR_BODY, hv, datalen, (datalen == 0 ? OBEX_FL_STREAM_DATAEND : OBEX_FL_STREAM_DATA)) < 0) { DEBUG("\terror adding body data\n"); Py_DECREF(buf); buf = NULL; } return buf; } int lightblueobex_streamtofile(obex_t *obex, obex_object_t *obj, PyObject *fileobj) { const uint8_t *buf; int buflen; DEBUG("%s()\n", __func__); if (fileobj == NULL) return -1; buflen = OBEX_ObjectReadStream(obex, obj, &buf); if (buflen == 0) return 0; if (buflen < 0) { DEBUG("\tunable to read body data from request\n"); return -1; } DEBUG("\treading %d bytes\n", buflen); PyObject *pybuf = PyBuffer_FromMemory((void*)buf, buflen); if (pybuf == NULL) { DEBUG("\terror reading received body\n"); return -1; } PyObject *result = PyObject_CallMethod(fileobj, "write", "O", pybuf); Py_DECREF(pybuf); if (result == NULL) { if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); /* let caller set exception */ } DEBUG("error calling write() on file object\n"); return -1; } Py_DECREF(result); return buflen; } static PyMethodDef module_methods[] = { {NULL} /* Sentinel */ }; #ifndef PyMODINIT_FUNC /* declarations for DLL import/export */ #define PyMODINIT_FUNC void #endif PyMODINIT_FUNC init_lightblueobex(void) { PyObject *m; PyTypeObject *clientType; PyTypeObject *serverType; clientType = lightblueobex_getclienttype(); serverType = lightblueobex_getservertype(); if ( (PyType_Ready(clientType) < 0) || (PyType_Ready(serverType) < 0) ) { return; } m = Py_InitModule3("_lightblueobex", module_methods, "Module containing the OBEXClient and OBEXServer classes."); if (m == NULL) return; PyModule_AddIntConstant(m, "CONNECT", 0x00); PyModule_AddIntConstant(m, "DISCONNECT", 0x01); PyModule_AddIntConstant(m, "PUT", 0x02); PyModule_AddIntConstant(m, "GET", 0x03); PyModule_AddIntConstant(m, "SETPATH", 0x05); PyModule_AddIntConstant(m, "SESSION", 0x07); PyModule_AddIntConstant(m, "ABORT", 0x7f); /* or should these constants be in the linux obex module? */ PyModule_AddIntConstant(m, "COUNT", 0xc0); PyModule_AddIntConstant(m, "NAME", 0x01); PyModule_AddIntConstant(m, "TYPE", 0x42); PyModule_AddIntConstant(m, "LENGTH", 0xc3); PyModule_AddIntConstant(m, "TIME", 0x44); PyModule_AddIntConstant(m, "DESCRIPTION", 0x05); PyModule_AddIntConstant(m, "TARGET", 0x46); PyModule_AddIntConstant(m, "HTTP", 0x47); PyModule_AddIntConstant(m, "BODY", 0x48); PyModule_AddIntConstant(m, "END_OF_BODY", 0x49); PyModule_AddIntConstant(m, "WHO", 0x4a); PyModule_AddIntConstant(m, "CONNECTION_ID", 0xcb); PyModule_AddIntConstant(m, "APP_PARAMETERS", 0x4c); PyModule_AddIntConstant(m, "AUTH_CHALLENGE", 0x4d); PyModule_AddIntConstant(m, "AUTH_RESPONSE", 0x4e); PyModule_AddIntConstant(m, "CREATOR", 0xcf); PyModule_AddIntConstant(m, "WAN_UUID", 0x50); PyModule_AddIntConstant(m, "OBJECT_CLASS", 0x51); PyModule_AddIntConstant(m, "SESSION_PARAMETERS", 0x52); PyModule_AddIntConstant(m, "SESSION_SEQUENCE_NUMBER", 0x93); PyModule_AddIntConstant(m, "CONTINUE", 0x10); PyModule_AddIntConstant(m, "SWITCH_PRO", 0x11); PyModule_AddIntConstant(m, "SUCCESS", 0x20); PyModule_AddIntConstant(m, "CREATED", 0x21); PyModule_AddIntConstant(m, "ACCEPTED", 0x22); PyModule_AddIntConstant(m, "NON_AUTHORITATIVE", 0x23); PyModule_AddIntConstant(m, "NO_CONTENT", 0x24); PyModule_AddIntConstant(m, "RESET_CONTENT", 0x25); PyModule_AddIntConstant(m, "PARTIAL_CONTENT", 0x26); PyModule_AddIntConstant(m, "MULTIPLE_CHOICES", 0x30); PyModule_AddIntConstant(m, "MOVED_PERMANENTLY", 0x31); PyModule_AddIntConstant(m, "MOVED_TEMPORARILY", 0x32); PyModule_AddIntConstant(m, "SEE_OTHER", 0x33); PyModule_AddIntConstant(m, "NOT_MODIFIED", 0x34); PyModule_AddIntConstant(m, "USE_PROXY", 0x35); PyModule_AddIntConstant(m, "BAD_REQUEST", 0x40); PyModule_AddIntConstant(m, "UNAUTHORIZED", 0x41); PyModule_AddIntConstant(m, "PAYMENT_REQUIRED", 0x42); PyModule_AddIntConstant(m, "FORBIDDEN", 0x43); PyModule_AddIntConstant(m, "NOT_FOUND", 0x44); PyModule_AddIntConstant(m, "METHOD_NOT_ALLOWED", 0x45); PyModule_AddIntConstant(m, "NOT_ACCEPTABLE", 0x46); PyModule_AddIntConstant(m, "PROXY_AUTH_REQUIRED", 0x47); PyModule_AddIntConstant(m, "REQUEST_TIME_OUT", 0x48); PyModule_AddIntConstant(m, "CONFLICT", 0x49); PyModule_AddIntConstant(m, "GONE", 0x4a); PyModule_AddIntConstant(m, "LENGTH_REQUIRED", 0x4b); PyModule_AddIntConstant(m, "PRECONDITION_FAILED", 0x4c); PyModule_AddIntConstant(m, "REQ_ENTITY_TOO_LARGE", 0x4d); PyModule_AddIntConstant(m, "REQ_URL_TOO_LARGE", 0x4e); PyModule_AddIntConstant(m, "UNSUPPORTED_MEDIA_TYPE", 0x4f); PyModule_AddIntConstant(m, "INTERNAL_SERVER_ERROR", 0x50); PyModule_AddIntConstant(m, "NOT_IMPLEMENTED", 0x51); PyModule_AddIntConstant(m, "BAD_GATEWAY", 0x52); PyModule_AddIntConstant(m, "SERVICE_UNAVAILABLE", 0x53); PyModule_AddIntConstant(m, "GATEWAY_TIMEOUT", 0x54); PyModule_AddIntConstant(m, "VERSION_NOT_SUPPORTED", 0x55); PyModule_AddIntConstant(m, "DATABASE_FULL", 0x60); PyModule_AddIntConstant(m, "DATABASE_LOCKED", 0x61); Py_INCREF(clientType); PyModule_AddObject(m, "OBEXClient", (PyObject *)clientType); Py_INCREF(serverType); PyModule_AddObject(m, "OBEXServer", (PyObject *)serverType); } lightblue-0.3.2/src/linux/._lightblueobex_main.h0000644000076500000000000000012210747221507021515 0ustar beawheel00000000000000Mac OS X  2 RTEXT!Rchlightblue-0.3.2/src/linux/lightblueobex_main.h0000644000076500007650000000154110747221507020753 0ustar beabea00000000000000#ifndef LIGHTBLUEOBEX_MAIN_H #define LIGHTBLUEOBEX_MAIN_H #include "Python.h" #include /* compatibility with python versions before 2.5 (PEP 353) */ #if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN) typedef int Py_ssize_t; #define PY_SSIZE_T_MAX INT_MAX #define PY_SSIZE_T_MIN INT_MIN #endif #ifndef LIGHTBLUE_DEBUG #define LIGHTBLUE_DEBUG 0 #endif #if LIGHTBLUE_DEBUG #define DEBUG(format, args...) fprintf(stderr, format, ##args) #else #define DEBUG(format, args...) #endif PyObject *lightblueobex_readheaders(obex_t *obex, obex_object_t *obj); int lightblueobex_addheaders(obex_t *obex, PyObject *headers, obex_object_t *obj); PyObject *lightblueobex_filetostream(obex_t *obex, obex_object_t *obj, PyObject *fileobj, int bufsize); int lightblueobex_streamtofile(obex_t *obex, obex_object_t *obj, PyObject *fileobj); #endif lightblue-0.3.2/src/linux/lightblueobex_server.c0000644000076500000000000004047310765211706021672 0ustar beawheel00000000000000#include "lightblueobex_server.h" #include "lightblueobex_main.h" #include "structmember.h" //#define LIGHTBLUEOBEX_SERVER_TEST typedef struct { PyObject_HEAD obex_t *obex; int sendbufsize; PyObject *cb_error; PyObject *cb_newrequest; PyObject *cb_requestdone; int notifiednewrequest; int hasbodydata; PyObject *fileobj; PyObject *tempbuf; #ifdef LIGHTBLUEOBEX_SERVER_TEST uint32_t puttotal; #endif } OBEXServer; static void obexserver_error(OBEXServer *self, PyObject *type, PyObject *value) { PyObject *result; PyObject *tmpValue; DEBUG("%s()\n", __func__); if (self->cb_error == NULL) { /* shouldn't happen, checked in init */ DEBUG("\tcb_error() is NULL"); return; } tmpValue = (value == NULL ? PyString_FromString("server error") : value); result = PyObject_CallFunctionObjArgs(self->cb_error, (type == NULL ? PyExc_IOError : type), value, NULL); if (result == NULL) DEBUG("\tfailed to call cb_error()\n"); else Py_DECREF(result); /* don't want exceptions to be raised */ /*PyErr_Clear();*/ DEBUG("\terror was:\n"); PyErr_Print(); /* clears the error indicator */ } static void obexserver_errorstr(OBEXServer *self, PyObject *exc, char *message) { PyObject *msgobj; DEBUG("%s() - error with string msg\n", __func__); msgobj = PyString_FromString(message); obexserver_error(self, exc, msgobj); Py_XDECREF(msgobj); } static void obexserver_errorfetch(OBEXServer *self) { DEBUG("%s() - load from set error\n", __func__); if (!PyErr_Occurred()) { DEBUG("\tno error set, raise generic error\n"); obexserver_error(self, NULL, NULL); } else { PyObject *pType; PyObject *pValue; PyObject *pTraceback; PyErr_Fetch(&pType, &pValue, &pTraceback); /* value, TB can be null */ obexserver_error(self, pType, pValue); Py_XDECREF(pType); Py_XDECREF(pValue); Py_XDECREF(pTraceback); } } /* Returns new reference that must be decref'd. */ static PyObject* obexserver_notifynewrequest(OBEXServer *self, obex_object_t *obj, int obex_cmd, int *respcode) { PyObject *resp; PyObject *respheaders; PyObject *tmpfileobj; PyObject *reqheaders; int nonhdrdata_len; PyObject *nonhdrdata_obj; uint8_t *nonhdrdata; DEBUG("%s() cmd=%d\n", __func__, obex_cmd); if (self->notifiednewrequest) { DEBUG("\tAlready called cb_newrequest"); return NULL; } if (self->cb_newrequest == NULL) { /* shouldn't happen */ obexserver_errorstr(self, PyExc_IOError, "cb_newrequest is NULL"); return NULL; } reqheaders = lightblueobex_readheaders(self->obex, obj); if (reqheaders == NULL) { obexserver_errorstr(self, PyExc_IOError, "error reading request headers"); return NULL; } nonhdrdata_len = OBEX_ObjectGetNonHdrData(obj, &nonhdrdata); if (nonhdrdata_len < 0) { obexserver_errorstr(self, PyExc_IOError, "error reading non-header data"); return NULL; } nonhdrdata_obj = PyBuffer_FromMemory(nonhdrdata, (Py_ssize_t)nonhdrdata_len); if (nonhdrdata_obj == NULL) { obexserver_errorstr(self, PyExc_IOError, "error reading non-header buffer"); return NULL; } resp = PyObject_CallFunction(self->cb_newrequest, "iOOO", obex_cmd, reqheaders, nonhdrdata_obj, (self->hasbodydata ? Py_True : Py_False)); Py_DECREF(nonhdrdata_obj); self->notifiednewrequest = 1; if (resp == NULL) { DEBUG("\terror calling cb_newrequest\n"); obexserver_errorfetch(self); return NULL; } if ( !PyTuple_Check(resp) || PyTuple_Size(resp) < 3 || !PyInt_Check(PyTuple_GetItem(resp, 0)) || !PyDict_Check(PyTuple_GetItem(resp, 1)) ) { obexserver_errorstr(self, PyExc_TypeError, "callback must return (int, dict, fileobj | None) tuple"); return NULL; } tmpfileobj = PyTuple_GetItem(resp, 2); if (obex_cmd == OBEX_CMD_PUT && self->hasbodydata && !PyObject_HasAttrString(tmpfileobj, "write")) { obexserver_errorstr(self, PyExc_ValueError, "specified file object does not have 'write' method for Put request"); return NULL; } if (obex_cmd == OBEX_CMD_GET && !PyObject_HasAttrString(tmpfileobj, "read")) { obexserver_errorstr(self, PyExc_ValueError, "specified file object does not have 'read' method for Get request"); return NULL; } *respcode = PyInt_AsLong(PyTuple_GetItem(resp, 0)); if (PyErr_Occurred()) { PyErr_Clear(); obexserver_errorstr(self, PyExc_IOError, "error reading returned response code"); return NULL; } Py_XDECREF(self->fileobj); Py_INCREF(tmpfileobj); self->fileobj = tmpfileobj; respheaders = PyTuple_GetItem(resp, 1); Py_INCREF(respheaders); return respheaders; } static int obexserver_setresponse(OBEXServer *self, obex_object_t *obj, int responsecode, PyObject *responseheaders) { DEBUG("%s()\n", __func__); if (responseheaders != NULL) { if (lightblueobex_addheaders(self->obex, responseheaders, obj) < 0) { obexserver_errorstr(self, PyExc_IOError, "error setting response headers"); OBEX_ObjectSetRsp(obj, OBEX_RSP_INTERNAL_SERVER_ERROR, OBEX_RSP_INTERNAL_SERVER_ERROR); return -1; } } if (responsecode == OBEX_RSP_SUCCESS || responsecode == OBEX_RSP_CONTINUE) { DEBUG("\tAccepting request...\n"); OBEX_ObjectSetRsp(obj, OBEX_RSP_CONTINUE, OBEX_RSP_SUCCESS); } else { DEBUG("\tRefusing request (%s)...\n", OBEX_ResponseToString(responsecode)); OBEX_ObjectSetRsp(obj, responsecode, responsecode); } return 1; } static void obexserver_receivedrequest(OBEXServer *self, obex_object_t *obj, int obex_cmd) { int respcode; PyObject *respheaders; DEBUG("%s()\n", __func__); // Put requests are taken care of in obexserver_streamavailable() if (obex_cmd == OBEX_CMD_PUT && self->hasbodydata) { #ifdef LIGHTBLUEOBEX_SERVER_TEST obex_headerdata_t hv; hv.bq4 = self->puttotal; OBEX_ObjectAddHeader(self->obex, obj, OBEX_HDR_LENGTH, hv, 4, 0); #endif return; } respheaders = obexserver_notifynewrequest(self, obj, obex_cmd, &respcode); if (respheaders == NULL) { obexserver_setresponse(self, obj, OBEX_RSP_INTERNAL_SERVER_ERROR, NULL); } else { int result; result = obexserver_setresponse(self, obj, respcode, respheaders); /* for Get requests, indicate we want OBEX_EV_STREAMEMPTY events */ if (result >= 0 && obex_cmd == OBEX_CMD_GET && (respcode == OBEX_RSP_CONTINUE || respcode == OBEX_RSP_SUCCESS)) { obex_headerdata_t hv; hv.bs = NULL; OBEX_ObjectAddHeader(self->obex, obj, OBEX_HDR_BODY, hv, 0, OBEX_FL_STREAM_START); } } Py_XDECREF(respheaders); } static void obexserver_streamavailable(OBEXServer *self, obex_object_t *obj) { DEBUG("%s()\n", __func__); /* if got OBEX_EV_STREAMAVAIL, it means the request contains body data (and therefore is not a Put-Delete) */ self->hasbodydata = 1; if (!self->notifiednewrequest) { PyObject *respheaders; int respcode; respheaders = obexserver_notifynewrequest(self, obj, OBEX_CMD_PUT, &respcode); if (respheaders == NULL) { obexserver_setresponse(self, obj, OBEX_RSP_INTERNAL_SERVER_ERROR, NULL); return; } obexserver_setresponse(self, obj, respcode, respheaders); Py_DECREF(respheaders); if (respcode != OBEX_RSP_CONTINUE && respcode != OBEX_RSP_SUCCESS) return; } if (self->fileobj == NULL) { obexserver_errorstr(self, PyExc_IOError, "file object is null"); return; } int result; result = lightblueobex_streamtofile(self->obex, obj, self->fileobj); if (result < 0) { obexserver_errorstr(self, PyExc_IOError, "error reading body data or writing to file object"); OBEX_ObjectSetRsp(obj, OBEX_RSP_INTERNAL_SERVER_ERROR, OBEX_RSP_INTERNAL_SERVER_ERROR); } #ifdef LIGHTBLUEOBEX_SERVER_TEST if (result > 0) self->puttotal += result; #endif } static void obexserver_streamempty(OBEXServer *self, obex_object_t *obj) { DEBUG("%s()\n", __func__); Py_XDECREF(self->tempbuf); self->tempbuf = lightblueobex_filetostream(self->obex, obj, self->fileobj, self->sendbufsize); if (self->tempbuf == NULL) { obexserver_errorstr(self, PyExc_IOError, "error reading file object"); OBEX_ObjectSetRsp(obj, OBEX_RSP_INTERNAL_SERVER_ERROR, OBEX_RSP_INTERNAL_SERVER_ERROR); } } static void obexserver_requestdone(OBEXServer *self, obex_object_t *obj, int obex_cmd) { PyObject *result; if (self->cb_requestdone == NULL) { /* shouldn't happen */ obexserver_errorstr(self, PyExc_IOError, "cb_requestdone is NULL"); return; } result = PyObject_CallFunction(self->cb_requestdone, "i", obex_cmd); if (result == NULL) { obexserver_errorfetch(self); } else { Py_DECREF(result); } /* clean up */ Py_XDECREF(self->tempbuf); self->tempbuf = NULL; Py_XDECREF(self->fileobj); self->fileobj = NULL; } static void obexserver_incomingrequest(OBEXServer *self, obex_object_t *obj, int obex_cmd) { self->notifiednewrequest = 0; self->hasbodydata = 0; Py_XDECREF(self->tempbuf); Py_XDECREF(self->fileobj); // signal we want to stream body data if (obex_cmd == OBEX_CMD_PUT) { if (OBEX_ObjectReadStream(self->obex, obj, NULL) < 0) { DEBUG("\tUnable to stream body data\n"); OBEX_ObjectSetRsp(obj, OBEX_RSP_INTERNAL_SERVER_ERROR, OBEX_RSP_INTERNAL_SERVER_ERROR); return; } } OBEX_ObjectSetRsp(obj, OBEX_RSP_CONTINUE, OBEX_RSP_SUCCESS); #ifdef LIGHTBLUEOBEX_SERVER_TEST self->puttotal = 0; #endif } /* can this be static? */ void obexserver_event(obex_t *handle, obex_object_t *obj, int mode, int event, int obex_cmd, int obex_rsp) { DEBUG("%s()\n", __func__); DEBUG("\tEvent: %d Command: %d\n", event, obex_cmd); OBEXServer *self = (OBEXServer *)OBEX_GetUserData(handle); switch (event) { case OBEX_EV_LINKERR: case OBEX_EV_PARSEERR: DEBUG("\tOBEX_EV_LINKERR or OBEX_EV_PARSEERR\n"); obexserver_errorstr(self, PyExc_IOError, (event == OBEX_EV_LINKERR ? "connection error" : "parse error")); break; case OBEX_EV_REQHINT: DEBUG("\tOBEX_EV_REQHINT\n"); obexserver_incomingrequest(self, obj, obex_cmd); break; case OBEX_EV_REQ: DEBUG("\tOBEX_EV_REQ\n"); obexserver_receivedrequest(self, obj, obex_cmd); break; case OBEX_EV_STREAMAVAIL: DEBUG("\tOBEX_EV_STREAMAVAIL\n"); obexserver_streamavailable(self, obj); break; case OBEX_EV_STREAMEMPTY: DEBUG("\tOBEX_EV_STREAMEMPTY\n"); obexserver_streamempty(self, obj); break; case OBEX_EV_REQDONE: DEBUG("\tOBEX_EV_REQDONE\n"); obexserver_requestdone(self, obj, obex_cmd); break; default: DEBUG("\tNot handling event\n"); break; } } static PyObject * OBEXServer_process(OBEXServer *self, PyObject *args) { int timeout; int result; DEBUG("%s()\n", __func__); if (!PyArg_ParseTuple(args, "i", &timeout)) return NULL; result = OBEX_HandleInput(self->obex, timeout); return PyInt_FromLong(result); } PyDoc_STRVAR(OBEXServer_process__doc__, "process(timeout) -> result\n\n\ Processes and reads incoming data with the given timeout (in seconds). \ Blocks if no data is available. Returns -1 on error and 0 on timeout."); static PyObject * OBEXServer_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { OBEXServer *self; self = (OBEXServer *)type->tp_alloc(type, 0); if (self != NULL) { self->obex = NULL; self->sendbufsize = 1024; self->cb_error = NULL; self->cb_newrequest = NULL; self->cb_requestdone = NULL; self->notifiednewrequest = 0; self->hasbodydata = 0; self->fileobj = NULL; self->tempbuf = NULL; } return (PyObject *)self; } static int OBEXServer_init(OBEXServer *self, PyObject *args) { int fd; PyObject *cb_error; PyObject *cb_newrequest; PyObject *cb_requestdone; int mtu = 1024; /* todo make this a keyword arg */ if (!PyArg_ParseTuple(args, "iOOO", &fd, &cb_error, &cb_newrequest, &cb_requestdone)) { return -1; } if (!PyCallable_Check(cb_error) || !PyCallable_Check(cb_newrequest) || !PyCallable_Check(cb_requestdone)) { PyErr_SetString(PyExc_TypeError, "given callback is not callable"); return -1; } if (self->cb_error == NULL) { Py_INCREF(cb_error); self->cb_error = cb_error; } if (self->cb_newrequest == NULL) { Py_INCREF(cb_newrequest); self->cb_newrequest = cb_newrequest; } if (self->cb_requestdone == NULL) { Py_INCREF(cb_requestdone); self->cb_requestdone = cb_requestdone; } if (self->obex == NULL) { self->obex = OBEX_Init(OBEX_TRANS_FD, obexserver_event, 0); if (self->obex == NULL) { PyErr_SetString(PyExc_IOError, "error creating OBEX object"); return -1; } if (FdOBEX_TransportSetup(self->obex, fd, fd, mtu) < 0) { PyErr_SetString(PyExc_IOError, "error initialising transport"); return -1; } } OBEX_SetUserData(self->obex, self); return 0; } static void OBEXServer_dealloc(OBEXServer *self) { if (self->obex) OBEX_Cleanup(self->obex); Py_XDECREF(self->fileobj); Py_XDECREF(self->tempbuf); self->ob_type->tp_free((PyObject *)self); } static PyMemberDef OBEXServer_members[] = { {NULL} /* Sentinel */ }; static PyMethodDef OBEXServer_methods[] = { { "process", (PyCFunction)OBEXServer_process, METH_VARARGS, OBEXServer_process__doc__, }, {NULL} /* Sentinel */ }; static PyTypeObject OBEXServerType = { PyObject_HEAD_INIT(NULL) 0, /*ob_size*/ "obexserver.OBEXServer", /*tp_name*/ sizeof(OBEXServer), /*tp_basicsize*/ 0, /*tp_itemsize*/ (destructor)OBEXServer_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ 0, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash */ 0, /*tp_call*/ 0, /*tp_str*/ 0, /*tp_getattro*/ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ "An OBEX server class.", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ OBEXServer_methods, /* tp_methods */ OBEXServer_members, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc)OBEXServer_init, /* tp_init */ 0, /* tp_alloc */ OBEXServer_new, /* tp_new */ }; PyTypeObject *lightblueobex_getservertype(void) { return &OBEXServerType; } lightblue-0.3.2/src/linux/._lightblueobex_server.h0000644000076500000000000000012210717031044022067 0ustar beawheel00000000000000Mac OS X  2 RTEXT!Rchlightblue-0.3.2/src/linux/lightblueobex_server.h0000644000076500007650000000021610717031044021323 0ustar beabea00000000000000#ifndef LIGHTBLUEOBEX_SERVER_H #define LIGHTBLUEOBEX_SERVER_H #include "Python.h" PyTypeObject *lightblueobex_getservertype(void); #endif lightblue-0.3.2/src/linux/._obex.py0000644000076500000000000000012210750744236017015 0ustar beawheel00000000000000Mac OS X  2 RTEXTlightblue-0.3.2/src/linux/obex.py0000644000076500007650000000735210750744236016261 0ustar beabea00000000000000# Copyright (c) 2006 Bea Lam. All rights reserved. # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files # (the "Software"), to deal in the Software without restriction, # including without limitation the rights to use, copy, modify, merge, # publish, distribute, sublicense, and/or sell copies of the Software, # and to permit persons to whom the Software is furnished to do so, # subject to the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ Provides an OBEX client class and convenience functions for sending and receiving files over OBEX. This module also defines constants for response code values (without the final bit set). For example: >>> import lightblue >>> lightblue.obex.OK 32 # the OK/Success response 0x20 (i.e. 0xA0 without the final bit) >>> lightblue.obex.FORBIDDEN 67 # the Forbidden response 0x43 (i.e. 0xC3 without the final bit) """ # Docstrings for attributes in this module. _docstrings = { "sendfile": """ Sends a file to a remote device. Raises lightblue.obex.OBEXError if an error occurred during the request, or if the request was refused by the remote device. Arguments: - address: the address of the remote device - channel: the RFCOMM channel of the remote OBEX service - source: a filename or file-like object, containing the data to be sent. If a file object is given, it must be opened for reading. Note you can achieve the same thing using OBEXClient with something like this: >>> import lightblue >>> client = lightblue.obex.OBEXClient(address, channel) >>> client.connect() >>> putresponse = client.put({"name": "MyFile.txt"}, file("MyFile.txt", 'rb')) >>> client.disconnect() >>> if putresponse.code != lightblue.obex.OK: ... raise lightblue.obex.OBEXError("server denied the Put request") >>> """, "recvfile": """ Receives a file through an OBEX service. Arguments: - sock: the server socket on which the file is to be received. Note this socket must *not* be listening. Also, an OBEX service should have been advertised on this socket. - dest: a filename or file-like object, to which the received data will be written. If a filename is given, any existing file will be overwritten. If a file object is given, it must be opened for writing. For example, to receive a file and save it as "MyFile.txt": >>> from lightblue import * >>> s = socket() >>> s.bind(("", 0)) >>> advertise("My OBEX Service", s, OBEX) >>> obex.recvfile(s, "MyFile.txt") """ } # import implementation modules from _obex import * from _obexcommon import * import _obex import _obexcommon __all__ = _obex.__all__ + _obexcommon.__all__ # set docstrings localattrs = locals() for attr in _obex.__all__: try: localattrs[attr].__doc__ = _docstrings[attr] except KeyError: pass del attr, localattrs lightblue-0.3.2/src/mac/0000755000076500007650000000000010770565177014353 5ustar beabea00000000000000lightblue-0.3.2/src/mac/.___init__.py0000755000076500000000000000012210767432712017224 0ustar beawheel00000000000000Mac OS X  2 RTEXTlightblue-0.3.2/src/mac/__init__.py0000755000076500007650000001515210767432712016465 0ustar beabea00000000000000# Copyright (c) 2006 Bea Lam. All rights reserved. # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files # (the "Software"), to deal in the Software without restriction, # including without limitation the rights to use, copy, modify, merge, # publish, distribute, sublicense, and/or sell copies of the Software, # and to permit persons to whom the Software is furnished to do so, # subject to the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. "LightBlue - a simple bluetooth library." # Docstrings for attributes in this module. _docstrings = { "finddevices": """ Performs a device discovery and returns the found devices as a list of (address, name, class-of-device) tuples. Raises BluetoothError if an error occurs. Arguments: - getnames=True: True if device names should be retrieved during discovery. If false, None will be returned instead of the device name. - length=10: the number of seconds to spend discovering devices (this argument has no effect on Python for Series 60) Do not invoke a new discovery before a previous discovery has finished. Also, to minimise interference with other wireless and bluetooth traffic, and to conserve battery power on the local device, discoveries should not be invoked too frequently (an interval of at least 20 seconds is recommended). """, "findservices": """ Performs a service discovery and returns the found services as a list of (device-address, service-port, service-name) tuples. Raises BluetoothError if an error occurs. Arguments: - addr=None: a device address, to search only for services on a specific device - name=None: a service name string, to search only for a service with a specific name - servicetype=None: can be RFCOMM or OBEX to search only for RFCOMM or OBEX-type services. (OBEX services are not returned from an RFCOMM search) If more than one criteria is specified, this returns services that match all criteria. Currently the Python for Series 60 implementation will only find RFCOMM and OBEX services. """, "finddevicename": """ Returns the name of the device with the given bluetooth address. finddevicename(gethostaddr()) returns the local device name. Arguments: - address: the address of the device to look up - usecache=True: if True, the device name will be fetched from a local cache if possible. If False, or if the device name is not in the cache, the remote device will be contacted to request its name. Raise BluetoothError if the name cannot be retrieved. """, "gethostaddr": """ Returns the address of the local bluetooth device. Raise BluetoothError if the local device is not available. """, "gethostclass": """ Returns the class of device of the local bluetooth device. These values indicate the device's major services and the type of the device (e.g. mobile phone, laptop, etc.). If you google for "assigned numbers bluetooth baseband" you might find some documents that discuss how to extract this information from the class of device. Raise BluetoothError if the local device is not available. """, "socket": """ socket(proto=RFCOMM) -> socket object Returns a new socket object. Arguments: - proto=RFCOMM: the type of socket to be created - either L2CAP or RFCOMM. Note that L2CAP sockets are not available on Python For Series 60, and only L2CAP client sockets are supported on Mac OS X and Linux (i.e. you can connect() the socket but not bind(), accept(), etc.). """, "advertise": """ Starts advertising a service with the given name, using the given server socket. Raises BluetoothError if the service cannot be advertised. Arguments: - name: name of the service to be advertised - sock: the socket object that will serve this service. The socket must be already bound to a channel. If a RFCOMM service is being advertised, the socket should also be listening. - servicetype: the type of service to advertise - either RFCOMM or OBEX. (L2CAP services are not currently supported.) (If the servicetype is RFCOMM, the service will be advertised with the Serial Port Profile; if the servicetype is OBEX, the service will be advertised with the OBEX Object Push Profile.) """, "stopadvertise": """ Stops advertising the service on the given socket. Raises BluetoothError if no service is advertised on the socket. This will error if the given socket is already closed. """, "selectdevice": """ Displays a GUI which allows the end user to select a device from a list of discovered devices. Returns the selected device as an (address, name, class-of-device) tuple. Returns None if the selection was cancelled. (On Python For Series 60, the device selection will fail if there are any open bluetooth connections.) """, "selectservice": """ Displays a GUI which allows the end user to select a service from a list of discovered devices and their services. Returns the selected service as a (device-address, service-port, service- name) tuple. Returns None if the selection was cancelled. (On Python For Series 60, the device selection will fail if there are any open bluetooth connections.) Currently the Python for Series 60 implementation will only find RFCOMM and OBEX services. """ } # import implementation modules from _lightblue import * from _lightbluecommon import * import obex # plus submodule # set docstrings import _lightblue localattrs = locals() for attr in _lightblue.__all__: try: localattrs[attr].__doc__ = _docstrings[attr] except KeyError: pass del attr, localattrs lightblue-0.3.2/src/mac/.__bluetoothsockets.py0000755000076500000000000000012210750613031021210 0ustar beawheel00000000000000Mac OS X  2 RTEXTlightblue-0.3.2/src/mac/_bluetoothsockets.py0000755000076500000000000011026510750613031021005 0ustar beawheel00000000000000# Copyright (c) 2006 Bea Lam. All rights reserved. # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files # (the "Software"), to deal in the Software without restriction, # including without limitation the rights to use, copy, modify, merge, # publish, distribute, sublicense, and/or sell copies of the Software, # and to permit persons to whom the Software is furnished to do so, # subject to the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # Mac OS X bluetooth sockets implementation. # # To-do: # - allow socket options # # if doing security AUTH, should set bool arg when calling # openConnection_withPageTimeout_authenticationRequired_() in connect() import time import socket as _socket import threading import os import errno import types import objc import Foundation import _IOBluetooth import _lightbluecommon import _macutil from _LightAquaBlue import BBServiceAdvertiser, BBBluetoothChannelDelegate import sets # python 2.3 try: SHUT_RD, SHUT_WR, SHUT_RDWR = \ _socket.SHUT_RD, _socket.SHUT_WR, _socket.SHUT_RDWR except AttributeError: # python 2.3 SHUT_RD, SHUT_WR, SHUT_RDWR = (0, 1, 2) def _getavailableport(proto): # Just advertise a service and see what channel it was assigned, then # stop advertising the service and return the channel. # It's a hacky way of doing it, but IOBluetooth doesn't seem to provide # functionality for just getting an available channel. if proto == _lightbluecommon.RFCOMM: try: result, channelID, servicerecordhandle = BBServiceAdvertiser.addRFCOMMServiceDictionary_withName_UUID_channelID_serviceRecordHandle_(BBServiceAdvertiser.serialPortProfileDictionary(), "DummyService", None, None, None) except: result, channelID, servicerecordhandle = BBServiceAdvertiser.addRFCOMMServiceDictionary_withName_UUID_channelID_serviceRecordHandle_(BBServiceAdvertiser.serialPortProfileDictionary(), "DummyService", None) if result != _macutil.kIOReturnSuccess: raise _lightbluecommon.BluetoothError(result, \ "Could not retrieve an available service channel") result = BBServiceAdvertiser.removeService_(servicerecordhandle) if result != _macutil.kIOReturnSuccess: raise _lightbluecommon.BluetoothError(result, \ "Could not retrieve an available service channel") return channelID else: raise NotImplementedError("L2CAP server sockets not currently supported") def _checkaddrpair(address, checkbtaddr=True): # will want checkbtaddr=False if the address might be empty string # (for binding to a server address) if not isinstance(address, tuple): raise TypeError("address must be (address, port) tuple, was %s" % \ type(address)) if len(address) != 2: raise TypeError("address tuple must have 2 items (has %d)" % \ len(address)) if not isinstance(address[0], types.StringTypes): raise TypeError("address host value must be string, was %s" % \ type(address[0])) if checkbtaddr: if not _lightbluecommon._isbtaddr(address[0]): raise TypeError("address '%s' is not a bluetooth address" % \ address[0]) if not isinstance(address[1], int): raise TypeError("address port value must be int, was %s" % \ type(address[1])) # from std lib socket module class _closedsocket(object): __slots__ = [] def _dummy(*args): raise _socket.error(errno.EBADF, 'Bad file descriptor') send = recv = sendto = recvfrom = __getattr__ = _dummy # Thanks to Simon Wittber for string queue recipe # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/426060 # (this is a modified version) class _StringQueue(object): def __init__(self): self.l_buffer = [] self.s_buffer = "" self.lock = threading.RLock() self.bufempty = True def empty(self): return self.bufempty def write(self, data): # no type check, and assumes data is not empty! #append data to list, no need to "".join just yet. self.lock.acquire() try: self.l_buffer.append(data) self.bufempty = False finally: self.lock.release() def _build_str(self): #build a new string out of list new_string = "".join(self.l_buffer) #join string buffer and new string self.s_buffer = "".join((self.s_buffer, new_string)) #clear list self.l_buffer = [] def __len__(self): #calculate length without needing to _build_str return sum([len(i) for i in self.l_buffer]) + len(self.s_buffer) def read(self, count): self.lock.acquire() try: #if string doesn't have enough chars to satisfy caller if count > len(self.s_buffer): self._build_str() #get data requested by caller result = self.s_buffer[:count] #remove requested data from string buffer self.s_buffer = self.s_buffer[len(result):] self.bufempty = (len(self.s_buffer) == 0) finally: self.lock.release() return result #class _SocketWrapper(_socket._socketobject): class _SocketWrapper(object): """ A Bluetooth socket object has the same interface as a socket object from the Python standard library module. It also uses the same exceptions, raising socket.error for general errors and socket.timeout for timeout errors. Note that L2CAP sockets are not available on Python For Series 60, and only L2CAP client sockets are supported on Mac OS X and Linux. A simple client socket example: >>> from lightblue import * >>> s = socket() # or socket(L2CAP) to create an L2CAP socket >>> s.connect(("00:12:2c:45:8a:7b", 5)) >>> s.send("hello") 5 >>> s.close() A simple server socket example: >>> from lightblue import * >>> s = socket() >>> s.bind(("", 0)) >>> s.listen(1) >>> advertise("My RFCOMM Service", s, RFCOMM) >>> conn, addr = s.accept() >>> print "Connected by", addr Connected by ('00:0D:93:19:C8:68', 5) >>> conn.recv(1024) "hello" >>> conn.close() >>> s.close() """ def __init__(self, sock): self._sock = sock def accept(self): sock, addr = self._sock.accept() return _SocketWrapper(sock), addr accept.__doc__ = _lightbluecommon._socketdocs["accept"] def dup(self): return _SocketWrapper(self._sock) dup.__doc__ = _lightbluecommon._socketdocs["dup"] def close(self): self._sock.close() self._sock = _closedsocket() self.send = self.recv = self.sendto = self.recvfrom = self._sock._dummy try: import lightblue lightblue.stopadvertise(self) except: pass close.__doc__ = _lightbluecommon._socketdocs["close"] def makefile(self, mode='r', bufsize=-1): # use std lib socket's _fileobject return _socket._fileobject(self._sock, mode, bufsize) makefile.__doc__ = _lightbluecommon._socketdocs["makefile"] # delegate all other method calls to internal sock obj def __getattr__(self, attr): return getattr(self._sock, attr) # internal _sock object for RFCOMM and L2CAP sockets class _BluetoothSocket(object): _boundports = { _lightbluecommon.L2CAP: sets.Set(), _lightbluecommon.RFCOMM: sets.Set() } # conn is the associated _RFCOMMConnection or _L2CAPConnection def __init__(self, conn): self.__conn = conn if conn is not None and conn.channel is not None: self.__remotedevice = conn.channel.getDevice() else: self.__remotedevice = None # timeout=None cos sockets default to blocking mode self.__timeout = None #self.__isserverspawned = (conn.channel is not None) self.__port = 0 self.__eventlistener = None self.__closed = False self.__maxqueuedconns = 0 self.__incomingdata = _StringQueue() self.__queuedchannels = [] self.__queuedchannels_lock = threading.RLock() # whether send or recv has been shut down # set initial value to be other than SHUT_WR/SHUT_RD/SHUT_RDWR self.__commstate = -1 def accept(self): if not self.__isbound(): raise _socket.error('Socket not bound') if not self.__islistening(): raise _socket.error('Socket must be listening first') def clientconnected(): return len(self.__queuedchannels) > 0 if not clientconnected(): self.__waituntil(clientconnected, "accept timed out") self.__queuedchannels_lock.acquire() try: newchannel = self.__queuedchannels.pop(0) finally: self.__queuedchannels_lock.release() # return (new-socket, addr) pair using the new channel newconn = _SOCKET_CLASSES[self.__conn.proto](newchannel) sock = _SocketWrapper(_BluetoothSocket(newconn)) sock.__startevents() return (sock, sock.getpeername()) def bind(self, address): _checkaddrpair(address, False) if self.__isbound(): raise _socket.error('Socket is already bound') elif self.__isconnected(): raise _socket.error("Socket is already connected, cannot be bound") if self.__conn.proto == _lightbluecommon.L2CAP: raise NotImplementedError("L2CAP server sockets not currently supported") if address[1] != 0: raise _socket.error("must bind to port 0, other ports not supported on Mac OS X") address = (address[0], _getavailableport(self.__conn.proto)) # address must be either empty string or local device address if address[0] != "": try: import lightblue localaddr = lightblue.gethostaddr() except: localaddr = None if localaddr is None or address[0] != localaddr: raise _socket.error( errno.EADDRNOTAVAIL, os.strerror(errno.EADDRNOTAVAIL)) # is this port already in use? if address[1] in self._boundports[self.__conn.proto]: raise _socket.error(errno.EADDRINUSE, os.strerror(errno.EADDRINUSE)) self._boundports[self.__conn.proto].add(address[1]) self.__port = address[1] def close(self): wasconnected = self.__isconnected() or self.__isbound() self.__stopevents() if self.__conn is not None: if self.__isbound(): self._boundports[self.__conn.proto].discard(self.__port) else: if self.__conn.channel is not None: self.__conn.channel.setDelegate_(None) self.__conn.channel.closeChannel() # disconnect the baseband connection. # This will fail if other RFCOMM channels to the remote device are # still open (which is what we want, cos we don't know if another # process is talking to the device) if self.__remotedevice is not None: self.__remotedevice.closeConnection() # returns err code # if you don't run the event loop a little here, it's likely you won't # be able to reconnect to the same remote device later if wasconnected: _macutil.waitfor(0.5) def connect(self, address): if self.__isbound(): raise _socket.error("Can't connect, socket has been bound") elif self.__isconnected(): raise _socket.error("Socket is already connected") _checkaddrpair(address) # open a connection to device self.__remotedevice = _IOBluetooth.IOBluetoothDevice.withAddress_( _macutil.createbtdevaddr(address[0])) if not self.__remotedevice.isConnected(): if self.__timeout is None: result = self.__remotedevice.openConnection() else: result = self.__remotedevice.openConnection_withPageTimeout_authenticationRequired_( None, self.__timeout*1000, False) if result != _macutil.kIOReturnSuccess: if result == _macutil.kBluetoothHCIErrorPageTimeout: if self.__timeout == 0: raise _socket.error(errno.EAGAIN, "Resource temporarily unavailable") else: raise _socket.timeout("connect timed out") else: raise _socket.error(result, "Cannot connect to %s, can't open connection." \ % str(address[0])) # open RFCOMM or L2CAP channel self.__eventlistener = self.__createlistener() result = self.__conn.connect(self.__remotedevice, address[1], self.__eventlistener) # pass listener as cocoa delegate if result != _macutil.kIOReturnSuccess: self.__remotedevice.closeConnection() self.__stopevents() self.__eventlistener = None raise _socket.error(result, "Cannot connect to %d on %s" % (address[1], address[0])) return # if you don't run the event loop a little here, it's likely you won't # be able to reconnect to the same remote device later _macutil.waitfor(0.5) def connect_ex(self, address): try: self.connect(address) except _socket.error, err: if len(err.args) > 1: return err.args[0] else: # there's no error code, just a message, so this error wasn't # from a system call -- so re-raise the exception raise _socket.error(err) return 0 def getpeername(self): self.__checkconnected() addr = _macutil.formatdevaddr(self.__remotedevice.getAddressString()) return (addr, self._getport()) def getsockname(self): if self.__isbound() or self.__isconnected(): import lightblue return (lightblue.gethostaddr(), self._getport()) else: return ("00:00:00:00:00:00", 0) def listen(self, backlog): if self.__islistening(): return if not self.__isbound(): raise _socket.error('Socket not bound') if not isinstance(backlog, int): raise TypeError("backlog must be int, was %s" % type(backlog)) if backlog < 0: raise ValueError("backlog cannot be negative, was %d" % backlog) self.__maxqueuedconns = backlog # start listening for client connections self.__startevents() def _isclosed(self): # isOpen() check doesn't work for incoming (server-spawned) channels if (self.__conn.proto == _lightbluecommon.RFCOMM and self.__conn.channel is not None and not self.__conn.channel.isIncoming()): return not self.__conn.channel.isOpen() return self.__closed def recv(self, bufsize, flags=0): if self.__commstate in (SHUT_RD, SHUT_RDWR): return "" self.__checkconnected() if not isinstance(bufsize, int): raise TypeError("buffer size must be int, was %s" % type(bufsize)) if bufsize < 0: raise ValueError("negative buffersize in recv") # as for tcp if bufsize == 0: return "" # need this to ensure the _isclosed() check is up-to-date _macutil.looponce() if self._isclosed(): if len(self.__incomingdata) == 0: raise _socket.error(errno.ECONNRESET, os.strerror(errno.ECONNRESET)) return self.__incomingdata.read(bufsize) # if incoming data buffer is empty, wait until data is available or # channel is closed def gotdata(): return not self.__incomingdata.empty() or self._isclosed() if not gotdata(): self.__waituntil(gotdata, "recv timed out") # other side closed connection while waiting? if self._isclosed() and len(self.__incomingdata) == 0: raise _socket.error(errno.ECONNRESET, os.strerror(errno.ECONNRESET)) return self.__incomingdata.read(bufsize) # recvfrom() is really for datagram sockets not stream sockets but it # can be implemented anyway. def recvfrom(self, bufsize, flags=0): # stream sockets return None, instead of address return (self.recv(bufsize, flags), None) def sendall(self, data, flags=0): sentbytescount = self.send(data, flags) while sentbytescount < len(data): sentbytescount += self.send(data[sentbytescount:], flags) return None def send(self, data, flags=0): if not isinstance(data, types.StringTypes): raise TypeError("data must be string, was %s" % type(data)) if self.__commstate in (SHUT_WR, SHUT_RDWR): raise _socket.error(errno.EPIPE, os.strerror(errno.EPIPE)) self.__checkconnected() # do setup for if sock is in non-blocking mode if self.__timeout is not None: if self.__timeout == 0: # in non-blocking mode # isTransmissionPaused() is not available for L2CAP sockets, # what to do for that? if self.__conn.proto == _lightbluecommon.RFCOMM and \ self.__conn.channel.isTransmissionPaused(): # sending data now will block raise _socket.error(errno.EAGAIN, "Resource temporarily unavailable") elif self.__timeout > 0: # non-blocking with timeout starttime = time.time() # loop until all data is sent writebuf = data bytesleft = len(data) mtu = self.__conn.getwritemtu() while bytesleft > 0: if self.__timeout is not None and self.__timeout > 0: if time.time() - starttime > self.__timeout: raise _socket.timeout("send timed out") # write the data to the channel (only the allowed amount) # the method/selector is the same for L2CAP and RFCOMM channels if bytesleft > mtu: sendbytecount = mtu else: sendbytecount = bytesleft #result = self.__conn.channel.writeSync_length_( # writebuf[:sendbytecount], sendbytecount) result = self.__conn.write(writebuf[:sendbytecount]) # normal tcp sockets don't seem to actually error on the first # send() after a connection has broken; if you try a second time, # then you get the (32, 'Broken pipe') socket.error if result != _macutil.kIOReturnSuccess: raise _socket.error(result, "Error sending data") bytesleft -= sendbytecount writebuf = writebuf[sendbytecount:] # remove the data just sent return len(data) - bytesleft # sendto args may be one of: # - data, address # - data, flags, address # # The standard behaviour seems to be to ignore the given address if already # connected. # sendto() is really for datagram sockets not stream sockets but it # can be implemented anyway. def sendto(self, data, *args): if len(args) == 1: address = args[0] flags = 0 elif len(args) == 2: flags, address = args else: raise TypeError("sendto takes at most 3 arguments (%d given)" % \ (len(args) + 1)) _checkaddrpair(address) # must already be connected, cos this is stream socket self.__checkconnected() return self.send(data, flags) def fileno(self): raise NotImplementedError def getsockopt(self, level, optname, buflen=0): # see what options on Linux+s60 # possibly have socket security option. raise _socket.error( errno.ENOPROTOOPT, os.strerror(errno.ENOPROTOOPT)) def setsockopt(self, level, optname, value): # see what options on Linux+s60 # possibly have socket security option. raise _socket.error( errno.ENOPROTOOPT, os.strerror(errno.ENOPROTOOPT)) def setblocking(self, flag): if flag == 0: self.__timeout = 0 # non-blocking else: self.__timeout = None # blocking def gettimeout(self): return self.__timeout def settimeout(self, value): if value is not None and not isinstance(value, (float, int)): msg = "timeout value must be a number or None, was %s" % \ type(value) raise TypeError(msg) if value < 0: msg = "timeout value cannot be negative, was %d" % value raise ValueError(msg) self.__timeout = value def shutdown(self, how): if how not in (SHUT_RD, SHUT_WR, SHUT_RDWR): raise _socket.error(22, "Invalid argument") self.__commstate = how # This method is called from outside this file. def _getport(self): if self.__isconnected(): return self.__conn.getport() if self.__isbound(): return self.__port raise _lightbluecommon.BluetoothError("socket is neither connected nor bound") # This method is called from outside this file. def _getchannel(self): if self.__conn is None: return None return self.__conn.channel # Called by the event listener when data is available # 'channel' is IOBluetoothRFCOMMChannel or IOBluetoothL2CAPChannel object def _handle_channeldata(self, channel, data): self.__incomingdata.write(data) _macutil.interruptwait() # Called by the event listener when a client connects to a server socket def _handle_channelopened(self, channel): # put new channels into a queue, which 'accept' can then pull out self.__queuedchannels_lock.acquire() try: # need to implement max connections #if len(self.__queuedchannels) < self.__maxqueuedconns: self.__queuedchannels.append(channel) _macutil.interruptwait() finally: self.__queuedchannels_lock.release() # Called by the event listener when the channel is closed. def _handle_channelclosed(self, channel): # beware that this value won't actually be set until the event loop # has been driven so that this method is actually called self.__closed = True _macutil.interruptwait() def __waituntil(self, stopwaiting, timeoutmsg): """ Waits until stopwaiting() returns True, or until the wait times out (according to the self.__timeout value). This is to make a function wait until a buffer has been filled. i.e. stopwaiting() should return True when the buffer is no longer empty. """ if not stopwaiting(): if self.__timeout == 0: # in non-blocking mode (immediate timeout) # push event loop to really be sure there is no data available _macutil.looponce() if not stopwaiting(): # trying to perform operation now would block raise _socket.error(errno.EAGAIN, os.strerror(errno.EAGAIN)) else: # block and wait until we get data, or time out if not _macutil.waituntil(stopwaiting, self.__timeout): raise _socket.timeout(timeoutmsg) def __createlistener(self): if self.__isbound(): return _ChannelServerEventListener.alloc().initWithDelegate_port_protocol_(self, self._getport(), self.__conn.proto) else: listener = _ChannelEventListener.alloc().initWithDelegate_(self) if self.__conn.channel is not None: self.__conn.channel.setDelegate_(listener.delegate()) listener.registerclosenotif(self.__conn.channel) return listener # should not call this if connect() has been called to connect this socket def __startevents(self): if self.__eventlistener is not None: raise _lightbluecommon.BluetoothError("socket already listening") self.__eventlistener = self.__createlistener() def __stopevents(self): if self.__eventlistener is not None: self.__eventlistener.close() def __islistening(self): return self.__eventlistener is not None def __checkconnected(self): if not self._sock.isconnected(): # i.e. is connected, non-server socket # not connected, raise "socket not connected" raise _socket.error(errno.ENOTCONN, os.strerror(errno.ENOTCONN)) # returns whether socket is a bound server socket def __isbound(self): return self.__port != 0 def __isconnected(self): return self.__conn.channel is not None def __checkconnected(self): if not self.__isconnected(): # not connected, raise "socket not connected" raise _socket.error(errno.ENOTCONN, os.strerror(errno.ENOTCONN)) # set method docstrings definedmethods = locals() # i.e. defined methods in _SocketWrapper for name, doc in _lightbluecommon._socketdocs.items(): try: definedmethods[name].__doc__ = doc except KeyError: pass class _RFCOMMConnection(object): proto = _lightbluecommon.RFCOMM def __init__(self, channel=None): # self.channel is accessed by _BluetoothSocket parent self.channel = channel def connect(self, device, port, listener): # open RFCOMM channel (should timeout actually apply to opening of # channel as well? if so need to do timeout with async callbacks) try: # pyobjc 2.0 result, self.channel = device.openRFCOMMChannelSync_withChannelID_delegate_(None, port, listener.delegate()) except TypeError: result, self.channel = device.openRFCOMMChannelSync_withChannelID_delegate_(port, listener.delegate()) if result == _macutil.kIOReturnSuccess: self.channel.setDelegate_(listener.delegate()) listener.registerclosenotif(self.channel) else: self.channel = None return result def write(self, data): if self.channel is None: raise _socket.error("socket not connected") return \ BBBluetoothChannelDelegate.synchronouslyWriteData_toRFCOMMChannel_( buffer(data), self.channel) def getwritemtu(self): return self.channel.getMTU() def getport(self): return self.channel.getChannelID() class _L2CAPConnection(object): proto = _lightbluecommon.L2CAP def __init__(self, channel=None): # self.channel is accessed by _BluetoothSocket parent self.channel = channel def connect(self, device, port, listener): try: # pyobjc 2.0 result, self.channel = device.openL2CAPChannelSync_withPSM_delegate_(None, port, listener.delegate()) except TypeError: result, self.channel = device.openL2CAPChannelSync_withPSM_delegate_(port, listener.delegate()) if result == _macutil.kIOReturnSuccess: self.channel.setDelegate_(listener.delegate()) listener.registerclosenotif(self.channel) else: self.channel = None return result def write(self, data): if self.channel is None: raise _socket.error("socket not connected") return \ BBBluetoothChannelDelegate.synchronouslyWriteData_toL2CAPChannel_( buffer(data), self.channel) def getwritemtu(self): return self.channel.getOutgoingMTU() def getport(self): return self.channel.getPSM() class _ChannelEventListener(Foundation.NSObject): """ Uses a BBBluetoothChannelDelegate to listen for events on an IOBluetoothRFCOMMChannel or IOBluetoothL2CAPChannel, and makes callbacks to a specified object when events occur. """ # note this is a NSObject "init", not a python object "__init__" def initWithDelegate_(self, cb_obj): """ Arguments: - cb_obj: An object that receives callbacks when events occur. This object should have: - a method '_handle_channeldata' which takes the related channel (a IOBluetoothRFCOMMChannel or IOBluetoothL2CAPChannel) and the new data (a string) as the arguments. - a method '_handle_channelclosed' which takes the related channel as the argument. If this listener's delegate is passed to the openRFCOMMChannel... or openL2CAPChannel... selectors as the delegate, the delegate (and therefore this listener) will automatically start receiving events. Otherwise, call setDelegate_() on the channel with this listener's delegate as the argument to allow this listener to start receiving channel events. (This is the only option for server-spawned sockets.) """ self = super(_ChannelEventListener, self).init() if cb_obj is None: raise TypeError("callback object is None") self.__cb_obj = cb_obj self.__closenotif = None self.__channelDelegate = \ BBBluetoothChannelDelegate.alloc().initWithDelegate_(self) return self initWithDelegate_ = objc.selector(initWithDelegate_, signature="@@:@") def delegate(self): return self.__channelDelegate def registerclosenotif(self, channel): # oddly enough, sometimes the channelClosed: selector doesn't get called # (maybe if there's a lot of data being passed?) but this seems to work notif = channel.registerForChannelCloseNotification_selector_(self, "channelClosedEvent:channel:") if notif is not None: self.__closenotif = notif def close(self): if self.__closenotif is not None: self.__closenotif.unregister() def channelClosedEvent_channel_(self, notif, channel): if hasattr(self.__cb_obj, '_handle_channelclosed'): self.__cb_obj._handle_channelclosed(channel) channelClosedEvent_channel_ = objc.selector( channelClosedEvent_channel_, signature="v@:@@") # implement method from BBBluetoothChannelDelegateObserver protocol: # - (void)channelData:(id)channel data:(NSData *)data; def channelData_data_(self, channel, data): if hasattr(self.__cb_obj, '_handle_channeldata'): self.__cb_obj._handle_channeldata(channel, data[:]) channelData_data_ = objc.selector(channelData_data_, signature="v@:@@") # implement method from BBBluetoothChannelDelegateObserver protocol: # - (void)channelClosed:(id)channel; def channelClosed_(self, channel): if hasattr(self.__cb_obj, '_handle_channelclosed'): self.__cb_obj._handle_channelclosed(channel) channelClosed_ = objc.selector(channelClosed_, signature="v@:@") class _ChannelServerEventListener(Foundation.NSObject): """ Listens for server-specific events on a RFCOMM or L2CAP channel (i.e. when a client connects) and makes callbacks to a specified object when events occur. """ # note this is a NSObject "init", not a python object "__init__" def initWithDelegate_port_protocol_(self, cb_obj, port, proto): """ Arguments: - cb_obj: to receive callbacks when a client connects to to the channel, the callback object should have a method '_handle_channelopened' which takes the newly opened IOBluetoothRFCOMMChannel or IOBluetoothL2CAPChannel as its argument. - port: the channel or PSM that the server is listening on - proto: L2CAP or RFCOMM. """ self = super(_ChannelServerEventListener, self).init() if cb_obj is None: raise TypeError("callback object is None") self.__cb_obj = cb_obj self.__usernotif = None if proto == _lightbluecommon.RFCOMM: usernotif = _IOBluetooth.IOBluetoothRFCOMMChannel.registerForChannelOpenNotifications_selector_withChannelID_direction_(self, "newChannelOpened:channel:", port, _macutil.kIOBluetoothUserNotificationChannelDirectionIncoming) elif proto == _lightbluecommon.L2CAP: usernotif = _IOBluetooth.IOBluetoothL2CAPChannel.registerForChannelOpenNotifications_selector_withPSM_direction_(self, "newChannelOpened:channel:", port, _macutil.kIOBluetoothUserNotificationChannelDirectionIncoming) if usernotif is None: raise _socket.error("Unable to register for channel-" + \ "opened notifications on server socket on channel/PSM %d" % \ port) self.__usernotif = usernotif return self initWithDelegate_port_protocol_ = objc.selector( initWithDelegate_port_protocol_, signature="@@:@ii") def close(self): if self.__usernotif is not None: self.__usernotif.unregister() def newChannelOpened_channel_(self, notif, newChannel): """ Handle when a client connects to the server channel. (This method is called for both RFCOMM and L2CAP channels.) """ if newChannel is not None and newChannel.isIncoming(): # not sure if delegate really needs to be set newChannel.setDelegate_(self) if hasattr(self.__cb_obj, '_handle_channelopened'): self.__cb_obj._handle_channelopened(newChannel) # makes this method receive notif and channel as objects newChannelOpened_channel_ = objc.selector( newChannelOpened_channel_, signature="v@:@@") # ----------------------------------------------------------- # protocol-specific classes _SOCKET_CLASSES = { _lightbluecommon.RFCOMM: _RFCOMMConnection, _lightbluecommon.L2CAP: _L2CAPConnection } def _getsocketobject(proto): if proto not in _SOCKET_CLASSES.keys(): raise ValueError("Unknown socket protocol, must be L2CAP or RFCOMM") return _SocketWrapper(_BluetoothSocket(_SOCKET_CLASSES[proto]())) lightblue-0.3.2/src/mac/.__IOBluetooth.py0000755000076500000000000000012210746345143020017 0ustar beawheel00000000000000Mac OS X  2 RTEXTlightblue-0.3.2/src/mac/_IOBluetooth.py0000755000076500000000000000756410746345143017623 0ustar beawheel00000000000000# Copyright (c) 2006 Bea Lam. All rights reserved. # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files # (the "Software"), to deal in the Software without restriction, # including without limitation the rights to use, copy, modify, merge, # publish, distribute, sublicense, and/or sell copies of the Software, # and to permit persons to whom the Software is furnished to do so, # subject to the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ Provides a python interface to the Mac OSX IOBluetooth Framework classes, through PyObjC. For example: >>> from lightblue import _IOBluetooth >>> for d in _IOBluetooth.IOBluetoothDevice.recentDevices_(0): ... print d.getName() ... Munkey Adam My Nokia 6600 >>> See http://developer.apple.com/documentation/DeviceDrivers/Reference/IOBluetooth/index.html for Apple's IOBluetooth documentation. See http://pyobjc.sourceforge.net for details on how to access Objective-C classes through PyObjC. """ import objc try: # mac os 10.5 loads frameworks using bridgesupport metadata __bundle__ = objc.initFrameworkWrapper("IOBluetooth", frameworkIdentifier="com.apple.IOBluetooth", frameworkPath=objc.pathForFramework( "/System/Library/Frameworks/IOBluetooth.framework"), globals=globals()) except AttributeError: # earlier versions use loadBundle() and setSignatureForSelector() objc.loadBundle("IOBluetooth", globals(), bundle_path=objc.pathForFramework(u'/System/Library/Frameworks/IOBluetooth.framework')) # Sets selector signatures in order to receive arguments correctly from # PyObjC methods. These MUST be set, otherwise the method calls won't work # at all, mostly because you can't pass by pointers in Python. # set to return int, and take an unsigned char output arg # i.e. in python: return (int, unsigned char) and accept no args objc.setSignatureForSelector("IOBluetoothSDPServiceRecord", "getRFCOMMChannelID:", "i12@0:o^C") # set to return int, and take an unsigned int output arg # i.e. in python: return (int, unsigned int) and accept no args objc.setSignatureForSelector("IOBluetoothSDPServiceRecord", "getL2CAPPSM:", "i12@0:o^S") # set to return int, and take (output object, unsigned char, object) args # i.e. in python: return (int, object) and accept (unsigned char, object) objc.setSignatureForSelector("IOBluetoothDevice", "openRFCOMMChannelSync:withChannelID:delegate:", "i16@0:o^@C@") # set to return int, and take (output object, unsigned int, object) args # i.e. in python: return (int, object) and accept (unsigned int, object) objc.setSignatureForSelector("IOBluetoothDevice", "openL2CAPChannelSync:withPSM:delegate:", "i20@0:o^@I@") # set to return int, take a const 6-char array arg # i.e. in python: return object and accept 6-char list # this seems to work even though the selector doesn't take a char aray, # it takes a struct 'BluetoothDeviceAddress' which contains a char array. objc.setSignatureForSelector("IOBluetoothDevice", "withAddress:", '@12@0:r^[6C]') del objc lightblue-0.3.2/src/mac/.__IOBluetoothUI.py0000644000076500000000000000012210746316036020251 0ustar beawheel00000000000000Mac OS X  2 RTEXTlightblue-0.3.2/src/mac/_IOBluetoothUI.py0000644000076500007650000000463510746316036017516 0ustar beabea00000000000000# Copyright (c) 2006 Bea Lam. All rights reserved. # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files # (the "Software"), to deal in the Software without restriction, # including without limitation the rights to use, copy, modify, merge, # publish, distribute, sublicense, and/or sell copies of the Software, # and to permit persons to whom the Software is furnished to do so, # subject to the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ Provides a python interface to the Mac OSX IOBluetoothUI Framework classes, through PyObjC. For example: >>> from lightblue import _IOBluetoothUI >>> selector = _IOBluetoothUI.IOBluetoothDeviceSelectorController.deviceSelector() >>> selector.runModal() # ask user to select a device -1000 >>> for device in selector.getResults(): ... print device.getName() # show name of selected device ... Nokia 6600 >>> See http://developer.apple.com/documentation/DeviceDrivers/Reference/IOBluetoothUI/index.html for Apple's IOBluetoothUI documentation. See http://pyobjc.sourceforge.net for details on how to access Objective-C classes through PyObjC. """ import objc try: # mac os 10.5 loads frameworks using bridgesupport metadata __bundle__ = objc.initFrameworkWrapper("IOBluetoothUI", frameworkIdentifier="com.apple.IOBluetoothUI", frameworkPath=objc.pathForFramework( "/System/Library/Frameworks/IOBluetoothUI.framework"), globals=globals()) except AttributeError: # earlier versions use loadBundle() and setSignatureForSelector() objc.loadBundle("IOBluetoothUI", globals(), bundle_path=objc.pathForFramework(u'/System/Library/Frameworks/IOBluetoothUI.framework')) del objc lightblue-0.3.2/src/mac/.__LightAquaBlue.py0000755000076500000000000000012210747265251020313 0ustar beawheel00000000000000Mac OS X  2 RTEXTlightblue-0.3.2/src/mac/_LightAquaBlue.py0000755000076500000000000000461310747265251020107 0ustar beawheel00000000000000# Copyright (c) 2006 Bea Lam. All rights reserved. # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files # (the "Software"), to deal in the Software without restriction, # including without limitation the rights to use, copy, modify, merge, # publish, distribute, sublicense, and/or sell copies of the Software, # and to permit persons to whom the Software is furnished to do so, # subject to the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ Provides a python interface to the LightAquaBlue Framework classes, through PyObjC. See http://pyobjc.sourceforge.net for details on how to access Objective-C classes through PyObjC. """ _FRAMEWORK_PATH = u'/Library/Frameworks/LightAquaBlue.framework' import os.path if not os.path.isdir(_FRAMEWORK_PATH): raise ImportError("Cannot load LightAquaBlue framework, not found at" + \ _FRAMEWORK_PATH) import objc objc.loadBundle("LightAquaBlue", globals(), bundle_path=objc.pathForFramework(_FRAMEWORK_PATH)) def _setmethodsignatures(): # return int, take (object, object, object, output unsigned char, output int) # i.e. in python: return (int, char, int), take (object, object, object) objc.setSignatureForSelector("BBServiceAdvertiser", "addRFCOMMServiceDictionary:withName:UUID:channelID:serviceRecordHandle:", "i@0:@@@o^Co^I") # set to take (6-char array, unsigned char, object) # this seems to work even though the selector doesn't take a char aray, # it takes a struct 'BluetoothDeviceAddress' which contains a char array. objc.setSignatureForSelector("BBBluetoothOBEXClient", "initWithRemoteDeviceAddress:channelID:delegate:", '@@:r^[6C]C@') _setmethodsignatures() del objc lightblue-0.3.2/src/mac/.__lightblue.py0000755000076500000000000000012210750613250017571 0ustar beawheel00000000000000Mac OS X  2 RTEXTlightblue-0.3.2/src/mac/_lightblue.py0000755000076500000000000004673510750613250017400 0ustar beawheel00000000000000# Copyright (c) 2006 Bea Lam. All rights reserved. # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files # (the "Software"), to deal in the Software without restriction, # including without limitation the rights to use, copy, modify, merge, # publish, distribute, sublicense, and/or sell copies of the Software, # and to permit persons to whom the Software is furnished to do so, # subject to the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # Mac OS X main module implementation. import types import warnings import Foundation import AppKit import objc import _IOBluetooth import _LightAquaBlue import _lightbluecommon import _macutil import _bluetoothsockets # public attributes __all__ = ("finddevices", "findservices", "finddevicename", "selectdevice", "selectservice", "gethostaddr", "gethostclass", "socket", "advertise", "stopadvertise") # details of advertised services __advertised = {} def finddevices(getnames=True, length=10): inquiry = _SyncDeviceInquiry() inquiry.run(getnames, length) return inquiry.getfounddevices() def findservices(addr=None, name=None, servicetype=None): if servicetype not in (_lightbluecommon.RFCOMM, _lightbluecommon.OBEX, None): raise ValueError("servicetype must be RFCOMM, OBEX or None, was %s" % \ servicetype) if addr is None: try: founddevices = finddevices() except _lightbluecommon.BluetoothError, e: msg = "findservices() failed, " +\ "error while finding devices: " + str(e) raise _lightbluecommon.BluetoothError(msg) #print founddevices addresses = [dev[0] for dev in founddevices] else: addresses = [addr] services = [] for devaddr in addresses: iobtdevice = _IOBluetooth.IOBluetoothDevice.withAddress_( _macutil.createbtdevaddr(devaddr)) try: lastseen = iobtdevice.getLastServicesUpdate() if lastseen is None or lastseen.timeIntervalSinceNow() < -2: # perform SDP query to update known services. # wait at least a few seconds between service discovery cos # sometimes it doesn't work if doing updates too often. # In future should have option to not do updates. serviceupdater = _SDPQueryRunner.alloc().init() try: serviceupdater.query(iobtdevice) # blocks until updated except _lightbluecommon.BluetoothError, e: msg = "findservices() couldn't get services for %s: %s" % \ (iobtdevice.getNameOrAddress(), str(e)) warnings.warn(msg) # or should I use cached services instead of warning? # but sometimes the cached ones are totally wrong. # if searching for RFCOMM, exclude OBEX services if servicetype == _lightbluecommon.RFCOMM: uuidbad = _macutil.PROTO_UUIDS.get(_lightbluecommon.OBEX) else: uuidbad = None filtered = _searchservices(iobtdevice, name=name, uuid=_macutil.PROTO_UUIDS.get(servicetype), uuidbad=uuidbad) #print "unfiltered:", iobtdevice.getServices() services.extend([_getservicetuple(s) for s in filtered]) finally: # close baseband connection (not sure if this is necessary, but # sometimes the transport connection seems to stay open?) iobtdevice.closeConnection() return services def finddevicename(address, usecache=True): if not _lightbluecommon._isbtaddr(address): raise TypeError("%s is not a valid bluetooth address" % str(address)) if address == gethostaddr(): return _gethostname() device = _IOBluetooth.IOBluetoothDevice.withAddress_( _macutil.createbtdevaddr(address)) if usecache: name = device.getName() if name is not None: return name # do name request with timeout of 10 seconds result = device.remoteNameRequest_withPageTimeout_(None, 10000) if result == _macutil.kIOReturnSuccess: return device.getName() raise _lightbluecommon.BluetoothError( "Could not find device name for %s" % address) ### local device ### def gethostaddr(): addr = _LightAquaBlue.BBLocalDevice.getAddressString() if addr is not None: # PyObjC returns all strings as unicode, but the address doesn't need # to be unicode cos it's just hex values return _macutil.formatdevaddr(addr) raise _lightbluecommon.BluetoothError("Cannot read local device address") def gethostclass(): cod = _LightAquaBlue.BBLocalDevice.getClassOfDevice() if cod != -1: return int(cod) raise _lightbluecommon.BluetoothError("Cannot read local device class") def _gethostname(): name = _LightAquaBlue.BBLocalDevice.getName() if name is not None: return name raise _lightbluecommon.BluetoothError("Cannot read local device name") ### socket ### def socket(proto=_lightbluecommon.RFCOMM): return _bluetoothsockets._getsocketobject(proto) ### advertising services ### def advertise(name, sock, servicetype): if not isinstance(name, types.StringTypes): raise TypeError("name must be string, was %s" % \ type(name)) # raises exception if socket is not bound boundchannelID = sock._getport() # advertise the service if servicetype == _lightbluecommon.RFCOMM: try: result, finalchannelID, servicerecordhandle = _LightAquaBlue.BBServiceAdvertiser.addRFCOMMServiceDictionary_withName_UUID_channelID_serviceRecordHandle_( _LightAquaBlue.BBServiceAdvertiser.serialPortProfileDictionary(), name, None, None, None) except: result, finalchannelID, servicerecordhandle = _LightAquaBlue.BBServiceAdvertiser.addRFCOMMServiceDictionary_withName_UUID_channelID_serviceRecordHandle_( _LightAquaBlue.BBServiceAdvertiser.serialPortProfileDictionary(), name, None) elif servicetype == _lightbluecommon.OBEX: try: result, finalchannelID, servicerecordhandle = _LightAquaBlue.BBServiceAdvertiser.addRFCOMMServiceDictionary_withName_UUID_channelID_serviceRecordHandle_( _LightAquaBlue.BBServiceAdvertiser.objectPushProfileDictionary(), name, None, None, None) except: result, finalchannelID, servicerecordhandle = _LightAquaBlue.BBServiceAdvertiser.addRFCOMMServiceDictionary_withName_UUID_channelID_serviceRecordHandle_( _LightAquaBlue.BBServiceAdvertiser.objectPushProfileDictionary(), name, None) else: raise ValueError("servicetype must be either RFCOMM or OBEX") if result != _macutil.kIOReturnSuccess: raise _lightbluecommon.BluetoothError( result, "Error advertising service") if boundchannelID != finalchannelID: msg = "socket bound to unavailable channel (%d), " % boundchannelID +\ "use channel value of 0 to bind to dynamically assigned channel" raise _lightbluecommon.BluetoothError(msg) # note service record handle, so that the service can be stopped later __advertised[id(sock)] = servicerecordhandle def stopadvertise(sock): if sock is None: raise TypeError("Given socket is None") servicerecordhandle = __advertised.get(id(sock)) if servicerecordhandle is None: raise _lightbluecommon.BluetoothError("no service advertised") result = _LightAquaBlue.BBServiceAdvertiser.removeService_(servicerecordhandle) if result != _macutil.kIOReturnSuccess: raise _lightbluecommon.BluetoothError( result, "Error stopping advertising of service") ### GUI ### def selectdevice(): import _IOBluetoothUI gui = _IOBluetoothUI.IOBluetoothDeviceSelectorController.deviceSelector() # try to bring GUI to foreground by setting it as floating panel # (if this is called from pyobjc app, it would automatically be in foreground) try: gui.window().setFloatingPanel_(True) except: pass # show the window and wait for user's selection response = gui.runModal() # problems here if transferring a lot of data?? if response == AppKit.NSRunStoppedResponse: results = gui.getResults() if len(results) > 0: # should always be > 0, but check anyway devinfo = _getdevicetuple(results[0]) # sometimes the baseband connection stays open which causes # problems with connections w so close it here, see if this fixes # it dev = _IOBluetooth.IOBluetoothDevice.withAddress_( _macutil.createbtdevaddr(devinfo[0])) if dev.isConnected(): dev.closeConnection() return devinfo # user cancelled selection return None def selectservice(): import _IOBluetoothUI gui = _IOBluetoothUI.IOBluetoothServiceBrowserController.serviceBrowserController_( _macutil.kIOBluetoothServiceBrowserControllerOptionsNone) # try to bring GUI to foreground by setting it as floating panel # (if this is called from pyobjc app, it would automatically be in foreground) try: gui.window().setFloatingPanel_(True) except: pass # show the window and wait for user's selection response = gui.runModal() if response == AppKit.NSRunStoppedResponse: results = gui.getResults() if len(results) > 0: # should always be > 0, but check anyway serviceinfo = _getservicetuple(results[0]) # sometimes the baseband connection stays open which causes # problems with connections ... so close it here, see if this fixes # it dev = _IOBluetooth.IOBluetoothDevice.withAddress_( _macutil.createbtdevaddr(serviceinfo[0])) if dev.isConnected(): dev.closeConnection() return serviceinfo # user cancelled selection return None ### classes ### class _SDPQueryRunner(Foundation.NSObject): """ Convenience class for performing a synchronous or asynchronous SDP query on an IOBluetoothDevice. """ def query(self, device, timeout=10.0): # do SDP query err = device.performSDPQuery_(self) if err != _macutil.kIOReturnSuccess: raise _lightbluecommon.BluetoothError(err, self._errmsg(device)) # performSDPQuery_ is async, so block-wait self._queryresult = None if not _macutil.waituntil(lambda: self._queryresult is not None, timeout): raise _lightbluecommon.BluetoothError( "Timed out getting services for %s" % \ device.getNameOrAddress()) # query is now complete if self._queryresult != _macutil.kIOReturnSuccess: raise _lightbluecommon.BluetoothError( self._queryresult, self._errmsg(device)) def sdpQueryComplete_status_(self, device, status): # can't raise exception during a callback, so just keep the err value self._queryresult = status _macutil.interruptwait() sdpQueryComplete_status_ = objc.selector( sdpQueryComplete_status_, signature="v@:@i") # accept object, int def _errmsg(self, device): return "Error getting services for %s" % device.getNameOrAddress() class _SyncDeviceInquiry(object): def __init__(self): super(_SyncDeviceInquiry, self).__init__() self._inquiry = _AsyncDeviceInquiry.alloc().init() self._inquiry.cb_completed = self._inquirycomplete self._inquiring = False def run(self, getnames, duration): if self._inquiring: raise _lightbluecommon.BluetoothError( "Another inquiry in progress") # set inquiry attributes self._inquiry.updatenames = getnames self._inquiry.length = duration # start the inquiry err = self._inquiry.start() if err != _macutil.kIOReturnSuccess: raise _lightbluecommon.BluetoothError( err, "Error starting device inquiry") # if error occurs during inquiry, set _inquiryerr to the error code self._inquiryerr = _macutil.kIOReturnSuccess # wait until the inquiry is complete self._inquiring = True _macutil.waituntil(lambda: not self._inquiring) # if error occured during inquiry, raise exception if self._inquiryerr != _macutil.kIOReturnSuccess: raise _lightbluecommon.BluetoothError(self._inquiryerr, "Error during device inquiry") def getfounddevices(self): # return as list of device-info tuples return [_getdevicetuple(device) for device in \ self._inquiry.getfounddevices()] def _inquirycomplete(self, err, aborted): self._inquiryerr = err self._inquiring = False _macutil.interruptwait() # Wrapper around IOBluetoothDeviceInquiry, with python callbacks that you can # set to receive callbacks when the inquiry is started or stopped, or when it # finds a device. # # This discovery doesn't block, so it could be used in a PyObjC application # that is running an event loop. # # Properties: # - 'length': the inquiry length (seconds) # - 'updatenames': whether to update device names during the inquiry # (i.e. perform remote name requests, which will take a little longer) # class _AsyncDeviceInquiry(Foundation.NSObject): # NSObject init, not python __init__ def init(self): try: attr = _IOBluetooth.IOBluetoothDeviceInquiry except AttributeError: raise ImportError("Cannot find IOBluetoothDeviceInquiry class " +\ "to perform device discovery. This class was introduced in " +\ "Mac OS X 10.4, are you running an earlier version?") self = super(_AsyncDeviceInquiry, self).init() self._inquiry = \ _IOBluetooth.IOBluetoothDeviceInquiry.inquiryWithDelegate_(self) # callbacks self.cb_started = None self.cb_completed = None self.cb_founddevice = None return self # length property def _setlength(self, length): self._inquiry.setInquiryLength_(length) length = property( lambda self: self._inquiry.inquiryLength(), _setlength) # updatenames property def _setupdatenames(self, update): self._inquiry.setUpdateNewDeviceNames_(update) updatenames = property( lambda self: self._inquiry.updateNewDeviceNames(), _setupdatenames) # returns error code def start(self): return self._inquiry.start() # returns error code def stop(self): return self._inquiry.stop() # returns list of IOBluetoothDevice objects def getfounddevices(self): return self._inquiry.foundDevices() # # delegate methods follow (these are called by the internal # IOBluetoothDeviceInquiry object when inquiry events occur) # # - (void)deviceInquiryDeviceFound:(IOBluetoothDeviceInquiry*)sender # device:(IOBluetoothDevice*)device; def deviceInquiryDeviceFound_device_(self, inquiry, device): if self.cb_founddevice: self.cb_founddevice(device) deviceInquiryDeviceFound_device_ = objc.selector( deviceInquiryDeviceFound_device_, signature="v@:@@") # - (void)deviceInquiryComplete:error:aborted; def deviceInquiryComplete_error_aborted_(self, inquiry, err, aborted): if self.cb_completed: self.cb_completed(err, aborted) deviceInquiryComplete_error_aborted_ = objc.selector( deviceInquiryComplete_error_aborted_, signature="v@:@iB") # - (void)deviceInquiryStarted:(IOBluetoothDeviceInquiry*)sender; def deviceInquiryStarted_(self, inquiry): if self.cb_started: self.cb_started() ### utility methods ### def _searchservices(device, name=None, uuid=None, uuidbad=None): """ Searches the given IOBluetoothDevice using the specified parameters. Returns an empty list if the device has no services. uuid should be IOBluetoothSDPUUID object. """ if not isinstance(device, _IOBluetooth.IOBluetoothDevice): raise ValueError("device must be IOBluetoothDevice, was %s" % \ type(device)) services = [] allservices = device.getServices() if uuid: gooduuids = (uuid, ) else: gooduuids = () if uuidbad: baduuids = (uuidbad, ) else: baduuids = () if allservices is not None: for s in allservices: if gooduuids and not s.hasServiceFromArray_(gooduuids): continue if baduuids and s.hasServiceFromArray_(baduuids): continue if name is None or s.getServiceName() == name: services.append(s) return services def _getdevicetuple(iobtdevice): """ Returns an (addr, name, COD) device tuple from a IOBluetoothDevice object. """ addr = _macutil.formatdevaddr(iobtdevice.getAddressString()) name = iobtdevice.getName() cod = iobtdevice.getClassOfDevice() return (addr, name, cod) def _getservicetuple(servicerecord): """ Returns a (device-addr, service-channel, service-name) tuple from the given IOBluetoothSDPServiceRecord. """ addr = _macutil.formatdevaddr(servicerecord.getDevice().getAddressString()) name = servicerecord.getServiceName() try: result, channel = servicerecord.getRFCOMMChannelID_(None) # pyobjc 2.0 except TypeError: result, channel = servicerecord.getRFCOMMChannelID_() if result != _macutil.kIOReturnSuccess: try: result, channel = servicerecord.getL2CAPPSM_(None) # pyobjc 2.0 except: result, channel = servicerecord.getL2CAPPSM_() if result != _macutil.kIOReturnSuccess: channel = None return (addr, channel, name) lightblue-0.3.2/src/mac/.__lightbluecommon.py0000755000076500000000000000012210770142100020773 0ustar beawheel00000000000000Mac OS X  2 RTEXTlightblue-0.3.2/src/mac/_lightbluecommon.py0000755000076500007650000002573710770142100020246 0ustar beabea00000000000000# Copyright (c) 2006 Bea Lam. All rights reserved. # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files # (the "Software"), to deal in the Software without restriction, # including without limitation the rights to use, copy, modify, merge, # publish, distribute, sublicense, and/or sell copies of the Software, # and to permit persons to whom the Software is furnished to do so, # subject to the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # Defines attributes with common implementations across the different # platforms. # public attributes __all__ = ("L2CAP", "RFCOMM", "OBEX", "BluetoothError", "splitclass") # Protocol/service class types, used for sockets and advertising services L2CAP, RFCOMM, OBEX = (10, 11, 12) class BluetoothError(IOError): """ Generic exception raised for Bluetooth errors. This is not raised for socket-related errors; socket objects raise the socket.error and socket.timeout exceptions from the standard library socket module. Note that error codes are currently platform-independent. In particular, the Mac OS X implementation returns IOReturn error values from the IOKit framework, and OBEXError codes from for OBEX operations. """ pass def splitclass(classofdevice): """ Splits the given class of device to return a 3-item tuple with the major service class, major device class and minor device class values. These values indicate the device's major services and the type of the device (e.g. mobile phone, laptop, etc.). If you google for "assigned numbers bluetooth baseband" you might find some documents that discuss how to extract this information from the class of device. Example: >>> splitclass(1057036) (129, 1, 3) >>> """ if not isinstance(classofdevice, int): try: classofdevice = int(classofdevice) except (TypeError, ValueError): raise TypeError("Given device class '%s' cannot be split" % \ str(classofdevice)) data = classofdevice >> 2 # skip over the 2 "format" bits service = data >> 11 major = (data >> 6) & 0x1F minor = data & 0x3F return (service, major, minor) _validbtaddr = None def _isbtaddr(address): """ Returns whether the given address is a valid bluetooth address. For example, "00:0e:6d:7b:a2:0a" is a valid address. Returns False if the argument is None or is not a string. """ # Define validity regex. Accept either ":" or "-" as separators. global _validbtaddr if _validbtaddr is None: import re _validbtaddr = re.compile("((\d|[a-f]){2}(:|-)){5}(\d|[a-f]){2}", re.IGNORECASE) import types if not isinstance(address, types.StringTypes): return False return _validbtaddr.match(address) is not None # --------- other attributes --------- def _joinclass(codtuple): """ The opposite of splitclass(). Joins a (service, major, minor) class-of- device tuple into a whole class of device value. """ if not isinstance(codtuple, tuple): raise TypeError("argument must be tuple, was %s" % type(codtuple)) if len(codtuple) != 3: raise ValueError("tuple must have 3 items, has %d" % len(codtuple)) serviceclass = codtuple[0] << 2 << 11 majorclass = codtuple[1] << 2 << 6 minorclass = codtuple[2] << 2 return (serviceclass | majorclass | minorclass) # Docstrings for socket objects. # Based on std lib socket docs. _socketdocs = { "accept": """ accept() -> (socket object, address info) Wait for an incoming connection. Return a new socket representing the connection, and the address of the client. For RFCOMM sockets, the address info is a pair (hostaddr, channel). The socket must be bound and listening before calling this method. """, "bind": """ bind(address) Bind the socket to a local address. For RFCOMM sockets, the address is a pair (host, channel); the host must refer to the local host. A port value of 0 binds the socket to a dynamically assigned port. (Note that on Mac OS X, the port value must always be 0.) The socket must not already be bound. """, "close": """ close() Close the socket. It cannot be used after this call. """, "connect": """ connect(address) Connect the socket to a remote address. The address should be a (host, channel) pair for RFCOMM sockets, and a (host, PSM) pair for L2CAP sockets. The socket must not be already connected. """, "connect_ex": """ connect_ex(address) -> errno This is like connect(address), but returns an error code instead of raising an exception when an error occurs. """, "dup": """ dup() -> socket object Returns a new socket object connected to the same system resource. """, "fileno": """ fileno() -> integer Return the integer file descriptor of the socket. Raises NotImplementedError on Mac OS X and Python For Series 60. """, "getpeername": """ getpeername() -> address info Return the address of the remote endpoint. The address info is a (host, channel) pair for RFCOMM sockets, and a (host, PSM) pair for L2CAP sockets. If the socket has not been connected, socket.error will be raised. """, "getsockname": """ getsockname() -> address info Return the address of the local endpoint. The address info is a (host, channel) pair for RFCOMM sockets, and a (host, PSM) pair for L2CAP sockets. If the socket has not been connected nor bound, this returns the tuple ("00:00:00:00:00:00", 0). """, "getsockopt": """ getsockopt(level, option[, bufsize]) -> value Get a socket option. See the Unix manual for level and option. If a nonzero buffersize argument is given, the return value is a string of that length; otherwise it is an integer. Currently support for socket options are platform independent -- i.e. depends on the underlying Series 60 or BlueZ socket options support. The Mac OS X implementation currently does not support any options at all and automatically raises socket.error. """, "gettimeout": """ gettimeout() -> timeout Returns the timeout in floating seconds associated with socket operations. A timeout of None indicates that timeouts on socket operations are disabled. Currently not supported on Python For Series 60 implementation, which will always return None. """, "listen": """ listen(backlog) Enable a server to accept connections. The backlog argument must be at least 1; it specifies the number of unaccepted connection that the system will allow before refusing new connections. The socket must not be already listening. Currently not implemented on Mac OS X. """, "makefile": """ makefile([mode[, bufsize]]) -> file object Returns a regular file object corresponding to the socket. The mode and bufsize arguments are as for the built-in open() function. """, "recv": """ recv(bufsize[, flags]) -> data Receive up to bufsize bytes from the socket. For the optional flags argument, see the Unix manual. When no data is available, block until at least one byte is available or until the remote end is closed. When the remote end is closed and all data is read, return the empty string. Currently the flags argument has no effect on Mac OS X. """, "recvfrom": """ recvfrom(bufsize[, flags]) -> (data, address info) Like recv(buffersize, flags) but also return the sender's address info. """, "send": """ send(data[, flags]) -> count Send a data string to the socket. For the optional flags argument, see the Unix manual. Return the number of bytes sent. The socket must be connected to a remote socket. Currently the flags argument has no effect on Mac OS X. """, "sendall": """ sendall(data[, flags]) Send a data string to the socket. For the optional flags argument, see the Unix manual. This calls send() repeatedly until all data is sent. If an error occurs, it's impossible to tell how much data has been sent. """, "sendto": """ sendto(data[, flags], address) -> count Like send(data, flags) but allows specifying the destination address. For RFCOMM sockets, the address is a pair (hostaddr, channel). """, "setblocking": """ setblocking(flag) Set the socket to blocking (flag is true) or non-blocking (false). setblocking(True) is equivalent to settimeout(None); setblocking(False) is equivalent to settimeout(0.0). Initially a socket is in blocking mode. In non-blocking mode, if a socket operation cannot be performed immediately, socket.error is raised. The underlying implementation on Python for Series 60 only supports non-blocking mode for send() and recv(), and ignores it for connect() and accept(). """, "setsockopt": """ setsockopt(level, option, value) Set a socket option. See the Unix manual for level and option. The value argument can either be an integer or a string. Currently support for socket options are platform independent -- i.e. depends on the underlying Series 60 or BlueZ socket options support. The Mac OS X implementation currently does not support any options at all and automatically raise socket.error. """, "settimeout": """ settimeout(timeout) Set a timeout on socket operations. 'timeout' can be a float, giving in seconds, or None. Setting a timeout of None disables the timeout feature and is equivalent to setblocking(1). Setting a timeout of zero is the same as setblocking(0). If a timeout is set, the connect, accept, send and receive operations will raise socket.timeout if a timeout occurs. Raises NotImplementedError on Python For Series 60. """, "shutdown": """ shutdown(how) Shut down the reading side of the socket (flag == socket.SHUT_RD), the writing side of the socket (flag == socket.SHUT_WR), or both ends (flag == socket.SHUT_RDWR). """ } lightblue-0.3.2/src/mac/.__macutil.py0000755000076500000000000000012210770564220017254 0ustar beawheel00000000000000Mac OS X  2 RTEXTlightblue-0.3.2/src/mac/_macutil.py0000755000076500007650000002235310770564220016516 0ustar beabea00000000000000# Copyright (c) 2006 Bea Lam. All rights reserved. # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files # (the "Software"), to deal in the Software without restriction, # including without limitation the rights to use, copy, modify, merge, # publish, distribute, sublicense, and/or sell copies of the Software, # and to permit persons to whom the Software is furnished to do so, # subject to the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # Mac-specific utility functions and constants. from Foundation import NSObject, NSDate, NSPoint, NSDefaultRunLoopMode, NSTimer from AppKit import NSApplication, NSEvent, NSApplicationDefined, NSAnyEventMask import objc import time import _IOBluetooth import _lightbluecommon # for mac os 10.5 try: from Foundation import NSUIntegerMax NSAnyEventMask = NSUIntegerMax except: pass # values of constants used in _IOBluetooth.framework kIOReturnSuccess = 0 # defined in kIOBluetoothUserNotificationChannelDirectionIncoming = 1 # defined in kBluetoothHCIErrorPageTimeout = 0x04 # # defined in kIOBluetoothServiceBrowserControllerOptionsNone = 0L LIGHTBLUE_NOTIFY_ID = 5444 # any old number WAIT_MAX_TIMEOUT = 3 # IOBluetoothSDPUUID objects for RFCOMM and OBEX protocol UUIDs PROTO_UUIDS = { _lightbluecommon.RFCOMM: _IOBluetooth.IOBluetoothSDPUUID.uuid16_(0x0003), _lightbluecommon.OBEX: _IOBluetooth.IOBluetoothSDPUUID.uuid16_(0x0008) } def formatdevaddr(addr): """ Returns address of a device in usual form e.g. "00:00:00:00:00:00" - addr: address as returned by device.getAddressString() on an IOBluetoothDevice """ # make uppercase cos PyS60 & Linux seem to always return uppercase # addresses # can safely encode to ascii cos BT addresses are only in hex (pyobjc # returns all strings in unicode) return addr.replace("-", ":").encode('ascii').upper() def createbtdevaddr(addr): # in mac 10.5, can use BluetoothDeviceAddress directly chars = btaddrtochars(addr) try: btdevaddr = _IOBluetooth.BluetoothDeviceAddress(chars) return btdevaddr except: return chars def btaddrtochars(addr): """ Takes a bluetooth address and returns a tuple with the corresponding char values. This can then be used to construct a IOBluetoothDevice object, providing the signature of the withAddress: selector has been set (as in _setpyobjcsignatures() in this module). For example: >>> chars = btaddrtochars("00:0e:0a:00:a2:00") >>> chars (0, 14, 10, 0, 162, 0) >>> device = _IOBluetooth.IOBluetoothDevice.withAddress_(chars) >>> type(device) >>> device.getAddressString() u'00-0e-0a-00-a2-00' """ if not _lightbluecommon._isbtaddr(addr): raise TypeError("address %s not valid bluetooth address" % str(addr)) if addr.find(":") == -1: addr = addr.replace("-", ":") # consider alternative addr separator # unhexlify gives binary value like '\x0e', then ord to get the char value. # unhexlify throws TypeError if value is not a hex pair. import binascii chars = [ord(binascii.unhexlify(part)) for part in addr.split(":")] return tuple(chars) def looponce(): app = NSApplication.sharedApplication() # to push the run loops I seem to have to do this twice # use NSEventTrackingRunLoopMode or NSDefaultRunLoopMode? for i in range(2): event = app.nextEventMatchingMask_untilDate_inMode_dequeue_( NSAnyEventMask, NSDate.dateWithTimeIntervalSinceNow_(0.02), NSDefaultRunLoopMode, False) def waituntil(conditionfunc, timeout=None): """ Waits until conditionfunc() returns true, or seconds have passed. (If timeout=None, this waits indefinitely until conditionfunc() returns true.) Returns false if the process timed out, otherwise returns true. Note!! You must call interruptwait() when you know that conditionfunc() should be checked (e.g. if you are waiting for data and you know some data has arrived) so that this can check conditionfunc(); otherwise it will just continue to wait. (This allows the function to wait for an event that is sent by interruptwait() instead of polling conditionfunc().) This allows the caller to wait while the main event loop processes its events. This must be done for certain situations, e.g. to receive socket data or to accept client connections on a server socket, since IOBluetooth requires the presence of an event loop to run these operations. This function doesn't need to be called if there is something else that is already processing the main event loop, e.g. if called from within a Cocoa application. """ app = NSApplication.sharedApplication() starttime = time.time() if timeout is None: timeout = NSDate.distantFuture().timeIntervalSinceNow() if not isinstance(timeout, (int, float)): raise TypeError("timeout must be int or float, was %s" % \ type(timeout)) endtime = starttime + timeout while True: currtime = time.time() if currtime >= endtime: return False # use WAIT_MAX_TIMEOUT, don't wait forever in case of KeyboardInterrupt e = app.nextEventMatchingMask_untilDate_inMode_dequeue_(NSAnyEventMask, NSDate.dateWithTimeIntervalSinceNow_(min(endtime - currtime, WAIT_MAX_TIMEOUT)), NSDefaultRunLoopMode, True) if e is not None: if (e.type() == NSApplicationDefined and e.subtype() == LIGHTBLUE_NOTIFY_ID): if conditionfunc(): return True else: app.postEvent_atStart_(e, True) def interruptwait(): """ If waituntil() has been called, this will interrupt the waiting process so it can check whether it should stop waiting. """ evt = NSEvent.otherEventWithType_location_modifierFlags_timestamp_windowNumber_context_subtype_data1_data2_(NSApplicationDefined, NSPoint(), NSApplicationDefined, 0, 1, None, LIGHTBLUE_NOTIFY_ID, 0, 0) NSApplication.sharedApplication().postEvent_atStart_(evt, True) class BBCocoaSleeper(NSObject): def init(self): self = super(BBCocoaSleeper, self).init() self.timedout = False return self def sleep(self, timeout): NSTimer.scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_( timeout, self, "timedOut:", None, False) self.timedout = False waituntil(lambda: self.timedout) def timedOut_(self, timer): self.timedout = True interruptwait() timedOut_ = objc.selector(timedOut_, signature="v@:@") def waitfor(timeout): sleeper = BBCocoaSleeper.alloc().init() sleeper.sleep(timeout) class BBFileLikeObjectReader(NSObject): """ Provides a suitable delegate class for the BBDelegatingInputStream class in LightAquaBlue.framework. This basically provides a wrapper for a python file-like object so that it can be read through a NSInputStream. """ def initWithFileLikeObject_(self, fileobj): self = super(BBFileLikeObjectReader, self).init() self.__fileobj = fileobj return self initWithFileLikeObject_ = objc.selector(initWithFileLikeObject_, signature="@@:@") def readDataWithMaxLength_(self, maxlength): try: data = self.__fileobj.read(maxlength) except Exception: return None return buffer(data) readDataWithMaxLength_ = objc.selector(readDataWithMaxLength_, signature="@@:I") #"@12@0:4I8" #"@:I" class BBFileLikeObjectWriter(NSObject): """ Provides a suitable delegate class for the BBDelegatingOutputStream class in LightAquaBlue.framework. This basically provides a wrapper for a python file-like object so that it can be written to through a NSOutputStream. """ def initWithFileLikeObject_(self, fileobj): self = super(BBFileLikeObjectWriter, self).init() self.__fileobj = fileobj return self initWithFileLikeObject_ = objc.selector(initWithFileLikeObject_, signature="@@:@") def write_(self, data): try: self.__fileobj.write(data[:]) except Exception: return -1 return data.length() write_ = objc.selector(write_, signature="i12@0:4@8") #i12@0:4@8 #i@:@ lightblue-0.3.2/src/mac/.__obex.py0000755000076500000000000000012210767105406016556 0ustar beawheel00000000000000Mac OS X  2 RTEXTlightblue-0.3.2/src/mac/_obex.py0000755000076500000000000005777510767105406016373 0ustar beawheel00000000000000# Copyright (c) 2006 Bea Lam. All rights reserved. # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files # (the "Software"), to deal in the Software without restriction, # including without limitation the rights to use, copy, modify, merge, # publish, distribute, sublicense, and/or sell copies of the Software, # and to permit persons to whom the Software is furnished to do so, # subject to the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. import datetime import time import types import objc from Foundation import NSObject, NSDate from _IOBluetooth import OBEXSession, IOBluetoothDevice, \ IOBluetoothRFCOMMChannel from _LightAquaBlue import BBBluetoothOBEXClient, BBBluetoothOBEXServer, \ BBStreamingInputStream, BBStreamingOutputStream, \ BBMutableOBEXHeaderSet, \ BBLocalDevice import _lightbluecommon import _obexcommon import _macutil from _obexcommon import OBEXError # from _kOBEXSuccess = 0 _kOBEXGeneralError = -21850 _kOBEXSessionNotConnectedError = -21876 _kOBEXSessionAlreadyConnectedError = -21882 _kOBEXSessionNoTransportError = -21879 _kOBEXSessionTransportDiedError = -21880 _OBEX_FINAL_MASK = 0x80 _HEADER_MASK = 0xc0 _HEADER_UNICODE = 0x00 _HEADER_BYTE_SEQ = 0x40 _HEADER_1BYTE = 0x80 _HEADER_4BYTE = 0xc0 # public attributes __all__ = ("OBEXClient", "sendfile", "recvfile") _obexerrorcodes = { 0: "no error", -21850: "general error", -21851: "no resources", -21852: "operation not supported", -21853: "internal error", -21854: "bad argument", -21855: "timeout", -21856: "bad request", -21857: "cancelled", -21875: "session is busy", -21876: "OBEX session not connected", -21877: "bad request in OBEX session", -21878: "bad response from other party", -21879: "Bluetooth transport not available", -21880: "Bluetooth transport connection died", -21881: "OBEX session timed out", -21882: "OBEX session already connected" } def errdesc(errorcode): return _obexerrorcodes.get(errorcode, str(errorcode)) # OBEXSession provides response codes with the final bit set, but OBEXResponse # class expects the response code to not have the final bit set. def _cutresponsefinalbit(responsecode): return (responsecode & ~_OBEX_FINAL_MASK) def _headersdicttoset(headers): headerset = BBMutableOBEXHeaderSet.alloc().init() for header, value in headers.items(): if isinstance(header, types.StringTypes): hid = _obexcommon._HEADER_STRINGS_TO_IDS.get(header.lower()) else: hid = header if hid is None: raise ValueError("unknown header '%s'" % header) if isinstance(value, datetime.datetime): value = value.strftime("%Y%m%dT%H%M%S") mask = hid & _HEADER_MASK if mask == _HEADER_UNICODE: if not isinstance(value, types.StringTypes): raise TypeError("value for '%s' must be string, was %s" % (str(header), type(value))) headerset.setValue_forUnicodeHeader_(value, hid) elif mask == _HEADER_BYTE_SEQ: try: value = buffer(value) except: raise TypeError("value for '%s' must be string, array or other buffer type, was %s" % (str(header), type(value))) headerset.setValue_forByteSequenceHeader_(value, hid) elif mask == _HEADER_1BYTE: if not isinstance(value, int): raise TypeError("value for '%s' must be int, was %s" % (str(header), type(value))) headerset.setValue_for1ByteHeader_(value, hid) elif mask == _HEADER_4BYTE: if not isinstance(value, int) and not isinstance(value, long): raise TypeError("value for '%s' must be int, was %s" % (str(header), type(value))) headerset.setValue_for4ByteHeader_(value, hid) if not headerset.containsValueForHeader_(hid): raise ValueError("cannot set OBEX header value for '%s'" % header) return headerset # returns in { header-id: value } form. def _headersettodict(headerset): headers = {} for number in headerset.allHeaders(): hid = number.unsignedCharValue() mask = hid & _HEADER_MASK if mask == _HEADER_UNICODE: value = headerset.valueForUnicodeHeader_(hid) elif mask == _HEADER_BYTE_SEQ: value = headerset.valueForByteSequenceHeader_(hid)[:] if hid == 0x42: # type if len(value) > 0 and value[-1] == '\0': value = value[:-1] # remove null byte elif hid == 0x44: # time iso-8601 string value = _obexcommon._datetimefromstring(value) elif mask == _HEADER_1BYTE: value = headerset.valueFor1ByteHeader_(hid) elif mask == _HEADER_4BYTE: value = headerset.valueFor4ByteHeader_(hid) headers[hid] = value return headers class OBEXClient: __doc__ = _obexcommon._obexclientclassdoc def __init__(self, address, channel): if not _lightbluecommon._isbtaddr(address): raise TypeError("address '%s' is not a valid bluetooth address" % address) if not type(channel) == int: raise TypeError("channel must be int, was %s" % type(channel)) if channel < 0: raise ValueError("channel cannot be negative") self.__serveraddr = (address, channel) self.__busy = False self.__client = None self.__obexsession = None # for testing #BBBluetoothOBEXClient.setDebug_(True) def connect(self, headers={}): if self.__client is not None: raise OBEXError(_kOBEXSessionAlreadyConnectedError, "session is already connected") if not BBLocalDevice.isPoweredOn(): raise OBEXError(_kOBEXSessionNoTransportError, "Bluetooth device not available") self.__delegate = _BBOBEXClientDelegate.alloc().initWithCallback_( self._finishedrequest) self.__client = BBBluetoothOBEXClient.alloc().initWithRemoteDeviceAddress_channelID_delegate_( _macutil.btaddrtochars(self.__serveraddr[0]), self.__serveraddr[1], self.__delegate) if self.__obexsession is not None: self.__client.performSelector_withObject_("setOBEXSession:", self.__obexsession) self.__reset() headerset = _headersdicttoset(headers) r = self.__client.sendConnectRequestWithHeaders_(headerset) if r != _kOBEXSuccess: self.__closetransport() raise OBEXError(r, "error starting Connect request (%s)" % errdesc(r)) _macutil.waituntil(self._done) if self.__error != _kOBEXSuccess: self.__closetransport() raise OBEXError(self.__error, "error during Connect request (%s)" % errdesc(self.__error)) resp = self.__getresponse() if resp.code != _obexcommon.OK: self.__closetransport() return resp def disconnect(self, headers={}): self.__checkconnected() self.__reset() try: headerset = _headersdicttoset(headers) r = self.__client.sendDisconnectRequestWithHeaders_(headerset) if r != _kOBEXSuccess: raise OBEXError(r, "error starting Disconnect request (%s)" % errdesc(r)) _macutil.waituntil(self._done) if self.__error != _kOBEXSuccess: raise OBEXError(self.__error, "error during Disconnect request (%s)" % errdesc(self.__error)) finally: # close channel regardless of disconnect result self.__closetransport() return self.__getresponse() def put(self, headers, fileobj): if not hasattr(fileobj, "read"): raise TypeError("file-like object must have read() method") self.__checkconnected() self.__reset() headerset = _headersdicttoset(headers) self.fileobj = fileobj self.__fileobjdelegate = _macutil.BBFileLikeObjectReader.alloc().initWithFileLikeObject_(fileobj) self.instream = BBStreamingInputStream.alloc().initWithDelegate_(self.__fileobjdelegate) self.instream.open() r = self.__client.sendPutRequestWithHeaders_readFromStream_( headerset, self.instream) if r != _kOBEXSuccess: raise OBEXError(r, "error starting Put request (%s)" % errdesc(r)) _macutil.waituntil(self._done) if self.__error != _kOBEXSuccess: raise OBEXError(self.__error, "error during Put request (%s)" % errdesc(self.__error)) return self.__getresponse() def delete(self, headers): self.__checkconnected() self.__reset() headerset = _headersdicttoset(headers) r = self.__client.sendPutRequestWithHeaders_readFromStream_(headerset, None) if r != _kOBEXSuccess: raise OBEXError(r, "error starting Delete request (%s)" % errdesc(r)) _macutil.waituntil(self._done) if self.__error != _kOBEXSuccess: raise OBEXError(self.__error, "error during Delete request (%s)" % errdesc(self.__error)) return self.__getresponse() def get(self, headers, fileobj): if not hasattr(fileobj, "write"): raise TypeError("file-like object must have write() method") self.__checkconnected() self.__reset() headerset = _headersdicttoset(headers) delegate = _macutil.BBFileLikeObjectWriter.alloc().initWithFileLikeObject_(fileobj) outstream = BBStreamingOutputStream.alloc().initWithDelegate_(delegate) outstream.open() r = self.__client.sendGetRequestWithHeaders_writeToStream_( headerset, outstream) if r != _kOBEXSuccess: raise OBEXError(r, "error starting Get request (%s)" % errdesc(r)) _macutil.waituntil(self._done) if self.__error != _kOBEXSuccess: raise OBEXError(self.__error, "error during Get request (%s)" % errdesc(self.__error)) return self.__getresponse() def setpath(self, headers, cdtoparent=False, createdirs=False): self.__checkconnected() self.__reset() headerset = _headersdicttoset(headers) r = self.__client.sendSetPathRequestWithHeaders_changeToParentDirectoryFirst_createDirectoriesIfNeeded_(headerset, cdtoparent, createdirs) if r != _kOBEXSuccess: raise OBEXError(r, "error starting SetPath request (%s)" % errdesc(r)) _macutil.waituntil(self._done) if self.__error != _kOBEXSuccess: raise OBEXError(self.__error, "error during SetPath request (%s)" % errdesc(self.__error)) return self.__getresponse() def _done(self): return not self.__busy def _finishedrequest(self, error, response): if error in (_kOBEXSessionNoTransportError, _kOBEXSessionTransportDiedError): self.__closetransport() self.__error = error self.__response = response self.__busy = False _macutil.interruptwait() def _setobexsession(self, session): self.__obexsession = session # Note that OBEXSession returns kOBEXSessionNotConnectedError if you don't # send CONNECT before sending any other requests; this means the OBEXClient # must send connect() before other requests, so this restriction is enforced # in the Linux version as well, for consistency. def __checkconnected(self): if self.__client is None: raise OBEXError(_kOBEXSessionNotConnectedError, "must connect() before sending other requests") def __closetransport(self): if self.__client is not None: try: self.__client.RFCOMMChannel().closeChannel() self.__client.RFCOMMChannel().getDevice().closeConnection() except: pass self.__client = None def __reset(self): self.__busy = True self.__error = None self.__response = None def __getresponse(self): code = self.__response.responseCode() rawheaders = _headersettodict(self.__response.allHeaders()) return _obexcommon.OBEXResponse(_cutresponsefinalbit(code), rawheaders) # set method docstrings definedmethods = locals() # i.e. defined methods in OBEXClient for name, doc in _obexcommon._obexclientdocs.items(): try: definedmethods[name].__doc__ = doc except KeyError: pass class _BBOBEXClientDelegate(NSObject): def initWithCallback_(self, cb_requestdone): self = super(_BBOBEXClientDelegate, self).init() self._cb_requestdone = cb_requestdone return self initWithCallback_ = objc.selector(initWithCallback_, signature="@@:@") # # Delegate methods follow. objc signatures for all methods must be set # using objc.selector or else may get bus error. # # - (void)client:(BBBluetoothOBEXClient *)client # didFinishConnectRequestWithError:(OBEXError)error # response:(BBOBEXResponse *)response; def client_didFinishConnectRequestWithError_response_(self, client, error, response): self._cb_requestdone(error, response) client_didFinishConnectRequestWithError_response_ = objc.selector( client_didFinishConnectRequestWithError_response_, signature="v@:@i@") # - (void)client:(BBBluetoothOBEXClient *)client # didFinishDisconnectRequestWithError:(OBEXError)error # response:(BBOBEXResponse *)response; def client_didFinishDisconnectRequestWithError_response_(self, client, error, response): self._cb_requestdone(error, response) client_didFinishDisconnectRequestWithError_response_ = objc.selector( client_didFinishDisconnectRequestWithError_response_,signature="v@:@i@") # - (void)client:(BBBluetoothOBEXClient *)client # didFinishPutRequestForStream:(NSInputStream *)inputStream # error:(OBEXError)error # response:(BBOBEXResponse *)response; def client_didFinishPutRequestForStream_error_response_(self, client, instream, error, response): self._cb_requestdone(error, response) client_didFinishPutRequestForStream_error_response_ = objc.selector( client_didFinishPutRequestForStream_error_response_,signature="v@:@@i@") # - (void)client:(BBBluetoothOBEXClient *)client # didFinishGetRequestForStream:(NSOutputStream *)outputStream # error:(OBEXError)error # response:(BBOBEXResponse *)response; def client_didFinishGetRequestForStream_error_response_(self, client, outstream, error, response): self._cb_requestdone(error, response) client_didFinishGetRequestForStream_error_response_ = objc.selector( client_didFinishGetRequestForStream_error_response_,signature="v@:@@i@") # - (void)client:(BBBluetoothOBEXClient *)client # didFinishSetPathRequestWithError:(OBEXError)error # response:(BBOBEXResponse *)response; def client_didFinishSetPathRequestWithError_response_(self, client, error, response): self._cb_requestdone(error, response) client_didFinishSetPathRequestWithError_response_ = objc.selector( client_didFinishSetPathRequestWithError_response_, signature="v@:@i@") # client:didAbortRequestWithStream:error:response: not # implemented since OBEXClient does not allow abort requests # ------------------------------------------------------------------ def sendfile(address, channel, source): if not _lightbluecommon._isbtaddr(address): raise TypeError("address '%s' is not a valid bluetooth address" % address) if not isinstance(channel, int): raise TypeError("channel must be int, was %s" % type(channel)) if not isinstance(source, types.StringTypes) and \ not hasattr(source, "read"): raise TypeError("source must be string or file-like object with read() method") if isinstance(source, types.StringTypes): headers = {"name": source} fileobj = file(source, "rb") closefileobj = True else: if hasattr(source, "name"): headers = {"name": source.name} fileobj = source closefileobj = False client = OBEXClient(address, channel) client.connect() try: resp = client.put(headers, fileobj) finally: if closefileobj: fileobj.close() try: client.disconnect() except: pass # always ignore disconnection errors if resp.code != _obexcommon.OK: raise OBEXError("server denied the Put request") # ------------------------------------------------------------------ class BBOBEXObjectPushServer(NSObject): def initWithChannel_fileLikeObject_(self, channel, fileobject): if not isinstance(channel, IOBluetoothRFCOMMChannel) and \ not isinstance(channel, OBEXSession): raise TypeError("internal error, channel is of wrong type %s" % type(channel)) if not hasattr(fileobject, "write"): raise TypeError("fileobject must be file-like object with write() method") self = super(BBOBEXObjectPushServer, self).init() self.__fileobject = fileobject self.__server = BBBluetoothOBEXServer.alloc().initWithIncomingRFCOMMChannel_delegate_(channel, self) #BBBluetoothOBEXServer.setDebug_(True) self.__error = None self.__gotfile = False self.__gotdisconnect = False self.__disconnected = False # for internal testing if isinstance(channel, OBEXSession): self.__server.performSelector_withObject_("setOBEXSession:", channel) return self initWithChannel_fileLikeObject_ = objc.selector( initWithChannel_fileLikeObject_, signature="@@:i@") def run(self): self.__server.run() # wait until client sends a file, or an error occurs _macutil.waituntil(lambda: self.__gotfile or self.__error is not None) # wait briefly for a disconnect request (client may have decided to just # close the connection without sending a disconnect request) if self.__error is None: ok = _macutil.waituntil(lambda: self.__gotdisconnect, 3) if ok: _macutil.waituntil(lambda: self.__disconnected) # only raise OBEXError if file was not received if not self.__gotfile: if self.__error is not None: raise OBEXError(self.__error[0], self.__error[1]) # if client connected but didn't send PUT raise OBEXError(_kOBEXGeneralError, "client did not send a file") # # BBBluetoothOBEXClientDelegate methods follow. # These enable this class to get callbacks when some event occurs on the # server (e.g. got a new client request, or an error occurred, etc.). # # - (BOOL)server:(BBBluetoothOBEXServer *)server # shouldHandleConnectRequest:(BBOBEXHeaderSet *)requestHeaders; def server_shouldHandleConnectRequest_(self, server, requestheaders): return True server_shouldHandleConnectRequest_ = objc.selector( server_shouldHandleConnectRequest_, signature="c@:@@") # - (BOOL)server:(BBBluetoothOBEXServer *)server # shouldHandleDisconnectRequest:(BBOBEXHeaderSet *)requestHeaders; def server_shouldHandleDisconnectRequest_(self, server, requestheaders): self.__gotdisconnect = True _macutil.interruptwait() return True server_shouldHandleDisconnectRequest_ = objc.selector( server_shouldHandleDisconnectRequest_, signature="c@:@@") # - (void)serverDidHandleDisconnectRequest:(BBBluetoothOBEXServer *)server; def serverDidHandleDisconnectRequest_(self, server): self.__disconnected = True _macutil.interruptwait() serverDidHandleDisconnectRequest_ = objc.selector( serverDidHandleDisconnectRequest_, signature="v@:@") # - (NSOutputStream *)server:(BBBluetoothOBEXServer *)server # shouldHandlePutRequest:(BBOBEXHeaderSet *)requestHeaders; def server_shouldHandlePutRequest_(self, server, requestheaders): #print "Incoming file:", requestHeaders.valueForNameHeader() self.delegate = _macutil.BBFileLikeObjectWriter.alloc().initWithFileLikeObject_(self.__fileobject) outstream = BBStreamingOutputStream.alloc().initWithDelegate_(self.delegate) outstream.open() return outstream server_shouldHandlePutRequest_ = objc.selector( server_shouldHandlePutRequest_, signature="@@:@@") # - (void)server:(BBBluetoothOBEXServer *)server # didHandlePutRequestForStream:(NSOutputStream *)outputStream # requestWasAborted:(BOOL)aborted; def server_didHandlePutRequestForStream_requestWasAborted_(self, server, stream, aborted): if aborted: self.__error = (_kOBEXGeneralError, "client aborted file transfer") else: self.__gotfile = True _macutil.interruptwait() server_didHandlePutRequestForStream_requestWasAborted_ = objc.selector( server_didHandlePutRequestForStream_requestWasAborted_, signature="v@:@@c") # - (void)server:(BBBluetoothOBEXServer *)server # errorOccurred:(OBEXError)error # description:(NSString *)description; def server_errorOccurred_description_(self, server, error, desc): self.__error = (error, desc) _macutil.interruptwait() server_errorOccurred_description_ = objc.selector( server_errorOccurred_description_, signature="v@:@i@") # ------------------------------------------------------------------ def recvfile(sock, dest): if sock is None: raise TypeError("Given socket is None") if not isinstance(dest, (types.StringTypes, types.FileType)): raise TypeError("dest must be string or file-like object with write() method") if isinstance(dest, types.StringTypes): fileobj = open(dest, "wb") closefileobj = True else: fileobj = dest closefileobj = False try: sock.listen(1) conn, addr = sock.accept() #print "A client connected:", addr server = BBOBEXObjectPushServer.alloc().initWithChannel_fileLikeObject_( conn._getchannel(), fileobj) server.run() conn.close() finally: if closefileobj: fileobj.close() lightblue-0.3.2/src/mac/.__obexcommon.py0000755000076500000000000000012210750556342017770 0ustar beawheel00000000000000Mac OS X  2 RTEXTlightblue-0.3.2/src/mac/_obexcommon.py0000755000076500007650000004434310750556342017235 0ustar beabea00000000000000# Copyright (c) 2006 Bea Lam. All rights reserved. # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files # (the "Software"), to deal in the Software without restriction, # including without limitation the rights to use, copy, modify, merge, # publish, distribute, sublicense, and/or sell copies of the Software, # and to permit persons to whom the Software is furnished to do so, # subject to the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. import _lightbluecommon __all__ = ('OBEXResponse', 'OBEXError', 'CONTINUE', 'OK', 'CREATED', 'ACCEPTED', 'NON_AUTHORITATIVE_INFORMATION', 'NO_CONTENT', 'RESET_CONTENT', 'PARTIAL_CONTENT', 'MULTIPLE_CHOICES', 'MOVED_PERMANENTLY', 'MOVED_TEMPORARILY', 'SEE_OTHER', 'NOT_MODIFIED', 'USE_PROXY', 'BAD_REQUEST', 'UNAUTHORIZED', 'PAYMENT_REQUIRED', 'FORBIDDEN', 'NOT_FOUND', 'METHOD_NOT_ALLOWED', 'NOT_ACCEPTABLE', 'PROXY_AUTHENTICATION_REQUIRED', 'REQUEST_TIME_OUT', 'CONFLICT', 'GONE', 'LENGTH_REQUIRED', 'PRECONDITION_FAILED', 'REQUESTED_ENTITY_TOO_LARGE', 'REQUEST_URL_TOO_LARGE', 'UNSUPPORTED_MEDIA_TYPE', 'INTERNAL_SERVER_ERROR', 'NOT_IMPLEMENTED', 'BAD_GATEWAY', 'SERVICE_UNAVAILABLE', 'GATEWAY_TIMEOUT', 'HTTP_VERSION_NOT_SUPPORTED', 'DATABASE_FULL', 'DATABASE_LOCKED') class OBEXError(_lightbluecommon.BluetoothError): """ Generic exception raised for OBEX-related errors. """ pass class OBEXResponse: """ Contains the OBEX response received from an OBEX server. When an OBEX client sends a request, the OBEX server sends back a response code (to indicate whether the request was successful) and a set of response headers (to provide other useful information). For example, if a client sends a 'Get' request to retrieve a file, the client might get a response like this: >>> import lightblue >>> client = lightblue.obex.OBEXClient("aa:bb:cc:dd:ee:ff", 10) >>> response = client.get({"name": "file.txt"}, file("file.txt", "w")) >>> print response You can get the response code and response headers in different formats: >>> print response.reason 'OK' # a string description of the response code >>> print response.code 32 # the response code (e.g. this is 0x20) >>> print response.headers {'length': 35288} # the headers, with string keys >>> print response.rawheaders {195: 35288} # the headers, with raw header ID keys >>> Note how the 'code' attribute does not have the final bit set - e.g. for OK/Success, the response code is 0x20, not 0xA0. The lightblue.obex module defines constants for response code values (e.g. lightblue.obex.OK, lightblue.obex.FORBIDDEN, etc.). """ def __init__(self, code, rawheaders): self.__code = code self.__reason = _OBEX_RESPONSES.get(code, "Unknown response code") self.__rawheaders = rawheaders self.__headers = None code = property(lambda self: self.__code, doc='The response code, without the final bit set.') reason = property(lambda self: self.__reason, doc='A string description of the response code.') rawheaders = property(lambda self: self.__rawheaders, doc='The response headers, as a dictionary with header ID (unsigned byte) keys.') def getheader(self, header, default=None): ''' Returns the response header value for the given header, which may either be a string (not case-sensitive) or the raw byte value of the header ID. Returns the specified default value if the header is not present. ''' if isinstance(header, types.StringTypes): return self.headers.get(header.lower(), default) return self.__rawheaders.get(header, default) def __getheaders(self): if self.__headers is None: self.__headers = {} for headerid, value in self.__rawheaders.items(): if headerid in _HEADER_IDS_TO_STRINGS: self.__headers[_HEADER_IDS_TO_STRINGS[headerid]] = value else: self.__headers["0x%02x" % headerid] = value return self.__headers headers = property(__getheaders, doc='The response headers, as a dictionary with string keys.') def __repr__(self): return "" % \ (self.__reason, self.__code, (self.__code | 0x80), str(self.headers)) try: import datetime # as from python docs example class UTC(datetime.tzinfo): """UTC""" def utcoffset(self, dt): return datetime.timedelta(0) def tzname(self, dt): return "UTC" def dst(self, dt): return datetime.timedelta(0) except: pass # no datetime on pys60 _LOCAL_TIME_FORMAT = "%Y%m%dT%H%M%S" _UTC_TIME_FORMAT = _LOCAL_TIME_FORMAT + "Z" def _datetimefromstring(s): import time if s[-1:] == "Z": # add UTC() instance as tzinfo args = (time.strptime(s, _UTC_TIME_FORMAT)[0:6]) + (0, UTC()) return datetime.datetime(*args) else: return datetime.datetime(*(time.strptime(s, _LOCAL_TIME_FORMAT)[0:6])) _HEADER_STRINGS_TO_IDS = { "count": 0xc0, "name": 0x01, "type": 0x42, "length": 0xc3, "time": 0x44, "description": 0x05, "target": 0x46, "http": 0x47, "who": 0x4a, "connection-id": 0xcb, "application-parameters": 0x4c, "authentication-challenge": 0x4d, "authentication-response": 0x4e, "creator-id": 0xcf, "wan-uuid": 0x50, "object-class": 0x51, "session-parameters": 0x52, "session-sequence-number": 0x93 } _HEADER_IDS_TO_STRINGS = {} for key, value in _HEADER_STRINGS_TO_IDS.items(): _HEADER_IDS_TO_STRINGS[value] = key assert len(_HEADER_IDS_TO_STRINGS) == len(_HEADER_STRINGS_TO_IDS) # These match the associated strings in httplib.responses, since OBEX response # codes are matched to HTTP status codes (except for 0x60 and 0x61). # Note these are the responses *without* the final bit set. _OBEX_RESPONSES = { 0x10: "Continue", 0x20: "OK", 0x21: "Created", 0x22: "Accepted", 0x23: "Non-Authoritative Information", 0x24: "No Content", 0x25: "Reset Content", 0x26: "Partial Content", 0x30: "Multiple Choices", 0x31: "Moved Permanently", 0x32: "Moved Temporarily", # but is 'Found' (302) in httplib.response??? 0x33: "See Other", 0x34: "Not Modified", 0x35: "Use Proxy", 0x40: "Bad Request", 0x41: "Unauthorized", 0x42: "Payment Required", 0x43: "Forbidden", 0x44: "Not Found", 0x45: "Method Not Allowed", 0x46: "Not Acceptable", 0x47: "Proxy Authentication Required", 0x48: "Request Timeout", 0x49: "Conflict", 0x4A: "Gone", 0x48: "Length Required", 0x4C: "Precondition Failed", 0x4D: "Request Entity Too Large", 0x4E: "Request-URI Too Long", 0x4F: "Unsupported Media Type", 0x50: "Internal Server Error", 0x51: "Not Implemented", 0x52: "Bad Gateway", 0x53: "Service Unavailable", 0x54: "Gateway Timeout", 0x55: "HTTP Version Not Supported", 0x60: "Database Full", 0x61: "Database Locked" } _obexclientclassdoc = \ """ An OBEX client class. (Note this is not available on Python for Series 60.) For example, to connect to an OBEX server and send a file: >>> import lightblue >>> client = lightblue.obex.OBEXClient("aa:bb:cc:dd:ee:ff", 10) >>> client.connect() >>> client.put({"name": "photo.jpg"}, file("photo.jpg", "rb")) >>> client.disconnect() >>> A client must call connect() to establish a connection before it can send any other requests. The connect(), disconnect(), put(), delete(), get() and setpath() methods all accept the request headers as a dictionary of header-value mappings. The request headers are used to provide the server with additional information for the request. For example, this sends a Put request that includes Name, Type and Length headers in the request headers, to provide details about the transferred file: >>> f = file("file.txt") >>> client.put({"name": "file.txt", "type": "text/plain", ... "length": 5192}, f) >>> Here is a list of all the different string header keys that you can use in the request headers, and the expected type of the value for each header: - "name" -> a string - "type" -> a string - "length" -> an int - "time" -> a datetime object from the datetime module - "description" -> a string - "target" -> a string or buffer - "http" -> a string or buffer - "who" -> a string or buffer - "connection-id" -> an int - "application-parameters" -> a string or buffer - "authentication-challenge" -> a string or buffer - "authentication-response" -> a string or buffer - "creator-id" -> an int - "wan-uuid" -> a string or buffer - "object-class" -> a string or buffer - "session-parameters" -> a string or buffer - "session-sequence-number" -> an int less than 256 (The string header keys are not case-sensitive.) Alternatively, you can use raw header ID values instead of the above convenience strings. So, the previous example can be rewritten as: >>> client.put({0x01: "file.txt", 0x42: "text/plain", 0xC3: 5192}, ... fileobject) >>> This is also useful for inserting custom headers. For example, a PutImage request for a Basic Imaging client requires the Img-Descriptor (0x71) header: >>> client.put({"type": "x-bt/img-img", ... "name": "photo.jpg", ... 0x71: ''}, ... file('photo.jpg', 'rb')) >>> Notice that the connection-id header is not sent, because this is automatically included by OBEXClient in the request headers if a connection-id was received in a previous Connect response. See the included src/examples/obex_ftp_client.py for an example of using OBEXClient to implement a File Transfer client for browsing the files on a remote device. """ _obexclientdocs = { "__init__": """ Creates an OBEX client. Arguments: - address: the address of the remote device - channel: the RFCOMM channel of the remote OBEX service """, "connect": """ Establishes the Bluetooth connection to the remote OBEX server and sends a Connect request to open the OBEX session. Returns an OBEXResponse instance containing the server response. Raises lightblue.obex.OBEXError if the session is already connected, or if an error occurs during the request. If the server refuses the Connect request (i.e. if it sends a response code other than OK/Success), the Bluetooth connection will be closed. Arguments: - headers={}: the headers to send for the Connect request """, "disconnect": """ Sends a Disconnect request to end the OBEX session and closes the Bluetooth connection to the remote OBEX server. Returns an OBEXResponse instance containing the server response. Raises lightblue.obex.OBEXError if connect() has not been called, or if an error occurs during the request. Note that you don't need to send any connection-id headers - this is automatically included if the client received one in a Connect response. Arguments: - headers={}: the headers to send for the request """, "put": """ Sends a Put request. Returns an OBEXResponse instance containing the server response. Raises lightblue.obex.OBEXError if connect() has not been called, or if an error occurs during the request. Note that you don't need to send any connection-id headers - this is automatically included if the client received one in a Connect response. Arguments: - headers: the headers to send for the request - fileobj: a file-like object containing the file data to be sent for the request For example, to send a file named 'photo.jpg', using the request headers to notify the server of the file's name, MIME type and length: >>> client = lightblue.obex.OBEXClient("aa:bb:cc:dd:ee:ff", 10) >>> client.connect() >>> client.put({"name": "photo.jpg", "type": "image/jpeg", "length": 28566}, file("photo.jpg", "rb")) >>> """, "delete": """ Sends a Put-Delete request in order to delete a file or folder on the remote server. Returns an OBEXResponse instance containing the server response. Raises lightblue.obex.OBEXError if connect() has not been called, or if an error occurs during the request. Note that you don't need to send any connection-id headers - this is automatically included if the client received one in a Connect response. Arguments: - headers: the headers to send for the request - you should use the 'name' header to specify the file you want to delete If the file on the server can't be deleted because it's a read-only file, you might get an 'Unauthorized' response, like this: >>> client = lightblue.obex.OBEXClient("aa:bb:cc:dd:ee:ff", 10) >>> client.connect() >>> client.delete({"name": "random_file.txt"}) >>> """, "get": """ Sends a Get request. Returns an OBEXResponse instance containing the server response. Raises lightblue.obex.OBEXError if connect() has not been called, or if an error occurs during the request. Note that you don't need to send any connection-id headers - this is automatically included if the client received one in a Connect response. Arguments: - headers: the headers to send for the request - you should use these to specify the file you want to retrieve - fileobj: a file-like object, to which the received data will be written An example: >>> client = lightblue.obex.OBEXClient("aa:bb:cc:dd:ee:ff", 10) >>> client.connect() >>> f = file("received_file.txt", "w+") >>> client.get({"name": "testfile.txt"}, f) >>> f.seek(0) >>> f.read() 'test file' >>> """, "setpath": """ Sends a SetPath request in order to set the "current path" on the remote server for file transfers. Returns an OBEXResponse instance containing the server response. Raises lightblue.obex.OBEXError if connect() has not been called, or if an error occurs during the request. Note that you don't need to send any connection-id headers - this is automatically included if the client received one in a Connect response. Arguments: - headers: the headers to send for the request - you should use the 'name' header to specify the directory you want to change to - cdtoparent=False: True if the remote server should move up one directory before applying the specified directory (i.e. 'cd ../dirname') - createdirs=False: True if the specified directory should be created if it doesn't exist (if False, the server will return an error response if the directory doesn't exist) For example: # change to the "images" subdirectory >>> client.setpath({"name": "images"}) >>> # change to the parent directory >>> client.setpath({}, cdtoparent=True) >>> # create a subdirectory "My_Files" >>> client.setpath({"name": "My_Files"}, createdirs=True) >>> # change to the root directory - you can use an empty "name" header # to specify this >>> client.setpath({"name": ""}) >>> """ } # response constants CONTINUE = 0x10 OK = 0x20 CREATED = 0x21 ACCEPTED = 0x22 NON_AUTHORITATIVE_INFORMATION = 0x23 NO_CONTENT = 0x24 RESET_CONTENT = 0x25 PARTIAL_CONTENT = 0x26 MULTIPLE_CHOICES = 0x30 MOVED_PERMANENTLY = 0x31 MOVED_TEMPORARILY = 0x32 SEE_OTHER = 0x33 NOT_MODIFIED = 0x34 USE_PROXY = 0x35 BAD_REQUEST = 0x40 UNAUTHORIZED = 0x41 PAYMENT_REQUIRED = 0x42 FORBIDDEN = 0x43 NOT_FOUND = 0x44 METHOD_NOT_ALLOWED = 0x45 NOT_ACCEPTABLE = 0x46 PROXY_AUTHENTICATION_REQUIRED = 0x47 REQUEST_TIME_OUT = 0x48 CONFLICT = 0x49 GONE = 0x4A LENGTH_REQUIRED = 0x4B PRECONDITION_FAILED = 0x4C REQUESTED_ENTITY_TOO_LARGE = 0x4D REQUEST_URL_TOO_LARGE = 0x4E UNSUPPORTED_MEDIA_TYPE = 0x4F INTERNAL_SERVER_ERROR = 0x50 NOT_IMPLEMENTED = 0x51 BAD_GATEWAY = 0x52 SERVICE_UNAVAILABLE = 0x53 GATEWAY_TIMEOUT = 0x54 HTTP_VERSION_NOT_SUPPORTED = 0x55 DATABASE_FULL = 0x60 DATABASE_LOCKED = 0x61 lightblue-0.3.2/src/mac/LightAquaBlue/0000755000076500007650000000000010770565177017042 5ustar beabea00000000000000lightblue-0.3.2/src/mac/LightAquaBlue/advertising-notes.txt0000644000076500007650000000276310747223642023250 0ustar beabea00000000000000SerialPortDictionary.plist is a modification of the same file in the OBEXSample project available in the Mac OS X Developer examples (under 'Bluetooth'). I modified it by adding a 0x1101 (Serial Port Profile) in the ServiceClassIDList entry. Without this entry, the Nokia 6600 (and it seems Series 60 phones in general) could not find the service when searching for RFCOMM services. Other web resources seem to confirm that the 0x1101 is necessary for RFCOMM services. How does OBEXObjectPushDictionary.plist differ from SerialPortDictionary.plist? In OBEXObjectPushDictionary.plist: - ServiceClassIDList property has <1105> as a data object to represent the OBEX Object Push Profile. - BluetoothProfileDescriptorList has <1105> too as an entry. - ProtocolDescriptorList has <0008> (for OBEX protocol) along with the L2CAP (0x0100) and RFCOMM (0x0003) entries I used 0x1105 which is the UUID for the OBEX Object Push Profile. OBEXFileTransferDictionary uses 0x1106 instead which is the UUID for the File Transfer Profile. The thing to do is to build a server which supports particular operations and then choose the right profile depending on whether you support the corresponding operations. E.g. if you advertise the File Transfer Profile your server should support folder browsing, and Object Push servers might support some kind of business card exchange via vCards etc. See BluetoothAssignedNumbers.h in the IOBluetooth.framework headers for a list of all the appropriate UUIDs and numbers and what they mean.lightblue-0.3.2/src/mac/LightAquaBlue/BBBluetoothChannelDelegate.h0000644000076500007650000000406010747262564024306 0ustar beabea00000000000000/* * Copyright (c) 2006 Bea Lam. All rights reserved. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation files * (the "Software"), to deal in the Software without restriction, * including without limitation the rights to use, copy, modify, merge, * publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ // // BBBluetoothChannelDelegate.h // LightAquaBlue // // Provides a delegate for RFCOMM and L2CAP channels. This is only intended // for use from the LightBlue library. // #include @class IOBluetoothRFCOMMChannel; @class IOBluetoothL2CAPChannel; @interface BBBluetoothChannelDelegate : NSObject { id m_delegate; } - (id)initWithDelegate:(id)delegate; + (IOReturn)synchronouslyWriteData:(NSData *)data toRFCOMMChannel:(IOBluetoothRFCOMMChannel *)channel; + (IOReturn)synchronouslyWriteData:(NSData *)data toL2CAPChannel:(IOBluetoothL2CAPChannel *)channel; @end /* * These are the methods that should be implemented by the delegate of this * delegate (very awkward, but that's what it is). */ @protocol BBBluetoothChannelDelegateObserver - (void)channelData:(id)channel data:(NSData *)data; - (void)channelClosed:(id)channel; @end lightblue-0.3.2/src/mac/LightAquaBlue/BBBluetoothChannelDelegate.m0000644000076500007650000000637710747262555024330 0ustar beabea00000000000000/* * Copyright (c) 2006 Bea Lam. All rights reserved. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation files * (the "Software"), to deal in the Software without restriction, * including without limitation the rights to use, copy, modify, merge, * publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ // // BBBluetoothChannelDelegate.m // LightAquaBlue // #import "BBBluetoothChannelDelegate.h" #import #import static SEL channelDataSelector; static SEL channelClosedSelector; @implementation BBBluetoothChannelDelegate + (void)initialize { channelDataSelector = @selector(channelData:data:); channelClosedSelector = @selector(channelClosed:); } - (id)initWithDelegate:(id)delegate { self = [super init]; m_delegate = delegate; return self; } - (void)rfcommChannelData:(IOBluetoothRFCOMMChannel *)rfcommChannel data:(void *)dataPointer length:(size_t)dataLength { if (m_delegate && [m_delegate respondsToSelector:channelDataSelector]) { [m_delegate channelData:rfcommChannel data:[NSData dataWithBytes:dataPointer length:dataLength]]; } } - (void)rfcommChannelClosed:(IOBluetoothRFCOMMChannel *)rfcommChannel { if (m_delegate && [m_delegate respondsToSelector:channelClosedSelector]) { [m_delegate channelClosed:rfcommChannel]; } } - (void)l2capChannelData:(IOBluetoothL2CAPChannel *)l2capChannel data:(void *)dataPointer length:(size_t)dataLength { if (m_delegate && [m_delegate respondsToSelector:channelDataSelector]) { [m_delegate channelData:l2capChannel data:[NSData dataWithBytes:dataPointer length:dataLength]]; } } - (void)l2capChannelClosed:(IOBluetoothL2CAPChannel *)l2capChannel { if (m_delegate && [m_delegate respondsToSelector:channelClosedSelector]) { [m_delegate channelClosed:l2capChannel]; } } + (IOReturn)synchronouslyWriteData:(NSData *)data toRFCOMMChannel:(IOBluetoothRFCOMMChannel *)channel { return [channel writeSync:(void *)[data bytes] length:[data length]]; } + (IOReturn)synchronouslyWriteData:(NSData *)data toL2CAPChannel:(IOBluetoothL2CAPChannel *)channel { return [channel writeSync:(void *)[data bytes] length:[data length]]; } @end lightblue-0.3.2/src/mac/LightAquaBlue/._BBBluetoothOBEXClient.h0000644000076500000000000000012210750073373023732 0ustar beawheel00000000000000Mac OS X  2 RTEXTlightblue-0.3.2/src/mac/LightAquaBlue/BBBluetoothOBEXClient.h0000644000076500007650000003075310750073373023177 0ustar beabea00000000000000/* * Copyright (c) 2006 Bea Lam. All rights reserved. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation files * (the "Software"), to deal in the Software without restriction, * including without limitation the rights to use, copy, modify, merge, * publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ // // BBBluetoothOBEXClient.h // LightAquaBlue // // Implements the client side of an OBEX session over a Bluetooth transport. // // There is an example in examples/LightAquaBlue/SimpleOBEXClient that shows // how to use this class to connect to and send files to an OBEX server. // #import #import #import @class OBEXSession; @class BBOBEXHeaderSet; @class BBOBEXRequest; @class IOBluetoothDevice; @class BBOBEXResponse; @class IOBluetoothRFCOMMChannel; @interface BBBluetoothOBEXClient : NSObject { OBEXSession* mSession; id mDelegate; OBEXMaxPacketLength mMaxPacketLength; int mLastServerResponse; uint32_t mConnectionID; BOOL mHasConnectionID; BOOL mAborting; BBOBEXRequest *mCurrentRequest; } /* * Creates a BBBluetoothOBEXClient with the given . The client will * connect to the OBEX server on the given and . */ - (id)initWithRemoteDeviceAddress:(const BluetoothDeviceAddress *)deviceAddress channelID:(BluetoothRFCOMMChannelID)channelID delegate:(id)delegate; /* * Sends a Connect request with the given . * * Returns kOBEXSuccess if the request was sent, or some other OBEXError value * from if there was an error. The delegate is informed * through client:didFinishConnectRequestWithError:responseCode:responseHeaders: * when the request is finished. */ - (OBEXError)sendConnectRequestWithHeaders:(BBOBEXHeaderSet *)headers; /* * Sends a Disconnect request with the given . * * Returns kOBEXSuccess if the request was sent, or some other OBEXError value * from if there was an error. The delegate is informed * through client:didFinishDisconnectRequestWithError:responseCode:responseHeaders: * when the request is finished. * * You must have already sent a Connect request; otherwise, this fails and * return kOBEXSessionNotConnectedError. * * Note the Connection ID is automatically sent in the request headers if one * was provided by the server in a previous Connect response. */ - (OBEXError)sendDisconnectRequestWithHeaders:(BBOBEXHeaderSet *)headers; /* * Sends a Put request with the given that will send the data from * the given . (The stream must be already open, or the request * will fail!) To send a Put-Delete request, set to nil. * * Returns kOBEXSuccess if the request was sent, or some other OBEXError value * from if there was an error. The delegate is informed * through client:didFinishPutRequestForStream:error:responseCode:responseHeaders: * when the request is finished. * * You must have already sent a Connect request; otherwise, this fails and * return kOBEXSessionNotConnectedError. * * Note the Connection ID is automatically sent in the request headers if one * was provided by the server in a previous Connect response. */ - (OBEXError)sendPutRequestWithHeaders:(BBOBEXHeaderSet *)headers readFromStream:(NSInputStream *)inputStream; /* * Sends a Get request with the given that will write received data * to the given . (The stream must be already open, or the request * will fail!) * * Returns kOBEXSuccess if the request was sent, or some other OBEXError value * from if there was an error. The delegate is informed * through client:didFinishGetRequestForStream:error:responseCode:responseHeaders: * when the request is finished. * * You must have already sent a Connect request; otherwise, this fails and * return kOBEXSessionNotConnectedError. * * Note the Connection ID is automatically sent in the request headers if one * was provided by the server in a previous Connect response. */ - (OBEXError)sendGetRequestWithHeaders:(BBOBEXHeaderSet *)headers writeToStream:(NSOutputStream *)outputStream; /* * Sends a SetPath request with the given . Set * to YES if you want to move up one directory * (i.e. "..") before changing to the directory specified in the headers. Set * to YES if you want to create a directory * (instead of receiving an error response) if it does not currently exist. * * Returns kOBEXSuccess if the request was sent, or some other OBEXError value * from if there was an error. The delegate is informed * through client:didFinishSetPathRequestWithError:responseCode:responseHeaders: * when the request is finished. * * You must have already sent a Connect request; otherwise, this fails and * return kOBEXSessionNotConnectedError. * * Note the Connection ID is automatically sent in the request headers if one * was provided by the server in a previous Connect response. */ - (OBEXError)sendSetPathRequestWithHeaders:(BBOBEXHeaderSet *)headers changeToParentDirectoryFirst:(BOOL)changeToParentDirectoryFirst createDirectoriesIfNeeded:(BOOL)createDirectoriesIfNeeded; /* * Aborts the current request if a Put or Get request is currently in progress. * * This schedules an Abort request to be sent when possible (i.e. when the * client next receives a server response). * * Returns kOBEXSuccess if the request was sent, or some other OBEXError value * from if there was an error. The delegate is informed * through client:didFinishAbortRequestWithError:responseCode:forStream: * when the Abort request is finished. */ - (void)abortCurrentRequest; /* * Returns whether the OBEX session is connected (i.e. whether a Connect * request has been sent, without a following Disconnect request). */ - (BOOL)isConnected; /* * Returns the Connection ID for the OBEX session, if one was received in a * response to a previous Connect request. * * This is automatically sent by the client for each request; you do not need * to add it to the request headers yourself. */ - (uint32_t)connectionID; /* * Returns the RFCOMM channel for the client. */ - (IOBluetoothRFCOMMChannel *)RFCOMMChannel; /* * Returns whether this client has a Connection ID. */ - (BOOL)hasConnectionID; /* * Returns the maximum packet length for the OBEX session. */ - (OBEXMaxPacketLength)maximumPacketLength; /* * Sets the client delegate to . */ - (void)setDelegate:(id)delegate; /* * Returns the delegate for this client. */ - (id)delegate; /* * Sets whether debug messages should be displayed (default is NO). */ + (void)setDebug:(BOOL)debug; @end /* * This informal protocol describes the methods that can be implemented for a * BBBluetoothOBEXClient delegate. */ @protocol BBBluetoothOBEXClientDelegate /* * Called when a Connect request is completed. is set to kOBEXSuccess * if the request finished without an error, or some other OBEXError value * from if an error occured. contains the * response code and headers. * * The only indicates whether the request was processed successfully; * use the response code in to see whether the request was actually * accepted by the OBEX server. */ - (void)client:(BBBluetoothOBEXClient *)client didFinishConnectRequestWithError:(OBEXError)error response:(BBOBEXResponse *)response; /* * Called when a Disconnect request is completed. is set to kOBEXSuccess * if the request finished without an error, or some other OBEXError value * from if an error occured. contains the * response code and headers. * * The only indicates whether the request was processed successfully; * use the response code in to see whether the request was actually * accepted by the OBEX server. */ - (void)client:(BBBluetoothOBEXClient *)client didFinishDisconnectRequestWithError:(OBEXError)error response:(BBOBEXResponse *)response; /* * Called when a Put request is completed. is set to kOBEXSuccess * if the request finished without an error, or some other OBEXError value * from if an error occured. The is the * stream originally passed to sendPutRequestWithHeaders:readFromStream:, and * contains the response code and headers. * * The only indicates whether the request was processed successfully; * use the response code in to see whether the request was actually * accepted by the OBEX server. */ - (void)client:(BBBluetoothOBEXClient *)client didFinishPutRequestForStream:(NSInputStream *)inputStream error:(OBEXError)error response:(BBOBEXResponse *)response; /* * Called each time the client sends another chunk of data to the OBEX server * during a Put request. is the number of bytes sent. */ - (void)client:(BBBluetoothOBEXClient *)client didSendDataOfLength:(unsigned)length; /* * Called each time the client receives another chunk of data from the OBEX * server during a Get request. is the number of bytes received, and * is the total number of bytes the client expects to receive * for the request. is zero if the total length is unknown. */ - (void)client:(BBBluetoothOBEXClient *)session didReceiveDataOfLength:(unsigned)length ofTotalLength:(unsigned)totalLength; /* * Called when a Get request is completed. is set to kOBEXSuccess * if the request finished without an error, or some other OBEXError value * from if an error occured. The is the * stream originally passed to sendGetRequestWithHeaders:writeToStream:, and * contains the response code and headers. * * The only indicates whether the request was processed successfully; * use the response code in to see whether the request was actually * accepted by the OBEX server. */ - (void)client:(BBBluetoothOBEXClient *)client didFinishGetRequestForStream:(NSOutputStream *)outputStream error:(OBEXError)error response:(BBOBEXResponse *)response; /* * Called when a SetPath request is completed. is set to kOBEXSuccess * if the request finished without an error, or some other OBEXError value * from if an error occured. contains the * response code and headers. * * The only indicates whether the request was processed successfully; * use the response code in to see whether the request was actually * accepted by the OBEX server. */ - (void)client:(BBBluetoothOBEXClient *)client didFinishSetPathRequestWithError:(OBEXError)error response:(BBOBEXResponse *)response; /* * Called when an Abort request is completed following a call to * abortCurrentRequest:. is set to kOBEXSuccess * if the request finished without an error, or some other OBEXError value * from if an error occured. The is the * stream originally passed to sendPutRequestWithHeaders:readFromStream: or * sendGetRequestWithHeaders:writeToStream:, and contains the * response code and headers. * * The only indicates whether the request was processed successfully; * use the response code in to see whether the request was actually * accepted by the OBEX server. */ - (void)client:(BBBluetoothOBEXClient *)session didAbortRequestWithStream:(NSStream *)stream error:(OBEXError)error response:(BBOBEXResponse *)response; @end lightblue-0.3.2/src/mac/LightAquaBlue/._BBBluetoothOBEXClient.m0000644000076500000000000000012210750747454023747 0ustar beawheel00000000000000Mac OS X  2 RTEXTlightblue-0.3.2/src/mac/LightAquaBlue/BBBluetoothOBEXClient.m0000644000076500007650000003077410750747454023217 0ustar beabea00000000000000/* * Copyright (c) 2006 Bea Lam. All rights reserved. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation files * (the "Software"), to deal in the Software without restriction, * including without limitation the rights to use, copy, modify, merge, * publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ // // BBBluetoothOBEXClient.m // LightAquaBlue // #import "BBBluetoothOBEXClient.h" #import "BBMutableOBEXHeaderSet.h" #import "BBOBEXRequest.h" #import #import #import #import #define DEBUG_NAME @"[BBBluetoothOBEXClient] " static BOOL _debug = NO; @implementation BBBluetoothOBEXClient - (id)initWithRemoteDeviceAddress:(const BluetoothDeviceAddress *)deviceAddress channelID:(BluetoothRFCOMMChannelID)channelID delegate:(id)delegate; { self = [super init]; mSession = [[IOBluetoothOBEXSession alloc] initWithDevice:[IOBluetoothDevice withAddress:deviceAddress] channelID:channelID]; mDelegate = delegate; mMaxPacketLength = 0x2000; mLastServerResponse = kOBEXResponseCodeSuccessWithFinalBit; mConnectionID = 0; mHasConnectionID = NO; mAborting = NO; mCurrentRequest = nil; return self; } - (BBOBEXHeaderSet *)addConnectionIDToHeaders:(BBOBEXHeaderSet *)headers { if (!mHasConnectionID) return headers; BBMutableOBEXHeaderSet *modifiedHeaders = [BBMutableOBEXHeaderSet headerSet]; [modifiedHeaders addHeadersFromHeaderSet:headers]; [modifiedHeaders setValueForConnectionIDHeader:mConnectionID]; return modifiedHeaders; } - (OBEXError)sendConnectRequestWithHeaders:(BBOBEXHeaderSet *)headers { if (mCurrentRequest && ![mCurrentRequest isFinished]) return kOBEXSessionBusyError; BBOBEXConnectRequest *request = [[BBOBEXConnectRequest alloc] initWithClient:self eventSelector:@selector(handleSessionEvent:) session:mSession]; OBEXError status = [request beginWithHeaders:headers]; if (status == kOBEXSuccess) { [mCurrentRequest release]; mCurrentRequest = request; } else { [request release]; } return status; } - (OBEXError)sendDisconnectRequestWithHeaders:(BBOBEXHeaderSet *)headers { if (mCurrentRequest && ![mCurrentRequest isFinished]) return kOBEXSessionBusyError; BBOBEXDisconnectRequest *request = [[BBOBEXDisconnectRequest alloc] initWithClient:self eventSelector:@selector(handleSessionEvent:) session:mSession]; BBOBEXHeaderSet *realHeaders = (mHasConnectionID ? [self addConnectionIDToHeaders:headers] : headers); OBEXError status = [request beginWithHeaders:realHeaders]; if (status == kOBEXSuccess) { [mCurrentRequest release]; mCurrentRequest = request; } else { [request release]; } return status; } - (OBEXError)sendPutRequestWithHeaders:(BBOBEXHeaderSet *)headers readFromStream:(NSInputStream *)inputStream { if (mCurrentRequest && ![mCurrentRequest isFinished]) return kOBEXSessionBusyError; BBOBEXPutRequest *request = [[BBOBEXPutRequest alloc] initWithClient:self eventSelector:@selector(handleSessionEvent:) session:mSession inputStream:inputStream]; BBOBEXHeaderSet *realHeaders = (mHasConnectionID ? [self addConnectionIDToHeaders:headers] : headers); OBEXError status = [request beginWithHeaders:realHeaders]; if (status == kOBEXSuccess) { [mCurrentRequest release]; mCurrentRequest = request; } else { [request release]; } return status; } - (OBEXError)sendGetRequestWithHeaders:(BBOBEXHeaderSet *)headers writeToStream:(NSOutputStream *)outputStream { if (mCurrentRequest && ![mCurrentRequest isFinished]) return kOBEXSessionBusyError; BBOBEXGetRequest *request = [[BBOBEXGetRequest alloc] initWithClient:self eventSelector:@selector(handleSessionEvent:) session:mSession outputStream:outputStream]; BBOBEXHeaderSet *realHeaders = (mHasConnectionID ? [self addConnectionIDToHeaders:headers] : headers); OBEXError status = [request beginWithHeaders:realHeaders]; if (status == kOBEXSuccess) { [mCurrentRequest release]; mCurrentRequest = request; } else { [request release]; } return status; } - (OBEXError)sendSetPathRequestWithHeaders:(BBOBEXHeaderSet *)headers changeToParentDirectoryFirst:(BOOL)changeToParentDirectoryFirst createDirectoriesIfNeeded:(BOOL)createDirectoriesIfNeeded { if (mCurrentRequest && ![mCurrentRequest isFinished]) return kOBEXSessionBusyError; BBOBEXSetPathRequest *request = [[BBOBEXSetPathRequest alloc] initWithClient:self eventSelector:@selector(handleSessionEvent:) session:mSession changeToParentDirectoryFirst:changeToParentDirectoryFirst createDirectoriesIfNeeded:createDirectoriesIfNeeded]; BBOBEXHeaderSet *realHeaders = (mHasConnectionID ? [self addConnectionIDToHeaders:headers] : headers); OBEXError status = [request beginWithHeaders:realHeaders]; if (status == kOBEXSuccess) { [mCurrentRequest release]; mCurrentRequest = request; } else { [request release]; } return status; } - (void)abortCurrentRequest { if (_debug) NSLog(DEBUG_NAME @"[abortCurrentRequest]"); if (!mCurrentRequest || [mCurrentRequest isFinished] || mAborting) return; if (_debug) NSLog(DEBUG_NAME @"Aborting later..."); // Just set an abort flag -- can't send OBEXAbort right away because we // might be in the middle of a transaction, and we must wait our turn to // send the abort (i.e. wait until after we've received a server response) mAborting = YES; } #pragma mark - - (void)finishedCurrentRequestWithError:(OBEXError)error responseCode:(int)responseCode { if (_debug) NSLog(DEBUG_NAME @"[finishedCurrentRequestWithError] %d 0x%02x", error, responseCode); mAborting = NO; [mCurrentRequest finishedWithError:error responseCode:responseCode]; [mCurrentRequest release]; mCurrentRequest = nil; } - (void)performAbort { if (_debug) NSLog(DEBUG_NAME @"[performAbort]"); [mCurrentRequest release]; mCurrentRequest = [[BBOBEXAbortRequest alloc] initWithClient:self eventSelector:@selector(handleSessionEvent:) session:mSession]; OBEXError status = [mCurrentRequest beginWithHeaders:nil]; if (status != kOBEXSuccess) [self finishedCurrentRequestWithError:status responseCode:0]; } - (void)processResponseWithHeaders:(BBMutableOBEXHeaderSet *)responseHeaders responseCode:(int)responseCode { if (_debug) NSLog(DEBUG_NAME @"processResponseWithHeaders 0x%x", responseCode); if (responseCode == kOBEXResponseCodeContinueWithFinalBit) { if (mAborting) { [self performAbort]; return; } [mCurrentRequest receivedResponseWithHeaders:responseHeaders]; OBEXError status = [mCurrentRequest sendNextRequestPacket]; if (status != kOBEXSuccess) { [self finishedCurrentRequestWithError:status responseCode:responseCode]; return; } } else { [mCurrentRequest receivedResponseWithHeaders:responseHeaders]; mLastServerResponse = responseCode; [self finishedCurrentRequestWithError:kOBEXSuccess responseCode:responseCode]; } } - (void)handleSessionEvent:(const OBEXSessionEvent *)event { if (_debug) NSLog(DEBUG_NAME @"[handleSessionEvent] event %d (current request=%@)", event->type, mCurrentRequest); if (mCurrentRequest) { if (event->type == kOBEXSessionEventTypeError) { if (_debug) NSLog(DEBUG_NAME @"[handleSessionEvent] error occurred %d", event->u.errorData.error); [self finishedCurrentRequestWithError:event->u.errorData.error responseCode:0]; return; } int responseCode; BBMutableOBEXHeaderSet *responseHeaders = nil; if ([mCurrentRequest readOBEXResponseHeaders:&responseHeaders andResponseCode:&responseCode fromSessionEvent:event]) { if (!responseHeaders) responseHeaders = [BBMutableOBEXHeaderSet headerSet]; if (event->type == kOBEXSessionEventTypeConnectCommandResponseReceived) { // note any received connection id so it can be sent with later // requests if ([responseHeaders containsValueForHeader:kOBEXHeaderIDConnectionID]) { mConnectionID = [responseHeaders valueForConnectionIDHeader]; mHasConnectionID = YES; } } else if (event->type == kOBEXSessionEventTypeDisconnectCommandResponseReceived) { // disconnected, can clear connection id now mConnectionID = 0; mHasConnectionID = NO; } [self processResponseWithHeaders:responseHeaders responseCode:responseCode]; } else { // unable to read response / response didn't match current request if (_debug) NSLog(DEBUG_NAME @"[handleSessionEvent] can't parse event!"); [self finishedCurrentRequestWithError:kOBEXSessionBadResponseError responseCode:0]; } } else { if (_debug) NSLog(DEBUG_NAME @"ignoring event received while idle: %d", event->type); } } - (int)serverResponseForLastRequest { return mLastServerResponse; } // for internal testing - (void)setOBEXSession:(OBEXSession *)session { [session retain]; [mSession release]; mSession = session; } - (BOOL)isConnected { if (mSession) return [mSession hasOpenOBEXConnection]; return NO; } - (IOBluetoothRFCOMMChannel *)RFCOMMChannel { if (mSession && [mSession isKindOfClass:[IOBluetoothOBEXSession class]]) { IOBluetoothOBEXSession *session = (IOBluetoothOBEXSession *)mSession; return [session getRFCOMMChannel]; } return nil; } - (uint32_t)connectionID { return mConnectionID; } - (BOOL)hasConnectionID { return mHasConnectionID; } - (OBEXMaxPacketLength)maximumPacketLength { return mMaxPacketLength; } - (void)setDelegate:(id)delegate { mDelegate = delegate; } - (id)delegate { return mDelegate; } + (void)setDebug:(BOOL)debug { _debug = debug; [BBOBEXRequest setDebug:debug]; } - (void)dealloc { [mSession setEventSelector:NULL target:nil refCon:NULL]; [mCurrentRequest release]; mCurrentRequest = nil; // if client is deleted during a delegate callback [mSession closeTransportConnection]; [mSession release]; mSession = nil; [super dealloc]; } @end lightblue-0.3.2/src/mac/LightAquaBlue/._BBBluetoothOBEXServer.h0000644000076500000000000000012210750072721023756 0ustar beawheel00000000000000Mac OS X  2 RTEXTlightblue-0.3.2/src/mac/LightAquaBlue/BBBluetoothOBEXServer.h0000644000076500007650000002423410750072721023220 0ustar beabea00000000000000/* * Copyright (c) 2006 Bea Lam. All rights reserved. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation files * (the "Software"), to deal in the Software without restriction, * including without limitation the rights to use, copy, modify, merge, * publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ // // BBBluetoothOBEXServer.h // LightAquaBlue // // Implements the server side of an OBEX session over a Bluetooth transport. // // Generally you will use BBBluetoothOBEXServer like this: // 1. Call registerForChannelOpenNotifications:selector:withChannelID:direction: // in the IOBluetoothRFCOMMChannel class in order be notified whenever // you receive a client connection on a particular RFCOMM channel ID. // 2. When you are notified that a client has connected, use the provided // IOBluetoothRFCOMMChannel to create a a BBBluetoothOBEXServer // object, and then call run: on the object to start the server. // // There is an example in examples/LightAquaBlue/SimpleOBEXServer that shows // how to use this class to run a basic OBEX server session. // #import #import @class OBEXSession; @class BBOBEXHeaderSet; @class BBMutableOBEXHeaderSet; @class BBOBEXRequestHandler; @class IOBluetoothRFCOMMChannel; @class IOBluetoothUserNotification; @interface BBBluetoothOBEXServer : NSObject { IOBluetoothRFCOMMChannel *mChannel; OBEXSession *mSession; id mDelegate; BBOBEXRequestHandler *mCurrentRequestHandler; IOBluetoothUserNotification *mChannelNotif; } /* * Creates and returns a BBBluetoothOBEXServer. */ + (id)serverWithIncomingRFCOMMChannel:(IOBluetoothRFCOMMChannel *)channel delegate:(id)delegate; /* * Initialises a BBBluetoothOBEXServer that will run on the given . * The will be notified when events occur on the server. */ - (id)initWithIncomingRFCOMMChannel:(IOBluetoothRFCOMMChannel *)channel delegate:(id)delegate; /* * Starts the server. The server will now receive and process client requests. */ - (void)run; /* * Closes the server. It cannot be started again after this. */ - (void)close; /* * Sets to be the next response code to be sent for the current * request. * * Available response codes are listed in the OBEXOpCodeResponseValues enum in * . (Use the codes that end with "WithFinalBit".) For * example, you could set the response code to * kOBEXResponseCodeNotFoundWithFinalBit if a client requests a non-existent * file. */ - (void)setResponseCodeForCurrentRequest:(int)responseCode; /* * Adds to the next response headers to be sent for the * current request. * * For example, OBEX servers commonly include a "Length" header when * responding to a Get request to indicate the size of the file to be * transferred. */ - (void)addResponseHeadersForCurrentRequest:(BBOBEXHeaderSet *)responseHeaders; /* * Sets the server's delegate to . */ - (void)setDelegate:(id)delegate; /* * Returns the delegate for this server. */ - (id)delegate; /* * Sets whether debug messages should be displayed (default is NO). */ + (void)setDebug:(BOOL)debug; @end /* * This informal protocol describes the methods that can be implemented for a * BBBluetoothOBEXServer delegate. * * For each type of client request, there is a "...shouldHandle..." method * (e.g. server:shouldHandleConnectRequest:) that is called when the request * is received. If the delegate does not implement this method for a * particular type of request, the server will automatically refuse all * requests of this type with a "Not Implemented" response. */ @protocol BBBluetoothOBEXServerDelegate /* * Called when an error occurs on the server. is an error code from * and is a description of the error. */ - (void)server:(BBBluetoothOBEXServer *)server errorOccurred:(OBEXError)error description:(NSString *)description; /* * Called when a Connect request is received with the specified . * * This should return YES if the request should be allowed to continue, or NO * if the request should be refused. (By default, a request will be refused * with a 'Forbidden' response; call setResponseCodeForCurrentRequest: to set * a more specific response.) */ - (BOOL)server:(BBBluetoothOBEXServer *)server shouldHandleConnectRequest:(BBOBEXHeaderSet *)requestHeaders; /* * Called when the server finishes processing of a Connect request. */ - (void)serverDidHandleConnectRequest:(BBBluetoothOBEXServer *)server; /* * Called when a Disconnect request is received with the specified . * * This should return YES if the request should be allowed to continue, or NO * if the request should be refused. (By default, a request will be refused * with a 'Forbidden' response; call setResponseCodeForCurrentRequest: to set * a more specific response.) */ - (BOOL)server:(BBBluetoothOBEXServer *)server shouldHandleDisconnectRequest:(BBOBEXHeaderSet *)requestHeaders; /* * Called when the server finishes processing of a Disconnect request. */ - (void)serverDidHandleDisconnectRequest:(BBBluetoothOBEXServer *)server; /* * Called when a Put request is received with the specified . * * This should return an opened NSOutputStream if the request should be allowed * to continue, or nil if the request should be refused. (By default, a request * will be refused with a 'Forbidden' response; call * setResponseCodeForCurrentRequest: to set a more specific response.) * * Note the returned stream *must* be open, or the request will fail. */ - (NSOutputStream *)server:(BBBluetoothOBEXServer *)server shouldHandlePutRequest:(BBOBEXHeaderSet *)requestHeaders; /* * Called each time a chunk of data is received during a Put request. * The indicates the number of bytes received, and is * set to YES if all the data has now been received and the server is about to * send the final response for the request. */ - (void)server:(BBBluetoothOBEXServer *)server didReceiveDataOfLength:(unsigned)length isLastPacket:(BOOL)isLastPacket; /* * Called when the server finishes processing of a Put request. The * is the stream originally provided from * server:shouldHandlePutRequest: and is set to YES if the client * sent an Abort request to cancel the Put request before it was completed. */ - (void)server:(BBBluetoothOBEXServer *)server didHandlePutRequestForStream:(NSOutputStream *)outputStream requestWasAborted:(BOOL)aborted; /* * Called when a Put-Delete request is received with the specified . * * This should return YES if the request should be allowed to continue, or NO * if the request should be refused. (By default, a request will be refused * with a 'Forbidden' response; call setResponseCodeForCurrentRequest: to set * a more specific response.) */ - (BOOL)server:(BBBluetoothOBEXServer *)server shouldHandlePutDeleteRequest:(BBOBEXHeaderSet *)requestHeaders; /* * Called when the server finishes processing of a Put-Delete request. */ - (void)serverDidHandlePutDeleteRequest:(BBBluetoothOBEXServer *)server; /* * Called when a Get request is received with the specified . * * This should return an opened NSInputStream if the request should be allowed * to continue, or nil if the request should be refused. (By default, a request * will be refused with a 'Forbidden' response; call * setResponseCodeForCurrentRequest: to set a more specific response.) * * Note the returned stream *must* be open, or the request will fail. */ - (NSInputStream *)server:(BBBluetoothOBEXServer *)server shouldHandleGetRequest:(BBOBEXHeaderSet *)requestHeaders; /* * Called each time a chunk of data is sent during a Get request. * The indicates the number of bytes sent. */ - (void)server:(BBBluetoothOBEXServer *)server didSendDataOfLength:(unsigned)length; /* * Called when the server finishes processing of a Get request. The * is the stream originally provided from * server:shouldHandleGetRequest: and is set to YES if the client * sent an Abort request to cancel the Get request before it was completed. */ - (void)server:(BBBluetoothOBEXServer *)server didHandleGetRequestForStream:(NSInputStream *)inputStream requestWasAborted:(BOOL)aborted; /* * Called when a SetPath request is received with the specified * and SetPath . The first two bits of the flags are significant * for the SetPath operation: * - Bit 0 is set if the server should back up one level (i.e. "..") * before applying the requested path name * - Bit 1 is set if the server should respond with an error instead of * creating a directory if the specified directory does not exist * * This should return YES if the request should be allowed to continue, or NO * if the request should be refused. (By default, a request will be refused * with a 'Forbidden' response; call setResponseCodeForCurrentRequest: to set * a more specific response.) */ - (BOOL)server:(BBBluetoothOBEXServer *)server shouldHandleSetPathRequest:(BBOBEXHeaderSet *)requestHeaders withFlags:(OBEXFlags)flags; /* * Called when the server finishes processing of a SetPath request. */ - (void)serverDidHandleSetPathRequest:(BBBluetoothOBEXServer *)server; @end lightblue-0.3.2/src/mac/LightAquaBlue/._BBBluetoothOBEXServer.m0000644000076500000000000000012210750744407023772 0ustar beawheel00000000000000Mac OS X  2 RTEXTlightblue-0.3.2/src/mac/LightAquaBlue/BBBluetoothOBEXServer.m0000644000076500007650000002174610750744407023241 0ustar beabea00000000000000/* * Copyright (c) 2006 Bea Lam. All rights reserved. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation files * (the "Software"), to deal in the Software without restriction, * including without limitation the rights to use, copy, modify, merge, * publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ // // BBBluetoothOBEXServer.m // LightAquaBlue // #import "BBBluetoothOBEXServer.h" #import "BBOBEXRequestHandler.h" #import "BBOBEXHeaderSet.h" #import #import #define DEBUG_NAME @"[BBBluetoothOBEXServer] " static BOOL _debug = NO; @implementation BBBluetoothOBEXServer - (void)errorOccurred:(OBEXError)error description:(NSString *)description { if (_debug) NSLog(DEBUG_NAME @"errorOccurred: %d description: %@", error, description); if ([mDelegate respondsToSelector:@selector(server:errorOccurred:description:)]) { [mDelegate server:self errorOccurred:error description:description]; } } + (id)serverWithIncomingRFCOMMChannel:(IOBluetoothRFCOMMChannel *)channel delegate:(id)delegate { return [[[BBBluetoothOBEXServer alloc] initWithIncomingRFCOMMChannel:channel delegate:delegate] autorelease]; } - (id)initWithIncomingRFCOMMChannel:(IOBluetoothRFCOMMChannel *)channel delegate:(id)delegate { self = [super init]; mChannel = [channel retain]; mDelegate = delegate; return self; } - (BBOBEXRequestHandler *)handlerForEventType:(OBEXSessionEventType)type { BBOBEXRequestHandler *handler = nil; SEL selector = @selector(handleSessionEvent:); switch (type) { case kOBEXSessionEventTypeConnectCommandReceived: handler = [[BBOBEXConnectRequestHandler alloc] initWithServer:self eventSelector:selector session:mSession]; break; case kOBEXSessionEventTypeDisconnectCommandReceived: handler = [[BBOBEXDisconnectRequestHandler alloc] initWithServer:self eventSelector:selector session:mSession]; break; case kOBEXSessionEventTypePutCommandReceived: handler = [[BBOBEXPutRequestHandler alloc] initWithServer:self eventSelector:selector session:mSession]; break; case kOBEXSessionEventTypeGetCommandReceived: handler = [[BBOBEXGetRequestHandler alloc] initWithServer:self eventSelector:selector session:mSession]; break; case kOBEXSessionEventTypeSetPathCommandReceived: handler = [[BBOBEXSetPathRequestHandler alloc] initWithServer:self eventSelector:selector session:mSession]; break; } [handler autorelease]; return handler; } - (void)channelClosed:(IOBluetoothUserNotification *)notification channel:(IOBluetoothRFCOMMChannel *)channel { if (_debug) NSLog(DEBUG_NAME @"RFCOMM channel closed!"); if (mSession) { [self errorOccurred:kOBEXSessionTransportDiedError description:@"Bluetooth transport connection died"]; } if (channel == mChannel) [self close]; } - (void)run { if (mChannel) { IOBluetoothOBEXSession *session = [IOBluetoothOBEXSession withIncomingRFCOMMChannel:mChannel eventSelector:@selector(handleSessionEvent:) selectorTarget:self refCon:NULL]; mSession = [session retain]; mChannelNotif = [mChannel registerForChannelCloseNotification:self selector:@selector(channelClosed:channel:)]; } else if (mSession) { // for internal testing //NSLog(@"send dummy event"); // dummy event - event selector doesn't seem to get set otherwise if // I just call setEventSelector:target:refCon: [mSession OBEXConnectResponse:kOBEXResponseCodeSuccessWithFinalBit flags:0 maxPacketLength:1024 optionalHeaders:NULL optionalHeadersLength:0 eventSelector:@selector(handleSessionEvent:) selectorTarget:self refCon:NULL]; } } - (void)close { if (_debug) NSLog(DEBUG_NAME @"close"); [mChannelNotif unregister]; mChannelNotif = nil; // must set the event selector and target to NULL, otherwise the // OBEXSession might continue to try to send us events (e.g. if there's // a link error) if (mSession) [mSession setEventSelector:NULL target:nil refCon:NULL]; [mCurrentRequestHandler release]; mCurrentRequestHandler = nil; [mChannel release]; mChannel = nil; [mSession release]; mSession = nil; } - (void)setResponseCodeForCurrentRequest:(int)responseCode { if (mCurrentRequestHandler) [mCurrentRequestHandler setNextResponseCode:responseCode]; } - (void)addResponseHeadersForCurrentRequest:(BBOBEXHeaderSet *)responseHeaders { if (mCurrentRequestHandler) [mCurrentRequestHandler addResponseHeaders:responseHeaders]; } - (void)handleSessionEvent:(const OBEXSessionEvent *)event { if (_debug) NSLog(DEBUG_NAME @"handleSessionEvent %d", event->type); if (event->type == kOBEXSessionEventTypeError) { if (mCurrentRequestHandler) { [self errorOccurred:event->u.errorData.error description:@"Error occurred during client request"]; } else { [self errorOccurred:event->u.errorData.error description:@"Error occurred while server was idle"]; } } else if (event->type == kOBEXSessionEventTypeAbortCommandReceived) { if (mCurrentRequestHandler) { if (_debug) NSLog(DEBUG_NAME @"Aborting current request..."); [mCurrentRequestHandler handleRequestAborted]; } else { if (_debug) NSLog(DEBUG_NAME @"Got Abort request, but no request to abort"); // not really an error, so errorOccurred: not called } } else { if (_debug) NSLog(DEBUG_NAME @"Received client request"); if (!mCurrentRequestHandler) { mCurrentRequestHandler = [self handlerForEventType:event->type]; if (!mCurrentRequestHandler) { [self errorOccurred:kOBEXGeneralError description:[NSString stringWithFormat:@"Server received unknown event: %d", event->type]]; return; } [mCurrentRequestHandler retain]; } if (_debug) NSLog(DEBUG_NAME @"Found handler, %@", mCurrentRequestHandler); BOOL requestFinished = [mCurrentRequestHandler handleRequestEvent:event]; if (requestFinished) { if (_debug) NSLog(DEBUG_NAME @"Finished request"); [mCurrentRequestHandler release]; mCurrentRequestHandler = nil; } } } - (void)setOBEXSession:(OBEXSession *)session { [session retain]; [mSession release]; mSession = session; } - (void)setDelegate:(id)delegate { mDelegate = delegate; } - (id)delegate { return mDelegate; } + (void)setDebug:(BOOL)debug { _debug = debug; [BBOBEXRequestHandler setDebug:debug]; } - (void)dealloc { [self close]; [super dealloc]; } @end lightblue-0.3.2/src/mac/LightAquaBlue/BBLocalDevice.h0000644000076500007650000000347410747223642021571 0ustar beabea00000000000000/* * Copyright (c) 2006 Bea Lam. All rights reserved. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation files * (the "Software"), to deal in the Software without restriction, * including without limitation the rights to use, copy, modify, merge, * publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ // // BBLocalDevice.h // LightAquaBlue // // Provides information about the local Bluetooth device. // #import #import @interface BBLocalDevice : NSObject { // } /* * Returns the local device name, or nil if it can't be read. */ + (NSString *)getName; /* * Returns the local device address as a string, or nil if it can't be read. * The address is separated by hyphens, e.g. "00-11-22-33-44-55". */ + (NSString *)getAddressString; /* * Returns the local device's class of device, or -1 if it can't be read. */ + (BluetoothClassOfDevice)getClassOfDevice; /* * Returns YES if the local device is available and switched on. */ + (BOOL)isPoweredOn; @end lightblue-0.3.2/src/mac/LightAquaBlue/BBLocalDevice.m0000644000076500007650000000476310747223642021600 0ustar beabea00000000000000/* * Copyright (c) 2006 Bea Lam. All rights reserved. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation files * (the "Software"), to deal in the Software without restriction, * including without limitation the rights to use, copy, modify, merge, * publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ // // BBLocalDevice.m // LightAquaBlue // #import #import #import #import "BBLocalDevice.h" @implementation BBLocalDevice + (NSString *)getName { BluetoothDeviceName name; IOReturn result; result = IOBluetoothLocalDeviceReadName(name, NULL, NULL, NULL); if (result == kIOReturnSuccess) { NSString *s = [NSString stringWithCString:(char *)name]; if (s != nil) return s; } return nil; } + (NSString *)getAddressString { BluetoothDeviceAddress address; IOReturn result; result = IOBluetoothLocalDeviceReadAddress(&address, NULL, NULL, NULL); if (result == kIOReturnSuccess) { return IOBluetoothNSStringFromDeviceAddress(&address); } return nil; } + (BluetoothClassOfDevice)getClassOfDevice { BluetoothClassOfDevice classOfDevice; IOReturn result; result = IOBluetoothLocalDeviceReadClassOfDevice(&classOfDevice, NULL, NULL, NULL); if (result == kIOReturnSuccess) { return classOfDevice; } return -1; } + (BOOL)isPoweredOn { if (!IOBluetoothLocalDeviceAvailable()) return NO; BluetoothHCIPowerState powerState; IOReturn status = IOBluetoothLocalDeviceGetPowerState(&powerState); if (status != kIOReturnSuccess || powerState != kBluetoothHCIPowerStateON) return NO; return YES; } @end lightblue-0.3.2/src/mac/LightAquaBlue/._BBMutableOBEXHeaderSet.h0000644000076500000000000000012210750035142023774 0ustar beawheel00000000000000Mac OS X  2 RTEXTlightblue-0.3.2/src/mac/LightAquaBlue/BBMutableOBEXHeaderSet.h0000644000076500007650000001244510750035142023237 0ustar beabea00000000000000/* * Copyright (c) 2006 Bea Lam. All rights reserved. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation files * (the "Software"), to deal in the Software without restriction, * including without limitation the rights to use, copy, modify, merge, * publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ // // BBMutableOBEXHeaderSet.h // LightAquaBlue // // A mutable version of BBOBEXHeaderSet that allows adding and removing // of headers. // #import "BBOBEXHeaderSet.h" @interface BBMutableOBEXHeaderSet : BBOBEXHeaderSet { CFMutableDictionaryRef mMutableDict; } /* * Creates and returns an empty header set. */ + (id)headerSet; /* * Removes the header from this header set. */ - (void)removeValueForHeader:(uint8_t)headerID; /* * Sets the value for the Name header to , overriding any existing * value for this header. */ - (void)setValueForNameHeader:(NSString *)name; /* * Sets the value for the Type header to , overriding any existing * value for this header. */ - (void)setValueForTypeHeader:(NSString *)type; /* * Sets the value for the Length header to , overriding any existing * value for this header. */ - (void)setValueForLengthHeader:(uint32_t)length; /* * Sets the value for the Time (0x44) header to , overriding any * existing value for this header. Assumes is in local time. */ - (void)setValueForTimeHeader:(NSDate *)date; /* * Sets the value for the Time (0x44) header to , overriding any * existing value for this header. If is YES, the time value is * marked as UTC time, otherwise it is marked as local time. */ - (void)setValueForTimeHeader:(NSDate *)date isUTCTime:(BOOL)isUTCTime; /* * Sets the value for the Description header to , overriding * any existing value for this header. */ - (void)setValueForDescriptionHeader:(NSString *)description; /* * Sets the value for the Target header to , overriding * any existing value for this header. */ - (void)setValueForTargetHeader:(NSData *)target; /* * Sets the value for the HTTP header to , overriding * any existing value for this header. */ - (void)setValueForHTTPHeader:(NSData *)http; /* * Sets the value for the Who header to , overriding * any existing value for this header. */ - (void)setValueForWhoHeader:(NSData *)who; /* * Sets the value for the Connection Id header to , overriding * any existing value for this header. */ - (void)setValueForConnectionIDHeader:(uint32_t)connectionID; /* * Sets the value for the Application Parameters header to , * overriding any existing value for this header. */ - (void)setValueForApplicationParametersHeader:(NSData *)appParameters; /* * Sets the value for the Authorization Challenge header to , * overriding any existing value for this header. */ - (void)setValueForAuthorizationChallengeHeader:(NSData *)authChallenge; /* * Sets the value for the Authorization Response header to , * overriding any existing value for this header. */ - (void)setValueForAuthorizationResponseHeader:(NSData *)authResponse; /* * Sets the value for the Object Class header to , * overriding any existing value for this header. */ - (void)setValueForObjectClassHeader:(NSData *)objectClass; /* * Sets the value for the unicode-encoded header to , * overriding any existing value for this header. */ - (void)setValue:(NSString *)value forUnicodeHeader:(uint8_t)headerID; /* * Sets the value for the byte-sequence-encoded header to , * overriding any existing value for this header. */ - (void)setValue:(NSData *)value forByteSequenceHeader:(uint8_t)headerID; /* * Sets the value for the 4-byte-header to , * overriding any existing value for this header. */ - (void)setValue:(uint32_t)value for4ByteHeader:(uint8_t)headerID; /* * Sets the value for the 1-byte-encoded header to , * overriding any existing value for this header. */ - (void)setValue:(uint8_t)value for1ByteHeader:(uint8_t)headerID; /* * Adds the headers from to this header set. */ - (void)addHeadersFromHeaderSet:(BBOBEXHeaderSet *)headerSet; /* * Adds the headers from to this header set. specifies * the size of . * * Returns NO if there was an error parsing the . */ - (BOOL)addHeadersFromHeadersData:(const uint8_t *)headersData length:(size_t)length; @end lightblue-0.3.2/src/mac/LightAquaBlue/BBMutableOBEXHeaderSet.m0000644000076500007650000002424510747225767023271 0ustar beabea00000000000000/* * Copyright (c) 2006 Bea Lam. All rights reserved. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation files * (the "Software"), to deal in the Software without restriction, * including without limitation the rights to use, copy, modify, merge, * publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ // // BBMutableOBEXHeaderSet.m // LightAquaBlue // #import "BBMutableOBEXHeaderSet.h" enum kOBEXHeaderEncoding { kHeaderEncodingMask = 0xc0, kHeaderEncodingUnicode = 0x00, kHeaderEncodingByteSequence = 0x40, kHeaderEncoding1Byte = 0x80, kHeaderEncoding4Byte = 0xc0 }; static NSString *LOCAL_TIME_FORMAT_STRING = @"%Y%m%dT%H%M%S"; static NSString *UTC_FORMAT_STRING = @"%Y%m%dT%H%M%SZ"; @implementation BBMutableOBEXHeaderSet + (id)headerSet { return [[[BBMutableOBEXHeaderSet alloc] init] autorelease]; } - (void)setValueForCountHeader:(uint32_t)count { [self setValue:count for4ByteHeader:kOBEXHeaderIDCount]; } - (void)setValueForNameHeader:(NSString *)name { [self setValue:name forUnicodeHeader:kOBEXHeaderIDName]; } - (void)setValueForTypeHeader:(NSString *)type { NSMutableData *data; if ([type length] == 0) { data = [NSMutableData data]; } else { const char *s = [type cStringUsingEncoding:NSASCIIStringEncoding]; if (!s) // cannot be converted to ascii return; // add null terminator data = [NSMutableData dataWithBytes:s length:[type length] + 1]; [data resetBytesInRange:NSMakeRange([type length], 1)]; } [self setValue:data forByteSequenceHeader:kOBEXHeaderIDType]; } - (void)setValueForLengthHeader:(uint32_t)length { [self setValue:length for4ByteHeader:kOBEXHeaderIDLength]; } - (void)setValueForTimeHeader:(NSDate *)date { [self setValueForTimeHeader:date isUTCTime:NO]; } - (void)setValueForTimeHeader:(NSDate *)date isUTCTime:(BOOL)isUTCTime { NSString *dateString = [date descriptionWithCalendarFormat: (isUTCTime ? UTC_FORMAT_STRING : LOCAL_TIME_FORMAT_STRING) timeZone: (isUTCTime ? [NSTimeZone timeZoneWithName:@"UTC"] : [NSTimeZone localTimeZone]) locale: nil]; [self setValue:[dateString dataUsingEncoding:NSASCIIStringEncoding] forByteSequenceHeader:kOBEXHeaderIDTimeISO]; } - (void)setValueForDescriptionHeader:(NSString *)description { [self setValue:description forUnicodeHeader:kOBEXHeaderIDDescription]; } - (void)setValueForTargetHeader:(NSData *)target { [self setValue:target forByteSequenceHeader:kOBEXHeaderIDTarget]; } - (void)setValueForHTTPHeader:(NSData *)http { [self setValue:http forByteSequenceHeader:kOBEXHeaderIDHTTP]; } - (void)setValueForWhoHeader:(NSData *)who { [self setValue:who forByteSequenceHeader:kOBEXHeaderIDWho]; } - (void)setValueForConnectionIDHeader:(uint32_t)connectionID { [self setValue:connectionID for4ByteHeader:kOBEXHeaderIDConnectionID]; } - (void)setValueForApplicationParametersHeader:(NSData *)appParameters { [self setValue:appParameters forByteSequenceHeader:kOBEXHeaderIDAppParameters]; } - (void)setValueForAuthorizationChallengeHeader:(NSData *)authChallenge { [self setValue:authChallenge forByteSequenceHeader:kOBEXHeaderIDAuthorizationChallenge]; } - (void)setValueForAuthorizationResponseHeader:(NSData *)authResponse { [self setValue:authResponse forByteSequenceHeader:kOBEXHeaderIDAuthorizationResponse]; } - (void)setValueForObjectClassHeader:(NSData *)objectClass { [self setValue:objectClass forByteSequenceHeader:0x51]; } - (void)setValue:(NSString *)value forUnicodeHeader:(uint8_t)headerID { if (!value || ((headerID & kHeaderEncodingMask) != kHeaderEncodingUnicode)) return; NSNumber *key = [NSNumber numberWithUnsignedChar:headerID]; if ([mDict objectForKey:key] == nil) [mKeys addObject:key]; [mDict setObject:value forKey:key]; } - (void)setValue:(NSData *)value forByteSequenceHeader:(uint8_t)headerID { if (!value || ((headerID & kHeaderEncodingMask) != kHeaderEncodingByteSequence)) return; NSNumber *key = [NSNumber numberWithUnsignedChar:headerID]; if ([mDict objectForKey:key] == nil) [mKeys addObject:key]; [mDict setObject:value forKey:key]; } - (void)setValue:(uint32_t)value for4ByteHeader:(uint8_t)headerID { if ((headerID & kHeaderEncodingMask) != kHeaderEncoding4Byte) return; NSNumber *key = [NSNumber numberWithUnsignedChar:headerID]; if ([mDict objectForKey:key] == nil) [mKeys addObject:key]; [mDict setObject:[NSNumber numberWithUnsignedInt:value] forKey:key]; } - (void)setValue:(uint8_t)value for1ByteHeader:(uint8_t)headerID { if ((headerID & kHeaderEncodingMask) != kHeaderEncoding1Byte) return; NSNumber *key = [NSNumber numberWithUnsignedChar:headerID]; if ([mDict objectForKey:key] == nil) [mKeys addObject:key]; [mDict setObject:[NSNumber numberWithUnsignedChar:value] forKey:key]; } - (void)addHeadersFromHeaderSet:(BBOBEXHeaderSet *)headerSet { NSDictionary *dict = [headerSet valueForKey:@"mDict"]; [mDict addEntriesFromDictionary:dict]; NSArray *keys = [headerSet allHeaders]; int i; for (i=0; i<[keys count]; i++) { if (![mKeys containsObject:[keys objectAtIndex:i]]) { [mKeys addObject:[keys objectAtIndex:i]]; } } } static uint16_t parseUInt16(const uint8_t *bytes) { uint16_t value; memcpy((void *)&value, bytes, sizeof(value)); return NSSwapBigShortToHost(value); } static uint32_t parseUInt32(const uint8_t *bytes) { uint32_t value; memcpy((void *)&value, bytes, sizeof(value)); return NSSwapBigIntToHost(value); } static NSString *parseString(const uint8_t *bytes, unsigned int length) { if (length < 0) return nil; NSString *s = [[NSString alloc] initWithBytes:bytes length:length encoding:NSUnicodeStringEncoding]; return [s autorelease]; } static NSData *parseData(const uint8_t *bytes, unsigned int length) { if (length < 0) return nil; return [NSData dataWithBytes:bytes length:length]; } - (BOOL)addHeadersFromHeadersData:(const uint8_t *)headersData length:(size_t)length { if (length == 0) // nothing to add return YES; if (length == 1) // must have at least 2 headersData return NO; NSNumber *hi; uint16_t hlen; //NSLog(@"reading %d bytes", length); int i = 0; while (i < length) { hi = [NSNumber numberWithUnsignedChar:headersData[i]]; //NSLog(@"Next header: %d", headersData[i]); switch (headersData[i] & kHeaderEncodingMask) { case kHeaderEncoding4Byte: // ID V V V V { //NSLog(@"\tint:"); hlen = 5; if (i + hlen > length) return NO; [self setValue:parseUInt32(&headersData[i+1]) for4ByteHeader:headersData[i]]; break; } case kHeaderEncoding1Byte: // ID V { //NSLog(@"\tbyte:"); hlen = 2; if (i + hlen > length) return NO; [self setValue:headersData[i+1] for1ByteHeader:headersData[i]]; break; } case kHeaderEncodingUnicode: // ID L L V V .. .. O O { //NSLog(@"\tunicode:"); if (i + 3 > length) return NO; hlen = parseUInt16(&headersData[i+1]); if (i + hlen > length) return NO; NSString *s = nil; if (hlen - 3 == 0) { // empty string (3 headersData is for ID + length) s = [NSString string]; } else { // account for 3 headersData of ID + length s = parseString(&headersData[i+3], (hlen - 3 - 2)); } if (!s) return NO; [self setValue:s forUnicodeHeader:headersData[i]]; break; } case kHeaderEncodingByteSequence: // ID L L V .. { //NSLog(@"\tbyte stream:"); if (i + 3 > length) return NO; hlen = parseUInt16(&headersData[i+1]); //NSLog(@"\tbytes length: %d", hlen); if (i + hlen > length) return NO; NSData *data = nil; if (hlen - 3 == 0) { data = [NSData data]; } else { // account for 3 headersData of ID + length data = parseData(&headersData[i+3], hlen-3); } //NSLog(@"\tread bytes"); if (!data) return NO; [self setValue:data forByteSequenceHeader:headersData[i]]; break; } } i += hlen; //NSLog(@"\tRead header ok, now to index %d...", i); } //NSLog(@"\tFinished reading headers"); return YES; } - (void)removeValueForHeader:(uint8_t)headerID { NSNumber *number = [NSNumber numberWithUnsignedChar:headerID]; [mDict removeObjectForKey:number]; [mKeys removeObject:number]; } @end lightblue-0.3.2/src/mac/LightAquaBlue/._BBOBEXHeaderSet.h0000644000076500000000000000012210750034215022462 0ustar beawheel00000000000000Mac OS X  2 RTEXTlightblue-0.3.2/src/mac/LightAquaBlue/BBOBEXHeaderSet.h0000644000076500007650000001230010750034215021713 0ustar beabea00000000000000/* * Copyright (c) 2006 Bea Lam. All rights reserved. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation files * (the "Software"), to deal in the Software without restriction, * including without limitation the rights to use, copy, modify, merge, * publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ // // BBOBEXHeaderSet.h // LightAquaBlue // // A collection of unique OBEX headers. // // The mutable counterpart to this class is BBMutableOBEXHeaderSet. // #import #import #import @interface BBOBEXHeaderSet : NSObject { NSMutableDictionary *mDict; NSMutableArray *mKeys; } /* * Creates and returns an empty header set. */ + (id)headerSet; /* * Returns whether this header set contains the header with . * * Common header IDs are defined in the OBEXHeaderIdentifiers enum in * (for example, kOBEXHeaderIDName). */ - (BOOL)containsValueForHeader:(uint8_t)headerID; /* * Returns the number of headers in this header set. */ - (unsigned)count; /* * Returns the "Count" header value, or 0 if the header is not present or cannot * be read. */ - (unsigned int)valueForCountHeader; /* * Returns the value for the Name header, or nil if the header is not present or * cannot be read. */ - (NSString *)valueForNameHeader; /* * Returns the value for the Type header, or nil if the header is not present or * cannot be read. */ - (NSString *)valueForTypeHeader; /* * Returns the value for the Length header, or 0 if the header is not present or * cannot be read. */ - (unsigned int)valueForLengthHeader; /* * Returns the value for the 0x44 Time header, or the 0xC4 value if the 0x44 * header is not present, or nil if neither header is present or cannot be read. */ - (NSDate *)valueForTimeHeader; /* * Returns the value for the Description header, or nil if the header is not * present or cannot be read. */ - (NSString *)valueForDescriptionHeader; /* * Returns the value for the Target header, or nil if the header is not present * or cannot be read. */ - (NSData *)valueForTargetHeader; /* * Returns the value for the HTTP header, or nil if the header is not present * or cannot be read. */ - (NSData *)valueForHTTPHeader; /* * Returns the value for the Who header, or nil if the header is not present * or cannot be read. */ - (NSData *)valueForWhoHeader; /* * Returns the value for the Connection Id header, or 0 if the header is not * present or cannot be read. */ - (uint32_t)valueForConnectionIDHeader; /* * Returns the value for the Application Parameters header, or nil if the * header is not present or cannot be read. */ - (NSData *)valueForApplicationParametersHeader; /* * Returns the value for the Authorization Challenge header, or nil if the * header is not present or cannot be read. */ - (NSData *)valueForAuthorizationChallengeHeader; /* * Returns the value for the Authorization Response header, or nil if the * header is not present or cannot be read. */ - (NSData *)valueForAuthorizationResponseHeader; /* * Returns the value for the Object Class header, or nil if the * header is not present or cannot be read. */ - (NSData *)valueForObjectClassHeader; /* * Returns the value for the 4-byte header , or 0 if the header is * not present or cannot be read as a 4-byte value. */ - (unsigned int)valueFor4ByteHeader:(uint8_t)headerID; /* * Returns the value for the byte-sequence header , or nil if the * header is not present or cannot be read as a byte-sequence value. */ - (NSData *)valueForByteSequenceHeader:(uint8_t)headerID; /* * Returns the value for the 1-byte header , or 0 if the header is * not present or cannot be read as a 1-byte value. */ - (uint8_t)valueFor1ByteHeader:(uint8_t)headerID; /* * Returns the value for the unicode header , or nil if the * header is not present or cannot be read as a unicode value. */ - (NSString *)valueForUnicodeHeader:(uint8_t)headerID; /* * Returns all the headers in the header set as a list of NSNumber objects. * Each NSNumber contains an unsigned char value (the header ID). * * The headers are returned in the order in which they were added. */ - (NSArray *)allHeaders; /* * Returns a stream of bytes that contain the headers in this header set, as * specified by the IrOBEX specification (section 2.1). */ - (NSMutableData *)toBytes; @end lightblue-0.3.2/src/mac/LightAquaBlue/._BBOBEXHeaderSet.m0000755000076500000000000000012210747275353022512 0ustar beawheel00000000000000Mac OS X  2 RTEXTlightblue-0.3.2/src/mac/LightAquaBlue/BBOBEXHeaderSet.m0000755000076500000000000003075110747275353022310 0ustar beawheel00000000000000/* * Copyright (c) 2006 Bea Lam. All rights reserved. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation files * (the "Software"), to deal in the Software without restriction, * including without limitation the rights to use, copy, modify, merge, * publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ // // BBOBEXHeaderSet.m // LightAquaBlue // #import "BBOBEXHeaderSet.h" enum kOBEXHeaderEncoding { kHeaderEncodingMask = 0xc0, kHeaderEncodingUnicode = 0x00, kHeaderEncodingByteSequence = 0x40, kHeaderEncoding1Byte = 0x80, kHeaderEncoding4Byte = 0xc0 }; static NSString *DATE_FORMAT_STRING = @"%Y%m%dT%H%M%S"; // no 'Z' at end static const uint8_t NULL_TERMINATORS[2] = { 0x00, 0x00 }; @implementation BBOBEXHeaderSet + (id)headerSet { return [[[BBOBEXHeaderSet alloc] init] autorelease]; } - (id)init { self = [super init]; mDict = [[NSMutableDictionary alloc] initWithCapacity:0]; mKeys = [[NSMutableArray alloc] initWithCapacity:0]; return self; } - (BOOL)containsValueForHeader:(uint8_t)headerID { return ([mDict objectForKey:[NSNumber numberWithUnsignedChar:headerID]] != nil); } #pragma mark - - (unsigned int)valueForCountHeader { return [self valueFor4ByteHeader:kOBEXHeaderIDCount]; } - (NSString *)valueForNameHeader { return [self valueForUnicodeHeader:kOBEXHeaderIDName]; } - (NSString *)valueForTypeHeader { NSData *data = [self valueForByteSequenceHeader:kOBEXHeaderIDType]; if (!data) return nil; if ([data length] == 0) return [NSString string]; const char *s = (const char *)[data bytes]; if (s[[data length]-1] == '\0') { return [NSString stringWithCString:(const char *)[data bytes] encoding:NSASCIIStringEncoding]; } return [[[NSString alloc] initWithBytes:[data bytes] length:[data length] encoding:NSASCIIStringEncoding] autorelease]; } - (unsigned int)valueForLengthHeader { return [self valueFor4ByteHeader:kOBEXHeaderIDLength]; } - (NSDate *)valueForTimeHeader { NSData *data = [self valueForByteSequenceHeader:kOBEXHeaderIDTimeISO]; if (data && [data length] > 0) { NSString *s = [[[NSString alloc] initWithBytes:[data bytes] length:[data length] encoding:NSASCIIStringEncoding] autorelease]; NSCalendarDate *calendarDate = nil; if ([s characterAtIndex:[s length]-1] == 'Z') { calendarDate = [NSCalendarDate dateWithString:[s substringToIndex:[s length]-1] calendarFormat:DATE_FORMAT_STRING]; [calendarDate setTimeZone:[NSTimeZone timeZoneWithName:@"UTC"]]; } else { calendarDate = [NSCalendarDate dateWithString:s calendarFormat:DATE_FORMAT_STRING]; [calendarDate setTimeZone:[NSTimeZone localTimeZone]]; } return calendarDate; } else { uint32_t time = [self valueFor4ByteHeader:kOBEXHeaderIDTime4Byte]; return [NSDate dateWithTimeIntervalSince1970:time]; } return nil; } - (NSString *)valueForDescriptionHeader { return [self valueForUnicodeHeader:kOBEXHeaderIDDescription]; } - (NSData *)valueForTargetHeader { return [self valueForByteSequenceHeader:kOBEXHeaderIDTarget]; } - (NSData *)valueForHTTPHeader { return [self valueForByteSequenceHeader:kOBEXHeaderIDHTTP]; } - (NSData *)valueForWhoHeader { return [self valueForByteSequenceHeader:kOBEXHeaderIDWho]; } - (uint32_t)valueForConnectionIDHeader { return [self valueFor4ByteHeader:kOBEXHeaderIDConnectionID]; } - (NSData *)valueForApplicationParametersHeader { return [self valueForByteSequenceHeader:kOBEXHeaderIDAppParameters]; } - (NSData *)valueForAuthorizationChallengeHeader { return [self valueForByteSequenceHeader:kOBEXHeaderIDAuthorizationChallenge]; } - (NSData *)valueForAuthorizationResponseHeader { return [self valueForByteSequenceHeader:kOBEXHeaderIDAuthorizationResponse]; } - (NSData *)valueForObjectClassHeader { return [self valueForByteSequenceHeader:0x51]; } #pragma mark - - (unsigned int)valueFor4ByteHeader:(uint8_t)headerID { NSNumber *number = [mDict objectForKey:[NSNumber numberWithUnsignedChar:headerID]]; if (number) return [number unsignedIntValue]; return 0; } - (NSData *)valueForByteSequenceHeader:(uint8_t)headerID { return [mDict objectForKey:[NSNumber numberWithUnsignedChar:headerID]]; } - (uint8_t)valueFor1ByteHeader:(uint8_t)headerID { NSNumber *number = [mDict objectForKey:[NSNumber numberWithUnsignedChar:headerID]]; if (number) return [number unsignedCharValue]; return 0; } - (NSString *)valueForUnicodeHeader:(uint8_t)headerID { return [mDict objectForKey:[NSNumber numberWithUnsignedChar:headerID]]; } - (NSArray *)allHeaders { return mKeys; } #pragma mark - static void addUInt16Bytes(uint16_t value, NSMutableData *data) { uint16_t swapped = NSSwapHostShortToBig(value); [data appendBytes:&swapped length:sizeof(swapped)]; } static NSMutableData *stringHeaderBytes(uint8_t hid, NSString *s) { NSMutableData *bytes = [NSMutableData dataWithBytes:&hid length:1]; if ([s length] == 0) { addUInt16Bytes(3, bytes); // empty string == header length of 3 return bytes; } CFStringEncoding encoding; #if __BIG_ENDIAN__ encoding = kCFStringEncodingUnicode; #elif __LITTLE_ENDIAN__ encoding = kCFStringEncodingUTF16BE; // not in 10.3 #endif CFDataRef encodedString = CFStringCreateExternalRepresentation(NULL, (CFStringRef)s, encoding, '?'); if (encodedString) { // CFStringCreateExternalRepresentation() may insert 2-byte BOM if (CFDataGetLength(encodedString) >= 2) { const uint8_t *bytePtr = CFDataGetBytePtr(encodedString); CFIndex length = CFDataGetLength(encodedString); if ( (bytePtr[0] == 0xFE && bytePtr[1] == 0xFF) || (bytePtr[0] == 0xFF && bytePtr[1] == 0xFE) ) { bytePtr = &CFDataGetBytePtr(encodedString)[2]; length -= 2; } addUInt16Bytes(length + 2 + 3, bytes); [bytes appendBytes:bytePtr length:length]; [bytes appendBytes:NULL_TERMINATORS length:2]; } CFRelease(encodedString); } return bytes; } static NSData *byteSequenceHeaderBytes(uint8_t hid, NSData *data) { NSMutableData *headerBytes = [NSMutableData dataWithBytes:&hid length:1]; uint16_t headerLength = ([data length] + 3); addUInt16Bytes(headerLength, headerBytes); if ([data length] == 0) return headerBytes; [headerBytes appendData:data]; return headerBytes; } static NSData *fourByteHeaderBytes(uint8_t hid, uint32_t value) { NSMutableData *bytes = [NSMutableData dataWithBytes:&hid length:1]; uint32_t swapped = NSSwapHostIntToBig(value); [bytes appendBytes:&swapped length:sizeof(swapped)]; return bytes; } static NSData *oneByteHeaderBytes(uint8_t hid, uint8_t value) { NSMutableData *bytes = [NSMutableData dataWithBytes:&hid length:1]; [bytes appendBytes:&value length:1]; return bytes; } - (NSMutableData *)toBytes { if ([mDict count] == 0) return NULL; NSMutableData *headerBytes = [[NSMutableData alloc] initWithLength:0]; if ([self containsValueForHeader:kOBEXHeaderIDTarget]) { NSData *bytes; NSData *target = [self valueForTargetHeader]; if (target) bytes = byteSequenceHeaderBytes(kOBEXHeaderIDTarget, target); if (!bytes) return NULL; [headerBytes appendData:bytes]; } if ([self containsValueForHeader:kOBEXHeaderIDConnectionID]) { NSData *bytes = fourByteHeaderBytes(kOBEXHeaderIDConnectionID, [self valueForConnectionIDHeader]); if (!bytes) return NULL; [headerBytes appendData:bytes]; } NSArray *headers = [self allHeaders]; uint8_t rawHeaderID; int i; for (i=0; i<[headers count]; i++) { rawHeaderID = [(NSNumber *)[headers objectAtIndex:i] unsignedCharValue]; //NSLog(@"--- toBytes() writing header 0x%02x", rawHeaderID); if (rawHeaderID == kOBEXHeaderIDTarget || rawHeaderID == kOBEXHeaderIDConnectionID) continue; // already handled these NSData *bytes = nil; switch (rawHeaderID & kHeaderEncodingMask) { case kHeaderEncodingUnicode: { NSString *s = [self valueForUnicodeHeader:rawHeaderID]; if (!s) return NULL; bytes = stringHeaderBytes(rawHeaderID, s); break; } case kHeaderEncodingByteSequence: { NSData *data = [self valueForByteSequenceHeader:rawHeaderID]; if (!data) return NULL; bytes = byteSequenceHeaderBytes(rawHeaderID, data); break; } case kHeaderEncoding1Byte: { bytes = oneByteHeaderBytes(rawHeaderID, [self valueFor1ByteHeader:rawHeaderID]); break; } case kHeaderEncoding4Byte: { bytes = fourByteHeaderBytes(rawHeaderID, [self valueFor4ByteHeader:rawHeaderID]); break; } default: return NULL; } if (bytes == nil) return NULL; [headerBytes appendData:bytes]; } return headerBytes; } - (unsigned)count { return [mDict count]; } #pragma mark - static NSString *getHeaderDescription(uint8_t headerID) { switch (headerID) { case kOBEXHeaderIDCount: return @"Count"; case kOBEXHeaderIDName: return @"Name"; case kOBEXHeaderIDDescription: return @"Description"; case kOBEXHeaderIDType: return @"Type"; case kOBEXHeaderIDLength: return @"Length"; case kOBEXHeaderIDTimeISO: case kOBEXHeaderIDTime4Byte: return @"Time"; case kOBEXHeaderIDTarget: return @"Target"; case kOBEXHeaderIDHTTP: return @"HTTP"; case kOBEXHeaderIDBody: return @"Body"; case kOBEXHeaderIDEndOfBody: return @"End of Body"; case kOBEXHeaderIDWho: return @"Who"; case kOBEXHeaderIDConnectionID: return @"Connection ID"; case kOBEXHeaderIDAppParameters: return @"Application Parameters"; case kOBEXHeaderIDAuthorizationChallenge: return @"Authorization Challenge"; case kOBEXHeaderIDAuthorizationResponse: return @"Authorization Response"; case 0x51: return @"Object Class"; case 0x52: return @"Session-Parameters"; case 0x93: return @"Session-Sequence-Number"; default: return [NSString stringWithFormat:@"0x%02x", headerID]; } } - (NSString *)description { NSMutableString *string = [NSMutableString stringWithCapacity:0]; [string appendString:@"{"]; NSNumber *n; int i; for (i=0; i<[mKeys count]; i++) { n = [mKeys objectAtIndex:i]; [string appendFormat:@"%@: %@%@", getHeaderDescription([n unsignedCharValue]), [mDict objectForKey:n], (i == [mKeys count]-1 ? @"" : @", ")]; } [string appendString:@"}"]; return string; } - (void)dealloc { [mDict release]; [mKeys release]; [super dealloc]; } @end lightblue-0.3.2/src/mac/LightAquaBlue/BBOBEXRequest.h0000644000076500007650000000751410747223641021523 0ustar beabea00000000000000/* * Copyright (c) 2006 Bea Lam. All rights reserved. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation files * (the "Software"), to deal in the Software without restriction, * including without limitation the rights to use, copy, modify, merge, * publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ // // BBOBEXRequest.h // LightAquaBlue // // These are internal classes used by BBBluetoothOBEXClient for sending OBEX // client requests. Each BBOBEXRequest subclass encapsulates the process // for performing a particular type of request. // #import #import @class BBBluetoothOBEXClient; @class BBOBEXHeaderSet; @class BBMutableOBEXHeaderSet; @class OBEXSession; @interface BBOBEXRequest : NSObject { BBBluetoothOBEXClient *mClient; SEL mClientEventSelector; OBEXSession *mSession; BOOL mFinished; BBMutableOBEXHeaderSet *mResponseHeaders; } + (void)setDebug:(BOOL)debug; - (id)initWithClient:(BBBluetoothOBEXClient *)client eventSelector:(SEL)selector session:(OBEXSession *)session; - (BOOL)isFinished; - (OBEXError)beginWithHeaders:(BBOBEXHeaderSet *)headers; - (void)receivedResponseWithHeaders:(BBMutableOBEXHeaderSet *)responseHeaders; - (OBEXError)sendNextRequestPacket; - (void)finishedWithError:(OBEXError)error responseCode:(int)responseCode; - (BOOL)readOBEXResponseHeaders:(BBMutableOBEXHeaderSet **)responseHeaders andResponseCode:(int *)responseCode fromSessionEvent:(const OBEXSessionEvent *)event; @end @interface BBOBEXConnectRequest : BBOBEXRequest { CFMutableDataRef mHeadersDataRef; } @end @interface BBOBEXDisconnectRequest : BBOBEXRequest { CFMutableDataRef mHeadersDataRef; } @end @interface BBOBEXPutRequest : BBOBEXRequest { CFMutableDataRef mHeadersDataRef; NSMutableData *mSentBodyData; NSInputStream *mInputStream; } - (id)initWithClient:(BBBluetoothOBEXClient *)client eventSelector:(SEL)selector session:(OBEXSession *)session inputStream:(NSInputStream *)inputStream; @end @interface BBOBEXGetRequest : BBOBEXRequest { CFMutableDataRef mHeadersDataRef; NSOutputStream *mOutputStream; unsigned int mTotalGetLength; } - (id)initWithClient:(BBBluetoothOBEXClient *)client eventSelector:(SEL)selector session:(OBEXSession *)session outputStream:(NSOutputStream *)outputStream; @end @interface BBOBEXSetPathRequest : BBOBEXRequest { CFMutableDataRef mHeadersDataRef; OBEXFlags mRequestFlags; } - (id)initWithClient:(BBBluetoothOBEXClient *)client eventSelector:(SEL)selector session:(OBEXSession *)session changeToParentDirectoryFirst:(BOOL)changeToParentDirectoryFirst createDirectoriesIfNeeded:(BOOL)createDirectoriesIfNeeded; @end @interface BBOBEXAbortRequest : BBOBEXRequest { NSStream *mStream; } - (id)initWithClient:(BBBluetoothOBEXClient *)client eventSelector:(SEL)selector session:(OBEXSession *)session currentRequestStream:(NSStream *)stream; @end lightblue-0.3.2/src/mac/LightAquaBlue/._BBOBEXRequest.m0000644000076500000000000000012210750747511022265 0ustar beawheel00000000000000Mac OS X  2 RTEXTlightblue-0.3.2/src/mac/LightAquaBlue/BBOBEXRequest.m0000644000076500007650000005772310750747511021540 0ustar beabea00000000000000/* * Copyright (c) 2006 Bea Lam. All rights reserved. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation files * (the "Software"), to deal in the Software without restriction, * including without limitation the rights to use, copy, modify, merge, * publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ // // BBOBEXRequest.m // LightAquaBlue // #import "BBOBEXRequest.h" #import "BBBluetoothOBEXClient.h" #import "BBMutableOBEXHeaderSet.h" #import "BBOBEXHeaderSet.h" #import "BBOBEXResponse.h" #import #import static BOOL _debug = NO; @implementation BBOBEXRequest + (void)setDebug:(BOOL)debug { _debug = debug; } - (id)initWithClient:(BBBluetoothOBEXClient *)client eventSelector:(SEL)selector session:(OBEXSession *)session { self = [super init]; mClient = client; // don't retain, avoid retaining both ways mClientEventSelector = selector; mSession = session; // don't retain mFinished = NO; return self; } - (BOOL)isFinished { return mFinished; } - (OBEXError)beginWithHeaders:(BBOBEXHeaderSet *)headers { return kOBEXSuccess; } - (void)receivedResponseWithHeaders:(BBMutableOBEXHeaderSet *)responseHeaders { if (!mResponseHeaders) mResponseHeaders = [[BBMutableOBEXHeaderSet alloc] init]; // add to current response headers // this way we won't lose previous response headers in multi-packet // Put & Get requests [mResponseHeaders addHeadersFromHeaderSet:responseHeaders]; } - (OBEXError)sendNextRequestPacket { return kOBEXSuccess; } - (void)finishedWithError:(OBEXError)error responseCode:(int)responseCode { } - (BOOL)readOBEXResponseHeaders:(BBMutableOBEXHeaderSet **)responseHeaders andResponseCode:(int *)responseCode fromSessionEvent:(const OBEXSessionEvent *)event { return YES; } - (void)dealloc { [mResponseHeaders release]; [super dealloc]; } @end @implementation BBOBEXConnectRequest - (OBEXError)beginWithHeaders:(BBOBEXHeaderSet *)headers { if (_debug) NSLog(@"[BBOBEXConnectRequest] beginWithHeaders (%d headers)", [headers count]); CFMutableDataRef bytes = (CFMutableDataRef)[headers toBytes]; if (!bytes && [headers count] > 0) return kOBEXInternalError; OBEXError status = [mSession OBEXConnect:(OBEXFlags)kOBEXConnectFlagNone maxPacketLength:[mClient maximumPacketLength] optionalHeaders:(bytes ? (void *)CFDataGetBytePtr(bytes) : NULL) optionalHeadersLength:(bytes ? CFDataGetLength(bytes) : 0) eventSelector:mClientEventSelector selectorTarget:mClient refCon:NULL]; if (bytes) mHeadersDataRef = bytes; return status; } - (void)finishedWithError:(OBEXError)error responseCode:(int)responseCode { if (_debug) NSLog(@"[BBOBEXConnectRequest] finishedWithError %@: %d", [mClient delegate], error); mFinished = YES; if ([[mClient delegate] respondsToSelector:@selector(client:didFinishConnectRequestWithError:response:)]) { [[mClient delegate] client:mClient didFinishConnectRequestWithError:error response:[BBOBEXResponse responseWithCode:responseCode headers:mResponseHeaders]]; } } - (BOOL)readOBEXResponseHeaders:(BBMutableOBEXHeaderSet **)responseHeaders andResponseCode:(int *)responseCode fromSessionEvent:(const OBEXSessionEvent *)event { if (event->type != kOBEXSessionEventTypeConnectCommandResponseReceived) return NO; const OBEXConnectCommandResponseData *resp = &event->u.connectCommandResponseData; *responseCode = resp->serverResponseOpCode; BBMutableOBEXHeaderSet *headers = [BBMutableOBEXHeaderSet headerSet]; if ([headers addHeadersFromHeadersData:resp->headerDataPtr length:resp->headerDataLength]) { *responseHeaders = headers; } return YES; } - (void)dealloc { if (mHeadersDataRef) CFRelease(mHeadersDataRef); [super dealloc]; } @end @implementation BBOBEXDisconnectRequest - (OBEXError)beginWithHeaders:(BBOBEXHeaderSet *)headers { if (_debug) NSLog(@"[BBOBEXDisconnectRequest] beginWithHeaders (%d headers)", [headers count]); CFMutableDataRef bytes = (CFMutableDataRef)[headers toBytes]; if (!bytes && [headers count] > 0) return kOBEXInternalError; OBEXError status = [mSession OBEXDisconnect:(bytes ? (void *)CFDataGetBytePtr(bytes) : NULL) optionalHeadersLength:(bytes ? CFDataGetLength(bytes) : 0) eventSelector:mClientEventSelector selectorTarget:mClient refCon:NULL]; if (bytes) mHeadersDataRef = bytes; return status; } - (void)finishedWithError:(OBEXError)error responseCode:(int)responseCode { if (_debug) NSLog(@"[BBOBEXDisconnectRequest] finishedWithError: %d", error); mFinished = YES; if ([[mClient delegate] respondsToSelector:@selector(client:didFinishDisconnectRequestWithError:response:)]) { [[mClient delegate] client:mClient didFinishDisconnectRequestWithError:error response:[BBOBEXResponse responseWithCode:responseCode headers:mResponseHeaders]]; } } - (BOOL)readOBEXResponseHeaders:(BBMutableOBEXHeaderSet **)responseHeaders andResponseCode:(int *)responseCode fromSessionEvent:(const OBEXSessionEvent *)event { if (event->type != kOBEXSessionEventTypeDisconnectCommandResponseReceived) return NO; const OBEXDisconnectCommandResponseData *resp = &event->u.disconnectCommandResponseData; *responseCode = resp->serverResponseOpCode; BBMutableOBEXHeaderSet *headers = [BBMutableOBEXHeaderSet headerSet]; if ([headers addHeadersFromHeadersData:resp->headerDataPtr length:resp->headerDataLength]) { *responseHeaders = headers; } return YES; } - (void)dealloc { if (mHeadersDataRef) CFRelease(mHeadersDataRef); [super dealloc]; } @end @implementation BBOBEXPutRequest - (id)initWithClient:(BBBluetoothOBEXClient *)client eventSelector:(SEL)selector session:(OBEXSession *)session inputStream:(NSInputStream *)inputStream { self = [super initWithClient:client eventSelector:selector session:session]; mInputStream = [inputStream retain]; return self; } - (NSMutableData *)readNextChunkForHeaderLength:(size_t)headersLength isLastChunk:(BOOL *)outIsLastChunk { if (mInputStream == nil) return nil; OBEXMaxPacketLength maxPacketSize = [mSession getAvailableCommandPayloadLength:kOBEXOpCodePut]; if (maxPacketSize == 0 || headersLength > maxPacketSize) return nil; OBEXMaxPacketLength maxBodySize = maxPacketSize - headersLength; NSMutableData *data = [NSMutableData dataWithLength:maxBodySize]; int len = [mInputStream read:[data mutableBytes] maxLength:maxBodySize]; if (_debug) NSLog(@"[BBOBEXPutRequest] read %d bytes (maxBodySize = %d)", len, maxBodySize); // is last packet if there wasn't enough body data to fill up the packet if (len >= 0) *outIsLastChunk = (len < maxBodySize); [data setLength:len]; return data; } + (OBEXError)appendEmptyEndOfBodyHeaderToData:(CFMutableDataRef)headerData { if (!headerData) return kOBEXInternalError; // can't see to add the data using raw bytes, so make a dictionary // and use OBEXHeadersToBytes() to get the end of body header bytes CFMutableDictionaryRef mutableDict = CFDictionaryCreateMutable(NULL, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); OBEXError status = OBEXAddBodyHeader(NULL, 0, TRUE, mutableDict); if (status == kOBEXSuccess) { CFMutableDataRef bodyData = OBEXHeadersToBytes(mutableDict); if (bodyData == NULL) { CFRelease(mutableDict); return kOBEXGeneralError; } CFDataAppendBytes(headerData, CFDataGetBytePtr(bodyData), CFDataGetLength(bodyData)); CFRelease(bodyData); } CFRelease(mutableDict); return status; } - (OBEXError)beginWithHeaders:(BBOBEXHeaderSet *)headers { if (_debug) NSLog(@"[BBOBEXPutRequest] beginWithHeaders (%d headers)", [headers count]); // there's no stream if it's a Put-Delete // but if there is a stream, it must be open if (mInputStream && [mInputStream streamStatus] != NSStreamStatusOpen) { if (_debug) NSLog(@"[BBOBEXPutRequest] given input stream not opened!"); return kOBEXBadArgumentError; } CFMutableDataRef bytes = (CFMutableDataRef)[headers toBytes]; if (!bytes && [headers count] > 0) return kOBEXInternalError; BOOL isLastChunk = YES; NSMutableData *mutableData = nil; if (mInputStream != nil) { mutableData = [self readNextChunkForHeaderLength:(bytes ? CFDataGetLength(bytes) : 0) isLastChunk:&isLastChunk]; if (!mutableData) { if (_debug) NSLog(@"[BBOBEXPutRequest] error reading from stream!"); if (bytes) CFRelease(bytes); return kOBEXInternalError; } // If zero data read, then this must be a Create-Empty (IrOBEX 3.3.3.6) // so add an empty End-of-Body header, because OBEXPut: won't add body // headers if bodyDataLength is zero, in which case the server will // think it's a Put-Delete instead of a Put. if ([mutableData length] == 0) { if (!bytes) bytes = CFDataCreateMutable(NULL, 0); OBEXError status = [BBOBEXPutRequest appendEmptyEndOfBodyHeaderToData:bytes]; if (status != kOBEXSuccess) { if (_debug) NSLog(@"[BBOBEXPutRequest] error adding empty end of body"); CFRelease(bytes); return status; } } } OBEXError status; status = [mSession OBEXPut:isLastChunk headersData:(bytes ? (void*)CFDataGetBytePtr(bytes) : NULL) headersDataLength:(bytes ? CFDataGetLength(bytes) : 0) bodyData:(mutableData ? [mutableData mutableBytes] : NULL) bodyDataLength:(mutableData ? [mutableData length] : 0) eventSelector:mClientEventSelector selectorTarget:mClient refCon:NULL]; if (bytes) mHeadersDataRef = bytes; if (mutableData) { [mutableData retain]; [mSentBodyData release]; mSentBodyData = mutableData; if (status == kOBEXSuccess) { if ([[mClient delegate] respondsToSelector:@selector(client:didSendDataOfLength:)]) { [[mClient delegate] client:mClient didSendDataOfLength:[mutableData length]]; } } } return status; } - (OBEXError)sendNextRequestPacket { if (_debug) NSLog(@"[BBOBEXPutRequest] sendNextRequestPacket"); BOOL isLastChunk; NSMutableData *mutableData = [self readNextChunkForHeaderLength:0 isLastChunk:&isLastChunk]; if (!mutableData) return kOBEXInternalError; OBEXError status = [mSession OBEXPut:isLastChunk headersData:NULL headersDataLength:0 bodyData:[mutableData mutableBytes] bodyDataLength:[mutableData length] eventSelector:mClientEventSelector selectorTarget:mClient refCon:NULL]; [mutableData retain]; [mSentBodyData release]; mSentBodyData = mutableData; if (status == kOBEXSuccess) { if ([[mClient delegate] respondsToSelector:@selector(client:didSendDataOfLength:)]) { [[mClient delegate] client:mClient didSendDataOfLength:[mutableData length]]; } } return status; } - (void)finishedWithError:(OBEXError)error responseCode:(int)responseCode { if (_debug) NSLog(@"[BBOBEXPutRequest] finishedWithError: %d", error); mFinished = YES; if ([[mClient delegate] respondsToSelector:@selector(client:didFinishPutRequestForStream:error:response:)]) { [[mClient delegate] client:mClient didFinishPutRequestForStream:mInputStream error:error response:[BBOBEXResponse responseWithCode:responseCode headers:mResponseHeaders]]; } } - (BOOL)readOBEXResponseHeaders:(BBMutableOBEXHeaderSet **)responseHeaders andResponseCode:(int *)responseCode fromSessionEvent:(const OBEXSessionEvent *)event { if (event->type != kOBEXSessionEventTypePutCommandResponseReceived) return NO; const OBEXPutCommandResponseData *resp = &event->u.putCommandResponseData; *responseCode = resp->serverResponseOpCode; BBMutableOBEXHeaderSet *headers = [BBMutableOBEXHeaderSet headerSet]; if ([headers addHeadersFromHeadersData:resp->headerDataPtr length:resp->headerDataLength]) { *responseHeaders = headers; } return YES; } - (void)dealloc { if (mHeadersDataRef) CFRelease(mHeadersDataRef); [mSentBodyData release]; [mInputStream release]; [super dealloc]; } @end @implementation BBOBEXGetRequest - (id)initWithClient:(BBBluetoothOBEXClient *)client eventSelector:(SEL)selector session:(OBEXSession *)session outputStream:(NSOutputStream *)outputStream { self = [super initWithClient:client eventSelector:selector session:session]; mOutputStream = [outputStream retain]; return self; } - (OBEXError)beginWithHeaders:(BBOBEXHeaderSet *)headers { if (_debug) NSLog(@"[BBOBEXGetRequest] beginWithHeaders (%d headers)", [headers count]); if (mOutputStream == nil || [mOutputStream streamStatus] != NSStreamStatusOpen) return kOBEXBadArgumentError; mTotalGetLength = 0; CFMutableDataRef bytes = (CFMutableDataRef)[headers toBytes]; if (!bytes && [headers count] > 0) return kOBEXInternalError; OBEXError status = [mSession OBEXGet:YES headers:(bytes ? (void *)CFDataGetBytePtr(bytes) : NULL) headersLength:(bytes ? CFDataGetLength(bytes) : 0) eventSelector:mClientEventSelector selectorTarget:mClient refCon:NULL]; if (bytes) mHeadersDataRef = bytes; return status; } - (void)receivedResponseWithHeaders:(BBMutableOBEXHeaderSet *)responseHeaders { if (_debug) NSLog(@"[BBOBEXGetRequest] receivedResponseWithHeaders"); // don't pass body/end-of-body headers onto client delegate NSData *bodyData = [responseHeaders valueForByteSequenceHeader:kOBEXHeaderIDBody]; NSData *endOfBodyData = [responseHeaders valueForByteSequenceHeader:kOBEXHeaderIDEndOfBody]; [responseHeaders removeValueForHeader:kOBEXHeaderIDBody]; [responseHeaders removeValueForHeader:kOBEXHeaderIDEndOfBody]; // super impl. stores response headers to pass them onto delegate later [super receivedResponseWithHeaders:responseHeaders]; int totalBytesReceived = 0; if (bodyData) { if ([mOutputStream write:[bodyData bytes] maxLength:[bodyData length]] < 0) totalBytesReceived = -1; else totalBytesReceived += [bodyData length]; } if (endOfBodyData && totalBytesReceived != -1) { if ([mOutputStream write:[endOfBodyData bytes] maxLength:[endOfBodyData length]] < 0) totalBytesReceived = -1; else totalBytesReceived += [endOfBodyData length]; } if (totalBytesReceived < 0) { if (_debug) NSLog(@"[BBOBEXGetRequest] error writing to output stream"); return; } // read length (in initial headers) - ok if zero (key not present) mTotalGetLength = [mResponseHeaders valueForLengthHeader]; if (totalBytesReceived > 0) { if ([[mClient delegate] respondsToSelector:@selector(client:didReceiveDataOfLength:ofTotalLength:)]) { [[mClient delegate] client:mClient didReceiveDataOfLength:totalBytesReceived ofTotalLength:mTotalGetLength]; } } } - (OBEXError)sendNextRequestPacket { // Previous GET request packet was successful, and we need to send another // packet to get more data. return [mSession OBEXGet:YES headers:NULL headersLength:0 eventSelector:mClientEventSelector selectorTarget:mClient refCon:NULL]; } - (void)finishedWithError:(OBEXError)error responseCode:(int)responseCode { if (_debug) NSLog(@"[BBOBEXGetRequest] finishedWithError: %d", error); mFinished = YES; if ([[mClient delegate] respondsToSelector:@selector(client:didFinishGetRequestForStream:error:response:)]) { [[mClient delegate] client:mClient didFinishGetRequestForStream:mOutputStream error:error response:[BBOBEXResponse responseWithCode:responseCode headers:mResponseHeaders]]; } } - (BOOL)readOBEXResponseHeaders:(BBMutableOBEXHeaderSet **)responseHeaders andResponseCode:(int *)responseCode fromSessionEvent:(const OBEXSessionEvent *)event { if (event->type != kOBEXSessionEventTypeGetCommandResponseReceived) return NO; const OBEXGetCommandResponseData *resp = &event->u.getCommandResponseData; *responseCode = resp->serverResponseOpCode; BBMutableOBEXHeaderSet *headers = [BBMutableOBEXHeaderSet headerSet]; if ([headers addHeadersFromHeadersData:resp->headerDataPtr length:resp->headerDataLength]) { *responseHeaders = headers; } return YES; } - (void)dealloc { if (mHeadersDataRef) CFRelease(mHeadersDataRef); [mOutputStream release]; [super dealloc]; } @end @implementation BBOBEXSetPathRequest - (id)initWithClient:(BBBluetoothOBEXClient *)client eventSelector:(SEL)selector session:(OBEXSession *)session changeToParentDirectoryFirst:(BOOL)changeToParentDirectoryFirst createDirectoriesIfNeeded:(BOOL)createDirectoriesIfNeeded { self = [super initWithClient:client eventSelector:selector session:session]; mRequestFlags = 0; if (changeToParentDirectoryFirst) mRequestFlags |= 1; if (!createDirectoriesIfNeeded) mRequestFlags |= 2; return self; } - (OBEXError)beginWithHeaders:(BBOBEXHeaderSet *)headers { if (_debug) NSLog(@"[BBOBEXSetPathRequest] beginWithHeaders (%d headers)", [headers count]); CFMutableDataRef bytes = (CFMutableDataRef)[headers toBytes]; if (!bytes && [headers count] > 0) return kOBEXInternalError; OBEXError status = [mSession OBEXSetPath:mRequestFlags constants:0 optionalHeaders:(bytes ? (void *)CFDataGetBytePtr(bytes) : NULL) optionalHeadersLength:(bytes ? CFDataGetLength(bytes) : 0) eventSelector:mClientEventSelector selectorTarget:mClient refCon:NULL]; if (bytes) mHeadersDataRef = bytes; return status; } - (void)finishedWithError:(OBEXError)error responseCode:(int)responseCode { if (_debug) NSLog(@"[BBOBEXSetPathRequest] finishedWithError: %d", error); mFinished = YES; if ([[mClient delegate] respondsToSelector:@selector(client:didFinishSetPathRequestWithError:response:)]) { [[mClient delegate] client:mClient didFinishSetPathRequestWithError:error response:[BBOBEXResponse responseWithCode:responseCode headers:mResponseHeaders]]; } } - (BOOL)readOBEXResponseHeaders:(BBMutableOBEXHeaderSet **)responseHeaders andResponseCode:(int *)responseCode fromSessionEvent:(const OBEXSessionEvent *)event { if (event->type != kOBEXSessionEventTypeSetPathCommandResponseReceived) return NO; const OBEXSetPathCommandResponseData *resp = &event->u.setPathCommandResponseData; *responseCode = resp->serverResponseOpCode; BBMutableOBEXHeaderSet *headers = [BBMutableOBEXHeaderSet headerSet]; if ([headers addHeadersFromHeadersData:resp->headerDataPtr length:resp->headerDataLength]) { *responseHeaders = headers; } return YES; } - (void)dealloc { if (mHeadersDataRef) CFRelease(mHeadersDataRef); [super dealloc]; } @end @implementation BBOBEXAbortRequest - (id)initWithClient:(BBBluetoothOBEXClient *)client eventSelector:(SEL)selector session:(OBEXSession *)session currentRequestStream:(NSStream *)stream { self = [super initWithClient:client eventSelector:selector session:session]; mStream = [stream retain]; return self; } - (OBEXError)beginWithHeaders:(BBOBEXHeaderSet *)headers { if (_debug) NSLog(@"[BBOBEXAbortRequest] beginWithHeaders (%d headers)", [headers count]); CFMutableDataRef bytes = (CFMutableDataRef)[headers toBytes]; if (!bytes && [headers count] > 0) return kOBEXInternalError; return [mSession OBEXAbort:(bytes ? (void *)CFDataGetBytePtr(bytes) : NULL) optionalHeadersLength:(bytes ? CFDataGetLength(bytes) : 0) eventSelector:mClientEventSelector selectorTarget:mClient refCon:NULL]; } - (void)finishedWithError:(OBEXError)error responseCode:(int)responseCode { if (_debug) NSLog(@"[BBOBEXAbortRequest] finishedWithError: %d", error); mFinished = YES; if ([[mClient delegate] respondsToSelector:@selector(client:didAbortRequestWithStream:error:response:)]) { [[mClient delegate] client:mClient didAbortRequestWithStream:mStream error:error response:[BBOBEXResponse responseWithCode:responseCode headers:mResponseHeaders]]; } } - (BOOL)readOBEXResponseHeaders:(BBMutableOBEXHeaderSet **)responseHeaders andResponseCode:(int *)responseCode fromSessionEvent:(const OBEXSessionEvent *)event { if (event->type != kOBEXSessionEventTypeAbortCommandResponseReceived) return NO; const OBEXAbortCommandResponseData *resp = &event->u.abortCommandResponseData; *responseCode = resp->serverResponseOpCode; BBMutableOBEXHeaderSet *headers = [BBMutableOBEXHeaderSet headerSet]; if ([headers addHeadersFromHeadersData:resp->headerDataPtr length:resp->headerDataLength]) { *responseHeaders = headers; } return YES; } - (void)dealloc { [mStream release]; [super dealloc]; } @end lightblue-0.3.2/src/mac/LightAquaBlue/BBOBEXRequestHandler.h0000644000076500007650000000732310747224731023020 0ustar beabea00000000000000/* * Copyright (c) 2006 Bea Lam. All rights reserved. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation files * (the "Software"), to deal in the Software without restriction, * including without limitation the rights to use, copy, modify, merge, * publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ // // BBOBEXRequestHandler.h // LightAquaBlue // // These are internal classes used by BBBluetoothOBEXServer for handling // incoming client requests. Each BBOBEXRequestHandler subclass encapsulates // the process for handling a particular type of request. // #import #import @class BBBluetoothOBEXServer; @class BBOBEXHeaderSet; @class BBMutableOBEXHeaderSet; @class OBEXSession; @interface BBOBEXRequestHandler : NSObject { BBBluetoothOBEXServer *mServer; SEL mServerEventSelector; OBEXSession *mSession; int mNextResponseCode; BBMutableOBEXHeaderSet *mNextResponseHeaders; } + (void)setDebug:(BOOL)debug; - (id)initWithServer:(BBBluetoothOBEXServer *)server eventSelector:(SEL)selector session:(OBEXSession *)session; - (void)setNextResponseCode:(int)responseCode; - (void)addResponseHeaders:(BBOBEXHeaderSet *)responseHeaders; - (BOOL)handleRequestEvent:(const OBEXSessionEvent *)event; /*** for subclasses - calls errorOccurred:description: on delegate ***/ - (void)errorOccurred:(OBEXError)error description:(NSString *)description; /*** methods below must be overriden by subclasses, and should be regarded as 'protected' - they don't need to be called by outside classes ***/ - (BOOL)readOBEXRequestHeaders:(BBMutableOBEXHeaderSet **)requestHeaders andRequestFlags:(OBEXFlags *)flags fromSessionEvent:(const OBEXSessionEvent *)event; - (void)prepareResponseForRequestWithHeaders:(BBMutableOBEXHeaderSet *)requestHeaders flags:(OBEXFlags)flags isFinalRequestPacket:(BOOL)isFinalRequestPacket; - (OBEXError)sendNextResponsePacket; - (void)handleRequestAborted; - (void)notifyRequestFinished; @end @interface BBOBEXConnectRequestHandler : BBOBEXRequestHandler { CFMutableDataRef mHeadersDataRef; OBEXMaxPacketLength mMaxPacketLength; } @end @interface BBOBEXDisconnectRequestHandler : BBOBEXRequestHandler { CFMutableDataRef mHeadersDataRef; } @end @interface BBOBEXPutRequestHandler : BBOBEXRequestHandler { CFMutableDataRef mHeadersDataRef; BBMutableOBEXHeaderSet *mPreviousRequestHeaders; NSOutputStream *mOutputStream; BOOL mDefinitelyIsPut; BOOL mAborted; } @end @interface BBOBEXGetRequestHandler : BBOBEXRequestHandler { CFMutableDataRef mHeadersDataRef; NSMutableData *mSentBodyData; NSInputStream *mInputStream; BOOL mAborted; } @end @interface BBOBEXSetPathRequestHandler : BBOBEXRequestHandler { CFMutableDataRef mHeadersDataRef; } @end lightblue-0.3.2/src/mac/LightAquaBlue/._BBOBEXRequestHandler.m0000644000076500000000000000012210750746127023565 0ustar beawheel00000000000000Mac OS X  2 RTEXTlightblue-0.3.2/src/mac/LightAquaBlue/BBOBEXRequestHandler.m0000644000076500007650000007604710750746127023040 0ustar beabea00000000000000// // BBOBEXRequestHandler.m // LightAquaBlue // // Created by Bea on 3/10/07. // Copyright 2007 __MyCompanyName__. All rights reserved. // #import "BBOBEXRequestHandler.h" #import "BBBluetoothOBEXServer.h" #import "BBMutableOBEXHeaderSet.h" #import static BOOL _debug = NO; @implementation BBOBEXRequestHandler + (void)setDebug:(BOOL)debug { _debug = debug; } - (id)initWithServer:(BBBluetoothOBEXServer *)server eventSelector:(SEL)selector session:(OBEXSession *)session { self = [super init]; mServer = server; // don't retain mServerEventSelector = selector; mSession = session; // don't retain mNextResponseCode = -1; return self; } - (void)setNextResponseCode:(int)responseCode; { mNextResponseCode = responseCode; } - (int)nextResponseCode { return mNextResponseCode; } - (void)addResponseHeaders:(BBOBEXHeaderSet *)responseHeaders { if (!mNextResponseHeaders) mNextResponseHeaders = [[BBMutableOBEXHeaderSet alloc] init]; [mNextResponseHeaders addHeadersFromHeaderSet:responseHeaders]; } /* Returns whether the request has finished (i.e. sent the final response, or error occurred while sending a response). */ - (BOOL)handleRequestEvent:(const OBEXSessionEvent *)event { if (_debug) NSLog(@"[BBOBEXRequestHandler] handleRequestEvent (type=%d)", event->type); BBMutableOBEXHeaderSet *requestHeaders = nil; OBEXFlags requestFlags = 0; if (mNextResponseHeaders) { [mNextResponseHeaders release]; mNextResponseHeaders = nil; } // read the client request if (_debug) NSLog(@"[BBOBEXRequestHandler] handleRequestEvent read headers"); if (![self readOBEXRequestHeaders:&requestHeaders andRequestFlags:&requestFlags fromSessionEvent:event]) { [self errorOccurred:kOBEXInternalError description:@"Wrong request handler assigned to read request headers!"]; // OR we got a new request before the last one finished??? mNextResponseCode = kOBEXResponseCodeInternalServerErrorWithFinalBit; [self sendNextResponsePacket]; [self notifyRequestFinished]; return YES; } if (!requestHeaders) { [self errorOccurred:kOBEXInternalError description:@"Can't read request headers!"]; mNextResponseCode = kOBEXResponseCodeBadRequestWithFinalBit; [self sendNextResponsePacket]; [self notifyRequestFinished]; return YES; } // get the response for this request if (_debug) NSLog(@"[BBOBEXRequestHandler] handleRequestEvent getting response"); [self prepareResponseForRequestWithHeaders:requestHeaders flags:requestFlags isFinalRequestPacket:event->isEndOfEventData]; // send the response if (_debug) NSLog(@"[BBOBEXRequestHandler] handleRequestEvent sending response 0x%x", mNextResponseCode); OBEXError status = [self sendNextResponsePacket]; if (status != kOBEXSuccess) { [self errorOccurred:status description:@"Error sending server response"]; return YES; } if (mNextResponseCode != kOBEXResponseCodeContinueWithFinalBit) { [self notifyRequestFinished]; return YES; } return NO; } - (void)errorOccurred:(OBEXError)error description:(NSString *)description { if ([[mServer delegate] respondsToSelector:@selector(server:errorOccurred:description:)]) { [[mServer delegate] server:mServer errorOccurred:error description:description]; } } /*** methods below are to be overriden ***/ - (BOOL)readOBEXRequestHeaders:(BBMutableOBEXHeaderSet **)requestHeaders andRequestFlags:(OBEXFlags *)flags fromSessionEvent:(const OBEXSessionEvent *)event { return NO; } - (void)prepareResponseForRequestWithHeaders:(BBMutableOBEXHeaderSet *)requestHeaders flags:(OBEXFlags)flags isFinalRequestPacket:(BOOL)isFinalRequestPacket { mNextResponseCode = kOBEXResponseCodeInternalServerErrorWithFinalBit; } - (void)handleRequestAborted { [mSession OBEXAbortResponse:kOBEXResponseCodeSuccessWithFinalBit optionalHeaders:NULL optionalHeadersLength:0 eventSelector:mServerEventSelector selectorTarget:mServer refCon:NULL]; } - (OBEXError)sendNextResponsePacket { return kOBEXInternalError; } - (void)notifyRequestFinished { } - (void)dealloc { [mNextResponseHeaders release]; [super dealloc]; } @end @implementation BBOBEXConnectRequestHandler - (BOOL)readOBEXRequestHeaders:(BBMutableOBEXHeaderSet **)requestHeaders andRequestFlags:(OBEXFlags *)flags fromSessionEvent:(const OBEXSessionEvent *)event { if (_debug) NSLog(@"[BBOBEXConnectRequestHandler] readOBEXRequestHeaders"); if (event->type != kOBEXSessionEventTypeConnectCommandReceived) return NO; const OBEXConnectCommandData *request = &event->u.connectCommandData; *flags = request->flags; BBMutableOBEXHeaderSet *headers = [BBMutableOBEXHeaderSet headerSet]; if ([headers addHeadersFromHeadersData:request->headerDataPtr length:request->headerDataLength]) { *requestHeaders = headers; } // note the request max packet size mMaxPacketLength = request->maxPacketSize; return YES; } - (void)prepareResponseForRequestWithHeaders:(BBMutableOBEXHeaderSet *)requestHeaders flags:(OBEXFlags)flags isFinalRequestPacket:(BOOL)isFinalRequestPacket { if (_debug) NSLog(@"[BBOBEXConnectRequestHandler] prepareResponseForRequestWithHeaders"); if (![[mServer delegate] respondsToSelector:@selector(server:shouldHandleConnectRequest:)]) { mNextResponseCode = kOBEXResponseCodeNotImplementedWithFinalBit; return; } // ask delegate to accept/deny request and set the response code & headers mNextResponseCode = -1; BOOL accept = [[mServer delegate] server:mServer shouldHandleConnectRequest:requestHeaders]; if (mNextResponseCode == -1) { mNextResponseCode = (accept ? kOBEXResponseCodeSuccessWithFinalBit : kOBEXResponseCodeForbiddenWithFinalBit); } } - (OBEXError)sendNextResponsePacket { if (_debug) NSLog(@"[BBOBEXConnectRequestHandler] sendNextResponsePacket"); CFMutableDataRef bytes = NULL; if (mNextResponseHeaders) { bytes = (CFMutableDataRef)[mNextResponseHeaders toBytes]; if (!bytes && [mNextResponseHeaders count] > 0) return kOBEXInternalError; } OBEXError status = [mSession OBEXConnectResponse:mNextResponseCode flags:0 maxPacketLength:mMaxPacketLength optionalHeaders:(bytes ? (void *)CFDataGetBytePtr(bytes) : NULL) optionalHeadersLength:(bytes ? CFDataGetLength(bytes) : 0) eventSelector:mServerEventSelector selectorTarget:mServer refCon:NULL]; if (bytes) mHeadersDataRef = bytes; return status; } - (void)notifyRequestFinished { if (_debug) NSLog(@"[BBOBEXConnectRequestHandler] notifyRequestFinished"); if ([[mServer delegate] respondsToSelector:@selector(serverDidHandleConnectRequest:)]) { [[mServer delegate] serverDidHandleConnectRequest:mServer]; } } - (void)dealloc { if (mHeadersDataRef) CFRelease(mHeadersDataRef); [super dealloc]; } @end @implementation BBOBEXDisconnectRequestHandler - (BOOL)readOBEXRequestHeaders:(BBMutableOBEXHeaderSet **)requestHeaders andRequestFlags:(OBEXFlags *)flags fromSessionEvent:(const OBEXSessionEvent *)event { if (_debug) NSLog(@"[BBOBEXDisconnectRequestHandler] readOBEXRequestHeaders"); if (event->type != kOBEXSessionEventTypeDisconnectCommandReceived) return NO; const OBEXDisconnectCommandData *request = &event->u.disconnectCommandData; BBMutableOBEXHeaderSet *headers = [BBMutableOBEXHeaderSet headerSet]; if ([headers addHeadersFromHeadersData:request->headerDataPtr length:request->headerDataLength]) { *requestHeaders = headers; } return YES; } - (void)prepareResponseForRequestWithHeaders:(BBMutableOBEXHeaderSet *)requestHeaders flags:(OBEXFlags)flags isFinalRequestPacket:(BOOL)isFinalRequestPacket { if (_debug) NSLog(@"[BBOBEXDisconnectRequestHandler] prepareResponseForRequestWithHeaders"); if (![[mServer delegate] respondsToSelector: @selector(server:shouldHandleDisconnectRequest:)]) { mNextResponseCode = kOBEXResponseCodeNotImplementedWithFinalBit; return; } // ask delegate to accept/deny request and set the response code & headers mNextResponseCode = -1; BOOL accept = [[mServer delegate] server:mServer shouldHandleDisconnectRequest:requestHeaders]; if (mNextResponseCode == -1) { mNextResponseCode = (accept ? kOBEXResponseCodeSuccessWithFinalBit : kOBEXResponseCodeForbiddenWithFinalBit); } } - (OBEXError)sendNextResponsePacket { if (_debug) NSLog(@"[BBOBEXDisconnectRequestHandler] sendNextResponsePacket"); CFMutableDataRef bytes = NULL; if (mNextResponseHeaders) { bytes = (CFMutableDataRef)[mNextResponseHeaders toBytes]; if (!bytes && [mNextResponseHeaders count] > 0) return kOBEXInternalError; } OBEXError status = [mSession OBEXDisconnectResponse:mNextResponseCode optionalHeaders:(bytes ? (void *)CFDataGetBytePtr(bytes) : NULL) optionalHeadersLength:(bytes ? CFDataGetLength(bytes) : 0) eventSelector:mServerEventSelector selectorTarget:mServer refCon:NULL]; if (bytes) mHeadersDataRef = bytes; return status; } - (void)notifyRequestFinished { if ([[mServer delegate] respondsToSelector:@selector(serverDidHandleDisconnectRequest:)]) { [[mServer delegate] serverDidHandleDisconnectRequest:mServer]; } } - (void)dealloc { if (mHeadersDataRef) CFRelease(mHeadersDataRef); [super dealloc]; } @end @implementation BBOBEXPutRequestHandler - (id)initWithServer:(BBBluetoothOBEXServer *)server eventSelector:(SEL)selector session:(OBEXSession *)session { self = [super initWithServer:server eventSelector:selector session:session]; mDefinitelyIsPut = NO; mAborted = NO; return self; } - (BOOL)readOBEXRequestHeaders:(BBMutableOBEXHeaderSet **)requestHeaders andRequestFlags:(OBEXFlags *)flags fromSessionEvent:(const OBEXSessionEvent *)event { if (_debug) NSLog(@"[BBOBEXPutRequestHandler] readOBEXRequestHeaders"); if (event->type != kOBEXSessionEventTypePutCommandReceived) return NO; const OBEXPutCommandData *request = &event->u.putCommandData; BBMutableOBEXHeaderSet *headers = [BBMutableOBEXHeaderSet headerSet]; if ([headers addHeadersFromHeadersData:request->headerDataPtr length:request->headerDataLength]) { *requestHeaders = headers; } return YES; } - (void)preparePutResponseForRequestWithHeaders:(BBMutableOBEXHeaderSet *)requestHeaders isFinalRequestPacket:(BOOL)isFinalRequestPacket { if (_debug) NSLog(@"[BBOBEXPutRequestHandler] preparePutResponseForRequestWithHeaders"); // don't pass body & end of body headers onto delegate NSData *bodyData = [requestHeaders valueForByteSequenceHeader:kOBEXHeaderIDBody]; NSData *endOfBodyData = [requestHeaders valueForByteSequenceHeader:kOBEXHeaderIDEndOfBody]; [requestHeaders removeValueForHeader:kOBEXHeaderIDBody]; [requestHeaders removeValueForHeader:kOBEXHeaderIDEndOfBody]; if (!mOutputStream) { if (![[mServer delegate] respondsToSelector:@selector(server:shouldHandlePutRequest:)]) { mNextResponseCode = kOBEXResponseCodeNotImplementedWithFinalBit; return; } // ask delegate to accept/deny request and set the response code & headers BBOBEXHeaderSet *allRequestHeaders; if (mPreviousRequestHeaders) { [mPreviousRequestHeaders addHeadersFromHeaderSet:requestHeaders]; allRequestHeaders = mPreviousRequestHeaders; } else { allRequestHeaders = requestHeaders; } mNextResponseCode = -1; NSOutputStream *outputStream = [[mServer delegate] server:mServer shouldHandlePutRequest:allRequestHeaders]; [mPreviousRequestHeaders release]; mPreviousRequestHeaders = nil; // see if delegate accepted request BOOL accept = (outputStream != nil); if (mNextResponseCode == -1) { if (accept) { mNextResponseCode = (isFinalRequestPacket ? kOBEXResponseCodeSuccessWithFinalBit : kOBEXResponseCodeContinueWithFinalBit); } else { mNextResponseCode = kOBEXResponseCodeForbiddenWithFinalBit; } } // delegate refused request? if (mNextResponseCode != kOBEXResponseCodeContinueWithFinalBit && mNextResponseCode != kOBEXResponseCodeSuccessWithFinalBit) { outputStream = nil; return; } if (!outputStream) { [self errorOccurred:kOBEXInternalError description:@"Put request error: delegate accepted request but returned nil NSOutputStream"]; mNextResponseCode = kOBEXResponseCodeInternalServerErrorWithFinalBit; return; } if ([outputStream streamStatus] != NSStreamStatusOpen) { [self errorOccurred:kOBEXInternalError description:@"Put request error: output stream specified by delegate must be opened"]; mNextResponseCode = kOBEXResponseCodeInternalServerErrorWithFinalBit; return; } mOutputStream = [outputStream retain]; } int dataLength = 0; if (bodyData) { if ([mOutputStream write:[bodyData bytes] maxLength:[bodyData length]] < 0) dataLength = -1; else dataLength += [bodyData length]; } if (endOfBodyData && dataLength != -1) { if ([mOutputStream write:[endOfBodyData bytes] maxLength:[endOfBodyData length]] < 0) dataLength = -1; else dataLength += [endOfBodyData length]; } if (dataLength < 0) { [self errorOccurred:kOBEXGeneralError description:@"Put request error: can't write body data to output stream"]; mNextResponseCode = kOBEXResponseCodeInternalServerErrorWithFinalBit; return; } // will notify delegate even if data length is zero if ([[mServer delegate] respondsToSelector:@selector(server:didReceiveDataOfLength:isLastPacket:)]) { [[mServer delegate] server:mServer didReceiveDataOfLength:dataLength isLastPacket:isFinalRequestPacket]; } if (mNextResponseCode == -1 || mNextResponseCode == kOBEXResponseCodeContinueWithFinalBit || mNextResponseCode == kOBEXResponseCodeSuccessWithFinalBit) { mNextResponseCode = (isFinalRequestPacket ? kOBEXResponseCodeSuccessWithFinalBit : kOBEXResponseCodeContinueWithFinalBit); } } - (void)preparePutDeleteResponseForRequestWithHeaders:(BBOBEXHeaderSet *)requestHeaders { if (_debug) NSLog(@"[BBOBEXPutRequestHandler] preparePutDeleteResponseForRequestWithHeaders"); if (![[mServer delegate] respondsToSelector:@selector(server:shouldHandlePutDeleteRequest:)]) { mNextResponseCode = kOBEXResponseCodeNotImplementedWithFinalBit; return; } // ask delegate to accept/deny request and set the response code & headers BBOBEXHeaderSet *allRequestHeaders; if (mPreviousRequestHeaders) { [mPreviousRequestHeaders addHeadersFromHeaderSet:requestHeaders]; allRequestHeaders = mPreviousRequestHeaders; } else { allRequestHeaders = requestHeaders; } BOOL accept = [[mServer delegate] server:mServer shouldHandlePutDeleteRequest:allRequestHeaders]; [mPreviousRequestHeaders release]; mPreviousRequestHeaders = nil; if (mNextResponseCode == -1 || mNextResponseCode == kOBEXResponseCodeContinueWithFinalBit || mNextResponseCode == kOBEXResponseCodeSuccessWithFinalBit) { mNextResponseCode = (accept ? kOBEXResponseCodeSuccessWithFinalBit : kOBEXResponseCodeForbiddenWithFinalBit); } } - (void)prepareResponseForRequestWithHeaders:(BBMutableOBEXHeaderSet *)requestHeaders flags:(OBEXFlags)flags isFinalRequestPacket:(BOOL)isFinalRequestPacket { if (_debug) NSLog(@"[BBOBEXPutRequestHandler] prepareResponseForRequestWithHeaders"); BOOL hasBodyData = ( requestHeaders && ([requestHeaders containsValueForHeader:kOBEXHeaderIDBody] || [requestHeaders containsValueForHeader:kOBEXHeaderIDEndOfBody]) ); if (hasBodyData) mDefinitelyIsPut = YES; if (mDefinitelyIsPut) { [self preparePutResponseForRequestWithHeaders:requestHeaders isFinalRequestPacket:isFinalRequestPacket]; } else { if (isFinalRequestPacket) { [self preparePutDeleteResponseForRequestWithHeaders:requestHeaders]; } else { if (_debug) NSLog(@"[BBOBEXPutRequestHandler] don't know if it's a Put or Put-Delete, so just continue"); mNextResponseCode = kOBEXResponseCodeContinueWithFinalBit; // keep request headers so they can be passed to delegate later if (!mPreviousRequestHeaders) mPreviousRequestHeaders = [[BBMutableOBEXHeaderSet alloc] init]; [mPreviousRequestHeaders addHeadersFromHeaderSet:requestHeaders]; } } } - (OBEXError)sendNextResponsePacket { if (_debug) NSLog(@"[BBOBEXPutRequestHandler] sendNextResponsePacket"); CFMutableDataRef bytes = NULL; if (mNextResponseHeaders) { bytes = (CFMutableDataRef)[mNextResponseHeaders toBytes]; if (!bytes && [mNextResponseHeaders count] > 0) return kOBEXInternalError; } OBEXError status = [mSession OBEXPutResponse:mNextResponseCode optionalHeaders:(bytes ? (void *)CFDataGetBytePtr(bytes) : NULL) optionalHeadersLength:(bytes ? CFDataGetLength(bytes) : 0) eventSelector:mServerEventSelector selectorTarget:mServer refCon:NULL]; if (bytes) { if (mHeadersDataRef) CFRelease(mHeadersDataRef); mHeadersDataRef = bytes; } return status; } - (void)handleRequestAborted { if (_debug) NSLog(@"[BBOBEXPutRequestHandler] handleRequestAborted"); mAborted = YES; mDefinitelyIsPut = YES; // can't abort Delete ops, so this must be a Put [super handleRequestAborted]; [self notifyRequestFinished]; } - (void)notifyRequestFinished { if (_debug) NSLog(@"[BBOBEXPutRequestHandler] notifyRequestFinished"); if (mDefinitelyIsPut) { if ([[mServer delegate] respondsToSelector:@selector(server:didHandlePutRequestForStream:requestWasAborted:)]) { [[mServer delegate] server:mServer didHandlePutRequestForStream:mOutputStream requestWasAborted:mAborted]; } } else { if ([[mServer delegate] respondsToSelector:@selector(serverDidHandlePutDeleteRequest:)]) { [[mServer delegate] serverDidHandlePutDeleteRequest:mServer]; } } [mOutputStream release]; mOutputStream = nil; mDefinitelyIsPut = NO; mAborted = NO; } - (void)dealloc { if (mHeadersDataRef) CFRelease(mHeadersDataRef); [mPreviousRequestHeaders release]; [mOutputStream release]; [super dealloc]; } @end @implementation BBOBEXGetRequestHandler - (id)initWithServer:(BBBluetoothOBEXServer *)server eventSelector:(SEL)selector session:(OBEXSession *)session { self = [super initWithServer:server eventSelector:selector session:session]; mAborted = NO; return self; } - (BOOL)readOBEXRequestHeaders:(BBMutableOBEXHeaderSet **)requestHeaders andRequestFlags:(OBEXFlags *)flags fromSessionEvent:(const OBEXSessionEvent *)event { if (_debug) NSLog(@"[BBOBEXGetRequestHandler] readOBEXRequestHeaders"); if (event->type != kOBEXSessionEventTypeGetCommandReceived) return NO; const OBEXGetCommandData *request = &event->u.getCommandData; BBMutableOBEXHeaderSet *headers = [BBMutableOBEXHeaderSet headerSet]; if ([headers addHeadersFromHeadersData:request->headerDataPtr length:request->headerDataLength]) { *requestHeaders = headers; } return YES; } - (NSMutableData *)readNextChunkForHeaderLength:(size_t)headersLength isLastChunk:(BOOL *)outIsLastChunk { if (_debug) NSLog(@"[BBOBEXGetRequestHandler] readNextChunkForHeaderLength"); if (mInputStream == nil) return nil; OBEXMaxPacketLength maxPacketSize = [mSession getAvailableCommandPayloadLength:kOBEXOpCodePut]; if (maxPacketSize == 0 || headersLength > maxPacketSize) return nil; OBEXMaxPacketLength maxBodySize = maxPacketSize - headersLength; NSMutableData *data = [NSMutableData dataWithLength:maxBodySize]; int len = [mInputStream read:[data mutableBytes] maxLength:maxBodySize]; if (_debug) NSLog(@"[BBOBEXGetRequestHandler] read %d bytes (maxBodySize = %d)", len, maxBodySize); // is last packet if there wasn't enough body data to fill up the packet if (len >= 0) *outIsLastChunk = (len < maxBodySize); [data setLength:len]; return data; } - (void)prepareResponseForRequestWithHeaders:(BBMutableOBEXHeaderSet *)requestHeaders flags:(OBEXFlags)flags isFinalRequestPacket:(BOOL)isFinalRequestPacket { if (_debug) NSLog(@"[BBOBEXGetRequestHandler] prepareResponseForRequestWithHeaders"); if (!mInputStream) { if (![[mServer delegate] respondsToSelector:@selector(server:shouldHandleGetRequest:)]) { mNextResponseCode = kOBEXResponseCodeNotImplementedWithFinalBit; return; } // ask delegate to accept/deny request and set the response code & headers mNextResponseCode = -1; NSInputStream *inputStream = [[mServer delegate] server:mServer shouldHandleGetRequest:requestHeaders]; BOOL accept = (inputStream != nil); if (mNextResponseCode == -1) { mNextResponseCode = (accept ? kOBEXResponseCodeSuccessWithFinalBit : kOBEXResponseCodeForbiddenWithFinalBit); } // delegate refused request? if (mNextResponseCode != kOBEXResponseCodeContinueWithFinalBit && mNextResponseCode != kOBEXResponseCodeSuccessWithFinalBit) { inputStream = nil; return; } if (!inputStream) { [self errorOccurred:kOBEXInternalError description:@"Get request error: delegate accepted request but returned nil NSInputStream"]; mNextResponseCode = kOBEXResponseCodeInternalServerErrorWithFinalBit; return; } if ([inputStream streamStatus] != NSStreamStatusOpen) { [self errorOccurred:kOBEXInternalError description:@"Get request error: input stream specified by delegate must be opened"]; mNextResponseCode = kOBEXResponseCodeInternalServerErrorWithFinalBit; return; } mInputStream = [inputStream retain]; } if (!mNextResponseHeaders) mNextResponseHeaders = [[BBMutableOBEXHeaderSet alloc] init]; CFMutableDataRef tempHeaderBytes = (CFMutableDataRef)[mNextResponseHeaders toBytes]; int currentHeaderLength = 0; if (tempHeaderBytes) { currentHeaderLength = CFDataGetLength(tempHeaderBytes); CFRelease(tempHeaderBytes); } BOOL isLastChunk = YES; // the GET headers seem to need 3 bytes padding, maybe for the response code // plus packet length in the headers. NSMutableData *mutableData = [self readNextChunkForHeaderLength:currentHeaderLength + 3 isLastChunk:&isLastChunk]; if (!mutableData) { [self errorOccurred:kOBEXInternalError description:@"Get request error: can't read data from input stream"]; mNextResponseCode = kOBEXResponseCodeInternalServerErrorWithFinalBit; return; } uint8_t headerID = (isLastChunk ? kOBEXHeaderIDBody : kOBEXHeaderIDEndOfBody); [mNextResponseHeaders setValue:mutableData forByteSequenceHeader:headerID]; if (![mNextResponseHeaders containsValueForHeader:headerID]) { [self errorOccurred:kOBEXInternalError description:@"Get request error: can't add body data to response headers"]; mNextResponseCode = kOBEXResponseCodeInternalServerErrorWithFinalBit; return; } [mutableData retain]; [mSentBodyData release]; mSentBodyData = mutableData; if (mNextResponseCode == -1 || mNextResponseCode == kOBEXResponseCodeContinueWithFinalBit || mNextResponseCode == kOBEXResponseCodeSuccessWithFinalBit) { if (isLastChunk) mNextResponseCode = kOBEXResponseCodeSuccessWithFinalBit; else mNextResponseCode = kOBEXResponseCodeContinueWithFinalBit; } } - (OBEXError)sendNextResponsePacket { if (_debug) NSLog(@"[BBOBEXGetRequestHandler] sendNextResponsePacket"); CFMutableDataRef bytes = NULL; if (mNextResponseHeaders) { bytes = (CFMutableDataRef)[mNextResponseHeaders toBytes]; if (!bytes && [mNextResponseHeaders count] > 0) return kOBEXInternalError; } OBEXError status = [mSession OBEXGetResponse:mNextResponseCode optionalHeaders:(bytes ? (void *)CFDataGetBytePtr(bytes) : NULL) optionalHeadersLength:(bytes ? CFDataGetLength(bytes) : 0) eventSelector:mServerEventSelector selectorTarget:mServer refCon:NULL]; if (bytes) { if (mHeadersDataRef) CFRelease(mHeadersDataRef); mHeadersDataRef = bytes; } if (mSentBodyData && [[mServer delegate] respondsToSelector:@selector(server:didSendDataOfLength:)]) { [[mServer delegate] server:mServer didSendDataOfLength:[mSentBodyData length]]; } return status; } - (void)handleRequestAborted { if (_debug) NSLog(@"[BBOBEXGetRequestHandler] handleRequestAborted"); mAborted = YES; [super handleRequestAborted]; [self notifyRequestFinished]; } - (void)notifyRequestFinished { if ([[mServer delegate] respondsToSelector:@selector(server:didHandleGetRequestForStream:requestWasAborted:)]) { [[mServer delegate] server:mServer didHandleGetRequestForStream:mInputStream requestWasAborted:mAborted]; } [mInputStream release]; mInputStream = nil; mAborted = NO; } - (void)dealloc { if (mHeadersDataRef) CFRelease(mHeadersDataRef); [mSentBodyData release]; [mInputStream release]; [super dealloc]; } @end @implementation BBOBEXSetPathRequestHandler - (BOOL)readOBEXRequestHeaders:(BBMutableOBEXHeaderSet **)requestHeaders andRequestFlags:(OBEXFlags *)flags fromSessionEvent:(const OBEXSessionEvent *)event { if (_debug) NSLog(@"[BBOBEXSetPathRequestHandler] readOBEXRequestHeaders"); if (event->type != kOBEXSessionEventTypeSetPathCommandReceived) return NO; const OBEXSetPathCommandData *request = &event->u.setPathCommandData; *flags = request->flags; BBMutableOBEXHeaderSet *headers = [BBMutableOBEXHeaderSet headerSet]; if ([headers addHeadersFromHeadersData:request->headerDataPtr length:request->headerDataLength]) { *requestHeaders = headers; } return YES; } - (void)prepareResponseForRequestWithHeaders:(BBMutableOBEXHeaderSet *)requestHeaders flags:(OBEXFlags)flags isFinalRequestPacket:(BOOL)isFinalRequestPacket { if (_debug) NSLog(@"[BBOBEXSetPathRequestHandler] prepareResponseForRequestWithHeaders"); if (![[mServer delegate] respondsToSelector:@selector(server:shouldHandleSetPathRequest:withFlags:)]) { mNextResponseCode = kOBEXResponseCodeNotImplementedWithFinalBit; return; } //BOOL changeToParentDirectoryFirst = (flags & 1) != 0; //BOOL createDirectoriesIfNeeded = (flags & 2) == 0; // ask delegate to accept/deny request and set the response code & headers mNextResponseCode = -1; BOOL accept = [[mServer delegate] server:mServer shouldHandleSetPathRequest:requestHeaders withFlags:flags]; if (mNextResponseCode == -1) { mNextResponseCode = (accept ? kOBEXResponseCodeSuccessWithFinalBit : kOBEXResponseCodeForbiddenWithFinalBit); } } - (OBEXError)sendNextResponsePacket { if (_debug) NSLog(@"[BBOBEXSetPathRequestHandler] sendNextResponsePacket"); CFMutableDataRef bytes = NULL; if (mNextResponseHeaders) { bytes = (CFMutableDataRef)[mNextResponseHeaders toBytes]; if (!bytes && [mNextResponseHeaders count] > 0) return kOBEXInternalError; } OBEXError status = [mSession OBEXSetPathResponse:mNextResponseCode optionalHeaders:(bytes ? (void *)CFDataGetBytePtr(bytes) : NULL) optionalHeadersLength:(bytes ? CFDataGetLength(bytes) : 0) eventSelector:mServerEventSelector selectorTarget:mServer refCon:NULL]; if (bytes) mHeadersDataRef = bytes; return status; } - (void)notifyRequestFinished { if ([[mServer delegate] respondsToSelector:@selector(serverDidHandleSetPathRequest:)]) [[mServer delegate] serverDidHandleSetPathRequest:mServer]; } - (void)dealloc { if (mHeadersDataRef) CFRelease(mHeadersDataRef); [super dealloc]; } @endlightblue-0.3.2/src/mac/LightAquaBlue/BBOBEXResponse.h0000644000076500007650000000444610747223642021673 0ustar beabea00000000000000/* * Copyright (c) 2006 Bea Lam. All rights reserved. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation files * (the "Software"), to deal in the Software without restriction, * including without limitation the rights to use, copy, modify, merge, * publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ // // BBOBEXResponse.h // LightAquaBlue // // Contains the details for an OBEX server response. // // This is used by BBBluetoothOBEXClient to pass the details of an OBEX // server response to its delegate. // #import @class BBOBEXHeaderSet; @interface BBOBEXResponse : NSObject { int mCode; BBOBEXHeaderSet *mHeaders; } + (id)responseWithCode:(int)responseCode headers:(BBOBEXHeaderSet *)headers; - (id)initWithCode:(int)responseCode headers:(BBOBEXHeaderSet *)headers; /* * Returns the response code. * * Use the response codes listed in the OBEXOpCodeResponseValues enum in * to match against this response code. If the client * request was accepted by the OBEX server, this response code will be * kOBEXResponseCodeSuccessWithFinalBit. Otherwise, it will be set to one of * the other response codes that end with "WithFinalBit". */ - (int)responseCode; /* * Returns a string description of the response code. E.g. * kOBEXResponseCodeSuccessWithFinalBit translates to "Success". */ - (NSString *)responseCodeDescription; /* * Returns the response headers. */ - (BBOBEXHeaderSet *)allHeaders; @end lightblue-0.3.2/src/mac/LightAquaBlue/BBOBEXResponse.m0000644000076500007650000001346710747223642021703 0ustar beabea00000000000000/* * Copyright (c) 2006 Bea Lam. All rights reserved. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation files * (the "Software"), to deal in the Software without restriction, * including without limitation the rights to use, copy, modify, merge, * publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ // // BBOBEXResponse.m // LightAquaBlue // #import "BBOBEXResponse.h" #import "BBOBEXHeaderSet.h" @implementation BBOBEXResponse + (id)responseWithCode:(int)responseCode headers:(BBOBEXHeaderSet *)headers { return [[[BBOBEXResponse alloc] initWithCode:responseCode headers:headers] autorelease]; } - (id)initWithCode:(int)responseCode headers:(BBOBEXHeaderSet *)headers { self = [super init]; mCode = responseCode; mHeaders = [headers retain]; return self; } - (int)responseCode { return mCode; } - (NSString *)responseCodeDescription { switch (mCode) { case kOBEXResponseCodeContinueWithFinalBit: return @"Continue"; case kOBEXResponseCodeSuccessWithFinalBit: return @"Success"; case kOBEXResponseCodeCreatedWithFinalBit: return @"Created"; case kOBEXResponseCodeAcceptedWithFinalBit: return @"Accepted"; case kOBEXResponseCodeNonAuthoritativeInfoWithFinalBit: return @"Non-authoritative info"; case kOBEXResponseCodeNoContentWithFinalBit: return @"No content"; case kOBEXResponseCodeResetContentWithFinalBit: return @"Reset content"; case kOBEXResponseCodePartialContentWithFinalBit: return @"Partial content"; case kOBEXResponseCodeMultipleChoicesWithFinalBit: return @"Multiple choices"; case kOBEXResponseCodeMovedPermanentlyWithFinalBit: return @"Moved permanently"; case kOBEXResponseCodeMovedTemporarilyWithFinalBit: return @"Moved temporarily"; case kOBEXResponseCodeSeeOtherWithFinalBit: return @"See other"; case kOBEXResponseCodeNotModifiedWithFinalBit: return @"Code not modified"; case kOBEXResponseCodeUseProxyWithFinalBit: return @"Use proxy"; case kOBEXResponseCodeBadRequestWithFinalBit: return @"Bad request"; case kOBEXResponseCodeUnauthorizedWithFinalBit: return @"Unauthorized"; case kOBEXResponseCodePaymentRequiredWithFinalBit: return @"Payment required"; case kOBEXResponseCodeForbiddenWithFinalBit: return @"Forbidden"; case kOBEXResponseCodeNotFoundWithFinalBit: return @"Not found"; case kOBEXResponseCodeMethodNotAllowedWithFinalBit: return @"Method not allowed"; case kOBEXResponseCodeNotAcceptableWithFinalBit: return @"Not acceptable"; case kOBEXResponseCodeProxyAuthenticationRequiredWithFinalBit: return @"Proxy authentication required"; case kOBEXResponseCodeRequestTimeOutWithFinalBit: return @"Request time out"; case kOBEXResponseCodeConflictWithFinalBit: return @"Conflict"; case kOBEXResponseCodeGoneWithFinalBit: return @"Gone"; case kOBEXResponseCodeLengthRequiredFinalBit: return @"Length required"; case kOBEXResponseCodePreconditionFailedWithFinalBit: return @"Precondition failed"; case kOBEXResponseCodeRequestedEntityTooLargeWithFinalBit: return @"Requested entity too large"; case kOBEXResponseCodeRequestURLTooLargeWithFinalBit: return @"Requested URL too large"; case kOBEXResponseCodeUnsupportedMediaTypeWithFinalBit: return @"Unsupported media type"; case kOBEXResponseCodeInternalServerErrorWithFinalBit: return @"Internal server error"; case kOBEXResponseCodeNotImplementedWithFinalBit: return @"Not implemented"; case kOBEXResponseCodeBadGatewayWithFinalBit: return @"Bad gateway"; case kOBEXResponseCodeServiceUnavailableWithFinalBit: return @"Service unavailable"; case kOBEXResponseCodeGatewayTimeoutWithFinalBit: return @"Gateway timeout"; case kOBEXResponseCodeHTTPVersionNotSupportedWithFinalBit: return @"HTTP version not supported"; case kOBEXResponseCodeDatabaseFullWithFinalBit: return @"Database full"; case kOBEXResponseCodeDatabaseLockedWithFinalBit: return @"Database locked"; default: return @"Unknown response"; } } - (BBOBEXHeaderSet *)allHeaders { return mHeaders; } - (void)dealloc { [mHeaders release]; [super dealloc]; } @end lightblue-0.3.2/src/mac/LightAquaBlue/BBServiceAdvertiser.h0000644000076500007650000000550710747223642023047 0ustar beabea00000000000000/* * Copyright (c) 2006 Bea Lam. All rights reserved. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation files * (the "Software"), to deal in the Software without restriction, * including without limitation the rights to use, copy, modify, merge, * publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ // // BBServiceAdvertiser.h // LightAquaBlue // // Provides some basic operations for advertising Bluetooth services. // #import @class IOBluetoothSDPUUID; @interface BBServiceAdvertiser : NSObject { // } /* * Returns a ready-made dictionary for advertising a service with the Serial * Port Profile. */ + (NSDictionary *)serialPortProfileDictionary; /* * Returns a ready-made dictionary for advertising a service with the OBEX * Object Push Profile. */ + (NSDictionary *)objectPushProfileDictionary; /* * Returns a ready-made dictionary for advertising a service with the OBEX * File Transfer Profile. */ + (NSDictionary *)fileTransferProfileDictionary; /* * Advertise a RFCOMM-based (i.e. RFCOMM or OBEX) service. * * Arguments: * - dict: the dictionary containing the service details. * - serviceName: the service name to advertise, which will be added to the * dictionary. Can be nil. * - uuid: the custom UUID to advertise for the service, which will be added to * the dictionary. Can be nil. * - outChannelID: once the service is advertised, this will be set to the * RFCOMM channel ID that was used to advertise the service. * - outServiceRecordHandle: once the service is advertised, this will be set * to the service record handle which identifies the service. */ + (IOReturn)addRFCOMMServiceDictionary:(NSDictionary *)dict withName:(NSString *)serviceName UUID:(IOBluetoothSDPUUID *)uuid channelID:(BluetoothRFCOMMChannelID *)outChannelID serviceRecordHandle:(BluetoothSDPServiceRecordHandle *)outServiceRecordHandle; /* * Stop advertising a service. */ + (IOReturn)removeService:(BluetoothSDPServiceRecordHandle)handle; @end lightblue-0.3.2/src/mac/LightAquaBlue/BBServiceAdvertiser.m0000644000076500007650000001217010747223642023046 0ustar beabea00000000000000/* * Copyright (c) 2006 Bea Lam. All rights reserved. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation files * (the "Software"), to deal in the Software without restriction, * including without limitation the rights to use, copy, modify, merge, * publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ // // BBServiceAdvertiser.m // LightAquaBlue // #import #import #import #import "BBServiceAdvertiser.h" static NSString *kServiceItemKeyServiceClassIDList; static NSString *kServiceItemKeyServiceName; static NSString *kServiceItemKeyProtocolDescriptorList; // template service dictionaries for each pre-defined profile static NSDictionary *serialPortProfileDict; static NSDictionary *objectPushProfileDict; static NSDictionary *fileTransferProfileDict; @implementation BBServiceAdvertiser + (void)initialize { kServiceItemKeyServiceClassIDList = @"0001 - ServiceClassIDList"; kServiceItemKeyServiceName = @"0100 - ServiceName*"; kServiceItemKeyProtocolDescriptorList = @"0004 - ProtocolDescriptorList"; // initialize the template service dictionaries NSBundle *classBundle = [NSBundle bundleForClass:[BBServiceAdvertiser class]]; serialPortProfileDict = [[NSDictionary alloc] initWithContentsOfFile:[classBundle pathForResource:@"SerialPortDictionary" ofType:@"plist"]]; objectPushProfileDict = [[NSDictionary alloc] initWithContentsOfFile:[classBundle pathForResource:@"OBEXObjectPushDictionary" ofType:@"plist"]]; fileTransferProfileDict = [[NSDictionary alloc] initWithContentsOfFile:[classBundle pathForResource:@"OBEXFileTransferDictionary" ofType:@"plist"]]; //kRFCOMMChannelNone = 0; //kRFCOMM_UUID = [[IOBluetoothSDPUUID uuid16:kBluetoothSDPUUID16RFCOMM] retain]; } + (NSDictionary *)serialPortProfileDictionary { return serialPortProfileDict; } + (NSDictionary *)objectPushProfileDictionary { return objectPushProfileDict; } + (NSDictionary *)fileTransferProfileDictionary { return fileTransferProfileDict; } + (void)updateServiceDictionary:(NSMutableDictionary *)sdpEntries withName:(NSString *)serviceName withUUID:(IOBluetoothSDPUUID *)uuid { if (sdpEntries == nil) return; // set service name if (serviceName != nil) { [sdpEntries setObject:serviceName forKey:kServiceItemKeyServiceName]; } // set service uuid if given if (uuid != nil) { NSMutableArray *currentServiceList = [sdpEntries objectForKey:kServiceItemKeyServiceClassIDList]; if (currentServiceList == nil) { currentServiceList = [NSMutableArray array]; } [currentServiceList addObject:[NSData dataWithBytes:[uuid bytes] length:[uuid length]]]; // update dict [sdpEntries setObject:currentServiceList forKey:kServiceItemKeyServiceClassIDList]; } } + (IOReturn)addRFCOMMServiceDictionary:(NSDictionary *)dict withName:(NSString *)serviceName UUID:(IOBluetoothSDPUUID *)uuid channelID:(BluetoothRFCOMMChannelID *)outChannelID serviceRecordHandle:(BluetoothSDPServiceRecordHandle *)outServiceRecordHandle { if (dict == nil) return kIOReturnError; NSMutableDictionary *sdpEntries = [NSMutableDictionary dictionaryWithDictionary:dict]; [BBServiceAdvertiser updateServiceDictionary:sdpEntries withName:serviceName withUUID:uuid]; // publish the service IOBluetoothSDPServiceRecordRef serviceRecordRef; IOReturn status = IOBluetoothAddServiceDict((CFDictionaryRef) sdpEntries, &serviceRecordRef); if (status == kIOReturnSuccess) { IOBluetoothSDPServiceRecord *serviceRecord = [IOBluetoothSDPServiceRecord withSDPServiceRecordRef:serviceRecordRef]; // get service channel ID & service record handle status = [serviceRecord getRFCOMMChannelID:outChannelID]; if (status == kIOReturnSuccess) { status = [serviceRecord getServiceRecordHandle:outServiceRecordHandle]; } // cleanup IOBluetoothObjectRelease(serviceRecordRef); } return status; } + (IOReturn)removeService:(BluetoothSDPServiceRecordHandle)handle { return IOBluetoothRemoveServiceWithRecordHandle(handle); } @end lightblue-0.3.2/src/mac/LightAquaBlue/BBStreamingInputStream.h0000644000076500007650000000333310747223642023536 0ustar beabea00000000000000/* * Copyright (c) 2006 Bea Lam. All rights reserved. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation files * (the "Software"), to deal in the Software without restriction, * including without limitation the rights to use, copy, modify, merge, * publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ // // BBDelegatingInputStream.h // LightAquaBlue // // A NSInputStream subclass that calls readDataWithMaxLength: on the delegate // when data is required. // This class is only intended for use from the LightBlue library. // Most methods are not implemented, and there are no stream:HandleEvent: // calls to the delegate. // #import @interface BBStreamingInputStream : NSInputStream { id mDelegate; NSStreamStatus mStatus; } - (id)initWithDelegate:(id)delegate; @end @protocol BBStreamingInputStreamDelegate - (NSData *)readDataWithMaxLength:(unsigned int)maxLength; @end lightblue-0.3.2/src/mac/LightAquaBlue/BBStreamingInputStream.m0000644000076500007650000000503010747223642023537 0ustar beabea00000000000000/* * Copyright (c) 2006 Bea Lam. All rights reserved. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation files * (the "Software"), to deal in the Software without restriction, * including without limitation the rights to use, copy, modify, merge, * publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ // // BBDelegatingInputStream.m // LightAquaBlue // #import "BBStreamingInputStream.h" @implementation BBStreamingInputStream - (id)initWithDelegate:(id)delegate { self = [super init]; mDelegate = delegate; mStatus = NSStreamStatusNotOpen; return self; } - (int)read:(uint8_t *)buffer maxLength:(unsigned int)maxLength { NSData *data = [mDelegate readDataWithMaxLength:maxLength]; if (!data) return -1; int copyLength = [data length] < maxLength ? [data length] : maxLength; [data getBytes:buffer length:copyLength]; return copyLength; } - (BOOL)getBuffer:(uint8_t **)buffer length:(unsigned int *)len { // we cannot access buffer data without calling read:maxLength: return NO; } - (BOOL)hasBytesAvailable { // don't know whether bytes are available until read:maxLength: is called return YES; } - (void)open { mStatus = NSStreamStatusOpen; } - (void)close { mStatus = NSStreamStatusClosed; } - (void)setDelegate:(id)delegate { mDelegate = delegate; } - (id)delegate { return mDelegate; } - (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode { } - (void)removeFromRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode { } - (BOOL)setProperty:(id)property forKey:(NSString *)key { return NO; } - (id)propertyForKey:(NSString *)key { return nil; } - (NSStreamStatus)streamStatus { return mStatus; } @end lightblue-0.3.2/src/mac/LightAquaBlue/BBStreamingOutputStream.h0000644000076500007650000000326110747223641023736 0ustar beabea00000000000000/* * Copyright (c) 2006 Bea Lam. All rights reserved. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation files * (the "Software"), to deal in the Software without restriction, * including without limitation the rights to use, copy, modify, merge, * publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ // // BBStreamingOutputStream.h // LightAquaBlue // // A NSOutputStream subclass that calls write: on its delegate when data // is available. // This class is only intended for use from the LightBlue library. // Most methods are not implemented, and there are no stream:HandleEvent: // calls to the delegate. // #import @interface BBStreamingOutputStream : NSOutputStream { id mDelegate; NSStreamStatus mStatus; } - (id)initWithDelegate:(id)delegate; @end @protocol BBStreamingOutputStreamDelegate - (int)write:(NSData *)data; @end lightblue-0.3.2/src/mac/LightAquaBlue/BBStreamingOutputStream.m0000644000076500007650000000456010747223641023746 0ustar beabea00000000000000/* * Copyright (c) 2006 Bea Lam. All rights reserved. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation files * (the "Software"), to deal in the Software without restriction, * including without limitation the rights to use, copy, modify, merge, * publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ // // BBStreamingOutputStream.m // LightAquaBlue // #import "BBStreamingOutputStream.h" @implementation BBStreamingOutputStream - (id)initWithDelegate:(id)delegate { self = [super init]; mDelegate = delegate; return self; } - (int)write:(const uint8_t *)buffer maxLength:(unsigned int)len { //NSLog(@"[BBStreamingOutputStream] writing data..."); int buflen = [mDelegate write:[NSData dataWithBytesNoCopy:(void *)buffer length:len freeWhenDone:NO]]; return buflen; } - (BOOL)hasSpaceAvailable { // must do write to determine whether space is available return YES; } - (void)open { mStatus = NSStreamStatusOpen; } - (void)close { mStatus = NSStreamStatusClosed; } - (void)setDelegate:(id)delegate { mDelegate = delegate; } - (id)delegate { return mDelegate; } - (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode { } - (void)removeFromRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode { } - (BOOL)setProperty:(id)property forKey:(NSString *)key { return NO; } - (id)propertyForKey:(NSString *)key { return nil; } - (NSStreamStatus)streamStatus { return mStatus; } @end lightblue-0.3.2/src/mac/LightAquaBlue/English.lproj/0000755000076500007650000000000010770565177021560 5ustar beabea00000000000000lightblue-0.3.2/src/mac/LightAquaBlue/English.lproj/InfoPlist.strings0000644000076500007650000000031410747223641025067 0ustar beabea00000000000000þÿ/* Localized versions of Info.plist keys */ NSHumanReadableCopyright = "© __MyCompanyName__, 2007"; lightblue-0.3.2/src/mac/LightAquaBlue/Info.plist0000644000076500007650000000142610747223642021005 0ustar beabea00000000000000 CFBundleDevelopmentRegion English CFBundleExecutable ${EXECUTABLE_NAME} CFBundleName ${PRODUCT_NAME} CFBundleIconFile CFBundleIdentifier com.yourcompany.yourcocoaframework CFBundleInfoDictionaryVersion 6.0 CFBundlePackageType FMWK CFBundleSignature ???? CFBundleVersion 1.0 NSPrincipalClass lightblue-0.3.2/src/mac/LightAquaBlue/LightAquaBlue.h0000644000076500007650000000105010747223642021666 0ustar beabea00000000000000#import #import #import #import #import #import #import #import #import #import #import #import lightblue-0.3.2/src/mac/LightAquaBlue/LightAquaBlue.xcodeproj/0000755000076500007650000000000010770565177023525 5ustar beabea00000000000000lightblue-0.3.2/src/mac/LightAquaBlue/LightAquaBlue.xcodeproj/project.pbxproj0000644000076500007650000005662710750617153026607 0ustar beabea00000000000000// !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 42; objects = { /* Begin PBXBuildFile section */ 8100016A0D09536000FA9985 /* BBStreamingOutputStream.h in Headers */ = {isa = PBXBuildFile; fileRef = 810001680D09536000FA9985 /* BBStreamingOutputStream.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8100016B0D09536000FA9985 /* BBStreamingOutputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = 810001690D09536000FA9985 /* BBStreamingOutputStream.m */; }; 8100FE030D056D0100FA9985 /* BBBluetoothOBEXClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 8100FDEB0D056D0000FA9985 /* BBBluetoothOBEXClient.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8100FE040D056D0100FA9985 /* BBBluetoothOBEXClient.m in Sources */ = {isa = PBXBuildFile; fileRef = 8100FDEC0D056D0000FA9985 /* BBBluetoothOBEXClient.m */; }; 8100FE050D056D0100FA9985 /* BBBluetoothOBEXServer.h in Headers */ = {isa = PBXBuildFile; fileRef = 8100FDED0D056D0000FA9985 /* BBBluetoothOBEXServer.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8100FE060D056D0100FA9985 /* BBBluetoothOBEXServer.m in Sources */ = {isa = PBXBuildFile; fileRef = 8100FDEE0D056D0000FA9985 /* BBBluetoothOBEXServer.m */; }; 8100FE070D056D0100FA9985 /* BBStreamingInputStream.h in Headers */ = {isa = PBXBuildFile; fileRef = 8100FDEF0D056D0000FA9985 /* BBStreamingInputStream.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8100FE080D056D0100FA9985 /* BBStreamingInputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = 8100FDF00D056D0000FA9985 /* BBStreamingInputStream.m */; }; 8100FE090D056D0100FA9985 /* BBLocalDevice.h in Headers */ = {isa = PBXBuildFile; fileRef = 8100FDF10D056D0000FA9985 /* BBLocalDevice.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8100FE0A0D056D0100FA9985 /* BBLocalDevice.m in Sources */ = {isa = PBXBuildFile; fileRef = 8100FDF20D056D0000FA9985 /* BBLocalDevice.m */; }; 8100FE0B0D056D0100FA9985 /* BBMutableOBEXHeaderSet.h in Headers */ = {isa = PBXBuildFile; fileRef = 8100FDF30D056D0000FA9985 /* BBMutableOBEXHeaderSet.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8100FE0C0D056D0100FA9985 /* BBMutableOBEXHeaderSet.m in Sources */ = {isa = PBXBuildFile; fileRef = 8100FDF40D056D0000FA9985 /* BBMutableOBEXHeaderSet.m */; }; 8100FE0D0D056D0100FA9985 /* BBOBEXHeaderSet.h in Headers */ = {isa = PBXBuildFile; fileRef = 8100FDF50D056D0000FA9985 /* BBOBEXHeaderSet.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8100FE0E0D056D0100FA9985 /* BBOBEXHeaderSet.m in Sources */ = {isa = PBXBuildFile; fileRef = 8100FDF60D056D0000FA9985 /* BBOBEXHeaderSet.m */; }; 8100FE0F0D056D0100FA9985 /* BBOBEXRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 8100FDF70D056D0100FA9985 /* BBOBEXRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8100FE100D056D0100FA9985 /* BBOBEXRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8100FDF80D056D0100FA9985 /* BBOBEXRequest.m */; }; 8100FE110D056D0100FA9985 /* BBOBEXRequestHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 8100FDF90D056D0100FA9985 /* BBOBEXRequestHandler.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8100FE120D056D0100FA9985 /* BBOBEXRequestHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 8100FDFA0D056D0100FA9985 /* BBOBEXRequestHandler.m */; }; 8100FE150D056D0100FA9985 /* BBServiceAdvertiser.h in Headers */ = {isa = PBXBuildFile; fileRef = 8100FDFD0D056D0100FA9985 /* BBServiceAdvertiser.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8100FE160D056D0100FA9985 /* BBServiceAdvertiser.m in Sources */ = {isa = PBXBuildFile; fileRef = 8100FDFE0D056D0100FA9985 /* BBServiceAdvertiser.m */; }; 8100FE170D056D0100FA9985 /* LightAquaBlue.h in Headers */ = {isa = PBXBuildFile; fileRef = 8100FDFF0D056D0100FA9985 /* LightAquaBlue.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8100FE180D056D0100FA9985 /* OBEXFileTransferDictionary.plist in Resources */ = {isa = PBXBuildFile; fileRef = 8100FE000D056D0100FA9985 /* OBEXFileTransferDictionary.plist */; }; 8100FE190D056D0100FA9985 /* OBEXObjectPushDictionary.plist in Resources */ = {isa = PBXBuildFile; fileRef = 8100FE010D056D0100FA9985 /* OBEXObjectPushDictionary.plist */; }; 8100FE1A0D056D0100FA9985 /* SerialPortDictionary.plist in Resources */ = {isa = PBXBuildFile; fileRef = 8100FE020D056D0100FA9985 /* SerialPortDictionary.plist */; }; 8100FE1C0D056D4300FA9985 /* IOBluetooth.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8100FE1B0D056D4300FA9985 /* IOBluetooth.framework */; }; 817D21A30D3D7FA500469B5D /* BBBluetoothChannelDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 817D21A10D3D7FA500469B5D /* BBBluetoothChannelDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; 817D21A40D3D7FA500469B5D /* BBBluetoothChannelDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 817D21A20D3D7FA500469B5D /* BBBluetoothChannelDelegate.m */; }; 817D239D0D40313600469B5D /* BBOBEXResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = 817D239B0D40313600469B5D /* BBOBEXResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; 817D239E0D40313600469B5D /* BBOBEXResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 817D239C0D40313600469B5D /* BBOBEXResponse.m */; }; 8DC2EF530486A6940098B216 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C1666FE841158C02AAC07 /* InfoPlist.strings */; }; 8DC2EF570486A6940098B216 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ 0867D69BFE84028FC02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; 0867D6A5FE840307C02AAC07 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; }; 089C1667FE841158C02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = ""; }; 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; 32DBCF5E0370ADEE00C91783 /* LightAquaBlue_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LightAquaBlue_Prefix.pch; sourceTree = ""; }; 810001680D09536000FA9985 /* BBStreamingOutputStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BBStreamingOutputStream.h; sourceTree = ""; }; 810001690D09536000FA9985 /* BBStreamingOutputStream.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BBStreamingOutputStream.m; sourceTree = ""; }; 8100FDEB0D056D0000FA9985 /* BBBluetoothOBEXClient.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = BBBluetoothOBEXClient.h; sourceTree = ""; }; 8100FDEC0D056D0000FA9985 /* BBBluetoothOBEXClient.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = BBBluetoothOBEXClient.m; sourceTree = ""; }; 8100FDED0D056D0000FA9985 /* BBBluetoothOBEXServer.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = BBBluetoothOBEXServer.h; sourceTree = ""; }; 8100FDEE0D056D0000FA9985 /* BBBluetoothOBEXServer.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = BBBluetoothOBEXServer.m; sourceTree = ""; }; 8100FDEF0D056D0000FA9985 /* BBStreamingInputStream.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = BBStreamingInputStream.h; sourceTree = ""; }; 8100FDF00D056D0000FA9985 /* BBStreamingInputStream.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = BBStreamingInputStream.m; sourceTree = ""; }; 8100FDF10D056D0000FA9985 /* BBLocalDevice.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = BBLocalDevice.h; sourceTree = ""; }; 8100FDF20D056D0000FA9985 /* BBLocalDevice.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = BBLocalDevice.m; sourceTree = ""; }; 8100FDF30D056D0000FA9985 /* BBMutableOBEXHeaderSet.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = BBMutableOBEXHeaderSet.h; sourceTree = ""; }; 8100FDF40D056D0000FA9985 /* BBMutableOBEXHeaderSet.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = BBMutableOBEXHeaderSet.m; sourceTree = ""; }; 8100FDF50D056D0000FA9985 /* BBOBEXHeaderSet.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = BBOBEXHeaderSet.h; sourceTree = ""; }; 8100FDF60D056D0000FA9985 /* BBOBEXHeaderSet.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = BBOBEXHeaderSet.m; sourceTree = ""; }; 8100FDF70D056D0100FA9985 /* BBOBEXRequest.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = BBOBEXRequest.h; sourceTree = ""; }; 8100FDF80D056D0100FA9985 /* BBOBEXRequest.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = BBOBEXRequest.m; sourceTree = ""; }; 8100FDF90D056D0100FA9985 /* BBOBEXRequestHandler.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = BBOBEXRequestHandler.h; sourceTree = ""; }; 8100FDFA0D056D0100FA9985 /* BBOBEXRequestHandler.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = BBOBEXRequestHandler.m; sourceTree = ""; }; 8100FDFD0D056D0100FA9985 /* BBServiceAdvertiser.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = BBServiceAdvertiser.h; sourceTree = ""; }; 8100FDFE0D056D0100FA9985 /* BBServiceAdvertiser.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = BBServiceAdvertiser.m; sourceTree = ""; }; 8100FDFF0D056D0100FA9985 /* LightAquaBlue.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = LightAquaBlue.h; sourceTree = ""; }; 8100FE000D056D0100FA9985 /* OBEXFileTransferDictionary.plist */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.xml; path = OBEXFileTransferDictionary.plist; sourceTree = ""; }; 8100FE010D056D0100FA9985 /* OBEXObjectPushDictionary.plist */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.xml; path = OBEXObjectPushDictionary.plist; sourceTree = ""; }; 8100FE020D056D0100FA9985 /* SerialPortDictionary.plist */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.xml; path = SerialPortDictionary.plist; sourceTree = ""; }; 8100FE1B0D056D4300FA9985 /* IOBluetooth.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOBluetooth.framework; path = /System/Library/Frameworks/IOBluetooth.framework; sourceTree = ""; }; 817D21A10D3D7FA500469B5D /* BBBluetoothChannelDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BBBluetoothChannelDelegate.h; sourceTree = ""; }; 817D21A20D3D7FA500469B5D /* BBBluetoothChannelDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BBBluetoothChannelDelegate.m; sourceTree = ""; }; 817D239B0D40313600469B5D /* BBOBEXResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BBOBEXResponse.h; sourceTree = ""; }; 817D239C0D40313600469B5D /* BBOBEXResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BBOBEXResponse.m; sourceTree = ""; }; 8DC2EF5A0486A6940098B216 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; 8DC2EF5B0486A6940098B216 /* LightAquaBlue.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = LightAquaBlue.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 8DC2EF560486A6940098B216 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 8DC2EF570486A6940098B216 /* Cocoa.framework in Frameworks */, 8100FE1C0D056D4300FA9985 /* IOBluetooth.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 034768DFFF38A50411DB9C8B /* Products */ = { isa = PBXGroup; children = ( 8DC2EF5B0486A6940098B216 /* LightAquaBlue.framework */, ); name = Products; sourceTree = ""; }; 0867D691FE84028FC02AAC07 /* LightAquaBlue */ = { isa = PBXGroup; children = ( 8100FDFF0D056D0100FA9985 /* LightAquaBlue.h */, 08FB77AEFE84172EC02AAC07 /* Classes */, 32C88DFF0371C24200C91783 /* Other Sources */, 089C1665FE841158C02AAC07 /* Resources */, 0867D69AFE84028FC02AAC07 /* External Frameworks and Libraries */, 034768DFFF38A50411DB9C8B /* Products */, ); name = LightAquaBlue; sourceTree = ""; }; 0867D69AFE84028FC02AAC07 /* External Frameworks and Libraries */ = { isa = PBXGroup; children = ( 8100FE1B0D056D4300FA9985 /* IOBluetooth.framework */, 1058C7B0FEA5585E11CA2CBB /* Linked Frameworks */, 1058C7B2FEA5585E11CA2CBB /* Other Frameworks */, ); name = "External Frameworks and Libraries"; sourceTree = ""; }; 089C1665FE841158C02AAC07 /* Resources */ = { isa = PBXGroup; children = ( 8100FE000D056D0100FA9985 /* OBEXFileTransferDictionary.plist */, 8100FE010D056D0100FA9985 /* OBEXObjectPushDictionary.plist */, 8100FE020D056D0100FA9985 /* SerialPortDictionary.plist */, 8DC2EF5A0486A6940098B216 /* Info.plist */, 089C1666FE841158C02AAC07 /* InfoPlist.strings */, ); name = Resources; sourceTree = ""; }; 08FB77AEFE84172EC02AAC07 /* Classes */ = { isa = PBXGroup; children = ( 810001680D09536000FA9985 /* BBStreamingOutputStream.h */, 810001690D09536000FA9985 /* BBStreamingOutputStream.m */, 8100FDEB0D056D0000FA9985 /* BBBluetoothOBEXClient.h */, 8100FDEC0D056D0000FA9985 /* BBBluetoothOBEXClient.m */, 8100FDED0D056D0000FA9985 /* BBBluetoothOBEXServer.h */, 8100FDEE0D056D0000FA9985 /* BBBluetoothOBEXServer.m */, 8100FDEF0D056D0000FA9985 /* BBStreamingInputStream.h */, 8100FDF00D056D0000FA9985 /* BBStreamingInputStream.m */, 8100FDF10D056D0000FA9985 /* BBLocalDevice.h */, 8100FDF20D056D0000FA9985 /* BBLocalDevice.m */, 8100FDF30D056D0000FA9985 /* BBMutableOBEXHeaderSet.h */, 8100FDF40D056D0000FA9985 /* BBMutableOBEXHeaderSet.m */, 8100FDF50D056D0000FA9985 /* BBOBEXHeaderSet.h */, 8100FDF60D056D0000FA9985 /* BBOBEXHeaderSet.m */, 8100FDF70D056D0100FA9985 /* BBOBEXRequest.h */, 8100FDF80D056D0100FA9985 /* BBOBEXRequest.m */, 8100FDF90D056D0100FA9985 /* BBOBEXRequestHandler.h */, 8100FDFA0D056D0100FA9985 /* BBOBEXRequestHandler.m */, 8100FDFD0D056D0100FA9985 /* BBServiceAdvertiser.h */, 8100FDFE0D056D0100FA9985 /* BBServiceAdvertiser.m */, 817D21A10D3D7FA500469B5D /* BBBluetoothChannelDelegate.h */, 817D21A20D3D7FA500469B5D /* BBBluetoothChannelDelegate.m */, 817D239B0D40313600469B5D /* BBOBEXResponse.h */, 817D239C0D40313600469B5D /* BBOBEXResponse.m */, ); name = Classes; sourceTree = ""; }; 1058C7B0FEA5585E11CA2CBB /* Linked Frameworks */ = { isa = PBXGroup; children = ( 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */, ); name = "Linked Frameworks"; sourceTree = ""; }; 1058C7B2FEA5585E11CA2CBB /* Other Frameworks */ = { isa = PBXGroup; children = ( 0867D6A5FE840307C02AAC07 /* AppKit.framework */, 0867D69BFE84028FC02AAC07 /* Foundation.framework */, ); name = "Other Frameworks"; sourceTree = ""; }; 32C88DFF0371C24200C91783 /* Other Sources */ = { isa = PBXGroup; children = ( 32DBCF5E0370ADEE00C91783 /* LightAquaBlue_Prefix.pch */, ); name = "Other Sources"; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ 8DC2EF500486A6940098B216 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( 8100FE030D056D0100FA9985 /* BBBluetoothOBEXClient.h in Headers */, 8100FE050D056D0100FA9985 /* BBBluetoothOBEXServer.h in Headers */, 8100FE070D056D0100FA9985 /* BBStreamingInputStream.h in Headers */, 8100FE090D056D0100FA9985 /* BBLocalDevice.h in Headers */, 8100FE0B0D056D0100FA9985 /* BBMutableOBEXHeaderSet.h in Headers */, 8100FE0D0D056D0100FA9985 /* BBOBEXHeaderSet.h in Headers */, 8100FE0F0D056D0100FA9985 /* BBOBEXRequest.h in Headers */, 8100FE110D056D0100FA9985 /* BBOBEXRequestHandler.h in Headers */, 8100FE150D056D0100FA9985 /* BBServiceAdvertiser.h in Headers */, 8100FE170D056D0100FA9985 /* LightAquaBlue.h in Headers */, 8100016A0D09536000FA9985 /* BBStreamingOutputStream.h in Headers */, 817D21A30D3D7FA500469B5D /* BBBluetoothChannelDelegate.h in Headers */, 817D239D0D40313600469B5D /* BBOBEXResponse.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ 8DC2EF4F0486A6940098B216 /* LightAquaBlue */ = { isa = PBXNativeTarget; buildConfigurationList = 1DEB91AD08733DA50010E9CD /* Build configuration list for PBXNativeTarget "LightAquaBlue" */; buildPhases = ( 8DC2EF500486A6940098B216 /* Headers */, 8DC2EF520486A6940098B216 /* Resources */, 8DC2EF540486A6940098B216 /* Sources */, 8DC2EF560486A6940098B216 /* Frameworks */, ); buildRules = ( ); dependencies = ( ); name = LightAquaBlue; productInstallPath = "$(HOME)/Library/Frameworks"; productName = LightAquaBlue; productReference = 8DC2EF5B0486A6940098B216 /* LightAquaBlue.framework */; productType = "com.apple.product-type.framework"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 0867D690FE84028FC02AAC07 /* Project object */ = { isa = PBXProject; buildConfigurationList = 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "LightAquaBlue" */; hasScannedForEncodings = 1; mainGroup = 0867D691FE84028FC02AAC07 /* LightAquaBlue */; productRefGroup = 034768DFFF38A50411DB9C8B /* Products */; projectDirPath = ""; targets = ( 8DC2EF4F0486A6940098B216 /* LightAquaBlue */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ 8DC2EF520486A6940098B216 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 8DC2EF530486A6940098B216 /* InfoPlist.strings in Resources */, 8100FE180D056D0100FA9985 /* OBEXFileTransferDictionary.plist in Resources */, 8100FE190D056D0100FA9985 /* OBEXObjectPushDictionary.plist in Resources */, 8100FE1A0D056D0100FA9985 /* SerialPortDictionary.plist in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ 8DC2EF540486A6940098B216 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 8100FE040D056D0100FA9985 /* BBBluetoothOBEXClient.m in Sources */, 8100FE060D056D0100FA9985 /* BBBluetoothOBEXServer.m in Sources */, 8100FE080D056D0100FA9985 /* BBStreamingInputStream.m in Sources */, 8100FE0A0D056D0100FA9985 /* BBLocalDevice.m in Sources */, 8100FE0C0D056D0100FA9985 /* BBMutableOBEXHeaderSet.m in Sources */, 8100FE0E0D056D0100FA9985 /* BBOBEXHeaderSet.m in Sources */, 8100FE100D056D0100FA9985 /* BBOBEXRequest.m in Sources */, 8100FE120D056D0100FA9985 /* BBOBEXRequestHandler.m in Sources */, 8100FE160D056D0100FA9985 /* BBServiceAdvertiser.m in Sources */, 8100016B0D09536000FA9985 /* BBStreamingOutputStream.m in Sources */, 817D21A40D3D7FA500469B5D /* BBBluetoothChannelDelegate.m in Sources */, 817D239E0D40313600469B5D /* BBOBEXResponse.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXVariantGroup section */ 089C1666FE841158C02AAC07 /* InfoPlist.strings */ = { isa = PBXVariantGroup; children = ( 089C1667FE841158C02AAC07 /* English */, ); name = InfoPlist.strings; sourceTree = ""; }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ 1DEB91AE08733DA50010E9CD /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { COPY_PHASE_STRIP = NO; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; GCC_DYNAMIC_NO_PIC = NO; GCC_ENABLE_FIX_AND_CONTINUE = YES; GCC_MODEL_TUNING = G5; GCC_OPTIMIZATION_LEVEL = 0; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = LightAquaBlue_Prefix.pch; INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Frameworks"; PRODUCT_NAME = LightAquaBlue; WRAPPER_EXTENSION = framework; ZERO_LINK = YES; }; name = Debug; }; 1DEB91AF08733DA50010E9CD /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ARCHS = ( ppc, i386, ); DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; FRAMEWORK_VERSION = A; GCC_GENERATE_DEBUGGING_SYMBOLS = NO; GCC_MODEL_TUNING = G5; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = LightAquaBlue_Prefix.pch; INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Frameworks"; PRODUCT_NAME = LightAquaBlue; WRAPPER_EXTENSION = framework; }; name = Release; }; 1DEB91B208733DA50010E9CD /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNUSED_VARIABLE = YES; MACOSX_DEPLOYMENT_TARGET = ""; PREBINDING = NO; SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk; }; name = Debug; }; 1DEB91B308733DA50010E9CD /* Release */ = { isa = XCBuildConfiguration; buildSettings = { GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNUSED_VARIABLE = YES; PREBINDING = NO; SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 1DEB91AD08733DA50010E9CD /* Build configuration list for PBXNativeTarget "LightAquaBlue" */ = { isa = XCConfigurationList; buildConfigurations = ( 1DEB91AE08733DA50010E9CD /* Debug */, 1DEB91AF08733DA50010E9CD /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "LightAquaBlue" */ = { isa = XCConfigurationList; buildConfigurations = ( 1DEB91B208733DA50010E9CD /* Debug */, 1DEB91B308733DA50010E9CD /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = 0867D690FE84028FC02AAC07 /* Project object */; } lightblue-0.3.2/src/mac/LightAquaBlue/LightAquaBlue_Prefix.pch0000644000076500007650000000023610747223642023533 0ustar beabea00000000000000// // Prefix header for all source files of the 'LightAquaBlue' target in the 'LightAquaBlue' project. // #ifdef __OBJC__ #import #endif lightblue-0.3.2/src/mac/LightAquaBlue/OBEXFileTransferDictionary.plist0000644000076500007650000000354010747223642025201 0ustar beabea00000000000000 0001 - ServiceClassIDList EQY= 0004 - ProtocolDescriptorList AQA= AAM= DataElementSize 1 DataElementType 1 DataElementValue 3 AAg= 0005 - BrowseGroupList* EAI= 0006 - LanguageBaseAttributeIDList* DataElementSize 2 DataElementType 1 DataElementValue 25966 DataElementSize 2 DataElementType 1 DataElementValue 106 DataElementSize 2 DataElementType 1 DataElementValue 256 0009 - BluetoothProfileDescriptorList EQY= DataElementSize 2 DataElementType 1 DataElementValue 256 0303 - Supported Formats List DataElementType 1 DataElementValue /w== lightblue-0.3.2/src/mac/LightAquaBlue/OBEXObjectPushDictionary.plist0000644000076500007650000000354010747223642024663 0ustar beabea00000000000000 0001 - ServiceClassIDList EQU= 0004 - ProtocolDescriptorList AQA= AAM= DataElementSize 1 DataElementType 1 DataElementValue 3 AAg= 0005 - BrowseGroupList* EAI= 0006 - LanguageBaseAttributeIDList* DataElementSize 2 DataElementType 1 DataElementValue 25966 DataElementSize 2 DataElementType 1 DataElementValue 106 DataElementSize 2 DataElementType 1 DataElementValue 256 0009 - BluetoothProfileDescriptorList EQU= DataElementSize 2 DataElementType 1 DataElementValue 256 0303 - Supported Formats List DataElementType 1 DataElementValue /w== lightblue-0.3.2/src/mac/LightAquaBlue/SerialPortDictionary.plist0000644000076500007650000000345610747223641024230 0ustar beabea00000000000000 0001 - ServiceClassIDList EQE= 0004 - ProtocolDescriptorList AQA= AAM= DataElementSize 1 DataElementType 1 DataElementValue 3 0005 - BrowseGroupList* EAI= 0006 - LanguageBaseAttributeIDList* DataElementSize 2 DataElementType 1 DataElementValue 25966 DataElementSize 2 DataElementType 1 DataElementValue 106 DataElementSize 2 DataElementType 1 DataElementValue 256 0009 - BluetoothProfileDescriptorList EQE= DataElementSize 2 DataElementType 1 DataElementValue 256 0303 - Supported Formats List DataElementType 1 DataElementValue /w== lightblue-0.3.2/src/mac/obex.py0000644000076500007650000000735210767110217015654 0ustar beabea00000000000000# Copyright (c) 2006 Bea Lam. All rights reserved. # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files # (the "Software"), to deal in the Software without restriction, # including without limitation the rights to use, copy, modify, merge, # publish, distribute, sublicense, and/or sell copies of the Software, # and to permit persons to whom the Software is furnished to do so, # subject to the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ Provides an OBEX client class and convenience functions for sending and receiving files over OBEX. This module also defines constants for response code values (without the final bit set). For example: >>> import lightblue >>> lightblue.obex.OK 32 # the OK/Success response 0x20 (i.e. 0xA0 without the final bit) >>> lightblue.obex.FORBIDDEN 67 # the Forbidden response 0x43 (i.e. 0xC3 without the final bit) """ # Docstrings for attributes in this module. _docstrings = { "sendfile": """ Sends a file to a remote device. Raises lightblue.obex.OBEXError if an error occurred during the request, or if the request was refused by the remote device. Arguments: - address: the address of the remote device - channel: the RFCOMM channel of the remote OBEX service - source: a filename or file-like object, containing the data to be sent. If a file object is given, it must be opened for reading. Note you can achieve the same thing using OBEXClient with something like this: >>> import lightblue >>> client = lightblue.obex.OBEXClient(address, channel) >>> client.connect() >>> putresponse = client.put({"name": "MyFile.txt"}, file("MyFile.txt", 'rb')) >>> client.disconnect() >>> if putresponse.code != lightblue.obex.OK: ... raise lightblue.obex.OBEXError("server denied the Put request") >>> """, "recvfile": """ Receives a file through an OBEX service. Arguments: - sock: the server socket on which the file is to be received. Note this socket must *not* be listening. Also, an OBEX service should have been advertised on this socket. - dest: a filename or file-like object, to which the received data will be written. If a filename is given, any existing file will be overwritten. If a file object is given, it must be opened for writing. For example, to receive a file and save it as "MyFile.txt": >>> from lightblue import * >>> s = socket() >>> s.bind(("", 0)) >>> advertise("My OBEX Service", s, OBEX) >>> obex.recvfile(s, "MyFile.txt") """ } # import implementation modules from _obex import * from _obexcommon import * import _obex import _obexcommon __all__ = _obex.__all__ + _obexcommon.__all__ # set docstrings localattrs = locals() for attr in _obex.__all__: try: localattrs[attr].__doc__ = _docstrings[attr] except KeyError: pass del attr, localattrs lightblue-0.3.2/src/series60/0000755000076500007650000000000010770565176015252 5ustar beabea00000000000000lightblue-0.3.2/src/series60/build/0000755000076500007650000000000010770565176016351 5ustar beabea00000000000000lightblue-0.3.2/src/series60/build/s60-1stEd_FP1/0000755000076500007650000000000010770565177020406 5ustar beabea00000000000000lightblue-0.3.2/src/series60/build/s60-1stEd_FP1/_lightblueutil.mmp0000755000076500007650000000140110660027773024125 0ustar beabea00000000000000TARGETTYPE dll TARGET _lightblueutil.pyd TARGETPATH \system\libs #ifdef EKA2 CAPABILITY NetworkServices LocalServices ReadUserData WriteUserData UserEnvironment #endif NOSTRICTDEF DEFFILE _lightblueutil.frz SYSTEMINCLUDE \epoc32\include SYSTEMINCLUDE \epoc32\include\libc SYSTEMINCLUDE \epoc32\include\python USERINCLUDE ..\..\src\_lightblueutil LIBRARY python222.lib LIBRARY euser.lib LIBRARY estlib.lib LIBRARY btextnotifiers.lib LIBRARY esock.lib bluetooth.lib btdevice.lib SOURCEPATH ..\..\src\_lightblueutil SOURCE apnresolver.cpp SOURCEPATH ..\..\src\_lightblueutil SOURCE localepocpyutils.cpp SOURCEPATH ..\..\src\_lightblueutil SOURCE panic.cpp SOURCEPATH ..\..\src\_lightblueutil SOURCE _lightblueutil.cpp lightblue-0.3.2/src/series60/build/s60-1stEd_FP1/bld.inf0000755000076500007650000000014010660027773021634 0ustar beabea00000000000000 PRJ_PLATFORMS #ifdef EKA2 gcce winscw #else wins armi #endif PRJ_MMPFILES _lightblueutil.mmp lightblue-0.3.2/src/series60/build/s60-1stEd_FP1/lightblue.pkg0000755000076500007650000000127010747243654023070 0ustar beabea00000000000000; LightBlue v 0.2.0 #{"LightBlue"}, (0x200031CC), 0, 2, 0 ; Supports Series 60 1st Edition (0x101f8202), 0, 0, 0, {"Series60ProductID"} ;;; Python for Series 60 (0x10201510), 0, 0, 0, {"Python"} ; Python sources for all distributions "..\..\src\__init__.py" - "!:\system\libs\lightblue\__init__.py" "..\..\src\_lightblue.py" - "!:\system\libs\lightblue\_lightblue.py" "..\..\src\obex.py" - "!:\system\libs\lightblue\obex.py" "..\..\src\_obex.py" - "!:\system\libs\lightblue\_obex.py" "..\..\src\_lightbluecommon.py" - "!:\system\libs\lightblue\_lightbluecommon.py" ; Specific builds for this distribution "_lightblueutil.pyd" - "!:\system\libs\lightblue\_lightblueutil.pyd" lightblue-0.3.2/src/series60/build/s60-2ndEd/0000755000076500007650000000000010770565177017714 5ustar beabea00000000000000lightblue-0.3.2/src/series60/build/s60-2ndEd/_lightblueutil.mmp0000755000076500007650000000136410660027772023442 0ustar beabea00000000000000TARGETTYPE dll TARGET _lightblueutil.pyd TARGETPATH \system\libs #ifdef EKA2 CAPABILITY NetworkServices LocalServices ReadUserData WriteUserData UserEnvironment #endif NOSTRICTDEF EXPORTUNFROZEN SYSTEMINCLUDE \epoc32\include SYSTEMINCLUDE \epoc32\include\libc SYSTEMINCLUDE \epoc32\include\python USERINCLUDE ..\..\src\_lightblueutil LIBRARY python222.lib LIBRARY euser.lib LIBRARY estlib.lib LIBRARY btextnotifiers.lib LIBRARY esock.lib bluetooth.lib btdevice.lib SOURCEPATH ..\..\src\_lightblueutil SOURCE apnresolver.cpp SOURCEPATH ..\..\src\_lightblueutil SOURCE localepocpyutils.cpp SOURCEPATH ..\..\src\_lightblueutil SOURCE panic.cpp SOURCEPATH ..\..\src\_lightblueutil SOURCE _lightblueutil.cpp lightblue-0.3.2/src/series60/build/s60-2ndEd/bld.inf0000755000076500007650000000014010564554670021146 0ustar beabea00000000000000 PRJ_PLATFORMS #ifdef EKA2 gcce winscw #else wins armi #endif PRJ_MMPFILES _lightblueutil.mmp lightblue-0.3.2/src/series60/build/s60-2ndEd/._lightblue.pkg0000755000076500000000000000012210767433740023140 0ustar beawheel00000000000000Mac OS X  2 RTEXTlightblue-0.3.2/src/series60/build/s60-2ndEd/lightblue.pkg0000755000076500007650000000127410767433740022401 0ustar beabea00000000000000; LightBlue v 0.3.2 #{"LightBlue"}, (0x200031CC), 0, 3, 2 ; UID for this Series 60 edition (0x101f7960), 0, 0, 0, {"Series60ProductID"} ;;; Python for Series 60 (0x10201510), 0, 0, 0, {"Python"} ; Python sources "..\..\src\__init__.py" - "!:\system\libs\lightblue\__init__.py" "..\..\src\_lightblue.py" - "!:\system\libs\lightblue\_lightblue.py" "..\..\src\obex.py" - "!:\system\libs\lightblue\obex.py" "..\..\src\_obex.py" - "!:\system\libs\lightblue\_obex.py" "..\..\src\_lightbluecommon.py" - "!:\system\libs\lightblue\_lightbluecommon.py" "..\..\src\_obexcommon.py" - "!:\system\libs\lightblue\_obexcommon.py" ; Extensions "_lightblueutil.pyd" - "!:\system\libs\lightblue\_lightblueutil.pyd" lightblue-0.3.2/src/series60/build/s60-2ndEd_FP2/0000755000076500007650000000000010770565177020363 5ustar beabea00000000000000lightblue-0.3.2/src/series60/build/s60-2ndEd_FP2/_lightblueutil.mmp0000755000076500007650000000141110660027774024104 0ustar beabea00000000000000TARGETTYPE dll TARGET _lightblueutil.pyd TARGETPATH \system\libs #ifdef EKA2 CAPABILITY NetworkServices LocalServices ReadUserData WriteUserData UserEnvironment #endif NOSTRICTDEF EXPORTUNFROZEN SYSTEMINCLUDE \epoc32\include SYSTEMINCLUDE \epoc32\include\libc SYSTEMINCLUDE \epoc32\include\python USERINCLUDE ..\..\src\_lightblueutil LIBRARY python222.lib LIBRARY euser.lib LIBRARY estlib.lib LIBRARY btextnotifiers.lib LIBRARY esock.lib bluetooth.lib btdevice.lib SOURCEPATH ..\..\src\_lightblueutil SOURCE apnresolver.cpp SOURCEPATH ..\..\src\_lightblueutil SOURCE localepocpyutils.cpp SOURCEPATH ..\..\src\_lightblueutil SOURCE panic.cpp SOURCEPATH ..\..\src\_lightblueutil SOURCE _lightblueutil.cpp macro __SYMBIAN_8__ lightblue-0.3.2/src/series60/build/s60-2ndEd_FP2/bld.inf0000755000076500007650000000014010660027774021612 0ustar beabea00000000000000 PRJ_PLATFORMS #ifdef EKA2 gcce winscw #else wins armi #endif PRJ_MMPFILES _lightblueutil.mmp lightblue-0.3.2/src/series60/build/s60-2ndEd_FP2/._lightblue.pkg0000755000076500000000000000012210767433733023611 0ustar beawheel00000000000000Mac OS X  2 RTEXTlightblue-0.3.2/src/series60/build/s60-2ndEd_FP2/lightblue.pkg0000755000076500007650000000127410767433733023052 0ustar beabea00000000000000; LightBlue v 0.3.2 #{"LightBlue"}, (0x200031CC), 0, 3, 2 ; UID for this Series 60 edition (0x10200bab), 0, 0, 0, {"Series60ProductID"} ;;; Python for Series 60 (0x10201510), 0, 0, 0, {"Python"} ; Python sources "..\..\src\__init__.py" - "!:\system\libs\lightblue\__init__.py" "..\..\src\_lightblue.py" - "!:\system\libs\lightblue\_lightblue.py" "..\..\src\obex.py" - "!:\system\libs\lightblue\obex.py" "..\..\src\_obex.py" - "!:\system\libs\lightblue\_obex.py" "..\..\src\_lightbluecommon.py" - "!:\system\libs\lightblue\_lightbluecommon.py" "..\..\src\_obexcommon.py" - "!:\system\libs\lightblue\_obexcommon.py" ; Extensions "_lightblueutil.pyd" - "!:\system\libs\lightblue\_lightblueutil.pyd" lightblue-0.3.2/src/series60/build/s60-2ndEd_FP3/0000755000076500007650000000000010770565177020364 5ustar beabea00000000000000lightblue-0.3.2/src/series60/build/s60-2ndEd_FP3/_lightblueutil.mmp0000755000076500007650000000142510660027774024112 0ustar beabea00000000000000TARGETTYPE dll TARGET _lightblueutil.pyd TARGETPATH \system\libs #ifdef EKA2 CAPABILITY NetworkServices LocalServices ReadUserData WriteUserData UserEnvironment #endif NOSTRICTDEF DEFFILE _lightblueutil.frz SYSTEMINCLUDE \epoc32\include SYSTEMINCLUDE \epoc32\include\libc SYSTEMINCLUDE \epoc32\include\python USERINCLUDE ..\..\src\_lightblueutil LIBRARY python222.lib LIBRARY euser.lib LIBRARY estlib.lib LIBRARY btextnotifiers.lib LIBRARY esock.lib bluetooth.lib btdevice.lib SOURCEPATH ..\..\src\_lightblueutil SOURCE apnresolver.cpp SOURCEPATH ..\..\src\_lightblueutil SOURCE localepocpyutils.cpp SOURCEPATH ..\..\src\_lightblueutil SOURCE panic.cpp SOURCEPATH ..\..\src\_lightblueutil SOURCE _lightblueutil.cpp macro __SYMBIAN_8__ lightblue-0.3.2/src/series60/build/s60-2ndEd_FP3/bld.inf0000755000076500007650000000014010660027774021613 0ustar beabea00000000000000 PRJ_PLATFORMS #ifdef EKA2 gcce winscw #else wins armi #endif PRJ_MMPFILES _lightblueutil.mmp lightblue-0.3.2/src/series60/build/s60-2ndEd_FP3/._lightblue.pkg0000755000076500000000000000012210767433725023613 0ustar beawheel00000000000000Mac OS X  2 RTEXTlightblue-0.3.2/src/series60/build/s60-2ndEd_FP3/lightblue.pkg0000755000076500007650000000127410767433725023054 0ustar beabea00000000000000; LightBlue v 0.3.2 #{"LightBlue"}, (0x200031CC), 0, 3, 2 ; UID for this Series 60 edition (0x102032bd), 0, 0, 0, {"Series60ProductID"} ;;; Python for Series 60 (0x10201510), 0, 0, 0, {"Python"} ; Python sources "..\..\src\__init__.py" - "!:\system\libs\lightblue\__init__.py" "..\..\src\_lightblue.py" - "!:\system\libs\lightblue\_lightblue.py" "..\..\src\obex.py" - "!:\system\libs\lightblue\obex.py" "..\..\src\_obex.py" - "!:\system\libs\lightblue\_obex.py" "..\..\src\_lightbluecommon.py" - "!:\system\libs\lightblue\_lightbluecommon.py" "..\..\src\_obexcommon.py" - "!:\system\libs\lightblue\_obexcommon.py" ; Extensions "_lightblueutil.pyd" - "!:\system\libs\lightblue\_lightblueutil.pyd" lightblue-0.3.2/src/series60/build/s60-3rdEd/0000755000076500007650000000000010770565177017721 5ustar beabea00000000000000lightblue-0.3.2/src/series60/build/s60-3rdEd/_lightblueutil.mmp0000755000076500007650000000141210660027773023442 0ustar beabea00000000000000TARGETTYPE dll TARGET _lightblueutil.pyd TARGETPATH \system\libs #ifdef EKA2 CAPABILITY NetworkServices LocalServices ReadUserData WriteUserData UserEnvironment #endif NOSTRICTDEF EXPORTUNFROZEN SYSTEMINCLUDE \epoc32\include SYSTEMINCLUDE \epoc32\include\libc SYSTEMINCLUDE \epoc32\include\python USERINCLUDE ..\..\src\_lightblueutil LIBRARY python222.lib LIBRARY euser.lib LIBRARY estlib.lib LIBRARY btextnotifiers.lib LIBRARY esock.lib bluetooth.lib btdevice.lib SOURCEPATH ..\..\src\_lightblueutil SOURCE apnresolver.cpp SOURCEPATH ..\..\src\_lightblueutil SOURCE localepocpyutils.cpp SOURCEPATH ..\..\src\_lightblueutil SOURCE panic.cpp SOURCEPATH ..\..\src\_lightblueutil SOURCE _lightblueutil.cpp macro __SYMBIAN_9__ lightblue-0.3.2/src/series60/build/s60-3rdEd/bld.inf0000755000076500007650000000014010564554670021153 0ustar beabea00000000000000 PRJ_PLATFORMS #ifdef EKA2 gcce winscw #else wins armi #endif PRJ_MMPFILES _lightblueutil.mmp lightblue-0.3.2/src/series60/build/s60-3rdEd/lightblue.pkg0000755000076500000000000000125610767445324022742 0ustar beawheel00000000000000&EN ; LightBlue v 0.3.2 (UID for 3rd Edition) #{"LightBlue"}, (0xA000211E), 0, 3, 2, TYPE=SA %{"Vendor-EN"} :"Vendor" ; Supports Series 60 3rd Edition [0x101F7961], 0, 0, 0, {"S60ProductID"} ; Python sources "..\..\src\__init__.py" - "!:\resource\lightblue\__init__.py" "..\..\src\_lightblue.py" - "!:\resource\lightblue\_lightblue.py" "..\..\src\obex.py" - "!:\resource\lightblue\obex.py" "..\..\src\_obex.py" - "!:\resource\lightblue\_obex.py" "..\..\src\_lightbluecommon.py" - "!:\resource\lightblue\_lightbluecommon.py" "..\..\src\_obexcommon.py" - "!:\resource\lightblue\_obexcommon.py" ; Extensions "_lightblueutil.pyd" - "!:\sys\bin\_lightblueutil.pyd" lightblue-0.3.2/src/series60/build/s60-3rdEd_freedevcert/0000755000076500007650000000000010770565177022277 5ustar beabea00000000000000lightblue-0.3.2/src/series60/build/s60-3rdEd_freedevcert/_lightblueutil.mmp0000755000076500007650000000154610660027773026030 0ustar beabea00000000000000TARGETTYPE dll TARGET _lightblueutil.pyd TARGETPATH \system\libs #ifdef EKA2 CAPABILITY NetworkServices LocalServices ReadUserData WriteUserData Location UserEnvironment PowerMgmt ProtServ SwEvent SurroundingsDD ReadDeviceData WriteDeviceData TrustedUI #endif NOSTRICTDEF EXPORTUNFROZEN SYSTEMINCLUDE \epoc32\include SYSTEMINCLUDE \epoc32\include\libc SYSTEMINCLUDE \epoc32\include\python USERINCLUDE ..\..\src\_lightblueutil LIBRARY python222.lib LIBRARY euser.lib LIBRARY estlib.lib LIBRARY btextnotifiers.lib LIBRARY esock.lib bluetooth.lib btdevice.lib SOURCEPATH ..\..\src\_lightblueutil SOURCE apnresolver.cpp SOURCEPATH ..\..\src\_lightblueutil SOURCE localepocpyutils.cpp SOURCEPATH ..\..\src\_lightblueutil SOURCE panic.cpp SOURCEPATH ..\..\src\_lightblueutil SOURCE _lightblueutil.cpp macro __SYMBIAN_9__ lightblue-0.3.2/src/series60/build/s60-3rdEd_freedevcert/bld.inf0000755000076500007650000000014010660027773023525 0ustar beabea00000000000000 PRJ_PLATFORMS #ifdef EKA2 gcce winscw #else wins armi #endif PRJ_MMPFILES _lightblueutil.mmp lightblue-0.3.2/src/series60/build/s60-3rdEd_freedevcert/lightblue.pkg0000755000076500000000000000125610767445412025316 0ustar beawheel00000000000000&EN ; LightBlue v 0.3.2 (UID for 3rd Edition) #{"LightBlue"}, (0xA000211E), 0, 3, 2, TYPE=SA %{"Vendor-EN"} :"Vendor" ; Supports Series 60 3rd Edition [0x101F7961], 0, 0, 0, {"S60ProductID"} ; Python sources "..\..\src\__init__.py" - "!:\resource\lightblue\__init__.py" "..\..\src\_lightblue.py" - "!:\resource\lightblue\_lightblue.py" "..\..\src\obex.py" - "!:\resource\lightblue\obex.py" "..\..\src\_obex.py" - "!:\resource\lightblue\_obex.py" "..\..\src\_lightbluecommon.py" - "!:\resource\lightblue\_lightbluecommon.py" "..\..\src\_obexcommon.py" - "!:\resource\lightblue\_obexcommon.py" ; Extensions "_lightblueutil.pyd" - "!:\sys\bin\_lightblueutil.pyd" lightblue-0.3.2/src/series60/src/0000755000076500007650000000000010770565177016042 5ustar beabea00000000000000lightblue-0.3.2/src/series60/src/.___init__.py0000755000076500000000000000012210767432712020713 0ustar beawheel00000000000000Mac OS X  2 RTEXTlightblue-0.3.2/src/series60/src/__init__.py0000755000076500007650000001515210767432712020154 0ustar beabea00000000000000# Copyright (c) 2006 Bea Lam. All rights reserved. # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files # (the "Software"), to deal in the Software without restriction, # including without limitation the rights to use, copy, modify, merge, # publish, distribute, sublicense, and/or sell copies of the Software, # and to permit persons to whom the Software is furnished to do so, # subject to the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. "LightBlue - a simple bluetooth library." # Docstrings for attributes in this module. _docstrings = { "finddevices": """ Performs a device discovery and returns the found devices as a list of (address, name, class-of-device) tuples. Raises BluetoothError if an error occurs. Arguments: - getnames=True: True if device names should be retrieved during discovery. If false, None will be returned instead of the device name. - length=10: the number of seconds to spend discovering devices (this argument has no effect on Python for Series 60) Do not invoke a new discovery before a previous discovery has finished. Also, to minimise interference with other wireless and bluetooth traffic, and to conserve battery power on the local device, discoveries should not be invoked too frequently (an interval of at least 20 seconds is recommended). """, "findservices": """ Performs a service discovery and returns the found services as a list of (device-address, service-port, service-name) tuples. Raises BluetoothError if an error occurs. Arguments: - addr=None: a device address, to search only for services on a specific device - name=None: a service name string, to search only for a service with a specific name - servicetype=None: can be RFCOMM or OBEX to search only for RFCOMM or OBEX-type services. (OBEX services are not returned from an RFCOMM search) If more than one criteria is specified, this returns services that match all criteria. Currently the Python for Series 60 implementation will only find RFCOMM and OBEX services. """, "finddevicename": """ Returns the name of the device with the given bluetooth address. finddevicename(gethostaddr()) returns the local device name. Arguments: - address: the address of the device to look up - usecache=True: if True, the device name will be fetched from a local cache if possible. If False, or if the device name is not in the cache, the remote device will be contacted to request its name. Raise BluetoothError if the name cannot be retrieved. """, "gethostaddr": """ Returns the address of the local bluetooth device. Raise BluetoothError if the local device is not available. """, "gethostclass": """ Returns the class of device of the local bluetooth device. These values indicate the device's major services and the type of the device (e.g. mobile phone, laptop, etc.). If you google for "assigned numbers bluetooth baseband" you might find some documents that discuss how to extract this information from the class of device. Raise BluetoothError if the local device is not available. """, "socket": """ socket(proto=RFCOMM) -> socket object Returns a new socket object. Arguments: - proto=RFCOMM: the type of socket to be created - either L2CAP or RFCOMM. Note that L2CAP sockets are not available on Python For Series 60, and only L2CAP client sockets are supported on Mac OS X and Linux (i.e. you can connect() the socket but not bind(), accept(), etc.). """, "advertise": """ Starts advertising a service with the given name, using the given server socket. Raises BluetoothError if the service cannot be advertised. Arguments: - name: name of the service to be advertised - sock: the socket object that will serve this service. The socket must be already bound to a channel. If a RFCOMM service is being advertised, the socket should also be listening. - servicetype: the type of service to advertise - either RFCOMM or OBEX. (L2CAP services are not currently supported.) (If the servicetype is RFCOMM, the service will be advertised with the Serial Port Profile; if the servicetype is OBEX, the service will be advertised with the OBEX Object Push Profile.) """, "stopadvertise": """ Stops advertising the service on the given socket. Raises BluetoothError if no service is advertised on the socket. This will error if the given socket is already closed. """, "selectdevice": """ Displays a GUI which allows the end user to select a device from a list of discovered devices. Returns the selected device as an (address, name, class-of-device) tuple. Returns None if the selection was cancelled. (On Python For Series 60, the device selection will fail if there are any open bluetooth connections.) """, "selectservice": """ Displays a GUI which allows the end user to select a service from a list of discovered devices and their services. Returns the selected service as a (device-address, service-port, service- name) tuple. Returns None if the selection was cancelled. (On Python For Series 60, the device selection will fail if there are any open bluetooth connections.) Currently the Python for Series 60 implementation will only find RFCOMM and OBEX services. """ } # import implementation modules from _lightblue import * from _lightbluecommon import * import obex # plus submodule # set docstrings import _lightblue localattrs = locals() for attr in _lightblue.__all__: try: localattrs[attr].__doc__ = _docstrings[attr] except KeyError: pass del attr, localattrs lightblue-0.3.2/src/series60/src/.__lightblue.py0000755000076500000000000000012210756007234021265 0ustar beawheel00000000000000Mac OS X  2 RTEXTlightblue-0.3.2/src/series60/src/_lightblue.py0000755000076500007650000003222110756007234020522 0ustar beabea00000000000000import socket as _socket import _lightbluecommon # public attributes __all__ = ("finddevices", "findservices", "finddevicename", "gethostaddr", "gethostclass", "socket", "advertise", "stopadvertise", "selectdevice", "selectservice") # details of advertised services __advertised = {} def finddevices(getnames=True, length=10): # originally this used DiscoverDevices in _lightblueutil extension, but # that blocks the UI import e32 inquiry = _DeviceInquiry() inquiry.start(getnames, length) timer = None try: while not inquiry.isdone(): # keep waiting timer = e32.Ao_timer() timer.after(0.1) finally: inquiry.stop() if timer is not None: timer.cancel() return inquiry.getfounddevices() def findservices(addr=None, name=None, servicetype=None): if servicetype is None: funcs = (_socket.bt_discover, _socket.bt_obex_discover) elif servicetype == _lightbluecommon.RFCOMM: funcs = (_socket.bt_discover, ) elif servicetype == _lightbluecommon.OBEX: funcs = (_socket.bt_obex_discover, ) else: raise ValueError("servicetype must be RFCOMM, OBEX or None, was %s" % \ servicetype) if addr is None: devices = finddevices() btaddrs = [d[0] for d in devices] else: btaddrs = [addr] services = [] for addr in btaddrs: for func in funcs: try: devaddr, servicesdict = func(addr) except _socket.error, e: #raise _lightbluecommon.BluetoothError(str(e)) print "[lightblue] cannot look up services for %s" % addr continue if name is not None: for servicename in servicesdict.keys(): if servicename != name: del servicesdict[servicename] services.extend(_getservicetuples(devaddr, servicesdict)) return services def finddevicename(address, usecache=True): if not _lightbluecommon._isbtaddr(address): raise ValueError("%s is not a valid bluetooth address" % str(address)) if address == gethostaddr(): return _gethostname() try: # lookupName() expects address without colon separators import _lightblueutil address_no_sep = address.replace(":", "").replace("-", "") name = _lightblueutil.lookupName(address_no_sep, (not usecache)) except SymbianError, e: raise _lightbluecommon.BluetoothError( "Cannot find device name for %s: %s" % (address, str(e))) return name def gethostaddr(): import _lightblueutil try: addr = _lightblueutil.getLocalAddress() except SymbianError, exc: raise _lightbluecommon.BluetoothError( "Cannot read local device address: " + str(exc)) return addr def gethostclass(): import _lightblueutil try: cod = _lightblueutil.getLocalDeviceClass() except SymbianError, exc: raise _lightbluecommon.BluetoothError( "Cannot read local device class: " + str(exc)) return cod def _gethostname(): import _lightblueutil try: name = _lightblueutil.getLocalName() except SymbianError, exc: raise _lightbluecommon.BluetoothError( "Cannot read local device name: " + str(exc)) return name class _SocketWrapper(object): def __init__(self, sock, connaddr=()): self.__dict__["_sock"] = sock self._setconnaddr(connaddr) # must implement accept() to return _SocketWrapper objects def accept(self): conn, addr = self._sock.accept() # modify returned address cos PyS60 accept() only returns address, not # (addr, channel) tuple addrtuple = (addr.upper(), self._connaddr[1]) return (_SocketWrapper(conn, addrtuple), addrtuple) accept.__doc__ = _lightbluecommon._socketdocs["accept"] def bind(self, addr): # if port==0, find an available port if addr[1] == 0: addr = (addr[0], _getavailableport(self)) try: self._sock.bind(addr) except Exception, e: raise _socket.error(str(e)) self._setconnaddr(addr) bind.__doc__ = _lightbluecommon._socketdocs["bind"] def close(self): self._sock.close() # try to stop advertising try: stopadvertise(self) except: pass close.__doc__ = _lightbluecommon._socketdocs["close"] def connect(self, addr): self._sock.connect(addr) self._setconnaddr(addr) connect.__doc__ = _lightbluecommon._socketdocs["connect"] def connect_ex(self, addr): try: self.connect(addr) except _socket.error, e: return e.args[0] return 0 connect_ex.__doc__ = _lightbluecommon._socketdocs["connect_ex"] # must implement dup() to return _SocketWrapper objects def dup(self): return _SocketWrapper(self._sock.dup()) dup.__doc__ = _lightbluecommon._socketdocs["dup"] def listen(self, backlog): self._sock.listen(backlog) # when listen() is called, set a default security level since S60 # sockets are required to have a security level # This should be changed later to allow to set security using # setsockopt() _socket.set_security(self._sock, _socket.AUTH) listen.__doc__ = _lightbluecommon._socketdocs["listen"] # PyS60 raises socket.error("Bad protocol") when this is called for stream # sockets, but implement it here like recv() for consistency with Linux+Mac def recvfrom(self, bufsize, flags=0): return (self._sock.recv(bufsize, flags), None) recvfrom.__doc__ = _lightbluecommon._socketdocs["recvfrom"] # PyS60 raises socket.error("Bad protocol") when this is called for stream # sockets, but implement it here like send() for consistency with Linux+Mac def sendto(self, data, *extra): if len(extra) == 1: address = extra[0] flags = 0 elif len(extra) == 2: flags, address = extra else: raise TypeError("sendto takes at most 3 arguments (%d given)" % \ (len(extra) + 1)) return self._sock.send(data, flags) sendto.__doc__ = _lightbluecommon._socketdocs["sendto"] # sendall should return None on success but PyS60 seems to have it return # bytes sent like send def sendall(self, data, flags=0): self.send(data, flags) return None sendall.__doc__ = _lightbluecommon._socketdocs["sendall"] # implement to return (remote-address, common-channel) like PyBluez # (PyS60 implementation raises error when this method is called, saying # it's not implemented - maybe cos a remote BT socket doesn't really have # an outgoing channel like TCP sockets? But it seems handy to return the # channel we're communicating over anyway i.e. the local RFCOMM channel) def getpeername(self): if not self._connaddr: raise _socket.error(57, "Socket is not connected") return self._connaddr getpeername.__doc__ = _lightbluecommon._socketdocs["getpeername"] # like getpeername(), PyS60 does not implement this method def getsockname(self): if not self._connaddr: # sock is neither bound nor connected return ("00:00:00:00:00:00", 0) return (gethostaddr(), self._connaddr[1]) getsockname.__doc__ = _lightbluecommon._socketdocs["getsockname"] def fileno(self): raise NotImplementedError fileno.__doc__ = _lightbluecommon._socketdocs["fileno"] def settimeout(self, timeout): raise NotImplementedError settimeout.__doc__ = _lightbluecommon._socketdocs["settimeout"] def gettimeout(self): return None gettimeout.__doc__ = _lightbluecommon._socketdocs["gettimeout"] def _setconnaddr(self, connaddr): if len(connaddr) == 2: connaddr = (connaddr[0].upper(), connaddr[1]) self.__dict__["_connaddr"] = connaddr # wrap all other socket methods, to set LightBlue-specific docstrings _othermethods = [_m for _m in _lightbluecommon._socketdocs.keys() \ if _m not in locals()] # methods other than those already defined _methoddef = """def %s(self, *args, **kwargs): return self._sock.%s(*args, **kwargs) %s.__doc__ = _lightbluecommon._socketdocs['%s']\n""" for _m in _othermethods: exec _methoddef % (_m, _m, _m, _m) del _m, _methoddef def socket(proto=_lightbluecommon.RFCOMM): if proto == _lightbluecommon.L2CAP: raise NotImplementedError("L2CAP sockets not supported on this platform") sock = _socket.socket(_socket.AF_BT, _socket.SOCK_STREAM, _socket.BTPROTO_RFCOMM) return _SocketWrapper(sock) def _getavailableport(sock): # can just use bt_rfcomm_get_available_server_channel since only RFCOMM is # currently supported return _socket.bt_rfcomm_get_available_server_channel(sock._sock) def advertise(name, sock, servicetype): if servicetype == _lightbluecommon.RFCOMM: servicetype = _socket.RFCOMM elif servicetype == _lightbluecommon.OBEX: servicetype = _socket.OBEX else: raise ValueError("servicetype must be either RFCOMM or OBEX") name = unicode(name) # advertise the service _socket.bt_advertise_service(name, sock._sock, True, servicetype) # note details, for if advertising needs to be stopped later __advertised[id(sock)] = (name, servicetype) def stopadvertise(sock): details = __advertised.get(id(sock)) if details is None: raise _lightbluecommon.BluetoothError("no service advertised") name, servicetype = details _socket.bt_advertise_service(name, sock._sock, False, servicetype) def selectdevice(): import _lightblueutil try: result = _lightblueutil.selectDevice() except SymbianError, e: raise _lightbluecommon.BluetoothError(str(e)) # underlying method returns class of device as tuple, not whole class devinfo = (result[0], result[1], _lightbluecommon._joinclass(result[2])) return devinfo def selectservice(): device = selectdevice() if device is None: return None import appuifw services = findservices(addr=device[0]) choice = appuifw.popup_menu( [unicode("%d: %s" % (s[1], s[2])) for s in services], u"Choose service:") if choice is None: return None return services[choice] # Returns a list of (addr, channel, name) service tuples from a device # address and a dictionary of {name: channel} mappings. def _getservicetuples(devaddr, servicesdict): return [(devaddr.upper(), channel, name) for name, channel in servicesdict.items()] class _DeviceInquiry(object): def __init__(self): super(_DeviceInquiry, self).__init__() self._founddevices = [] self._resolver = None self._done = False def start(self, getnames, length): self._founddevices = [] self._done = False import _lightblueutil self._resolver = _lightblueutil.AoResolver() self._resolver.open() self._resolver.discover(self._founddevice, None, getnames) def isdone(self): return self._done def stop(self): if self.isdone(): return if self._resolver: self._resolver.cancel() self._resolver.close() self._done = True def getfounddevices(self): return self._founddevices[:] def _founddevice(self, err, addr, name, devclass, param): try: if err == 0: # no err #print "Found device", addr # PDIS AoResolver returns addres without the colons addr = addr[0:2] + ":" + addr[2:4] + ":" + addr[4:6] + ":" + \ addr[6:8] + ":" + addr[8:10] + ":" + addr[10:12] devinfo = (addr.encode("utf-8").upper(), name, _lightbluecommon._joinclass(devclass)) self._founddevices.append(devinfo) # keep looking for devices self._resolver.next() else: if err == -25: # KErrEof (no more devices) # finished discovery self._resolver.close() self._done = True else: print "[lightblue] device discovery error (%d)" % err except Exception, e: # catch all exceptions, the app will crash if exception is raised # during callback print "Error during _founddevice() callback: "+ str(e) lightblue-0.3.2/src/series60/src/.__lightbluecommon.py0000755000076500000000000000012210770142100022462 0ustar beawheel00000000000000Mac OS X  2 RTEXTlightblue-0.3.2/src/series60/src/_lightbluecommon.py0000755000076500007650000002573710770142100021735 0ustar beabea00000000000000# Copyright (c) 2006 Bea Lam. All rights reserved. # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files # (the "Software"), to deal in the Software without restriction, # including without limitation the rights to use, copy, modify, merge, # publish, distribute, sublicense, and/or sell copies of the Software, # and to permit persons to whom the Software is furnished to do so, # subject to the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # Defines attributes with common implementations across the different # platforms. # public attributes __all__ = ("L2CAP", "RFCOMM", "OBEX", "BluetoothError", "splitclass") # Protocol/service class types, used for sockets and advertising services L2CAP, RFCOMM, OBEX = (10, 11, 12) class BluetoothError(IOError): """ Generic exception raised for Bluetooth errors. This is not raised for socket-related errors; socket objects raise the socket.error and socket.timeout exceptions from the standard library socket module. Note that error codes are currently platform-independent. In particular, the Mac OS X implementation returns IOReturn error values from the IOKit framework, and OBEXError codes from for OBEX operations. """ pass def splitclass(classofdevice): """ Splits the given class of device to return a 3-item tuple with the major service class, major device class and minor device class values. These values indicate the device's major services and the type of the device (e.g. mobile phone, laptop, etc.). If you google for "assigned numbers bluetooth baseband" you might find some documents that discuss how to extract this information from the class of device. Example: >>> splitclass(1057036) (129, 1, 3) >>> """ if not isinstance(classofdevice, int): try: classofdevice = int(classofdevice) except (TypeError, ValueError): raise TypeError("Given device class '%s' cannot be split" % \ str(classofdevice)) data = classofdevice >> 2 # skip over the 2 "format" bits service = data >> 11 major = (data >> 6) & 0x1F minor = data & 0x3F return (service, major, minor) _validbtaddr = None def _isbtaddr(address): """ Returns whether the given address is a valid bluetooth address. For example, "00:0e:6d:7b:a2:0a" is a valid address. Returns False if the argument is None or is not a string. """ # Define validity regex. Accept either ":" or "-" as separators. global _validbtaddr if _validbtaddr is None: import re _validbtaddr = re.compile("((\d|[a-f]){2}(:|-)){5}(\d|[a-f]){2}", re.IGNORECASE) import types if not isinstance(address, types.StringTypes): return False return _validbtaddr.match(address) is not None # --------- other attributes --------- def _joinclass(codtuple): """ The opposite of splitclass(). Joins a (service, major, minor) class-of- device tuple into a whole class of device value. """ if not isinstance(codtuple, tuple): raise TypeError("argument must be tuple, was %s" % type(codtuple)) if len(codtuple) != 3: raise ValueError("tuple must have 3 items, has %d" % len(codtuple)) serviceclass = codtuple[0] << 2 << 11 majorclass = codtuple[1] << 2 << 6 minorclass = codtuple[2] << 2 return (serviceclass | majorclass | minorclass) # Docstrings for socket objects. # Based on std lib socket docs. _socketdocs = { "accept": """ accept() -> (socket object, address info) Wait for an incoming connection. Return a new socket representing the connection, and the address of the client. For RFCOMM sockets, the address info is a pair (hostaddr, channel). The socket must be bound and listening before calling this method. """, "bind": """ bind(address) Bind the socket to a local address. For RFCOMM sockets, the address is a pair (host, channel); the host must refer to the local host. A port value of 0 binds the socket to a dynamically assigned port. (Note that on Mac OS X, the port value must always be 0.) The socket must not already be bound. """, "close": """ close() Close the socket. It cannot be used after this call. """, "connect": """ connect(address) Connect the socket to a remote address. The address should be a (host, channel) pair for RFCOMM sockets, and a (host, PSM) pair for L2CAP sockets. The socket must not be already connected. """, "connect_ex": """ connect_ex(address) -> errno This is like connect(address), but returns an error code instead of raising an exception when an error occurs. """, "dup": """ dup() -> socket object Returns a new socket object connected to the same system resource. """, "fileno": """ fileno() -> integer Return the integer file descriptor of the socket. Raises NotImplementedError on Mac OS X and Python For Series 60. """, "getpeername": """ getpeername() -> address info Return the address of the remote endpoint. The address info is a (host, channel) pair for RFCOMM sockets, and a (host, PSM) pair for L2CAP sockets. If the socket has not been connected, socket.error will be raised. """, "getsockname": """ getsockname() -> address info Return the address of the local endpoint. The address info is a (host, channel) pair for RFCOMM sockets, and a (host, PSM) pair for L2CAP sockets. If the socket has not been connected nor bound, this returns the tuple ("00:00:00:00:00:00", 0). """, "getsockopt": """ getsockopt(level, option[, bufsize]) -> value Get a socket option. See the Unix manual for level and option. If a nonzero buffersize argument is given, the return value is a string of that length; otherwise it is an integer. Currently support for socket options are platform independent -- i.e. depends on the underlying Series 60 or BlueZ socket options support. The Mac OS X implementation currently does not support any options at all and automatically raises socket.error. """, "gettimeout": """ gettimeout() -> timeout Returns the timeout in floating seconds associated with socket operations. A timeout of None indicates that timeouts on socket operations are disabled. Currently not supported on Python For Series 60 implementation, which will always return None. """, "listen": """ listen(backlog) Enable a server to accept connections. The backlog argument must be at least 1; it specifies the number of unaccepted connection that the system will allow before refusing new connections. The socket must not be already listening. Currently not implemented on Mac OS X. """, "makefile": """ makefile([mode[, bufsize]]) -> file object Returns a regular file object corresponding to the socket. The mode and bufsize arguments are as for the built-in open() function. """, "recv": """ recv(bufsize[, flags]) -> data Receive up to bufsize bytes from the socket. For the optional flags argument, see the Unix manual. When no data is available, block until at least one byte is available or until the remote end is closed. When the remote end is closed and all data is read, return the empty string. Currently the flags argument has no effect on Mac OS X. """, "recvfrom": """ recvfrom(bufsize[, flags]) -> (data, address info) Like recv(buffersize, flags) but also return the sender's address info. """, "send": """ send(data[, flags]) -> count Send a data string to the socket. For the optional flags argument, see the Unix manual. Return the number of bytes sent. The socket must be connected to a remote socket. Currently the flags argument has no effect on Mac OS X. """, "sendall": """ sendall(data[, flags]) Send a data string to the socket. For the optional flags argument, see the Unix manual. This calls send() repeatedly until all data is sent. If an error occurs, it's impossible to tell how much data has been sent. """, "sendto": """ sendto(data[, flags], address) -> count Like send(data, flags) but allows specifying the destination address. For RFCOMM sockets, the address is a pair (hostaddr, channel). """, "setblocking": """ setblocking(flag) Set the socket to blocking (flag is true) or non-blocking (false). setblocking(True) is equivalent to settimeout(None); setblocking(False) is equivalent to settimeout(0.0). Initially a socket is in blocking mode. In non-blocking mode, if a socket operation cannot be performed immediately, socket.error is raised. The underlying implementation on Python for Series 60 only supports non-blocking mode for send() and recv(), and ignores it for connect() and accept(). """, "setsockopt": """ setsockopt(level, option, value) Set a socket option. See the Unix manual for level and option. The value argument can either be an integer or a string. Currently support for socket options are platform independent -- i.e. depends on the underlying Series 60 or BlueZ socket options support. The Mac OS X implementation currently does not support any options at all and automatically raise socket.error. """, "settimeout": """ settimeout(timeout) Set a timeout on socket operations. 'timeout' can be a float, giving in seconds, or None. Setting a timeout of None disables the timeout feature and is equivalent to setblocking(1). Setting a timeout of zero is the same as setblocking(0). If a timeout is set, the connect, accept, send and receive operations will raise socket.timeout if a timeout occurs. Raises NotImplementedError on Python For Series 60. """, "shutdown": """ shutdown(how) Shut down the reading side of the socket (flag == socket.SHUT_RD), the writing side of the socket (flag == socket.SHUT_WR), or both ends (flag == socket.SHUT_RDWR). """ } lightblue-0.3.2/src/series60/src/_lightblueutil/0000755000076500007650000000000010770565177021056 5ustar beabea00000000000000lightblue-0.3.2/src/series60/src/_lightblueutil/_lightblueutil.cpp0000755000076500007650000004557210660027774024610 0ustar beabea00000000000000/* * Copyright (c) 2006 Bea Lam. All rights reserved. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation files * (the "Software"), to deal in the Software without restriction, * including without limitation the rights to use, copy, modify, merge, * publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ // This file provides a Python extension for performing operations not offered // in the standard Python for Series 60 API, including: // - non-GUI discovery // - access to local device information #include #include #include #include #include #if defined(__SYMBIAN_9__) || defined(__SYMBIAN_8__) # include #endif #include // TBTDeviceResponseParamsPckg #include #include #include // structure that will hold the info about a remote device struct TDeviceData { THostName iDeviceName; TBTDevAddr iDeviceAddr; TInt16 iServiceClass; TInt8 iMajorClass; TInt8 iMinorClass; }; // typedef for a list of TDeviceData structures typedef RPointerArray TDeviceDataList; // Converts a TBTDevAddr to a bluetooth address string // Arguments: // - addr - the address to convert // - aString - the TDes8 to hold the converted string result static void DevAddressToString(TBTDevAddr& addr, TDes8& aString) { // from PDIS miso library // GetReadable() does not produce a "standard" result, // so have to construct a string manually. aString.Zero(); _LIT8(KColon, ":"); for (TInt i=0; i<6; i++) { const TUint8& val = addr[i]; aString.AppendNumFixedWidthUC(val, EHex, 2); if (i < 5) aString.Append(KColon); } } // Presents a Device Selection UI and returns the error code. // // Arguments: // - aResponse - the object to hold the details of the selected device. // // Returns an error code. TInt SelectDeviceUI(TBTDeviceResponseParamsPckg& aResponse) { TInt err; RNotifier notifier; err = notifier.Connect(); if (err) return err; TBTDeviceSelectionParamsPckg selectionFilter; TRequestStatus status; notifier.StartNotifierAndGetResponse( status, KDeviceSelectionNotifierUid, selectionFilter, aResponse ); User::WaitForRequest(status); err = status.Int(); notifier.CancelNotifier(KDeviceSelectionNotifierUid); notifier.Close(); return err; } // Wraps SelectDeviceUI() to provide a Python method interface. // Takes no arguments. // // Returns None if user cancelled, otherwise returns a // (name, address, (service,major,minor)) Python tuple. static PyObject* LightBlue_SelectDevice(PyObject* self, PyObject* args) { if (!PyArg_ParseTuple(args, "")) return NULL; TBTDeviceResponseParamsPckg response; TInt err = SelectDeviceUI(response); if (err) { if (err == KErrCancel) { // user cancelled Py_INCREF(Py_None); return Py_None; } else { // some other error occured return SPyErr_SetFromSymbianOSErr(err); } } if (!(response().IsValidDeviceName())) { PyErr_SetString(PyExc_SymbianError, "discovery returned invalid data"); return NULL; } // get device address TBuf8<6*2+5> addrString; TBTDevAddr addr = response().BDAddr(); DevAddressToString(addr, addrString); // get device class details TBTDeviceClass deviceClass = response().DeviceClass(); TUint16 service = deviceClass.MajorServiceClass(); TUint8 major = deviceClass.MajorDeviceClass(); TUint8 minor = deviceClass.MinorDeviceClass(); return Py_BuildValue("s#u#(iii)", addrString.Ptr(), addrString.Length(), response().DeviceName().Ptr(), response().DeviceName().Length(), service, major, minor); } // Looks up the name of the device with the given address. // Arguments: // - wantedAddr - address of the device to look up. Note that the address // is expected without colons!! e.g. "000D9319C868" // - aDeviceName - the object to hold the name once it is found // - ignoreCache - if True, performs a remote name request even if the device // name is known in the cache from a previous request. // // Returns an error code. static TInt LookupName(TBTDevAddr& wantedAddr, THostName* aDeviceName, bool ignoreCache) { TInt err = KErrNone; RSocketServ socketServer; RHostResolver hostResolver; TRequestStatus status; TNameEntry nameEntry; // make a TInquirySockAddr with the address of the device we want to look up TBTSockAddr sockAddr; sockAddr.SetBTAddr(wantedAddr); TInquirySockAddr addr = addr.Cast(sockAddr); // connect err = socketServer.Connect(); if (err) return err; // load protocol for discovery TProtocolDesc protocolDesc; err = socketServer.FindProtocol(_L("BTLinkManager"), protocolDesc); if (!err) { // initialize host resolver err = hostResolver.Open(socketServer, protocolDesc.iAddrFamily, protocolDesc.iProtocol); if (!err) { // Request name lookup. // We don't need to call SetIAC() if we're just doing name lookup. // Don't put KHostResInquiry flag in SetAction(), because that // will start a device inquiry, when we just want to find the one // name. if (ignoreCache) { addr.SetAction(KHostResName|KHostResIgnoreCache); } else { addr.SetAction(KHostResName); } hostResolver.GetByAddress(addr, nameEntry, status); User::WaitForRequest(status); if (status == KErrNone) { *aDeviceName = nameEntry().iName; err = KErrNone; } else { err = KErrGeneral; } hostResolver.Close(); } } socketServer.Close(); return err; } // Wraps LookupName() to provide a Python method interface. // Arguments: // - address of the device to look up. Note that the address // is expected without colons!! e.g. "000D9319C868" // - True/False - if True, performs a remote name request even if the device // name is known in the cache from a previous request. // // Returns the name (a Python unicode string). static PyObject* LightBlue_LookupName(PyObject* self, PyObject* args) { const char *addr; bool ignoreCache; if (!PyArg_ParseTuple(args, "sb", &addr, &ignoreCache)) return NULL; THostName deviceName; TBTDevAddr wantedAddr; TBuf16<128> buf; buf.Copy(_L8(addr)); wantedAddr.SetReadable(buf); TInt err = LookupName(wantedAddr, &deviceName, ignoreCache); if (err) return SPyErr_SetFromSymbianOSErr(err); return Py_BuildValue("u#", deviceName.Ptr(), deviceName.Length()); } // Performs a device discovery. Blocks until all devices are found. // Arguments: // - aDevDataList - details of each found device will be put in a TDeviceData // and added to this list // - lookupNames - whether to perform name lookups // // Returns an error code. static TInt DiscoverDevices(TDeviceDataList* aDevDataList, bool lookupNames) { TInt err = KErrNone; RSocketServ socketServer; RHostResolver hostResolver; TInquirySockAddr addr; TRequestStatus status; TNameEntry nameEntry; // connect err = socketServer.Connect(); if (err) return err; // load protocol for discovery TProtocolDesc protocolDesc; err = socketServer.FindProtocol(_L("BTLinkManager"), protocolDesc); if (!err) { // initialize host resolver err = hostResolver.Open(socketServer, protocolDesc.iAddrFamily, protocolDesc.iProtocol); if (!err) { // start device discovery by invoking remote address lookup addr.SetIAC(KGIAC); if (lookupNames) { addr.SetAction(KHostResInquiry|KHostResName|KHostResIgnoreCache); } else { addr.SetAction(KHostResInquiry|KHostResIgnoreCache); } hostResolver.GetByAddress(addr, nameEntry, status); while( User::WaitForRequest( status ), (status == KErrNone || status == KRequestPending) ) { // store new device data entry TDeviceData *devData = new (ELeave) TDeviceData(); if (lookupNames) devData->iDeviceName = nameEntry().iName; devData->iDeviceAddr = static_cast(nameEntry().iAddr).BTAddr(); TInquirySockAddr &isa = TInquirySockAddr::Cast(nameEntry().iAddr); devData->iServiceClass = (TInt16)isa.MajorServiceClass(); devData->iMajorClass = (TInt8)isa.MajorClassOfDevice(); devData->iMinorClass = (TInt8)isa.MinorClassOfDevice(); // add device data entry to list aDevDataList->Append(devData); // get next discovered device hostResolver.Next( nameEntry, status ); } hostResolver.Close(); } } socketServer.Close(); return err; } // Wraps DiscoverDevices() to provide a Python method interface. // Arguments: // - boolean value of whether to perform name lookup // // Returns list of (address, name, (service,major,minor)) tuples detailing the // found devices. static PyObject* LightBlue_DiscoverDevices(PyObject* self, PyObject* args) { bool lookupNames; if (!PyArg_ParseTuple(args, "b", &lookupNames)) return NULL; // run the discovery TDeviceDataList devDataList; TInt err = DiscoverDevices(&devDataList, lookupNames); if (err) return SPyErr_SetFromSymbianOSErr(err); // put the results into a python list TInt i; TDeviceData *devData; TBuf8<6*2+5> addrString; PyObject *addrList = PyList_New(0); // python list to hold results for( i=0; iiDeviceAddr, addrString); PyObject *devValues; if (lookupNames) { THostName name = devData->iDeviceName; devValues = Py_BuildValue("(s#u#(iii))", addrString.Ptr(), addrString.Length(), // s# - address name.Ptr(), name.Length(), // u# - name devData->iServiceClass, // i - service class devData->iMajorClass, // i - major class devData->iMinorClass // i - minor class ); } else { devValues = Py_BuildValue("(s#O(iii))", addrString.Ptr(), addrString.Length(), // s# - address Py_None, devData->iServiceClass, // i - service class devData->iMajorClass, // i - major class devData->iMinorClass // i - minor class ); } // add tuple to list PyList_Append(addrList, devValues); } devDataList.ResetAndDestroy(); return addrList; } // from PDIS miso library // Gets the local device address. // Arguments: // - aAddress - object to hold the retrieved address. // // Returns an error code. static TInt GetLocalAddress(TBTDevAddr& aAddress) { TInt err = KErrNone; #if defined(__SYMBIAN_9__) TPtr8 ptr(aAddress.Des()); err = RProperty::Get(KPropertyUidBluetoothCategory, KPropertyKeyBluetoothGetLocalDeviceAddress, ptr); #elif defined(__SYMBIAN_8__) TPtr8 ptr(aAddress.Des()); err = RProperty::Get(KPropertyUidBluetoothCategory, KPropertyKeyBluetoothLocalDeviceAddress, ptr); #else RSocketServ socketServ; err = socketServ.Connect(); if (err) return err; // this solution comes from the "bthci" Series 60 example; // does not work on Symbian 8-up RSocket socket; err = socket.Open(socketServ, KBTAddrFamily, KSockSeqPacket, KL2CAP); if (!err) { TPckgBuf btDevAddrPckg; TRequestStatus status; socket.Ioctl(KHCILocalAddressIoctl, status, &btDevAddrPckg, KSolBtHCI); User::WaitForRequest(status); err = status.Int(); if (!err) { TPtrC8 src(btDevAddrPckg().Des()); TPtr8 dest(aAddress.Des()); dest.Copy(src); } socket.Close(); } socketServ.Close(); #endif return err; } // Wraps GetLocalAddress() to provide a Python method interface. // Takes no arguments. // // Returns local device address as Python string. static PyObject* LightBlue_GetLocalAddress(PyObject* self, PyObject* args) { TBTDevAddr addr; TBuf8<6*2+5> addrString; if (!PyArg_ParseTuple(args, "")) return NULL; TInt err = GetLocalAddress(addr); if (err) return SPyErr_SetFromSymbianOSErr(err); DevAddressToString(addr, addrString); return Py_BuildValue("s#", addrString.Ptr(), addrString.Length()); } // from PDIS miso library // Gets the local device name. // Arguments: // - aName - object to hold the retrieved name. // // Returns an error code. static TInt GetLocalName(TDes& aName) { TInt err = KErrNone; RSocketServ socketServ; err = socketServ.Connect(); if (!err) { TProtocolName protocolName; // address and name queries are apparently supplied // by the BT stack's link manager _LIT(KBtLinkManager, "BTLinkManager"); protocolName.Copy(KBtLinkManager); TProtocolDesc protocolDesc; err = socketServ.FindProtocol(protocolName, protocolDesc); if (!err) { RHostResolver hostResolver; err = hostResolver.Open(socketServ, protocolDesc.iAddrFamily, protocolDesc.iProtocol); if (!err) { err = hostResolver.GetHostName(aName); hostResolver.Close(); } } socketServ.Close(); } return err; } // Wraps GetLocalName() to provide a Python method interface. // Takes no arguments. // // Returns the local device name as a unicode python string. static PyObject* LightBlue_GetLocalName(PyObject* self, PyObject* args) { TBTDeviceName deviceName; if (!PyArg_ParseTuple(args, "")) return NULL; TInt err = GetLocalName(deviceName); if (err) return SPyErr_SetFromSymbianOSErr(err); return Py_BuildValue("u#", deviceName.Ptr(), deviceName.Length()); } // Gets the local device class. // Arguments: // - aDeviceData - object to hold the retrieved class data. // // Returns an error code. //static TInt GetLocalDeviceClass(TDeviceData& aDeviceData) static TInt GetLocalDeviceClass(TBTDeviceClass& aDeviceClass) { TInt err = KErrNone; #if defined(__SYMBIAN_9__) TInt cod; err = RProperty::Get(KPropertyUidBluetoothCategory, KPropertyKeyBluetoothGetDeviceClass, cod); if (err == KErrNone) { aDeviceClass = TBTDeviceClass(cod); } #elif defined(__SYMBIAN_8__) TInt cod; err = RProperty::Get(KPropertyUidBluetoothCategory, KPropertyKeyBluetoothDeviceClass, cod); if (err == KErrNone) { aDeviceClass = TBTDeviceClass(cod); } #else RSocketServ socketServ; RSocket sock; err = socketServ.Connect(); if (!err) { err = sock.Open(socketServ, KBTAddrFamily, KSockSeqPacket, KL2CAP); if (!err) { THCIDeviceClassBuf codBuf; TRequestStatus status; sock.Ioctl(KHCIReadDeviceClassIoctl, status, &codBuf, KSolBtHCI); User::WaitForRequest(status); if (status.Int() == KErrNone) { aDeviceClass = TBTDeviceClass(codBuf().iMajorServiceClass, codBuf().iMajorDeviceClass, codBuf().iMinorDeviceClass); } sock.Close(); } socketServ.Close(); } #endif return err; } // Wraps GetLocalDeviceClass() to provide a Python method interface. // Takes no arguments. // // Returns the local device class (an integer). static PyObject* LightBlue_GetLocalDeviceClass(PyObject* self, PyObject* args) { //TDeviceData aDeviceData; TBTDeviceClass aDeviceClass; if (!PyArg_ParseTuple(args, "")) return NULL; TInt err = GetLocalDeviceClass(aDeviceClass); if (err) return SPyErr_SetFromSymbianOSErr(err); return Py_BuildValue("i", aDeviceClass.DeviceClass()); } // provide access to apn_resolver from PDIS library extern PyObject* apn_resolver_new(PyObject* /*self*/, PyObject* /*args*/); extern TInt apn_resolver_ConstructType(); // define module methods static const PyMethodDef _lightblueutil_methods[] = { {"lookupName", (PyCFunction)LightBlue_LookupName, METH_VARARGS}, {"selectDevice", (PyCFunction)LightBlue_SelectDevice, METH_VARARGS}, {"discoverDevices", (PyCFunction)LightBlue_DiscoverDevices, METH_VARARGS}, {"getLocalAddress", (PyCFunction)LightBlue_GetLocalAddress, METH_VARARGS}, {"getLocalName", (PyCFunction)LightBlue_GetLocalName, METH_VARARGS}, {"getLocalDeviceClass", (PyCFunction)LightBlue_GetLocalDeviceClass, METH_VARARGS}, {"AoResolver", (PyCFunction)apn_resolver_new, METH_NOARGS}, {0, 0} }; /* initialise the module */ DL_EXPORT(void) init_lightblueutil(void) { /* Create the module and add the functions */ Py_InitModule("_lightblueutil", (PyMethodDef*)_lightblueutil_methods); if (apn_resolver_ConstructType() < 0) return; } /* This function is mandatory in Symbian DLL's. */ #ifndef EKA2 GLDEF_C TInt E32Dll(TDllReason) { return KErrNone; } #endif /*EKA2*/ lightblue-0.3.2/src/series60/src/_lightblueutil/apnresolver.cpp0000755000076500007650000002774610533016742024127 0ustar beabea00000000000000// Modified by Bea Lam Jan-06: // - CAoResolver::Discover now accepts a boolean 'getNames' to control // whether the discovery performs name lookup. // - CAoResolver::RunL now adds class-of-device information to the data that // is passed when invoking the python callback method. // // This is a modified version of apnresolver.cpp from the PDIS project source // code. Original license is below. // -*- symbian-c++ -*- // // apnresolver.cpp // // Copyright 2004 Helsinki Institute for Information Technology (HIIT) // and the authors. All rights reserved. // // Authors: Tero Hasu // // Implements non-interactive Bluetooth device discovery, by // utilizing the functionality provided by the native RHostResolver class. // // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation files // (the "Software"), to deal in the Software without restriction, // including without limitation the rights to use, copy, modify, merge, // publish, distribute, sublicense, and/or sell copies of the Software, // and to permit persons to whom the Software is furnished to do so, // subject to the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS // BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. #include #include #include #include "localepocpyutils.h" #include "panic.h" #include "settings.h" // -------------------------------------------------------------------- // CAoResolver... class CAoResolver : public CActive { public: static CAoResolver* NewL(); ~CAoResolver(); //void Discover(PyObject* aCallback, PyObject* aParam); void Discover(PyObject* aCallback, PyObject* aParam, bool getNames); void Next(); private: CAoResolver(); void ConstructL(); private: // CActive void RunL(); void DoCancel(); private: RSocketServ iSocketServ; RHostResolver iHostResolver; TInquirySockAddr iInquirySockAddr; TNameEntry iNameEntry; void Free(); PyObject* iCallback; PyObject* iParam; bool iLookupNames; PyThreadState* iThreadState; CTC_DEF_HANDLE(ctc); }; void CAoResolver::Free() { if (iCallback) { Py_DECREF(iCallback); iCallback = NULL; } if (iParam) { Py_DECREF(iParam); iParam = NULL; } } CAoResolver* CAoResolver::NewL() { CAoResolver* obj = new (ELeave) CAoResolver; CleanupStack::PushL(obj); obj->ConstructL(); CleanupStack::Pop(); return obj; } CAoResolver::CAoResolver() : CActive(EPriorityStandard) { CActiveScheduler::Add(this); CTC_STORE_HANDLE(ctc); } void CAoResolver::ConstructL() { User::LeaveIfError(iSocketServ.Connect()); TProtocolName protocolName; _LIT(KBtLinkManager, "BTLinkManager"); protocolName.Copy(KBtLinkManager); TProtocolDesc protocolDesc; User::LeaveIfError(iSocketServ.FindProtocol(protocolName, protocolDesc)); User::LeaveIfError(iHostResolver.Open(iSocketServ, protocolDesc.iAddrFamily, protocolDesc.iProtocol)); } CAoResolver::~CAoResolver() { CTC_CHECK(ctc); Cancel(); Free(); if (iHostResolver.SubSessionHandle() != 0) { iHostResolver.Close(); } if (iSocketServ.Handle() != 0) { iSocketServ.Close(); } } //void CAoResolver::Discover(PyObject* aCallback, PyObject* aParam) void CAoResolver::Discover(PyObject* aCallback, PyObject* aParam, bool lookupNames) { if (IsActive()) { AoSocketPanic(EPanicRequestAlreadyPending); } Free(); AssertNonNull(aCallback); AssertNonNull(aParam); iCallback = aCallback; Py_INCREF(aCallback); iParam = aParam; Py_INCREF(aParam); iThreadState = PyThreadState_Get(); iInquirySockAddr.SetIAC(KGIAC); //iInquirySockAddr.SetAction(KHostResInquiry|KHostResName); // MODIFIED: use lookupNames parameter iLookupNames = lookupNames; if (iLookupNames) iInquirySockAddr.SetAction(KHostResInquiry|KHostResName|KHostResIgnoreCache); else iInquirySockAddr.SetAction(KHostResInquiry|KHostResIgnoreCache); iHostResolver.GetByAddress(iInquirySockAddr, iNameEntry, iStatus); SetActive(); } void CAoResolver::Next() { if (IsActive()) { AoSocketPanic(EPanicRequestAlreadyPending); } if (!iCallback) { AoSocketPanic(EPanicNextBeforeFirst); } iHostResolver.Next(iNameEntry, iStatus); SetActive(); } void CAoResolver::RunL() { TInt error = iStatus.Int(); AssertNonNull(iCallback); AssertNonNull(iParam); PyEval_RestoreThread(iThreadState); PyObject* arg; if (error == KErrNone) { TSockAddr& sockAddr = iNameEntry().iAddr; TBTDevAddr btDevAddr = static_cast(sockAddr).BTAddr(); TBuf<32> addrBuf; btDevAddr.GetReadable(addrBuf); THostName& hostName = iNameEntry().iName; //arg = Py_BuildValue("(iu#u#O)", error, // addrBuf.Ptr(), // addrBuf.Length(), // hostName.Ptr(), // hostName.Length(), // iParam); // MODIFIED - add a 3-item tuple containing device class info into 'arg' TInquirySockAddr &isa = TInquirySockAddr::Cast(iNameEntry().iAddr); TInt16 serviceClass = (TInt16)isa.MajorServiceClass(); TInt8 majorClass = (TInt8)isa.MajorClassOfDevice(); TInt8 minorClass = (TInt8)isa.MinorClassOfDevice(); if (iLookupNames) { arg = Py_BuildValue("(iu#u#(iii)O)", error, addrBuf.Ptr(), addrBuf.Length(), hostName.Ptr(), hostName.Length(), serviceClass, majorClass, minorClass, iParam); } else { // pass None instead of the hostName arg = Py_BuildValue("(iu#O(iii)O)", error, addrBuf.Ptr(), addrBuf.Length(), Py_None, serviceClass, majorClass, minorClass, iParam); } } else { //arg = Py_BuildValue("(iOOO)", error, Py_None, Py_None, iParam); // MODIFIED - pass an extra Py_None, since there's now the extra // tuple containing device class info arg = Py_BuildValue("(iOOOO)", error, Py_None, Py_None, Py_None, iParam); } if (arg) { PyObject* result = PyObject_CallObject(iCallback, arg); Py_DECREF(arg); Py_XDECREF(result); if (!result) { // Callbacks are not supposed to throw exceptions. // Make sure that the error gets noticed. PyErr_Clear(); AoSocketPanic(EPanicExceptionInCallback); } } else { // It is misleading for an exception stack trace // to pop up later in some other context. // Perhaps we shall simply accept that an out // of memory condition will cause all sorts of // weird problems that we cannot properly act on. // We will just put a stop to things right here. PyErr_Clear(); AoSocketPanic(EPanicOutOfMemory); } PyEval_SaveThread(); // the callback may have done anything, including // deleting the object whose method we are in, // so do not attempt to access any property anymore } void CAoResolver::DoCancel() { // iHostResolver will have been initialized if we get this // far, so calling Cancel() on it is fine iHostResolver.Cancel(); } // -------------------------------------------------------------------- // object structure... // we store the state we require in a Python object typedef struct { PyObject_VAR_HEAD; CAoResolver* iResolver; } apn_resolver_object; // -------------------------------------------------------------------- // instance methods... static PyObject* apn_resolver_discover(apn_resolver_object* self, PyObject* args) { PyObject* cb; PyObject* param; char getNames; //if (!PyArg_ParseTuple(args, "OO", &cb, ¶m)) // MODIFIED: add a boolean argument if (!PyArg_ParseTuple(args, "OOb", &cb, ¶m, &getNames)) { return NULL; } if (!PyCallable_Check(cb)) { PyErr_SetString(PyExc_TypeError, "parameter must be callable"); return NULL; } if (!self->iResolver) { AoSocketPanic(EPanicUseBeforeInit); } //self->iResolver->Discover(cb, param); self->iResolver->Discover(cb, param, getNames); RETURN_NO_VALUE; } static PyObject* apn_resolver_next(apn_resolver_object* self, PyObject* /*args*/) { if (!self->iResolver) { AoSocketPanic(EPanicUseBeforeInit); } self->iResolver->Next(); RETURN_NO_VALUE; } static PyObject* apn_resolver_cancel(apn_resolver_object* self, PyObject* /*args*/) { if (self->iResolver) { self->iResolver->Cancel(); } RETURN_NO_VALUE; } /** Creates the Symbian object (the Python object has already been created). This must be done in the thread that will be using the object, as we want to register with the active scheduler of that thread. */ static PyObject* apn_resolver_open(apn_resolver_object* self, PyObject* /*args*/) { AssertNull(self->iResolver); TRAPD(error, self->iResolver = CAoResolver::NewL()); if (error) { return SPyErr_SetFromSymbianOSErr(error); } RETURN_NO_VALUE; } /** Destroys the Symbian object, but not the Python object. This must be done in the thread that used the object, as we must deregister with the correct active scheduler. */ static PyObject* apn_resolver_close(apn_resolver_object* self, PyObject* /*args*/) { delete self->iResolver; self->iResolver = NULL; RETURN_NO_VALUE; } const static PyMethodDef apn_resolver_methods[] = { {"open", (PyCFunction)apn_resolver_open, METH_NOARGS}, {"discover", (PyCFunction)apn_resolver_discover, METH_VARARGS}, {"next", (PyCFunction)apn_resolver_next, METH_NOARGS}, {"cancel", (PyCFunction)apn_resolver_cancel, METH_NOARGS}, {"close", (PyCFunction)apn_resolver_close, METH_NOARGS}, {NULL, NULL} // sentinel }; static void apn_dealloc_resolver(apn_resolver_object *self) { delete self->iResolver; self->iResolver = NULL; PyObject_Del(self); } static PyObject *apn_resolver_getattr(apn_resolver_object *self, char *name) { return Py_FindMethod((PyMethodDef*)apn_resolver_methods, (PyObject*)self, name); } // -------------------------------------------------------------------- // type... const PyTypeObject apn_resolver_typetmpl = { PyObject_HEAD_INIT(NULL) 0, /*ob_size*/ "aosocketnativenew.AoResolver", /*tp_name*/ sizeof(apn_resolver_object), /*tp_basicsize*/ 0, /*tp_itemsize*/ /* methods */ (destructor)apn_dealloc_resolver, /*tp_dealloc*/ 0, /*tp_print*/ (getattrfunc)apn_resolver_getattr, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ 0, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0 /*tp_hash*/ }; TInt apn_resolver_ConstructType() { return ConstructType(&apn_resolver_typetmpl, "AoResolver"); } // -------------------------------------------------------------------- // module methods... #define AoResolverType ((PyTypeObject*)SPyGetGlobalString("AoResolver")) // Returns NULL if cannot allocate. // The reference count of any returned object will be 1. // The created socket object will be initialized, // but the socket will not be open. static apn_resolver_object* NewResolverObject() { apn_resolver_object* newResolver = // sets refcount to 1 if successful, // so decrefing should delete PyObject_New(apn_resolver_object, AoResolverType); if (newResolver == NULL) { // raise an exception with the reason set by PyObject_New return NULL; } newResolver->iResolver = NULL; return newResolver; } // allocates a new AoResolver object, or raises and exception PyObject* apn_resolver_new(PyObject* /*self*/, PyObject* /*args*/) { return reinterpret_cast(NewResolverObject()); } lightblue-0.3.2/src/series60/src/_lightblueutil/localepocpyutils.cpp0000755000076500007650000000404610533016742025146 0ustar beabea00000000000000// -*- symbian-c++ -*- // // localepocpyutils.cpp // // Copyright 2004 Helsinki Institute for Information Technology (HIIT) // and the authors. All rights reserved. // // Authors: Tero Hasu // // Utilities to assist in the creation of native Python extensions. // // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation files // (the "Software"), to deal in the Software without restriction, // including without limitation the rights to use, copy, modify, merge, // publish, distribute, sublicense, and/or sell copies of the Software, // and to permit persons to whom the Software is furnished to do so, // subject to the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS // BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. #include "localepocpyutils.h" TInt ConstructType(const PyTypeObject* aTypeTemplate, char* aClassName) { //// construct the type; //// sets object refcount to 1 PyTypeObject* typeObj = PyObject_New(PyTypeObject, &PyType_Type); *typeObj = *aTypeTemplate; // fill in from a template typeObj->ob_type = &PyType_Type; // fill in the missing bit //// store a reference to the type, for our own use TInt error = SPyAddGlobalString(aClassName, (PyObject*)typeObj); if (error < 0) { // the error codes for the above are not documented, // but have seen the code, and believe return -1 on failure return error; } //// a "global" hash now has a reference, too Py_INCREF(typeObj); return KErrNone; } lightblue-0.3.2/src/series60/src/_lightblueutil/localepocpyutils.h0000755000076500007650000000372410533016742024615 0ustar beabea00000000000000// -*- symbian-c++ -*- // // localepocpyutils.h // // Copyright 2004 Helsinki Institute for Information Technology (HIIT) // and the authors. All rights reserved. // // Authors: Tero Hasu // // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation files // (the "Software"), to deal in the Software without restriction, // including without limitation the rights to use, copy, modify, merge, // publish, distribute, sublicense, and/or sell copies of the Software, // and to permit persons to whom the Software is furnished to do so, // subject to the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS // BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. #ifndef __LOCALEPOCPYUTILS_H__ #define __LOCALEPOCPYUTILS_H__ // note that we get warnings, or even errors with some compilers // (e.g. .NET 7.1) unless this is before the Python headers #include #include #include #define RETURN_NO_VALUE \ Py_INCREF(Py_None); \ return Py_None; #define RETURN_TRUE \ Py_INCREF(Py_True); \ return Py_True; #define RETURN_FALSE \ Py_INCREF(Py_False); \ return Py_False; #define WAIT_STAT(stat) \ Py_BEGIN_ALLOW_THREADS;\ User::WaitForRequest(stat);\ Py_END_ALLOW_THREADS /** Returns an error code. */ TInt ConstructType(const PyTypeObject* aTypeTemplate, char* aClassName); #endif // __LOCALEPOCPYUTILS_H__ lightblue-0.3.2/src/series60/src/_lightblueutil/panic.cpp0000755000076500007650000000321610533016742022643 0ustar beabea00000000000000// -*- symbian-c++ -*- // // panic.cpp // // Copyright 2004 Helsinki Institute for Information Technology (HIIT) // and the authors. All rights reserved. // // Authors: Tero Hasu // // Utilities related to panicking. // // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation files // (the "Software"), to deal in the Software without restriction, // including without limitation the rights to use, copy, modify, merge, // publish, distribute, sublicense, and/or sell copies of the Software, // and to permit persons to whom the Software is furnished to do so, // subject to the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS // BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. #include #include "panic.h" void AoSocketPanic(TInt aReason) { _LIT(KPanicCat, "aosocketnative"); User::Panic(KPanicCat, aReason); } void AssertFail() { AoSocketPanic(EPanicAssertionFailed); } void AssertNonNull(void* aPtr) { if (!aPtr) { AssertFail(); } } void AssertNull(void* aPtr) { if (aPtr) { AssertFail(); } } lightblue-0.3.2/src/series60/src/_lightblueutil/panic.h0000755000076500007650000000356710533016742022321 0ustar beabea00000000000000// -*- symbian-c++ -*- // // panic.h // // Copyright 2004 Helsinki Institute for Information Technology (HIIT) // and the authors. All rights reserved. // // Authors: Tero Hasu // // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation files // (the "Software"), to deal in the Software without restriction, // including without limitation the rights to use, copy, modify, merge, // publish, distribute, sublicense, and/or sell copies of the Software, // and to permit persons to whom the Software is furnished to do so, // subject to the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS // BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. #ifndef __PANIC_H__ #define __PANIC_H__ enum TAoSocketPanicCodes { EPanicAssertionFailed = 0, EPanicSocketAlreadyOpen, EPanicSocketNotOpen, EPanicRequestAlreadyPending, EPanicExceptionInCallback, EPanicOutOfMemory, EPanicSessionDoesNotExist, EPanicSessionAlreadyExists, EPanicAccessWithWrongThread, EPanicConfigBeforeListen, EPanicAcceptBeforeListen, EPanicUseBeforeInit, EPanicNextBeforeFirst, EPanicWrongTransportMode, EPanicSocketServNotSet, EPanicArgumentError }; void AoSocketPanic(TInt aReason); void AssertFail(); void AssertNonNull(void* aPtr); void AssertNull(void* aPtr); #endif // __PANIC_H__ lightblue-0.3.2/src/series60/src/_lightblueutil/settings.h0000755000076500007650000000540010533016742023053 0ustar beabea00000000000000// -*- symbian-c++ -*- // // settings.h // // Copyright 2004 Helsinki Institute for Information Technology (HIIT) // and the authors. All rights reserved. // // Authors: Tero Hasu // // Some compile-time settings for this library. // // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation files // (the "Software"), to deal in the Software without restriction, // including without limitation the rights to use, copy, modify, merge, // publish, distribute, sublicense, and/or sell copies of the Software, // and to permit persons to whom the Software is furnished to do so, // subject to the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS // BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. #ifndef __SETTINGS_H__ #define __SETTINGS_H__ #ifdef __WINS__ #define ON_WINS 1 #else #define ON_WINS 0 #endif #define SUPPORT_PEROON 0 // ON_WINS #define NO_SESSION_HANDLE_ACCESS SUPPORT_PEROON /* Handle() and SubSessionHandle() always appear to return 0 with Peroon sockets. Do not know if it is safe to call Close() on a closed Peroon socket; it is not okay with Symbian sockets. But we shall keep track of what is open an what is not, using these macros. */ #if NO_SESSION_HANDLE_ACCESS #define IS_SESSION_OPEN(x) (x##IsOpen) #define IS_SUBSESSION_OPEN(x) (x##IsOpen) #define DEF_SESSION_OPEN(x) TBool x##IsOpen #define SET_SESSION_OPEN(x) x##IsOpen = ETrue #define SET_SESSION_CLOSED(x) x##IsOpen = EFalse #else #define IS_SESSION_OPEN(x) ((x).Handle() != 0) #define IS_SUBSESSION_OPEN(x) ((x).SubSessionHandle() != 0) #define DEF_SESSION_OPEN(x) #define SET_SESSION_OPEN(x) #define SET_SESSION_CLOSED(x) Mem::FillZ(&x, sizeof(x)) #endif #define CHECK_THREAD_CORRECT 1 #if CHECK_THREAD_CORRECT #define CTC_DEF_HANDLE(x) TThreadId x##ThreadId #define CTC_STORE_HANDLE(x) x##ThreadId = RThread().Id() #define CTC_IS_SAME_HANDLE(x) (x##ThreadId == RThread().Id()) #define CTC_PANIC(x) AoSocketPanic(EPanicAccessWithWrongThread) #define CTC_CHECK(x) if (!CTC_IS_SAME_HANDLE(x)) CTC_PANIC(x) #else #define CTC_DEF_HANDLE(x) #define CTC_STORE_HANDLE(x) #define CTC_IS_SAME_HANDLE(x) ETrue #define CTC_PANIC(x) #define CTC_CHECK(x) #endif #endif // __SETTINGS_H__ lightblue-0.3.2/src/series60/src/.__obex.py0000755000076500000000000000012210747242106020242 0ustar beawheel00000000000000Mac OS X  2 RTEXTlightblue-0.3.2/src/series60/src/_obex.py0000755000076500007650000001157110747242106017504 0ustar beabea00000000000000# Copyright (c) 2006 Bea Lam. All rights reserved. # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files # (the "Software"), to deal in the Software without restriction, # including without limitation the rights to use, copy, modify, merge, # publish, distribute, sublicense, and/or sell copies of the Software, # and to permit persons to whom the Software is furnished to do so, # subject to the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. import socket as _socket import os import types import _lightbluecommon from _obexcommon import OBEXError # public attributes __all__ = ("sendfile", "recvfile") def sendfile(address, channel, source): if not isinstance(source, (types.StringTypes, types.FileType)): raise TypeError("source must be string or built-in file object") if isinstance(source, types.StringTypes): try: _socket.bt_obex_send_file(address, channel, unicode(source)) except Exception, e: raise OBEXError(str(e)) else: # given file object if hasattr(source, "name"): localpath = _tempfilename(source.name) else: localpath = _tempfilename() try: # write the source file object's data into a file, then send it f = file(localpath, "wb") f.write(source.read()) f.close() try: _socket.bt_obex_send_file(address, channel, unicode(localpath)) except Exception, e: raise OBEXError(str(e)) finally: # remove temporary file if os.path.isfile(localpath): try: os.remove(localpath) except Exception, e: print "[lightblue.obex] unable to remove temporary file %s: %s" %\ (localpath, str(e)) def recvfile(sock, dest): if not isinstance(dest, (types.StringTypes, types.FileType)): raise TypeError("dest must be string or built-in file object") if isinstance(dest, types.StringTypes): _recvfile(sock, dest) else: # given file object localpath = _tempfilename() try: # receive a file and then read it into the file object _recvfile(sock, localpath) recvdfile = file(localpath, "rb") dest.write(recvdfile.read()) recvdfile.close() finally: # remove temporary file if os.path.isfile(localpath): try: os.remove(localpath) except Exception, e: print "[lightblue.obex] unable to remove temporary file %s: %s" %\ (localpath, str(e)) # receives file and saves to local path def _recvfile(sock, localpath): # PyS60's bt_obex_receive() won't receive the file if given a file path # that already exists (it tells the client there's a conflict error). So # we need to handle this somehow, and preferably backup the original file # so that we can put it back if the recv fails. if os.path.isfile(localpath): # if given an existing path, rename existing file temppath = _tempfilename(localpath) os.rename(localpath, temppath) else: temppath = None try: # receive a file (get internal _sock cos sock is our own SocketWrapper # object) _socket.bt_obex_receive(sock._sock, unicode(localpath)) except _socket.error, e: try: if temppath is not None: # recv failed, put original file back os.rename(temppath, localpath) finally: # if the renaming of the original file fails, this will still # get raised raise OBEXError(str(e)) else: # recv successful, remove the original file if temppath is not None: os.remove(temppath) # Must point to C:\ because can't write in start-up dir (on Z:?) def _tempfilename(basename="C:\\lightblue_obex_received_file"): version = 1 while os.path.isfile(basename): version += 1 basename = basename[:-1] + str(version) return basenamelightblue-0.3.2/src/series60/src/.__obexcommon.py0000755000076500000000000000012210750556342021457 0ustar beawheel00000000000000Mac OS X  2 RTEXTlightblue-0.3.2/src/series60/src/_obexcommon.py0000755000076500007650000004434310750556342020724 0ustar beabea00000000000000# Copyright (c) 2006 Bea Lam. All rights reserved. # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files # (the "Software"), to deal in the Software without restriction, # including without limitation the rights to use, copy, modify, merge, # publish, distribute, sublicense, and/or sell copies of the Software, # and to permit persons to whom the Software is furnished to do so, # subject to the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. import _lightbluecommon __all__ = ('OBEXResponse', 'OBEXError', 'CONTINUE', 'OK', 'CREATED', 'ACCEPTED', 'NON_AUTHORITATIVE_INFORMATION', 'NO_CONTENT', 'RESET_CONTENT', 'PARTIAL_CONTENT', 'MULTIPLE_CHOICES', 'MOVED_PERMANENTLY', 'MOVED_TEMPORARILY', 'SEE_OTHER', 'NOT_MODIFIED', 'USE_PROXY', 'BAD_REQUEST', 'UNAUTHORIZED', 'PAYMENT_REQUIRED', 'FORBIDDEN', 'NOT_FOUND', 'METHOD_NOT_ALLOWED', 'NOT_ACCEPTABLE', 'PROXY_AUTHENTICATION_REQUIRED', 'REQUEST_TIME_OUT', 'CONFLICT', 'GONE', 'LENGTH_REQUIRED', 'PRECONDITION_FAILED', 'REQUESTED_ENTITY_TOO_LARGE', 'REQUEST_URL_TOO_LARGE', 'UNSUPPORTED_MEDIA_TYPE', 'INTERNAL_SERVER_ERROR', 'NOT_IMPLEMENTED', 'BAD_GATEWAY', 'SERVICE_UNAVAILABLE', 'GATEWAY_TIMEOUT', 'HTTP_VERSION_NOT_SUPPORTED', 'DATABASE_FULL', 'DATABASE_LOCKED') class OBEXError(_lightbluecommon.BluetoothError): """ Generic exception raised for OBEX-related errors. """ pass class OBEXResponse: """ Contains the OBEX response received from an OBEX server. When an OBEX client sends a request, the OBEX server sends back a response code (to indicate whether the request was successful) and a set of response headers (to provide other useful information). For example, if a client sends a 'Get' request to retrieve a file, the client might get a response like this: >>> import lightblue >>> client = lightblue.obex.OBEXClient("aa:bb:cc:dd:ee:ff", 10) >>> response = client.get({"name": "file.txt"}, file("file.txt", "w")) >>> print response You can get the response code and response headers in different formats: >>> print response.reason 'OK' # a string description of the response code >>> print response.code 32 # the response code (e.g. this is 0x20) >>> print response.headers {'length': 35288} # the headers, with string keys >>> print response.rawheaders {195: 35288} # the headers, with raw header ID keys >>> Note how the 'code' attribute does not have the final bit set - e.g. for OK/Success, the response code is 0x20, not 0xA0. The lightblue.obex module defines constants for response code values (e.g. lightblue.obex.OK, lightblue.obex.FORBIDDEN, etc.). """ def __init__(self, code, rawheaders): self.__code = code self.__reason = _OBEX_RESPONSES.get(code, "Unknown response code") self.__rawheaders = rawheaders self.__headers = None code = property(lambda self: self.__code, doc='The response code, without the final bit set.') reason = property(lambda self: self.__reason, doc='A string description of the response code.') rawheaders = property(lambda self: self.__rawheaders, doc='The response headers, as a dictionary with header ID (unsigned byte) keys.') def getheader(self, header, default=None): ''' Returns the response header value for the given header, which may either be a string (not case-sensitive) or the raw byte value of the header ID. Returns the specified default value if the header is not present. ''' if isinstance(header, types.StringTypes): return self.headers.get(header.lower(), default) return self.__rawheaders.get(header, default) def __getheaders(self): if self.__headers is None: self.__headers = {} for headerid, value in self.__rawheaders.items(): if headerid in _HEADER_IDS_TO_STRINGS: self.__headers[_HEADER_IDS_TO_STRINGS[headerid]] = value else: self.__headers["0x%02x" % headerid] = value return self.__headers headers = property(__getheaders, doc='The response headers, as a dictionary with string keys.') def __repr__(self): return "" % \ (self.__reason, self.__code, (self.__code | 0x80), str(self.headers)) try: import datetime # as from python docs example class UTC(datetime.tzinfo): """UTC""" def utcoffset(self, dt): return datetime.timedelta(0) def tzname(self, dt): return "UTC" def dst(self, dt): return datetime.timedelta(0) except: pass # no datetime on pys60 _LOCAL_TIME_FORMAT = "%Y%m%dT%H%M%S" _UTC_TIME_FORMAT = _LOCAL_TIME_FORMAT + "Z" def _datetimefromstring(s): import time if s[-1:] == "Z": # add UTC() instance as tzinfo args = (time.strptime(s, _UTC_TIME_FORMAT)[0:6]) + (0, UTC()) return datetime.datetime(*args) else: return datetime.datetime(*(time.strptime(s, _LOCAL_TIME_FORMAT)[0:6])) _HEADER_STRINGS_TO_IDS = { "count": 0xc0, "name": 0x01, "type": 0x42, "length": 0xc3, "time": 0x44, "description": 0x05, "target": 0x46, "http": 0x47, "who": 0x4a, "connection-id": 0xcb, "application-parameters": 0x4c, "authentication-challenge": 0x4d, "authentication-response": 0x4e, "creator-id": 0xcf, "wan-uuid": 0x50, "object-class": 0x51, "session-parameters": 0x52, "session-sequence-number": 0x93 } _HEADER_IDS_TO_STRINGS = {} for key, value in _HEADER_STRINGS_TO_IDS.items(): _HEADER_IDS_TO_STRINGS[value] = key assert len(_HEADER_IDS_TO_STRINGS) == len(_HEADER_STRINGS_TO_IDS) # These match the associated strings in httplib.responses, since OBEX response # codes are matched to HTTP status codes (except for 0x60 and 0x61). # Note these are the responses *without* the final bit set. _OBEX_RESPONSES = { 0x10: "Continue", 0x20: "OK", 0x21: "Created", 0x22: "Accepted", 0x23: "Non-Authoritative Information", 0x24: "No Content", 0x25: "Reset Content", 0x26: "Partial Content", 0x30: "Multiple Choices", 0x31: "Moved Permanently", 0x32: "Moved Temporarily", # but is 'Found' (302) in httplib.response??? 0x33: "See Other", 0x34: "Not Modified", 0x35: "Use Proxy", 0x40: "Bad Request", 0x41: "Unauthorized", 0x42: "Payment Required", 0x43: "Forbidden", 0x44: "Not Found", 0x45: "Method Not Allowed", 0x46: "Not Acceptable", 0x47: "Proxy Authentication Required", 0x48: "Request Timeout", 0x49: "Conflict", 0x4A: "Gone", 0x48: "Length Required", 0x4C: "Precondition Failed", 0x4D: "Request Entity Too Large", 0x4E: "Request-URI Too Long", 0x4F: "Unsupported Media Type", 0x50: "Internal Server Error", 0x51: "Not Implemented", 0x52: "Bad Gateway", 0x53: "Service Unavailable", 0x54: "Gateway Timeout", 0x55: "HTTP Version Not Supported", 0x60: "Database Full", 0x61: "Database Locked" } _obexclientclassdoc = \ """ An OBEX client class. (Note this is not available on Python for Series 60.) For example, to connect to an OBEX server and send a file: >>> import lightblue >>> client = lightblue.obex.OBEXClient("aa:bb:cc:dd:ee:ff", 10) >>> client.connect() >>> client.put({"name": "photo.jpg"}, file("photo.jpg", "rb")) >>> client.disconnect() >>> A client must call connect() to establish a connection before it can send any other requests. The connect(), disconnect(), put(), delete(), get() and setpath() methods all accept the request headers as a dictionary of header-value mappings. The request headers are used to provide the server with additional information for the request. For example, this sends a Put request that includes Name, Type and Length headers in the request headers, to provide details about the transferred file: >>> f = file("file.txt") >>> client.put({"name": "file.txt", "type": "text/plain", ... "length": 5192}, f) >>> Here is a list of all the different string header keys that you can use in the request headers, and the expected type of the value for each header: - "name" -> a string - "type" -> a string - "length" -> an int - "time" -> a datetime object from the datetime module - "description" -> a string - "target" -> a string or buffer - "http" -> a string or buffer - "who" -> a string or buffer - "connection-id" -> an int - "application-parameters" -> a string or buffer - "authentication-challenge" -> a string or buffer - "authentication-response" -> a string or buffer - "creator-id" -> an int - "wan-uuid" -> a string or buffer - "object-class" -> a string or buffer - "session-parameters" -> a string or buffer - "session-sequence-number" -> an int less than 256 (The string header keys are not case-sensitive.) Alternatively, you can use raw header ID values instead of the above convenience strings. So, the previous example can be rewritten as: >>> client.put({0x01: "file.txt", 0x42: "text/plain", 0xC3: 5192}, ... fileobject) >>> This is also useful for inserting custom headers. For example, a PutImage request for a Basic Imaging client requires the Img-Descriptor (0x71) header: >>> client.put({"type": "x-bt/img-img", ... "name": "photo.jpg", ... 0x71: ''}, ... file('photo.jpg', 'rb')) >>> Notice that the connection-id header is not sent, because this is automatically included by OBEXClient in the request headers if a connection-id was received in a previous Connect response. See the included src/examples/obex_ftp_client.py for an example of using OBEXClient to implement a File Transfer client for browsing the files on a remote device. """ _obexclientdocs = { "__init__": """ Creates an OBEX client. Arguments: - address: the address of the remote device - channel: the RFCOMM channel of the remote OBEX service """, "connect": """ Establishes the Bluetooth connection to the remote OBEX server and sends a Connect request to open the OBEX session. Returns an OBEXResponse instance containing the server response. Raises lightblue.obex.OBEXError if the session is already connected, or if an error occurs during the request. If the server refuses the Connect request (i.e. if it sends a response code other than OK/Success), the Bluetooth connection will be closed. Arguments: - headers={}: the headers to send for the Connect request """, "disconnect": """ Sends a Disconnect request to end the OBEX session and closes the Bluetooth connection to the remote OBEX server. Returns an OBEXResponse instance containing the server response. Raises lightblue.obex.OBEXError if connect() has not been called, or if an error occurs during the request. Note that you don't need to send any connection-id headers - this is automatically included if the client received one in a Connect response. Arguments: - headers={}: the headers to send for the request """, "put": """ Sends a Put request. Returns an OBEXResponse instance containing the server response. Raises lightblue.obex.OBEXError if connect() has not been called, or if an error occurs during the request. Note that you don't need to send any connection-id headers - this is automatically included if the client received one in a Connect response. Arguments: - headers: the headers to send for the request - fileobj: a file-like object containing the file data to be sent for the request For example, to send a file named 'photo.jpg', using the request headers to notify the server of the file's name, MIME type and length: >>> client = lightblue.obex.OBEXClient("aa:bb:cc:dd:ee:ff", 10) >>> client.connect() >>> client.put({"name": "photo.jpg", "type": "image/jpeg", "length": 28566}, file("photo.jpg", "rb")) >>> """, "delete": """ Sends a Put-Delete request in order to delete a file or folder on the remote server. Returns an OBEXResponse instance containing the server response. Raises lightblue.obex.OBEXError if connect() has not been called, or if an error occurs during the request. Note that you don't need to send any connection-id headers - this is automatically included if the client received one in a Connect response. Arguments: - headers: the headers to send for the request - you should use the 'name' header to specify the file you want to delete If the file on the server can't be deleted because it's a read-only file, you might get an 'Unauthorized' response, like this: >>> client = lightblue.obex.OBEXClient("aa:bb:cc:dd:ee:ff", 10) >>> client.connect() >>> client.delete({"name": "random_file.txt"}) >>> """, "get": """ Sends a Get request. Returns an OBEXResponse instance containing the server response. Raises lightblue.obex.OBEXError if connect() has not been called, or if an error occurs during the request. Note that you don't need to send any connection-id headers - this is automatically included if the client received one in a Connect response. Arguments: - headers: the headers to send for the request - you should use these to specify the file you want to retrieve - fileobj: a file-like object, to which the received data will be written An example: >>> client = lightblue.obex.OBEXClient("aa:bb:cc:dd:ee:ff", 10) >>> client.connect() >>> f = file("received_file.txt", "w+") >>> client.get({"name": "testfile.txt"}, f) >>> f.seek(0) >>> f.read() 'test file' >>> """, "setpath": """ Sends a SetPath request in order to set the "current path" on the remote server for file transfers. Returns an OBEXResponse instance containing the server response. Raises lightblue.obex.OBEXError if connect() has not been called, or if an error occurs during the request. Note that you don't need to send any connection-id headers - this is automatically included if the client received one in a Connect response. Arguments: - headers: the headers to send for the request - you should use the 'name' header to specify the directory you want to change to - cdtoparent=False: True if the remote server should move up one directory before applying the specified directory (i.e. 'cd ../dirname') - createdirs=False: True if the specified directory should be created if it doesn't exist (if False, the server will return an error response if the directory doesn't exist) For example: # change to the "images" subdirectory >>> client.setpath({"name": "images"}) >>> # change to the parent directory >>> client.setpath({}, cdtoparent=True) >>> # create a subdirectory "My_Files" >>> client.setpath({"name": "My_Files"}, createdirs=True) >>> # change to the root directory - you can use an empty "name" header # to specify this >>> client.setpath({"name": ""}) >>> """ } # response constants CONTINUE = 0x10 OK = 0x20 CREATED = 0x21 ACCEPTED = 0x22 NON_AUTHORITATIVE_INFORMATION = 0x23 NO_CONTENT = 0x24 RESET_CONTENT = 0x25 PARTIAL_CONTENT = 0x26 MULTIPLE_CHOICES = 0x30 MOVED_PERMANENTLY = 0x31 MOVED_TEMPORARILY = 0x32 SEE_OTHER = 0x33 NOT_MODIFIED = 0x34 USE_PROXY = 0x35 BAD_REQUEST = 0x40 UNAUTHORIZED = 0x41 PAYMENT_REQUIRED = 0x42 FORBIDDEN = 0x43 NOT_FOUND = 0x44 METHOD_NOT_ALLOWED = 0x45 NOT_ACCEPTABLE = 0x46 PROXY_AUTHENTICATION_REQUIRED = 0x47 REQUEST_TIME_OUT = 0x48 CONFLICT = 0x49 GONE = 0x4A LENGTH_REQUIRED = 0x4B PRECONDITION_FAILED = 0x4C REQUESTED_ENTITY_TOO_LARGE = 0x4D REQUEST_URL_TOO_LARGE = 0x4E UNSUPPORTED_MEDIA_TYPE = 0x4F INTERNAL_SERVER_ERROR = 0x50 NOT_IMPLEMENTED = 0x51 BAD_GATEWAY = 0x52 SERVICE_UNAVAILABLE = 0x53 GATEWAY_TIMEOUT = 0x54 HTTP_VERSION_NOT_SUPPORTED = 0x55 DATABASE_FULL = 0x60 DATABASE_LOCKED = 0x61 lightblue-0.3.2/src/series60/src/._obex.py0000755000076500000000000000012210750744236020110 0ustar beawheel00000000000000Mac OS X  2 RTEXTlightblue-0.3.2/src/series60/src/obex.py0000755000076500007650000000735210750744236017354 0ustar beabea00000000000000# Copyright (c) 2006 Bea Lam. All rights reserved. # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files # (the "Software"), to deal in the Software without restriction, # including without limitation the rights to use, copy, modify, merge, # publish, distribute, sublicense, and/or sell copies of the Software, # and to permit persons to whom the Software is furnished to do so, # subject to the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ Provides an OBEX client class and convenience functions for sending and receiving files over OBEX. This module also defines constants for response code values (without the final bit set). For example: >>> import lightblue >>> lightblue.obex.OK 32 # the OK/Success response 0x20 (i.e. 0xA0 without the final bit) >>> lightblue.obex.FORBIDDEN 67 # the Forbidden response 0x43 (i.e. 0xC3 without the final bit) """ # Docstrings for attributes in this module. _docstrings = { "sendfile": """ Sends a file to a remote device. Raises lightblue.obex.OBEXError if an error occurred during the request, or if the request was refused by the remote device. Arguments: - address: the address of the remote device - channel: the RFCOMM channel of the remote OBEX service - source: a filename or file-like object, containing the data to be sent. If a file object is given, it must be opened for reading. Note you can achieve the same thing using OBEXClient with something like this: >>> import lightblue >>> client = lightblue.obex.OBEXClient(address, channel) >>> client.connect() >>> putresponse = client.put({"name": "MyFile.txt"}, file("MyFile.txt", 'rb')) >>> client.disconnect() >>> if putresponse.code != lightblue.obex.OK: ... raise lightblue.obex.OBEXError("server denied the Put request") >>> """, "recvfile": """ Receives a file through an OBEX service. Arguments: - sock: the server socket on which the file is to be received. Note this socket must *not* be listening. Also, an OBEX service should have been advertised on this socket. - dest: a filename or file-like object, to which the received data will be written. If a filename is given, any existing file will be overwritten. If a file object is given, it must be opened for writing. For example, to receive a file and save it as "MyFile.txt": >>> from lightblue import * >>> s = socket() >>> s.bind(("", 0)) >>> advertise("My OBEX Service", s, OBEX) >>> obex.recvfile(s, "MyFile.txt") """ } # import implementation modules from _obex import * from _obexcommon import * import _obex import _obexcommon __all__ = _obex.__all__ + _obexcommon.__all__ # set docstrings localattrs = locals() for attr in _obex.__all__: try: localattrs[attr].__doc__ = _docstrings[attr] except KeyError: pass del attr, localattrs