File Coverage

blib/lib/Protocol/DBus/Message.pm
Criterion Covered Total %
statement 90 98 91.8
branch 29 44 65.9
condition 7 18 38.8
subroutine 19 20 95.0
pod 7 11 63.6
total 152 191 79.5


line stmt bran cond sub pod time code
1             package Protocol::DBus::Message;
2              
3 1     1   745 use strict;
  1         2  
  1         29  
4 1     1   5 use warnings;
  1         1  
  1         26  
5              
6             =encoding utf-8
7              
8             =head1 NAME
9              
10             Protocol::DBus::Message
11              
12             =head1 DESCRIPTION
13              
14             This class encapsulates a single DBus message. You generally should not
15             instantiate it directly.
16              
17             =cut
18              
19 1     1   5 use Protocol::DBus::Marshal ();
  1         2  
  1         13  
20 1     1   462 use Protocol::DBus::Message::Header ();
  1         3  
  1         23  
21              
22 1     1   6 use constant _PROTOCOL_VERSION => 1;
  1         2  
  1         260  
23              
24             sub parse {
25 8     8 0 29685 my ($class, $buf_sr, $filehandles_ar) = @_;
26              
27 8 50       25 if ( my ($hdr, $hdr_len, $is_be) = Protocol::DBus::Message::Header::parse_simple($buf_sr) ) {
28              
29 8 50       25 if (length($$buf_sr) >= ($hdr_len + $hdr->[4])) {
30              
31 8         29 my $body_sig = $hdr->[6]{ Protocol::DBus::Message::Header::FIELD()->{'SIGNATURE'} };
32              
33 8 100       17 if ($hdr->[4]) {
34 6 50       15 die "No SIGNATURE header field!" if !defined $body_sig;
35             }
36              
37 8         14 my $body_data;
38              
39 8 100       15 if ($body_sig) {
40 6         9 local $Protocol::DBus::Marshal::FILEHANDLES = $filehandles_ar;
41              
42 6 50       41 ($body_data) = Protocol::DBus::Marshal->can( $is_be ? 'unmarshal_be' : 'unmarshal_le' )->($buf_sr, $hdr_len, $body_sig);
43             }
44              
45 8         27 my %self = ( _body_sig => $body_sig );
46 8         12 @self{'_type', '_flags', '_serial', '_hfields', '_body'} = (@{$hdr}[1, 2, 5, 6], $body_data);
  8         38  
47              
48             # Remove the unmarshaled bytes.
49 8         34 substr( $$buf_sr, 0, $hdr_len + $hdr->[4], q<> );
50              
51 8         42 return bless \%self, $class;
52             }
53             }
54              
55 0         0 return undef;
56             }
57              
58 1     1   8 use constant _REQUIRED => ('type', 'serial', 'hfields');
  1         2  
  1         886  
