File Coverage

blib/lib/Protocol/DBus/Message.pm
Criterion Covered Total %
statement 88 96 91.6
branch 28 42 66.6
condition 5 15 33.3
subroutine 19 20 95.0
pod 7 11 63.6
total 147 184 79.8


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