File Coverage

blib/lib/XMLRPC/Fast.pm
Criterion Covered Total %
statement 19 21 90.4
branch n/a
condition n/a
subroutine 7 7 100.0
pod n/a
total 26 28 92.8


line stmt bran cond sub pod time code
1             package XMLRPC::Fast;
2              
3 3     3   80357 use strict;
  3         5  
  3         72  
4 3     3   12 use warnings;
  3         5  
  3         92  
5              
6 3     3   20 use B qw< svref_2object SVf_IOK SVf_NOK >;
  3         5  
  3         209  
7 3     3   16268 use Encode;
  3         20647  
  3         231  
8 3     3   16 use Exporter qw< import >;
  3         5  
  3         75  
9 3     3   2075 use MIME::Base64;
  3         424434  
  3         396  
10 3     3   4870 use XML::Parser;
  0            
  0            
11              
12              
13             our $VERSION = "0.10";
14              
15             our @EXPORT = qw<
16             decode_xmlrpc encode_xmlrpc
17             encode_xmlrpc_request encode_xmlrpc_response encode_xmlrpc_fault
18             >;
19              
20              
21             my $utf8 = find_encoding("UTF-8");
22              
23              
24              
25             #
26             # encode_xmlrpc_request()
27             # ---------------------
28             sub encode_xmlrpc_request {
29             encode_xmlrpc(method => @_)
30             }
31              
32              
33             #
34             # encode_xmlrpc_response()
35             # ----------------------
36             sub encode_xmlrpc_response {
37             encode_xmlrpc(response => "", @_)
38             }
39              
40              
41             #
42             # encode_xmlrpc_fault()
43             # -------------------
44             sub encode_xmlrpc_fault {
45             encode_xmlrpc(fault => "", $_[0], $_[1])
46             }
47              
48              
49             #
50             # encode_xmlrpc()
51             # -------------
52             sub encode_xmlrpc {
53             my ($type, $method, @args) = @_;
54              
55             my $tag = $type eq "method" ? "methodCall" : "methodResponse";
56              
57             my $xml = q{};
58             $xml .= "<$tag>";
59             $xml .= "$method" if $type eq "method";
60              
61             if ($type eq "fault") {
62             $args[0] //= "";
63             $args[1] //= "";
64              
65             $xml .= "faultCode"
66             . "$args[0]"
67             . "faultString"
68             . "$args[1]"
69             . ""
70             }
71             else {
72             if (@args) {
73             $xml .= "";
74             $xml .= "".encode_xmlrpc_thing($_).""
75             for @args;
76             $xml .= "";
77             }
78             }
79              
80             $xml .= "";
81             }
82              
83              
84             #
85             # encode_xmlrpc_thing()
86             # -------------------
87             sub encode_xmlrpc_thing {
88             if (ref $_[0]) {
89             # handle structures and objects
90             my $struct = $_[0];
91              
92             if (ref $struct eq "ARRAY") {
93             return join "",
94             "",
95             (map encode_xmlrpc_thing($_), @$struct),
96             ""
97             }
98             elsif (ref $struct eq "HASH") {
99             return join "",
100             "",
101             (map "$_"
102             . encode_xmlrpc_thing($struct->{$_})
103             . "",
104             keys %$struct),
105             ""
106             }
107             elsif (ref $struct eq "DateTime") {
108             my $date = $struct->strftime("%Y-%m-%dT%H:%M:%S");
109             return "$date"
110             }
111             elsif (ref $struct eq "DateTime::Tiny") {
112             my $date = $struct->as_string;
113             return "$date"
114             }
115             }
116             else {
117             # handle scalar values
118             return "" if not defined $_[0];
119              
120             my $copy = $_[0];
121             my $sv = svref_2object(\$_[0]);
122              
123             return "$copy" if $sv->FLAGS & SVf_NOK;
124             return "$copy" if $sv->FLAGS & SVf_IOK;
125              
126             if (Encode::is_utf8($_[0])) {
127             $copy = $utf8->encode($_[0]);
128             }
129              
130             if ($copy ne $_[0] or $copy =~ /[^\x09\x0a\x0d\x20-\x7f]/) {
131             return "" . encode_base64($copy, "") . ""
132             }
133             else {
134             $copy =~ s/&/&/g;
135             $copy =~ s/
136             $copy =~ s/>/>/g;
137             return "$copy"
138             }
139             }
140             }
141              
142              
143             #
144             # decode_xmlrpc()
145             # -------------
146             sub decode_xmlrpc {
147             my ($xml) = shift;
148              
149             # parse the XML document
150             my $parser = XML::Parser->new(Style => "Tree");
151             my $tree = $parser->parse($xml);
152             my $root = $tree->[1];
153             my %struct;
154              
155             # detect the message type
156             if ($tree->[0] eq "methodCall") {
157             $struct{type} = "request";
158             }
159             elsif ($tree->[0] eq "methodResponse") {
160             $struct{type} = "response";
161             }
162             else {
163             die "unknown type of message";
164             }
165              
166             # handle first-level elements + detect if fault message
167             while (defined (my $e = shift @$root)) {
168             next if ref $e eq "HASH"; # skip attributes
169             shift @$root and next if $e eq "0"; # skip text outside elements
170              
171             if ($e eq "params") {
172             $struct{params} = [ decode_node(shift @$root) ];
173             }
174             elsif ($e eq "methodName") {
175             $struct{methodName} = (shift @$root)->[2];
176             }
177             elsif ($e eq "fault") {
178             %struct = (
179             type => "fault",
180             fault => decode_node(shift @$root),
181             );
182             }
183             }
184              
185             return \%struct;
186             }
187              
188              
189             #
190             # decode_node()
191             # -----------
192             sub decode_node {
193             my ($node) = shift;
194             my @result;
195              
196             while (defined (my $e = shift @$node)) {
197             next if ref $e eq "HASH"; # skip attributes
198             shift @$node and next if $e eq "0"; # skip text outside elements
199              
200             if ($e eq "value") {
201             # small dance to correctly handle empty values, which must
202             # generate an undef in order to keep things balanced
203             my $v = shift @$node;
204             push @result, @$v > 1 ? decode_node($v) : undef;
205             }
206             elsif ($e eq "data" or $e eq "member" or $e eq "param") {
207             push @result, decode_node(shift @$node);
208             }
209             elsif ($e eq "array") {
210             push @result, [ decode_node(shift @$node) ];
211             }
212             elsif ($e eq "struct") {
213             push @result, { decode_node(shift @$node) };
214             }
215             elsif ($e eq "int" or $e eq "i4" or $e eq "boolean") {
216             push @result, int((shift @$node)->[2]);
217             }
218             elsif ($e eq "double") {
219             push @result, (shift @$node)->[2] / 1.0;
220             }
221             elsif ($e eq "string" or $e eq "name" or $e eq "dateTime.iso8601") {
222             push @result, (shift @$node)->[2];
223             }
224             elsif ($e eq "base64") {
225             push @result, decode_base64((shift @$node)->[2]);
226             }
227             elsif ($e eq "nil") {
228             push @result, undef;
229             }
230             }
231              
232             return @result
233             }
234              
235              
236              
237             __END__