File Coverage

blib/lib/RPC/XML.pm
Criterion Covered Total %
statement 609 616 98.8
branch 137 144 95.1
condition 69 98 70.4
subroutine 106 106 100.0
pod 0 9 0.0
total 921 973 94.6


line stmt bran cond sub pod time code
1             ###############################################################################
2             #
3             # This file copyright (c) 2001-2014 Randy J. Ray, all rights reserved
4             #
5             # Copying and distribution are permitted under the terms of the Artistic
6             # License 2.0 (http://www.opensource.org/licenses/artistic-license-2.0.php) or
7             # the GNU LGPL (http://www.opensource.org/licenses/lgpl-2.1.php).
8             #
9             ###############################################################################
10             #
11             # Description: This module provides the core XML <-> RPC conversion and
12             # structural management.
13             #
14             # Functions: This module contains many, many subclasses. Better to
15             # examine them individually.
16             #
17             # Libraries: RPC::XML::base64 uses MIME::Base64
18             # DateTime::Format::ISO8601 is used if available
19             #
20             # Global Consts: $VERSION
21             #
22             ###############################################################################
23              
24             package RPC::XML;
25              
26 21     21   290274 use 5.008008;
  21         47  
27 21     21   69 use strict;
  21         25  
  21         329  
28 21     21   66 use warnings;
  21         20  
  21         599  
29 21         1866 use vars qw(@EXPORT_OK %EXPORT_TAGS $VERSION $ERROR
30             %XMLMAP $XMLRE $ENCODING $FORCE_STRING_ENCODING $ALLOW_NIL
31 21     21   69 $DATETIME_REGEXP $DATETIME_ISO8601_AVAILABLE);
  21         20  
32 21     21   6280 use subs qw(time2iso8601 smart_encode);
  21         222  
  21         90  
33 21     21   755 use base 'Exporter';
  21         61  
  21         1676  
34              
35 21     21   3934 use Module::Load;
  21         7072  
  21         110  
36 21     21   751 use Scalar::Util qw(blessed reftype);
  21         24  
  21         1976  
