PageRenderTime 55ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/Net/DBus/Binding/Iterator.pm

https://gitlab.com/intrigeri/perl-net-dbus
Perl | 729 lines | 481 code | 156 blank | 92 comment | 104 complexity | f05e6901cef8ce2134609156af391ca4 MD5 | raw file
Possible License(s): AGPL-1.0
  1. # -*- perl -*-
  2. #
  3. # Copyright (C) 2004-2011 Daniel P. Berrange
  4. #
  5. # This program is free software; You can redistribute it and/or modify
  6. # it under the same terms as Perl itself. Either:
  7. #
  8. # a) the GNU General Public License as published by the Free
  9. # Software Foundation; either version 2, or (at your option) any
  10. # later version,
  11. #
  12. # or
  13. #
  14. # b) the "Artistic License"
  15. #
  16. # The file "COPYING" distributed along with this file provides full
  17. # details of the terms and conditions of the two licenses.
  18. =pod
  19. =head1 NAME
  20. Net::DBus::Binding::Iterator - Reading and writing message parameters
  21. =head1 SYNOPSIS
  22. Creating a new message
  23. my $msg = new Net::DBus::Binding::Message::Signal;
  24. my $iterator = $msg->iterator;
  25. $iterator->append_boolean(1);
  26. $iterator->append_byte(123);
  27. Reading from a mesage
  28. my $msg = ...get it from somewhere...
  29. my $iter = $msg->iterator();
  30. my $i = 0;
  31. while ($iter->has_next()) {
  32. $iter->next();
  33. $i++;
  34. if ($i == 1) {
  35. my $val = $iter->get_boolean();
  36. } elsif ($i == 2) {
  37. my $val = $iter->get_byte();
  38. }
  39. }
  40. =head1 DESCRIPTION
  41. Provides an iterator for reading or writing message
  42. fields. This module provides a Perl API to access the
  43. dbus_message_iter_XXX methods in the C API. The array
  44. and dictionary types are not yet supported, and there
  45. are bugs in the Quad support (ie it always returns -1!).
  46. =head1 METHODS
  47. =over 4
  48. =cut
  49. package Net::DBus::Binding::Iterator;
  50. use 5.006;
  51. use strict;
  52. use warnings;
  53. use Net::DBus;
  54. =item $res = $iter->has_next()
  55. Determines if there are any more fields in the message
  56. itertor to be read. Returns a positive value if there
  57. are more fields, zero otherwise.
  58. =item $success = $iter->next()
  59. Skips the iterator onto the next field in the message.
  60. Returns a positive value if the current field pointer
  61. was successfully advanced, zero otherwise.
  62. =item my $val = $iter->get_boolean()
  63. =item $iter->append_boolean($val);
  64. Read or write a boolean value from/to the
  65. message iterator
  66. =item my $val = $iter->get_byte()
  67. =item $iter->append_byte($val);
  68. Read or write a single byte value from/to the
  69. message iterator.
  70. =item my $val = $iter->get_string()
  71. =item $iter->append_string($val);
  72. Read or write a UTF-8 string value from/to the
  73. message iterator
  74. =item my $val = $iter->get_object_path()
  75. =item $iter->append_object_path($val);
  76. Read or write a UTF-8 string value, whose contents is
  77. a valid object path, from/to the message iterator
  78. =item my $val = $iter->get_signature()
  79. =item $iter->append_signature($val);
  80. Read or write a UTF-8 string, whose contents is a
  81. valid type signature, value from/to the message iterator
  82. =item my $val = $iter->get_int16()
  83. =item $iter->append_int16($val);
  84. Read or write a signed 16 bit value from/to the
  85. message iterator
  86. =item my $val = $iter->get_uint16()
  87. =item $iter->append_uint16($val);
  88. Read or write an unsigned 16 bit value from/to the
  89. message iterator
  90. =item my $val = $iter->get_int32()
  91. =item $iter->append_int32($val);
  92. Read or write a signed 32 bit value from/to the
  93. message iterator
  94. =item my $val = $iter->get_uint32()
  95. =item $iter->append_uint32($val);
  96. Read or write an unsigned 32 bit value from/to the
  97. message iterator
  98. =item my $val = $iter->get_int64()
  99. =item $iter->append_int64($val);
  100. Read or write a signed 64 bit value from/to the
  101. message iterator. An error will be raised if this
  102. build of Perl does not support 64 bit integers
  103. =item my $val = $iter->get_uint64()
  104. =item $iter->append_uint64($val);
  105. Read or write an unsigned 64 bit value from/to the
  106. message iterator. An error will be raised if this
  107. build of Perl does not support 64 bit integers
  108. =item my $val = $iter->get_double()
  109. =item $iter->append_double($val);
  110. Read or write a double precision floating point value
  111. from/to the message iterator
  112. =item my $val = $iter->get_unix_fd()
  113. =item $iter->append_unix_fd($val);
  114. Read or write a unix_fd value from/to the
  115. message iterator
  116. =cut
  117. sub get_int64 {
  118. my $self = shift;
  119. return $self->_get_int64;
  120. }
  121. sub get_uint64 {
  122. my $self = shift;
  123. return $self->_get_uint64;
  124. }
  125. sub append_int64 {
  126. my $self = shift;
  127. $self->_append_int64(shift);
  128. }
  129. sub append_uint64 {
  130. my $self = shift;
  131. $self->_append_uint64(shift);
  132. }
  133. =item my $value = $iter->get()
  134. =item my $value = $iter->get($type);
  135. Get the current value pointed to by this iterator. If the optional
  136. C<$type> parameter is supplied, the wire type will be compared with
  137. the desired type & a warning output if their differ. The C<$type>
  138. value must be one of the C<Net::DBus::Binding::Message::TYPE*>
  139. constants.
  140. =cut
  141. sub get {
  142. my $self = shift;
  143. my $type = shift;
  144. if (defined $type) {
  145. if (ref($type)) {
  146. if (ref($type) eq "ARRAY") {
  147. # XXX we should recursively validate types
  148. $type = $type->[0];
  149. if ($type eq &Net::DBus::Binding::Message::TYPE_DICT_ENTRY) {
  150. $type = &Net::DBus::Binding::Message::TYPE_ARRAY;
  151. }
  152. } else {
  153. die "unsupport type reference $type";
  154. }
  155. }
  156. my $actual = $self->get_arg_type;
  157. if ($actual != $type) {
  158. # "Be strict in what you send, be leniant in what you accept"
  159. # - ie can't rely on python to send correct types, eg int32 vs uint32
  160. # But, don't complain for variants because a number of apps (eg HAL)
  161. # claim to return variants, but in fact don't correctly encode their
  162. # data as variants. Technically a bug in the server, but it does
  163. # 'just work' normally.
  164. warn "requested type '" . chr($type) . "' ($type) did not match wire type '" . chr($actual) . "' ($actual)"
  165. if $type != &Net::DBus::Binding::Message::TYPE_VARIANT;
  166. $type = $actual;
  167. }
  168. } else {
  169. $type = $self->get_arg_type;
  170. }
  171. if ($type == &Net::DBus::Binding::Message::TYPE_STRING) {
  172. return $self->get_string;
  173. } elsif ($type == &Net::DBus::Binding::Message::TYPE_BOOLEAN) {
  174. return $self->get_boolean;
  175. } elsif ($type == &Net::DBus::Binding::Message::TYPE_BYTE) {
  176. return $self->get_byte;
  177. } elsif ($type == &Net::DBus::Binding::Message::TYPE_INT16) {
  178. return $self->get_int16;
  179. } elsif ($type == &Net::DBus::Binding::Message::TYPE_UINT16) {
  180. return $self->get_uint16;
  181. } elsif ($type == &Net::DBus::Binding::Message::TYPE_INT32) {
  182. return $self->get_int32;
  183. } elsif ($type == &Net::DBus::Binding::Message::TYPE_UINT32) {
  184. return $self->get_uint32;
  185. } elsif ($type == &Net::DBus::Binding::Message::TYPE_INT64) {
  186. return $self->get_int64;
  187. } elsif ($type == &Net::DBus::Binding::Message::TYPE_UINT64) {
  188. return $self->get_uint64;
  189. } elsif ($type == &Net::DBus::Binding::Message::TYPE_DOUBLE) {
  190. return $self->get_double;
  191. } elsif ($type == &Net::DBus::Binding::Message::TYPE_ARRAY) {
  192. my $array_type = $self->get_element_type();
  193. if ($array_type == &Net::DBus::Binding::Message::TYPE_DICT_ENTRY) {
  194. return $self->get_dict();
  195. } else {
  196. return $self->get_array($array_type);
  197. }
  198. } elsif ($type == &Net::DBus::Binding::Message::TYPE_STRUCT) {
  199. return $self->get_struct();
  200. } elsif ($type == &Net::DBus::Binding::Message::TYPE_VARIANT) {
  201. return $self->get_variant();
  202. } elsif ($type == &Net::DBus::Binding::Message::TYPE_DICT_ENTRY) {
  203. die "dictionary can only occur as part of an array type";
  204. } elsif ($type == &Net::DBus::Binding::Message::TYPE_INVALID) {
  205. die "cannot handle Net::DBus::Binding::Message::TYPE_INVALID";
  206. } elsif ($type == &Net::DBus::Binding::Message::TYPE_OBJECT_PATH) {
  207. return $self->get_object_path();
  208. } elsif ($type == &Net::DBus::Binding::Message::TYPE_SIGNATURE) {
  209. return $self->get_signature();
  210. } elsif ($type == &Net::DBus::Binding::Message::TYPE_UNIX_FD) {
  211. return $self->get_unix_fd();
  212. } else {
  213. die "unknown argument type '" . chr($type) . "' ($type)";
  214. }
  215. }
  216. =item my $hashref = $iter->get_dict()
  217. If the iterator currently points to a dictionary value, unmarshalls
  218. and returns the value as a hash reference.
  219. =cut
  220. sub get_dict {
  221. my $self = shift;
  222. my $iter = $self->_recurse();
  223. my $type = $iter->get_arg_type();
  224. my $dict = {};
  225. while ($type == &Net::DBus::Binding::Message::TYPE_DICT_ENTRY) {
  226. my $entry = $iter->get_struct();
  227. if ($#{$entry} != 1) {
  228. die "Dictionary entries must be structs of 2 elements. This entry has " . ($#{$entry}+1) ." elements";
  229. }
  230. $dict->{$entry->[0]} = $entry->[1];
  231. $iter->next();
  232. $type = $iter->get_arg_type();
  233. }
  234. return $dict;
  235. }
  236. =item my $hashref = $iter->get_array()
  237. If the iterator currently points to an array value, unmarshalls
  238. and returns the value as a array reference.
  239. =cut
  240. sub get_array {
  241. my $self = shift;
  242. my $array_type = shift;
  243. my $iter = $self->_recurse();
  244. my $type = $iter->get_arg_type();
  245. my $array = [];
  246. while ($type != &Net::DBus::Binding::Message::TYPE_INVALID) {
  247. if ($type != $array_type) {
  248. die "Element $type not of array type $array_type";
  249. }
  250. my $value = $iter->get($type);
  251. push @{$array}, $value;
  252. $iter->next();
  253. $type = $iter->get_arg_type();
  254. }
  255. return $array;
  256. }
  257. =item my $hashref = $iter->get_variant()
  258. If the iterator currently points to a variant value, unmarshalls
  259. and returns the value contained in the variant.
  260. =cut
  261. sub get_variant {
  262. my $self = shift;
  263. my $iter = $self->_recurse();
  264. return $iter->get();
  265. }
  266. =item my $hashref = $iter->get_struct()
  267. If the iterator currently points to an struct value, unmarshalls
  268. and returns the value as a array reference. The values in the array
  269. correspond to members of the struct.
  270. =cut
  271. sub get_struct {
  272. my $self = shift;
  273. my $iter = $self->_recurse();
  274. my $type = $iter->get_arg_type();
  275. my $struct = [];
  276. while ($type != &Net::DBus::Binding::Message::TYPE_INVALID) {
  277. my $value = $iter->get($type);
  278. push @{$struct}, $value;
  279. $iter->next();
  280. $type = $iter->get_arg_type();
  281. }
  282. return $struct;
  283. }
  284. =item $iter->append($value)
  285. =item $iter->append($value, $type)
  286. Appends a value to the message associated with this iterator. The
  287. value is marshalled into wire format, according to the following
  288. rules.
  289. If the C<$value> is an instance of L<Net::DBus::Binding::Value>,
  290. the embedded data type is used.
  291. If the C<$type> parameter is supplied, that is taken to represent
  292. the data type. The type must be one of the C<Net::DBus::Binding::Message::TYPE_*>
  293. constants.
  294. Otherwise, the data type is chosen to be a string, dict or array
  295. according to the perl data types SCALAR, HASH or ARRAY.
  296. =cut
  297. sub append {
  298. my $self = shift;
  299. my $value = shift;
  300. my $type = shift;
  301. if (ref($value) eq "Net::DBus::Binding::Value" &&
  302. ((! defined ref($type)) ||
  303. (ref($type) ne "ARRAY") ||
  304. $type->[0] != &Net::DBus::Binding::Message::TYPE_VARIANT)) {
  305. $type = $value->type;
  306. $value = $value->value;
  307. }
  308. if (!defined $type) {
  309. $type = $self->guess_type($value);
  310. }
  311. if (ref($type) eq "ARRAY") {
  312. my $maintype = $type->[0];
  313. my $subtype = $type->[1];
  314. if ($maintype == &Net::DBus::Binding::Message::TYPE_DICT_ENTRY) {
  315. $self->append_dict($value, $subtype);
  316. } elsif ($maintype == &Net::DBus::Binding::Message::TYPE_STRUCT) {
  317. $self->append_struct($value, $subtype);
  318. } elsif ($maintype == &Net::DBus::Binding::Message::TYPE_ARRAY) {
  319. $self->append_array($value, $subtype);
  320. } elsif ($maintype == &Net::DBus::Binding::Message::TYPE_VARIANT) {
  321. $self->append_variant($value, $subtype);
  322. } else {
  323. die "Unsupported compound type ", $maintype, " ('", chr($maintype), "')";
  324. }
  325. } else {
  326. # XXX is this good idea or not
  327. $value = '' unless defined $value;
  328. if ($type == &Net::DBus::Binding::Message::TYPE_BOOLEAN) {
  329. $self->append_boolean($value);
  330. } elsif ($type == &Net::DBus::Binding::Message::TYPE_BYTE) {
  331. $self->append_byte($value);
  332. } elsif ($type == &Net::DBus::Binding::Message::TYPE_STRING) {
  333. $self->append_string($value);
  334. } elsif ($type == &Net::DBus::Binding::Message::TYPE_INT16) {
  335. $self->append_int16($value);
  336. } elsif ($type == &Net::DBus::Binding::Message::TYPE_UINT16) {
  337. $self->append_uint16($value);
  338. } elsif ($type == &Net::DBus::Binding::Message::TYPE_INT32) {
  339. $self->append_int32($value);
  340. } elsif ($type == &Net::DBus::Binding::Message::TYPE_UINT32) {
  341. $self->append_uint32($value);
  342. } elsif ($type == &Net::DBus::Binding::Message::TYPE_INT64) {
  343. $self->append_int64($value);
  344. } elsif ($type == &Net::DBus::Binding::Message::TYPE_UINT64) {
  345. $self->append_uint64($value);
  346. } elsif ($type == &Net::DBus::Binding::Message::TYPE_DOUBLE) {
  347. $self->append_double($value);
  348. } elsif ($type == &Net::DBus::Binding::Message::TYPE_OBJECT_PATH) {
  349. $self->append_object_path($value);
  350. } elsif ($type == &Net::DBus::Binding::Message::TYPE_SIGNATURE) {
  351. $self->append_signature($value);
  352. } else {
  353. die "Unsupported scalar type ", $type, " ('", chr($type), "')";
  354. }
  355. }
  356. }
  357. =item my $type = $iter->guess_type($value)
  358. Make a best guess at the on the wire data type to use for
  359. marshalling C<$value>. If the value is a hash reference,
  360. the dictionary type is returned; if the value is an array
  361. reference the array type is returned; otherwise the string
  362. type is returned.
  363. =cut
  364. sub guess_type {
  365. my $self = shift;
  366. my $value = shift;
  367. if (ref($value)) {
  368. if (UNIVERSAL::isa($value, "Net::DBus::Binding::Value")) {
  369. my $type = $value->type;
  370. if (ref($type) && ref($type) eq "ARRAY") {
  371. my $maintype = $type->[0];
  372. my $subtype = $type->[1];
  373. if (!defined $subtype) {
  374. if ($maintype == &Net::DBus::Binding::Message::TYPE_DICT_ENTRY) {
  375. $subtype = [ $self->guess_type((keys(%{ $value->value }))[0]),
  376. $self->guess_type((values(%{ $value->value }))[0]) ];
  377. } elsif ($maintype == &Net::DBus::Binding::Message::TYPE_ARRAY) {
  378. $subtype = [ $self->guess_type(($value->value())[0]->[0]) ];
  379. } elsif ($maintype == &Net::DBus::Binding::Message::TYPE_STRUCT) {
  380. $subtype = [ map { $self->guess_type($_) } @{($value->value())[0]} ];
  381. } elsif ($maintype == &Net::DBus::Binding::Message::TYPE_VARIANT) {
  382. $subtype = $self->guess_type($value->value);
  383. } else {
  384. die "Unguessable compound type '$maintype' ('", chr($maintype), "')\n";
  385. }
  386. }
  387. return [$maintype, $subtype];
  388. } else {
  389. return $type;
  390. }
  391. } elsif (ref($value) eq "HASH") {
  392. my $key = (keys %{$value})[0];
  393. my $val = $value->{$key};
  394. # XXX Basically impossible to decide between DICT & STRUCT
  395. return [ &Net::DBus::Binding::Message::TYPE_DICT_ENTRY,
  396. [ &Net::DBus::Binding::Message::TYPE_STRING, $self->guess_type($val)] ];
  397. } elsif (ref($value) eq "ARRAY") {
  398. return [ &Net::DBus::Binding::Message::TYPE_ARRAY,
  399. [$self->guess_type($value->[0])] ];
  400. } else {
  401. die "cannot marshall reference of type " . ref($value);
  402. }
  403. } else {
  404. # XXX Should we bother trying to guess integer & floating point types ?
  405. # I say sod it, because strongly typed languages will support introspection
  406. # and loosely typed languages won't care about the difference
  407. return &Net::DBus::Binding::Message::TYPE_STRING;
  408. }
  409. }
  410. =item my $sig = $iter->format_signature($type)
  411. Given a data type representation, construct a corresponding
  412. signature string
  413. =cut
  414. sub format_signature {
  415. my $self = shift;
  416. my $type = shift;
  417. my ($sig, $t, $i);
  418. $sig = "";
  419. $i = 0;
  420. if (ref($type) eq "ARRAY") {
  421. while ($i <= $#{$type}) {
  422. $t = $$type[$i];
  423. if (ref($t) eq "ARRAY") {
  424. $sig .= $self->format_signature($t);
  425. } elsif ($t == &Net::DBus::Binding::Message::TYPE_DICT_ENTRY) {
  426. $sig .= chr (&Net::DBus::Binding::Message::TYPE_ARRAY);
  427. $sig .= "{" . $self->format_signature($$type[++$i]) . "}";
  428. } elsif ($t == &Net::DBus::Binding::Message::TYPE_STRUCT) {
  429. $sig .= "(" . $self->format_signature($$type[++$i]) . ")";
  430. } else {
  431. $sig .= chr($t);
  432. if ($t == &Net::DBus::Binding::Message::TYPE_VARIANT)
  433. {
  434. last;
  435. }
  436. }
  437. $i++;
  438. }
  439. } else {
  440. $sig .= chr ($type);
  441. }
  442. return $sig;
  443. }
  444. =item $iter->append_array($value, $type)
  445. Append an array of values to the message. The C<$value> parameter
  446. must be an array reference, whose elements all have the same data
  447. type specified by the C<$type> parameter.
  448. =cut
  449. sub append_array {
  450. my $self = shift;
  451. my $array = shift;
  452. my $type = shift;
  453. if (!defined($type)) {
  454. $type = [$self->guess_type($array->[0])];
  455. }
  456. die "array must only have one type"
  457. if $#{$type} > 0;
  458. my $sig = $self->format_signature($type);
  459. my $iter = $self->_open_container(&Net::DBus::Binding::Message::TYPE_ARRAY, $sig);
  460. foreach my $value (@{$array}) {
  461. $iter->append($value, $type->[0]);
  462. }
  463. $self->_close_container($iter);
  464. }
  465. =item $iter->append_struct($value, $type)
  466. Append a struct to the message. The C<$value> parameter
  467. must be an array reference, whose elements correspond to
  468. members of the structure. The C<$type> parameter encodes
  469. the type of each member of the struct.
  470. =cut
  471. sub append_struct {
  472. my $self = shift;
  473. my $struct = shift;
  474. my $type = shift;
  475. if (defined($type) &&
  476. $#{$struct} != $#{$type}) {
  477. die "number of values does not match type";
  478. }
  479. my $iter = $self->_open_container(&Net::DBus::Binding::Message::TYPE_STRUCT, "");
  480. my @type = defined $type ? @{$type} : ();
  481. foreach my $value (@{$struct}) {
  482. $iter->append($value, shift @type);
  483. }
  484. $self->_close_container($iter);
  485. }
  486. =item $iter->append_dict($value, $type)
  487. Append a dictionary to the message. The C<$value> parameter
  488. must be an hash reference.The C<$type> parameter encodes
  489. the type of the key and value of the hash.
  490. =cut
  491. sub append_dict {
  492. my $self = shift;
  493. my $hash = shift;
  494. my $type = shift;
  495. my $sig;
  496. $sig = "{";
  497. $sig .= $self->format_signature($type);
  498. $sig .= "}";
  499. my $iter = $self->_open_container(&Net::DBus::Binding::Message::TYPE_ARRAY, $sig);
  500. foreach my $key (keys %{$hash}) {
  501. my $value = $hash->{$key};
  502. my $entry = $iter->_open_container(&Net::DBus::Binding::Message::TYPE_DICT_ENTRY, "");
  503. $entry->append($key, $type->[0]);
  504. $entry->append($value, $type->[1]);
  505. $iter->_close_container($entry);
  506. }
  507. $self->_close_container($iter);
  508. }
  509. =item $iter->append_variant($value)
  510. Append a value to the message, encoded as a variant type. The
  511. C<$value> can be of any type, however, the variant will be
  512. encoded as either a string, dictionary or array according to
  513. the rules of the C<guess_type> method.
  514. =cut
  515. sub append_variant {
  516. my $self = shift;
  517. my $value = shift;
  518. my $type = shift;
  519. if (UNIVERSAL::isa($value, "Net::DBus::Binding::Value")) {
  520. $type = [$self->guess_type($value)];
  521. $value = $value->value;
  522. } elsif (!defined $type || !defined $type->[0]) {
  523. $type = [$self->guess_type($value)];
  524. }
  525. die "variant must only have one type"
  526. if defined $type && $#{$type} > 0;
  527. my $sig = $self->format_signature($type->[0]);
  528. my $iter = $self->_open_container(&Net::DBus::Binding::Message::TYPE_VARIANT, $sig);
  529. $iter->append($value, $type->[0]);
  530. $self->_close_container($iter);
  531. }
  532. =item my $type = $iter->get_arg_type
  533. Retrieves the type code of the value pointing to by this iterator.
  534. The returned code will correspond to one of the constants
  535. C<Net::DBus::Binding::Message::TYPE_*>
  536. =item my $type = $iter->get_element_type
  537. If the iterator points to an array, retrieves the type code of
  538. array elements. The returned code will correspond to one of the
  539. constants C<Net::DBus::Binding::Message::TYPE_*>
  540. =cut
  541. 1;
  542. =pod
  543. =back
  544. =head1 AUTHOR
  545. Daniel P. Berrange
  546. =head1 COPYRIGHT
  547. Copyright (C) 2004-2011 Daniel P. Berrange
  548. =head1 SEE ALSO
  549. L<Net::DBus::Binding::Message>
  550. =cut