line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package XML::RPC::Enc::LibXML; |
2
|
|
|
|
|
|
|
|
3
|
2
|
|
|
2
|
|
7714
|
use strict; |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
70
|
|
4
|
2
|
|
|
2
|
|
10
|
use warnings; |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
56
|
|
5
|
2
|
|
|
2
|
|
9
|
use base 'XML::RPC::Enc'; |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
843
|
|
6
|
2
|
|
|
2
|
|
2323
|
use XML::LibXML; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
use XML::Hash::LX; |
8
|
|
|
|
|
|
|
use Carp; |
9
|
|
|
|
|
|
|
#use Encode (); |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
use XML::RPC::Fast (); |
12
|
|
|
|
|
|
|
our $VERSION = $XML::RPC::Fast::VERSION; |
13
|
|
|
|
|
|
|
BEGIN { |
14
|
|
|
|
|
|
|
if (eval { my $x = pack 'q', -1; 1 }) { |
15
|
|
|
|
|
|
|
*_HAVE_BIGINT = sub () { 1 }; |
16
|
|
|
|
|
|
|
my $maxint = eval q{ 0+"9223372036854775807" }; |
17
|
|
|
|
|
|
|
*_MAX_BIGINT = sub () { $maxint }; |
18
|
|
|
|
|
|
|
} else { |
19
|
|
|
|
|
|
|
require Math::BigInt; |
20
|
|
|
|
|
|
|
*_HAVE_BIGINT = sub () { 0 }; |
21
|
|
|
|
|
|
|
my $maxint = Math::BigInt->new("0x7fffffffffffffff"); |
22
|
|
|
|
|
|
|
*_MAX_BIGINT = sub () { $maxint }; |
23
|
|
|
|
|
|
|
} |
24
|
|
|
|
|
|
|
} |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
=head1 NAME |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
XML::RPC::Enc::LibXML - Encode/decode XML-RPC using LibXML |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
=head1 SYNOPSIS |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
use XML::RPC::Fast; |
34
|
|
|
|
|
|
|
use XML::RPC::Enc::LibXML; |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
my $rpc = XML::RPC::Fast->new( |
37
|
|
|
|
|
|
|
$uri, |
38
|
|
|
|
|
|
|
encoder => XML::RPC::Enc::LibXML->new( |
39
|
|
|
|
|
|
|
# internal_encoding currently not implemented, always want wide chars |
40
|
|
|
|
|
|
|
internal_encoding => undef, |
41
|
|
|
|
|
|
|
external_encoding => 'windows-1251', |
42
|
|
|
|
|
|
|
) |
43
|
|
|
|
|
|
|
); |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
$rpc->registerType( base64 => sub { |
46
|
|
|
|
|
|
|
my $node = shift; |
47
|
|
|
|
|
|
|
return MIME::Base64::decode($node->textContent); |
48
|
|
|
|
|
|
|
}); |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
$rpc->registerType( 'dateTime.iso8601' => sub { |
51
|
|
|
|
|
|
|
my $node = shift; |
52
|
|
|
|
|
|
|
return DateTime::Format::ISO8601->parse_datetime($node->textContent); |
53
|
|
|
|
|
|
|
}); |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
$rpc->registerClass( DateTime => sub { |
56
|
|
|
|
|
|
|
return ( 'dateTime.iso8601' => $_[0]->strftime('%Y%m%dT%H%M%S.%3N%z') ); |
57
|
|
|
|
|
|
|
}); |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
$rpc->registerClass( DateTime => sub { |
60
|
|
|
|
|
|
|
my $node = XML::LibXML::Element->new('dateTime.iso8601'); |
61
|
|
|
|
|
|
|
$node->appendText($_[0]->strftime('%Y%m%dT%H%M%S.%3N%z')); |
62
|
|
|
|
|
|
|
return $node; |
63
|
|
|
|
|
|
|
}); |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
=head1 DESCRIPTION |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
Default encoder/decoder for L |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
If MIME::Base64 is installed, decoder for C type C will be setup |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
If DateTime::Format::ISO8601 is installed, decoder for C type C will be setup |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
Also will be setup by default encoders for L and L (will be encoded as C) |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
Ty avoid default decoders setup: |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
BEGIN { |
78
|
|
|
|
|
|
|
$XML::RPC::Enc::LibXML::TYPES{base64} = 0; |
79
|
|
|
|
|
|
|
$XML::RPC::Enc::LibXML::TYPES{'dateTime.iso8601'} = 0; |
80
|
|
|
|
|
|
|
} |
81
|
|
|
|
|
|
|
use XML::RPC::Enc::LibXML; |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
=head1 IMPLEMENTED METHODS |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
=head2 new |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
=head2 request |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
=head2 response |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
=head2 fault |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
=head2 decode |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
=head2 registerType |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
=head2 registerClass |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
=head1 SEE ALSO |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
=over 4 |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
=item * L |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
Base class (also contains documentation) |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
=back |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
=cut |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
# xml => perl |
112
|
|
|
|
|
|
|
# args: xml-nodes (children of <$type> ... $type>) |
113
|
|
|
|
|
|
|
# retv: any scalar |
114
|
|
|
|
|
|
|
our %TYPES; |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
# perl => xml |
117
|
|
|
|
|
|
|
# args: object |
118
|
|
|
|
|
|
|
# retv: ( type => string ) || xml-node |
119
|
|
|
|
|
|
|
our %CLASS; |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
our $E; |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
BEGIN { |
124
|
|
|
|
|
|
|
if ( !exists $TYPES{base64} and eval{ require MIME::Base64;1 } ) { |
125
|
|
|
|
|
|
|
$TYPES{base64} = sub { |
126
|
|
|
|
|
|
|
#defined $E ? $E->encode( |
127
|
|
|
|
|
|
|
MIME::Base64::decode(shift->textContent); |
128
|
|
|
|
|
|
|
}; |
129
|
|
|
|
|
|
|
} |
130
|
|
|
|
|
|
|
# DateTime is the most "standart" datetime object in perl, try to use it |
131
|
|
|
|
|
|
|
if ( !exists $TYPES{'dateTime.iso8601'} and eval{ require DateTime::Format::ISO8601;1 } ) { |
132
|
|
|
|
|
|
|
$TYPES{'dateTime.iso8601'} = sub { |
133
|
|
|
|
|
|
|
DateTime::Format::ISO8601->parse_datetime(shift->textContent) |
134
|
|
|
|
|
|
|
}; |
135
|
|
|
|
|
|
|
} |
136
|
|
|
|
|
|
|
} |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
#%TYPES = ( |
139
|
|
|
|
|
|
|
# custom => sub { ... }, |
140
|
|
|
|
|
|
|
# %TYPES, |
141
|
|
|
|
|
|
|
#); |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
# We need no modules to predefine encoders for dates |
144
|
|
|
|
|
|
|
%CLASS = ( |
145
|
|
|
|
|
|
|
DateTime => sub { |
146
|
|
|
|
|
|
|
'dateTime.iso8601',$_[0]->strftime('%Y%m%dT%H%M%S.%3N%z'); |
147
|
|
|
|
|
|
|
}, |
148
|
|
|
|
|
|
|
'Class::Date' => sub { |
149
|
|
|
|
|
|
|
'dateTime.iso8601',$_[0]->strftime('%Y%m%dT%H%M%S').sprintf( '%+03d%02d', $_[0]->tzoffset / 3600, ( $_[0]->tzoffset % 3600 ) / 60 ); |
150
|
|
|
|
|
|
|
}, |
151
|
|
|
|
|
|
|
%CLASS, |
152
|
|
|
|
|
|
|
); |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
sub new { |
155
|
|
|
|
|
|
|
my $pkg = shift; |
156
|
|
|
|
|
|
|
my $self = bless { |
157
|
|
|
|
|
|
|
@_, |
158
|
|
|
|
|
|
|
parser => XML::LibXML->new(), |
159
|
|
|
|
|
|
|
types => { }, |
160
|
|
|
|
|
|
|
class => { }, |
161
|
|
|
|
|
|
|
#internal_encoding => undef, |
162
|
|
|
|
|
|
|
}, $pkg; |
163
|
|
|
|
|
|
|
$self->{external_encoding} = 'utf-8' unless defined $self->{external_encoding}; |
164
|
|
|
|
|
|
|
return $self; |
165
|
|
|
|
|
|
|
} |
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
sub registerType { |
169
|
|
|
|
|
|
|
my ( $self,$type,$decode ) = @_; |
170
|
|
|
|
|
|
|
my $old; |
171
|
|
|
|
|
|
|
if (ref $self) { |
172
|
|
|
|
|
|
|
$old = $self->{types}{$type}; |
173
|
|
|
|
|
|
|
$self->{types}{$type} = $decode; |
174
|
|
|
|
|
|
|
} else { |
175
|
|
|
|
|
|
|
$old = $TYPES{$type}; |
176
|
|
|
|
|
|
|
$TYPES{$type} = $decode; |
177
|
|
|
|
|
|
|
} |
178
|
|
|
|
|
|
|
$old; |
179
|
|
|
|
|
|
|
} |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
sub registerClass { |
182
|
|
|
|
|
|
|
my ( $self,$class,$encode ) = @_; |
183
|
|
|
|
|
|
|
my $old; |
184
|
|
|
|
|
|
|
if (ref $self) { |
185
|
|
|
|
|
|
|
$old = $self->{class}{$class}; |
186
|
|
|
|
|
|
|
$self->{class}{$class} = $encode; |
187
|
|
|
|
|
|
|
} else { |
188
|
|
|
|
|
|
|
$old = $CLASS{$class}; |
189
|
|
|
|
|
|
|
$CLASS{$class} = $encode; |
190
|
|
|
|
|
|
|
} |
191
|
|
|
|
|
|
|
$old; |
192
|
|
|
|
|
|
|
} |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
# Encoder part |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
sub _unparse_param { |
197
|
|
|
|
|
|
|
my $p = shift; |
198
|
|
|
|
|
|
|
my $r = XML::LibXML::Element->new('value'); |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
if ( ref($p) eq 'HASH' ) { |
201
|
|
|
|
|
|
|
# struct -> ( member -> { name, value } )* |
202
|
|
|
|
|
|
|
my $s = XML::LibXML::Element->new('struct'); |
203
|
|
|
|
|
|
|
$r->appendChild($s); |
204
|
|
|
|
|
|
|
for ( keys %$p ) { |
205
|
|
|
|
|
|
|
my $m = XML::LibXML::Element->new('member'); |
206
|
|
|
|
|
|
|
my $n = XML::LibXML::Element->new('name'); |
207
|
|
|
|
|
|
|
$n->appendText(defined $E ? $E->decode($_) : $_); |
208
|
|
|
|
|
|
|
$m->appendChild($n); |
209
|
|
|
|
|
|
|
$m->appendChild(_unparse_param($p->{$_})); |
210
|
|
|
|
|
|
|
$s->appendChild($m); |
211
|
|
|
|
|
|
|
} |
212
|
|
|
|
|
|
|
} |
213
|
|
|
|
|
|
|
elsif ( ref($p) eq 'ARRAY' ) { |
214
|
|
|
|
|
|
|
my $a = XML::LibXML::Element->new('array'); |
215
|
|
|
|
|
|
|
my $d = XML::LibXML::Element->new('data'); |
216
|
|
|
|
|
|
|
$a->appendChild($d); |
217
|
|
|
|
|
|
|
$r->appendChild($a); |
218
|
|
|
|
|
|
|
for (@$p) { |
219
|
|
|
|
|
|
|
$d->appendChild( _unparse_param($_) ) |
220
|
|
|
|
|
|
|
} |
221
|
|
|
|
|
|
|
} |
222
|
|
|
|
|
|
|
elsif ( ref($p) eq 'CODE' ) { |
223
|
|
|
|
|
|
|
$r->appendChild(hash2xml($p->(), doc => 1)->documentElement); |
224
|
|
|
|
|
|
|
} |
225
|
|
|
|
|
|
|
elsif (ref $p) { |
226
|
|
|
|
|
|
|
if (exists $CLASS{ ref $p }) { |
227
|
|
|
|
|
|
|
my ($t,$x) = $CLASS{ ref $p }->($p); |
228
|
|
|
|
|
|
|
if (ref $t and eval{ $t->isa('XML::LibXML::Node') }) { |
229
|
|
|
|
|
|
|
$r->appendChild($t); |
230
|
|
|
|
|
|
|
} else { |
231
|
|
|
|
|
|
|
my $v = XML::LibXML::Element->new($t); |
232
|
|
|
|
|
|
|
$v->appendText(defined $E ? $E->decode($x) : $x); |
233
|
|
|
|
|
|
|
$r->appendChild($v); |
234
|
|
|
|
|
|
|
} |
235
|
|
|
|
|
|
|
} |
236
|
|
|
|
|
|
|
elsif ( UNIVERSAL::isa($p,'SCALAR') ) { |
237
|
|
|
|
|
|
|
my $v = XML::LibXML::Element->new(ref $p); |
238
|
|
|
|
|
|
|
$v->appendText(defined $E ? $E->decode($$p) : $$p) if defined $$p; |
239
|
|
|
|
|
|
|
$r->appendChild($v); |
240
|
|
|
|
|
|
|
} |
241
|
|
|
|
|
|
|
elsif ( UNIVERSAL::isa($p,'REF') ) { |
242
|
|
|
|
|
|
|
my $v = XML::LibXML::Element->new(ref $p); |
243
|
|
|
|
|
|
|
$v->appendChild(hash2xml($$p, doc => 1)->documentElement); |
244
|
|
|
|
|
|
|
$r->appendChild($v); |
245
|
|
|
|
|
|
|
} |
246
|
|
|
|
|
|
|
else { |
247
|
|
|
|
|
|
|
warn "Bad reference: $p"; |
248
|
|
|
|
|
|
|
#$result = undef; |
249
|
|
|
|
|
|
|
} |
250
|
|
|
|
|
|
|
} |
251
|
|
|
|
|
|
|
else { |
252
|
|
|
|
|
|
|
#no warnings; |
253
|
|
|
|
|
|
|
if (!defined $p) { |
254
|
|
|
|
|
|
|
my $v = XML::LibXML::Element->new('string'); |
255
|
|
|
|
|
|
|
$r->appendChild($v); |
256
|
|
|
|
|
|
|
} |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
=for rem |
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
Q: What is the legal syntax (and range) for integers? |
261
|
|
|
|
|
|
|
How to deal with leading zeros? |
262
|
|
|
|
|
|
|
Is a leading plus sign allowed? |
263
|
|
|
|
|
|
|
How to deal with whitespace? |
264
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
A: An integer is a 32-bit signed number. |
266
|
|
|
|
|
|
|
You can include a plus or minus at the beginning of a string of numeric characters. |
267
|
|
|
|
|
|
|
Leading zeros are collapsed. |
268
|
|
|
|
|
|
|
Whitespace is not permitted. |
269
|
|
|
|
|
|
|
Just numeric characters preceeded by a plus or minus. |
270
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
Q: What is the legal syntax (and range) for floating point values (doubles)? |
272
|
|
|
|
|
|
|
How is the exponent represented? |
273
|
|
|
|
|
|
|
How to deal with whitespace? |
274
|
|
|
|
|
|
|
Can infinity and "not a number" be represented? |
275
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
A: There is no representation for infinity or negative infinity or "not a number". |
277
|
|
|
|
|
|
|
At this time, only decimal point notation is allowed, a plus or a minus, |
278
|
|
|
|
|
|
|
followed by any number of numeric characters, |
279
|
|
|
|
|
|
|
followed by a period and any number of numeric characters. |
280
|
|
|
|
|
|
|
Whitespace is not allowed. |
281
|
|
|
|
|
|
|
The range of allowable values is implementation-dependent, is not specified. |
282
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
# int |
284
|
|
|
|
|
|
|
'+0' => 0 |
285
|
|
|
|
|
|
|
'-0' => 0 |
286
|
|
|
|
|
|
|
'+1234567' => 1234567 |
287
|
|
|
|
|
|
|
'0777' => 777 |
288
|
|
|
|
|
|
|
'0000000000000' => 0 |
289
|
|
|
|
|
|
|
'0000000000000000000000000000000000000000000000000' => 0 |
290
|
|
|
|
|
|
|
# not int |
291
|
|
|
|
|
|
|
'999999999999999999999999999999999999'; |
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
=cut |
294
|
|
|
|
|
|
|
elsif ($p =~ m/^([\-+]?)\d+(\.\d+|)$/) { |
295
|
|
|
|
|
|
|
my ($have_sign,$is_double) = ($1,$2); |
296
|
|
|
|
|
|
|
if ( $is_double ) { |
297
|
|
|
|
|
|
|
my $v = XML::LibXML::Element->new('double'); |
298
|
|
|
|
|
|
|
$v->appendText( $p ); |
299
|
|
|
|
|
|
|
$r->appendChild($v); |
300
|
|
|
|
|
|
|
} |
301
|
|
|
|
|
|
|
else { |
302
|
|
|
|
|
|
|
my $v; |
303
|
|
|
|
|
|
|
# TODO: should we pass sign "+"? |
304
|
|
|
|
|
|
|
if( $p == unpack "l", pack "l", $p ) { |
305
|
|
|
|
|
|
|
# i4 |
306
|
|
|
|
|
|
|
$v = XML::LibXML::Element->new('i4'); |
307
|
|
|
|
|
|
|
$v->appendText(int $p); |
308
|
|
|
|
|
|
|
} |
309
|
|
|
|
|
|
|
elsif ( _HAVE_BIGINT and $p == unpack "q", pack "q", $p ) { |
310
|
|
|
|
|
|
|
# i8 |
311
|
|
|
|
|
|
|
$v = XML::LibXML::Element->new('i8'); |
312
|
|
|
|
|
|
|
$v->appendText(int $p); |
313
|
|
|
|
|
|
|
} |
314
|
|
|
|
|
|
|
elsif ( !_HAVE_BIGINT and abs( my $bi = Math::BigInt->new($p) ) < _MAX_BIGINT ) { |
315
|
|
|
|
|
|
|
$v = XML::LibXML::Element->new('i8'); |
316
|
|
|
|
|
|
|
$v->appendText($bi->bstr); |
317
|
|
|
|
|
|
|
} |
318
|
|
|
|
|
|
|
else { |
319
|
|
|
|
|
|
|
# string |
320
|
|
|
|
|
|
|
$v = XML::LibXML::Element->new('string'); |
321
|
|
|
|
|
|
|
$v->appendText($p); |
322
|
|
|
|
|
|
|
} |
323
|
|
|
|
|
|
|
$r->appendChild($v); |
324
|
|
|
|
|
|
|
} |
325
|
|
|
|
|
|
|
} |
326
|
|
|
|
|
|
|
else { |
327
|
|
|
|
|
|
|
my $v = XML::LibXML::Element->new('string'); |
328
|
|
|
|
|
|
|
$v->appendText(defined $E ? $E->decode($p) : $p); |
329
|
|
|
|
|
|
|
$r->appendChild($v); |
330
|
|
|
|
|
|
|
} |
331
|
|
|
|
|
|
|
} |
332
|
|
|
|
|
|
|
return $r; |
333
|
|
|
|
|
|
|
} |
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
sub request { |
336
|
|
|
|
|
|
|
my $self = shift; |
337
|
|
|
|
|
|
|
local @CLASS{keys %{ $self->{class} }} = values %{ $self->{class} }; |
338
|
|
|
|
|
|
|
local $E = Encode::find_encoding($self->{internal_encoding}) |
339
|
|
|
|
|
|
|
or croak "Could not find encoding $self->{internal_encoding}" |
340
|
|
|
|
|
|
|
if defined $self->{internal_encoding}; |
341
|
|
|
|
|
|
|
my $method = shift; |
342
|
|
|
|
|
|
|
my $doc = XML::LibXML::Document->new('1.0',$self->{external_encoding}); |
343
|
|
|
|
|
|
|
my $root = XML::LibXML::Element->new('methodCall'); |
344
|
|
|
|
|
|
|
$doc->setDocumentElement($root); |
345
|
|
|
|
|
|
|
my $n = XML::LibXML::Element->new('methodName'); |
346
|
|
|
|
|
|
|
$n->appendText(defined $E ? $E->decode($method) : $method); |
347
|
|
|
|
|
|
|
$root->appendChild($n); |
348
|
|
|
|
|
|
|
my $prms = XML::LibXML::Element->new('params'); |
349
|
|
|
|
|
|
|
$root->appendChild($prms); |
350
|
|
|
|
|
|
|
for my $v (@_) { |
351
|
|
|
|
|
|
|
my $p = XML::LibXML::Element->new('param'); |
352
|
|
|
|
|
|
|
$p->appendChild( _unparse_param($v) ); |
353
|
|
|
|
|
|
|
$prms->appendChild($p); |
354
|
|
|
|
|
|
|
} |
355
|
|
|
|
|
|
|
my $x = $doc->toString; |
356
|
|
|
|
|
|
|
utf8::encode($x) if utf8::is_utf8($x); |
357
|
|
|
|
|
|
|
return $x; |
358
|
|
|
|
|
|
|
} |
359
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
sub response { |
361
|
|
|
|
|
|
|
my $self = shift; |
362
|
|
|
|
|
|
|
local @CLASS{keys %{ $self->{class} }} = values %{ $self->{class} }; |
363
|
|
|
|
|
|
|
local $E = Encode::find_encoding($self->{internal_encoding}) |
364
|
|
|
|
|
|
|
or croak "Could not find encoding $self->{internal_encoding}" |
365
|
|
|
|
|
|
|
if defined $self->{internal_encoding}; |
366
|
|
|
|
|
|
|
my $doc = XML::LibXML::Document->new('1.0',$self->{external_encoding}); |
367
|
|
|
|
|
|
|
my $root = XML::LibXML::Element->new('methodResponse'); |
368
|
|
|
|
|
|
|
$doc->setDocumentElement($root); |
369
|
|
|
|
|
|
|
my $prms = XML::LibXML::Element->new('params'); |
370
|
|
|
|
|
|
|
$root->appendChild($prms); |
371
|
|
|
|
|
|
|
for my $v (@_) { |
372
|
|
|
|
|
|
|
my $p = XML::LibXML::Element->new('param'); |
373
|
|
|
|
|
|
|
$p->appendChild( _unparse_param($v) ); |
374
|
|
|
|
|
|
|
$prms->appendChild($p); |
375
|
|
|
|
|
|
|
} |
376
|
|
|
|
|
|
|
my $x = $doc->toString; |
377
|
|
|
|
|
|
|
utf8::encode($x) if utf8::is_utf8($x); |
378
|
|
|
|
|
|
|
return $x; |
379
|
|
|
|
|
|
|
} |
380
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
sub fault { |
382
|
|
|
|
|
|
|
my $self = shift; |
383
|
|
|
|
|
|
|
local @CLASS{keys %{ $self->{class} }} = values %{ $self->{class} }; |
384
|
|
|
|
|
|
|
local $E = Encode::find_encoding($self->{internal_encoding}) |
385
|
|
|
|
|
|
|
or croak "Could not find encoding $self->{internal_encoding}" |
386
|
|
|
|
|
|
|
if defined $self->{internal_encoding}; |
387
|
|
|
|
|
|
|
my ($code,$err) = @_; |
388
|
|
|
|
|
|
|
my $doc = XML::LibXML::Document->new('1.0',$self->{external_encoding}); |
389
|
|
|
|
|
|
|
my $root = XML::LibXML::Element->new('methodResponse'); |
390
|
|
|
|
|
|
|
$doc->setDocumentElement($root); |
391
|
|
|
|
|
|
|
my $f = XML::LibXML::Element->new('fault'); |
392
|
|
|
|
|
|
|
my $v = XML::LibXML::Element->new('value'); |
393
|
|
|
|
|
|
|
my $s = XML::LibXML::Element->new('struct'); |
394
|
|
|
|
|
|
|
for (qw(faultCode faultString)){ |
395
|
|
|
|
|
|
|
my $m = XML::LibXML::Element->new('member'); |
396
|
|
|
|
|
|
|
my $n = XML::LibXML::Element->new('name'); |
397
|
|
|
|
|
|
|
$n->appendText(defined $E ? $E->decode($_) : $_); |
398
|
|
|
|
|
|
|
$m->appendChild($n); |
399
|
|
|
|
|
|
|
$m->appendChild(_unparse_param(shift)); |
400
|
|
|
|
|
|
|
$s->appendChild($m); |
401
|
|
|
|
|
|
|
} |
402
|
|
|
|
|
|
|
$v->appendChild($s); |
403
|
|
|
|
|
|
|
$f->appendChild($v); |
404
|
|
|
|
|
|
|
$root->appendChild($f); |
405
|
|
|
|
|
|
|
my $x = $doc->toString; |
406
|
|
|
|
|
|
|
utf8::encode($x) if utf8::is_utf8($x); |
407
|
|
|
|
|
|
|
return $x; |
408
|
|
|
|
|
|
|
} |
409
|
|
|
|
|
|
|
|
410
|
|
|
|
|
|
|
# Decoder part |
411
|
|
|
|
|
|
|
our $src; |
412
|
|
|
|
|
|
|
sub decode { |
413
|
|
|
|
|
|
|
my $self = shift; |
414
|
|
|
|
|
|
|
my $string = shift; |
415
|
|
|
|
|
|
|
#utf8::encode $string if utf8::is_utf8($string); |
416
|
|
|
|
|
|
|
local $src = $string; |
417
|
|
|
|
|
|
|
$self->_parse( $self->{parser}->parse_string($string) ) |
418
|
|
|
|
|
|
|
} |
419
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
sub _parse_param { |
421
|
|
|
|
|
|
|
my $v = shift; |
422
|
|
|
|
|
|
|
for my $t ($v->childNodes) { |
423
|
|
|
|
|
|
|
next if ref $t eq 'XML::LibXML::Text'; |
424
|
|
|
|
|
|
|
my $type = $t->nodeName; |
425
|
|
|
|
|
|
|
#print $t->nodeName,"\n"; |
426
|
|
|
|
|
|
|
if ($type eq 'string') { |
427
|
|
|
|
|
|
|
return defined $E ? $E->encode(''.$t->textContent) : ''.$t->textContent; |
428
|
|
|
|
|
|
|
} |
429
|
|
|
|
|
|
|
elsif ($type eq 'i4' or $type eq 'int') { |
430
|
|
|
|
|
|
|
return int $t->textContent; |
431
|
|
|
|
|
|
|
} |
432
|
|
|
|
|
|
|
elsif ($type eq 'double') { |
433
|
|
|
|
|
|
|
return 0+$t->textContent; |
434
|
|
|
|
|
|
|
} |
435
|
|
|
|
|
|
|
elsif ($type eq 'bool') { |
436
|
|
|
|
|
|
|
$v = $t->textContent; |
437
|
|
|
|
|
|
|
return $v eq 'false' ? 0 : !!$v ? 1 : 0; |
438
|
|
|
|
|
|
|
} |
439
|
|
|
|
|
|
|
elsif ($type eq 'struct') { |
440
|
|
|
|
|
|
|
my $r = {}; |
441
|
|
|
|
|
|
|
for my $m ($t->childNodes) { |
442
|
|
|
|
|
|
|
my ($mn,$mv); |
443
|
|
|
|
|
|
|
if ($m->nodeName eq 'member') { |
444
|
|
|
|
|
|
|
for my $x ($m->childNodes) { |
445
|
|
|
|
|
|
|
#print "\tmember:".$x->nodeName,"\n"; |
446
|
|
|
|
|
|
|
if ($x->nodeName eq 'name') { |
447
|
|
|
|
|
|
|
$mn = $x->textContent; |
448
|
|
|
|
|
|
|
#last; |
449
|
|
|
|
|
|
|
} |
450
|
|
|
|
|
|
|
elsif ($x->nodeName eq 'value') { |
451
|
|
|
|
|
|
|
$mv = _parse_param ($x); |
452
|
|
|
|
|
|
|
$mn and last; |
453
|
|
|
|
|
|
|
} |
454
|
|
|
|
|
|
|
} |
455
|
|
|
|
|
|
|
if (defined $E) { |
456
|
|
|
|
|
|
|
$mn = $E->encode($mn); |
457
|
|
|
|
|
|
|
$mv = $E->encode($mv); |
458
|
|
|
|
|
|
|
} |
459
|
|
|
|
|
|
|
$r->{$mn} = $mv; |
460
|
|
|
|
|
|
|
} |
461
|
|
|
|
|
|
|
} |
462
|
|
|
|
|
|
|
return $r; |
463
|
|
|
|
|
|
|
} |
464
|
|
|
|
|
|
|
elsif ($type eq 'array') { |
465
|
|
|
|
|
|
|
my $r = []; |
466
|
|
|
|
|
|
|
for my $d ($t->childNodes) { |
467
|
|
|
|
|
|
|
#print "\tdata:".$d->nodeName,"\n"; |
468
|
|
|
|
|
|
|
unless (defined $d) { |
469
|
|
|
|
|
|
|
warn "!!! Internal bug: childNodes return undef. XML=\n$src"; |
470
|
|
|
|
|
|
|
next; |
471
|
|
|
|
|
|
|
} |
472
|
|
|
|
|
|
|
if ($d->nodeName eq 'data') { |
473
|
|
|
|
|
|
|
for my $x ($d->childNodes) { |
474
|
|
|
|
|
|
|
#print "\tdata:".$x->nodeName,"\n"; |
475
|
|
|
|
|
|
|
if ($x->nodeName eq 'value') { |
476
|
|
|
|
|
|
|
push @$r, _parse_param ($x); |
477
|
|
|
|
|
|
|
} |
478
|
|
|
|
|
|
|
} |
479
|
|
|
|
|
|
|
} |
480
|
|
|
|
|
|
|
} |
481
|
|
|
|
|
|
|
return $r; |
482
|
|
|
|
|
|
|
} |
483
|
|
|
|
|
|
|
# elsif ($type eq 'base64') { |
484
|
|
|
|
|
|
|
# return decode_base64($t->textContent); |
485
|
|
|
|
|
|
|
# } |
486
|
|
|
|
|
|
|
# elsif ($type eq 'dateTime.iso8601') { |
487
|
|
|
|
|
|
|
# return $t->textContent; |
488
|
|
|
|
|
|
|
# } |
489
|
|
|
|
|
|
|
else { |
490
|
|
|
|
|
|
|
if (exists $TYPES{$type} and $TYPES{$type}) { |
491
|
|
|
|
|
|
|
return $TYPES{$type}( $t->childNodes ); |
492
|
|
|
|
|
|
|
} else { |
493
|
|
|
|
|
|
|
my @children = $t->childNodes; |
494
|
|
|
|
|
|
|
@children or return bless( \do{ my $o }, $type ); |
495
|
|
|
|
|
|
|
if (( @children > 1 ) xor ( ref $children[0] ne 'XML::LibXML::Text' )) { |
496
|
|
|
|
|
|
|
#print STDERR + (0+@children)."; $type => ",ref $children[0], ' ', $children[0]->nodeName, "\n"; |
497
|
|
|
|
|
|
|
return bless \(xml2hash($t)->{$type}),$type; |
498
|
|
|
|
|
|
|
} else { |
499
|
|
|
|
|
|
|
#print STDERR + "*** ".(0+@children)."; $type => ",ref $children[0], ' ', $children[0]->nodeName, "\n"; |
500
|
|
|
|
|
|
|
return bless \( |
501
|
|
|
|
|
|
|
defined $E ? $E->encode($children[0]->textContent) : $children[0]->textContent |
502
|
|
|
|
|
|
|
),$type; |
503
|
|
|
|
|
|
|
} |
504
|
|
|
|
|
|
|
} |
505
|
|
|
|
|
|
|
} |
506
|
|
|
|
|
|
|
last; |
507
|
|
|
|
|
|
|
} |
508
|
|
|
|
|
|
|
return defined $E ? $E->encode($v->textContent) : $v->textContent |
509
|
|
|
|
|
|
|
} |
510
|
|
|
|
|
|
|
|
511
|
|
|
|
|
|
|
sub _parse { |
512
|
|
|
|
|
|
|
my $self = shift; |
513
|
|
|
|
|
|
|
my $doc = shift; |
514
|
|
|
|
|
|
|
my @r; |
515
|
|
|
|
|
|
|
my $root = $doc->documentElement; |
516
|
|
|
|
|
|
|
local @TYPES{keys %{ $self->{types} }} = values %{ $self->{types} }; |
517
|
|
|
|
|
|
|
local $E = Encode::find_encoding($self->{internal_encoding}) |
518
|
|
|
|
|
|
|
or croak "Could not find encoding $self->{internal_encoding}" |
519
|
|
|
|
|
|
|
if defined $self->{internal_encoding}; |
520
|
|
|
|
|
|
|
for my $p ($doc->findnodes('//param')) { |
521
|
|
|
|
|
|
|
#for my $ps ($root->childNodes) { |
522
|
|
|
|
|
|
|
# if ($ps->nodeName eq 'params') { |
523
|
|
|
|
|
|
|
# for my $p ($ps->childNodes) { |
524
|
|
|
|
|
|
|
# if ($p->nodeName eq 'param') { |
525
|
|
|
|
|
|
|
#print $p->nodeName,"\n"; |
526
|
|
|
|
|
|
|
for my $v ($p->childNodes) { |
527
|
|
|
|
|
|
|
if ($v->nodeName eq 'value') { |
528
|
|
|
|
|
|
|
#print $p->nodeName,'=',_parse_param($v),"\n"; |
529
|
|
|
|
|
|
|
push @r, _parse_param ($v); |
530
|
|
|
|
|
|
|
} |
531
|
|
|
|
|
|
|
} |
532
|
|
|
|
|
|
|
# } |
533
|
|
|
|
|
|
|
# } |
534
|
|
|
|
|
|
|
# } |
535
|
|
|
|
|
|
|
} |
536
|
|
|
|
|
|
|
for my $m ($doc->findnodes('//methodName')) { |
537
|
|
|
|
|
|
|
unshift @r, defined $E ? $E->encode($m->textContent) : $m->textContent; |
538
|
|
|
|
|
|
|
last; |
539
|
|
|
|
|
|
|
} |
540
|
|
|
|
|
|
|
unless(@r) { |
541
|
|
|
|
|
|
|
for my $f ($doc->findnodes('//fault')) { |
542
|
|
|
|
|
|
|
my ($c,$e); |
543
|
|
|
|
|
|
|
|
544
|
|
|
|
|
|
|
for ($f->childNodes) { |
545
|
|
|
|
|
|
|
if ( $_->nodeName eq 'value' ) { |
546
|
|
|
|
|
|
|
my $flt = _parse_param ( $_ ); |
547
|
|
|
|
|
|
|
$c = $flt->{faultCode}; |
548
|
|
|
|
|
|
|
$e = $flt->{faultString}; |
549
|
|
|
|
|
|
|
last; |
550
|
|
|
|
|
|
|
} else { |
551
|
|
|
|
|
|
|
$c = defined $E ? $E->encode($_->textContent) : $_->textContent if $_->nodeName eq 'faultCode'; |
552
|
|
|
|
|
|
|
$e = defined $E ? $E->encode($_->textContent) : $_->textContent if $_->nodeName eq 'faultString'; |
553
|
|
|
|
|
|
|
} |
554
|
|
|
|
|
|
|
} |
555
|
|
|
|
|
|
|
return { fault => { faultCode => $c, faultString => $e } }; |
556
|
|
|
|
|
|
|
} |
557
|
|
|
|
|
|
|
} |
558
|
|
|
|
|
|
|
#warn "@r"; |
559
|
|
|
|
|
|
|
return @r; |
560
|
|
|
|
|
|
|
} |
561
|
|
|
|
|
|
|
|
562
|
|
|
|
|
|
|
=head1 COPYRIGHT & LICENSE |
563
|
|
|
|
|
|
|
|
564
|
|
|
|
|
|
|
Copyright (c) 2008-2009 Mons Anderson. |
565
|
|
|
|
|
|
|
|
566
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify it |
567
|
|
|
|
|
|
|
under the same terms as Perl itself. |
568
|
|
|
|
|
|
|
|
569
|
|
|
|
|
|
|
=head1 AUTHOR |
570
|
|
|
|
|
|
|
|
571
|
|
|
|
|
|
|
Mons Anderson, C<< >> |
572
|
|
|
|
|
|
|
|
573
|
|
|
|
|
|
|
=cut |
574
|
|
|
|
|
|
|
|
575
|
|
|
|
|
|
|
1; |