NAME

Net::OBEX - implementation of OBEX protocol

SYNOPSIS

use strict;
use warnings;

use Net::OBEX;

my $obex = Net::OBEX->new;

$obex->connect(
    address => '00:17:E3:37:76:BB',
    port    => 9,
    target  => 'F9EC7BC4953C11D2984E525400DC9E09', # OBEX FTP UUID
) or die "Failed to connect: " . $obex->error;

$obex->success
    or die "Server no liky :( " . $obex->status;

$obex->set_path
    or die "Error: " . $obex->error;

$obex->success
    die "Server no liky :( " . $obex->status;

# this is an OBEX FTP example, so we'll get the folder listing now
my $response_ref = $obex->get( type => 'x-obex/folder-listing' )
    or die "Error: " . $obex->error;

$obex->success
    or die "Server no liky :( " . $obes->status;

print "This is folder listing XML: \n$response_ref->{body}\n";

# send Disconnect packet with description header and close the socket
$obex->close('No want you no moar');

DESCRIPTION

WARNING!!! This module is still in its early alpha stage, it is recommended that you use it only for testing. A lot of functionality is still not implemented.

The module is a Perl implementation of IrOBEX protocol.

CONSTRUCTOR

new

my $obex = Net::OBEX->new;

Takes no arguments, returns a freshly baked Net::OBEX object ready to use and abuse.

STATUS METHODS

success

$obex->success
    or die 'Error: (code: ' . $obex->code . ') ' . $obex->status;

Must be called after either connect(), set_path(), get() or put() method. Returns either true or false value indicating whether or not the call to last connect(), set_path(), get() or put() method ended with a successful response from the server (code 200). Note: the aforementioned methods returning a non-error (see descriptions below) does NOT imply that success() will return a true value.

code

$obex->success
    or die 'Error: (code: ' . $obex->code . ') ' . $obex->status;

Must be called after either connect(), set_path(), get() or put() method. Returns the status code of the last response from the server.

status

$obex->success
    or die 'Error: (code: ' . $obex->code . ') ' . $obex->status;

Must be called after either connect(), set_path(), get() or put() method. Returns the status code description of the last response from the server (i.e. "Ok, Success" if code() is 200)

METHODS

connect

my $response_ref = $obex->connect(
    address => '00:17:E3:37:76:BB',
    port    => 9,
) or die "Failed to connect: " . $obex->error;

$obex->connect(
    address => '00:17:E3:37:76:BB',
    port    => 9,
    version => "\x10",
    mtu     => 4096,
    domain  => 'bluetooth',
    type    => 'stream',
    proto   => 'rfcomm',
    headers => [ $some, $raw, $headers ],
) or die "Failed to connect: " . $obex->error;

Creates a new socket and connects it. Takes a bunch of arguments, two of which (address and port) are mandatory. Net::OBEX uses Socket::Class as its "horse" but it might be possible to use a different socket if you want to (see sock() method). Returns a hashref which is described below after arguments. Possible arguments are as follows:

address