59              
60             sub new {
61 2     2 0 1177 my ($class, %opts) = @_;
62              
63 2         6 my @missing = grep { !defined $opts{$_} } _REQUIRED();
  6         17  
64 2 50       8 die "missing: @missing" if @missing;
65              
66 2   50     9 $opts{'type'} = Protocol::DBus::Message::Header::MESSAGE_TYPE()->{ $opts{'type'} } || die "Bad “type”: '$opts{'type'}'";
67              
68 2         3 my $flags = 0;
69 2 50       5 if ($opts{'flags'}) {
70 0         0 for my $f (@{ $opts{'flags'} }) {
  0         0  
71 0   0     0 $flags |= Protocol::DBus::Message::Header::FLAG()->{$f} || die "Bad “flag”: $f";
72             }
73             }
74              
75 2         4 $opts{'flags'} = $flags;
76              
77 2         4 my %hfields;
78              
79 2 50       5 if ($opts{'hfields'}) {
80 2         2 my $field_num;
81              
82 2         3 my $fi = 0;
83 2         3 while ( $fi < @{ $opts{'hfields'} } ) {
  11         25  
84 9         12 my ($name, $value) = @{ $opts{'hfields'} }[ $fi, 1 + $fi ];
  9         20  
85 9         14 $fi += 2;
86              
87 9   33     23 $field_num = Protocol::DBus::Message::Header::FIELD()->{$name} || do {
88             die "Bad “hfields” name: “$name”";
89             };
90              
91             $hfields{ $field_num } = [
92 9         22 Protocol::DBus::Message::Header::FIELD_SIGNATURE()->{$name},
93             $value,
94             ];
95              
96 9 100       19 if ($field_num == Protocol::DBus::Message::Header::FIELD()->{'SIGNATURE'}) {
97 1         2 $opts{'body_sig'} = $value;
98             }
99             }
100             }
101              
102 2         10 $opts{'hfields'} = bless \%hfields, 'Protocol::DBus::Type::Dict';
103              
104 2 100       7 if ($opts{'body'}) {
    50          
105 1 50       4 die "“body” requires a SIGNATURE header!" if !$opts{'body_sig'};
106             }
107             elsif ($opts{'body_sig'}) {
108 0         0 die "SIGNATURE header given without “body”!";
109             }
110             else {
111 1         2 $opts{'body'} = \q<>;
112             }
113              
114 2         6 my %self = map { ( "_$_" => $opts{$_} ) } keys %opts;
  11         29  
115              
116 2         11 return bless \%self, $class;
117             }
118              
119             #----------------------------------------------------------------------
120              
121             =head1 METHODS
122              
123             =head2 I->get_header( $NAME )
124              
125             $NAME is, e.g., C or the value of the corresponding
126             member of C.
127              
128             =cut
129              
130             sub get_header {
131 27 50   27 1 683 if ($_[1] =~ tr<0-9><>c) {
132 27   50     102 return $_[0]->{'_hfields'}{ Protocol::DBus::Message::Header::FIELD()->{$_[1]} || die("Bad header: “$_[1]”") };
133             }
134              
135 0         0 return $_[0]->{'_hfields'}{$_[1]};
136             }
137              
138             =head2 I->get_body()
139              
140             Always returned as an array reference or undef. See below about mapping
141             between D-Bus and Perl.
142              
143             =cut
144              
145             sub get_body {
146 6     6 1 151 return $_[0]->{'_body'};
147             }
148              
149             =head2 I->get_type()
150              
151             Returns a number. Cross-reference with the D-Bus specification.
152              
153             =cut
154              
155             sub get_type {
156 4     4 1 2370 return $_[0]->{'_type'};
157             }
158              
159             =head2 I->type_is( $NAME )
160              
161             Convenience method; $NAME is, e.g., C.
162              
163             =cut
164              
165             sub type_is {
166 8     8 1 1239 my ($self, $name) = @_;
167              
168 8   33     44 return $_[0]->{'_type'} == (Protocol::DBus::Message::Header::MESSAGE_TYPE()->{$name} || do {
169             die "Invalid type name: $name";
170             });
171             }
172              
173             =head2 I->get_flags()
174              
175             Returns a number. Cross-reference with the D-Bus specification.
176              
177             =cut
178              
179             sub get_flags {
180 4     4 1 113 return $_[0]->{'_flags'};
181             }
182              
183             =head2 I->flags_have( @NAME )
184              
185             Convenience method; indicates whether all of the given @NAMES
186             (e.g., C) correspond to flags that are set in the message.
187              
188             =cut
189              
190             sub flags_have {
191 2     2 1 74 my ($self, @names) = @_;
192              
193 2 50       6 die "Need flag names!" if !@names;
194              
195 2         6 for my $name (@names) {
196 2 50 33     9 return 0 if !($_[0]->{'_flags'} & (Protocol::DBus::Message::Header::FLAG()->{$name} || do {
197             die "Invalid flag name: “$name”";
198             }));
199             }
200              
201 2         6 return 1;
202             }
203              
204             =head2 I->get_serial()
205              
206             Returns a number.
207              
208             =cut
209              
210             sub get_serial {
211 4     4 1 107 return $_[0]->{'_serial'};
212             }
213              
214             #----------------------------------------------------------------------
215              
216             our $_use_be;
217             BEGIN {
218 1     1   108 $_use_be = 0;
219             }
220              
221             sub to_string_le {
222 2     2 0 11 return _to_string(@_);
223             }
224              
225             sub to_string_be {
226 0     0 0 0 local $_use_be = 1;
227 0         0 return _to_string(@_);
228             }
229              
230             #----------------------------------------------------------------------
231              
232 1     1   8 use constant _LEADING_BYTE => map { ord } ('l', 'B');
  1         2  
  1         2  
  2         296  