37              
38             # The RPC_* convenience-encoders need prototypes:
39             ## no critic (ProhibitSubroutinePrototypes)
40             # This module declares all the data-type packages:
41             ## no critic (ProhibitMultiplePackages)
42             # The data-type package names trigger this one:
43             ## no critic (Capitalization)
44             # The XML escape map now has CR in it but I don't want to use charnames:
45             ## no critic (ProhibitEscapedCharacters)
46              
47             BEGIN
48             {
49             # Default encoding:
50 21     21   33 $ENCODING = 'us-ascii';
51              
52             # force strings?
53 21         31 $FORCE_STRING_ENCODING = 0;
54              
55             # Allow the extension?
56 21         34 $ALLOW_NIL = 0;
57              
58             # Determine if the DateTime::Format::ISO8601 module is available for
59             # RPC::XML::datetime_iso8601 to use:
60 21         104 $DATETIME_ISO8601_AVAILABLE = eval { load DateTime::Format::ISO8601; 1; };
  21         68  
  21         2489827  
61             }
62              
63             @EXPORT_OK = qw(time2iso8601 smart_encode
64             RPC_BOOLEAN RPC_INT RPC_I4 RPC_I8 RPC_DOUBLE
65             RPC_DATETIME_ISO8601 RPC_BASE64 RPC_STRING RPC_NIL
66             $ENCODING $FORCE_STRING_ENCODING $ALLOW_NIL);
67             %EXPORT_TAGS = (types => [ qw(RPC_BOOLEAN RPC_INT RPC_I4 RPC_I8 RPC_DOUBLE
68             RPC_STRING RPC_DATETIME_ISO8601 RPC_BASE64
69             RPC_NIL) ],
70             all => [ @EXPORT_OK ]);
71              
72             $VERSION = '1.61';
73             $VERSION = eval $VERSION; ## no critic (ProhibitStringyEval)
74              
75             # Global error string
76             $ERROR = q{};
77              
78             # These are used for stringifying XML-sensitive characters that may appear
79             # in struct keys:
80             %XMLMAP = (
81             q{>} => '>',
82             q{<} => '<',
83             q{&} => '&',
84             q{"} => '"',
85             q{'} => ''',
86             "\x0d" => ' ',
87             );
88             $XMLRE = join q{} => keys %XMLMAP; $XMLRE = qr/([$XMLRE])/;
89              
90             # The XMLRPC spec only allows for the incorrect iso8601 format
91             # without dashes, but dashes are part of the standard so we include
92             # them. Note that the actual RPC::XML::datetime_iso8601 class will strip
93             # them out if present.
94             my $date_re =
95             qr{
96             (\d{4})-?
97             ([01]\d)-?
98             ([0123]\d)
99             }x;
100             my $time_re =
101             qr{
102             ([012]\d):
103             ([0-5]\d):
104             ([0-5]\d)([.,]\d+)?
105             (Z|[-+]\d\d:\d\d)?
106             }x;
107             $DATETIME_REGEXP = qr{^${date_re}T?${time_re}$};
108              
109             # All of the RPC_* functions are convenience-encoders
110             sub RPC_STRING ($)
111             {
112 1     1 0 253 return RPC::XML::string->new(shift);
113             }
114             sub RPC_BOOLEAN ($)
115             {
116 1     1 0 4 return RPC::XML::boolean->new(shift);
117             }
118             sub RPC_INT ($)
119             {
120 1     1 0 5 return RPC::XML::int->new(shift);
121             }
122             sub RPC_I4 ($)
123             {
124 1     1 0 6 return RPC::XML::i4->new(shift);
125             }
126             sub RPC_I8 ($)
127             {
128 1     1 0 7 return RPC::XML::i8->new(shift);
129             }
130             sub RPC_DOUBLE ($)
131             {
132 1     1 0 5 return RPC::XML::double->new(shift);
133             }
134             sub RPC_DATETIME_ISO8601 ($)
135             {
136 1     1 0 5 return RPC::XML::datetime_iso8601->new(shift);
137             }
138             sub RPC_BASE64 ($;$)
139             {
140 1     1 0 5 return RPC::XML::base64->new(shift, shift);
141             }
142             sub RPC_NIL ()
143             {
144 2     2 0 15 return RPC::XML::nil->new();
145             }
146              
147             # This is a dead-simple ISO8601-from-UNIX-time stringifier. Always expresses
148             # time in UTC. The format isn't strictly ISO8601, though, as the XML-RPC spec
149             # fucked it up.
150             sub time2iso8601
151             {
152 2   66 2   638 my $time = shift || time;
153              
154 2         47 my @time = gmtime $time;
155 2         21 $time = sprintf '%4d%02d%02dT%02d:%02d:%02dZ',
156             $time[5] + 1900, $time[4] + 1, @time[3, 2, 1, 0];
157              
158 2         9 return $time;
159             }
160              
161             # This is a (futile?) attempt to provide a "smart" encoding method that will
162             # take a Perl scalar and promote it to the appropriate RPC::XML::_type_.
163             {
164             # The regex for ints and floats uses [0-9] instead of \d on purpose, to
165             # only match ASCII digits.
166             ## no critic (ProhibitEnumeratedClasses)
167             # The regex for floats is long, but I don't feel like factoring it out
168             # right now.
169             ## no critic (ProhibitComplexRegexes)
170              
171             my $MAX_INT = 2_147_483_647;
172             my $MIN_INT = -2_147_483_648;
173             my $MAX_BIG_INT = 9_223_372_036_854_775_807;
174             my $MIN_BIG_INT = -9_223_372_036_854_775_808;
175              
176             my $MAX_DOUBLE = 1e37;
177             my $MIN_DOUBLE = $MAX_DOUBLE * -1;
178              
179             sub smart_encode ## no critic (ProhibitExcessComplexity)
180             {
181 87     87   7544 my @values = @_;
182 87         80 my ($type, $seenrefs, @newvalues);
183              
184             # Look for sooper-sekrit pseudo-blessed hashref as first argument.
185             # It means this is a recursive call, and it contains a map of any
186             # references we've already seen.
187 87 100 100     536 if ((blessed $values[0]) && ($values[0]->isa('RPC::XML::refmap')))
188             {
189             # Peel it off of the list
190 34         40 $seenrefs = shift @values;
191             }
192             else
193             {
194             # Create one just in case we need it
195 53         144 $seenrefs = bless {}, 'RPC::XML::refmap';
196             }
197              
198 87         141 for my $value (@values)
199             {
200 152 100 100     1399 if (! defined $value)
    100 66        
    100 66        
    100 100        
    100 100        
      100        
201             {
202 2 100       19 $type = $ALLOW_NIL ?
203             RPC::XML::nil->new() : RPC::XML::string->new(q{});
204             }
205             elsif (ref $value)
206             {
207             # Skip any that we've already seen
208 53 100       164 next if $seenrefs->{$value}++;
209              
210 49 100 100     310 if (blessed($value) &&
    100 66        
    100          
    100          
211             ($value->isa('RPC::XML::datatype') || $value->isa('DateTime')))
212             {
213             # Only if the reference is a datatype or a DateTime
214             # instance, do we short-cut here...
215              
216 27 100       47 if ($value->isa('RPC::XML::datatype'))
217             {
218             # Pass through any that have already been encoded
219 26         24 $type = $value;
220             }
221             else
222             {
223             # Must be a DateTime object, convert to ISO8601
224 1         6 $type = RPC::XML::datetime_iso8601
225             ->new($value->clone->set_time_zone('UTC'));
226             }
227             }
228             elsif (reftype($value) eq 'HASH')
229             {
230             # Per RT 41063, to catch circular refs I can't delegate
231             # to the struct constructor, I have to create my own
232             # copy of the hash with locally-recursively-encoded
233             # values
234 10         12 my %newhash;
235 10         12 for my $key (keys %{$value})
  10         28  
236             {
237             # Forcing this into a list-context *should* make the
238             # test be true even if the return value is a hard
239             # undef. Only if the return value is an empty list
240             # should this evaluate as false...
241 23 100       52 if (my @value = smart_encode($seenrefs, $value->{$key}))
242             {
243 22         40 $newhash{$key} = $value[0];
244             }
245             }
246              
247 10         45 $type = RPC::XML::struct->new(\%newhash);
248             }
249             elsif (reftype($value) eq 'ARRAY')
250             {
251             # This is a somewhat-ugly approach, but I don't want to
252             # dereference @$value, but I also want people to be able to
253             # pass array-refs in to this constructor and have them
254             # be treated as single elements, as one would expect
255             # (see RT 35106)
256             # Per RT 41063, looks like I get to deref $value after all...
257             $type = RPC::XML::array->new(
258 7         9 from => [ smart_encode($seenrefs, @{$value}) ]
  7         34  
259             );
260             }
261             elsif (reftype($value) eq 'SCALAR')
262             {
263             # This is a rare excursion into recursion, since the scalar
264             # nature (de-refed from the object, so no longer magic)
265             # will prevent further recursing.
266 4         5 $type = smart_encode($seenrefs, ${$value});
  4         6  
267             }
268             else
269             {
270             # If the user passed in a reference that didn't pass one
271             # of the above tests, we can't do anything with it:
272 1         7 $type = reftype $value;
273 1         14 die "Un-convertable reference: $type, cannot use\n";
274             }
275 48         112 $seenrefs->{$value}--;
276             }
277             # You have to check ints first, because they match the
278             # next pattern (for doubles) too
279             elsif (! $FORCE_STRING_ENCODING &&
280             $value =~ /^[-+]?[0-9]+$/ &&
281             $value >= $MIN_BIG_INT &&
282             $value <= $MAX_BIG_INT)
283             {
284 53 100 100     167 if (($value > $MAX_INT) || ($value < $MIN_INT))
285             {
286 5         16 $type = RPC::XML::i8->new($value);
287             }
288             else
289             {
290 48         101 $type = RPC::XML::int->new($value);
291             }
292             }
293             # Pattern taken from perldata(1)
294             elsif (! $FORCE_STRING_ENCODING &&
295             $value =~ m{
296             ^
297             [+-]?
298             (?=[0-9]|[.][0-9])
299             [0-9]*
300             (?:[.][0-9]*)?
301             (?:[Ee](?:[+-]?[0-9]+))?
302             $
303             }x &&
304             $value > $MIN_DOUBLE &&
305             $value < $MAX_DOUBLE)
306             {
307 4         17 $type = RPC::XML::double->new($value);
308             }
309             elsif ($value =~ /$DATETIME_REGEXP/)
310             {
311 3         10 $type = RPC::XML::datetime_iso8601->new($value);
312             }
313             else
314             {
315 37         129 $type = RPC::XML::string->new($value);
316             }
317              
318 147         214 push @newvalues, $type;
319             }
320              
321 86 100       364 return (wantarray ? @newvalues : $newvalues[0]);
322             }
323             }
324              
325             # This is a (mostly) empty class used as a common superclass for simple and
326             # complex types, so that their derivatives may be universally type-checked.
327             package RPC::XML::datatype;
328              
329             sub type
330             {
331 130     130   6729 my $self = shift;
332              
333 130   33     319 my $class = ref($self) || $self;
334 130         536 $class =~ s/.*://;
335              
336 130         430 return $class;
337             }
338              
339 1     1   5 sub is_fault { return 0; }
340              
341             ###############################################################################
342             #
343             # Package: RPC::XML::simple_type
344             #
345             # Description: A base class for the simpler type-classes to inherit from,
346             # for default constructor, stringification, etc.
347             #
348             ###############################################################################
349             package RPC::XML::simple_type;
350              
351 21     21   184 use strict;
  21         31  
  21         459  
352 21     21   71 use base 'RPC::XML::datatype';
  21         27  
  21         6984  
353              
354 21     21   106 use Scalar::Util 'reftype';
  21         26  
  21         7592  
355              
356             # new - a generic constructor that presumes the value being stored is scalar
357             sub new
358             {
359 126     126   4793 my $class = shift;
360 126         121 my $value = shift;
361              
362 126         126 $RPC::XML::ERROR = q{};
363 126   33     368 $class = ref($class) || $class;
364              
365 126 100       227 if ($class eq 'RPC::XML::simple_type')
366             {
367 1         3 $RPC::XML::ERROR = 'RPC::XML::simple_type::new: Cannot instantiate ' .
368             'this class directly';
369 1         4 return;
370             }
371              
372 125 100       184 if (ref $value)
373             {
374             # If it is a scalar reference, just deref
375 6 100       29 if (reftype($value) eq 'SCALAR')
376             {
377 5         4 $value = ${$value};
  5         10  
378             }
379             else
380             {
381             # We can only manage scalar references (or blessed scalar refs)
382 1         5 $RPC::XML::ERROR = "${class}::new: Cannot instantiate from a " .
383             'reference not derived from scalar';
384 1         4 return;
385             }
386             }
387              
388 124         289 return bless \$value, $class;
389             }
390              
391             # value - a generic accessor
392             sub value
393             {
394 131     131   53317 my $self = shift;
395              
396 131 100       342 if (! ref $self)
397             {
398 1         3 $RPC::XML::ERROR =
399             "{$self}::value: Cannot be called as a static method";
400 1         3 return;
401             }
402              
403 130         119 return ${$self};
  130         501  
404             }
405              
406             # as_string - return the value as an XML snippet
407             sub as_string
408             {
409 164     164   1709 my $self = shift;
410              
411 164         197 my $class = ref $self;
412 164 100       275 if (! $class)
413             {
414 1         6 $RPC::XML::ERROR =
415             "{$self}::as_string: Cannot be called as a static method";
416 1         6 return;
417             }
418 163         451 $class =~ s/^.*\://;
419 163         197 $class =~ s/_/./g;
420 163 100       315 if (substr($class, 0, 8) eq 'datetime')
421             {
422 11         18 substr $class, 0, 8, 'dateTime';
423             }
424              
425 163         181 return "<$class>${$self}";
  163         599  
426             }
427              
428             # Serialization for simple types is just a matter of sending as_string over
429             sub serialize
430             {
431 14     14   12 my ($self, $fh) = @_;
432              
433 14         18 utf8::encode(my $str = $self->as_string);
434 14         11 print {$fh} $str;
  14         76  
435              
436 14         16 return;
437             }
438              
439             # The switch to serialization instead of in-memory strings means having to
440             # calculate total size in bytes for Content-Length headers:
441             sub length ## no critic (ProhibitBuiltinHomonyms)
442             {
443 67     67   71 my $self = shift;
444              
445 67         82 utf8::encode(my $str = $self->as_string);
446              
447 67         136 return length $str;
448             }
449              
450             ###############################################################################
451             #
452             # Package: RPC::XML::int
453             #
454             # Description: Data-type class for integers
455             #
456             ###############################################################################
457             package RPC::XML::int;
458              
459 21     21   91 use strict;
  21         26  
  21         427  
460 21     21   63 use base 'RPC::XML::simple_type';
  21         26  
  21         5351  
461              
462             ###############################################################################
463             #
464             # Package: RPC::XML::i4
465             #
466             # Description: Data-type class for i4. Forces data into an int object.
467             #
468             ###############################################################################
469             package RPC::XML::i4;
470              
471 21     21   95 use strict;
  21         24  
  21         388  
472 21     21   62 use base 'RPC::XML::simple_type';
  21         22  
  21         4385  
473              
474             ###############################################################################
475             #
476             # Package: RPC::XML::i8
477             #
478             # Description: Data-type class for i8. Forces data into a 8-byte int.
479             #
480             ###############################################################################
481             package RPC::XML::i8;
482              
483 21     21   171 use strict;
  21         27  
  21         349  
484 21     21   56 use base 'RPC::XML::simple_type';
  21         22  
  21         4224  
485              
486             ###############################################################################
487             #
488             # Package: RPC::XML::double
489             #
490             # Description: The "double" type-class
491             #
492             ###############################################################################
493             package RPC::XML::double;
494              
495 21     21   84 use strict;
  21         25  
  21         380  
496 21     21   60 use base 'RPC::XML::simple_type';
  21         22  
  21         6254  
497              
498             sub as_string
499             {
500 15     15   387 my $self = shift;
501              
502 15 100       40 if (! ref $self)
503             {
504 1         5 $RPC::XML::ERROR =
505             "{$self}::as_string: Cannot be called as a static method";
506 1         5 return;
507             }
508 14         37 my $class = $self->type;
509              
510 14         18 (my $value = sprintf '%.20f', ${$self}) =~ s/([.]\d+?)0+$/$1/;
  14         209  
511              
512 14         97 return "<$class>$value";
513             }
514              
515             ###############################################################################
516             #
517             # Package: RPC::XML::string
518             #
519             # Description: The "string" type-class
520             #
521             ###############################################################################
522             package RPC::XML::string;
523              
524 21     21   82 use strict;
  21         25  
  21         369  
525 21     21   61 use base 'RPC::XML::simple_type';
  21         23  
  21         6507  
526              
527             # as_string - return the value as an XML snippet
528             sub as_string
529             {
530 57     57   403 my $self = shift;
531              
532 57         53 my ($class, $value);
533              
534 57 100       153 if (! ref $self)
535             {
536 1         5 $RPC::XML::ERROR =
537             "{$self}::as_string: Cannot be called as a static method";
538 1         47 return;
539             }
540 56         121 $class = $self->type;
541              
542 56 50       61 ($value = defined ${$self} ? ${$self} : q{} )
  56         108  
  56         184  
543 3         14 =~ s/$RPC::XML::XMLRE/$RPC::XML::XMLMAP{$1}/ge;
544              
545 56         218 return "<$class>$value";
546             }
547              
548             ###############################################################################
549             #
550             # Package: RPC::XML::boolean
551             #
552             # Description: The type-class for boolean data. Handles some "extra" cases
553             #
554             ###############################################################################
555             package RPC::XML::boolean;
556              
557 21     21   98 use strict;
  21         45  
  21         402  
558 21     21   78 use base 'RPC::XML::simple_type';
  21         23  
  21         7137  
559              
560             # This constructor allows any of true, false, yes or no to be specified
561             sub new
562             {
563 8     8   1899 my $class = shift;
564 8   100     28 my $value = shift || 0;
565              
566 8         12 $RPC::XML::ERROR = q{};
567 8 100       51 if ($value =~ /true|yes|1/i)
    100          
568             {
569 4         8 $value = 1;
570             }
571             elsif ($value =~ /false|no|0/i)
572             {
573 3         7 $value = 0;
574             }
575             else
576             {
577 1   33     11 $class = ref($class) || $class;
578 1         8 $RPC::XML::ERROR = "${class}::new: Value must be one of yes, no, " .
579             'true, false, 1, 0 (case-insensitive)';
580 1         4 return;
581             }
582              
583 7         22 return bless \$value, $class;
584             }
585              
586             ###############################################################################
587             #
588             # Package: RPC::XML::datetime_iso8601
589             #
590             # Description: This is the class to manage ISO8601-style date/time values
591             #
592             ###############################################################################
593             package RPC::XML::datetime_iso8601;
594              
595 21     21   86 use strict;
  21         28  
  21         411  
596 21     21   60 use base 'RPC::XML::simple_type';
  21         24  
  21         4334  
597              
598 21     21   80 use Scalar::Util 'reftype';
  21         25  
  21         5170  
599              
600 7     7   1050 sub type { return 'dateTime.iso8601'; };
601              
602             # Check the value passed in for sanity, and normalize the string representation
603             sub new
604             {
605 70     70   20419 my ($class, $value) = @_;
606 70         68 my $newvalue;
607              
608 70 100 100     181 if (ref($value) && reftype($value) eq 'SCALAR')
609             {
610 1         2 $value = ${$value};
  1         3  
611             }
612              
613 70 100       108 if (defined $value)
614             {
615 69 100       370 if ($value =~ /$RPC::XML::DATETIME_REGEXP/)
    50          
616             {
617             # This is *not* a valid ISO 8601 format, but it's the way it is
618             # given in the spec, so assume that other implementations can only
619             # accept this form. Also, this should match the form that
620             # time2iso8601 produces.
621 10 100       88 $newvalue = $7 ? "$1$2$3T$4:$5:$6$7" : "$1$2$3T$4:$5:$6";
622 10 100       25 if ($8) {
623 6         15 $newvalue .= $8;
624             }
625             }
626             elsif ($RPC::XML::DATETIME_ISO8601_AVAILABLE)
627             {
628             $newvalue =
629 59         71 eval { DateTime::Format::ISO8601->parse_datetime($value) };
  59         204  
630 59 100       36133 if ($newvalue)
631             {
632             # This both removes the dashes (*sigh*) and forces it from an
633             # object to an ordinary string:
634 56         1505 $newvalue =~ s/-//g;
635             }
636             }
637              
638 69 100       1480 if (! $newvalue)
639             {
640 3         12 $RPC::XML::ERROR = "${class}::new: Malformed data ($value) " .
641             'passed as dateTime.iso8601';
642 3         7 return;
643             }
644             }
645             else
646             {
647 1         6 $RPC::XML::ERROR = "${class}::new: Value required in constructor";
648 1         5 return;
649             }
650              
651 66         172 return bless \$newvalue, $class;
652             }
653              
654             ###############################################################################
655             #
656             # Package: RPC::XML::nil
657             #
658             # Description: The "nil" type-class extension
659             #
660             ###############################################################################
661             package RPC::XML::nil;
662              
663 21     21   82 use strict;
  21         38  
  21         403  
664 21     21   59 use base 'RPC::XML::simple_type';
  21         32  
  21         6774  
665              
666             # no value need be passed to this method
667             sub new
668             {
669 6     6   1210 my ($class, $value, $flag) = @_;
670             # We need $value so we can bless a reference to it. But regardless of
671             # what was passed, it needs to be undef to be a proper "nil".
672 6         8 undef $value;
673              
674 6 50 66     20 if (! $RPC::XML::ALLOW_NIL && ! $flag)
675             {
676 1         7 $RPC::XML::ERROR = "${class}::new: \$RPC::XML::ALLOW_NIL must be set" .
677             ' for RPC::XML::nil objects to be supported';
678 1         4 return;
679             }
680              
681 5         21 return bless \$value, $class;
682             }
683              
684             # Stringification and serialsation are trivial..
685             sub as_string
686             {
687 9     9   617 return '';
688             }
689              
690             sub serialize
691             {
692 1     1   2 my ($self, $fh) = @_;
693              
694 1         0 print {$fh} $self->as_string; # In case someone sub-classes this
  1         3  
695              
696 1         2 return;
697             }
698              
699             ###############################################################################
700             #
701             # Package: RPC::XML::array
702             #
703             # Description: This class encapsulates the array data type. Each element
704             # within the array should be one of the datatype classes.
705             #
706             ###############################################################################
707             package RPC::XML::array;
708              
709 21     21   87 use strict;
  21         203  
  21         395  
710 21     21   62 use base 'RPC::XML::datatype';
  21         23  
  21         4951  
711              
712 21     21   87 use Scalar::Util qw(blessed reftype);
  21         19  
  21         6337  
713              
714             # The constructor for this class mainly needs to sanity-check the value data
715             sub new
716             {
717 10     10   1275 my ($class, @args) = @_;
718              
719             # Special-case time: If the args-list has exactly two elements, and the
720             # first element is "from" and the second element is an array-ref (or a
721             # type derived from), then copy the ref's contents into @args.
722 10 50 66     81 if ((2 == @args) && ($args[0] eq 'from') && (reftype($args[1]) eq 'ARRAY'))
      66        
723             {
724 8         9 @args = @{$args[1]};
  8         18  
725             }
726              
727             # Ensure that each argument passed in is itself one of the data-type
728             # class instances.
729 10         26 return bless [ RPC::XML::smart_encode(@args) ], $class;
730             }
731              
732             # This became more complex once it was shown that there may be a need to fetch
733             # the value while preserving the underlying objects.
734             sub value
735             {
736 10     10   1448 my $self = shift;
737 10   100     38 my $no_recurse = shift || 0;
738 10         9 my $ret;
739              
740 10 100       25 if ($no_recurse)
741             {
742 2         4 $ret = [ @{$self} ];
  2         9  
743             }
744             else
745             {
746 8         11 $ret = [ map { $_->value } @{$self} ];
  30         50  
  8         27  
747             }
748              
749 10         59 return $ret;
750             }
751              
752             sub as_string
753             {
754 7     7   9 my $self = shift;
755              
756             return join q{},
757             '',
758 7         8 (map { ('', $_->as_string(), '') } (@{$self})),
  49         76  
  7         18  
759             '';
760             }
761              
762             # Serialization for arrays is not as straight-forward as it is for simple
763             # types. One or more of the elements may be a base64 object, which has a
764             # non-trivial serialize() method. Thus, rather than just sending the data from
765             # as_string down the pipe, instead call serialize() recursively on all of the
766             # elements.
767             sub serialize
768             {
769 1     1   2 my ($self, $fh) = @_;
770              
771 1         1 print {$fh} '';
  1         4  
772 1         2 for (@{$self})
  1         2  
773             {
774 3         3 print {$fh} '';
  3         12  
775 3         5 $_->serialize($fh);
776 3         3 print {$fh} '';
  3         14  
777             }
778 1         1 print {$fh} '';
  1         5  
779              
780 1         1 return;
781             }
782              
783             # Length calculation starts to get messy here, due to recursion
784             sub length ## no critic (ProhibitBuiltinHomonyms)
785             {
786 3     3   6 my $self = shift;
787              
788             # Start with the constant components in the text
789 3         7 my $len = 28; # That the part
790 3         4 for (@{$self}) { $len += (15 + $_->length) } # 15 is for
  3         10  
  23         37  
791              
792 3         13 return $len;
793             }
794              
795             ###############################################################################
796             #
797             # Package: RPC::XML::struct
798             #
799             # Description: This is the "struct" data class. The struct is like Perl's
800             # hash, with the constraint that all values are instances
801             # of the datatype classes.
802             #
803             ###############################################################################
804             package RPC::XML::struct;
805              
806 21     21   84 use strict;
  21         24  
  21         353  
807 21     21   54 use base 'RPC::XML::datatype';
  21         25  
  21         4443  
808              
809 21     21   83 use Scalar::Util qw(blessed reftype);
  21         19  
  21         9620  
810              
811             # The constructor for this class mainly needs to sanity-check the value data
812             sub new
813             {
814 20     20   1015 my ($class, @args) = @_;
815             my %args = (ref $args[0] and reftype($args[0]) eq 'HASH') ?
816 20 100 66     144 %{$args[0]} : @args;
  14         51  
817              
818             # RT 41063: If all the values are datatype objects, either they came in
819             # that way or we've already laundered them through smart_encode(). If there
820             # is even one that isn't, then we have to pass the whole mess to be
821             # encoded.
822             my $ref =
823 20 100 66     44 (grep { ! (blessed($_) && $_->isa('RPC::XML::datatype')) } values %args)
  46         248  
824             ? RPC::XML::smart_encode(\%args) : \%args;
825              
826 20         62 return bless $ref, $class;
827             }
828              
829             # This became more complex once it was shown that there may be a need to fetch
830             # the value while preserving the underlying objects.
831             sub value
832             {
833 12     12   3351 my $self = shift;
834 12   100     46 my $no_recurse = shift || 0;
835 12         12 my %value;
836              
837 12 100       32 if ($no_recurse)
838             {
839 4         7 %value = map { ($_, $self->{$_}) } (keys %{$self});
  8         25  
  4         14  
840             }
841             else
842             {
843 8         8 %value = map { ($_, $self->{$_}->value) } (keys %{$self});
  18         46  
  8         35  
844             }
845              
846 12         51 return \%value;
847             }
848              
849             sub as_string
850             {
851 12     12   243 my $self = shift;
852 12         16 my $key;
853              
854             # Clean the keys of $self, in case they have any HTML-special characters
855             my %clean;
856 12         12 for (keys %{$self})
  12         41  
857             {
858 24         90 ($key = $_) =~ s/$RPC::XML::XMLRE/$RPC::XML::XMLMAP{$1}/ge;
  7         25  
859 24         60 $clean{$key} = $self->{$_}->as_string;
860             }
861              
862             return join q{},
863             '',
864             (map {
865 12         29 ("$_",
866 24         525 $clean{$_},
867             '')
868             } (keys %clean)),
869             '';
870             }
871              
872             # As with the array type, serialization here isn't cut and dried, since one or
873             # more values may be base64.
874             sub serialize
875             {
876 2     2   3 my ($self, $fh) = @_;
877 2         2 my $key;
878              
879 2         1 print {$fh} '';
  2         9  
880 2         2 for (keys %{$self})
  2         5  
881             {
882 3         8 ($key = $_) =~ s/$RPC::XML::XMLRE/$RPC::XML::XMLMAP{$1}/ge;
  0         0  
883 3         6 utf8::encode($key);
884 3         2 print {$fh} "$key";
  3         16  
885 3         5 $self->{$_}->serialize($fh);
886 3         3 print {$fh} '';
  3         15  
887             }
888 2         2 print {$fh} '';
  2         8  
889              
890 2         2 return;
891             }
892              
893             # Length calculation is a real pain here. But not as bad as base64 promises
894             sub length ## no critic (ProhibitBuiltinHomonyms)
895             {
896 5     5   8 my $self = shift;
897              
898 5         11 my $len = 17; #
899 5         7 for my $key (keys %{$self})
  5         15  
900             {
901 9         11 $len += 45; # For all the constant XML presence
902 9         24 $len += $self->{$key}->length;
903 9         15 utf8::encode($key);
904 9         13 $len += length $key;
905             }
906              
907 5         18 return $len;
908             }
909              
910             ###############################################################################
911             #
912             # Package: RPC::XML::base64
913             #
914             # Description: This is the base64-encoding type. Plain data is passed in,
915             # plain data is returned. Plain is always returned. All the
916             # encoding/decoding is done behind the scenes.
917             #
918             ###############################################################################
919             package RPC::XML::base64;
920              
921 21     21   91 use strict;
  21         25  
  21         424  
922 21     21   58 use base 'RPC::XML::datatype';
  21         24  
  21         4306  
923              
924 21     21   81 use Scalar::Util 'reftype';
  21         23  
  21         17192  
925              
926             sub new
927             {
928 14     14   4120 my ($class, $value, $encoded) = @_;
929              
930 14         536 require MIME::Base64;
931              
932 14         602 my $self = {};
933              
934 14         20 $RPC::XML::ERROR = q{};
935              
936 14 100       41 $self->{encoded} = $encoded ? 1 : 0; # Is this already Base-64?
937 14         17 $self->{inmem} = 0; # To signal in-memory vs. filehandle
938              
939             # First, determine if the call sent actual data, a reference to actual
940             # data, or an open filehandle.
941 14 100 100     77 if (ref $value and reftype($value) eq 'GLOB')
942             {
943             # This is a seekable filehandle (or acceptable substitute thereof).
944             # This assignment increments the ref-count, and prevents destruction
945             # in other scopes.
946 8         16 binmode $value;
947 8         10 $self->{value_fh} = $value;
948 8         12 $self->{fh_pos} = tell $value;
949             }
950             else
951             {
952             # Not a filehandle. Might be a scalar ref, but other than that it's
953             # in-memory data.
954 6         9 $self->{inmem}++;
955 6 100 100     26 $self->{value} = ref($value) ? ${$value} : ($value || q{});
  1         3  
956             # We want in-memory data to always be in the clear, to reduce the tests
957             # needed in value(), below.
958 6 100       19 if ($self->{encoded})
959             {
960 1         8 local $^W = 0; # Disable warnings in case the data is underpadded
961 1         8 $self->{value} = MIME::Base64::decode_base64($self->{value});
962 1         3 $self->{encoded} = 0;
963             }
964             }
965              
966 14         42 return bless $self, $class;
967             }
968              
969             sub value
970             {
971 26     26   2500 my ($self, $flag) = @_;
972 26 100 66     107 my $as_base64 = (defined $flag and $flag) ? 1 : 0;
973              
974             # There are six cases here, based on whether or not the data exists in
975             # Base-64 or clear form, and whether the data is in-memory or needs to be
976             # read from a filehandle.
977 26 100       52 if ($self->{inmem})
978             {
979             # This is simplified into two cases (rather than four) since we always
980             # keep in-memory data as cleartext
981             return $as_base64 ?
982 11 100       82 MIME::Base64::encode_base64($self->{value}, q{}) : $self->{value};
983             }
984             else
985             {
986             # This is trickier with filehandle-based data, since we chose not to
987             # change the state of the data. Thus, the behavior is dependant not
988             # only on $as_base64, but also on $self->{encoded}. This is why we
989             # took pains to explicitly set $as_base64 to either 0 or 1, rather than
990             # just accept whatever non-false value the caller sent. It makes this
991             # first test possible.
992 15         10 my ($accum, $pos, $res);
993 15         12 $accum = q{};
994              
995 15         20 $self->{fh_pos} = tell $self->{value_fh};
996 15         40 seek $self->{value_fh}, 0, 0;
997 15 100       19 if ($as_base64 == $self->{encoded})
998             {
999 7         8 $pos = 0;
1000 7         56 while ($res = read $self->{value_fh}, $accum, 1024, $pos)
1001             {
1002 14         32 $pos += $res;
1003             }
1004             }
1005             else
1006             {
1007 8 100       48 if ($as_base64)
1008             {
1009             # We're reading cleartext and converting it to Base-64. Read in
1010             # multiples of 57 bytes for best Base-64 calculation. The
1011             # choice of 60 for the multiple is purely arbitrary.
1012 6         6 $res = q{};
1013 6         51 while (read $self->{value_fh}, $res, 60*57)
1014             {
1015 6         68 $accum .= MIME::Base64::encode_base64($res, q{});
1016             }
1017             }
1018             else
1019             {
1020             # Reading Base-64 and converting it back to cleartext. If the
1021             # Base-64 data doesn't have any line-breaks, no telling how
1022             # much memory this will eat up.
1023 2         6 local $^W = 0; # Disable padding-length warnings
1024 2         3 $pos = $self->{value_fh};
1025 2         17 while (defined($res = <$pos>))
1026             {
1027 2         11 $accum .= MIME::Base64::decode_base64($res);
1028             }
1029             }
1030             }
1031 15         23 seek $self->{value_fh}, $self->{fh_pos}, 0;
1032              
1033 15         112 return $accum;
1034             }
1035             }
1036              
1037             # The value needs to be encoded before being output
1038             sub as_string
1039             {
1040 18     18   516 my $self = shift;
1041              
1042 18         32 return '' . $self->value('encoded') . '';
1043             }
1044              
1045             # If it weren't for Tellme and their damnable WAV files, and ViAir and their
1046             # half-baked XML-RPC server, I wouldn't have to do any of this...
1047             #
1048             # (On the plus side, at least here I don't have to worry about encodings...)
1049             sub serialize
1050             {
1051 3     3   9 my ($self, $fh) = @_;
1052              
1053             # If the data is in-memory, just call as_string and pass it down the pipe
1054 3 100       5 if ($self->{inmem})
1055             {
1056 1         2 print {$fh} $self->as_string;
  1         3  
1057             }
1058             else
1059             {
1060             # If it's a filehandle, at least we take comfort in knowing that we
1061             # always want Base-64 at this level.
1062 2         3 my $buf = q{};
1063 2         3 $self->{fh_pos} = tell $self->{value_fh};
1064 2         5 seek $self->{value_fh}, 0, 0;
1065 2         2 print {$fh} '';
  2         10  
1066 2 100       4 if ($self->{encoded})
1067             {
1068             # Easy-- just use read() to send it down in palatably-sized chunks
1069 1         9 while (read $self->{value_fh}, $buf, 4096)
1070             {
1071 1         1 print {$fh} $buf;
  1         7  
1072             }
1073             }
1074             else
1075             {
1076             # This actually requires work. As with value(), the 60*57 is based
1077             # on ideal Base-64 chunks, with the 60 part being arbitrary.
1078 1         12 while (read $self->{value_fh}, $buf, 60*57)
1079             {
1080 1         1 print {$fh} MIME::Base64::encode_base64($buf, q{});
  1         23  
1081             }
1082             }
1083 2         2 print {$fh} '';
  2         10  
1084 2         5 seek $self->{value_fh}, $self->{fh_pos}, 0;
1085             }
1086              
1087 3         3 return;
1088             }
1089              
1090             # This promises to be a big enough pain that I seriously considered opening
1091             # an anon-temp file (one that's unlinked for security, and survives only as
1092             # long as the FH is open) and passing that to serialize just to -s on the FH.
1093             # But I'll do this the "right" way instead...
1094             sub length ## no critic (ProhibitBuiltinHomonyms)
1095             {
1096 5     5   8 my $self = shift;
1097              
1098             # Start with the constant bits
1099 5         7 my $len = 17; #
1100              
1101 5 100       15 if ($self->{inmem})
1102             {
1103             # If it's in-memory, it's cleartext. Size the encoded version
1104 2         9 $len += length(MIME::Base64::encode_base64($self->{value}, q{}));
1105             }
1106             else
1107             {
1108 3 100       6 if ($self->{encoded})
1109             {
1110             # We're lucky, it's already encoded in the file, and -s will do
1111 1         7 $len += -s $self->{value_fh};
1112             }
1113             else
1114             {
1115             # Oh bugger. We have to encode it.
1116 2         4 my $buf = q{};
1117 2         2 my $cnt = 0;
1118              
1119 2         3 $self->{fh_pos} = tell $self->{value_fh};
1120 2         5 seek $self->{value_fh}, 0, 0;
1121 2         23 while ($cnt = read $self->{value_fh}, $buf, 60*57)
1122             {
1123 2         18 $len += length(MIME::Base64::encode_base64($buf, q{}));
1124             }
1125 2         4 seek $self->{value_fh}, $self->{fh_pos}, 0;
1126             }
1127             }
1128              
1129 5         15 return $len;
1130             }
1131              
1132             # This allows writing the decoded data to an arbitrary file. It's useful when
1133             # an application has gotten a RPC::XML::base64 object back from a request, and
1134             # knows that it needs to go straight to a file without being completely read
1135             # into memory, first.
1136             sub to_file
1137             {
1138 5     5   1248 my ($self, $file) = @_;
1139              
1140 5         8 my ($fh, $buf, $do_close, $count) = (undef, q{}, 0, 0);
1141              
1142 5 100       9 if (ref $file)
1143             {
1144 2 100       10 if (reftype($file) eq 'GLOB')
1145             {
1146 1         3 $fh = $file;
1147             }
1148             else
1149             {
1150 1         2 $RPC::XML::ERROR = 'Unusable reference type passed to to_file';
1151 1         4 return -1;
1152             }
1153             }
1154             else
1155             {
1156 3 50       155 if (! open $fh, '>', $file) ## no critic (RequireBriefOpen)
1157             {
1158 0         0 $RPC::XML::ERROR = "Error opening $file for writing: $!";
1159 0         0 return -1;
1160             }
1161 3         6 binmode $fh;
1162 3         4 $do_close++;
1163             }
1164              
1165             # If all the data is in-memory, then we know that it's clear, and we
1166             # don't have to jump through hoops in moving it to the filehandle.
1167 4 100       7 if ($self->{inmem})
1168             {
1169 2         3 print {$fh} $self->{value};
  2         24  
1170 2         3 $count = CORE::length($self->{value});
1171             }
1172             else
1173             {
1174             # Filehandle-to-filehandle transfer.
1175              
1176             # Now determine if the data can be copied over directly, or if we have
1177             # to decode it along the way.
1178 2         4 $self->{fh_pos} = tell $self->{value_fh};
1179 2         4 seek $self->{value_fh}, 0, 0;
1180 2 100       5 if ($self->{encoded})
1181             {
1182             # As with the caveat in value(), if the base-64 data doesn't have
1183             # any line-breaks, no telling how much memory this will eat up.
1184 1         4 local $^W = 0; # Disable padding-length warnings
1185 1         2 my $tmp_fh = $self->{value_fh};
1186 1         12 while (defined($_ = <$tmp_fh>))
1187             {
1188 32         41 $buf = MIME::Base64::decode_base64($_);
1189 32         20 print {$fh} $buf;
  32         30  
1190 32         45 $count += CORE::length($buf);
1191             }
1192             }
1193             else
1194             {
1195             # If the data is already decoded in the filehandle, then just copy
1196             # it over.
1197 1         1 my $size;
1198 1         15 while ($size = read $self->{value_fh}, $buf, 4096)
1199             {
1200 1         2 print {$fh} $buf;
  1         7  
1201 1         4 $count += $size;
1202             }
1203             }
1204              
1205             # Restore the position of the file-pointer for the internal FH
1206 2         3 seek $self->{value_fh}, $self->{fh_pos}, 0;
1207             }
1208              
1209 4 100       7 if ($do_close)
1210             {
1211 3 50       71 if (! close $fh)
1212             {
1213 0         0 $RPC::XML::ERROR = "Error closing $file after writing: $!";
1214 0         0 return -1;
1215             }
1216             }
1217              
1218 4         13 return $count;
1219             }
1220              
1221             ###############################################################################
1222             #
1223             # Package: RPC::XML::fault
1224             #
1225             # Description: This is the class that encapsulates the data for a RPC
1226             # fault-response. Like the others, it takes the relevant
1227             # information and maintains it internally. This is put
1228             # at the end of the datum types, though it isn't really a
1229             # data type in the sense that it cannot be passed in to a
1230             # request. But it is separated so as to better generalize
1231             # responses.
1232             #
1233             ###############################################################################
1234             package RPC::XML::fault;
1235              
1236 21     21   97 use strict;
  21         27  
  21         400  
1237 21     21   62 use base 'RPC::XML::struct';
  21         24  
  21         5212  
1238              
1239 21     21   91 use Scalar::Util 'blessed';
  21         23  
  21         7074  
1240              
1241             # For our new(), we only need to ensure that we have the two required members
1242             sub new
1243             {
1244 6     6   2029 my ($class, @args) = @_;
1245              
1246 6         10 my %args;
1247              
1248 6         10 $RPC::XML::ERROR = q{};
1249 6 50 33     93 if (blessed $args[0] and $args[0]->isa('RPC::XML::struct'))
    100 100        
      66        
1250             {
1251             # Take the keys and values from the struct object as our own
1252 0         0 %args = %{$args[0]->value('shallow')};
  0         0  
1253             }
1254             elsif ((@args == 2) && ($args[0] =~ /^-?\d+$/) && length $args[1])
1255             {
1256             # This is a special convenience-case to make simple new() calls clearer
1257 3         15 %args = (faultCode => RPC::XML::int->new($args[0]),
1258             faultString => RPC::XML::string->new($args[1]));
1259             }
1260             else
1261             {
1262 3         12 %args = @args;
1263             }
1264              
1265 6 100 33     33 if (! ($args{faultCode} and $args{faultString}))
1266             {
1267 1   33     7 $class = ref($class) || $class;
1268 1         4 $RPC::XML::ERROR = "${class}::new: Missing required struct fields";
1269 1         4 return;
1270             }
1271 5 100       18 if (scalar(keys %args) > 2)
1272             {
1273 1   33     17 $class = ref($class) || $class;
1274 1         3 $RPC::XML::ERROR = "${class}::new: Extra struct fields not allowed";
1275 1         5 return;
1276             }
1277              
1278 4         29 return $class->SUPER::new(%args);
1279             }
1280              
1281             # This only differs from the display of a struct in that it has some extra
1282             # wrapped around it. Let the superclass as_string method do most of the work.
1283             sub as_string
1284             {
1285 4     4   5 my $self = shift;
1286              
1287 4         18 return '' . $self->SUPER::as_string . '';
1288             }
1289              
1290             # Again, only differs from struct in that it has some extra wrapped around it.
1291             sub serialize
1292             {
1293 1     1   1 my ($self, $fh) = @_;
1294              
1295 1         2 print {$fh} '';
  1         5  
1296 1         5 $self->SUPER::serialize($fh);
1297 1         1 print {$fh} '';
  1         5  
1298              
1299 1         1 return;
1300             }
1301              
1302             # Because of the slight diff above, length() has to be different from struct
1303             sub length ## no critic (ProhibitBuiltinHomonyms)
1304             {
1305 2     2   4 my $self = shift;
1306              
1307 2         12 return $self->SUPER::length + 30; # For constant XML content
1308             }
1309              
1310             # Convenience methods:
1311 1     1   260 sub code { return shift->{faultCode}->value; }
1312 1     1   6 sub string { return shift->{faultString}->value; }
1313              
1314             # This is the only one to override this method, for obvious reasons
1315 1     1   8 sub is_fault { return 1; }
1316              
1317             ###############################################################################
1318             #
1319             # Package: RPC::XML::request
1320             #
1321             # Description: This is the class that encapsulates the data for a RPC
1322             # request. It takes the relevant information and maintains
1323             # it internally until asked to stringify. Only then is the
1324             # XML generated, encoding checked, etc. This allows for
1325             # late-selection of or as a
1326             # containing tag.
1327             #
1328             # This class really only needs a constructor and a method
1329             # to stringify.
1330             #
1331             ###############################################################################
1332             package RPC::XML::request;
1333              
1334 21     21   89 use strict;
  21         30  
  21         392  
1335              
1336 21     21   67 use Scalar::Util 'blessed';
  21         28  
  21         17807  
1337              
1338             ###############################################################################
1339             #
1340             # Sub Name: new
1341             #
1342             # Description: Creating a new request object, in this (reference) case,
1343             # means checking the list of arguments for sanity and
1344             # packaging it up for later use.
1345             #
1346             # Arguments: NAME IN/OUT TYPE DESCRIPTION
1347             # $class in scalar Class/ref to bless into
1348             # @argz in list The exact disposition of the
1349             # arguments is based on the
1350             # type of the various elements
1351             #
1352             # Returns: Success: object ref
1353             # Failure: undef, error in $RPC::XML::ERROR
1354             #
1355             ###############################################################################
1356             sub new
1357             {
1358 8     8   2704 my ($class, @argz) = @_;
1359              
1360 8         11 my $name;
1361              
1362 8   33     41 $class = ref($class) || $class;
1363 8         11 $RPC::XML::ERROR = q{};
1364              
1365 8 100       23 if (! @argz)
1366             {
1367 1         3 $RPC::XML::ERROR = 'RPC::XML::request::new: At least a method name ' .
1368             'must be specified';
1369 1         4 return;
1370             }
1371              
1372             # This is the method name to be called
1373 7         14 $name = shift @argz;
1374             # Is it valid?
1375 7 100       36 if ($name !~ m{^[\w.:/]+$})
1376             {
1377 1         3 $RPC::XML::ERROR =
1378             'RPC::XML::request::new: Invalid method name specified';
1379 1         4 return;
1380             }
1381              
1382             # All the remaining args must be data.
1383 6         20 @argz = RPC::XML::smart_encode(@argz);
1384              
1385 6         40 return bless { args => [ @argz ], name => $name }, $class;
1386             }
1387              
1388             # Accessor methods
1389 1     1   283 sub name { return shift->{name}; }
1390 6     6   30 sub args { return shift->{args}; }
1391              
1392             ###############################################################################
1393             #
1394             # Sub Name: as_string
1395             #
1396             # Description: This is a fair bit more complex than the simple as_string
1397             # methods for the datatypes. Express the invoking object as
1398             # a well-formed XML document.
1399             #
1400             # Arguments: NAME IN/OUT TYPE DESCRIPTION
1401             # $self in ref Invoking object
1402             # $indent in scalar Indention level for output
1403             #
1404             # Returns: Success: text
1405             # Failure: undef
1406             #
1407             ###############################################################################
1408             sub as_string
1409             {
1410 8     8   21 my $self = shift;
1411              
1412 8         9 my $text;
1413              
1414 8         11 $RPC::XML::ERROR = q{};
1415              
1416 8         21 $text = qq();
1417              
1418 8         35 $text .= "$self->{name}";
1419 8         10 for (@{$self->{args}})
  8         24  
1420             {
1421 55         138 $text .= '' . $_->as_string . '';
1422             }
1423 8         10 $text .= '';
1424              
1425 8         86 return $text;
1426             }
1427              
1428             # The difference between stringifying and serializing a request is much like
1429             # the difference was for structs and arrays. The boilerplate is the same, but
1430             # the destination is different in a sensitive way.
1431             sub serialize
1432             {
1433 1     1   2 my ($self, $fh) = @_;
1434 1         3 utf8::encode(my $name = $self->{name});
1435              
1436 1         1 print {$fh} qq();
  1         41  
1437              
1438 1         2 print {$fh} "$name";
  1         7  
1439 1         1 for (@{$self->{args}})
  1         3  
1440             {
1441 11         10 print {$fh} '';
  11         42  
1442 11         32 $_->serialize($fh);
1443 11         6 print {$fh} '';
  11         46  
1444             }
1445 1         1 print {$fh} '';
  1         5  
1446              
1447 1         2 return;
1448             }
1449              
1450             # Compared to base64, length-calculation here is pretty easy, much like struct
1451             sub length ## no critic (ProhibitBuiltinHomonyms)
1452             {
1453 2     2   3 my $self = shift;
1454              
1455 2         5 my $len = 100 + length $RPC::XML::ENCODING; # All the constant XML present
1456 2         7 utf8::encode(my $name = $self->{name});
1457 2         3 $len += length $name;
1458              
1459 2         3 for (@{$self->{args}})
  2         6  
1460             {
1461 21         16 $len += 30; # Constant XML
1462 21         39 $len += $_->length;
1463             }
1464              
1465 2         9 return $len;
1466             }
1467              
1468             ###############################################################################
1469             #
1470             # Package: RPC::XML::response
1471             #
1472             # Description: This is the class that encapsulates the data for a RPC
1473             # response. As above, it takes the information and maintains
1474             # it internally until asked to stringify. Only then is the
1475             # XML generated, encoding checked, etc. This allows for
1476             # late-selection of or
1477             # as above.
1478             #
1479             ###############################################################################
1480             package RPC::XML::response;
1481              
1482 21     21   95 use strict;
  21         26  
  21         417  
1483              
1484 21     21   63 use Scalar::Util 'blessed';
  21         28  
  21         8038  
1485              
1486             ###############################################################################
1487             #
1488             # Sub Name: new
1489             #
1490             # Description: Creating a new response object, in this (reference) case,
1491             # means checking the outgoing parameter(s) for sanity.
1492             #
1493             # Arguments: NAME IN/OUT TYPE DESCRIPTION
1494             # $class in scalar Class/ref to bless into
1495             # @argz in list The exact disposition of the
1496             # arguments is based on the
1497             # type of the various elements
1498             #
1499             # Returns: Success: object ref
1500             # Failure: undef, error in $RPC::XML::ERROR
1501             #
1502             ###############################################################################
1503             sub new
1504             {
1505 8     8   610 my ($class, @argz) = @_;
1506              
1507 8   33     31 $class = ref($class) || $class;
1508              
1509 8         13 $RPC::XML::ERROR = q{};
1510 8 100       29 if (! @argz)
    100          
1511             {
1512 1         4 $RPC::XML::ERROR = 'RPC::XML::response::new: One of a datatype, ' .
1513             'value or a fault object must be specified';
1514 1         3 return;
1515             }
1516             elsif (@argz > 1)
1517             {
1518 1         4 $RPC::XML::ERROR = 'RPC::XML::response::new: Responses may take ' .
1519             'only one argument';
1520 1         4 return;
1521             }
1522              
1523 6         17 $argz[0] = RPC::XML::smart_encode($argz[0]);
1524              
1525 6         22 return bless { value => $argz[0] }, $class;
1526             }
1527              
1528             # Accessor/status methods
1529 2     2   303 sub value { return shift->{value}; }
1530 2     2   244 sub is_fault { return shift->{value}->is_fault; }
1531              
1532             ###############################################################################
1533             #
1534             # Sub Name: as_string
1535             #
1536             # Description: This is a fair bit more complex than the simple as_string
1537             # methods for the datatypes. Express the invoking object as
1538             # a well-formed XML document.
1539             #
1540             # Arguments: NAME IN/OUT TYPE DESCRIPTION
1541             # $self in ref Invoking object
1542             # $indent in scalar Indention level for output
1543             #
1544             # Returns: Success: text
1545             # Failure: undef
1546             #
1547             ###############################################################################
1548             sub as_string
1549             {
1550 13     13   81 my $self = shift;
1551              
1552 13         11 my $text;
1553              
1554 13         15 $RPC::XML::ERROR = q{};
1555              
1556 13         24 $text = qq();
1557              
1558 13         18 $text .= '';
1559 13 100       53 if ($self->{value}->isa('RPC::XML::fault'))
1560             {
1561 2         5 $text .= $self->{value}->as_string;
1562             }
1563             else
1564             {
1565             $text .= '' . $self->{value}->as_string .
1566 11         23 '';
1567             }
1568 13         22 $text .= '';
1569              
1570 13         42 return $text;
1571             }
1572              
1573             # See the comment for serialize() above in RPC::XML::request
1574             sub serialize
1575             {
1576 4     4   6 my ($self, $fh) = @_;
1577              
1578 4         3 print {$fh} qq();
  4         93  
1579              
1580 4         5 print {$fh} '';
  4         20  
1581 4 100       19 if ($self->{value}->isa('RPC::XML::fault'))
1582             {
1583             # A fault lacks the params-boilerplate
1584 1         2 $self->{value}->serialize($fh);
1585             }
1586             else
1587             {
1588 3         3 print {$fh} '';
  3         13  
1589 3         7 $self->{value}->serialize($fh);
1590 3         3 print {$fh} '';
  3         13  
1591             }
1592 4         3 print {$fh} '';
  4         16  
1593              
1594 4         6 return;
1595             }
1596              
1597             # Compared to base64, length-calculation here is pretty easy, much like struct
1598             sub length ## no critic (ProhibitBuiltinHomonyms)
1599             {
1600 5     5   6 my $self = shift;
1601              
1602 5         7 my $len = 66 + length $RPC::XML::ENCODING; # All the constant XML present
1603              
1604             # This boilerplate XML is only present when it is NOT a fault
1605 5 100       28 if (! $self->{value}->isa('RPC::XML::fault'))
1606             {
1607 4         6 $len += 47;
1608             }
1609              
1610 5         11 $len += $self->{value}->length;
1611              
1612 5         17 return $len;
1613             }
1614              
1615             1;
1616              
1617             __END__