->connect( address => '00:17:E3:37:76:BB', ...

Mandatory. Specifies the MAC address of the device to connect to.

port

->connect( port => 9, ...

Mandatory. Specifies the port of the device to connect to.

version

->connect( version => "\x10", ...

Optional. Specifies the OBEX protocol version to use, takes a "version" byte to use in the Connect packet encoded with the major number in the high order 4 bits, and the minor version in the low order 4 bits. Generally speaking you won't have to touch this one. Defaults to: 0x10 (version 1.0)

mtu

->connect( mtu     => 4096, ...

Optional. Specifies the MTU of your device, i.e. the maximum length of the packet in bytes it can accept. Defaults to: 4096

domain

->connect( domain  => 'bluetooth', ...

Optional. Specifies the domain argument to pass to Socket::Class constructor. See documentation for Socket::Class for more information. Defaults to: bluetooth

type

->connect( type    => 'stream', ...

Optional. Specifies the type argument to pass to Socket::Class constructor. See documentation for Socket::Class for more information. Defaults to: stream

proto

->connect( proto   => 'rfcomm', ...

Optional. Specifies the proto argument to pass to Socket::Class constructor. See documentation for Socket::Class for more information. Defaults to: rfcomm

headers

->connect( headers => [ $some, $raw, $headers ], ...

Optional. If you want to pass along some additional packet headers to the Connect packet you can use the headers argument which takes an arrayref elements of which are OBEX packet headers. See Net::OBEX::Packet::Headers for information on how to make them. Defaults to: [] (no headers)

target

->connect( target => 'F9EC7BC4953C11D2984E525400DC9E09', ....

Optional. Since it's common that you will need a Target header in the Connect packet you can use the target argument instead of manually creating the header. Note: the module will automatically pack() what you specify in the target argument, so you can just use the UUID (without dashes). By default no target is specified.

connect RETURN VALUE

$VAR1 = {
    'info' => {
        'flags' => '00000000',
        'packet_length' => 31,
        'obex_version' => '00010000',
        'response_code' => 200,
        'headers_length' => 24,
        'response_code_meaning' => 'OK, Success',
        'mtu' => 5126
    },
    'headers' => {
        'connection_id' => '',
        'who' => '��{ĕ<ҘNRTܞ  '
    },
    'raw_packet' => '�J��{ĕ<ҘNRTܞ   �'
};

If an error occurred during the request, connect() will return either undef or an empty list, depending on the context and the reason for the error will be available via error() method. Otherwise it will return a hashref presented above. If the dump above is not self explanatory see Net::OBEX::Response parse_sock() method description for the return value when "is connect packet" option is true.

SPECIAL NOTE ON CONNECTION ID HEADER

If the Connection ID header is present in the Connect response packet the module will save it and automatically include it in any other packet as the first header as per specification. The raw generated Connection ID header which will be included in all other packets is accessible via connection_id() accessor/mutator. If you want to override the automatic inclusion of the header in all packets set connection_id('') after the call to connect() but generally this is a BadIdea(tm) and you probably will get a 403 on all the requests.

disconnect

my $response_ref = $obex->disconnect
    or die "Error: " . $obex->error;

my $response_ref = $obex->disconnect(
    description => 'die in a fire!',
    headers     => [ $some, $other, $raw, $headers ],
) or die "Error: " . $obex->error;

Instructs the object to send a Disconnect packet without closing the socket (whether it will actually stay open is another matter). If you want to close the socket as well, you probably would want to use the close() method instead. Takes two optional arguments:

description

$obex->disconnect( description => 'die in a fire!' );

Optional. Takes a scalar as an argument which will be passed in the Description header in the Disconnect packet. By default no description is supplied.

headers

$obex->disconnect( headers => [ $some, $raw, $headers ] );

Optional. If you want to pass along some additional packet headers to the Disconnect packet you can use the headers argument which takes an arrayref elements of which are OBEX packet headers. See Net::OBEX::Packet::Headers for information on how to make them. Defaults to: [] (no headers)

disconnect RETURN VALUE

$VAR1 = {
    'info' => {
        'packet_length' => 3,
        'response_code' => 200,
        'headers_length' => 0,
        'response_code_meaning' => 'OK, Success'
    },
    'raw_packet' => '�'
};

If an error occurred during the request, disconnect() will return either undef or an empty list, depending on the context and the reason for the error will be available via error() method. Otherwise it will return a hashref presented above. If the dump above is not self explanatory see Net::OBEX::Response parse_sock() method description for the return value when "is connect packet" option is false.

set_path

my $response_ref = $obex->set_path
    or die "Error: " . $obex->error;

my $response_ref = $obex->set_path(
    path    => 'there_somewhere',
    headers => [ $bunch, $of, $raw, $headers ],
) or die "Error: " . $obex->error;

Instructs the object to send a SetPath packet. Takes four optional arguments which are as follows:

path

$obex->set_path( path => 'there_somewhere' );

Optional. Whatever you specify in the path argument will be sent out in the packet's Name header, which is the path to change to. By default no path is set, meaning set path to "root folder".

do_up

$obex->set_path( do_up => 1 );

Optional. Takes either true or false value, indicating whether or not to set the "backup a level before applying name" flag in the SetPath packet. Defaults to: 0

no_create

$obex->set_path( no_create => 0 );

Optional. Takes either true or false value, indicating whether or not to set the "don't create directory if it does not exist, return an error instead." flag in the SetPath packet. Defaults to: 1

headers

$obex->set_path( headers => [ $some, $raw, $headers ] );

Optional. If you want to pass along some additional packet headers to the SetPath packet you can use the headers argument which takes an arrayref elements of which are OBEX packet headers. See Net::OBEX::Packet::Headers for information on how to make them. Defaults to: [] (no headers)

set_path RETURN VALUE

$VAR1 = {
    'info' => {
        'packet_length' => 3,
        'response_code' => 200,
        'headers_length' => 0,
        'response_code_meaning' => 'OK, Success'
    },
    'raw_packet' => '�'
};

If an error occurred during the request, set_path() will return either undef or an empty list, depending on the context and the reason for the error will be available via error() method. Otherwise it will return a hashref presented above. If the dump above is not self explanatory see Net::OBEX::Response parse_sock() method description for the return value when "is connect packet" option is false.

get

$response_ref = $obex->get
    or die "Error: " . $obex->error;

$response_ref = $obex->get(
    is_final    => 1,
    headers     => [ $bunch, $of, $raw, $headers ],
    type        => 'x-obex/folder-listing',
    name        => 'some_file',
    no_continue => 1,
    file        => $fh,
) or die "Error: " . $obex->error;

Instructs the object to send an OBEX Get packet and any number of Get (Continue) packets needed to finish the request (by default). Takes several arguments, all of which are optional. The possible arguments are as follows:

is_final

$obex->get( is_final => 1 );

Optional. When set to a true value will instruct the object to set the high bit of the Get packet on. When set to a false value will set the high bit off. Defaults to: 1

headers

$obex->get( headers => [ $some, $raw, $headers ] );

Optional. If you want to pass along some additional packet headers to the Get packet you can use the headers argument which takes an arrayref elements of which are OBEX packet headers. See Net::OBEX::Packet::Headers for information on how to make them. Defaults to: [] (no headers)

type

$obex->get( type => 'x-obex/folder-listing' );

Optional. Takes a scalar as value, whatever you specify will be packed up into a OBEX Type header and shipped along with your Get packet. By default type is not specified.

name

$obex->get( name => 'some_file' );

Optional. Takes a scalar as value, whatever you specify will be packed up into a OBEX Name header and shipped along with your Get packet. By default name is not specified.

no_continue

$obex->get( no_continue => 1 );

Optional. By default the get() method will automatically send out any Get (Continue) packets to get the entire data. However, if that's not what you want set the no_continue to a true value. When set to a false value will automatically send as many Get (Continue) packets as needed to get the entire thing, when set to a true value will send only one Get packet leaving the rest up to you. Defaults to: 0

file

$obex->get( file => $file_handle );

Optional. If you are retrieving large quantities of data it is probably not a good idea to stuff all of it into a hashref. The file argument takes an open file handle, and when specified will write the data into that file instead of storing it in the return hashref. By default fetched data will be returned in the return hashref.

get RETURN VALUE

$VAR1 = {
        'body' => '<?xml version="1.0" ?>
<!DOCTYPE folder-listing SYSTEM "obex-folder-listing.dtd">
<folder-listing>
<parent-folder />
<folder name="audio" size="0" type="folder" modified="19700101T000000Z" user-perm="RW" />
<folder name="video" size="0" type="folder" modified="19700101T000000Z" user-perm="RW" />
<folder name="picture" size="0" type="folder" modified="19700101T000000Z" user-perm="RW" />
</folder-listing>
',
        'responses' => [
                        {
                            'info' => {
                                        'packet_length' => 6,
                                        'response_code' => 100,
                                        'headers_length' => 3,
                                        'response_code_meaning' => 'Continue'
                                    },
                            'headers' => {
                                            'body' => ''
                                        },
                            'raw_packet' => '�H'
                        },
                        {
                            'info' => {
                                        'packet_length' => 413,
                                        'response_code' => 100,
                                        'headers_length' => 410,
                                        'response_code_meaning' => 'Continue'
                                    },
                            'headers' => {
                                            'body' => '<?xml version="1.0" ?>
<!DOCTYPE folder-listing SYSTEM "obex-folder-listing.dtd">
<folder-listing>
<parent-folder />
<folder name="audio" size="0" type="folder" modified="19700101T000000Z" user-perm="RW" />
<folder name="video" size="0" type="folder" modified="19700101T000000Z" user-perm="RW" />
<folder name="picture" size="0" type="folder" modified="19700101T000000Z" user-perm="RW" />
</folder-listing>
'
                                        },
                            'raw_packet' => '��H�<?xml version="1.0" ?>
<!DOCTYPE folder-listing SYSTEM "obex-folder-listing.dtd">
<folder-listing>
<parent-folder />
<folder name="audio" size="0" type="folder" modified="19700101T000000Z" user-perm="RW" />
<folder name="video" size="0" type="folder" modified="19700101T000000Z" user-perm="RW" />
<folder name="picture" size="0" type="folder" modified="19700101T000000Z" user-perm="RW" />
</folder-listing>
'
                        },
                        {
                            'info' => {
                                        'packet_length' => 6,
                                        'response_code' => 200,
                                        'headers_length' => 3,
                                        'response_code_meaning' => 'OK, Success'
                                    },
                            'headers' => {
                                            'end_of_body' => ''
                                        },
                            'raw_packet' => '�I'
                        }
                        ],
        'response_code' => 100,
        'response_code_meaning' => 'Continue'
        };

The get() method returns either undef or an empty list (depending on the context) if an error occurred and the explanation of the error will by available via error() method. Otherwise it returns a big hashref. As opposed to connect(), disconnect() and set_path() method the returned hashref from get() method is a bit different because it can send (by default) several Get requests to fetch entire data. The keys/values of the return are as follows:

body

The <body> key will contain the entire data that was retrieved (if no_continue is false) or the contents of the Body header of the packet (if no_continue is set to a true value). If file argument is set, the body key will be empty.

response_code

The response_code key will contain the response code of the first received packet, note that if the request requires several Get packets to be sent out, the response code will be 100 (Continue) not 200.

response_code_meaning

The response_code_meaning key will contain the meaning of the response code of the first received packet.

responses

The responses key will contain an arrayref elements of which will be the return values of parse_sock() method from Net::OBEX::Headers module. There will be as many elements as many Get packets were sent out to retrieve entire data; of course, there will be only one if no_continue argument to get() is set to a true value. For more information, see parse_sock() method in Net::OBEX::Headers with the "is connect packet" flag set to false. If file argument is set, responses arrayref will be empty.

put

$obex->put( what => 'some_file' )
    or die $obex->error;

my $response_ref = $obex->put(
    what          => 'some_file',
    body_in_first => 0,
    length        => 12312,
    no_name       => 1,
    name          => 'other_file',
    time          => '20080320T202020Z',
) or die $obex->error;

Instructs the object to send PUT packet. As of now only sending of files is supported and due to the limited testing environment this support may be broken. During my tests (with Motorolla KRZR phone) doing put on files which it doesn't seem to allow (text file instead of pictures) would end up with 200, OK Success BUT the file would not be actually uploaded to the device and trying to get() it would result in 404. Not sure if this is a "glitch" with my phone or it is the way it's supposed to be... silently giving OKs when things are failing.

The data to be sent will be split into packets of the maximum size the other party can accept, if you want to change the size call the mtu() method before calling put(). The put() method takes one mandatory and several optional arguments which are as follows:

what

$obex->put( what => 'some_file' );

Mandatory. Specifies the file name of the file to PUT, later this may be changed to allow to contain some arbitrary contents.

body_in_first

$obex->put( what => 'some_file', body_in_first => 1 );

Optional. Takes either true or false values. If a true value is specified will send a Body header in the first PUT packet. Otherwise first Body header will be sent only after receiving a Continue response from the party. Defaults to: 0

length

$obex->put( what => 'some_file', length => 31232 );

Optional. If specified will stuff the PUT packet with a Length header containing the value of length argument (the length of the contents to PUT), this header is optional and by default will not be sent.

time

$obex->put( what => 'some_file', time => '20080320T202020Z' );

Optional. If specified will stuff the PUT packet with a Unicode version of Time header (date/time of last modification). Local times should be represented in the format YYYYMMDDTHHMMSS and UTC time in the format YYYYMMDDTHHMMSSZ. The letter T delimits the date from the time. UTC time is identified by concatenating a Z to the end of the sequence. By default no Time headers will be sent.

name

$obex->put( what => 'some_file', name => 'other_file' );

Optional. If specified will insert a Name header into the PUT packet with the value you specify. By default the value of what argument will be used unless you set the no_name argument (see below) to a true value.

no_name

$obex->put( what => 'some_file', no_name => 1 );

Optional. By default the object will insert a Name header into the packet with value being the name of the file specified in what argument. If you want to prevent this set no_name argument to a true value. Note: the Name header WILL be sent if you specify the name argument irrelevant of the no_name argument's value. Note 2: yo do NOT have to specify the no_name argument if you specified the name argument. Defaults to: 0

headers

$obex->put( what => 'file', headers => [ $some, $raw, $headers ] );

Optional. If you want to pass along some additional packet headers to the SetPath packet you can use the headers argument which takes an arrayref elements of which are OBEX packet headers. See Net::OBEX::Packet::Headers for information on how to make them. Defaults to: [] (no headers)

put RETURN VALUE

$VAR1 = {
    'info' => {
        'packet_length' => 3,
        'response_code' => 200,
        'headers_length' => 0,
        'response_code_meaning' => 'OK, Success'
    },
    'raw_packet' => '�'
};

If an error occurred during the request, put() will return either undef or an empty list, depending on the context and the reason for the error will be available via error() method. Otherwise it will return a hashref presented above. If the dump above is not self explanatory see Net::OBEX::Response parse_sock() method description for the return value when "is connect packet" option is false.

close

$obex->close;

$obex->close('No want you no moar');

Similar to disconnect() method, except this one also closes the socket. Takes one optional argument which is the text to send out in the Description header of the Disconnect packet. Always returns 1.

response

my $last_response_ref = $obex->response;

Takes no arguments, returns the return value of the last successful get(), put(), set_path(), connect() or disconnect() method.

sock

my $socket = $obex->sock;

$obex->sock( $new_socket );

Returns a Socket::Class object which is used by the module for communications. Technically you can swap it out to the socket of your choice by giving it as an argument (but should you? :) ).

error

my $response_ref = $obex->set_path
    or die "Error: " . $obex->error;

If any of the connect(), disconnect(), set_path or get() methods fail they will return either undef or an empty list depending on the context and the reason for the failure will be available via error() method. Takes no arguments, returns a human readable error message.

mtu

my $server_mtu = $obex->mtu;

Takes no arguments, must be called after a successful call to connect() returns the maximum size of the packet in bytes the device we connected to can accept (as reported by the device in response to Connect).

connection_id

my $raw_connection_id_header = $obex->connection_id;

If Connection ID header was present in the response to the Connect packet when calling the connect() method the Net::OBEX object will automatically store it and include it in any other packets sent after connection (as per specs). The connection_id() method returns a raw Connection ID header, it may take an argument which will override the set header, but it's probably a BadIdea(tm).

obj_res

my $net_obex_response_object = $obex->obj_res;

Takes no arguments, returns a Net::OBEX::Response object used internally.

obj_head

my $net_obex_packet_headers_object = $obex->obj_head;

Takes no arguments, returns a Net::OBEX::Packet::Headers object used internally. You can use this object to create any additional headers you'd want to include in headers arguments (where applicable).

obj_req

my $net_obex_packet_request = $obex->obj_req;

Takes no arguments, returns a Net::OBEX::Packet::Request object used internally.

EXAMPLES

The examples directory of this distribution contains get.pl and put.pl scripts which work fine for me, note that you'll need to change address/port as well as filenames for your device.

REPOSITORY

Fork this module on GitHub: https://github.com/zoffixznet/Net-OBEX

BUGS

To report bugs or request features, please use https://github.com/zoffixznet/Net-OBEX/issues

If you can't access GitHub, you can email your request to bug-Net-OBEX at rt.cpan.org

AUTHOR

Zoffix Znet <zoffix at cpan.org> (http://zoffix.com/, http://haslayout.net/)

LICENSE

You can use and distribute this module under the same terms as Perl itself. See the LICENSE file included in this distribution for complete details.