File Coverage

blib/lib/RPC/XML.pm
Criterion Covered Total %
statement 610 617 98.8
branch 137 144 95.1
condition 70 98 71.4
subroutine 106 106 100.0
pod 0 9 0.0
total 923 974 94.7


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