233              
234             sub _to_string {
235 2     2   3 my ($self) = @_;
236              
237 2         4 my ($body_m_sr, $fds_ar);
238              
239 2 100       7 if ($self->{'_body_sig'}) {
240             ($body_m_sr, $fds_ar) = Protocol::DBus::Marshal->can( $_use_be ? 'marshal_be' : 'marshal_le' )->(
241             $self->{'_body_sig'},
242 1 50       32 $self->{'_body'},
243             );
244             }
245              
246             local $self->{'_hfields'}{ Protocol::DBus::Message::Header::FIELD()->{'UNIX_FDS'} } = [
247 2 50 66     10 Protocol::DBus::Message::Header::FIELD_SIGNATURE()->{'UNIX_FDS'},
248             0 + @$fds_ar,
249             ] if $fds_ar && @$fds_ar;
250              
251             my $data = [
252             (_LEADING_BYTE())[ $_use_be ],
253             $self->{'_type'},
254             $self->{'_flags'},
255             _PROTOCOL_VERSION(),
256             $body_m_sr ? length( $$body_m_sr ) : 0,
257             $self->{'_serial'},
258 2 100       9 $self->{'_hfields'},
259             ];
260              
261 2 50       16 my ($buf_sr) = Protocol::DBus::Marshal->can( $_use_be ? 'marshal_be' : 'marshal_le' )->(
262             Protocol::DBus::Message::Header::SIGNATURE(),
263             $data,
264             );
265              
266 2         7 Protocol::DBus::Pack::align_str($$buf_sr, 8);
267              
268 2 100       8 $$buf_sr .= $$body_m_sr if $body_m_sr;
269              
270 2         9 return( $buf_sr, $fds_ar );
271             }
272              
273             #----------------------------------------------------------------------
274              
275             =head1 MAPPING D-BUS TO PERL
276              
277             =over
278              
279             =item * Numeric and string types are represented as plain Perl scalars.
280              
281             =item * UNIX_FDs are normally represented as Perl filehandle objects.
282             If Protocol::DBus receives a UNIX_FD that doesn’t correspond to a received
283             file descriptor, the UNIX_FD will be represented as the number passed in
284             the raw D-Bus message, and a warning is thrown.
285              
286             =item * By default, variant signatures are discarded, and the values are
287             given by themselves. See L’s
288             C if you need an alternative mapping
289             method that preserves the signatures.
290              
291             =item * Other containers are represented as blessed references:
292             C, C, and
293             C. Currently these are just plain hash and
294             array references that are bless()ed; i.e., the classes themselves have no
295             methods defined (and aren’t even defined Perl namespaces).
296              
297             =back
298              
299             =head1 MAPPING PERL TO D-BUS
300              
301             =over
302              
303             =item * Use plain Perl scalars to represent all numeric and string types.
304              
305             =item * Use plain Perl filehandle objects to represent UNIX_FDs.
306              
307             =item * Use array references to represent D-Bus arrays and structs.
308             Use hash references for dicts.
309              
310             =item * Use a two-member array reference—signature then value—to represent
311             a D-Bus variant. (Note the inconsistency with the reverse mapping.)
312              
313             =back
314              
315             =head2 Examples
316              
317             =over
318              
319             =item * C - C<( $s0, [ $s1 ] )>
320              
321             =item * C - C<( \@ss )>
322              
323             =item * C - C<( \%ss )>
324              
325             =back
326              
327             =cut
328              
329             1;