RPC2.pm 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701
  1. #
  2. # Copyright (C) 1998, 1999 Ken MacLeod
  3. # Frontier::RPC is free software; you can redistribute it
  4. # and/or modify it under the same terms as Perl itself.
  5. #
  6. # $Id: RPC2.pm,v 1.18 2002/08/02 18:35:21 ivan420 Exp $
  7. #
  8. # NOTE: see Storable for marshalling.
  9. use strict;
  10. package Frontier::RPC2;
  11. use XML::Parser;
  12. use vars qw{%scalars %char_entities};
  13. %char_entities = (
  14. '&' => '&',
  15. '<' => '&lt;',
  16. '>' => '&gt;',
  17. '"' => '&quot;',
  18. );
  19. # FIXME I need a list of these
  20. %scalars = (
  21. 'base64' => 1,
  22. 'boolean' => 1,
  23. 'dateTime.iso8601' => 1,
  24. 'double' => 1,
  25. 'int' => 1,
  26. 'i4' => 1,
  27. 'string' => 1,
  28. );
  29. sub new {
  30. my $class = shift;
  31. my $self = ($#_ == 0) ? { %{ (shift) } } : { @_ };
  32. bless $self, $class;
  33. if (defined $self->{'encoding'}) {
  34. $self->{'encoding_'} = " encoding=\"$self->{'encoding'}\"";
  35. } else {
  36. $self->{'encoding_'} = "";
  37. }
  38. return $self;
  39. }
  40. sub encode_call {
  41. my $self = shift; my $proc = shift;
  42. my @text;
  43. push @text, <<EOF;
  44. <?xml version="1.0"$self->{'encoding_'}?>
  45. <methodCall>
  46. <methodName>$proc</methodName>
  47. <params>
  48. EOF
  49. push @text, $self->_params([@_]);
  50. push @text, <<EOF;
  51. </params>
  52. </methodCall>
  53. EOF
  54. return join('', @text);
  55. }
  56. sub encode_response {
  57. my $self = shift;
  58. my @text;
  59. push @text, <<EOF;
  60. <?xml version="1.0"$self->{'encoding_'}?>
  61. <methodResponse>
  62. <params>
  63. EOF
  64. push @text, $self->_params([@_]);
  65. push @text, <<EOF;
  66. </params>
  67. </methodResponse>
  68. EOF
  69. return join('', @text);
  70. }
  71. sub encode_fault {
  72. my $self = shift; my $code = shift; my $message = shift;
  73. my @text;
  74. push @text, <<EOF;
  75. <?xml version="1.0"$self->{'encoding_'}?>
  76. <methodResponse>
  77. <fault>
  78. EOF
  79. push @text, $self->_item({faultCode => $code, faultString => $message});
  80. push @text, <<EOF;
  81. </fault>
  82. </methodResponse>
  83. EOF
  84. return join('', @text);
  85. }
  86. sub serve {
  87. my $self = shift; my $xml = shift; my $methods = shift;
  88. my $call;
  89. # FIXME bug in Frontier's XML
  90. $xml =~ s/(<\?XML\s+VERSION)/\L$1\E/;
  91. eval { $call = $self->decode($xml) };
  92. if ($@) {
  93. return $self->encode_fault(1, "error decoding RPC.\n" . $@);
  94. }
  95. if ($call->{'type'} ne 'call') {
  96. return $self->encode_fault(2,"expected RPC \`methodCall', got \`$call->{'type'}'\n");
  97. }
  98. my $method = $call->{'method_name'};
  99. if (!defined $methods->{$method}) {
  100. return $self->encode_fault(3, "no such method \`$method'\n");
  101. }
  102. my $result;
  103. my $eval = eval { $result = &{ $methods->{$method} }(@{ $call->{'value'} }) };
  104. if ($@) {
  105. return $self->encode_fault(4, "error executing RPC \`$method'.\n" . $@);
  106. }
  107. my $response_xml = $self->encode_response($result);
  108. return $response_xml;
  109. }
  110. sub _params {
  111. my $self = shift; my $array = shift;
  112. my @text;
  113. my $item;
  114. foreach $item (@$array) {
  115. push (@text, "<param>",
  116. $self->_item($item),
  117. "</param>\n");
  118. }
  119. return @text;
  120. }
  121. sub _item {
  122. my $self = shift; my $item = shift;
  123. my @text;
  124. my $ref = ref($item);
  125. if (!$ref) {
  126. push (@text, $self->_scalar ($item));
  127. } elsif ($ref eq 'ARRAY') {
  128. push (@text, $self->_array($item));
  129. } elsif ($ref eq 'HASH') {
  130. push (@text, $self->_hash($item));
  131. } elsif ($ref eq 'Frontier::RPC2::Boolean') {
  132. push @text, "<value><boolean>", $item->repr, "</boolean></value>\n";
  133. } elsif ($ref eq 'Frontier::RPC2::String') {
  134. push @text, "<value><string>", $item->repr, "</string></value>\n";
  135. } elsif ($ref eq 'Frontier::RPC2::Integer') {
  136. push @text, "<value><int>", $item->repr, "</int></value>\n";
  137. } elsif ($ref eq 'Frontier::RPC2::Double') {
  138. push @text, "<value><double>", $item->repr, "</double></value>\n";
  139. } elsif ($ref eq 'Frontier::RPC2::DateTime::ISO8601') {
  140. push @text, "<value><dateTime.iso8601>", $item->repr, "</dateTime.iso8601></value>\n";
  141. } elsif ($ref eq 'Frontier::RPC2::Base64') {
  142. push @text, "<value><base64>", $item->repr, "</base64></value>\n";
  143. } elsif ($ref =~ /=HASH\(/) {
  144. push @text, $self->_hash($item);
  145. } elsif ($ref =~ /=ARRAY\(/) {
  146. push @text, $self->_array($item);
  147. } else {
  148. die "can't convert \`$item' to XML\n";
  149. }
  150. return @text;
  151. }
  152. sub _hash {
  153. my $self = shift; my $hash = shift;
  154. my @text = "<value><struct>\n";
  155. my ($key, $value);
  156. while (($key, $value) = each %$hash) {
  157. push (@text,
  158. "<member><name>$key</name>",
  159. $self->_item($value),
  160. "</member>\n");
  161. }
  162. push @text, "</struct></value>\n";
  163. return @text;
  164. }
  165. sub _array {
  166. my $self = shift; my $array = shift;
  167. my @text = "<value><array><data>\n";
  168. my $item;
  169. foreach $item (@$array) {
  170. push @text, $self->_item($item);
  171. }
  172. push @text, "</data></array></value>\n";
  173. return @text;
  174. }
  175. sub _scalar {
  176. my $self = shift; my $value = shift;
  177. # these are from `perldata(1)'
  178. if ($value =~ /^[+-]?\d+$/) {
  179. return ("<value><i4>$value</i4></value>");
  180. } elsif ($value =~ /^(-?(?:\d+(?:\.\d*)?|\.\d+)|([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?)$/) {
  181. return ("<value><double>$value</double></value>");
  182. } else {
  183. $value =~ s/([&<>\"])/$char_entities{$1}/ge;
  184. return ("<value><string>$value</string></value>");
  185. }
  186. }
  187. sub decode {
  188. my $self = shift; my $string = shift;
  189. $self->{'parser'} = XML::Parser->new( Style => ref($self),
  190. 'use_objects' => $self->{'use_objects'} );
  191. return $self->{'parser'}->parsestring($string);
  192. }
  193. # shortcuts
  194. sub base64 {
  195. my $self = shift;
  196. return Frontier::RPC2::Base64->new(@_);
  197. }
  198. sub boolean {
  199. my $self = shift;
  200. my $elem = shift;
  201. if($elem == 0 or $elem == 1) {
  202. return Frontier::RPC2::Boolean->new($elem);
  203. } else {
  204. die "error in rendering RPC type \`$elem\' not a boolean\n";
  205. }
  206. }
  207. sub double {
  208. my $self = shift;
  209. my $elem = shift;
  210. # this is from `perldata(1)'
  211. if($elem =~ /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/) {
  212. return Frontier::RPC2::Double->new($elem);
  213. } else {
  214. die "error in rendering RPC type \`$elem\' not a double\n";
  215. }
  216. }
  217. sub int {
  218. my $self = shift;
  219. my $elem = shift;
  220. # this is from `perldata(1)'
  221. if($elem =~ /^[+-]?\d+$/) {
  222. return Frontier::RPC2::Integer->new($elem);
  223. } else {
  224. die "error in rendering RPC type \`$elem\' not an int\n";
  225. }
  226. }
  227. sub string {
  228. my $self = shift;
  229. return Frontier::RPC2::String->new(@_);
  230. }
  231. sub date_time {
  232. my $self = shift;
  233. return Frontier::RPC2::DateTime::ISO8601->new(@_);
  234. }
  235. ######################################################################
  236. ###
  237. ### XML::Parser callbacks
  238. ###
  239. sub die {
  240. my $expat = shift; my $message = shift;
  241. die $message
  242. . "at line " . $expat->current_line
  243. . " column " . $expat->current_column . "\n";
  244. }
  245. sub init {
  246. my $expat = shift;
  247. $expat->{'rpc_state'} = [];
  248. $expat->{'rpc_container'} = [ [] ];
  249. $expat->{'rpc_member_name'} = [];
  250. $expat->{'rpc_type'} = undef;
  251. $expat->{'rpc_args'} = undef;
  252. }
  253. # FIXME this state machine wouldn't be necessary if we had a DTD.
  254. sub start {
  255. my $expat = shift; my $tag = shift;
  256. my $state = $expat->{'rpc_state'}[-1];
  257. if (!defined $state) {
  258. if ($tag eq 'methodCall') {
  259. $expat->{'rpc_type'} = 'call';
  260. push @{ $expat->{'rpc_state'} }, 'want_method_name';
  261. } elsif ($tag eq 'methodResponse') {
  262. push @{ $expat->{'rpc_state'} }, 'method_response';
  263. } else {
  264. Frontier::RPC2::die($expat, "unknown RPC type \`$tag'\n");
  265. }
  266. } elsif ($state eq 'want_method_name') {
  267. Frontier::RPC2::die($expat, "wanted \`methodName' tag, got \`$tag'\n")
  268. if ($tag ne 'methodName');
  269. push @{ $expat->{'rpc_state'} }, 'method_name';
  270. $expat->{'rpc_text'} = "";
  271. } elsif ($state eq 'method_response') {
  272. if ($tag eq 'params') {
  273. $expat->{'rpc_type'} = 'response';
  274. push @{ $expat->{'rpc_state'} }, 'params';
  275. } elsif ($tag eq 'fault') {
  276. $expat->{'rpc_type'} = 'fault';
  277. push @{ $expat->{'rpc_state'} }, 'want_value';
  278. }
  279. } elsif ($state eq 'want_params') {
  280. Frontier::RPC2::die($expat, "wanted \`params' tag, got \`$tag'\n")
  281. if ($tag ne 'params');
  282. push @{ $expat->{'rpc_state'} }, 'params';
  283. } elsif ($state eq 'params') {
  284. Frontier::RPC2::die($expat, "wanted \`param' tag, got \`$tag'\n")
  285. if ($tag ne 'param');
  286. push @{ $expat->{'rpc_state'} }, 'want_param_name_or_value';
  287. } elsif ($state eq 'want_param_name_or_value') {
  288. if ($tag eq 'value') {
  289. $expat->{'may_get_cdata'} = 1;
  290. $expat->{'rpc_text'} = "";
  291. push @{ $expat->{'rpc_state'} }, 'value';
  292. } elsif ($tag eq 'name') {
  293. push @{ $expat->{'rpc_state'} }, 'param_name';
  294. } else {
  295. Frontier::RPC2::die($expat, "wanted \`value' or \`name' tag, got \`$tag'\n");
  296. }
  297. } elsif ($state eq 'param_name') {
  298. Frontier::RPC2::die($expat, "wanted parameter name data, got tag \`$tag'\n");
  299. } elsif ($state eq 'want_value') {
  300. Frontier::RPC2::die($expat, "wanted \`value' tag, got \`$tag'\n")
  301. if ($tag ne 'value');
  302. $expat->{'rpc_text'} = "";
  303. $expat->{'may_get_cdata'} = 1;
  304. push @{ $expat->{'rpc_state'} }, 'value';
  305. } elsif ($state eq 'value') {
  306. $expat->{'may_get_cdata'} = 0;
  307. if ($tag eq 'array') {
  308. push @{ $expat->{'rpc_container'} }, [];
  309. push @{ $expat->{'rpc_state'} }, 'want_data';
  310. } elsif ($tag eq 'struct') {
  311. push @{ $expat->{'rpc_container'} }, {};
  312. push @{ $expat->{'rpc_member_name'} }, undef;
  313. push @{ $expat->{'rpc_state'} }, 'struct';
  314. } elsif ($scalars{$tag}) {
  315. $expat->{'rpc_text'} = "";
  316. push @{ $expat->{'rpc_state'} }, 'cdata';
  317. } else {
  318. Frontier::RPC2::die($expat, "wanted a data type, got \`$tag'\n");
  319. }
  320. } elsif ($state eq 'want_data') {
  321. Frontier::RPC2::die($expat, "wanted \`data', got \`$tag'\n")
  322. if ($tag ne 'data');
  323. push @{ $expat->{'rpc_state'} }, 'array';
  324. } elsif ($state eq 'array') {
  325. Frontier::RPC2::die($expat, "wanted \`value' tag, got \`$tag'\n")
  326. if ($tag ne 'value');
  327. $expat->{'rpc_text'} = "";
  328. $expat->{'may_get_cdata'} = 1;
  329. push @{ $expat->{'rpc_state'} }, 'value';
  330. } elsif ($state eq 'struct') {
  331. Frontier::RPC2::die($expat, "wanted \`member' tag, got \`$tag'\n")
  332. if ($tag ne 'member');
  333. push @{ $expat->{'rpc_state'} }, 'want_member_name';
  334. } elsif ($state eq 'want_member_name') {
  335. Frontier::RPC2::die($expat, "wanted \`name' tag, got \`$tag'\n")
  336. if ($tag ne 'name');
  337. push @{ $expat->{'rpc_state'} }, 'member_name';
  338. $expat->{'rpc_text'} = "";
  339. } elsif ($state eq 'member_name') {
  340. Frontier::RPC2::die($expat, "wanted data, got tag \`$tag'\n");
  341. } elsif ($state eq 'cdata') {
  342. Frontier::RPC2::die($expat, "wanted data, got tag \`$tag'\n");
  343. } else {
  344. Frontier::RPC2::die($expat, "internal error, unknown state \`$state'\n");
  345. }
  346. }
  347. sub end {
  348. my $expat = shift; my $tag = shift;
  349. my $state = pop @{ $expat->{'rpc_state'} };
  350. if ($state eq 'cdata') {
  351. my $value = $expat->{'rpc_text'};
  352. if ($tag eq 'base64') {
  353. $value = Frontier::RPC2::Base64->new($value);
  354. } elsif ($tag eq 'boolean') {
  355. $value = Frontier::RPC2::Boolean->new($value);
  356. } elsif ($tag eq 'dateTime.iso8601') {
  357. $value = Frontier::RPC2::DateTime::ISO8601->new($value);
  358. } elsif ($expat->{'use_objects'}) {
  359. if ($tag eq 'i4' or $tag eq 'int') {
  360. $value = Frontier::RPC2::Integer->new($value);
  361. } elsif ($tag eq 'float') {
  362. $value = Frontier::RPC2::Float->new($value);
  363. } elsif ($tag eq 'string') {
  364. $value = Frontier::RPC2::String->new($value);
  365. }
  366. }
  367. $expat->{'rpc_value'} = $value;
  368. } elsif ($state eq 'member_name') {
  369. $expat->{'rpc_member_name'}[-1] = $expat->{'rpc_text'};
  370. $expat->{'rpc_state'}[-1] = 'want_value';
  371. } elsif ($state eq 'method_name') {
  372. $expat->{'rpc_method_name'} = $expat->{'rpc_text'};
  373. $expat->{'rpc_state'}[-1] = 'want_params';
  374. } elsif ($state eq 'struct') {
  375. $expat->{'rpc_value'} = pop @{ $expat->{'rpc_container'} };
  376. pop @{ $expat->{'rpc_member_name'} };
  377. } elsif ($state eq 'array') {
  378. $expat->{'rpc_value'} = pop @{ $expat->{'rpc_container'} };
  379. } elsif ($state eq 'value') {
  380. # the rpc_text is a string if no type tags were given
  381. if ($expat->{'may_get_cdata'}) {
  382. $expat->{'may_get_cdata'} = 0;
  383. if ($expat->{'use_objects'}) {
  384. $expat->{'rpc_value'}
  385. = Frontier::RPC2::String->new($expat->{'rpc_text'});
  386. } else {
  387. $expat->{'rpc_value'} = $expat->{'rpc_text'};
  388. }
  389. }
  390. my $container = $expat->{'rpc_container'}[-1];
  391. if (ref($container) eq 'ARRAY') {
  392. push @$container, $expat->{'rpc_value'};
  393. } elsif (ref($container) eq 'HASH') {
  394. $container->{ $expat->{'rpc_member_name'}[-1] } = $expat->{'rpc_value'};
  395. }
  396. }
  397. }
  398. sub char {
  399. my $expat = shift; my $text = shift;
  400. $expat->{'rpc_text'} .= $text;
  401. }
  402. sub proc {
  403. }
  404. sub final {
  405. my $expat = shift;
  406. $expat->{'rpc_value'} = pop @{ $expat->{'rpc_container'} };
  407. return {
  408. value => $expat->{'rpc_value'},
  409. type => $expat->{'rpc_type'},
  410. method_name => $expat->{'rpc_method_name'},
  411. };
  412. }
  413. package Frontier::RPC2::DataType;
  414. sub new {
  415. my $type = shift; my $value = shift;
  416. return bless \$value, $type;
  417. }
  418. # `repr' returns the XML representation of this data, which may be
  419. # different [in the future] from what is returned from `value'
  420. sub repr {
  421. my $self = shift;
  422. return $$self;
  423. }
  424. # sets or returns the usable value of this data
  425. sub value {
  426. my $self = shift;
  427. @_ ? ($$self = shift) : $$self;
  428. }
  429. package Frontier::RPC2::Base64;
  430. use vars qw{@ISA};
  431. @ISA = qw{Frontier::RPC2::DataType};
  432. package Frontier::RPC2::Boolean;
  433. use vars qw{@ISA};
  434. @ISA = qw{Frontier::RPC2::DataType};
  435. package Frontier::RPC2::Integer;
  436. use vars qw{@ISA};
  437. @ISA = qw{Frontier::RPC2::DataType};
  438. package Frontier::RPC2::String;
  439. use vars qw{@ISA};
  440. @ISA = qw{Frontier::RPC2::DataType};
  441. sub repr {
  442. my $self = shift;
  443. my $value = $$self;
  444. $value =~ s/([&<>\"])/$Frontier::RPC2::char_entities{$1}/ge;
  445. $value;
  446. }
  447. package Frontier::RPC2::Double;
  448. use vars qw{@ISA};
  449. @ISA = qw{Frontier::RPC2::DataType};
  450. package Frontier::RPC2::DateTime::ISO8601;
  451. use vars qw{@ISA};
  452. @ISA = qw{Frontier::RPC2::DataType};
  453. =head1 NAME
  454. Frontier::RPC2 - encode/decode RPC2 format XML
  455. =head1 SYNOPSIS
  456. use Frontier::RPC2;
  457. $coder = Frontier::RPC2->new;
  458. $xml_string = $coder->encode_call($method, @args);
  459. $xml_string = $coder->encode_response($result);
  460. $xml_string = $coder->encode_fault($code, $message);
  461. $call = $coder->decode($xml_string);
  462. $response_xml = $coder->serve($request_xml, $methods);
  463. $boolean_object = $coder->boolean($boolean);
  464. $date_time_object = $coder->date_time($date_time);
  465. $base64_object = $coder->base64($base64);
  466. $int_object = $coder->int(42);
  467. $float_object = $coder->float(3.14159);
  468. $string_object = $coder->string("Foo");
  469. =head1 DESCRIPTION
  470. I<Frontier::RPC2> encodes and decodes XML RPC calls.
  471. =over 4
  472. =item $coder = Frontier::RPC2->new( I<OPTIONS> )
  473. Create a new encoder/decoder. The following option is supported:
  474. =over 4
  475. =item encoding
  476. The XML encoding to be specified in the XML declaration of encoded RPC
  477. requests or responses. Decoded results may have a different encoding
  478. specified; XML::Parser will convert decoded data to UTF-8. The
  479. default encoding is none, which uses XML 1.0's default of UTF-8. For
  480. example:
  481. $server = Frontier::RPC2->new( 'encoding' => 'ISO-8859-1' );
  482. =item use_objects
  483. If set to a non-zero value will convert incoming E<lt>i4E<gt>,
  484. E<lt>floatE<gt>, and E<lt>stringE<gt> values to objects instead of
  485. scalars. See int(), float(), and string() below for more details.
  486. =back
  487. =item $xml_string = $coder->encode_call($method, @args)
  488. `C<encode_call>' converts a method name and it's arguments into an
  489. RPC2 `C<methodCall>' element, returning the XML fragment.
  490. =item $xml_string = $coder->encode_response($result)
  491. `C<encode_response>' converts the return value of a procedure into an
  492. RPC2 `C<methodResponse>' element containing the result, returning the
  493. XML fragment.
  494. =item $xml_string = $coder->encode_fault($code, $message)
  495. `C<encode_fault>' converts a fault code and message into an RPC2
  496. `C<methodResponse>' element containing a `C<fault>' element, returning
  497. the XML fragment.
  498. =item $call = $coder->decode($xml_string)
  499. `C<decode>' converts an XML string containing an RPC2 `C<methodCall>'
  500. or `C<methodResponse>' element into a hash containing three members,
  501. `C<type>', `C<value>', and `C<method_name>'. `C<type>' is one of
  502. `C<call>', `C<response>', or `C<fault>'. `C<value>' is array
  503. containing the parameters or result of the RPC. For a `C<call>' type,
  504. `C<value>' contains call's parameters and `C<method_name>' contains
  505. the method being called. For a `C<response>' type, the `C<value>'
  506. array contains call's result. For a `C<fault>' type, the `C<value>'
  507. array contains a hash with the two members `C<faultCode>' and
  508. `C<faultMessage>'.
  509. =item $response_xml = $coder->serve($request_xml, $methods)
  510. `C<serve>' decodes `C<$request_xml>', looks up the called method name
  511. in the `C<$methods>' hash and calls it, and then encodes and returns
  512. the response as XML.
  513. =item $boolean_object = $coder->boolean($boolean);
  514. =item $date_time_object = $coder->date_time($date_time);
  515. =item $base64_object = $coder->base64($base64);
  516. These methods create and return XML-RPC-specific datatypes that can be
  517. passed to the encoder. The decoder may also return these datatypes.
  518. The corresponding package names (for use with `C<ref()>', for example)
  519. are `C<Frontier::RPC2::Boolean>',
  520. `C<Frontier::RPC2::DateTime::ISO8601>', and
  521. `C<Frontier::RPC2::Base64>'.
  522. You can change and retrieve the value of boolean, date/time, and
  523. base64 data using the `C<value>' method of those objects, i.e.:
  524. $boolean = $boolean_object->value;
  525. $boolean_object->value(1);
  526. Note: `C<base64()>' does I<not> encode or decode base64 data for you,
  527. you must use MIME::Base64 or similar module for that.
  528. =item $int_object = $coder->int(42);
  529. =item $float_object = $coder->float(3.14159);
  530. =item $string_object = $coder->string("Foo");
  531. By default, you may pass ordinary Perl values (scalars) to be encoded.
  532. RPC2 automatically converts them to XML-RPC types if they look like an
  533. integer, float, or as a string. This assumption causes problems when
  534. you want to pass a string that looks like "0096", RPC2 will convert
  535. that to an E<lt>i4E<gt> because it looks like an integer. With these
  536. methods, you could now create a string object like this:
  537. $part_num = $coder->string("0096");
  538. and be confident that it will be passed as an XML-RPC string. You can
  539. change and retrieve values from objects using value() as described
  540. above.
  541. =back
  542. =head1 SEE ALSO
  543. perl(1), Frontier::Daemon(3), Frontier::Client(3)
  544. <http://www.scripting.com/frontier5/xml/code/rpc.html>
  545. =head1 AUTHOR
  546. Ken MacLeod <[email protected]>
  547. =cut
  548. 1;