File Coverage

blib/lib/Net/OBEX.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             package Net::OBEX;
2              
3 2     2   37358 use warnings;
  2         5  
  2         64  
4 2     2   10 use strict;
  2         3  
  2         87  
5              
6             our $VERSION = '1.001001'; # VERSION
7              
8 2     2   10 use Carp;
  2         3  
  2         130  
9 2     2   889 use Socket::Class;
  0            
  0            
10             use IO::Handle;
11             use Net::OBEX::Packet::Request;
12             use Net::OBEX::Response;
13             use Net::OBEX::Packet::Headers;
14             use Devel::TakeHashArgs;
15              
16             use base qw(Class::Data::Accessor);
17              
18             __PACKAGE__->mk_classaccessors( qw(
19             sock
20             error
21             mtu
22             success
23             code
24             status
25             connection_id
26             obj_res
27             obj_head
28             obj_req
29             response
30             )
31             );
32              
33             sub new {
34             my $class = shift;
35             my $self = bless {}, $class;
36              
37             $self->obj_head( Net::OBEX::Packet::Headers->new );
38             $self->obj_req( Net::OBEX::Packet::Request->new );
39             $self->obj_res( Net::OBEX::Response->new );
40              
41             return $self;
42             }
43              
44             sub connect {
45             my $self = shift;
46             $self->$_(undef) for qw(success code status mtu);
47             get_args_as_hash(\@_, \ my %args, {
48             version => "\x10",
49             mtu => 4096,
50             domain => 'bluetooth',
51             type => 'stream',
52             proto => 'rfcomm',
53             headers => [],
54             },
55             [ qw(address port) ],
56             )
57             or croak $@;
58              
59             my $sock = Socket::Class->new(
60             'domain' => $args{domain},
61             'type' => $args{type},
62             'proto' => $args{proto},
63             'remote_addr' => $args{address},
64             'remote_port' => $args{port},
65             ) or return $self->_set_error(
66             'Failed to create socket: ' . Socket::Class->error
67             );
68              
69             $self->sock( $sock );
70              
71             defined $args{target}
72             and push @{ $args{headers} },
73             $self->obj_head->make( target => pack 'H*', $args{target} );
74              
75             my $connect_packet = $self->obj_req->make(
76             packet => 'connect',
77             mtu => $args{mtu},
78             version => $args{version},
79             headers => $args{headers},
80             );
81             $sock->send( $connect_packet );
82              
83             my $obj_response = $self->obj_res;
84             my $response_ref = $obj_response->parse_sock( $sock, 'connect' )
85             or return $self->_set_error( $obj_response->error );
86              
87             # make and save connection ID header.. we will need it in every
88             # packet
89             if ( defined (my $id = $response_ref->{headers}{connection_id}) ) {
90             $self->connection_id(
91             $self->obj_head->make( connection_id => $id )
92             );
93             }
94              
95             # save other party's MTU
96             $self->mtu( $response_ref->{info}{mtu} || 255 );
97              
98             $response_ref->{info}{response_code} == 200
99             and $self->success(1);
100              
101             $self->code( $response_ref->{info}{response_code} );
102             $self->status( $response_ref->{info}{response_code_meaning} );
103              
104             return $self->response( $response_ref );
105             }
106              
107             sub disconnect {
108             my $self = shift;
109             get_args_as_hash( \@_, \ my %args, { headers => [] } )
110             or croak $@;
111              
112             # Connection ID must be the first header if it's present
113             $self->_add_connection_id( $args{headers} );
114              
115             defined $args{description}
116             and push @{ $args{headers} },
117             $self->head->make( description => $args{description} );
118              
119             my $disconnect_packet = $self->obj_req->make(
120             packet => 'disconnect',
121             headers => $args{headers},
122             );
123              
124             my $sock = $self->sock;
125             $sock->send( $disconnect_packet );
126              
127             my $obj_response = $self->obj_res;
128             my $response_ref = $obj_response->parse_sock( $sock )
129             or return $self->_set_error( $obj_response->error );
130              
131             return $self->response( $response_ref );
132             }
133              
134             sub set_path {
135             my $self = shift;
136             $self->$_(undef) for qw(success code status);
137             get_args_as_hash( \@_, \ my %args, { headers => [] } )
138             or croak $@;
139              
140             # Connection ID must be the first header if it's present
141             $self->_add_connection_id( $args{headers} );
142              
143             # the path to setpath to should go into Name header
144             defined $args{path}
145             and push @{ $args{headers} },
146             $self->obj_head->make( name => $args{path} );
147              
148             my $set_path_packet = $self->obj_req->make(
149             packet => 'setpath',
150             headers => $args{headers},
151             (defined $args{do_up } ? ( do_up => $args{do_up } ) : ()),
152             (defined $args{no_create} ? ( no_create => $args{no_create} ) : ()),
153             );
154              
155             my $sock = $self->sock;
156             $sock->send( $set_path_packet );
157              
158             my $obj_response = $self->obj_res;
159             my $response_ref = $obj_response->parse_sock( $sock )
160             or return $self->_set_error( $obj_response->error );
161              
162             $response_ref->{info}{response_code} == 200
163             and $self->success(1);
164              
165             $self->code( $response_ref->{info}{response_code} );
166             $self->status( $response_ref->{info}{response_code_meaning} );
167              
168             return $self->response( $response_ref );
169             }
170              
171             sub get {
172             my $self = shift;
173             $self->$_(undef) for qw(success code status);
174             get_args_as_hash( \@_, \ my %args, { is_final => 1, headers => [] } )
175             or croak $@;
176              
177             # Connection ID must be the first header if it's present
178             $self->_add_connection_id( $args{headers} );
179              
180             my $head = $self->obj_head;
181             for ( qw(type name ) ) {
182             defined $args{ $_ }
183             and push @{ $args{headers} }, $head->make( $_ => $args{ $_ } );
184             }
185              
186             my $obj_request = $self->obj_req;
187             my $packet = $obj_request->make(
188             packet => 'get',
189             is_final => $args{is_final},
190             headers => $args{headers},
191             );
192              
193             my $sock = $self->sock;
194             $sock->send( $packet );
195              
196             my @responses;
197             my $obj_response = $self->obj_res;
198             my $full_body = '';
199             my $first_response_code;
200             my $first_response_code_meaning;
201             CONTINIUE_GET: {
202             my $response_ref = $obj_response->parse_sock( $sock )
203             or return $self->_set_error( $obj_response->error );
204              
205             unless ( defined $first_response_code ) {
206             ( $first_response_code, $first_response_code_meaning )
207             = @{ $response_ref->{info} }{
208             qw(response_code response_code_meaning)
209             }
210             }
211              
212             if ( exists $response_ref->{headers}{body}
213             or exists $response_ref->{headers}{end_of_body}
214             ) {
215             my $body = exists $response_ref->{headers}{end_of_body}
216             ? $response_ref->{headers}{end_of_body}
217             : $response_ref->{headers}{body};
218              
219             if ( exists $args{file} ) {
220             $args{file}->print($body);
221             }
222             else {
223             $full_body .= $body;
224             push @responses, $response_ref;
225             }
226             }
227              
228             # if server asks to "Continue"
229             if ( $response_ref->{info}{response_code} == 100
230             and not $args{no_continue}
231             ) {
232             $sock->send(
233             $obj_request->make( packet => 'get', is_final => 1 )
234             );
235              
236             redo CONTINIUE_GET;
237             }
238              
239             unless (
240             $response_ref->{info}{response_code} == 200
241             or $response_ref->{info}{response_code} == 100
242             ) {
243             $self->status(
244             $response_ref->{info}{response_code_meaning}
245             );
246             $self->code( $response_ref->{info}{response_code} );
247             $response_ref->{is_error} = 1;
248             return $response_ref;
249             }
250             } # CONTINUTE_GET block end
251              
252             $first_response_code == 200 or $first_response_code == 100
253             and $self->success(1);
254              
255             $self->code( $first_response_code );
256             $self->status( $first_response_code_meaning );
257              
258             return $self->response( {
259             body => $full_body,
260             responses => \@responses,
261             response_code => $first_response_code,
262             response_code_meaning => $first_response_code_meaning,
263             }
264             );
265             }
266              
267             sub put {
268             my $self = shift;
269             $self->$_(undef) for qw(success code status);
270             get_args_as_hash( \@_, \ my %args, {
271             headers => [],
272             body_in_first => 0,
273             no_name => 0,
274             },
275             [ 'what' ],
276             ) or croak $@;
277              
278             # Connection ID must be the first header if it's present
279             $self->_add_connection_id( $args{headers} );
280              
281             my $head = $self->obj_head;
282             for ( qw(length time name type) ) {
283             exists $args{ $_ }
284             and push @{ $args{headers} }, $head->make( $_, $args{$_} );
285             }
286              
287             unless ( $args{no_name} or exists $args{name} ) {
288             push @{ $args{headers} }, $head->make( name => $args{what} );
289             }
290              
291             open my $fh, '<', $args{what}
292             or return $self->_set_error("Failed to open $args{what} ($!)");
293              
294             binmode $fh;
295              
296             my $mtu = $self->mtu - 2 - length join '', @{ $args{headers} };
297              
298             my $sock = $self->sock;
299             my $obj_res = $self->obj_res;
300             my $obj_req = $self->obj_req;
301             unless ( $args{body_in_first} ) {
302             my $packet = $obj_req->make(
303             packet => 'put',
304             headers => $args{headers},
305             );
306              
307             $sock->send( $packet );
308             my $response_ref = $obj_res->parse_sock( $sock )
309             or return $self->_set_error(
310             'Socket error: ' . $obj_res->error
311             );
312              
313             unless (
314             $response_ref->{info}{response_code} == 200
315             or $response_ref->{info}{response_code} == 100
316             ) {
317             $self->status(
318             $response_ref->{info}{response_code_meaning}
319             );
320             $self->code( $response_ref->{info}{response_code} );
321             return $response_ref;
322             }
323             }
324              
325             {
326             local $/ = \$mtu;
327             while ( <$fh> ) {
328              
329             my $packet = $obj_req->make(
330             packet => 'put',
331             headers => [
332             ( $args{body_in_first} ? () : @{ $args{headers} } ),
333             $head->make( body => $_ ),
334             ],
335             );
336             $sock->send( $packet );
337             my $response_ref = $obj_res->parse_sock( $sock )
338             or return $self->_set_error(
339             'Socket error: ' . $obj_res->error
340             );
341              
342             unless (
343             $response_ref->{info}{response_code} == 200
344             or $response_ref->{info}{response_code} == 100
345             ) {
346             $self->status(
347             $response_ref->{info}{response_code_meaning}
348             );
349             $self->code( $response_ref->{info}{response_code} );
350             $response_ref->{is_error} = 1;
351             return $response_ref;
352             }
353             }
354             my $packet = $obj_req->make(
355             packet => 'put',
356             is_final => 1,
357             headers => [
358             @{ $args{headers} },
359             $head->make( end_of_body => '' ),
360             ],
361             );
362              
363             $sock->send( $packet );
364             my $response_ref = $obj_res->parse_sock( $sock );
365             $response_ref->{info}{response_code} == 200
366             and $self->success(1);
367              
368             $self->code( $response_ref->{info}{response_code} );
369             $self->status( $response_ref->{info}{response_code_meaning} );
370             return $self->response( $response_ref );
371             }
372             }
373              
374             sub close {
375             my ( $self, $description ) = @_;
376              
377             my $sock = $self->sock;
378             eval {
379             my $disconnect_packet = $self->obj_req->make(
380             packet => 'disconnect',
381             headers => [
382             defined $description
383             ? $self->obj_head->make( description => $description )
384             : ()
385             ],
386             );
387              
388             $sock->send( $disconnect_packet );
389             };
390             $sock->free();
391              
392             return 1;
393             }
394              
395             sub _add_connection_id {
396             my ( $self, $headers_ref ) = @_;
397             if ( defined ( my $id = $self->connection_id ) ) {
398             unshift @$headers_ref, $id;
399             }
400             }
401              
402             sub _set_error {
403             my ( $self, $error ) = @_;
404             $self->error( $error );
405             return;
406             }
407              
408             1;
409             __END__