File Coverage

blib/lib/XMLRPC/Lite.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             # ======================================================================
2             #
3             # Copyright (C) 2000-2001 Paul Kulchenko (paulclinger@yahoo.com)
4             # SOAP::Lite is free software; you can redistribute it
5             # and/or modify it under the same terms as Perl itself.
6             #
7             # $Id$
8             #
9             # ======================================================================
10              
11             package XMLRPC::Lite;
12              
13 3     3   33253 use SOAP::Lite;
  0            
  0            
14             use strict;
15              
16             our $VERSION = 0.717;
17              
18             # ======================================================================
19              
20             package XMLRPC::Constants;
21              
22             BEGIN {
23             no strict 'refs';
24             for (qw(
25             FAULT_CLIENT FAULT_SERVER
26             HTTP_ON_SUCCESS_CODE HTTP_ON_FAULT_CODE
27             DO_NOT_USE_XML_PARSER DO_NOT_USE_CHARSET
28             DO_NOT_USE_LWP_LENGTH_HACK DO_NOT_CHECK_CONTENT_TYPE
29             )) {
30             *$_ = \${'SOAP::Constants::' . $_}
31             }
32             # XML-RPC spec requires content-type to be "text/xml"
33             $XMLRPC::Constants::DO_NOT_USE_CHARSET = 1;
34             }
35              
36             # ======================================================================
37              
38             package XMLRPC::Data;
39              
40             @XMLRPC::Data::ISA = qw(SOAP::Data);
41              
42             # ======================================================================
43              
44             package XMLRPC::Serializer;
45              
46             @XMLRPC::Serializer::ISA = qw(SOAP::Serializer);
47              
48             sub new {
49             my $class = shift;
50              
51             return $class if ref $class;
52              
53             return $class->SUPER::new(
54             typelookup => {
55             base64 => [10, sub {$_[0] =~ /[^\x09\x0a\x0d\x20-\x7f]/}, 'as_base64'],
56             int => [20, sub {$_[0] =~ /^[+-]?\d+$/}, 'as_int'],
57             double => [30, sub {$_[0] =~ /^(-?(?:\d+(?:\.\d*)?|\.\d+)|([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?)$/}, 'as_double'],
58             dateTime => [35, sub {$_[0] =~ /^\d{8}T\d\d:\d\d:\d\d$/}, 'as_dateTime'],
59             string => [40, sub {1}, 'as_string'],
60             },
61             attr => {},
62             namespaces => {},
63             @_,
64             );
65             }
66              
67             sub envelope {
68             my $self = shift;
69             $self = $self->new() if not ref $self; # serves a method call if object
70             my $type = shift;
71              
72             my $body;
73             if ($type eq 'response') {
74             # shift off method name to make XMLRPT happy
75             my $method = shift
76             or die "Unspecified method for XMLRPC call\n";
77             $body = XMLRPC::Data->name( methodResponse => \XMLRPC::Data->value(
78             XMLRPC::Data->type(params => [@_])
79             )
80             );
81             }
82             elsif ($type eq 'method') {
83             # shift off method name to make XMLRPT happy
84             my $method = shift
85             or die "Unspecified method for XMLRPC call\n";
86             $body = XMLRPC::Data->name( methodCall => \XMLRPC::Data->value(
87             XMLRPC::Data->type(
88             methodName => UNIVERSAL::isa($method => 'XMLRPC::Data')
89             ? $method->name
90             : $method
91             ),
92             XMLRPC::Data->type(params => [@_])
93             ));
94             }
95             elsif ($type eq 'fault') {
96             $body = XMLRPC::Data->name(methodResponse =>
97             \XMLRPC::Data->type(fault => {faultCode => $_[0], faultString => $_[1]}),
98             );
99             }
100             else {
101             die "Wrong type of envelope ($type) for XMLRPC call\n";
102             }
103              
104             # SOAP::Lite keeps track of objects for XML aliasing and multiref
105             # encoding.
106             # Set/reset seen() hashref before/after encode_object avoids a
107             # memory leak
108             $self->seen({}); # initialize multiref table
109             my $envelope = $self->xmlize($self->encode_object($body));
110             $self->seen({}); # delete multi-ref table - avoids a memory hole...
111             return $envelope;
112             }
113              
114              
115             sub encode_object {
116             my $self = shift;
117             my @encoded = $self->SUPER::encode_object(@_);
118              
119             return $encoded[0]->[0] =~ /^(?:array|struct|i4|int|boolean|string|double|dateTime\.iso8601|base64)$/o
120             ? ['value', {}, [@encoded]]
121             : @encoded;
122             }
123              
124             sub encode_scalar {
125             my $self = shift;
126             return ['value', {}] unless defined $_[0];
127             return $self->SUPER::encode_scalar(@_);
128             }
129              
130             sub encode_array {
131             my ($self, $array) = @_;
132              
133             return ['array', {}, [
134             ['data', {}, [ map {$self->encode_object($_)} @{ $array } ] ]
135             ]];
136             }
137              
138             sub encode_hash {
139             my ($self, $hash) = @_;
140              
141             return ['struct', {}, [
142             map {
143             ['member', {}, [['name', {}, SOAP::Utils::encode_data($_)], $self->encode_object($hash->{$_})]]
144             } keys %{ $hash }
145             ]];
146             }
147              
148             sub as_methodName {
149             my ($self, $value, $name, $type, $attr) = @_;
150             return [ 'methodName', $attr, $value ];
151             }
152              
153             sub as_params {
154             my ($self, $params, $name, $type, $attr) = @_;
155             return ['params', $attr, [
156             map {
157             ['param', {}, [ $self->encode_object($_) ] ]
158             } @$params
159             ]];
160             }
161              
162             sub as_fault {
163             my ($self, $fault) = @_;
164             return ['fault', {}, [ $self->encode_object($fault) ] ];
165             }
166              
167             sub BEGIN {
168             no strict 'refs';
169             for my $type (qw(double i4 int)) {
170             my $method = 'as_' . $type;
171             *$method = sub {
172             my($self, $value) = @_;
173             return [ $type, {}, $value ];
174             }
175             }
176             }
177              
178             sub as_base64 {
179             my ($self, $value) = @_;
180             require MIME::Base64;
181             return ['base64', {}, MIME::Base64::encode_base64($value,'')];
182             }
183              
184             sub as_string {
185             my ($self, $value) = @_;
186             return ['string', {}, SOAP::Utils::encode_data($value)];
187             }
188              
189             sub as_dateTime {
190             my ($self, $value) = @_;
191             return ['dateTime.iso8601', {}, $value];
192             }
193              
194             sub as_boolean {
195             my ($self, $value) = @_;
196             return ['boolean', {}, $value ? 1 : 0];
197             }
198              
199             sub typecast {
200             my ($self, $value, $name, $type, $attr) = @_;
201              
202             die "Wrong/unsupported datatype '$type' specified\n" if defined $type;
203              
204             $self->SUPER::typecast(@_);
205             }
206              
207             # ======================================================================
208              
209             package XMLRPC::SOM;
210              
211             @XMLRPC::SOM::ISA = qw(SOAP::SOM);
212              
213             sub BEGIN {
214             no strict 'refs';
215             my %path = (
216             root => '/',
217             envelope => '/[1]',
218             method => '/methodCall/methodName',
219             fault => '/methodResponse/fault',
220             );
221              
222             for my $method (keys %path) {
223             *$method = sub {
224             my $self = shift;
225             ref $self or return $path{$method};
226             Carp::croak "Method '$method' is readonly and doesn't accept any parameters" if @_;
227             $self->valueof($path{$method});
228             };
229             }
230              
231             my %fault = (
232             faultcode => 'faultCode',
233             faultstring => 'faultString',
234             );
235              
236             for my $method (keys %fault) {
237             *$method = sub {
238             my $self = shift;
239             ref $self or Carp::croak "Method '$method' doesn't have shortcut";
240             Carp::croak "Method '$method' is readonly and doesn't accept any parameters" if @_;
241             defined $self->fault ? $self->fault->{$fault{$method}} : undef;
242             };
243             }
244              
245             my %results = (
246             result => '/methodResponse/params/[1]',
247             paramsin => '/methodCall/params/param',
248             paramsall => '/methodResponse/params/param',
249             );
250              
251             for my $method (keys %results) {
252             *$method = sub {
253             my $self = shift;
254             ref $self or return $results{$method};
255             Carp::croak "Method '$method' is readonly and doesn't accept any parameters" if @_;
256             defined $self->fault()
257             ? undef
258             : $self->valueof($results{$method});
259             };
260             }
261             }
262              
263             # ======================================================================
264              
265             package XMLRPC::Deserializer;
266              
267             @XMLRPC::Deserializer::ISA = qw(SOAP::Deserializer);
268              
269             BEGIN {
270             no strict 'refs';
271             for my $method (qw(o_child o_qname o_chars)) { # import from SOAP::Utils
272             *$method = \&{'SOAP::Utils::'.$method};
273             }
274             }
275              
276             sub deserialize {
277             # just deserialize with SOAP::Lite's deserializer, and re-bless as
278             # XMLRPC::SOM
279             bless shift->SUPER::deserialize(@_) => 'XMLRPC::SOM';
280             }
281              
282             sub decode_value {
283             my $self = shift;
284             my $ref = shift;
285             my($name, $attrs, $children, $value) = @$ref;
286              
287             if ($name eq 'value') {
288             $children ? scalar(($self->decode_object($children->[0]))[1]) : $value;
289             }
290             elsif ($name eq 'array') {
291             return [map {scalar(($self->decode_object($_))[1])} @{o_child($children->[0]) || []}];
292             }
293             elsif ($name eq 'struct') {
294             return {
295             map {
296             my %hash = map { o_qname($_) => $_ } @{o_child($_) || []};
297             # v----- scalar is required here, because 5.005 evaluates 'undef' in list context as empty array
298             (o_chars($hash{name}) => scalar(($self->decode_object($hash{value}))[1]));
299             } @{$children || []}};
300             }
301             elsif ($name eq 'base64') {
302             require MIME::Base64;
303             MIME::Base64::decode_base64($value);
304             }
305             elsif ($name =~ /^(?:int|i4|boolean|string|double|dateTime\.iso8601|methodName)$/) {
306             return $value;
307             }
308             elsif ($name =~ /^(?:params)$/) {
309             return [map {scalar(($self->decode_object($_))[1])} @{$children || []}];
310             }
311             elsif ($name =~ /^(?:methodResponse|methodCall)$/) {
312             return +{map {$self->decode_object($_)} @{$children || []}};
313             }
314             elsif ($name =~ /^(?:param|fault)$/) {
315             return scalar(($self->decode_object($children->[0]))[1]);
316             }
317             elsif ($name =~ /^(?:nil)$/) {
318             return undef;
319             }
320             else {
321             die "wrong element '$name'\n";
322             }
323             }
324              
325             # ======================================================================
326              
327             package XMLRPC::Server;
328              
329             @XMLRPC::Server::ISA = qw(SOAP::Server);
330              
331             sub initialize {
332             return (
333             deserializer => XMLRPC::Deserializer->new,
334             serializer => XMLRPC::Serializer->new,
335             on_action => sub {},
336             on_dispatch => sub { return map {s!\.!/!g; $_} shift->method =~ /^(?:(.*)\.)?(\w+)$/ },
337             );
338             }
339              
340             # ======================================================================
341              
342             package XMLRPC::Server::Parameters;
343              
344             @XMLRPC::Server::Parameters::ISA = qw(SOAP::Server::Parameters);
345              
346             # ======================================================================
347              
348             package XMLRPC;
349              
350             @XMLRPC::ISA = qw(SOAP);
351              
352             # ======================================================================
353              
354             package XMLRPC::Lite;
355              
356             @XMLRPC::Lite::ISA = qw(SOAP::Lite);
357              
358             sub new {
359             my $class = shift;
360              
361             return $class if ref $class;
362              
363             return $class->SUPER::new(
364             serializer => XMLRPC::Serializer->new,
365             deserializer => XMLRPC::Deserializer->new,
366             on_action => sub {return},
367             default_ns => 'http://unspecified/',
368             @_
369             );
370             }
371              
372             # ======================================================================
373              
374             1;
375              
376             __END__