/[cwmp]/google/lib/SOAP/Lite.pm
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /google/lib/SOAP/Lite.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 20 - (show annotations)
Mon May 21 19:56:30 2007 UTC (17 years, 1 month ago) by dpavlin
File size: 165482 byte(s)
remport namespace which is unspecified
1 # ======================================================================
2 #
3 # Copyright (C) 2000-2005 Paul Kulchenko (paulclinger@yahoo.com)
4 # SOAP::Lite is free software; you can redistribute it
5 # and/or modify it under the same terms as Perl itself.
6 #
7 # $Id: Lite.pm,v 1.43 2006/08/16 14:49:34 byrnereese Exp $
8 #
9 # ======================================================================
10
11 package SOAP::Lite;
12
13 use 5.004;
14 use strict;
15 use vars qw($VERSION);
16 #$VERSION = sprintf("%d.%s", map {s/_//g; $_} q$Name: $ =~ /-(\d+)_([\d_]+)/)
17 # or warn "warning: unspecified/non-released version of ", __PACKAGE__, "\n";
18 $VERSION = '0.69';
19
20 # ======================================================================
21
22 package SOAP::XMLSchemaSOAP1_1::Deserializer;
23
24 sub anyTypeValue { 'ur-type' }
25
26 sub as_boolean { shift; my $value = shift; $value eq '1' || $value eq 'true' ? 1 : $value eq '0' || $value eq 'false' ? 0 : die "Wrong boolean value '$value'\n" }
27 sub as_base64 { shift; require MIME::Base64; MIME::Base64::decode_base64(shift) }
28 sub as_ur_type { $_[1] }
29
30 BEGIN {
31 no strict 'refs';
32 for my $method (qw(
33 string float double decimal timeDuration recurringDuration uriReference
34 integer nonPositiveInteger negativeInteger long int short byte
35 nonNegativeInteger unsignedLong unsignedInt unsignedShort unsignedByte
36 positiveInteger timeInstant time timePeriod date month year century
37 recurringDate recurringDay language
38 )) { my $name = 'as_' . $method; *$name = sub { $_[1] } }
39 }
40
41 # ----------------------------------------------------------------------
42
43 package SOAP::XMLSchemaSOAP1_2::Deserializer;
44
45 sub anyTypeValue { 'anyType' }
46
47 sub as_boolean; *as_boolean = \&SOAP::XMLSchemaSOAP1_1::Deserializer::as_boolean;
48 sub as_base64 { shift; require MIME::Base64; MIME::Base64::decode_base64(shift) }
49 sub as_anyType { $_[1] }
50
51 BEGIN {
52 no strict 'refs';
53 for my $method (qw(
54 string float double decimal dateTime timePeriod gMonth gYearMonth gYear
55 century gMonthDay gDay duration recurringDuration anyURI
56 language integer nonPositiveInteger negativeInteger long int short byte
57 nonNegativeInteger unsignedLong unsignedInt unsignedShort unsignedByte
58 positiveInteger date time dateTime
59 )) { my $name = 'as_' . $method; *$name = sub { $_[1] } }
60 }
61
62 # ----------------------------------------------------------------------
63
64 package SOAP::XMLSchemaApacheSOAP::Deserializer;
65
66 sub as_map {
67 my $self = shift;
68 +{ map { my $hash = ($self->decode_object($_))[1]; ($hash->{key} => $hash->{value}) } @{$_[3] || []} };
69 }
70 sub as_Map; *as_Map = \&as_map;
71
72 # Thank to Kenneth Draper for this contribution
73 sub as_vector {
74 my $self = shift;
75 [ map { scalar(($self->decode_object($_))[1]) } @{$_[3] || []} ];
76 }
77 sub as_Vector; *as_Vector = \&as_vector;
78
79 # ----------------------------------------------------------------------
80
81 package SOAP::XMLSchema::Serializer;
82
83 use vars qw(@ISA);
84
85 sub xmlschemaclass {
86 my $self = shift;
87 return $ISA[0] unless @_;
88 @ISA = (shift);
89 return $self;
90 }
91
92 # ----------------------------------------------------------------------
93
94 package SOAP::XMLSchema1999::Serializer;
95
96 use vars qw(@EXPORT $AUTOLOAD);
97
98 sub AUTOLOAD {
99 local($1,$2);
100 my($package, $method) = $AUTOLOAD =~ m/(?:(.+)::)([^:]+)$/;
101 return if $method eq 'DESTROY';
102 no strict 'refs';
103
104 my $export_var = $package . '::EXPORT';
105 my @export = @$export_var;
106
107 # Removed in 0.69 - this is a total hack. For some reason this is failing
108 # despite not being a fatal error condition.
109 # die "Type '$method' can't be found in a schema class '$package'\n"
110 # unless $method =~ s/^as_// && grep {$_ eq $method} @{$export_var};
111
112 # This was added in its place - it is still a hack, but it performs the
113 # necessary substitution. It just does not die.
114 if ($method =~ s/^as_// && grep {$_ eq $method} @{$export_var}) {
115 # print STDERR "method is now '$method'\n";
116 } else {
117 return;
118 }
119
120 $method =~ s/_/-/; # fix ur-type
121
122 *$AUTOLOAD = sub {
123 my $self = shift;
124 my($value, $name, $type, $attr) = @_;
125 return [$name, {'xsi:type' => "xsd:$method", %$attr}, $value];
126 };
127 goto &$AUTOLOAD;
128 }
129
130 BEGIN {
131 @EXPORT = qw(ur_type
132 float double decimal timeDuration recurringDuration uriReference
133 integer nonPositiveInteger negativeInteger long int short byte
134 nonNegativeInteger unsignedLong unsignedInt unsignedShort unsignedByte
135 positiveInteger timeInstant time timePeriod date month year century
136 recurringDate recurringDay language
137 base64 hex string boolean
138 );
139 # predeclare subs, so ->can check will be positive
140 foreach (@EXPORT) { eval "sub as_$_" }
141 }
142
143 sub nilValue { 'null' }
144 sub anyTypeValue { 'ur-type' }
145
146 sub as_base64 {
147 my $self = shift;
148 my($value, $name, $type, $attr) = @_;
149 require MIME::Base64;
150 return [$name, {'xsi:type' => SOAP::Utils::qualify($self->encprefix => 'base64'), %$attr}, MIME::Base64::encode_base64($value,'')];
151 }
152
153 sub as_hex {
154 my $self = shift;
155 my($value, $name, $type, $attr) = @_;
156 return [$name, {'xsi:type' => 'xsd:hex', %$attr}, join '', map {uc sprintf "%02x", ord} split '', $value];
157 }
158
159 sub as_long {
160 my $self = shift;
161 my($value, $name, $type, $attr) = @_;
162 return [$name, {'xsi:type' => 'xsd:long', %$attr}, $value];
163 }
164
165 sub as_dateTime {
166 my $self = shift;
167 my($value, $name, $type, $attr) = @_;
168 return [$name, {'xsi:type' => 'xsd:dateTime', %$attr}, $value];
169 }
170
171 sub as_string {
172 my $self = shift;
173 my($value, $name, $type, $attr) = @_;
174 die "String value expected instead of @{[ref $value]} reference\n" if ref $value;
175 return [$name, {'xsi:type' => 'xsd:string', %$attr}, SOAP::Utils::encode_data($value)];
176 }
177
178 sub as_undef { $_[1] ? '1' : '0' }
179
180 sub as_boolean {
181 my $self = shift;
182 my($value, $name, $type, $attr) = @_;
183 return [$name, {'xsi:type' => 'xsd:boolean', %$attr}, $value ? '1' : '0'];
184 }
185
186 sub as_float {
187 my $self = shift;
188 my($value, $name, $type, $attr) = @_;
189 return [$name, {'xsi:type' => 'xsd:float', %$attr}, $value ];
190 }
191
192 # ----------------------------------------------------------------------
193
194 package SOAP::XMLSchema1999::Deserializer;
195
196 sub anyTypeValue { 'ur-type' }
197
198 sub as_string; *as_string = \&SOAP::XMLSchemaSOAP1_1::Deserializer::as_string;
199 sub as_boolean; *as_boolean = \&SOAP::XMLSchemaSOAP1_1::Deserializer::as_boolean;
200 sub as_hex { shift; my $value = shift; $value =~ s/([a-zA-Z0-9]{2})/chr oct '0x'.$1/ge; $value }
201 sub as_ur_type { $_[1] }
202 sub as_undef { shift; my $value = shift; $value eq '1' || $value eq 'true' ? 1 : $value eq '0' || $value eq 'false' ? 0 : die "Wrong null/nil value '$value'\n" }
203
204 BEGIN {
205 no strict 'refs';
206 for my $method (qw(
207 float double decimal timeDuration recurringDuration uriReference
208 integer nonPositiveInteger negativeInteger long int short byte
209 nonNegativeInteger unsignedLong unsignedInt unsignedShort unsignedByte
210 positiveInteger timeInstant time timePeriod date month year century
211 recurringDate recurringDay language
212 )) { my $name = 'as_' . $method; *$name = sub { $_[1] } }
213 }
214
215 # ----------------------------------------------------------------------
216
217 package SOAP::XMLSchema2001::Serializer;
218
219 use vars qw(@EXPORT);
220
221 # no more warnings about "used only once"
222 *AUTOLOAD if 0;
223
224 *AUTOLOAD = \&SOAP::XMLSchema1999::Serializer::AUTOLOAD;
225
226 BEGIN {
227 @EXPORT = qw(anyType anySimpleType float double decimal dateTime
228 timePeriod gMonth gYearMonth gYear century
229 gMonthDay gDay duration recurringDuration anyURI
230 language integer nonPositiveInteger negativeInteger
231 long int short byte nonNegativeInteger unsignedLong
232 unsignedInt unsignedShort unsignedByte positiveInteger
233 date time string hex base64 boolean
234 QName
235 );
236 # Add QName to @EXPORT
237 # predeclare subs, so ->can check will be positive
238 foreach (@EXPORT) { eval "sub as_$_" }
239 }
240
241 sub nilValue { 'nil' }
242 sub anyTypeValue { 'anyType' }
243
244 sub as_long; *as_long = \&SOAP::XMLSchema1999::Serializer::as_long;
245 sub as_float; *as_float = \&SOAP::XMLSchema1999::Serializer::as_float;
246 sub as_string; *as_string = \&SOAP::XMLSchema1999::Serializer::as_string;
247 # TODO - QNames still don't work for 2001 schema!
248 sub as_QName; *as_QName = \&SOAP::XMLSchema1999::Serializer::as_string;
249 sub as_hex; *as_hex = \&as_hexBinary;
250 sub as_base64; *as_base64 = \&as_base64Binary;
251 sub as_timeInstant; *as_timeInstant = \&as_dateTime;
252 sub as_undef { $_[1] ? 'true' : 'false' }
253
254 sub as_hexBinary {
255 my $self = shift;
256 my($value, $name, $type, $attr) = @_;
257 return [$name, {'xsi:type' => 'xsd:hexBinary', %$attr}, join '', map {uc sprintf "%02x", ord} split '', $value];
258 }
259
260 sub as_base64Binary {
261 my $self = shift;
262 my($value, $name, $type, $attr) = @_;
263 require MIME::Base64;
264 return [$name, {'xsi:type' => 'xsd:base64Binary', %$attr}, MIME::Base64::encode_base64($value,'')];
265 }
266
267 sub as_boolean {
268 my $self = shift;
269 my($value, $name, $type, $attr) = @_;
270 return [$name, {'xsi:type' => 'xsd:boolean', %$attr}, $value ? 'true' : 'false'];
271 }
272
273 # ----------------------------------------------------------------------
274
275 package SOAP::XMLSchema2001::Deserializer;
276
277 sub anyTypeValue { 'anyType' }
278
279 sub as_string; *as_string = \&SOAP::XMLSchema1999::Deserializer::as_string;
280 sub as_boolean; *as_boolean = \&SOAP::XMLSchemaSOAP1_2::Deserializer::as_boolean;
281 sub as_base64Binary; *as_base64Binary = \&SOAP::XMLSchemaSOAP1_2::Deserializer::as_base64;
282 sub as_hexBinary; *as_hexBinary = \&SOAP::XMLSchema1999::Deserializer::as_hex;
283 sub as_undef; *as_undef = \&SOAP::XMLSchema1999::Deserializer::as_undef;
284
285 BEGIN {
286 no strict 'refs';
287 for my $method (qw(
288 anyType anySimpleType
289 float double decimal dateTime timePeriod gMonth gYearMonth gYear century
290 gMonthDay gDay duration recurringDuration anyURI
291 language integer nonPositiveInteger negativeInteger long int short byte
292 nonNegativeInteger unsignedLong unsignedInt unsignedShort unsignedByte
293 positiveInteger date time dateTime
294 QName
295 )) { my $name = 'as_' . $method; *$name = sub { $_[1] } }
296 # put QName in @EXPORT
297 }
298
299 # ======================================================================
300
301 package SOAP::Constants;
302
303 BEGIN {
304
305 use constant URI_1999_SCHEMA_XSD => "http://www.w3.org/1999/XMLSchema";
306 use constant URI_1999_SCHEMA_XSI => "http://www.w3.org/1999/XMLSchema-instance";
307 use constant URI_2000_SCHEMA_XSD => "http://www.w3.org/2000/10/XMLSchema";
308 use constant URI_2000_SCHEMA_XSI => "http://www.w3.org/2000/10/XMLSchema-instance";
309 use constant URI_2001_SCHEMA_XSD => "http://www.w3.org/2001/XMLSchema";
310 use constant URI_2001_SCHEMA_XSI => "http://www.w3.org/2001/XMLSchema-instance";
311
312 use constant URI_LITERAL_ENC => "";
313 use constant URI_SOAP11_ENC => "http://schemas.xmlsoap.org/soap/encoding/";
314 use constant URI_SOAP11_ENV => "http://schemas.xmlsoap.org/soap/envelope/";
315 use constant URI_SOAP11_NEXT_ACTOR => "http://schemas.xmlsoap.org/soap/actor/next";
316 use constant URI_SOAP12_ENC => "http://www.w3.org/2003/05/soap-encoding";
317 use constant URI_SOAP12_ENV => "http://www.w3.org/2003/05/soap-envelope";
318 use constant URI_SOAP12_NOENC => "http://www.w3.org/2003/05/soap-envelope/encoding/none";
319 use constant URI_SOAP12_NEXT_ACTOR => "http://www.w3.org/2003/05/soap-envelope/role/next";
320
321 # These URIs are not the *current* 1.2 URIs
322 #use constant URI_SOAP12_ENC => "http://www.w3.org/2001/06/*";
323 #use constant URI_SOAP12_ENC => "http://www.w3.org/2001/09/*";
324 #use constant URI_SOAP12_ENC => "http://www.w3.org/2001/12/*";
325 #use constant URI_SOAP12_ENC => "http://www.w3.org/2002/06/*";
326 #use constant URI_SOAP12_ENC => "http://www.w3.org/2002/12/*";
327
328 use vars qw($NSMASK $ELMASK);
329
330 $NSMASK = '[a-zA-Z_:][\w.\-:]*';
331 $ELMASK = '^(?![xX][mM][lL])[a-zA-Z_][\w.\-]*$';
332
333 use vars qw($NEXT_ACTOR $NS_ENV $NS_ENC $NS_APS
334 $FAULT_CLIENT $FAULT_SERVER $FAULT_VERSION_MISMATCH
335 $HTTP_ON_FAULT_CODE $HTTP_ON_SUCCESS_CODE $FAULT_MUST_UNDERSTAND
336 $NS_XSI_ALL $NS_XSI_NILS %XML_SCHEMAS $DEFAULT_XML_SCHEMA
337 $DEFAULT_HTTP_CONTENT_TYPE
338 $SOAP_VERSION %SOAP_VERSIONS $WRONG_VERSION
339 $NS_SL_HEADER $NS_SL_PERLTYPE $PREFIX_ENV $PREFIX_ENC
340 $DO_NOT_USE_XML_PARSER $DO_NOT_CHECK_MUSTUNDERSTAND
341 $DO_NOT_USE_CHARSET $DO_NOT_PROCESS_XML_IN_MIME
342 $DO_NOT_USE_LWP_LENGTH_HACK $DO_NOT_CHECK_CONTENT_TYPE
343 $MAX_CONTENT_SIZE $PATCH_HTTP_KEEPALIVE $DEFAULT_PACKAGER
344 @SUPPORTED_ENCODING_STYLES $OBJS_BY_REF_KEEPALIVE
345 $DEFAULT_CACHE_TTL
346 );
347
348 $FAULT_CLIENT = 'Client';
349 $FAULT_SERVER = 'Server';
350 $FAULT_VERSION_MISMATCH = 'VersionMismatch';
351 $FAULT_MUST_UNDERSTAND = 'MustUnderstand';
352
353 $HTTP_ON_SUCCESS_CODE = 200; # OK
354 $HTTP_ON_FAULT_CODE = 500; # INTERNAL_SERVER_ERROR
355
356 @SUPPORTED_ENCODING_STYLES = ( URI_LITERAL_ENC,URI_SOAP11_ENC,URI_SOAP12_ENC,URI_SOAP12_NOENC );
357
358 $WRONG_VERSION = 'Wrong SOAP version specified.';
359
360 %SOAP_VERSIONS = (
361 ($SOAP_VERSION = 1.1) => {
362 NEXT_ACTOR => URI_SOAP11_NEXT_ACTOR,
363 NS_ENV => URI_SOAP11_ENV,
364 NS_ENC => URI_SOAP11_ENC,
365 DEFAULT_XML_SCHEMA => URI_2001_SCHEMA_XSD,
366 DEFAULT_HTTP_CONTENT_TYPE => 'text/xml',
367 },
368 1.2 => {
369 NEXT_ACTOR => URI_SOAP12_NEXT_ACTOR,
370 NS_ENV => URI_SOAP12_ENV,
371 NS_ENC => URI_SOAP12_ENC,
372 DEFAULT_XML_SCHEMA => URI_2001_SCHEMA_XSD,
373 DEFAULT_HTTP_CONTENT_TYPE => 'application/soap',
374 },
375 );
376
377 # schema namespaces
378 %XML_SCHEMAS = ( # The '()' is necessary to put constants in SCALAR form
379 URI_1999_SCHEMA_XSD() => 'SOAP::XMLSchema1999',
380 URI_2001_SCHEMA_XSD() => 'SOAP::XMLSchema2001',
381 URI_SOAP11_ENC() => 'SOAP::XMLSchemaSOAP1_1',
382 URI_SOAP12_ENC() => 'SOAP::XMLSchemaSOAP1_2',
383 );
384
385 $NS_XSI_ALL = join join('|', map {"$_-instance"} grep {/XMLSchema/} keys %XML_SCHEMAS), '(?:', ')';
386 $NS_XSI_NILS = join join('|', map { my $class = $XML_SCHEMAS{$_} . '::Serializer'; "\{($_)-instance\}" . $class->nilValue
387 } grep {/XMLSchema/} keys %XML_SCHEMAS),
388 '(?:', ')';
389
390 # ApacheSOAP namespaces
391 $NS_APS = 'http://xml.apache.org/xml-soap';
392
393 # SOAP::Lite namespace
394 $NS_SL_HEADER = 'http://namespaces.soaplite.com/header';
395 $NS_SL_PERLTYPE = 'http://namespaces.soaplite.com/perl';
396
397 # default prefixes
398 # $PREFIX_ENV = 'SOAP-ENV';
399 # $PREFIX_ENC = 'SOAP-ENC';
400 $PREFIX_ENV = 'soap';
401 $PREFIX_ENC = 'soapenc';
402
403 # others
404 $DO_NOT_USE_XML_PARSER = 0;
405 $DO_NOT_CHECK_MUSTUNDERSTAND = 0;
406 $DO_NOT_USE_CHARSET = 0;
407 $DO_NOT_PROCESS_XML_IN_MIME = 0;
408 $DO_NOT_USE_LWP_LENGTH_HACK = 0;
409 $DO_NOT_CHECK_CONTENT_TYPE = 0;
410 $PATCH_HTTP_KEEPALIVE = 1;
411 $OBJS_BY_REF_KEEPALIVE = 600; # seconds
412 # TODO - use default packager constant somewhere
413 $DEFAULT_PACKAGER = "SOAP::Packager::MIME";
414 $DEFAULT_CACHE_TTL = 0;
415 }
416
417 # ======================================================================
418
419 package SOAP::Utils;
420
421 sub qualify { $_[1] ? $_[1] =~ /:/ ? $_[1] : join(':', $_[0] || (), $_[1]) : defined $_[1] ? $_[0] : '' }
422 sub overqualify (&$) { for ($_[1]) { &{$_[0]}; s/^:|:$//g } }
423 sub disqualify {
424 (my $qname = shift) =~ s/^($SOAP::Constants::NSMASK?)://;
425 $qname;
426 }
427 sub splitqname { local($1,$2); $_[0] =~ /^(?:([^:]+):)?(.+)$/ ; return ($1,$2) }
428 sub longname { defined $_[0] ? sprintf('{%s}%s', $_[0], $_[1]) : $_[1] }
429 sub splitlongname { local($1,$2); $_[0] =~ /^(?:\{(.*)\})?(.+)$/; return ($1,$2) }
430
431 # Q: why only '&' and '<' are encoded, but not '>'?
432 # A: because it is not required according to XML spec.
433 #
434 # [http://www.w3.org/TR/REC-xml#syntax]
435 # The ampersand character (&) and the left angle bracket (<) may appear in
436 # their literal form only when used as markup delimiters, or within a comment,
437 # a processing instruction, or a CDATA section. If they are needed elsewhere,
438 # they must be escaped using either numeric character references or the
439 # strings "&amp;" and "&lt;" respectively. The right angle bracket (>) may be
440 # represented using the string "&gt;", and must, for compatibility, be
441 # escaped using "&gt;" or a character reference when it appears in the
442 # string "]]>" in content, when that string is not marking the end of a
443 # CDATA section.
444
445 my %encode_attribute = ('&' => '&amp;', '>' => '&gt;', '<' => '&lt;', '"' => '&quot;');
446 sub encode_attribute { (my $e = $_[0]) =~ s/([&<>\"])/$encode_attribute{$1}/g; $e }
447
448 my %encode_data = ('&' => '&amp;', '>' => '&gt;', '<' => '&lt;', "\xd" => '&#xd;');
449 sub encode_data { my $e = $_[0]; if ($e) { $e =~ s/([&<>\015])/$encode_data{$1}/g; $e =~ s/\]\]>/\]\]&gt;/g; } $e }
450
451 # methods for internal tree (SOAP::Deserializer, SOAP::SOM and SOAP::Serializer)
452
453 sub o_qname { $_[0]->[0] }
454 sub o_attr { $_[0]->[1] }
455 sub o_child { ref $_[0]->[2] ? $_[0]->[2] : undef }
456 sub o_chars { ref $_[0]->[2] ? undef : $_[0]->[2] }
457 # $_[0]->[3] is not used. Serializer stores object ID there
458 sub o_value { $_[0]->[4] }
459 sub o_lname { $_[0]->[5] }
460 sub o_lattr { $_[0]->[6] }
461
462 sub format_datetime {
463 my ($s,$m,$h,$D,$M,$Y) = (@_)[0,1,2,3,4,5];
464 my $time = sprintf("%04d-%02d-%02dT%02d:%02d:%02d",($Y+1900),($M+1),$D,$h,$m,$s);
465 return $time;
466 }
467
468 # make bytelength that calculates length in bytes regardless of utf/byte settings
469 # either we can do 'use bytes' or length will count bytes already
470 BEGIN {
471 sub bytelength;
472 eval ( eval('use bytes; 1') # 5.6.0 and later?
473 ? 'sub bytelength { use bytes; length(@_ ? $_[0] : $_) }; 1'
474 : 'sub bytelength { length(@_ ? $_[0] : $_) }; 1'
475 ) or die;
476 }
477
478 # ======================================================================
479
480 package SOAP::Cloneable;
481
482 sub clone {
483 my $self = shift;
484 return unless ref $self && UNIVERSAL::isa($self => __PACKAGE__);
485 my $clone = bless {} => ref($self) || $self;
486 foreach (keys %$self) {
487 my $value = $self->{$_};
488 $clone->{$_} = ref $value && UNIVERSAL::isa($value => __PACKAGE__) ? $value->clone : $value;
489 }
490 $clone;
491 }
492
493 # ======================================================================
494
495 package SOAP::Transport;
496
497 use vars qw($AUTOLOAD @ISA);
498
499 @ISA = qw(SOAP::Cloneable);
500
501 sub DESTROY { SOAP::Trace::objects('()') }
502
503 sub new {
504 my $self = shift;
505 return $self if ref $self;
506 my $class = ref($self) || $self;
507
508 SOAP::Trace::objects('()');
509 return bless {} => $class;
510 }
511
512 sub proxy {
513 my $self = shift->new;
514 # my $self = shift;
515 my $class = ref $self;
516 return $self->{_proxy} unless @_;
517 $_[0] =~ /^(\w+):/ or die "proxy: transport protocol not specified\n";
518 my $protocol = uc "$1"; # untainted now
519 # https: should be done through Transport::HTTP.pm
520 for ($protocol) { s/^HTTPS$/HTTP/ }
521
522 (my $protocol_class = "${class}::$protocol") =~ s/-/_/g;
523 no strict 'refs';
524 unless (defined %{"$protocol_class\::Client::"} && UNIVERSAL::can("$protocol_class\::Client" => 'new')) {
525 eval "require $protocol_class";
526 die "Unsupported protocol '$protocol'\n" if $@ =~ m!^Can\'t locate SOAP/Transport/!;
527 die if $@;
528 }
529 $protocol_class .= "::Client";
530 return $self->{_proxy} = $protocol_class->new(endpoint => shift, @_);
531 }
532
533 sub AUTOLOAD {
534 my $method = substr($AUTOLOAD, rindex($AUTOLOAD, '::') + 2);
535 return if $method eq 'DESTROY';
536
537 no strict 'refs';
538 *$AUTOLOAD = sub { shift->proxy->$method(@_) };
539 goto &$AUTOLOAD;
540 }
541
542 # ======================================================================
543
544 package SOAP::Fault;
545
546 use Carp ();
547
548 use overload fallback => 1, '""' => "stringify";
549
550 sub DESTROY { SOAP::Trace::objects('()') }
551
552 sub new {
553 my $self = shift;
554
555 unless (ref $self) {
556 my $class = ref($self) || $self;
557 $self = bless {} => $class;
558 SOAP::Trace::objects('()');
559 }
560
561 Carp::carp "Odd (wrong?) number of parameters in new()" if $^W && (@_ & 1);
562 while (@_) { my $method = shift; $self->$method(shift) if $self->can($method) }
563
564 return $self;
565 }
566
567 sub stringify {
568 my $self = shift;
569 return join ': ', $self->faultcode, $self->faultstring;
570 }
571
572 sub BEGIN {
573 no strict 'refs';
574 for my $method (qw(faultcode faultstring faultactor faultdetail)) {
575 my $field = '_' . $method;
576 *$method = sub {
577 my $self = UNIVERSAL::isa($_[0] => __PACKAGE__) ? shift->new : __PACKAGE__->new;
578 if (@_) { $self->{$field} = shift; return $self }
579 return $self->{$field};
580 }
581 }
582 *detail = \&faultdetail;
583 }
584
585 # ======================================================================
586
587 package SOAP::Data;
588
589 use vars qw(@ISA @EXPORT_OK);
590 use Exporter;
591 use Carp ();
592
593 @ISA = qw(Exporter);
594 @EXPORT_OK = qw(name type attr value uri);
595
596 sub DESTROY { SOAP::Trace::objects('()') }
597
598 sub new {
599 my $self = shift;
600
601 unless (ref $self) {
602 my $class = ref($self) || $self;
603 $self = bless {_attr => {}, _value => [], _signature => []} => $class;
604 SOAP::Trace::objects('()');
605 }
606
607 Carp::carp "Odd (wrong?) number of parameters in new()" if $^W && (@_ & 1);
608 while (@_) { my $method = shift; $self->$method(shift) if $self->can($method) }
609
610 return $self;
611 }
612
613 sub name {
614 my $self = UNIVERSAL::isa($_[0] => __PACKAGE__) ? shift->new : __PACKAGE__->new;
615 if (@_) {
616 my($name, $uri, $prefix) = shift;
617 if ($name) {
618 ($uri, $name) = SOAP::Utils::splitlongname($name);
619 unless (defined $uri) {
620 ($prefix, $name) = SOAP::Utils::splitqname($name);
621 $self->prefix($prefix) if defined $prefix;
622 } else {
623 $self->uri($uri);
624 }
625 }
626 $self->{_name} = $name;
627
628 $self->value(@_) if @_;
629 return $self;
630 }
631 return $self->{_name};
632 }
633
634 sub attr {
635 my $self = UNIVERSAL::isa($_[0] => __PACKAGE__) ? shift->new : __PACKAGE__->new;
636 if (@_) { $self->{_attr} = shift; $self->value(@_) if @_; return $self }
637 return $self->{_attr};
638 }
639
640 sub type {
641 my $self = UNIVERSAL::isa($_[0] => __PACKAGE__) ? shift->new : __PACKAGE__->new;
642 if (@_) {
643 $self->{_type} = shift;
644 $self->value(@_) if @_;
645 return $self;
646 }
647 if (!defined $self->{_type} && (my @types = grep {/^\{$SOAP::Constants::NS_XSI_ALL}type$/o} keys %{$self->{_attr}})) {
648 $self->{_type} = (SOAP::Utils::splitlongname(delete $self->{_attr}->{shift(@types)}))[1];
649 }
650 return $self->{_type};
651 }
652
653 BEGIN {
654 no strict 'refs';
655 for my $method (qw(root mustUnderstand)) {
656 my $field = '_' . $method;
657 *$method = sub {
658 my $attr = $method eq 'root' ? "{$SOAP::Constants::NS_ENC}$method" : "{$SOAP::Constants::NS_ENV}$method";
659 my $self = UNIVERSAL::isa($_[0] => __PACKAGE__) ? shift->new : __PACKAGE__->new;
660 if (@_) {
661 $self->{_attr}->{$attr} = $self->{$field} = shift() ? 1 : 0;
662 $self->value(@_) if @_;
663 return $self;
664 }
665 $self->{$field} = SOAP::XMLSchemaSOAP1_2::Deserializer->as_boolean($self->{_attr}->{$attr})
666 if !defined $self->{$field} && defined $self->{_attr}->{$attr};
667 return $self->{$field};
668 }
669 }
670 for my $method (qw(actor encodingStyle)) {
671 my $field = '_' . $method;
672 *$method = sub {
673 my $attr = "{$SOAP::Constants::NS_ENV}$method";
674 my $self = UNIVERSAL::isa($_[0] => __PACKAGE__) ? shift->new : __PACKAGE__->new;
675 if (@_) {
676 $self->{_attr}->{$attr} = $self->{$field} = shift;
677 $self->value(@_) if @_;
678 return $self;
679 }
680 $self->{$field} = $self->{_attr}->{$attr}
681 if !defined $self->{$field} && defined $self->{_attr}->{$attr};
682 return $self->{$field};
683 }
684 }
685 }
686
687 sub prefix {
688 my $self = UNIVERSAL::isa($_[0] => __PACKAGE__) ? shift->new : __PACKAGE__->new;
689 return $self->{_prefix} unless @_;
690 $self->{_prefix} = shift;
691 $self->value(@_) if @_;
692 return $self;
693 }
694
695 sub uri {
696 my $self = UNIVERSAL::isa($_[0] => __PACKAGE__) ? shift->new : __PACKAGE__->new;
697 return $self->{_uri} unless @_;
698 my $uri = $self->{_uri} = shift;
699 warn "Usage of '::' in URI ($uri) deprecated. Use '/' instead\n"
700 if defined $uri && $^W && $uri =~ /::/;
701 $self->value(@_) if @_;
702 return $self;
703 }
704
705 sub set_value {
706 my $self = UNIVERSAL::isa($_[0] => __PACKAGE__) ? shift->new : __PACKAGE__->new;
707 $self->{_value} = [@_];
708 return $self;
709 }
710
711 sub value {
712 my $self = UNIVERSAL::isa($_[0] => __PACKAGE__) ? shift->new : __PACKAGE__->new;
713 @_ ? ($self->set_value(@_), return $self)
714 : wantarray ? return @{$self->{_value}} : return $self->{_value}->[0];
715 }
716
717 sub signature {
718 my $self = UNIVERSAL::isa($_[0] => __PACKAGE__) ? shift->new : __PACKAGE__->new;
719 @_ ? ($self->{_signature} = shift, return $self) : (return $self->{_signature});
720 }
721
722 # ======================================================================
723
724 package SOAP::Header;
725
726 use vars qw(@ISA);
727 @ISA = qw(SOAP::Data);
728
729 # ======================================================================
730
731 package SOAP::Serializer;
732
733 use Carp ();
734 use vars qw(@ISA);
735
736 @ISA = qw(SOAP::Cloneable SOAP::XMLSchema::Serializer);
737
738 BEGIN {
739 # namespaces and anonymous data structures
740 my $ns = 0;
741 my $name = 0;
742 my $prefix = 'c-';
743 sub gen_ns { 'namesp' . ++$ns }
744 sub gen_name { join '', $prefix, 'gensym', ++$name }
745 sub prefix { $prefix =~ s/^[^\-]+-/$_[1]-/; $_[0]; }
746 }
747
748 sub BEGIN {
749 no strict 'refs';
750 for my $method (qw(readable level seen autotype typelookup attr maptype
751 namespaces multirefinplace encoding signature
752 on_nonserialized context
753 ns_uri ns_prefix use_default_ns)) {
754 my $field = '_' . $method;
755 *$method = sub {
756 my $self = shift->new;
757 @_ ? ($self->{$field} = shift, return $self) : return $self->{$field};
758 }
759 }
760 for my $method (qw(method fault freeform)) { # aliases for envelope
761 *$method = sub { shift->envelope($method => @_) }
762 }
763 # Is this necessary? Seems like work for nothing when a user could just use
764 # SOAP::Utils directly.
765 # for my $method (qw(qualify overqualify disqualify)) { # import from SOAP::Utils
766 # *$method = \&{'SOAP::Utils::'.$method};
767 # }
768 }
769
770 sub DESTROY { SOAP::Trace::objects('()') }
771
772 sub new {
773 my $self = shift;
774 return $self if ref $self;
775 unless (ref $self) {
776 my $class = ref($self) || $self;
777 $self = bless {
778 _level => 0,
779 _autotype => 1,
780 _readable => 0,
781 _ns_uri => '',
782 _ns_prefix => '',
783 _use_default_ns => 1,
784 _multirefinplace => 0,
785 _seen => {},
786 _typelookup => {
787 'base64Binary' =>
788 [10, sub {$_[0] =~ /[^\x09\x0a\x0d\x20-\x7f]/}, 'as_base64Binary'],
789 'int' =>
790 [20, sub {$_[0] =~ /^[+-]?(\d+)$/ && $1 <= 2147483648 && $1 >= -2147483648; }, 'as_int'],
791 'long' =>
792 [25, sub {$_[0] =~ /^[+-]?(\d+)$/ && $1 <= 9223372036854775807;}, 'as_long'],
793 'float' =>
794 [30, sub {$_[0] =~ /^(-?(?:\d+(?:\.\d*)?|\.\d+|NaN|INF)|([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?)$/}, 'as_float'],
795 'gMonth' =>
796 [35, sub { $_[0] =~ /^--\d\d--(-\d\d:\d\d)?$/; }, 'as_gMonth'],
797 'gDay' =>
798 [40, sub { $_[0] =~ /^---\d\d(-\d\d:\d\d)?$/; }, 'as_gDay'],
799 'gYear' =>
800 [45, sub { $_[0] =~ /^-?\d\d\d\d(-\d\d:\d\d)?$/; }, 'as_gYear'],
801 'gMonthDay' =>
802 [50, sub { $_[0] =~ /^-\d\d-\d\d(-\d\d:\d\d)?$/; }, 'as_gMonthDay'],
803 'gYearMonth' =>
804 [55, sub { $_[0] =~ /^-?\d\d\d\d-\d\d(Z|([+-]\d\d:\d\d))?$/; }, 'as_gYearMonth'],
805 'date' =>
806 [60, sub { $_[0] =~ /^-?\d\d\d\d-\d\d-\d\d(Z|([+-]\d\d:\d\d))?$/; }, 'as_date'],
807 'time' =>
808 [70, sub { $_[0] =~ /^\d\d:\d\d:\d\d(\.\d\d\d)?(Z|([+-]\d\d:\d\d))?$/; }, 'as_time'],
809 'dateTime' =>
810 [75, sub { $_[0] =~ /^\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\d(\.\d\d\d)?(Z|([+-]\d\d:\d\d))?$/; }, 'as_dateTime'],
811 'duration' =>
812 [80, sub { $_[0] =~ /^-?P(\d+Y)?(\d+M)?(\dD)?(T(\d+H)?(\d+M)?(\d+S)?)?$/; }, 'as_duration'],
813 'boolean' =>
814 [90, sub { $_[0] =~ /^(true|false)$/i; }, 'as_boolean'],
815 'anyURI' =>
816 [95, sub { $_[0] =~ /^(urn:)|(http:\/\/)/i; }, 'as_anyURI'],
817 'string' =>
818 [100, sub {1}, 'as_string'],
819 },
820 _encoding => 'UTF-8',
821 _objectstack => {},
822 _signature => [],
823 _maptype => {},
824 _on_nonserialized => sub {Carp::carp "Cannot marshall @{[ref shift]} reference" if $^W; return},
825 _encodingStyle => $SOAP::Constants::NS_ENC,
826 _attr => {
827 "{$SOAP::Constants::NS_ENV}encodingStyle" => $SOAP::Constants::NS_ENC,
828 },
829 _namespaces => {
830 # $SOAP::Constants::NS_ENC => $SOAP::Constants::PREFIX_ENC,
831 # $SOAP::Constants::PREFIX_ENV ? ($SOAP::Constants::NS_ENV => $SOAP::Constants::PREFIX_ENV) : (),
832 },
833 _soapversion => SOAP::Lite->soapversion,
834 } => $class;
835 $self->register_ns($SOAP::Constants::NS_ENC,$SOAP::Constants::PREFIX_ENC);
836 $self->register_ns($SOAP::Constants::NS_ENV,$SOAP::Constants::PREFIX_ENV)
837 if $SOAP::Constants::PREFIX_ENV;
838 $self->xmlschema($SOAP::Constants::DEFAULT_XML_SCHEMA);
839 SOAP::Trace::objects('()');
840 }
841
842 Carp::carp "Odd (wrong?) number of parameters in new()" if $^W && (@_ & 1);
843 while (@_) { my $method = shift; $self->$method(shift) if $self->can($method) }
844
845 return $self;
846 }
847
848 sub ns {
849 my $self = shift->new;
850 if (@_) {
851 my ($u,$p) = @_;
852 my $prefix;
853 if ($p) {
854 $prefix = $p;
855 } elsif (!$p && !($prefix = $self->find_prefix($u))) {
856 $prefix = gen_ns;
857 }
858 $self->{'_ns_uri'} = $u;
859 $self->{'_ns_prefix'} = $prefix;
860 $self->{'_use_default_ns'} = 0;
861 # $self->register_ns($u,$prefix);
862 $self->{'_namespaces'}->{$u} = $prefix;
863 return $self;
864 }
865 return $self->{'_ns_uri'};
866 }
867
868 sub default_ns {
869 my $self = shift->new;
870 if (@_) {
871 my ($u) = @_;
872 $self->{'_ns_uri'} = $u;
873 $self->{'_ns_prefix'} = '';
874 $self->{'_use_default_ns'} = 1;
875 return $self;
876 }
877 return $self->{'_ns_uri'};
878 }
879
880 sub use_prefix {
881 my $self = shift->new;
882 warn 'use_prefix has been deprecated. if you wish to turn off or on the use of a default namespace, then please use either ns(uri) or default_ns(uri)';
883 if (@_) {
884 my $use = shift;
885 $self->{'_use_default_ns'} = !$use || 0;
886 return $self;
887 } else {
888 return $self->{'_use_default_ns'};
889 }
890 }
891
892 # old
893 # sub uri {
894 # my $self = shift->new;
895 # if (@_) {
896 # $self->{'_uri'} = shift;
897 # $self->register_ns($self->{'_uri'}) if (!$self->use_prefix);
898 # return $self;
899 # }
900 # return $self->{'_uri'};
901 # }
902
903 sub uri {
904 my $self = shift->new;
905 # warn 'uri has been deprecated. if you wish to set the namespace for the request, then please use either ns(uri) or default_ns(uri)';
906 if (@_) {
907 my $ns = shift;
908 if ($self->{_use_default_ns}) {
909 $self->default_ns($ns);
910 } else {
911 $self->ns($ns);
912 }
913 # $self->{'_ns_uri'} = $ns;
914 # $self->register_ns($self->{'_ns_uri'}) if (!$self->{_use_default_ns});
915 return $self;
916 }
917 return $self->{'_ns_uri'};
918 }
919
920 sub encodingStyle {
921 my $self = shift;
922 return $self->{'_encodingStyle'} unless @_;
923 my $cur_style = $self->{'_encodingStyle'};
924 delete($self->{'_namespaces'}->{$cur_style});
925 my $new_style = shift;
926 if ($new_style eq "") {
927 delete($self->{'_attr'}->{"{$SOAP::Constants::NS_ENV}encodingStyle"});
928 } else {
929 $self->{'_attr'}->{"{$SOAP::Constants::NS_ENV}encodingStyle"} = $new_style;
930 $self->{'_namespaces'}->{$new_style} = $SOAP::Constants::PREFIX_ENC;
931 }
932 }
933
934 # TODO - changing SOAP version can affect previously set encodingStyle
935 sub soapversion {
936 my $self = shift;
937 return $self->{_soapversion} unless @_;
938 return $self if $self->{_soapversion} eq SOAP::Lite->soapversion;
939 $self->{_soapversion} = shift;
940
941 $self->attr({
942 "{$SOAP::Constants::NS_ENV}encodingStyle" => $SOAP::Constants::NS_ENC,
943 });
944 $self->namespaces({
945 $SOAP::Constants::NS_ENC => $SOAP::Constants::PREFIX_ENC,
946 $SOAP::Constants::PREFIX_ENV ? ($SOAP::Constants::NS_ENV => $SOAP::Constants::PREFIX_ENV) : (),
947 });
948 $self->xmlschema($SOAP::Constants::DEFAULT_XML_SCHEMA);
949
950 $self;
951 }
952
953 sub xmlschema {
954 my $self = shift->new;
955 return $self->{_xmlschema} unless @_;
956
957 my @schema;
958 if ($_[0]) {
959 @schema = grep {/XMLSchema/ && /$_[0]/} keys %SOAP::Constants::XML_SCHEMAS;
960 Carp::croak "More than one schema match parameter '$_[0]': @{[join ', ', @schema]}" if @schema > 1;
961 Carp::croak "No schema match parameter '$_[0]'" if @schema != 1;
962 }
963
964 # do nothing if current schema is the same as new
965 return $self if $self->{_xmlschema} && $self->{_xmlschema} eq $schema[0];
966
967 my $ns = $self->namespaces;
968
969 # delete current schema from namespaces
970 if (my $schema = $self->{_xmlschema}) {
971 delete $ns->{$schema};
972 delete $ns->{"$schema-instance"};
973 }
974
975 # add new schema into namespaces
976 if (my $schema = $self->{_xmlschema} = shift @schema) {
977 $ns->{$schema} = 'xsd';
978 $ns->{"$schema-instance"} = 'xsi';
979 }
980
981 # and here is the class serializer should work with
982 my $class = exists $SOAP::Constants::XML_SCHEMAS{$self->{_xmlschema}} ?
983 $SOAP::Constants::XML_SCHEMAS{$self->{_xmlschema}} . '::Serializer' : $self;
984
985 $self->xmlschemaclass($class);
986
987 return $self;
988 }
989
990 sub envprefix {
991 my $self = shift->new;
992 return $self->namespaces->{$SOAP::Constants::NS_ENV} unless @_;
993 $self->namespaces->{$SOAP::Constants::NS_ENV} = shift;
994 return $self;
995 }
996
997 sub encprefix {
998 my $self = shift->new;
999 return $self->namespaces->{$SOAP::Constants::NS_ENC} unless @_;
1000 $self->namespaces->{$SOAP::Constants::NS_ENC} = shift;
1001 return $self;
1002 }
1003
1004 sub gen_id { sprintf "%U", $_[1] }
1005
1006 sub multiref_object {
1007 my $self = shift;
1008 my $object = shift;
1009 my $id = $self->gen_id($object);
1010 my $seen = $self->seen;
1011 $seen->{$id}->{count}++;
1012 $seen->{$id}->{multiref} ||= $seen->{$id}->{count} > 1;
1013 $seen->{$id}->{value} = $object;
1014 $seen->{$id}->{recursive} ||= 0;
1015 return $id;
1016 }
1017
1018 sub recursive_object {
1019 my $self = shift;
1020 $self->seen->{$self->gen_id(shift)}->{recursive} = 1;
1021 }
1022
1023 sub is_href {
1024 my $self = shift;
1025 my $seen = $self->seen->{shift || return} or return;
1026 return 1 if $seen->{id};
1027 return $seen->{multiref} &&
1028 !($seen->{id} = (shift ||
1029 $seen->{recursive} ||
1030 $seen->{multiref} && $self->multirefinplace));
1031 }
1032
1033 sub multiref_anchor {
1034 my $seen = shift->seen->{my $id = shift || return undef};
1035 return $seen->{multiref} ? "ref-$id" : undef;
1036 }
1037
1038 sub encode_multirefs {
1039 my $self = shift;
1040 return if $self->multirefinplace;
1041
1042 my $seen = $self->seen;
1043 map { $_->[1]->{_id} = 1; $_
1044 } map { $self->encode_object($seen->{$_}->{value})
1045 } grep { $seen->{$_}->{multiref} && !$seen->{$_}->{recursive}
1046 } keys %$seen;
1047 }
1048
1049 sub maptypetouri {
1050 my($self, $type, $simple) = @_;
1051
1052 return $type unless defined $type;
1053 my($prefix, $name) = SOAP::Utils::splitqname($type);
1054
1055 unless (defined $prefix) {
1056 $name =~ s/__|\./::/g;
1057 $self->maptype->{$name} = $simple
1058 ? die "Schema/namespace for type '$type' is not specified\n"
1059 : $SOAP::Constants::NS_SL_PERLTYPE
1060 unless exists $self->maptype->{$name};
1061 $type = $self->maptype->{$name}
1062 ? SOAP::Utils::qualify($self->namespaces->{$self->maptype->{$name}} ||= gen_ns, $type)
1063 : undef;
1064 }
1065 return $type;
1066 }
1067
1068 sub encode_object {
1069 my($self, $object, $name, $type, $attr) = @_;
1070
1071 $attr ||= {};
1072
1073 return $self->encode_scalar($object, $name, $type, $attr) unless ref $object;
1074
1075 my $id = $self->multiref_object($object);
1076
1077 use vars '%objectstack'; # we'll play with symbol table
1078 local %objectstack = %objectstack; # want to see objects ONLY in the current tree
1079 # did we see this object in current tree? Seems to be recursive refs
1080 $self->recursive_object($object) if ++$objectstack{$id} > 1;
1081 # return if we already saw it twice. It should be already properly serialized
1082 return if $objectstack{$id} > 2;
1083
1084 if (UNIVERSAL::isa($object => 'SOAP::Data')) {
1085 # use $object->SOAP::Data:: to enable overriding name() and others in inherited classes
1086 $object->SOAP::Data::name($name) unless defined $object->SOAP::Data::name;
1087
1088 # apply ->uri() and ->prefix() which can modify name and attributes of
1089 # element, but do not modify SOAP::Data itself
1090 my($name, $attr) = $self->fixattrs($object);
1091 $attr = $self->attrstoqname($attr);
1092
1093 my @realvalues = $object->SOAP::Data::value;
1094 return [$name || gen_name, $attr] unless @realvalues;
1095
1096 my $method = "as_" . ($object->SOAP::Data::type || '-'); # dummy type if not defined
1097 # try to call method specified for this type
1098 my @values = map {
1099 # store null/nil attribute if value is undef
1100 local $attr->{SOAP::Utils::qualify(xsi => $self->xmlschemaclass->nilValue)} = $self->xmlschemaclass->as_undef(1)
1101 unless defined;
1102 $self->can($method) && $self->$method($_, $name || gen_name, $object->SOAP::Data::type, $attr)
1103 || $self->typecast($_, $name || gen_name, $object->SOAP::Data::type, $attr)
1104 || $self->encode_object($_, $name, $object->SOAP::Data::type, $attr)
1105 } @realvalues;
1106 $object->SOAP::Data::signature([map {join $;, $_->[0], SOAP::Utils::disqualify($_->[1]->{'xsi:type'} || '')} @values]) if @values;
1107 return wantarray ? @values : $values[0];
1108 }
1109
1110 my $class = ref $object;
1111
1112 if ($class !~ /^(?:SCALAR|ARRAY|HASH|REF)$/o) {
1113 # we could also check for CODE|GLOB|LVALUE, but we cannot serialize
1114 # them anyway, so they'll be cought by check below
1115 $class =~ s/::/__/g;
1116
1117 $name = $class if !defined $name;
1118 $type = $class if !defined $type && $self->autotype;
1119
1120 my $method = 'as_' . $class;
1121 if ($self->can($method)) {
1122 my $encoded = $self->$method($object, $name, $type, $attr);
1123 return $encoded if ref $encoded;
1124 # return only if handled, otherwise handle with default handlers
1125 }
1126 }
1127
1128 if (UNIVERSAL::isa($object => 'REF') || UNIVERSAL::isa($object => 'SCALAR')) {
1129 return $self->encode_scalar($object, $name, $type, $attr);
1130 } elsif (UNIVERSAL::isa($object => 'ARRAY')) {
1131 # Added in SOAP::Lite 0.65_6 to fix an XMLRPC bug
1132 return $self->encodingStyle eq "" || ref $self eq 'XMLRPC::Serializer' ?
1133 $self->encode_array($object, $name, $type, $attr) :
1134 $self->encode_literal_array($object, $name, $type, $attr);
1135 } elsif (UNIVERSAL::isa($object => 'HASH')) {
1136 return $self->encode_hash($object, $name, $type, $attr);
1137 } else {
1138 return $self->on_nonserialized->($object);
1139 }
1140 }
1141
1142 sub encode_scalar {
1143 my($self, $value, $name, $type, $attr) = @_;
1144 $name ||= gen_name;
1145
1146 my $schemaclass = $self->xmlschemaclass;
1147
1148 # null reference
1149 return [$name, {%$attr, SOAP::Utils::qualify(xsi => $schemaclass->nilValue) => $schemaclass->as_undef(1)}] unless defined $value;
1150
1151 # object reference
1152 return [$name, {'xsi:type' => $self->maptypetouri($type), %$attr}, [$self->encode_object($$value)], $self->gen_id($value)] if ref $value;
1153
1154 # autodefined type
1155 if ($self->autotype) {
1156 my $lookup = $self->typelookup;
1157 for (sort {$lookup->{$a}->[0] <=> $lookup->{$b}->[0]} keys %$lookup) {
1158 my $method = $lookup->{$_}->[2];
1159 return $self->can($method) && $self->$method($value, $name, $type, $attr)
1160 || $method->($value, $name, $type, $attr)
1161 if $lookup->{$_}->[1]->($value);
1162 }
1163 }
1164
1165 # invariant
1166 return [$name, $attr, $value];
1167 }
1168
1169 sub encode_array {
1170 my($self, $array, $name, $type, $attr) = @_;
1171 my $items = 'item';
1172
1173 # TODO: add support for multidimensional, partially transmitted and sparse arrays
1174 my @items = map {$self->encode_object($_, $items)} @$array;
1175 my $num = @items;
1176 my($arraytype, %types) = '-';
1177 for (@items) { $arraytype = $_->[1]->{'xsi:type'} || '-'; $types{$arraytype}++ }
1178 $arraytype = sprintf "%s\[$num]", keys %types > 1 || $arraytype eq '-' ? SOAP::Utils::qualify(xsd => $self->xmlschemaclass->anyTypeValue) : $arraytype;
1179
1180 $type = SOAP::Utils::qualify($self->encprefix => 'Array') if $self->autotype && !defined $type;
1181
1182 return [$name || SOAP::Utils::qualify($self->encprefix => 'Array'),
1183 {SOAP::Utils::qualify($self->encprefix => 'arrayType') => $arraytype, 'xsi:type' => $self->maptypetouri($type), %$attr},
1184 [@items],
1185 $self->gen_id($array)
1186 ];
1187 }
1188
1189 # Will encode arrays using doc-literal style
1190 sub encode_literal_array {
1191 my($self, $array, $name, $type, $attr) = @_;
1192
1193 # If typing is disabled, just serialize each of the array items
1194 # with no type information, each using the specified name,
1195 # and do not crete a wrapper array tag.
1196 if (!$self->autotype) {
1197 $name ||= gen_name;
1198 return map {$self->encode_object($_, $name)} @$array;
1199 }
1200
1201 my $items = 'item';
1202
1203 # TODO: add support for multidimensional, partially transmitted and sparse arrays
1204 my @items = map {$self->encode_object($_, $items)} @$array;
1205 my $num = @items;
1206 my($arraytype, %types) = '-';
1207 for (@items) { $arraytype = $_->[1]->{'xsi:type'} || '-'; $types{$arraytype}++ }
1208 $arraytype = sprintf "%s\[$num]", keys %types > 1 || $arraytype eq '-' ? SOAP::Utils::qualify(xsd => $self->xmlschemaclass->anyTypeValue) : $arraytype;
1209 $type = SOAP::Utils::qualify($self->encprefix => 'Array') if !defined $type;
1210 return [$name || SOAP::Utils::qualify($self->encprefix => 'Array'),
1211 {SOAP::Utils::qualify($self->encprefix => 'arrayType') => $arraytype, 'xsi:type' => $self->maptypetouri($type), %$attr},
1212 [@items],
1213 $self->gen_id($array)
1214 ];
1215 }
1216
1217 sub encode_hash_old {
1218 #sub encode_hash {
1219 my($self, $hash, $name, $type, $attr) = @_;
1220
1221 if ($self->autotype && grep {!/$SOAP::Constants::ELMASK/o} keys %$hash) {
1222 warn qq!Cannot encode @{[$name ? "'$name'" : 'unnamed']} element as 'hash'. Will be encoded as 'map' instead\n! if $^W;
1223 return $self->as_map($hash, $name || gen_name, $type, $attr);
1224 }
1225
1226 $type = 'SOAPStruct'
1227 if $self->autotype && !defined($type) && exists $self->maptype->{SOAPStruct};
1228 return [$name || gen_name,
1229 {'xsi:type' => $self->maptypetouri($type), %$attr},
1230 [map {$self->encode_object($hash->{$_}, $_)} keys %$hash],
1231 $self->gen_id($hash)
1232 ];
1233 }
1234
1235 #sub encode_hash_lexi_patch {
1236 sub encode_hash {
1237 my($self, $hash, $name, $type, $attr) = @_;
1238
1239 if ($self->autotype && grep {!/$SOAP::Constants::ELMASK/o} keys %$hash) {
1240 warn qq!Cannot encode @{[$name ? "'$name'" : 'unnamed']} element as 'hash'. Will be encoded as 'map' instead\n! if $^W;
1241 return $self->as_map($hash, $name || gen_name, $type, $attr);
1242 }
1243
1244 $type = 'SOAPStruct'
1245 if $self->autotype && !defined($type) && exists $self->maptype->{SOAPStruct};
1246 return [$name || gen_name,
1247 $self->autotype ? {'xsi:type' => $self->maptypetouri($type), %$attr} : { %$attr },
1248 [map {$self->encode_object($hash->{$_}, $_)} keys %$hash],
1249 $self->gen_id($hash)
1250 ];
1251 }
1252
1253 sub as_ordered_hash {
1254 my $self = shift;
1255 my ($value, $name, $type, $attr) = @_;
1256 die "Not an ARRAY reference for 'ordered_hash' type" unless UNIVERSAL::isa($value => 'ARRAY');
1257 return [ $name, $attr,
1258 [map{$self->encode_object(@{$value}[2*$_+1,2*$_])} 0..$#$value/2 ],
1259 $self->gen_id($value)
1260 ];
1261 }
1262
1263 sub as_map {
1264 my $self = shift;
1265 my($value, $name, $type, $attr) = @_;
1266 die "Not a HASH reference for 'map' type" unless UNIVERSAL::isa($value => 'HASH');
1267 my $prefix = ($self->namespaces->{$SOAP::Constants::NS_APS} ||= 'apachens');
1268 my @items = map {$self->encode_object(SOAP::Data->type(ordered_hash => [key => $_, value => $value->{$_}]), 'item', '')} keys %$value;
1269 return [$name, {'xsi:type' => "$prefix:Map", %$attr}, [@items], $self->gen_id($value)];
1270 }
1271
1272 sub as_xml {
1273 my $self = shift;
1274 my($value, $name, $type, $attr) = @_;
1275 return [$name, {'_xml' => 1}, $value];
1276 }
1277
1278 sub typecast {
1279 my $self = shift;
1280 my($value, $name, $type, $attr) = @_;
1281 return if ref $value; # skip complex object, caller knows how to deal with it
1282 return if $self->autotype && !defined $type; # we don't know, autotype knows
1283 return [$name,
1284 {(defined $type && $type gt '' ? ('xsi:type' => $self->maptypetouri($type, 'simple type')) : ()), %$attr},
1285 $value
1286 ];
1287 }
1288
1289 sub register_ns {
1290 my $self = shift->new;
1291 # my $self = shift;
1292 my ($ns,$prefix) = @_;
1293 # print STDERR ">> registering $prefix\n" if $prefix;
1294 $prefix = gen_ns if !$prefix;
1295 $self->{'_namespaces'}->{$ns} = $prefix if $ns;
1296 }
1297
1298 sub find_prefix {
1299 my $self = shift;
1300 my ($ns) = @_;
1301 foreach my $this_ns (keys %{$self->{'_namespaces'}}) {
1302 return $self->{'_namespaces'}->{$this_ns} if ($ns eq $this_ns);
1303 }
1304 }
1305
1306 sub fixattrs {
1307 my $self = shift;
1308 my $data = shift;
1309 my($name, $attr) = ($data->SOAP::Data::name, {%{$data->SOAP::Data::attr}});
1310 my($xmlns, $prefix) = ($data->uri, $data->prefix);
1311 unless (defined($xmlns) || defined($prefix)) {
1312 $self->register_ns($xmlns,$prefix) unless ($self->use_default_ns);
1313 return ($name, $attr);
1314 }
1315 $name ||= gen_name; # local name
1316 $prefix = gen_ns if !defined $prefix && $xmlns gt '';
1317 $prefix = '' if defined $xmlns && $xmlns eq '' ||
1318 defined $prefix && $prefix eq '';
1319
1320 $attr->{join ':', xmlns => $prefix || ()} = $xmlns if defined $xmlns;
1321 $name = join ':', $prefix, $name if $prefix;
1322 $self->register_ns($xmlns,$prefix) unless ($self->use_default_ns);
1323 return ($name, $attr);
1324 }
1325
1326 sub toqname {
1327 my $self = shift;
1328 my $long = shift;
1329
1330 return $long unless $long =~ /^\{(.*)\}(.+)$/;
1331 return SOAP::Utils::qualify $self->namespaces->{$1} ||= gen_ns, $2;
1332 }
1333
1334 sub attrstoqname {
1335 my $self = shift;
1336 my $attrs = shift;
1337
1338 return {
1339 map { /^\{(.*)\}(.+)$/
1340 ? ($self->toqname($_) => $2 eq 'type' || $2 eq 'arrayType' ? $self->toqname($attrs->{$_}) : $attrs->{$_})
1341 : ($_ => $attrs->{$_})
1342 } keys %$attrs
1343 };
1344 }
1345
1346 sub tag {
1347 my $self = shift;
1348 my($tag, $attrs, @values) = @_;
1349 my $value = join '', @values;
1350 my $level = $self->level;
1351 my $indent = $self->readable ? ' ' x (($level-1)*2) : '';
1352
1353 # check for special attribute
1354 return "$indent$value" if exists $attrs->{_xml} && delete $attrs->{_xml};
1355
1356 die "Element '$tag' can't be allowed in valid XML message. Died."
1357 if $tag !~ /^(?![xX][mM][lL])$SOAP::Constants::NSMASK$/o;
1358
1359 my $prolog = $self->readable ? "\n" : "";
1360 my $epilog = $self->readable ? "\n" : "";
1361 my $tagjoiner = " ";
1362 if ($level == 1) {
1363 my $namespaces = $self->namespaces;
1364 foreach (keys %$namespaces) { $attrs->{SOAP::Utils::qualify(xmlns => $namespaces->{$_})} = $_ }
1365 $prolog = qq!<?xml version="1.0" encoding="@{[$self->encoding]}"?>!
1366 if defined $self->encoding;
1367 $prolog .= "\n" if $self->readable;
1368 $tagjoiner = " \n".(' ' x (($level+1) * 2)) if $self->readable;
1369 }
1370 my $tagattrs = join($tagjoiner, '', map { sprintf '%s="%s"', $_, SOAP::Utils::encode_attribute($attrs->{$_}) }
1371 grep { $_ && defined $attrs->{$_} && ($_ ne 'xsi:type' || $attrs->{$_} ne '')
1372 } keys %$attrs);
1373
1374 if ($value gt '') {
1375 return sprintf("$prolog$indent<%s%s>%s%s</%s>$epilog",$tag,$tagattrs,$value,($value =~ /^\s*</ ? $indent : ""),$tag);
1376 } else {
1377 return sprintf("$prolog$indent<%s%s />$epilog$indent",$tag,$tagattrs);
1378 }
1379 }
1380
1381 sub xmlize {
1382 my $self = shift;
1383 my($name, $attrs, $values, $id) = @{+shift};
1384 $attrs ||= {};
1385
1386 local $self->{_level} = $self->{_level} + 1;
1387 return $self->tag($name, $attrs)
1388 unless defined $values;
1389 return $self->tag($name, $attrs, $values)
1390 unless UNIVERSAL::isa($values => 'ARRAY');
1391 return $self->tag($name, {%$attrs, href => '#'.$self->multiref_anchor($id)})
1392 if $self->is_href($id, delete($attrs->{_id}));
1393 return $self->tag($name,
1394 {%$attrs, id => $self->multiref_anchor($id)},
1395 map {$self->xmlize($_)} @$values);
1396 }
1397
1398 sub uriformethod {
1399 my $self = shift;
1400
1401 my $method_is_data = ref $_[0] && UNIVERSAL::isa($_[0] => 'SOAP::Data');
1402
1403 # drop prefrix from method that could be string or SOAP::Data object
1404 my($prefix, $method) = $method_is_data
1405 ? ($_[0]->prefix, $_[0]->name)
1406 : SOAP::Utils::splitqname($_[0]);
1407
1408 my $attr = {reverse %{$self->namespaces}};
1409 # try to define namespace that could be stored as
1410 # a) method is SOAP::Data
1411 # ? attribute in method's element as xmlns= or xmlns:${prefix}=
1412 # : uri
1413 # b) attribute in Envelope element as xmlns= or xmlns:${prefix}=
1414 # c) no prefix or prefix equal serializer->envprefix
1415 # ? '', but see coment below
1416 # : die with error message
1417 my $uri = $method_is_data
1418 ? ref $_[0]->attr && ($_[0]->attr->{$prefix ? "xmlns:$prefix" : 'xmlns'} || $_[0]->uri)
1419 : $self->uri;
1420
1421 defined $uri or $uri = $attr->{$prefix || ''};
1422
1423 defined $uri or $uri = !$prefix || $prefix eq $self->envprefix
1424 # still in doubts what should namespace be in this case
1425 # but will keep it like this for now and be compatible with our server
1426 ? ( $method_is_data && $^W && warn("URI is not provided as an attribute for method ($method)\n"),
1427 ''
1428 )
1429 : die "Can't find namespace for method ($prefix:$method)\n";
1430
1431 return ($uri, $method);
1432 }
1433
1434 sub serialize { SOAP::Trace::trace('()');
1435 my $self = shift->new;
1436 @_ == 1 or Carp::croak "serialize() method accepts one parameter";
1437
1438 $self->seen({}); # reinitialize multiref table
1439 my($encoded) = $self->encode_object($_[0]);
1440
1441 # now encode multirefs if any
1442 # v -------------- subelements of Envelope
1443 push(@{$encoded->[2]}, $self->encode_multirefs) if ref $encoded->[2];
1444 return $self->xmlize($encoded);
1445 }
1446
1447 sub envelope {
1448 SOAP::Trace::trace('()');
1449 my $self = shift->new;
1450 my $type = shift;
1451 my(@parameters, @header);
1452 for (@_) {
1453 # Find all the SOAP Headers
1454 if (defined($_) && ref($_) && UNIVERSAL::isa($_ => 'SOAP::Header')) {
1455 push(@header, $_);
1456
1457 # Find all the SOAP Message Parts (attachments)
1458 } elsif (defined($_) && ref($_) &&
1459 $self->context && $self->context->packager->is_supported_part($_)) {
1460 $self->context->packager->push_part($_);
1461
1462 # Find all the SOAP Body elements
1463 } else {
1464 push(@parameters, $_);
1465 }
1466 }
1467 my $header = @header ? SOAP::Data->set_value(@header) : undef;
1468 my($body,$parameters);
1469 if ($type eq 'method' || $type eq 'response') {
1470 SOAP::Trace::method(@parameters);
1471
1472 my $method = shift(@parameters);
1473 # or die "Unspecified method for SOAP call\n";
1474
1475 $parameters = @parameters ? SOAP::Data->set_value(@parameters) : undef;
1476 if (!defined($method)) {
1477 } elsif (UNIVERSAL::isa($method => 'SOAP::Data')) {
1478 $body = $method;
1479 } elsif ($self->use_default_ns) {
1480 if ($self->{'_ns_uri'}) {
1481 $body = SOAP::Data->name($method)->attr( {
1482 'xmlns' => $self->{'_ns_uri'},
1483 } );
1484 } else {
1485 $body = SOAP::Data->name($method);
1486 }
1487 } else {
1488 # Commented out by Byrne on 1/4/2006 - to address default namespace problems
1489 # $body = SOAP::Data->name($method)->uri($self->{'_ns_uri'});
1490 # $body = $body->prefix($self->{'_ns_prefix'}) if ($self->{'_ns_prefix'});
1491
1492 # Added by Byrne on 1/4/2006 - to avoid the unnecessary creation of a new
1493 # namespace
1494 # Begin New Code (replaces code commented out above)
1495 $body = SOAP::Data->name($method);
1496 my $pre = $self->find_prefix($self->{'_ns_uri'});
1497 $body = $body->prefix($pre) if ($self->{'_ns_prefix'});
1498 # End new code
1499
1500 }
1501 # This is breaking a unit test right now...
1502 $body->set_value(SOAP::Utils::encode_data($parameters ? \$parameters : ()))
1503 if $body;
1504 } elsif ($type eq 'fault') {
1505 SOAP::Trace::fault(@parameters);
1506 $body = SOAP::Data
1507 -> name(SOAP::Utils::qualify($self->envprefix => 'Fault'))
1508 # parameters[1] needs to be escaped - thanks to aka_hct at gmx dot de
1509 # commented on 2001/03/28 because of failing in ApacheSOAP
1510 # need to find out more about it
1511 # -> attr({'xmlns' => ''})
1512 -> value(\SOAP::Data->set_value(
1513 SOAP::Data->name(faultcode => SOAP::Utils::qualify($self->envprefix => $parameters[0]))->type(""),
1514 SOAP::Data->name(faultstring => SOAP::Utils::encode_data($parameters[1]))->type(""),
1515 defined($parameters[2]) ? SOAP::Data->name(detail => do{my $detail = $parameters[2]; ref $detail ? \$detail : $detail}) : (),
1516 defined($parameters[3]) ? SOAP::Data->name(faultactor => $parameters[3])->type("") : (),
1517 ));
1518 } elsif ($type eq 'freeform') {
1519 SOAP::Trace::freeform(@parameters);
1520 $body = SOAP::Data->set_value(@parameters);
1521 } elsif (!defined($type)) {
1522 # This occurs when the Body is intended to be null. When no method has been
1523 # passed in of any kind.
1524 } else {
1525 die "Wrong type of envelope ($type) for SOAP call\n";
1526 }
1527
1528 $self->seen({}); # reinitialize multiref table
1529 # Build the envelope
1530 # Right now it is possible for $body to be a SOAP::Data element that has not
1531 # XML escaped any values. How do you remedy this?
1532 my($encoded) = $self->encode_object(
1533 SOAP::Data->name(SOAP::Utils::qualify($self->envprefix => 'Envelope') => \SOAP::Data->value(
1534 ($header ? SOAP::Data->name(SOAP::Utils::qualify($self->envprefix => 'Header') => \$header) : ()),
1535 ($body ? SOAP::Data->name(SOAP::Utils::qualify($self->envprefix => 'Body') => \$body) :
1536 SOAP::Data->name(SOAP::Utils::qualify($self->envprefix => 'Body')) ),
1537 ))->attr($self->attr)
1538 );
1539 $self->signature($parameters->signature) if ref $parameters;
1540
1541 # IMHO multirefs should be encoded after Body, but only some
1542 # toolkits understand this encoding, so we'll keep them for now (04/15/2001)
1543 # as the last element inside the Body
1544 # v -------------- subelements of Envelope
1545 # vv -------- last of them (Body)
1546 # v --- subelements
1547 push(@{$encoded->[2]->[-1]->[2]}, $self->encode_multirefs) if ref $encoded->[2]->[-1]->[2];
1548
1549 # Sometimes SOAP::Serializer is invoked statically when there is no context.
1550 # So first check to see if a context exists.
1551 # TODO - a context needs to be initialized by a constructor?
1552 if ($self->context && $self->context->packager->parts) {
1553 # TODO - this needs to be called! Calling it though wraps the payload twice!
1554 # return $self->context->packager->package($self->xmlize($encoded));
1555 }
1556 return $self->xmlize($encoded);
1557 }
1558
1559 # ======================================================================
1560
1561 package SOAP::Parser;
1562
1563 sub DESTROY { SOAP::Trace::objects('()') }
1564
1565 sub xmlparser {
1566 my $self = shift;
1567 return eval { $SOAP::Constants::DO_NOT_USE_XML_PARSER ? undef : do {
1568 require XML::Parser; XML::Parser->new }} ||
1569 eval { require XML::Parser::Lite; XML::Parser::Lite->new } ||
1570 die "XML::Parser is not @{[$SOAP::Constants::DO_NOT_USE_XML_PARSER ? 'used' : 'available']} and ", $@;
1571 }
1572
1573 sub parser {
1574 my $self = shift->new;
1575 # my $self = shift;
1576 @_ ? ($self->{'_parser'} = shift, return $self) : return ($self->{'_parser'} ||= $self->xmlparser);
1577 }
1578
1579 sub new {
1580 my $self = shift;
1581 return $self if ref $self;
1582 my $class = ref($self) || $self;
1583 SOAP::Trace::objects('()');
1584 return bless {_parser => shift} => $class;
1585 }
1586
1587 sub decode { SOAP::Trace::trace('()');
1588 my $self = shift;
1589
1590 $self->parser->setHandlers(
1591 Final => sub { shift; $self->final(@_) },
1592 Start => sub { shift; $self->start(@_) },
1593 End => sub { shift; $self->end(@_) },
1594 Char => sub { shift; $self->char(@_) },
1595 ExternEnt => sub { shift; die "External entity (pointing to '$_[1]') is not allowed" },
1596 );
1597 # my $parsed = $self->parser->parse($_[0]);
1598 # return $parsed;
1599 #
1600 my $ret = undef;
1601 eval {
1602 $ret = $self->parser->parse($_[0]);
1603 };
1604 if ($@) {
1605 $self->final; # Clean up in the event of an error
1606 die $@; # Pass back the error
1607 }
1608 return $ret;
1609 }
1610
1611 sub final {
1612 my $self = shift;
1613
1614 # clean handlers, otherwise SOAP::Parser won't be deleted:
1615 # it refers to XML::Parser which refers to subs from SOAP::Parser
1616 # Thanks to Ryan Adams <iceman@mit.edu>
1617 # and Craig Johnston <craig.johnston@pressplay.com>
1618 # checked by number of tests in t/02-payload.t
1619
1620 undef $self->{_values};
1621 $self->parser->setHandlers(
1622 Final => undef, Start => undef, End => undef, Char => undef, ExternEnt => undef,
1623 );
1624 $self->{_done};
1625 }
1626
1627 sub start { push @{shift->{_values}}, [shift, {@_}] }
1628
1629 # string concatenation changed to arrays which should improve performance
1630 # for strings with many entity-encoded elements.
1631 # Thanks to Mathieu Longtin <mrdamnfrenchy@yahoo.com>
1632 sub char { push @{shift->{_values}->[-1]->[3]}, shift }
1633
1634 sub end {
1635 my $self = shift;
1636 my $done = pop @{$self->{_values}};
1637 $done->[2] = defined $done->[3] ? join('',@{$done->[3]}) : '' unless ref $done->[2];
1638 undef $done->[3];
1639 @{$self->{_values}} ? (push @{$self->{_values}->[-1]->[2]}, $done)
1640 : ($self->{_done} = $done);
1641 }
1642
1643 # ======================================================================
1644
1645 package SOAP::SOM;
1646
1647 use Carp ();
1648
1649 sub BEGIN {
1650 no strict 'refs';
1651 my %path = (
1652 root => '/',
1653 envelope => '/Envelope',
1654 body => '/Envelope/Body',
1655 header => '/Envelope/Header',
1656 headers => '/Envelope/Header/[>0]',
1657 fault => '/Envelope/Body/Fault',
1658 faultcode => '/Envelope/Body/Fault/faultcode',
1659 faultstring => '/Envelope/Body/Fault/faultstring',
1660 faultactor => '/Envelope/Body/Fault/faultactor',
1661 faultdetail => '/Envelope/Body/Fault/detail',
1662 );
1663 for my $method (keys %path) {
1664 *$method = sub {
1665 my $self = shift;
1666 ref $self or return $path{$method};
1667 Carp::croak "Method '$method' is readonly and doesn't accept any parameters" if @_;
1668 return $self->valueof($path{$method});
1669 };
1670 }
1671 my %results = (
1672 method => '/Envelope/Body/[1]',
1673 result => '/Envelope/Body/[1]/[1]',
1674 freeform => '/Envelope/Body/[>0]',
1675 paramsin => '/Envelope/Body/[1]/[>0]',
1676 paramsall => '/Envelope/Body/[1]/[>0]',
1677 paramsout => '/Envelope/Body/[1]/[>1]'
1678 );
1679 for my $method (keys %results) {
1680 *$method = sub {
1681 my $self = shift;
1682 ref $self or return $results{$method};
1683 Carp::croak "Method '$method' is readonly and doesn't accept any parameters" if @_;
1684 defined $self->fault ? return : return $self->valueof($results{$method});
1685 };
1686 }
1687 for my $method (qw(context)) {
1688 my $field = '_' . $method;
1689 *$method = sub {
1690 my $self = shift;
1691 @_ ? ($self->{$field} = shift, return $self) : return $self->{$field};
1692 }
1693 }
1694 for my $method (qw(o_child o_value o_lname o_lattr o_qname)) { # import from SOAP::Utils
1695 *$method = \&{'SOAP::Utils::'.$method};
1696 }
1697 }
1698
1699 # use object in boolean context return true/false on last match
1700 # Ex.: $som->match('//Fault') ? 'SOAP call failed' : 'success';
1701 use overload fallback => 1, 'bool' => sub { @{shift->{_current}} > 0 };
1702
1703 sub DESTROY { SOAP::Trace::objects('()') }
1704
1705 sub new {
1706 my $self = shift;
1707 my $class = ref($self) || $self;
1708 my $content = shift;
1709 SOAP::Trace::objects('()');
1710 return bless { _content => $content, _current => [$content] } => $class;
1711 }
1712
1713 sub parts {
1714 my $self = shift;
1715 if (@_) {
1716 $self->context->packager->parts(@_);
1717 return $self;
1718 } else {
1719 return $self->context->packager->parts;
1720 }
1721 }
1722
1723 sub is_multipart {
1724 my $self = shift;
1725 return defined($self->parts);
1726 }
1727
1728 sub current {
1729 my $self = shift;
1730 $self->{_current} = [@_], return $self if @_;
1731 return wantarray ? @{$self->{_current}} : $self->{_current}->[0];
1732 }
1733
1734 sub valueof {
1735 my $self = shift;
1736 local $self->{_current} = $self->{_current};
1737 $self->match(shift) if @_;
1738 return wantarray ? map {o_value($_)} @{$self->{_current}}
1739 : @{$self->{_current}} ? o_value($self->{_current}->[0]) : undef;
1740 }
1741
1742 sub headerof { # SOAP::Header is the same as SOAP::Data, so just rebless it
1743 wantarray
1744 ? map { bless $_ => 'SOAP::Header' } shift->dataof(@_)
1745 : do { # header returned by ->dataof can be undef in scalar context
1746 my $header = shift->dataof(@_);
1747 ref $header ? bless($header => 'SOAP::Header') : undef;
1748 };
1749 }
1750
1751 sub dataof {
1752 my $self = shift;
1753 local $self->{_current} = $self->{_current};
1754 $self->match(shift) if @_;
1755 return wantarray ? map {$self->_as_data($_)} @{$self->{_current}}
1756 : @{$self->{_current}} ? $self->_as_data($self->{_current}->[0]) : undef;
1757 }
1758
1759 sub namespaceuriof {
1760 my $self = shift;
1761 local $self->{_current} = $self->{_current};
1762 $self->match(shift) if @_;
1763 return wantarray ? map {(SOAP::Utils::splitlongname(o_lname($_)))[0]} @{$self->{_current}}
1764 : @{$self->{_current}} ? (SOAP::Utils::splitlongname(o_lname($self->{_current}->[0])))[0] : undef;
1765 }
1766
1767 sub _as_data {
1768 my $self = shift;
1769 my $pointer = shift;
1770
1771 SOAP::Data
1772 -> new(prefix => '', name => o_qname($pointer), name => o_lname($pointer), attr => o_lattr($pointer))
1773 -> set_value(o_value($pointer));
1774 }
1775
1776 sub match {
1777 my $self = shift;
1778 my $path = shift;
1779 $self->{_current} = [
1780 $path =~ s!^/!! || !@{$self->{_current}}
1781 ? $self->_traverse($self->{_content}, 1 => split '/' => $path)
1782 : map {$self->_traverse_tree(o_child($_), split '/' => $path)} @{$self->{_current}}
1783 ];
1784 return $self;
1785 }
1786
1787 sub _traverse {
1788 my $self = shift;
1789 my($pointer, $itself, $path, @path) = @_;
1790
1791 die "Incorrect parameter" unless $itself =~ /^\d*$/;
1792
1793 if ($path && substr($path, 0, 1) eq '{') {
1794 $path = join '/', $path, shift @path while @path && $path !~ /}/;
1795 }
1796
1797 my($op, $num) = $path =~ /^\[(<=|<|>=|>|=|!=?)?(\d+)\]$/ if defined $path;
1798
1799 return $pointer unless defined $path;
1800
1801 $op = '==' unless $op; $op .= '=' if $op eq '=' || $op eq '!';
1802 my $numok = defined $num && eval "$itself $op $num";
1803 my $nameok = (o_lname($pointer) || '') =~ /(?:^|\})$path$/ if defined $path; # name can be with namespace
1804
1805 my $anynode = $path eq '';
1806 unless ($anynode) {
1807 if (@path) {
1808 return if defined $num && !$numok || !defined $num && !$nameok;
1809 } else {
1810 return $pointer if defined $num && $numok || !defined $num && $nameok;
1811 return;
1812 }
1813 }
1814
1815 my @walk;
1816 push @walk, $self->_traverse_tree([$pointer], @path) if $anynode;
1817 push @walk, $self->_traverse_tree(o_child($pointer), $anynode ? ($path, @path) : @path);
1818 return @walk;
1819 }
1820
1821 sub _traverse_tree {
1822 my $self = shift;
1823 my($pointer, @path) = @_;
1824
1825 # can be list of children or value itself. Traverse only children
1826 return unless ref $pointer eq 'ARRAY';
1827
1828 my $itself = 1;
1829
1830 grep {defined}
1831 map {$self->_traverse($_, $itself++, @path)}
1832 grep {!ref o_lattr($_) ||
1833 !exists o_lattr($_)->{"{$SOAP::Constants::NS_ENC}root"} ||
1834 o_lattr($_)->{"{$SOAP::Constants::NS_ENC}root"} ne '0'}
1835 @$pointer;
1836 }
1837
1838 # ======================================================================
1839
1840 package SOAP::Deserializer;
1841
1842 use vars qw(@ISA);
1843
1844 @ISA = qw(SOAP::Cloneable);
1845
1846 sub DESTROY { SOAP::Trace::objects('()') }
1847
1848 sub BEGIN {
1849 no strict 'refs';
1850 for my $method (qw(ids hrefs parts parser base xmlschemas xmlschema context)) {
1851 my $field = '_' . $method;
1852 *$method = sub {
1853 my $self = shift->new;
1854 @_ ? ($self->{$field} = shift, return $self) : return $self->{$field};
1855
1856 }
1857 }
1858 }
1859
1860 sub new {
1861 my $self = shift;
1862 return $self if ref $self;
1863 my $class = ref($self) || $self;
1864 SOAP::Trace::objects('()');
1865 return bless {
1866 '_ids' => {},
1867 '_hrefs' => {},
1868 '_parser' => SOAP::Parser->new,
1869 '_xmlschemas' => {
1870 $SOAP::Constants::NS_APS => 'SOAP::XMLSchemaApacheSOAP::Deserializer',
1871 map { $_ => $SOAP::Constants::XML_SCHEMAS{$_} . '::Deserializer'
1872 } keys %SOAP::Constants::XML_SCHEMAS
1873 },
1874 } => $class;
1875 }
1876
1877 sub is_xml {
1878 # Added check for envelope delivery. Fairly standard with MMDF and sendmail
1879 # Thanks to Chris Davies <Chris.Davies@ManheimEurope.com>
1880 $_[1] =~ /^\s*</ || $_[1] !~ /^(?:[\w-]+:|From )/;
1881 }
1882
1883 sub baselocation {
1884 my $self = shift;
1885 my $location = shift;
1886 if ($location) {
1887 my $uri = URI->new($location);
1888 # make absolute location if relative
1889 $location = $uri->abs($self->base || 'thismessage:/')->as_string unless $uri->scheme;
1890 }
1891 return $location;
1892 }
1893
1894 # Returns the envelope and populates SOAP::Packager with parts
1895 sub decode_parts {
1896 my $self = shift;
1897 my $env = $self->context->packager->unpackage($_[0],$self->context);
1898 my $body = $self->parser->decode($env);
1899 # TODO - This shouldn't be here! This is packager specific!
1900 # However this does need to pull out all the cid's
1901 # to populate ids hash with.
1902 foreach (@{$self->context->packager->parts}) {
1903 my $data = $_->bodyhandle->as_string;
1904 my $type = $_->head->mime_attr('Content-Type');
1905 my $location = $_->head->mime_attr('Content-Location');
1906 my $id = $_->head->mime_attr('Content-Id');
1907 $location = $self->baselocation($location);
1908 my $part = lc($type) eq 'text/xml' && !$SOAP::Constants::DO_NOT_PROCESS_XML_IN_MIME ?
1909 $self->parser->decode($data)
1910 : ['mimepart', {}, $data];
1911 # This below looks like unnecessary bloat!!!
1912 # I should probably dereference the mimepart, provide a callback to get the string data
1913 $id =~ s/^<([^>]*)>$/$1/; # string any leading and trailing brackets
1914 $self->ids->{$id} = $part if $id;
1915 $self->ids->{$location} = $part if $location;
1916 }
1917 return $body;
1918 }
1919
1920 # decode returns a parsed body in the form of an ARRAY
1921 # each element of the ARRAY is a HASH, ARRAY or SCALAR
1922 sub decode {
1923 my $self = shift->new; # this actually is important
1924 return $self->is_xml($_[0])
1925 ? $self->parser->decode($_[0])
1926 : $self->decode_parts($_[0]);
1927 }
1928
1929 # deserialize returns a SOAP::SOM object and parses straight
1930 # text as input
1931 sub deserialize {
1932 SOAP::Trace::trace('()');
1933 my $self = shift->new;
1934
1935 # initialize
1936 $self->hrefs({});
1937 $self->ids({});
1938
1939 # If the document is XML, then ids will be empty
1940 # If the document is MIME, then ids will hold a list of cids
1941 my $parsed = $self->decode($_[0]);
1942
1943 # Having this code here makes multirefs in the Body work, but multirefs
1944 # that reference XML fragments in a MIME part do not work.
1945 if (keys %{$self->ids()}) {
1946 $self->traverse_ids($parsed);
1947 } else {
1948 # delay - set ids to be traversed later in decode_object, they only get
1949 # traversed if an href is found that is referencing an id.
1950 $self->ids($parsed);
1951 }
1952 $self->decode_object($parsed);
1953 my $som = SOAP::SOM->new($parsed);
1954 $som->context($self->context); # TODO - try removing this and see if it works!
1955 return $som;
1956 }
1957
1958 sub traverse_ids {
1959 my $self = shift;
1960 my $ref = shift;
1961 my($undef, $attrs, $children) = @$ref;
1962 # ^^^^^^ to fix nasty error on Mac platform (Carl K. Cunningham)
1963 $self->ids->{$attrs->{'id'}} = $ref if exists $attrs->{'id'};
1964 return unless ref $children;
1965 for (@$children) {$self->traverse_ids($_)};
1966 }
1967
1968 sub decode_object {
1969 my $self = shift;
1970 my $ref = shift;
1971 my($name, $attrs, $children, $value) = @$ref;
1972
1973 $ref->[6] = $attrs = {%$attrs}; # make a copy for long attributes
1974
1975 use vars qw(%uris);
1976 local %uris = (%uris, map {
1977 do { (my $ns = $_) =~ s/^xmlns:?//; $ns } => delete $attrs->{$_}
1978 } grep {/^xmlns(:|$)/} keys %$attrs);
1979
1980 foreach (keys %$attrs) {
1981 next unless m/^($SOAP::Constants::NSMASK?):($SOAP::Constants::NSMASK)$/;
1982
1983 $1 =~ /^[xX][mM][lL]/ ||
1984 $uris{$1} &&
1985 do {
1986 $attrs->{SOAP::Utils::longname($uris{$1}, $2)} = do {
1987 my $value = $attrs->{$_};
1988 $2 ne 'type' && $2 ne 'arrayType'
1989 ? $value
1990 : SOAP::Utils::longname($value =~ m/^($SOAP::Constants::NSMASK?):(${SOAP::Constants::NSMASK}(?:\[[\d,]*\])*)/
1991 ? ($uris{$1} || die("Unresolved prefix '$1' for attribute value '$value'\n"), $2)
1992 : ($uris{''} || die("Unspecified namespace '$1' for type '$value'\n"), $value)
1993 );
1994 };
1995 1;
1996 } ||
1997 die "Unresolved prefix '$1' for attribute '$_'\n";
1998 }
1999
2000 # and now check the element
2001 my $ns = ($name =~ s/^($SOAP::Constants::NSMASK?):// ? $1 : '');
2002 $ref->[5] = SOAP::Utils::longname(
2003 $ns ? ($uris{$ns} || die "Unresolved prefix '$ns' for element '$name'\n")
2004 : (defined $uris{''} ? $uris{''} : undef),
2005 $name
2006 );
2007
2008 ($children, $value) = (undef, $children) unless ref $children;
2009
2010 return $name => ($ref->[4] = $self->decode_value(
2011 [$ref->[5], $attrs, $children, $value]
2012 ));
2013 }
2014
2015 sub decode_value {
2016 my $self = shift;
2017 my $ref = shift;
2018 my($name, $attrs, $children, $value) = @$ref;
2019
2020 # check SOAP version if applicable
2021 use vars '$level'; local $level = $level || 0;
2022 if (++$level == 1) {
2023 my($namespace, $envelope) = SOAP::Utils::splitlongname($name);
2024 SOAP::Lite->soapversion($namespace) if $envelope eq 'Envelope' && $namespace;
2025 }
2026
2027 # check encodingStyle
2028 # future versions may bind deserializer to encodingStyle
2029 my $encodingStyle = $attrs->{"{$SOAP::Constants::NS_ENV}encodingStyle"} || "";
2030 my (%union,%isect);
2031 # TODO - SOAP 1.2 and 1.1 have different rules about valid encodingStyle values
2032 # For example, in 1.1 - any http://schemas.xmlsoap.org/soap/encoding/*
2033 # value is valid
2034 # Find intersection of declared and supported encoding styles
2035 foreach my $e (@SOAP::Constants::SUPPORTED_ENCODING_STYLES, split(/ +/,$encodingStyle)) {
2036 $union{$e}++ && $isect{$e}++;
2037 }
2038 die "Unrecognized/unsupported value of encodingStyle attribute '$encodingStyle'"
2039 if defined($encodingStyle) && length($encodingStyle) > 0 && !%isect &&
2040 !(SOAP::Lite->soapversion == 1.1 && $encodingStyle =~ /(?:^|\b)$SOAP::Constants::NS_ENC/);
2041
2042 # removed to provide literal support in 0.65
2043 #$encodingStyle !~ /(?:^|\b)$SOAP::Constants::NS_ENC/;
2044 # # ^^^^^^^^ \b causing problems (!?) on some systems
2045 # # as reported by David Dyck <dcd@tc.fluke.com>
2046 # # so use (?:^|\b) instead
2047
2048 use vars '$arraytype'; # type of Array element specified on Array itself
2049 # either specified with xsi:type, or <enc:name/> or array element
2050 my ($type) = grep {defined}
2051 map($attrs->{$_}, sort grep {/^\{$SOAP::Constants::NS_XSI_ALL\}type$/o} keys %$attrs),
2052 $name =~ /^\{$SOAP::Constants::NS_ENC\}/ ? $name : $arraytype;
2053 local $arraytype; # it's used only for one level, we don't need it anymore
2054
2055 # $name is not used here since type should be encoded as type, not as name
2056 my ($schema, $class) = SOAP::Utils::splitlongname($type) if $type;
2057 my $schemaclass = defined($schema) && $self->xmlschemas->{$schema}
2058 || $self;
2059 # store schema that is used in parsed message
2060 $self->xmlschema($schema) if $schema && $schema =~ /XMLSchema/;
2061
2062 # don't use class/type if anyType/ur-type is specified on wire
2063 undef $class if $schemaclass->can('anyTypeValue') && $schemaclass->anyTypeValue eq $class;
2064
2065 my $method = 'as_' . ($class || '-'); # dummy type if not defined
2066 $class =~ s/__|\./::/g if $class;
2067
2068 my $id = $attrs->{id};
2069 if (defined $id && exists $self->hrefs->{$id}) {
2070 return $self->hrefs->{$id};
2071 } elsif (exists $attrs->{href}) {
2072 (my $id = delete $attrs->{href}) =~ s/^(#|cid:|uuid:)?//;
2073 # convert to absolute if not internal '#' or 'cid:'
2074 $id = $self->baselocation($id) unless $1;
2075 return $self->hrefs->{$id} if exists $self->hrefs->{$id};
2076 # First time optimization. we don't traverse IDs unless asked for it.
2077 # This is where traversing id's is delayed from before
2078 # - the first time through - ids should contain a copy of the parsed XML
2079 # structure! seems silly to make so many copies
2080 my $ids = $self->ids;
2081 if (ref($ids) ne 'HASH') {
2082 $self->ids({}); # reset list of ids first time through
2083 $self->traverse_ids($ids);
2084 }
2085 if (exists($self->ids->{$id})) {
2086 my $obj = ($self->decode_object(delete($self->ids->{$id})))[1];
2087 return $self->hrefs->{$id} = $obj;
2088 } else {
2089 die "Unresolved (wrong?) href ($id) in element '$name'\n";
2090 }
2091 }
2092
2093 return undef if grep {
2094 /^$SOAP::Constants::NS_XSI_NILS$/ &&
2095 $self->xmlschemas->{$1 || $2}->as_undef($attrs->{$_})
2096 } keys %$attrs;
2097 # try to handle with typecasting
2098 my $res = $self->typecast($value, $name, $attrs, $children, $type);
2099 return $res if defined $res;
2100
2101 # ok, continue with others
2102 if (exists $attrs->{"{$SOAP::Constants::NS_ENC}arrayType"}) {
2103 my $res = [];
2104 $self->hrefs->{$id} = $res if defined $id;
2105
2106 # check for arrayType which could be [1], [,2][5] or []
2107 # [,][1] will NOT be allowed right now (multidimensional sparse array)
2108 my($type, $multisize) = $attrs->{"{$SOAP::Constants::NS_ENC}arrayType"}
2109 =~ /^(.+)\[(\d*(?:,\d+)*)\](?:\[(?:\d+(?:,\d+)*)\])*$/
2110 or die qq!Unrecognized/unsupported format of arrayType attribute '@{[$attrs->{"{$SOAP::Constants::NS_ENC}arrayType"}]}'\n!;
2111
2112 my @dimensions = map { $_ || undef } split /,/, $multisize;
2113 my $size = 1; foreach (@dimensions) { $size *= $_ || 0 }
2114
2115 local $arraytype = $type;
2116
2117 # multidimensional
2118 if ($multisize =~ /,/) {
2119 @$res = splitarray(
2120 [@dimensions],
2121 [map { scalar(($self->decode_object($_))[1]) } @{$children || []}]
2122 );
2123
2124 # normal
2125 } else {
2126 @$res = map { scalar(($self->decode_object($_))[1]) } @{$children || []};
2127 }
2128
2129 # sparse (position)
2130 if (ref $children && exists SOAP::Utils::o_lattr($children->[0])->{"{$SOAP::Constants::NS_ENC}position"}) {
2131 my @new;
2132 for (my $pos = 0; $pos < @$children; $pos++) {
2133 # TBD implement position in multidimensional array
2134 my($position) = SOAP::Utils::o_lattr($children->[$pos])->{"{$SOAP::Constants::NS_ENC}position"} =~ /^\[(\d+)\]$/
2135 or die "Position must be specified for all elements of sparse array\n";
2136 $new[$position] = $res->[$pos];
2137 }
2138 @$res = @new;
2139 }
2140
2141 # partially transmitted (offset)
2142 # TBD implement offset in multidimensional array
2143 my($offset) = $attrs->{"{$SOAP::Constants::NS_ENC}offset"} =~ /^\[(\d+)\]$/
2144 if exists $attrs->{"{$SOAP::Constants::NS_ENC}offset"};
2145 unshift(@$res, (undef) x $offset) if $offset;
2146
2147 die "Too many elements in array. @{[scalar@$res]} instead of claimed $multisize ($size)\n"
2148 if $multisize && $size < @$res;
2149
2150 # extend the array if number of elements is specified
2151 $#$res = $dimensions[0]-1 if defined $dimensions[0] && @$res < $dimensions[0];
2152
2153 return defined $class && $class ne 'Array' ? bless($res => $class) : $res;
2154
2155 } elsif ($name =~ /^\{$SOAP::Constants::NS_ENC\}Struct$/ ||
2156 !$schemaclass->can($method) &&
2157 (ref $children || defined $class && $value =~ /^\s*$/)) {
2158 my $res = {};
2159 $self->hrefs->{$id} = $res if defined $id;
2160 # Patch code introduced in 0.65 - deserializes array properly
2161 # %$res = map {$self->decode_object($_)} @{$children || []}; # removed in patch
2162 # Decode each element of the struct.
2163 foreach my $child (@{$children || []}) {
2164 my ($child_name, $child_value) = $self->decode_object($child);
2165 # Store the decoded element in the struct. If the element name is
2166 # repeated, replace the previous scalar value with a new array
2167 # containing both values.
2168 my $prev = $res->{$child_name};
2169 if (not defined $prev) {
2170 # first time to see this value: use scalar
2171 $res->{$child_name} = $child_value;
2172 } elsif (ref $prev ne "ARRAY") {
2173 # second time to see this value: convert scalar to array
2174 $res->{$child_name} = [ $prev, $child_value ];
2175 } else {
2176 # already have an array: append to it
2177 push @{$res->{$child_name}}, $child_value;
2178 }
2179 }
2180 # End patch code
2181 return defined $class && $class ne 'SOAPStruct' ? bless($res => $class) : $res;
2182 } else {
2183 my $res;
2184 if ($schemaclass->can($method)) {
2185 $method = "$schemaclass\::$method" unless ref $schemaclass;
2186 $res = $self->$method($value, $name, $attrs, $children, $type);
2187 } else {
2188 $res = $self->typecast($value, $name, $attrs, $children, $type);
2189 $res = $class ? die "Unrecognized type '$type'\n" : $value
2190 unless defined $res;
2191 }
2192 $self->hrefs->{$id} = $res if defined $id;
2193 return $res;
2194 }
2195 }
2196
2197 sub splitarray {
2198 my @sizes = @{+shift};
2199 my $size = shift @sizes;
2200 my $array = shift;
2201
2202 return splice(@$array, 0, $size) unless @sizes;
2203 my @array = ();
2204 push @array, [splitarray([@sizes], $array)] while @$array && (!defined $size || $size--);
2205 return @array;
2206 }
2207
2208 sub typecast { } # typecast is called for both objects AND scalar types
2209 # check ref of the second parameter (first is the object)
2210 # return undef if you don't want to handle it
2211
2212 # ======================================================================
2213
2214 package SOAP::Client;
2215
2216 sub BEGIN {
2217 no strict 'refs';
2218 for my $method (qw(endpoint code message is_success status options)) {
2219 my $field = '_' . $method;
2220 *$method = sub {
2221 my $self = shift->new;
2222 # my $self = shift;
2223 @_ ? ($self->{$field} = shift, return $self) : return $self->{$field};
2224 }
2225 }
2226 }
2227
2228 # ======================================================================
2229
2230 package SOAP::Server::Object;
2231
2232 sub gen_id; *gen_id = \&SOAP::Serializer::gen_id;
2233
2234 my %alive;
2235 my %objects;
2236
2237 sub objects_by_reference {
2238 shift;
2239 while (@_) { @alive{shift()} = ref $_[0] ? shift : sub { $_[1]-$_[$_[5] ? 5 : 4] > $SOAP::Constants::OBJS_BY_REF_KEEPALIVE } }
2240 keys %alive;
2241 }
2242
2243 sub reference {
2244 my $self = shift;
2245 my $stamp = time;
2246 my $object = shift;
2247 my $id = $stamp . $self->gen_id($object);
2248
2249 # this is code for garbage collection
2250 my $time = time;
2251 my $type = ref $object;
2252 my @objects = grep { $objects{$_}->[1] eq $type } keys %objects;
2253 for (grep { $alive{$type}->(scalar @objects, $time, @{$objects{$_}}) } @objects) {
2254 delete $objects{$_};
2255 }
2256
2257 $objects{$id} = [$object, $type, $stamp];
2258 bless { id => $id } => ref $object;
2259 }
2260
2261 sub references {
2262 my $self = shift;
2263 return @_ unless %alive; # small optimization
2264 map { ref($_) && exists $alive{ref $_} ? $self->reference($_) : $_ } @_;
2265 }
2266
2267 sub object {
2268 my $self = shift;
2269 my $class = ref($self) || $self;
2270 my $object = shift;
2271 return $object unless ref($object) && $alive{ref $object} && exists $object->{id};
2272 my $reference = $objects{$object->{id}};
2273 die "Object with specified id couldn't be found\n" unless ref $reference->[0];
2274 $reference->[3] = time; # last access time
2275 return $reference->[0]; # reference to actual object
2276 }
2277
2278 sub objects {
2279 my $self = shift;
2280 return @_ unless %alive; # small optimization
2281 map { ref($_) && exists $alive{ref $_} && exists $_->{id} ? $self->object($_) : $_ } @_;
2282 }
2283
2284 # ======================================================================
2285
2286 package SOAP::Server::Parameters;
2287
2288 sub byNameOrOrder {
2289 unless (UNIVERSAL::isa($_[-1] => 'SOAP::SOM')) {
2290 warn "Last parameter is expected to be envelope\n" if $^W;
2291 pop;
2292 return @_;
2293 }
2294 my $params = pop->method;
2295 my @mandatory = ref $_[0] eq 'ARRAY' ? @{shift()} : die "list of parameters expected as the first parameter for byName";
2296 my $byname = 0;
2297 my @res = map { $byname += exists $params->{$_}; $params->{$_} } @mandatory;
2298 return $byname ? @res : @_;
2299 }
2300
2301 sub byName {
2302 unless (UNIVERSAL::isa($_[-1] => 'SOAP::SOM')) {
2303 warn "Last parameter is expected to be envelope\n" if $^W;
2304 pop;
2305 return @_;
2306 }
2307 return @{pop->method}{ref $_[0] eq 'ARRAY' ? @{shift()} : die "list of parameters expected as the first parameter for byName"};
2308 }
2309
2310 # ======================================================================
2311
2312 package SOAP::Server;
2313
2314 use Carp ();
2315
2316 sub DESTROY { SOAP::Trace::objects('()') }
2317
2318 sub initialize {
2319 return (
2320 packager => SOAP::Packager::MIME->new,
2321 transport => SOAP::Transport->new,
2322 serializer => SOAP::Serializer->new,
2323 deserializer => SOAP::Deserializer->new,
2324 on_action => sub { ; },
2325 on_dispatch => sub { return; },
2326 );
2327 }
2328
2329 sub new {
2330 my $self = shift;
2331 return $self if ref $self;
2332
2333 unless (ref $self) {
2334 my $class = ref($self) || $self;
2335 my(@params, @methods);
2336
2337 while (@_) { my($method, $params) = splice(@_,0,2);
2338 $class->can($method) ? push(@methods, $method, $params)
2339 : $^W && Carp::carp "Unrecognized parameter '$method' in new()";
2340 }
2341 $self = bless {
2342 _dispatch_to => [],
2343 _dispatch_with => {},
2344 _dispatched => [],
2345 _action => '',
2346 _options => {},
2347 } => $class;
2348 unshift(@methods, $self->initialize);
2349 while (@methods) { my($method, $params) = splice(@methods,0,2);
2350 $self->$method(ref $params eq 'ARRAY' ? @$params : $params)
2351 }
2352 SOAP::Trace::objects('()');
2353 }
2354
2355 Carp::carp "Odd (wrong?) number of parameters in new()" if $^W && (@_ & 1);
2356 while (@_) { my($method, $params) = splice(@_,0,2);
2357 $self->can($method)
2358 ? $self->$method(ref $params eq 'ARRAY' ? @$params : $params)
2359 : $^W && Carp::carp "Unrecognized parameter '$method' in new()"
2360 }
2361
2362 return $self;
2363 }
2364
2365 sub init_context {
2366 my $self = shift;
2367 $self->{'_deserializer'}->{'_context'} = $self;
2368 $self->{'_serializer'}->{'_context'} = $self;
2369 }
2370
2371 sub destroy_context {
2372 my $self = shift;
2373 delete($self->{'_deserializer'}->{'_context'});
2374 delete($self->{'_serializer'}->{'_context'})
2375 }
2376
2377 sub BEGIN {
2378 no strict 'refs';
2379 for my $method (qw(serializer deserializer transport)) {
2380 my $field = '_' . $method;
2381 *$method = sub {
2382 my $self = shift->new;
2383 if (@_) {
2384 my $context = $self->{$field}->{'_context'}; # save the old context
2385 $self->{$field} = shift;
2386 $self->{$field}->{'_context'} = $context; # restore the old context
2387 return $self;
2388 } else {
2389 return $self->{$field};
2390 }
2391 }
2392 }
2393 for my $method (qw(action myuri options dispatch_with packager)) {
2394 my $field = '_' . $method;
2395 *$method = sub {
2396 my $self = shift->new;
2397 # my $self = shift;
2398 @_ ? ($self->{$field} = shift, return $self) : return $self->{$field};
2399 }
2400 }
2401 for my $method (qw(on_action on_dispatch)) {
2402 my $field = '_' . $method;
2403 *$method = sub {
2404 my $self = shift->new;
2405 # my $self = shift;
2406 return $self->{$field} unless @_;
2407 local $@;
2408 # commented out because that 'eval' was unsecure
2409 # > ref $_[0] eq 'CODE' ? shift : eval shift;
2410 # Am I paranoid enough?
2411 $self->{$field} = shift;
2412 Carp::croak $@ if $@;
2413 Carp::croak "$method() expects subroutine (CODE) or string that evaluates into subroutine (CODE)"
2414 unless ref $self->{$field} eq 'CODE';
2415 return $self;
2416 }
2417 }
2418 for my $method (qw(dispatch_to)) {
2419 my $field = '_' . $method;
2420 *$method = sub {
2421 my $self = shift->new;
2422 # my $self = shift;
2423 @_ ? ($self->{$field} = [@_], return $self)
2424 : return @{$self->{$field}};
2425 }
2426 }
2427 }
2428
2429 sub objects_by_reference {
2430 my $self = shift->new;
2431 # my $self = shift;
2432 @_ ? (SOAP::Server::Object->objects_by_reference(@_), return $self)
2433 : SOAP::Server::Object->objects_by_reference;
2434 }
2435
2436 sub dispatched {
2437 my $self = shift->new;
2438 # my $self = shift;
2439 @_ ? (push(@{$self->{_dispatched}}, @_), return $self)
2440 : return @{$self->{_dispatched}};
2441 }
2442
2443 sub find_target {
2444 my $self = shift;
2445 my $request = shift;
2446
2447 # try to find URI/method from on_dispatch call first
2448 my($method_uri, $method_name) = $self->on_dispatch->($request);
2449
2450 # if nothing there, then get it from envelope itself
2451 $request->match((ref $request)->method);
2452 ($method_uri, $method_name) = ($request->namespaceuriof || '', $request->dataof->name)
2453 unless $method_name;
2454
2455 $self->on_action->(my $action = $self->action, $method_uri, $method_name);
2456
2457 # check to avoid security vulnerability: Protected->Unprotected::method(@parameters)
2458 # see for more details: http://www.phrack.org/phrack/58/p58-0x09
2459 die "Denied access to method ($method_name)\n" unless $method_name =~ /^\w+$/;
2460
2461 my($class, $static);
2462 # try to bind directly
2463 if (defined($class = $self->dispatch_with->{$method_uri}
2464 || $self->dispatch_with->{$action || ''}
2465 || ($action =~ /^"(.+)"$/ ? $self->dispatch_with->{$1} : undef))) {
2466 # return object, nothing else to do here
2467 return ($class, $method_uri, $method_name) if ref $class;
2468 $static = 1;
2469 } else {
2470 die "URI path shall map to class" unless defined ($class = URI->new($method_uri)->path);
2471
2472 for ($class) { s!^/|/$!!g; s!/!::!g; s/^$/main/; }
2473 die "Failed to access class ($class)" unless $class =~ /^(\w[\w:]*)$/;
2474
2475 my $fullname = "$class\::$method_name";
2476 foreach ($self->dispatch_to) {
2477 return ($_, $method_uri, $method_name) if ref eq $class; # $OBJECT
2478 next if ref; # skip other objects
2479 # will ignore errors, because it may complain on
2480 # d:\foo\bar, which is PATH and not regexp
2481 eval {
2482 $static ||=
2483 $class =~ /^$_$/ || # MODULE
2484 $fullname =~ /^$_$/ || # MODULE::method
2485 $method_name =~ /^$_$/ && ($class eq 'main') # method ('main' assumed)
2486 ;
2487 };
2488 }
2489 }
2490
2491 no strict 'refs';
2492
2493 # TODO - sort this mess out:
2494 # SOAP::Lite 0.60:
2495 # unless (defined %{"${class}::"}) {
2496 # Patch to SOAP::Lite 0.60:
2497 # The following patch does not work for packages defined within a BEGIN block
2498 # unless (exists($INC{join '/', split /::/, $class.'.pm'})) {
2499 # Combination of 0.60 and patch:
2500 unless (defined(%{"${class}::"}) || exists($INC{join '/', split /::/, $class.'.pm'})) {
2501 # allow all for static and only specified path for dynamic bindings
2502 local @INC = (($static ? @INC : ()), grep {!ref && m![/\\.]!} $self->dispatch_to);
2503 eval 'local $^W; ' . "require $class";
2504 die "Failed to access class ($class): $@" if $@;
2505 $self->dispatched($class) unless $static;
2506 }
2507
2508 die "Denied access to method ($method_name) in class ($class)"
2509 unless $static || grep {/^$class$/} $self->dispatched;
2510
2511 return ($class, $method_uri, $method_name);
2512 }
2513
2514 sub handle {
2515 SOAP::Trace::trace('()');
2516 my $self = shift;
2517 $self = $self->new if !ref $self; # inits the server when called in a static context
2518
2519 ## XXX DbP: support empty requests from clients
2520 if ( $_[0] =~ m/^\s*$/s ) {
2521 return CWMP->empty_request( $self );
2522 }
2523
2524 $self->init_context();
2525 # we want to restore it when we are done
2526 local $SOAP::Constants::DEFAULT_XML_SCHEMA
2527 = $SOAP::Constants::DEFAULT_XML_SCHEMA;
2528
2529 # SOAP version WILL NOT be restored when we are done.
2530 # is it problem?
2531
2532 my $result = eval {
2533 local $SIG{__DIE__};
2534 # why is this here:
2535 $self->serializer->soapversion(1.1);
2536 my $request = eval { $self->deserializer->deserialize($_[0]) };
2537 die SOAP::Fault
2538 ->faultcode($SOAP::Constants::FAULT_VERSION_MISMATCH)
2539 ->faultstring($@)
2540 if $@ && $@ =~ /^$SOAP::Constants::WRONG_VERSION/;
2541 die "Application failed during request deserialization: $@" if $@;
2542 my $som = ref $request;
2543 die "Can't find root element in the message"
2544 unless $request->match($som->envelope);
2545 $self->serializer->soapversion(SOAP::Lite->soapversion);
2546 $self->serializer->xmlschema($SOAP::Constants::DEFAULT_XML_SCHEMA
2547 = $self->deserializer->xmlschema)
2548 if $self->deserializer->xmlschema;
2549
2550 die SOAP::Fault
2551 ->faultcode($SOAP::Constants::FAULT_MUST_UNDERSTAND)
2552 ->faultstring("Unrecognized header has mustUnderstand attribute set to 'true'")
2553 if !$SOAP::Constants::DO_NOT_CHECK_MUSTUNDERSTAND &&
2554 grep { $_->mustUnderstand &&
2555 (!$_->actor || $_->actor eq $SOAP::Constants::NEXT_ACTOR)
2556 } $request->dataof($som->headers);
2557
2558 # XXX DbP: per SOAP spec, we should keep headers
2559 my @headers = map { bless $_, 'SOAP::Header' } $request->dataof($som->headers);
2560
2561 die "Can't find method element in the message"
2562 unless $request->match($som->method);
2563 # TODO - SOAP::Dispatcher plugs in here
2564 # my $handler = $self->dispatcher->find_handler($request);
2565 my($class, $method_uri, $method_name) = $self->find_target($request);
2566 my @results = eval {
2567 local $^W;
2568 my @parameters = $request->paramsin;
2569
2570 # SOAP::Trace::dispatch($fullname);
2571 SOAP::Trace::parameters(@parameters);
2572
2573 push @parameters, $request
2574 if UNIVERSAL::isa($class => 'SOAP::Server::Parameters');
2575
2576 # XXX DbP: pass headers to our method as last argument
2577 push @parameters, [ @headers ];
2578
2579 SOAP::Server::Object->references(
2580 defined $parameters[0] && ref $parameters[0] &&
2581 UNIVERSAL::isa($parameters[0] => $class) ? do {
2582 my $object = shift @parameters;
2583 SOAP::Server::Object->object(ref $class ? $class : $object)
2584 ->$method_name(SOAP::Server::Object->objects(@parameters)),
2585 # send object back as a header
2586 # preserve name, specify URI
2587 SOAP::Header
2588 ->uri($SOAP::Constants::NS_SL_HEADER => $object)
2589 ->name($request->dataof($som->method.'/[1]')->name)
2590 } # end do block
2591 # SOAP::Dispatcher will plug-in here as well
2592 # $handler->dispatch(SOAP::Server::Object->objects(@parameters)
2593 : $class->$method_name(SOAP::Server::Object->objects(@parameters)) );
2594 }; # end eval block
2595 SOAP::Trace::result(@results);
2596
2597 # XXX DbP: add SOAP request headers back
2598 push @results, @headers if ( @headers );
2599
2600 # let application errors pass through with 'Server' code
2601 die ref $@ ?
2602 $@ : $@ =~ /^Can\'t locate object method "$method_name"/ ?
2603 "Failed to locate method ($method_name) in class ($class)" :
2604 SOAP::Fault->faultcode($SOAP::Constants::FAULT_SERVER)->faultstring($@)
2605 if $@;
2606
2607 my $result = $self->serializer
2608 ->prefix('s') # distinguish generated element names between client and server
2609 ->uri($method_uri)
2610 ->envelope(response => $method_name . 'Response', @results);
2611 $self->destroy_context();
2612 return $result;
2613 };
2614
2615 $self->destroy_context();
2616 # void context
2617 return unless defined wantarray;
2618
2619 # normal result
2620 return $result unless $@;
2621
2622 # check fails, something wrong with message
2623 return $self->make_fault($SOAP::Constants::FAULT_CLIENT, $@) unless ref $@;
2624
2625 # died with SOAP::Fault
2626 return $self->make_fault($@->faultcode || $SOAP::Constants::FAULT_SERVER,
2627 $@->faultstring || 'Application error',
2628 $@->faultdetail, $@->faultactor)
2629 if UNIVERSAL::isa($@ => 'SOAP::Fault');
2630
2631 # died with complex detail
2632 return $self->make_fault($SOAP::Constants::FAULT_SERVER, 'Application error' => $@);
2633
2634 } # end of handle()
2635
2636 sub make_fault {
2637 my $self = shift;
2638 my($code, $string, $detail, $actor) = @_;
2639 $self->serializer->fault($code, $string, $detail, $actor || $self->myuri);
2640 }
2641
2642 # ======================================================================
2643
2644 package SOAP::Trace;
2645
2646 use Carp ();
2647
2648 my @list = qw(transport dispatch result parameters headers objects method fault freeform trace debug);
2649 { no strict 'refs'; for (@list) { *$_ = sub {} } }
2650
2651 sub defaultlog {
2652 my $caller = (caller(1))[3]; # the 4th element returned by caller is the subroutine namea
2653 $caller = (caller(2))[3] if $caller =~ /eval/;
2654 chomp(my $msg = join ' ', @_);
2655 printf STDERR "%s: %s\n", $caller, $msg;
2656 }
2657
2658 sub import { no strict 'refs'; local $^W;
2659 my $pack = shift;
2660 my(@notrace, @symbols);
2661 for (@_) {
2662 if (ref eq 'CODE') {
2663 my $call = $_;
2664 foreach (@symbols) { *$_ = sub { $call->(@_) } }
2665 @symbols = ();
2666 } else {
2667 local $_ = $_;
2668 my $minus = s/^-//;
2669 my $all = $_ eq 'all';
2670 Carp::carp "Illegal symbol for tracing ($_)" unless $all || $pack->can($_);
2671 $minus ? push(@notrace, $all ? @list : $_) : push(@symbols, $all ? @list : $_);
2672 }
2673 }
2674 # TODO - I am getting a warning here about redefining a subroutine
2675 foreach (@symbols) { *$_ = \&defaultlog }
2676 foreach (@notrace) { *$_ = sub {} }
2677 }
2678
2679 # ======================================================================
2680
2681 package SOAP::Custom::XML::Data;
2682
2683 use vars qw(@ISA $AUTOLOAD);
2684 @ISA = qw(SOAP::Data);
2685
2686 use overload fallback => 1, '""' => sub { shift->value };
2687
2688 sub _compileit {
2689 no strict 'refs';
2690 my $method = shift;
2691 *$method = sub {
2692 return __PACKAGE__->SUPER::name($method => $_[0]->attr->{$method})
2693 if exists $_[0]->attr->{$method};
2694 my @elems = grep {
2695 ref $_ && UNIVERSAL::isa($_ => __PACKAGE__) && $_->SUPER::name =~ /(^|:)$method$/
2696 } $_[0]->value;
2697 return wantarray? @elems : $elems[0];
2698 }
2699 }
2700
2701 sub BEGIN { foreach (qw(name type import)) { _compileit($_) } }
2702
2703 sub AUTOLOAD {
2704 my $method = substr($AUTOLOAD, rindex($AUTOLOAD, '::') + 2);
2705 return if $method eq 'DESTROY';
2706
2707 _compileit($method);
2708 goto &$AUTOLOAD;
2709 }
2710
2711 # ======================================================================
2712
2713 package SOAP::Custom::XML::Deserializer;
2714
2715 use vars qw(@ISA);
2716 @ISA = qw(SOAP::Deserializer);
2717
2718 sub decode_value {
2719 my $self = shift;
2720 my $ref = shift;
2721 my($name, $attrs, $children, $value) = @$ref;
2722 # base class knows what to do with it
2723 return $self->SUPER::decode_value($ref) if exists $attrs->{href};
2724
2725 SOAP::Custom::XML::Data
2726 -> SOAP::Data::name($name)
2727 -> attr($attrs)
2728 -> set_value(ref $children && @$children ? map(scalar(($self->decode_object($_))[1]), @$children) : $value);
2729 }
2730
2731 # ======================================================================
2732
2733 package SOAP::Schema::Deserializer;
2734
2735 use vars qw(@ISA);
2736 @ISA = qw(SOAP::Custom::XML::Deserializer);
2737
2738 # ======================================================================
2739
2740 package SOAP::Schema::WSDL;
2741
2742 use vars qw(%imported @ISA);
2743 @ISA = qw(SOAP::Schema);
2744
2745 sub new {
2746 my $self = shift;
2747
2748 unless (ref $self) {
2749 my $class = ref($self) || $self;
2750 $self = $class->SUPER::new(@_);
2751 # $self = bless {} => $class;
2752 }
2753 return $self;
2754 }
2755
2756 sub base {
2757 my $self = shift->new;
2758 @_ ? ($self->{_base} = shift, return $self) : return $self->{_base};
2759 }
2760
2761 sub import {
2762 my $self = shift->new;
2763 my $s = shift;
2764 my $base = shift || $self->base || die "Missing base argument for ", __PACKAGE__, "\n";
2765
2766 # my $schema;
2767 my @a = $s->import;
2768 local %imported = %imported;
2769 foreach (@a) {
2770 next unless $_->location;
2771 my $location = URI->new_abs($_->location->value, $base)->as_string;
2772 if ($imported{$location}++) {
2773 warn "Recursion loop detected in service description from '$location'. Ignored\n" if $^W;
2774 return $s;
2775 }
2776 # $schema ||= SOAP::Schema->new;
2777 # my $root = $self->import($schema->deserializer->deserialize($schema->access($location))->root, $location);
2778 my $root = $self->import($self->deserializer->deserialize($self->access($location))->root, $location);
2779 $root->SOAP::Data::name eq 'definitions' ? $s->set_value($s->value, $root->value) :
2780 $root->SOAP::Data::name eq 'schema' ? do { # add <types> element if there is no one
2781 $s->set_value($s->value, $self->deserializer->deserialize('<types></types>')->root) unless $s->types;
2782 $s->types->set_value($s->types->value, $root) } :
2783 die "Don't know what to do with '@{[$root->SOAP::Data::name]}' in schema imported from '$location'\n";
2784 }
2785 # return the parsed WSDL file
2786 $s;
2787 }
2788
2789 # TODO - This is woefully incomplete!
2790 sub parse_schema_element {
2791 my $element = shift;
2792 # Current element is a complex type
2793 if (defined($element->complexType)) {
2794 if (defined($element->complexType->sequence)) {
2795 my @elements;
2796 foreach my $e ($element->complexType->sequence->element) {
2797 push @elements,parse_schema_element($e);
2798 }
2799 return @elements;
2800 }
2801 } elsif ($element->simpleType) {
2802 } else {
2803 return $element;
2804 }
2805 }
2806
2807 sub parse {
2808 my $self = shift->new;
2809 my($s, $service, $port) = @_;
2810 my @result;
2811
2812 # handle imports
2813 $self->import($s);
2814
2815 # handle descriptions without <service>, aka tModel-type descriptions
2816 my @services = $s->service;
2817 my $tns = $s->{'_attr'}->{'targetNamespace'};
2818 # if there is no <service> element we'll provide it
2819 @services = $self->deserializer->deserialize(<<"FAKE")->root->service unless @services;
2820 <definitions>
2821 <service name="@{[$service || 'FakeService']}">
2822 <port name="@{[$port || 'FakePort']}" binding="@{[$s->binding->name]}"/>
2823 </service>
2824 </definitions>
2825 FAKE
2826
2827 my $has_warned = 0;
2828 foreach (@services) {
2829 my $name = $_->name;
2830 next if $service && $service ne $name;
2831 my %services;
2832 foreach ($_->port) {
2833 next if $port && $port ne $_->name;
2834 my $binding = SOAP::Utils::disqualify($_->binding);
2835 my $endpoint = ref $_->address ? $_->address->location : undef;
2836 foreach ($s->binding) {
2837 # is this a SOAP binding?
2838 next unless grep { $_->uri eq 'http://schemas.xmlsoap.org/wsdl/soap/' } $_->binding;
2839 next unless $_->name eq $binding;
2840 my $default_style = $_->binding->style;
2841 my $porttype = SOAP::Utils::disqualify($_->type);
2842 foreach ($_->operation) {
2843 my $opername = $_->name;
2844 $services{$opername} = {}; # should be initialized in 5.7 and after
2845 my $soapaction = $_->operation->soapAction;
2846 my $invocationStyle = $_->operation->style || $default_style || "rpc";
2847 my $encodingStyle = $_->input->body->use || "encoded";
2848 my $namespace = $_->input->body->namespace || $tns;
2849 my @parts;
2850 foreach ($s->portType) {
2851 next unless $_->name eq $porttype;
2852 foreach ($_->operation) {
2853 next unless $_->name eq $opername;
2854 my $inputmessage = SOAP::Utils::disqualify($_->input->message);
2855 foreach my $msg ($s->message) {
2856 next unless $msg->name eq $inputmessage;
2857 if ($invocationStyle eq "document" && $encodingStyle eq "literal") {
2858 # warn "document/literal support is EXPERIMENTAL in SOAP::Lite"
2859 # if !$has_warned && ($has_warned = 1);
2860 my ($input_ns,$input_name) = SOAP::Utils::splitqname($msg->part->element);
2861 foreach my $schema ($s->types->schema) {
2862 foreach my $element ($schema->element) {
2863 next unless $element->name eq $input_name;
2864 push @parts,parse_schema_element($element);
2865 }
2866 $services{$opername}->{parameters} = [ @parts ];
2867 }
2868 } else {
2869 # TODO - support all combinations of doc|rpc/lit|enc.
2870 #warn "$invocationStyle/$encodingStyle is not supported in this version of SOAP::Lite";
2871 @parts = $msg->part;
2872 $services{$opername}->{parameters} = [ @parts ];
2873 }
2874 }
2875 }
2876
2877 for ($services{$opername}) {
2878 $_->{endpoint} = $endpoint;
2879 $_->{soapaction} = $soapaction;
2880 $_->{namespace} = $namespace;
2881 # $_->{parameters} = [@parts];
2882 }
2883
2884 }
2885 }
2886 }
2887 }
2888 # fix nonallowed characters in package name, and add 's' if started with digit
2889 for ($name) { s/\W+/_/g; s/^(\d)/s$1/ }
2890 push @result, $name => \%services;
2891 }
2892 return @result;
2893 }
2894
2895 # ======================================================================
2896
2897 # Naming? SOAP::Service::Schema?
2898 package SOAP::Schema;
2899
2900 use Carp ();
2901
2902 sub DESTROY { SOAP::Trace::objects('()') }
2903
2904 sub new {
2905 my $self = shift;
2906 return $self if ref $self;
2907 unless (ref $self) {
2908 my $class = ref($self) || $self;
2909 require LWP::UserAgent;
2910 $self = bless {
2911 '_deserializer' => SOAP::Schema::Deserializer->new,
2912 '_useragent' => LWP::UserAgent->new,
2913 } => $class;
2914
2915 SOAP::Trace::objects('()');
2916 }
2917
2918 Carp::carp "Odd (wrong?) number of parameters in new()" if $^W && (@_ & 1);
2919 while (@_) { my $method = shift; $self->$method(shift) if $self->can($method) }
2920
2921 return $self;
2922 }
2923
2924 sub schema {
2925 warn "SOAP::Schema->schema has been deprecated. Please use SOAP::Schema->schema_url instead.";
2926 return shift->schema_url(@_);
2927 }
2928
2929 sub BEGIN {
2930 no strict 'refs';
2931 for my $method (qw(deserializer schema_url services useragent stub cache_dir cache_ttl)) {
2932 my $field = '_' . $method;
2933 *$method = sub {
2934 my $self = shift->new;
2935 @_ ? ($self->{$field} = shift, return $self) : return $self->{$field};
2936 }
2937 }
2938 }
2939
2940 sub parse {
2941 my $self = shift;
2942 my $s = $self->deserializer->deserialize($self->access)->root;
2943 # here should be something that defines what schema description we want to use
2944 $self->services({SOAP::Schema::WSDL->base($self->schema_url)->parse($s, @_)});
2945 }
2946
2947 sub refresh_cache {
2948 my $self = shift;
2949 my ($filename,$contents) = @_;
2950 open CACHE,">$filename" or Carp::croak "Could not open cache file for writing: $!";
2951 print CACHE $contents;
2952 close CACHE;
2953 }
2954
2955 sub load {
2956 my $self = shift->new;
2957 local $^W; # supress warnings about redefining
2958 foreach (keys %{$self->services || Carp::croak 'Nothing to load. Schema is not specified'}) {
2959 # TODO - check age of cached file, and delete if older than configured amount
2960 if ($self->cache_dir) {
2961 my $cached_file = File::Spec->catfile($self->cache_dir,$_.".pm");
2962 my $ttl = $self->cache_ttl || $SOAP::Constants::DEFAULT_CACHE_TTL;
2963 open (CACHE, "<$cached_file");
2964 my @stat = stat($cached_file) unless eof(CACHE);
2965 close CACHE;
2966 if (@stat) {
2967 # Cache exists
2968 my $cache_lived = time() - $stat[9];
2969 if ($ttl > 0 && $cache_lived > $ttl) {
2970 $self->refresh_cache($cached_file,$self->generate_stub($_));
2971 }
2972 } else {
2973 # Cache doesn't exist
2974 $self->refresh_cache($cached_file,$self->generate_stub($_));
2975 }
2976 push @INC,$self->cache_dir;
2977 eval "require $_" or Carp::croak "Could not load cached file: $@";
2978 } else {
2979 eval $self->generate_stub($_) or Carp::croak "Bad stub: $@";
2980 }
2981 }
2982 $self;
2983 }
2984
2985 sub access {
2986 my $self = shift->new;
2987 my $url = shift || $self->schema_url || Carp::croak 'Nothing to access. URL is not specified';
2988 $self->useragent->env_proxy if $ENV{'HTTP_proxy'};
2989
2990 my $req = HTTP::Request->new(GET => $url);
2991 $req->proxy_authorization_basic($ENV{'HTTP_proxy_user'}, $ENV{'HTTP_proxy_pass'})
2992 if ($ENV{'HTTP_proxy_user'} && $ENV{'HTTP_proxy_pass'});
2993
2994 my $resp = $self->useragent->request($req);
2995 $resp->is_success ? $resp->content : die "Service description '$url' can't be loaded: ", $resp->status_line, "\n";
2996 }
2997
2998 sub generate_stub {
2999 my $self = shift->new;
3000 my $package = shift;
3001 my $services = $self->services->{$package};
3002 my $schema_url = $self->schema_url;
3003
3004 $self->{'_stub'} = <<"EOP";
3005 package $package;
3006 # Generated by SOAP::Lite (v$SOAP::Lite::VERSION) for Perl -- soaplite.com
3007 # Copyright (C) 2000-2006 Paul Kulchenko, Byrne Reese
3008 # -- generated at [@{[scalar localtime]}]
3009 EOP
3010 $self->{'_stub'} .= "# -- generated from $schema_url\n" if $schema_url;
3011 $self->{'_stub'} .= 'my %methods = ('."\n";
3012 foreach my $service (keys %$services) {
3013 $self->{'_stub'} .= "$service => {\n";
3014 foreach (qw(endpoint soapaction namespace)) {
3015 $self->{'_stub'} .= " $_ => '".$services->{$service}{$_}."',\n";
3016 }
3017 $self->{'_stub'} .= " parameters => [\n";
3018 foreach (@{$services->{$service}{parameters}}) {
3019 # next unless $_;
3020 $self->{'_stub'} .= " SOAP::Data->new(name => '".$_->name."', type => '".$_->type."', attr => {";
3021 $self->{'_stub'} .= do { my %attr = %{$_->attr};
3022 join(', ', map {"'$_' => '$attr{$_}'"}
3023 grep {/^xmlns:(?!-)/}
3024 keys %attr)
3025 };
3026 $self->{'_stub'} .= "}),\n";
3027 }
3028 $self->{'_stub'} .= " ], # end parameters\n";
3029 $self->{'_stub'} .= " }, # end $service\n";
3030 }
3031 $self->{'_stub'} .= "); # end my %methods\n";
3032 $self->{'_stub'} .= <<'EOP';
3033
3034 use SOAP::Lite;
3035 use Exporter;
3036 use Carp ();
3037
3038 use vars qw(@ISA $AUTOLOAD @EXPORT_OK %EXPORT_TAGS);
3039 @ISA = qw(Exporter SOAP::Lite);
3040 @EXPORT_OK = (keys %methods);
3041 %EXPORT_TAGS = ('all' => [@EXPORT_OK]);
3042
3043 sub _call {
3044 my ($self, $method) = (shift, shift);
3045 my $name = UNIVERSAL::isa($method => 'SOAP::Data') ? $method->name : $method;
3046 my %method = %{$methods{$name}};
3047 $self->proxy($method{endpoint} || Carp::croak "No server address (proxy) specified")
3048 unless $self->proxy;
3049 my @templates = @{$method{parameters}};
3050 my @parameters = ();
3051 foreach my $param (@_) {
3052 if (@templates) {
3053 my $template = shift @templates;
3054 my ($prefix,$typename) = SOAP::Utils::splitqname($template->type);
3055 my $method = 'as_'.$typename;
3056 # TODO - if can('as_'.$typename) {...}
3057 my $result = $self->serializer->$method($param, $template->name, $template->type, $template->attr);
3058 push(@parameters, $template->value($result->[2]));
3059 } else {
3060 push(@parameters, $param);
3061 }
3062 }
3063 $self->endpoint($method{endpoint})
3064 ->ns($method{namespace})
3065 ->on_action(sub{qq!"$method{soapaction}"!});
3066 EOP
3067 my $namespaces = $self->deserializer->ids->[1];
3068 foreach my $key (keys %{$namespaces}) {
3069 my ($ns,$prefix) = SOAP::Utils::splitqname($key);
3070 $self->{'_stub'} .= ' $self->serializer->register_ns("'.$namespaces->{$key}.'","'.$prefix.'");'."\n"
3071 if ($ns eq "xmlns");
3072 }
3073 $self->{'_stub'} .= <<'EOP';
3074 my $som = $self->SUPER::call($method => @parameters);
3075 if ($self->want_som) {
3076 return $som;
3077 }
3078 UNIVERSAL::isa($som => 'SOAP::SOM') ? wantarray ? $som->paramsall : $som->result : $som;
3079 }
3080
3081 sub BEGIN {
3082 no strict 'refs';
3083 for my $method (qw(want_som)) {
3084 my $field = '_' . $method;
3085 *$method = sub {
3086 my $self = shift->new;
3087 @_ ? ($self->{$field} = shift, return $self) : return $self->{$field};
3088 }
3089 }
3090 }
3091 no strict 'refs';
3092 for my $method (@EXPORT_OK) {
3093 my %method = %{$methods{$method}};
3094 *$method = sub {
3095 my $self = UNIVERSAL::isa($_[0] => __PACKAGE__)
3096 ? ref $_[0] ? shift # OBJECT
3097 # CLASS, either get self or create new and assign to self
3098 : (shift->self || __PACKAGE__->self(__PACKAGE__->new))
3099 # function call, either get self or create new and assign to self
3100 : (__PACKAGE__->self || __PACKAGE__->self(__PACKAGE__->new));
3101 $self->_call($method, @_);
3102 }
3103 }
3104
3105 sub AUTOLOAD {
3106 my $method = substr($AUTOLOAD, rindex($AUTOLOAD, '::') + 2);
3107 return if $method eq 'DESTROY' || $method eq 'want_som';
3108 die "Unrecognized method '$method'. List of available method(s): @EXPORT_OK\n";
3109 }
3110
3111 1;
3112 EOP
3113 return $self->stub;
3114 }
3115
3116 # ======================================================================
3117
3118 package SOAP;
3119
3120 use vars qw($AUTOLOAD);
3121 require URI;
3122
3123 my $soap; # shared between SOAP and SOAP::Lite packages
3124
3125 { no strict 'refs';
3126 *AUTOLOAD = sub {
3127 local($1,$2);
3128 my($package, $method) = $AUTOLOAD =~ m/(?:(.+)::)([^:]+)$/;
3129 return if $method eq 'DESTROY';
3130
3131 my $soap = ref $_[0] && UNIVERSAL::isa($_[0] => 'SOAP::Lite') ? $_[0] : $soap || die "SOAP:: prefix shall only be used in combination with +autodispatch option\n";
3132
3133 my $uri = URI->new($soap->uri);
3134 my $currenturi = $uri->path;
3135 $package =
3136 ref $_[0] && UNIVERSAL::isa($_[0] => 'SOAP::Lite') ? $currenturi :
3137 $package eq 'SOAP' ? ref $_[0] || ($_[0] eq 'SOAP'
3138 ? $currenturi || Carp::croak "URI is not specified for method call" : $_[0]) :
3139 $package eq 'main' ? $currenturi || $package
3140 : $package;
3141
3142 # drop first parameter if it's a class name
3143 {
3144 my $pack = $package;
3145 for ($pack) { s!^/!!; s!/!::!g; }
3146 shift @_ if @_ && !ref $_[0] && ($_[0] eq $pack || $_[0] eq 'SOAP') ||
3147 ref $_[0] && UNIVERSAL::isa($_[0] => 'SOAP::Lite');
3148 }
3149
3150 for ($package) { s!::!/!g; s!^/?!/!; }
3151 $uri->path($package);
3152
3153 my $som = $soap->uri($uri->as_string)->call($method => @_);
3154 UNIVERSAL::isa($som => 'SOAP::SOM') ? wantarray ? $som->paramsall : $som->result
3155 : $som;
3156 };
3157 }
3158
3159 # ======================================================================
3160
3161 package SOAP::Lite;
3162
3163 use vars qw($AUTOLOAD @ISA);
3164 use Carp ();
3165
3166 use SOAP::Packager;
3167
3168 @ISA = qw(SOAP::Cloneable);
3169
3170 # provide access to global/autodispatched object
3171 sub self { @_ > 1 ? $soap = $_[1] : $soap }
3172
3173 # no more warnings about "used only once"
3174 *UNIVERSAL::AUTOLOAD if 0;
3175
3176 sub autodispatched { \&{*UNIVERSAL::AUTOLOAD} eq \&{*SOAP::AUTOLOAD} };
3177
3178 sub soapversion {
3179 my $self = shift;
3180 my $version = shift or return $SOAP::Constants::SOAP_VERSION;
3181
3182 ($version) = grep { $SOAP::Constants::SOAP_VERSIONS{$_}->{NS_ENV} eq $version
3183 } keys %SOAP::Constants::SOAP_VERSIONS
3184 unless exists $SOAP::Constants::SOAP_VERSIONS{$version};
3185
3186 die qq!$SOAP::Constants::WRONG_VERSION Supported versions:\n@{[
3187 join "\n", map {" $_ ($SOAP::Constants::SOAP_VERSIONS{$_}->{NS_ENV})"} keys %SOAP::Constants::SOAP_VERSIONS
3188 ]}\n!
3189 unless defined($version) && defined(my $def = $SOAP::Constants::SOAP_VERSIONS{$version});
3190
3191 foreach (keys %$def) {
3192 eval "\$SOAP::Constants::$_ = '$SOAP::Constants::SOAP_VERSIONS{$version}->{$_}'";
3193 }
3194
3195 $SOAP::Constants::SOAP_VERSION = $version;
3196 $self;
3197 }
3198
3199 BEGIN { SOAP::Lite->soapversion(1.1) }
3200
3201 sub import {
3202 my $pkg = shift;
3203 my $caller = caller;
3204 no strict 'refs';
3205 # emulate 'use SOAP::Lite 0.99' behavior
3206 $pkg->require_version(shift) if defined $_[0] && $_[0] =~ /^\d/;
3207
3208 while (@_) {
3209 my $command = shift;
3210
3211 my @parameters = UNIVERSAL::isa($_[0] => 'ARRAY') ? @{shift()} : shift
3212 if @_ && $command ne 'autodispatch';
3213 if ($command eq 'autodispatch' || $command eq 'dispatch_from') {
3214 $soap = ($soap||$pkg)->new;
3215 no strict 'refs';
3216 foreach ($command eq 'autodispatch' ? 'UNIVERSAL' : @parameters) {
3217 my $sub = "${_}::AUTOLOAD";
3218 defined &{*$sub}
3219 ? (\&{*$sub} eq \&{*SOAP::AUTOLOAD} ? () : Carp::croak "$sub already assigned and won't work with DISPATCH. Died")
3220 : (*$sub = *SOAP::AUTOLOAD);
3221 }
3222 } elsif ($command eq 'service') {
3223 foreach (keys %{SOAP::Schema->schema_url(shift(@parameters))->parse(@parameters)->load->services}) {
3224 $_->export_to_level(1, undef, ':all');
3225 }
3226 } elsif ($command eq 'debug' || $command eq 'trace') {
3227 SOAP::Trace->import(@parameters ? @parameters : 'all');
3228 } elsif ($command eq 'import') {
3229 local $^W; # supress warnings about redefining
3230 my $package = shift(@parameters);
3231 $package->export_to_level(1, undef, @parameters ? @parameters : ':all') if $package;
3232 } else {
3233 Carp::carp "Odd (wrong?) number of parameters in import(), still continue" if $^W && !(@parameters & 1);
3234 $soap = ($soap||$pkg)->$command(@parameters);
3235 }
3236 }
3237 }
3238
3239 sub DESTROY { SOAP::Trace::objects('()') }
3240
3241 sub new {
3242 my $self = shift;
3243 return $self if ref $self;
3244 unless (ref $self) {
3245 my $class = ref($self) || $self;
3246 # Check whether we can clone. Only the SAME class allowed, no inheritance
3247 $self = ref($soap) eq $class ? $soap->clone : {
3248 _transport => SOAP::Transport->new,
3249 _serializer => SOAP::Serializer->new,
3250 _deserializer => SOAP::Deserializer->new,
3251 _packager => SOAP::Packager::MIME->new,
3252 _schema => undef,
3253 _autoresult => 0,
3254 _on_action => sub { sprintf '"%s#%s"', shift || '', shift },
3255 _on_fault => sub {ref $_[1] ? return $_[1] : Carp::croak $_[0]->transport->is_success ? $_[1] : $_[0]->transport->status},
3256 };
3257 bless $self => $class;
3258 $self->on_nonserialized($self->on_nonserialized || $self->serializer->on_nonserialized);
3259 SOAP::Trace::objects('()');
3260 }
3261
3262 Carp::carp "Odd (wrong?) number of parameters in new()" if $^W && (@_ & 1);
3263 while (@_) { my($method, $params) = splice(@_,0,2);
3264 $self->can($method)
3265 ? $self->$method(ref $params eq 'ARRAY' ? @$params : $params)
3266 : $^W && Carp::carp "Unrecognized parameter '$method' in new()"
3267 }
3268
3269 return $self;
3270 }
3271
3272 sub init_context {
3273 my $self = shift->new;
3274 $self->{'_deserializer'}->{'_context'} = $self;
3275 $self->{'_serializer'}->{'_context'} = $self;
3276 }
3277
3278 sub destroy_context {
3279 my $self = shift;
3280 delete($self->{'_deserializer'}->{'_context'});
3281 delete($self->{'_serializer'}->{'_context'})
3282 }
3283
3284 # Naming? wsdl_parser
3285 sub schema {
3286 my $self = shift;
3287 if (@_) {
3288 $self->{'_schema'} = shift;
3289 return $self;
3290 } else {
3291 if (!defined $self->{'_schema'}) { $self->{'_schema'} = SOAP::Schema->new; }
3292 return $self->{'_schema'};
3293 }
3294 }
3295
3296 sub BEGIN {
3297 no strict 'refs';
3298 for my $method (qw(serializer deserializer)) {
3299 my $field = '_' . $method;
3300 *$method = sub {
3301 my $self = shift->new;
3302 if (@_) {
3303 my $context = $self->{$field}->{'_context'}; # save the old context
3304 $self->{$field} = shift;
3305 $self->{$field}->{'_context'} = $context; # restore the old context
3306 return $self;
3307 } else {
3308 return $self->{$field};
3309 }
3310 }
3311 }
3312 for my $method (qw(endpoint transport outputxml autoresult packager)) {
3313 my $field = '_' . $method;
3314 *$method = sub {
3315 my $self = shift->new;
3316 @_ ? ($self->{$field} = shift, return $self) : return $self->{$field};
3317 }
3318 }
3319 for my $method (qw(on_action on_fault on_nonserialized)) {
3320 my $field = '_' . $method;
3321 *$method = sub {
3322 my $self = shift->new;
3323 return $self->{$field} unless @_;
3324 local $@;
3325 # commented out because that 'eval' was unsecure
3326 # > ref $_[0] eq 'CODE' ? shift : eval shift;
3327 # Am I paranoid enough?
3328 $self->{$field} = shift;
3329 Carp::croak $@ if $@;
3330 Carp::croak "$method() expects subroutine (CODE) or string that evaluates into subroutine (CODE)"
3331 unless ref $self->{$field} eq 'CODE';
3332 return $self;
3333 }
3334 }
3335 # SOAP::Transport Shortcuts
3336 # TODO - deprecate proxy() in favor of new language endpoint_url()
3337 for my $method (qw(proxy)) {
3338 *$method = sub {
3339 my $self = shift->new;
3340 @_ ? ($self->transport->$method(@_), return $self) : return $self->transport->$method();
3341 }
3342 }
3343
3344 # SOAP::Seriailizer Shortcuts
3345 for my $method (qw(autotype readable envprefix encodingStyle
3346 encprefix multirefinplace encoding
3347 typelookup header maptype xmlschema
3348 uri ns_prefix ns_uri use_prefix use_default_ns
3349 ns default_ns)) {
3350 *$method = sub {
3351 my $self = shift->new;
3352 @_ ? ($self->serializer->$method(@_), return $self) : return $self->serializer->$method();
3353 }
3354 }
3355 # SOAP::Schema Shortcuts
3356 for my $method (qw(cache_dir cache_ttl)) {
3357 *$method = sub {
3358 my $self = shift->new;
3359 @_ ? ($self->schema->$method(@_), return $self) : return $self->schema->$method();
3360 }
3361 }
3362 }
3363
3364 sub parts {
3365 my $self = shift;
3366 $self->packager->parts(@_);
3367 return $self;
3368 }
3369
3370 # Naming? wsdl
3371 sub service {
3372 my $self = shift->new;
3373 return $self->{'_service'} unless @_;
3374 $self->schema->schema_url($self->{'_service'} = shift);
3375 my %services = %{$self->schema->parse(@_)->load->services};
3376
3377 Carp::croak "More than one service in service description. Service and port names have to be specified\n"
3378 if keys %services > 1;
3379 my $service = (keys %services)[0]->new;
3380 return $service;
3381 }
3382
3383 sub AUTOLOAD {
3384 my $method = substr($AUTOLOAD, rindex($AUTOLOAD, '::') + 2);
3385 return if $method eq 'DESTROY';
3386
3387 ref $_[0] or Carp::croak qq!Can\'t locate class method "$method" via package \"! . __PACKAGE__ .'\"';
3388
3389 no strict 'refs';
3390 *$AUTOLOAD = sub {
3391 my $self = shift;
3392 my $som = $self->call($method => @_);
3393 return $self->autoresult && UNIVERSAL::isa($som => 'SOAP::SOM')
3394 ? wantarray ? $som->paramsall : $som->result
3395 : $som;
3396 };
3397 goto &$AUTOLOAD;
3398 }
3399
3400 sub call {
3401 SOAP::Trace::trace('()');
3402 my $self = shift;
3403 # Why is this here? Can't call be null? Indicating that there are no input arguments?
3404 #return $self->{_call} unless @_;
3405 die "A service address has not been specified either by using SOAP::Lite->proxy() or a service description)\n"
3406 unless defined $self->proxy && UNIVERSAL::isa($self->proxy => 'SOAP::Client');
3407
3408 $self->init_context();
3409 my $serializer = $self->serializer;
3410 $serializer->on_nonserialized($self->on_nonserialized);
3411 my $response = $self->transport->send_receive(
3412 context => $self, # this is provided for context
3413 endpoint => $self->endpoint,
3414 action => scalar($self->on_action->($serializer->uriformethod($_[0]))),
3415 # leave only parameters so we can later update them if required
3416 envelope => $serializer->envelope(method => shift, @_),
3417 # envelope => $serializer->envelope(method => shift, @_),
3418 encoding => $serializer->encoding,
3419 parts => @{$self->packager->parts} ? $self->packager->parts : undef,
3420 );
3421
3422 return $response if $self->outputxml;
3423
3424 # deserialize and store result
3425 my $result = $self->{'_call'} = eval { $self->deserializer->deserialize($response) } if $response;
3426
3427 if (!$self->transport->is_success || # transport fault
3428 $@ || # not deserializible
3429 # fault message even if transport OK
3430 # or no transport error (for example, fo TCP, POP3, IO implementations)
3431 UNIVERSAL::isa($result => 'SOAP::SOM') && $result->fault) {
3432 return $self->{'_call'} = ($self->on_fault->($self, $@ ? $@ . ($response || '') : $result) || $result);
3433 }
3434
3435 return unless $response; # nothing to do for one-ways
3436
3437 # little bit tricky part that binds in/out parameters
3438 if (UNIVERSAL::isa($result => 'SOAP::SOM') &&
3439 ($result->paramsout || $result->headers) &&
3440 $serializer->signature) {
3441 my $num = 0;
3442 my %signatures = map {$_ => $num++} @{$serializer->signature};
3443 for ($result->dataof(SOAP::SOM::paramsout), $result->dataof(SOAP::SOM::headers)) {
3444 my $signature = join $;, $_->name, $_->type || '';
3445 if (exists $signatures{$signature}) {
3446 my $param = $signatures{$signature};
3447 my($value) = $_->value; # take first value
3448 UNIVERSAL::isa($_[$param] => 'SOAP::Data') ? $_[$param]->SOAP::Data::value($value) :
3449 UNIVERSAL::isa($_[$param] => 'ARRAY') ? (@{$_[$param]} = @$value) :
3450 UNIVERSAL::isa($_[$param] => 'HASH') ? (%{$_[$param]} = %$value) :
3451 UNIVERSAL::isa($_[$param] => 'SCALAR') ? (${$_[$param]} = $$value) :
3452 ($_[$param] = $value)
3453 }
3454 }
3455 }
3456 $self->destroy_context();
3457 return $result;
3458 } # end of call()
3459
3460 # ======================================================================
3461
3462 package SOAP::Lite::COM;
3463
3464 require SOAP::Lite;
3465
3466 sub required {
3467 foreach (qw(
3468 URI::_foreign URI::http URI::https
3469 LWP::Protocol::http LWP::Protocol::https LWP::Authen::Basic LWP::Authen::Digest
3470 HTTP::Daemon Compress::Zlib SOAP::Transport::HTTP
3471 XMLRPC::Lite XMLRPC::Transport::HTTP
3472 )) {
3473 eval join ';', 'local $SIG{__DIE__}', "require $_";
3474 }
3475 }
3476
3477 sub new { required; SOAP::Lite->new(@_) }
3478
3479 sub create; *create = \&new; # make alias. Somewhere 'new' is registered keyword
3480
3481 sub soap; *soap = \&new; # also alias. Just to be consistent with .xmlrpc call
3482
3483 sub xmlrpc { required; XMLRPC::Lite->new(@_) }
3484
3485 sub server { required; shift->new(@_) }
3486
3487 sub data { SOAP::Data->new(@_) }
3488
3489 sub header { SOAP::Header->new(@_) }
3490
3491 sub hash { +{@_} }
3492
3493 sub instanceof {
3494 my $class = shift;
3495 die "Incorrect class name" unless $class =~ /^(\w[\w:]*)$/;
3496 eval "require $class";
3497 $class->new(@_);
3498 }
3499
3500 # ======================================================================
3501
3502 1;
3503
3504 __END__
3505
3506 =pod
3507
3508 =head1 NAME
3509
3510 SOAP::Lite - Perl's Web Services Toolkit
3511
3512 =head1 DESCRIPTION
3513
3514 SOAP::Lite is a collection of Perl modules which provides a simple and lightweight interface to the Simple Object Access Protocol (SOAP) both on client and server side.
3515
3516 =head1 OVERVIEW OF CLASSES AND PACKAGES
3517
3518 =over
3519
3520 =item F<lib/SOAP/Lite.pm>
3521
3522 L<SOAP::Lite> - Main class provides all logic
3523
3524 L<SOAP::Transport> - Supports transport architecture
3525
3526 L<SOAP::Data> - Provides extensions for serialization architecture
3527
3528 L<SOAP::Header> - Provides extensions for header serialization
3529
3530 SOAP::Parser - Parses XML file into object tree
3531
3532 L<SOAP::Serializer> - Serializes data structures to SOAP package
3533
3534 SOAP::Deserializer - Deserializes results of SOAP::Parser into objects
3535
3536 L<SOAP::SOM> - Provides access to deserialized object tree
3537
3538 L<SOAP::Constants> - Provides access to common constants
3539
3540 L<SOAP::Trace> - Provides tracing facilities
3541
3542 L<SOAP::Schema> - Provides access and stub(s) for schema(s)
3543
3544 L<SOAP::Schema::WSDL|SOAP::Schema/SOAP::Schema::WSDL> - WSDL implementation for SOAP::Schema
3545
3546 L<SOAP::Server> - Handles requests on server side
3547
3548 SOAP::Server::Object - Handles objects-by-reference
3549
3550 L<SOAP::Fault> - Provides support for Faults on server side
3551
3552 L<SOAP::Utils> - A set of private and public utility subroutines
3553
3554 =item F<lib/SOAP/Packager.pm>
3555
3556 L<SOAP::Packager> - Provides an abstract class for implementing custom packagers.
3557
3558 L<SOAP::Packager::MIME|SOAP::Packager/SOAP::Packager::MIME> - Provides MIME support to SOAP::Lite
3559
3560 L<SOAP::Packager::DIME|SOAP::Packager/SOAP::Packager::DIME> - Provides DIME support to SOAP::Lite
3561
3562 =item F<lib/SOAP/Transport/HTTP.pm>
3563
3564 L<SOAP::Transport::HTTP::Client|SOAP::Transport/SOAP::Transport::HTTP::Client> - Client interface to HTTP transport
3565
3566 L<SOAP::Transport::HTTP::Server|SOAP::Transport/SOAP::Transport::HTTP::Server> - Server interface to HTTP transport
3567
3568 L<SOAP::Transport::HTTP::CGI|SOAP::Transport/SOAP::Transport::HTTP::CGI> - CGI implementation of server interface
3569
3570 L<SOAP::Transport::HTTP::Daemon|SOAP::Transport/SOAP::Transport::HTTP::Daemon> - Daemon implementation of server interface
3571
3572 L<SOAP::Transport::HTTP::Apache|SOAP::Transport/SOAP::Transport::HTTP::Apache> - mod_perl implementation of server interface
3573
3574 =item F<lib/SOAP/Transport/POP3.pm>
3575
3576 L<SOAP::Transport::POP3::Server|SOAP::Transport/SOAP::Transport::POP3::Server> - Server interface to POP3 protocol
3577
3578 =item F<lib/SOAP/Transport/MAILTO.pm>
3579
3580 L<SOAP::Transport::MAILTO::Client|SOAP::Transport/SOAP::Transport::MAILTO::Client> - Client interface to SMTP/sendmail
3581
3582 =item F<lib/SOAP/Transport/LOCAL.pm>
3583
3584 L<SOAP::Transport::LOCAL::Client|SOAP::Transport/SOAP::Transport::LOCAL::Client> - Client interface to local transport
3585
3586 =item F<lib/SOAP/Transport/TCP.pm>
3587
3588 L<SOAP::Transport::TCP::Server|SOAP::Transport/SOAP::Transport::TCP::Server> - Server interface to TCP protocol
3589
3590 L<SOAP::Transport::TCP::Client|SOAP::Transport/SOAP::Transport::TCP::Client> - Client interface to TCP protocol
3591
3592 =item F<lib/SOAP/Transport/IO.pm>
3593
3594 L<SOAP::Transport::IO::Server|SOAP::Transport/SOAP::Transport::IO::Server> - Server interface to IO transport
3595
3596 =back
3597
3598 =head1 METHODS
3599
3600 The first group of methods presented are the constructor and the accessor methods. All accessor methods share the trait of returning the current appropriate value when called with no arguments, while returning the object reference itself when called with a new value for the field in question. This allows the set-attribute calls to be chained together.
3601
3602 =over
3603
3604 =item new(optional key/value pairs)
3605
3606 $client = SOAP::Lite->new(proxy => $endpoint)
3607
3608 This is the constructor of the class. Many of the accessor methods defined here may be initialized at creation by providing their name as a key, followed by the desired value. The example provides the value for the proxy element of the client.
3609
3610 =item transport(optional transport object)
3611
3612 $transp = $client->transport( );
3613
3614 Provides access to the transport object that the client has allocated to manage the communication layer operations. You can set this by passing a new object that derives from C<SOAP::Transport>, but this is generally not needed or recommended. Several of the following methods are shortcuts to this object's accessors.
3615
3616 =item serializer(optional serializer object)
3617
3618 $serial = $client->serializer( )
3619
3620 Provides access to the C<SOAP::Serializer> object that the client uses to transform the elements and data of a request into an XML document for the sake of transport. As with transport, this may be set by providing a new object reference, but it is generally not needed.
3621
3622 =item packager(optional packager object)
3623
3624 $packager = $client->packager( )
3625
3626 Provides access to the C<SOAP::Packager> object that the client uses to manage the use of attachments. The default packager is a MIME packager, but unless you specify parts to send, no MIME formatting will be done.
3627
3628 See also: L<SOAP::Packager>.
3629
3630 =item proxy(endpoint, optional extra arguments)
3631
3632 $client->proxy('http://soap.xml.info/ endPoint');
3633
3634 The proxy is the server or endpoint to which the client is going to connect. It shouldn't be confused with the uri method discussed later, which refers to a different element of the conversation. This method allows the setting of the endpoint, along with any extra information that the transport object may need when communicating the request. Indeed, this method is actually an alias to the proxy method of L<SOAP::Transport>. It is the same as typing:
3635
3636 $client->transport( )->proxy(...arguments);
3637
3638 When extra information is needed, it is also passed in the call to this method. Connecting to a server that uses browser cookies for authentication can be done by creating an instance of the HTTP::Cookies class (from the LWP package) and passing it as the value following a key of cookie_jar. The value for sockettime-outs may also be set this way. The full range of options vary by transport method. One common theme is that the endpoint string is always the first argument, with all additional arguments following it.
3639
3640 The following is a list of optional arguments that may be passed to C<proxy()>:
3641
3642 =over
3643
3644 =item options( HASH )
3645
3646 This is for SOAP::Lite specific options. Currently the only option one is allowed to set is the C<compress_threshold> option. See L<COMPRESSION|SOAP::Transport/"COMPRESSION"> in L<HTTP::Transport>.
3647
3648 =item All initialization options from LWP::UserAgent
3649
3650 =back
3651
3652 For example, if you wish to set the HTTP timeout for a SOAP::Lite client to 5 seconds, use the following code:
3653
3654 my $soap = SOAP::Lite
3655 ->uri($uri)
3656 ->proxy($proxyUrl, timeout => 5 );
3657
3658 See L<LWP::UserAgent>.
3659
3660 =item endpoint(optional new endpoint address)
3661
3662 $client->endpoint('http://soap.xml.info/ newPoint')
3663
3664 It may be preferable to set a new endpoint without the additional work of examining the new address for protocol information and checking to ensure the support code is loaded and available. This method allows the caller to change the endpoint that the client is currently set to connect to, without reloading the relevant transport code. Note that the proxy method must have already been called before this method is used.
3665
3666 =item service(service URL)
3667
3668 $client->service('http://svc.perl.org/Svc.wsdl');
3669
3670 C<SOAP::Lite> offers some support for creating method stubs from service descriptions. At present, only WSDL support is in place. This method loads the specified WSDL schema and uses it as the basis for generating stubs.
3671
3672 =item outputxml(boolean)
3673
3674 $client->outputxml('true');
3675
3676 Controls whether the returned information from a remote method call is the raw XML from the server. The default is to process the data from the server and present it to the caller as an object of the L<SOAP::SOM> class. If the application prefers to use a different parser or do something else entirely with the results, this method may be used to inhibit the parsing of the returned information.
3677
3678 =item autotype(boolean)
3679
3680 $client->autotype(0);
3681
3682 This method is a shortcut for:
3683
3684 $client->serializer->autotype(boolean);
3685
3686 By default, the serializer tries to automatically deduce types for the data being sent in a message. Setting a false value with this method disables the behavior.
3687
3688 =item readable(boolean)
3689
3690 $client->readable(1);
3691
3692 This method is a shortcut for:
3693
3694 $client->serializer->readable(boolean);
3695
3696 When this is used to set a true value for this property, the generated XML sent to the endpoint has extra characters (spaces and new lines) added in to make the XML itself more readable to human eyes (presumably for debugging). The default is to not send any additional characters.
3697
3698 =item default_ns($uri)
3699
3700 Sets the default namespace for the request to the specified uri. This overrides any previous namespace declaration that may have been set using a previous call to C<ns()> or C<default_ns()>. Setting the default namespace causes elements to be serialized without a namespace prefix, like so:
3701
3702 <soap:Envelope>
3703 <soap:Body>
3704 <myMethod xmlns="http://www.someuri.com">
3705 <foo />
3706 </myMethod>
3707 </soap:Body>
3708 </soap:Envelope>
3709
3710 =item ns($uri,$prefix=undef)
3711
3712 Sets the namespace uri and optionally the namespace prefix for the request to the specified values. This overrides any previous namespace declaration that may have been set using a previous call to C<ns()> or C<default_ns()>. If a prefix is not specified, one will be generated for you automatically. Setting the namespace causes elements to be serialized with a declared namespace prefix, like so:
3713
3714 <soap:Envelope>
3715 <soap:Body>
3716 <my:myMethod xmlns:my="http://www.someuri.com">
3717 <my:foo />
3718 </my:myMethod>
3719 </soap:Body>
3720 </soap:Envelope>
3721
3722 =item use_prefix(boolean)
3723
3724 Deprecated - the C<use_prefix()> subroutine has been deprecated in order to provide a more intuitive naming scheme for subroutines that set namespaces. C<use_prefix()> was originally added to allow users to turn on or off the use of a default namespace. This functionality is being replaced by C<ns()> and C<default_ns()>.
3725
3726 Shortcut for C<< serializer->use_prefix() >>. This lets you turn on/off the use of a namespace prefix for the children of the /Envelope/Body element. Default is 'true'. (This was introduced in 0.61 for better .NET compatibility)
3727
3728 When use_prefix is set to 'true', serialized XML will look like this:
3729
3730 <SOAP-ENV:Envelope ...attributes skipped>
3731 <SOAP-ENV:Body>
3732 <namesp1:mymethod xmlns:namesp1="urn:MyURI" />
3733 </SOAP-ENV:Body>
3734 </SOAP-ENV:Envelope>
3735
3736 When use_prefix is set to 'true', serialized XML will look like this:
3737
3738 <SOAP-ENV:Envelope ...attributes skipped>
3739 <SOAP-ENV:Body>
3740 <mymethod xmlns="urn:MyURI" />
3741 </SOAP-ENV:Body>
3742 </SOAP-ENV:Envelope>
3743
3744 =item soapversion(optional value)
3745
3746 $client->soapversion('1.2');
3747
3748 If no parameter is given, returns the current version of SOAP that is being used by the client object to encode requests. If a parameter is given, the method attempts to set that as the version of SOAP being used. The value should be either 1.1 or 1.2.
3749
3750 =item envprefix(QName)
3751
3752 $client->envprefix('env');
3753
3754 This method is a shortcut for:
3755
3756 $client->serializer->envprefix(QName);
3757
3758 The namespace label used for the main SOAP namespace elements (such as Envelope, Body, and the attributes) defaults to SOAP-ENV. As has been discussed in earlier chapters, the label itself isn't important. But applications that wish to explicitly choose a different one (such as env to denote a SOAP 1.2 message) may do so with this method.
3759
3760 =item encprefix(QName)
3761
3762 $client->encprefix('enc');
3763
3764 This method is a shortcut for:
3765
3766 $client->serializer->encprefix(QName);
3767
3768 As with the envprefix method, this gets or sets the label used for the namespace of the encoding rules. The default value is SOAP-ENC, as is generally used in SOAP 1.1 messages, though the label itself has no actual meaning.
3769
3770 =back
3771
3772 While it may seem to be an unnecessary operation to set a value that isn't relevant to the message, such as the namespace labels for the envelope and encoding URNs, the ability to set these labels explicitly can prove to be a great aid in distinguishing and debugging messages on the server side of operations.
3773
3774 =over
3775
3776 =item encoding(encoding URN)
3777
3778 $client->encoding($soap_12_encoding_URN);
3779
3780 This method is a shortcut for:
3781
3782 $client->serializer->encoding(args);
3783
3784 Where the earlier method dealt with the label used for the attributes related to the SOAP encoding scheme, this method actually sets the URN to be specified as the encoding scheme for the message. The default is to specify the encoding for SOAP 1.1, so this is handy for applications that need to encode according to SOAP 1.2 rules.
3785
3786 =item typelookup
3787
3788 $client->typelookup;
3789
3790 This method is a shortcut for:
3791
3792 $client->serializer->typelookup;
3793
3794 Gives the application access to the type-lookup table from the serializer object. See the section on L<SOAP::Serializer>.
3795
3796 =item uri(service specifier)
3797
3798 Deprecated - the C<uri> subroutine has been deprecated in order to provide a more intuitive naming scheme for subroutines that set namespaces. In the future, you will be required to use either the C<ns()> or C<default_ns()> subroutines in lieu of C<uri()>.
3799
3800 $client->uri($service_uri);
3801
3802 This method is a shortcut for:
3803
3804 $client->serializer->uri(service);
3805
3806 The URI associated with this accessor on a client object is the service-specifier for the request, often encoded for HTTP-based requests as the SOAPAction header. While the names may seem confusing, this method doesn't specify the endpoint itself. In most circumstances, the C<uri> refers to the namespace used for the request.
3807
3808 Often times, the value may look like a valid URL. Despite this, it doesn't have to point to an existing resource (and often doesn't). This method sets and retrieves this value from the object. Note that no transport code is triggered by this because it has no direct effect on the transport of the object.
3809
3810 =item multirefinplace(boolean)
3811
3812 $client->multirefinplace(1);
3813
3814 This method is a shortcut for:
3815
3816 $client->serializer->multirefinplace(boolean);
3817
3818 Controls how the serializer handles values that have multiple references to them. Recall from previous SOAP chapters that a value may be tagged with an identifier, then referred to in several places. When this is the case for a value, the serializer defaults to putting the data element towards the top of the message, right after the opening tag of the method-specification. It is serialized as a standalone entity with an ID that is then referenced at the relevant places later on. If this method is used to set a true value, the behavior is different. When the multirefinplace attribute is true, the data is serialized at the first place that references it, rather than as a separate element higher up in the body. This is more compact but may be harder to read or trace in a debugging environment.
3819
3820 =item parts( ARRAY )
3821
3822 Used to specify an array of L<MIME::Entity>'s to be attached to the transmitted SOAP message. Attachments that are returned in a response can be accessed by C<SOAP::SOM::parts()>.
3823
3824 =item self
3825
3826 $ref = SOAP::Lite->self;
3827
3828 Returns an object reference to the default global object the C<SOAP::Lite> package maintains. This is the object that processes many of the arguments when provided on the use line.
3829
3830 =back
3831
3832 The following method isn't an accessor style of method but neither does it fit with the group that immediately follows it:
3833
3834 =over
3835
3836 =item call(arguments)
3837
3838 $client->call($method => @arguments);
3839
3840 As has been illustrated in previous chapters, the C<SOAP::Lite> client objects can manage remote calls with auto-dispatching using some of Perl's more elaborate features. call is used when the application wants a greater degree of control over the details of the call itself. The method may be built up from a L<SOAP::Data> object, so as to allow full control over the namespace associated with the tag, as well as other attributes like encoding. This is also important for calling methods that contain characters not allowable in Perl function names, such as A.B.C.
3841
3842 =back
3843
3844 The next four methods used in the C<SOAP::Lite> class are geared towards handling the types of events than can occur during the message lifecycle. Each of these sets up a callback for the event in question:
3845
3846 =over
3847
3848 =item on_action(callback)
3849
3850 $client->on_action(sub { qq("$_[0]") });
3851
3852 Triggered when the transport object sets up the SOAPAction header for an HTTP-based call. The default is to set the header to the string, uri#method, in which URI is the value set by the uri method described earlier, and method is the name of the method being called. When called, the routine referenced (or the closure, if specified as in the example) is given two arguments, uri and method, in that order.
3853
3854 =item on_fault(callback)
3855
3856 $client->on_fault(sub { popup_dialog($_[1]) });
3857
3858 Triggered when a method call results in a fault response from the server. When it is called, the argument list is first the client object itself, followed by the object that encapsulates the fault. In the example, the fault object is passed (without the client object) to a hypothetical GUI function that presents an error dialog with the text of fault extracted from the object (which is covered shortly under the L<SOAP::SOM> methods).
3859
3860 =item on_nonserialized(callback)
3861
3862 $client->on_nonserialized(sub { die "$_[0]?!?" });
3863
3864 Occasionally, the serializer may be given data it can't turn into SOAP-savvy XML; for example, if a program bug results in a code reference or something similar being passed in as a parameter to method call. When that happens, this callback is activated, with one argument. That argument is the data item that could not be understood. It will be the only argument. If the routine returns, the return value is pasted into the message as the serialization. Generally, an error is in order, and this callback allows for control over signaling that error.
3865
3866 =item on_debug(callback)
3867
3868 $client->on_debug(sub { print @_ });
3869
3870 This is kept for backwards-compatibility with earlier versions of the toolkit. Each method has a trace step built in, which is called at routine entry. This specifies a callback to be used when these trace statements are reached. Because this is deprecated, it is recommended that applications use the +debug and +trace facilities described later under L<SOAP::Trace>. Note also that debugging isn't handled on a per-object basis; if this method is used on a given object, it sets debugging behavior for all objects of the class.
3871
3872 =back
3873
3874 =head1 WRITING A SOAP CLIENT
3875
3876 TODO - soap client example
3877
3878 =head1 WRITING A SOAP SERVER
3879
3880 See L<SOAP::Server>, or L<SOAP::Transport>.
3881
3882 =head1 FEATURES
3883
3884 =head2 ATTACHMENTS
3885
3886 C<SOAP::Lite> features support for the SOAP with Attachments specification.
3887 Currently, SOAP::Lite only supports MIME based attachments. DIME based attachments
3888 are yet to be fully functional.
3889
3890 =head3 EXAMPLES
3891
3892 =head4 Client sending an attachment
3893
3894 C<SOAP::Lite> clients can specify attachments to be sent along with a request by using the C<SOAP::Lite::parts()> method, which takes as an argument an ARRAY of C<MIME::Entity>'s.
3895
3896 use SOAP::Lite;
3897 use MIME::Entity;
3898 my $ent = build MIME::Entity
3899 Type => "image/gif",
3900 Encoding => "base64",
3901 Path => "somefile.gif",
3902 Filename => "saveme.gif",
3903 Disposition => "attachment";
3904 my $som = SOAP::Lite
3905 ->uri($SOME_NAMESPACE)
3906 ->parts([ $ent ])
3907 ->proxy($SOME_HOST)
3908 ->some_method(SOAP::Data->name("foo" => "bar"));
3909
3910 =head4 Client retrieving an attachment
3911
3912 A client accessing attachments that were returned in a response by using the C<SOAP::SOM::parts()> accessor.
3913
3914 use SOAP::Lite;
3915 use MIME::Entity;
3916 my $soap = SOAP::Lite
3917 ->uri($NS)
3918 ->proxy($HOST);
3919 my $som = $soap->foo();
3920 foreach my $part (${$som->parts}) {
3921 print $part->stringify;
3922 }
3923
3924 =head4 Server receiving an attachment
3925
3926 Servers, like clients, use the S<SOAP::SOM> module to access attachments trasmitted to it.
3927
3928 package Attachment;
3929 use SOAP::Lite;
3930 use MIME::Entity;
3931 use strict;
3932 use vars qw(@ISA);
3933 @ISA = qw(SOAP::Server::Parameters);
3934 sub someMethod {
3935 my $self = shift;
3936 my $envelope = pop;
3937 foreach my $part (@{$envelope->parts}) {
3938 print "AttachmentService: attachment found! (".ref($part).")\n";
3939 }
3940 # do something
3941 }
3942
3943 =head4 Server responding with an attachment
3944
3945 Servers wishing to return an attachment to the calling client need only return C<MIME::Entity> objects along with SOAP::Data elements, or any other data intended for the response.
3946
3947 package Attachment;
3948 use SOAP::Lite;
3949 use MIME::Entity;
3950 use strict;
3951 use vars qw(@ISA);
3952 @ISA = qw(SOAP::Server::Parameters);
3953 sub someMethod {
3954 my $self = shift;
3955 my $envelope = pop;
3956 my $ent = build MIME::Entity
3957 'Id' => "<1234>",
3958 'Type' => "text/xml",
3959 'Path' => "some.xml",
3960 'Filename' => "some.xml",
3961 'Disposition' => "attachment";
3962 return SOAP::Data->name("foo" => "blah blah blah"),$ent;
3963 }
3964
3965 =head2 DEFAULT SETTINGS
3966
3967 Though this feature looks similar to L<autodispatch|/"IN/OUT, OUT PARAMETERS AND AUTOBINDING"> they have (almost) nothing in common. This capability allows you specify default settings so that all objects created after that will be initialized with the proper default settings.
3968
3969 If you wish to provide common C<proxy()> or C<uri()> settings for all C<SOAP::Lite> objects in your application you may do:
3970
3971 use SOAP::Lite
3972 proxy => 'http://localhost/cgi-bin/soap.cgi',
3973 uri => 'http://my.own.com/My/Examples';
3974
3975 my $soap1 = new SOAP::Lite; # will get the same proxy()/uri() as above
3976 print $soap1->getStateName(1)->result;
3977
3978 my $soap2 = SOAP::Lite->new; # same thing as above
3979 print $soap2->getStateName(2)->result;
3980
3981 # or you may override any settings you want
3982 my $soap3 = SOAP::Lite->proxy('http://localhost/');
3983 print $soap3->getStateName(1)->result;
3984
3985 B<Any> C<SOAP::Lite> properties can be propagated this way. Changes in object copies will not affect global settings and you may still change global settings with C<< SOAP::Lite->self >> call which returns reference to global object. Provided parameter will update this object and you can even set it to C<undef>:
3986
3987 SOAP::Lite->self(undef);
3988
3989 The C<use SOAP::Lite> syntax also lets you specify default event handlers for your code. If you have different SOAP objects and want to share the same C<on_action()> (or C<on_fault()> for that matter) handler. You can specify C<on_action()> during initialization for every object, but you may also do:
3990
3991 use SOAP::Lite
3992 on_action => sub {sprintf '%s#%s', @_};
3993
3994 and this handler will be the default handler for all your SOAP objects. You can override it if you specify a handler for a particular object. See F<t/*.t> for example of on_fault() handler.
3995
3996 Be warned, that since C<use ...> is executed at compile time B<all> C<use> statements will be executed B<before> script execution that can make unexpected results. Consider code:
3997
3998 use SOAP::Lite proxy => 'http://localhost/';
3999 print SOAP::Lite->getStateName(1)->result;
4000
4001 use SOAP::Lite proxy => 'http://localhost/cgi-bin/soap.cgi';
4002 print SOAP::Lite->getStateName(1)->result;
4003
4004 B<Both> SOAP calls will go to C<'http://localhost/cgi-bin/soap.cgi'>. If you want to execute C<use> at run-time, put it in C<eval>:
4005
4006 eval "use SOAP::Lite proxy => 'http://localhost/cgi-bin/soap.cgi'; 1" or die;
4007
4008 Or alternatively,
4009
4010 SOAP::Lite->self->proxy('http://localhost/cgi-bin/soap.cgi');
4011
4012 =head2 SETTING MAXIMUM MESSAGE SIZE
4013
4014 One feature of C<SOAP::Lite> is the ability to control the maximum size of a message a SOAP::Lite server will be allowed to process. To control this feature simply define C<$SOAP::Constants::MAX_CONTENT_SIZE> in your code like so:
4015
4016 use SOAP::Transport::HTTP;
4017 use MIME::Entity;
4018 $SOAP::Constants::MAX_CONTENT_SIZE = 10000;
4019 SOAP::Transport::HTTP::CGI
4020 ->dispatch_to('TemperatureService')
4021 ->handle;
4022
4023 =head2 IN/OUT, OUT PARAMETERS AND AUTOBINDING
4024
4025 C<SOAP::Lite> gives you access to all parameters (both in/out and out) and also does some additional work for you. Lets consider following example:
4026
4027 <mehodResponse>
4028 <res1>name1</res1>
4029 <res2>name2</res2>
4030 <res3>name3</res3>
4031 </mehodResponse>
4032
4033 In that case:
4034
4035 $result = $r->result; # gives you 'name1'
4036 $paramout1 = $r->paramsout; # gives you 'name2', because of scalar context
4037 $paramout1 = ($r->paramsout)[0]; # gives you 'name2' also
4038 $paramout2 = ($r->paramsout)[1]; # gives you 'name3'
4039
4040 or
4041
4042 @paramsout = $r->paramsout; # gives you ARRAY of out parameters
4043 $paramout1 = $paramsout[0]; # gives you 'res2', same as ($r->paramsout)[0]
4044 $paramout2 = $paramsout[1]; # gives you 'res3', same as ($r->paramsout)[1]
4045
4046 Generally, if server returns C<return (1,2,3)> you will get C<1> as the result and C<2> and C<3> as out parameters.
4047
4048 If the server returns C<return [1,2,3]> you will get an ARRAY from C<result()> and C<undef> from C<paramsout()>.
4049
4050 Results can be arbitrary complex: they can be an array, they can be objects, they can be anything and still be returned by C<result()> . If only one parameter is returned, C<paramsout()> will return C<undef>.
4051
4052 Furthermore, if you have in your output parameters a parameter with the same signature (name+type) as in the input parameters this parameter will be mapped into your input automatically. For example:
4053
4054 B<Server Code>:
4055
4056 sub mymethod {
4057 shift; # object/class reference
4058 my $param1 = shift;
4059 my $param2 = SOAP::Data->name('myparam' => shift() * 2);
4060 return $param1, $param2;
4061 }
4062
4063 B<Client Code>:
4064
4065 $a = 10;
4066 $b = SOAP::Data->name('myparam' => 12);
4067 $result = $soap->mymethod($a, $b);
4068
4069 After that, C<< $result == 10 and $b->value == 24 >>! Magic? Sort of.
4070 Autobinding gives it to you. That will work with objects also with one difference: you do not need to worry about the name and the type of object parameter. Consider the C<PingPong> example (F<examples/My/PingPong.pm> and F<examples/pingpong.pl>):
4071
4072 B<Server Code>:
4073
4074 package My::PingPong;
4075
4076 sub new {
4077 my $self = shift;
4078 my $class = ref($self) || $self;
4079 bless {_num=>shift} => $class;
4080 }
4081
4082 sub next {
4083 my $self = shift;
4084 $self->{_num}++;
4085 }
4086
4087 B<Client Code>:
4088
4089 use SOAP::Lite +autodispatch =>
4090 uri => 'urn:',
4091 proxy => 'http://localhost/';
4092
4093 my $p = My::PingPong->new(10); # $p->{_num} is 10 now, real object returned
4094 print $p->next, "\n"; # $p->{_num} is 11 now!, object autobinded
4095
4096 =head2 STATIC AND DYNAMIC SERVICE DEPLOYMENT
4097
4098 Let us scrutinize the deployment process. When designing your SOAP server you can consider two kind of deployment: B<static> and B<dynamic>. For both, static and dynamic, you should specify C<MODULE>, C<MODULE::method>, C<method> or C<PATH/> when creating C<use>ing the SOAP::Lite module. The difference between static and dynamic deployment is that in case of 'dynamic', any module which is not present will be loaded on demand. See the L</"SECURITY"> section for detailed description.
4099
4100 =head3 STATIC DEPLOYMENT EXAMPLE
4101
4102 use SOAP::Transport::HTTP;
4103 use My::Examples; # module is preloaded
4104
4105 SOAP::Transport::HTTP::CGI
4106 # deployed module should be present here or client will get 'access denied'
4107 -> dispatch_to('My::Examples')
4108 -> handle;
4109
4110 For static deployment you should specify the MODULE name directly.
4111
4112 You should also use static binding when you have several different classes in one file and want to make them available for SOAP calls.
4113
4114 =head3 DYNAMIC DEPLOYMENT EXAMPLE
4115
4116 use SOAP::Transport::HTTP;
4117 # name is unknown, module will be loaded on demand
4118
4119 SOAP::Transport::HTTP::CGI
4120 # deployed module should be present here or client will get 'access denied'
4121 -> dispatch_to('/Your/Path/To/Deployed/Modules', 'My::Examples')
4122 -> handle;
4123
4124 For dynamic deployment you can specify the name either directly (in that case it will be C<require>d without any restriction) or indirectly, with a PATH. In that case, the ONLY path that will be available will be the PATH given to the dispatch_to() method). For information how to handle this situation see L</"SECURITY"> section.
4125
4126 =head3 SUMMARY
4127
4128 dispatch_to(
4129 # dynamic dispatch that allows access to ALL modules in specified directory
4130 PATH/TO/MODULES
4131 # 1. specifies directory
4132 # -- AND --
4133 # 2. gives access to ALL modules in this directory without limits
4134
4135 # static dispatch that allows access to ALL methods in particular MODULE
4136 MODULE
4137 # 1. gives access to particular module (all available methods)
4138 # PREREQUISITES:
4139 # module should be loaded manually (for example with 'use ...')
4140 # -- OR --
4141 # you can still specify it in PATH/TO/MODULES
4142
4143 # static dispatch that allows access to particular method ONLY
4144 MODULE::method
4145 # same as MODULE, but gives access to ONLY particular method,
4146 # so there is not much sense to use both MODULE and MODULE::method
4147 # for the same MODULE
4148 )
4149
4150 In addition to this C<SOAP::Lite> also supports an experimental syntax that allows you to bind a specific URL or SOAPAction to a CLASS/MODULE or object. For example:
4151
4152 dispatch_with({
4153 URI => MODULE, # 'http://www.soaplite.com/' => 'My::Class',
4154 SOAPAction => MODULE, # 'http://www.soaplite.com/method' => 'Another::Class',
4155 URI => object, # 'http://www.soaplite.com/obj' => My::Class->new,
4156 })
4157
4158 C<URI> is checked before C<SOAPAction>. You may use both the C<dispatch_to()> and C<dispatch_with()> methods in the same server, but note that C<dispatch_with()> has a higher order of precedence. C<dispatch_to()> will be checked only after C<URI> and C<SOAPAction> has been checked. See F<t/03-server.t> for more information and examples.
4159
4160 See also: L<EXAMPLE APACHE::REGISTRY USAGE|SOAP::Transport/"EXAMPLE APACHE::REGISTRY USAGE">, L</"SECURITY">
4161
4162 =head2 COMPRESSION
4163
4164 C<SOAP::Lite> provides you option to enable transparent compression over the wire. Compression can be enabled by specifying a threshold value (in the form of kilobytes) for compression on both the client and server sides:
4165
4166 I<Note: Compression currently only works for HTTP based servers and clients.>
4167
4168 B<Client Code>
4169
4170 print SOAP::Lite
4171 ->uri('http://localhost/My/Parameters')
4172 ->proxy('http://localhost/', options => {compress_threshold => 10000})
4173 ->echo(1 x 10000)
4174 ->result;
4175
4176 B<Server Code>
4177
4178 my $server = SOAP::Transport::HTTP::CGI
4179 ->dispatch_to('My::Parameters')
4180 ->options({compress_threshold => 10000})
4181 ->handle;
4182
4183 For more information see L<COMPRESSION|SOAP::Transport/"COMPRESSION"> in L<HTTP::Transport>.
4184
4185 =head1 SECURITY
4186
4187 For security reasons, the exisiting path for Perl modules (C<@INC>) will be disabled once you have chosen dynamic deployment and specified your own C<PATH/>. If you wish to access other modules in your included package you have several options:
4188
4189 =over 4
4190
4191 =item 1
4192
4193 Switch to static linking:
4194
4195 use MODULE;
4196 $server->dispatch_to('MODULE');
4197
4198 Which can also be useful when you want to import something specific from the deployed modules:
4199
4200 use MODULE qw(import_list);
4201
4202 =item 2
4203
4204 Change C<use> to C<require>. The path is only unavailable during the initialization phase. It is available once more during execution. Therefore, if you utilize C<require> somewhere in your package, it will work.
4205
4206 =item 3
4207
4208 Wrap C<use> in an C<eval> block:
4209
4210 eval 'use MODULE qw(import_list)'; die if $@;
4211
4212 =item 4
4213
4214 Set your include path in your package and then specify C<use>. Don't forget to put C<@INC> in a C<BEGIN{}> block or it won't work. For example,
4215
4216 BEGIN { @INC = qw(my_directory); use MODULE }
4217
4218 =back
4219
4220 =head1 INTEROPERABILITY
4221
4222 =head2 Microsoft .NET client with SOAP::Lite Server
4223
4224 In order to use a .NET client with a SOAP::Lite server, be sure you use fully qualified names for your return values. For example:
4225
4226 return SOAP::Data->name('myname')
4227 ->type('string')
4228 ->uri($MY_NAMESPACE)
4229 ->value($output);
4230
4231 In addition see comment about default incoding in .NET Web Services below.
4232
4233 =head2 SOAP::Lite client with a .NET server
4234
4235 If experiencing problems when using a SOAP::Lite client to call a .NET Web service, it is recommended you check, or adhere to all of the following recommendations:
4236
4237 =over 4
4238
4239 =item Declare a proper soapAction in your call
4240
4241 For example, use C<on_action( sub { 'http://www.myuri.com/WebService.aspx#someMethod'; } )>.
4242
4243 =item Disable charset definition in Content-type header
4244
4245 Some users have said that Microsoft .NET prefers the value of the Content-type header to be a mimetype exclusively, but SOAP::Lite specifies a character set in addition to the mimetype. This results in an error similar to:
4246
4247 Server found request content type to be 'text/xml; charset=utf-8',
4248 but expected 'text/xml'
4249
4250 To turn off this behavior specify use the following code:
4251
4252 use SOAP::Lite;
4253 $SOAP::Constants::DO_NOT_USE_CHARSET = 1;
4254 # The rest of your code
4255
4256 =item Use fully qualified name for method parameters
4257
4258 For example, the following code is preferred:
4259
4260 SOAP::Data->name(Query => 'biztalk')
4261 ->uri('http://tempuri.org/')
4262
4263 As opposed to:
4264
4265 SOAP::Data->name('Query' => 'biztalk')
4266
4267 =item Place method in default namespace
4268
4269 For example, the following code is preferred:
4270
4271 my $method = SOAP::Data->name('add')
4272 ->attr({xmlns => 'http://tempuri.org/'});
4273 my @rc = $soap->call($method => @parms)->result;
4274
4275 As opposed to:
4276
4277 my @rc = $soap->call(add => @parms)->result;
4278 # -- OR --
4279 my @rc = $soap->add(@parms)->result;
4280
4281 =item Disable use of explicit namespace prefixes
4282
4283 Some user's have reported that .NET will simply not parse messages that use namespace prefixes on anything but SOAP elements themselves. For example, the following XML would not be parsed:
4284
4285 <SOAP-ENV:Envelope ...attributes skipped>
4286 <SOAP-ENV:Body>
4287 <namesp1:mymethod xmlns:namesp1="urn:MyURI" />
4288 </SOAP-ENV:Body>
4289 </SOAP-ENV:Envelope>
4290
4291 SOAP::Lite allows users to disable the use of explicit namespaces through the C<use_prefix()> method. For example, the following code:
4292
4293 $som = SOAP::Lite->uri('urn:MyURI')
4294 ->proxy($HOST)
4295 ->use_prefix(0)
4296 ->myMethod();
4297
4298 Will result in the following XML, which is more pallatable by .NET:
4299
4300 <SOAP-ENV:Envelope ...attributes skipped>
4301 <SOAP-ENV:Body>
4302 <mymethod xmlns="urn:MyURI" />
4303 </SOAP-ENV:Body>
4304 </SOAP-ENV:Envelope>
4305
4306 =item Modify your .NET server, if possible
4307
4308 Stefan Pharies <stefanph@microsoft.com>:
4309
4310 SOAP::Lite uses the SOAP encoding (section 5 of the soap 1.1 spec), and
4311 the default for .NET Web Services is to use a literal encoding. So
4312 elements in the request are unqualified, but your service expects them to
4313 be qualified. .Net Web Services has a way for you to change the expected
4314 message format, which should allow you to get your interop working.
4315 At the top of your class in the asmx, add this attribute (for Beta 1):
4316
4317 [SoapService(Style=SoapServiceStyle.RPC)]
4318
4319 Another source said it might be this attribute (for Beta 2):
4320
4321 [SoapRpcService]
4322
4323 Full Web Service text may look like:
4324
4325 <%@ WebService Language="C#" Class="Test" %>
4326 using System;
4327 using System.Web.Services;
4328 using System.Xml.Serialization;
4329
4330 [SoapService(Style=SoapServiceStyle.RPC)]
4331 public class Test : WebService {
4332 [WebMethod]
4333 public int add(int a, int b) {
4334 return a + b;
4335 }
4336 }
4337
4338 Another example from Kirill Gavrylyuk <kirillg@microsoft.com>:
4339
4340 "You can insert [SoapRpcService()] attribute either on your class or on
4341 operation level".
4342
4343 <%@ WebService Language=CS class="DataType.StringTest"%>
4344
4345 namespace DataType {
4346
4347 using System;
4348 using System.Web.Services;
4349 using System.Web.Services.Protocols;
4350 using System.Web.Services.Description;
4351
4352 [SoapRpcService()]
4353 public class StringTest: WebService {
4354 [WebMethod]
4355 [SoapRpcMethod()]
4356 public string RetString(string x) {
4357 return(x);
4358 }
4359 }
4360 }
4361
4362 Example from Yann Christensen <yannc@microsoft.com>:
4363
4364 using System;
4365 using System.Web.Services;
4366 using System.Web.Services.Protocols;
4367
4368 namespace Currency {
4369 [WebService(Namespace="http://www.yourdomain.com/example")]
4370 [SoapRpcService]
4371 public class Exchange {
4372 [WebMethod]
4373 public double getRate(String country, String country2) {
4374 return 122.69;
4375 }
4376 }
4377 }
4378
4379 =back
4380
4381 Special thanks goes to the following people for providing the above description and details on .NET interoperability issues:
4382
4383 Petr Janata <petr.janata@i.cz>,
4384
4385 Stefan Pharies <stefanph@microsoft.com>,
4386
4387 Brian Jepson <bjepson@jepstone.net>, and others
4388
4389 =head1 TROUBLESHOOTING
4390
4391 =over 4
4392
4393 =item SOAP::Lite serializes "18373" as an integer, but I want it to be a string!
4394
4395 Because Perl is loosely typed, there is no 100% reliable way to predict what the *intended* type of a variable is. So SOAP::Lite has a system of guessing what a type is. But it is not 100% reliable. You do however, have the control to override this autotyping behavior with your own.
4396
4397 Suppose you wanted to case every element of an array as a string. Then the following code will change the precedence of SOAP::Seriailizer's is-a-string test to be the first test run. Because the is-a-string test always returns C<true> every element will be determined to be a string.
4398
4399 my @list = qw(-1 45 foo bar 3838);
4400 my $proxy = SOAP::Lite->uri($uri)->proxy($proxyUrl);
4401 $proxy->serializer->typelookup->{string}->[0] = 0;
4402 $proxy->myMethod(\@list);
4403
4404 Alternatively, you can build the XML data structure manually...
4405
4406 See L<SOAP::Serializer|SOAP::Serializer/AUTOTYPING>.
4407
4408 =item C<+autodispatch> doesn't work in Perl 5.8
4409
4410 There is a bug in Perl 5.8's C<UNIVERSAL::AUTOLOAD> functionality that prevents the C<+autodispatch> functionality from working properly. The workaround is to use C<dispatch_from> instead. Where you might normally do something like this:
4411
4412 use Some::Module;
4413 use SOAP::Lite +autodispatch =>
4414 uri => 'urn:Foo'
4415 proxy => 'http://...';
4416
4417 You would do something like this:
4418
4419 use SOAP::Lite dispatch_from(Some::Module) =>
4420 uri => 'urn:Foo'
4421 proxy => 'http://...';
4422
4423 =item Problems using SOAP::Lite's COM Interface
4424
4425 =over
4426
4427 =item Can't call method "server" on undefined value
4428
4429 You probably did not register F<Lite.dll> using C<regsvr32 Lite.dll>
4430
4431 =item Failed to load PerlCtrl Runtime
4432
4433 It is likely that you have install Perl in two different locations and the location of ActiveState's Perl is not the first instance of Perl specified in your PATH. To rectify, rename the directory in which the non-ActiveState Perl is installed, or be sure the path to ActiveState's Perl is specified prior to any other instance of Perl in your PATH.
4434
4435 =back
4436
4437 =item Dynamic libraries are not found
4438
4439 If you are using the Apache web server, and you are seeing something like the following in your webserver log file:
4440
4441 Can't load '/usr/local/lib/perl5/site_perl/.../XML/Parser/Expat/Expat.so'
4442 for module XML::Parser::Expat: dynamic linker: /usr/local/bin/perl:
4443 libexpat.so.0 is NEEDED, but object does not exist at
4444 /usr/local/lib/perl5/.../DynaLoader.pm line 200.
4445
4446 Then try placing the following into your F<httpd.conf> file and see if it fixes your problem.
4447
4448 <IfModule mod_env.c>
4449 PassEnv LD_LIBRARY_PATH
4450 </IfModule>
4451
4452 =item SOAP client reports "500 unexpected EOF before status line seen
4453
4454 See L</"Apache is crashing with segfaults">
4455
4456 =item Apache is crashing with segfaults
4457
4458 Using C<SOAP::Lite> (or L<XML::Parser::Expat>) in combination with mod_perl causes random segmentation faults in httpd processes. To fix, try configuring Apache with the following:
4459
4460 RULE_EXPAT=no
4461
4462 If you are using Apache 1.3.20 and later, try configuring Apache with the following option:
4463
4464 ./configure --disable-rule=EXPAT
4465
4466 See http://archive.covalent.net/modperl/2000/04/0185.xml for more details and lot of thanks to Robert Barta <rho@bigpond.net.au> for explaining this weird behavior.
4467
4468 If this doesn't address the problem, you may wish to try C<-Uusemymalloc>, or a similar option in order to instruct Perl to use the system's own C<malloc>.
4469
4470 Thanks to Tim Bunce <Tim.Bunce@pobox.com>.
4471
4472 =item CGI scripts do not work under Microsoft Internet Information Server (IIS)
4473
4474 CGI scripts may not work under IIS unless scripts use the C<.pl> extension, opposed to C<.cgi>.
4475
4476 =item Java SAX parser unable to parse message composed by SOAP::Lite
4477
4478 In some cases SOAP messages created by C<SOAP::Lite> may not be parsed properly by a SAX2/Java XML parser. This is due to a known bug in C<org.xml.sax.helpers.ParserAdapter>. This bug manifests itself when an attribute in an XML element occurs prior to the XML namespace declaration on which it depends. However, according to the XML specification, the order of these attributes is not significant.
4479
4480 http://www.megginson.com/SAX/index.html
4481
4482 Thanks to Steve Alpert (Steve_Alpert@idx.com) for pointing on it.
4483
4484 =back
4485
4486 =head1 PERFORMANCE
4487
4488 =over 4
4489
4490 =item Processing of XML encoded fragments
4491
4492 C<SOAP::Lite> is based on L<XML::Parser> which is basically wrapper around James
4493 Clark's expat parser. Expat's behavior for parsing XML encoded string can
4494 affect processing messages that have lot of encoded entities, like XML
4495 fragments, encoded as strings. Providing low-level details, parser will call
4496 char() callback for every portion of processed stream, but individually for
4497 every processed entity or newline. It can lead to lot of calls and additional
4498 memory manager expenses even for small messages. By contrast, XML messages
4499 which are encoded as base64Binary, don't have this problem and difference in
4500 processing time can be significant. For XML encoded string that has about 20
4501 lines and 30 tags, number of call could be about 100 instead of one for
4502 the same string encoded as base64Binary.
4503
4504 Since it is parser's feature there is NO fix for this behavior (let me know
4505 if you find one), especially because you need to parse message you already
4506 got (and you cannot control content of this message), however, if your are
4507 in charge for both ends of processing you can switch encoding to base64 on
4508 sender's side. It will definitely work with SOAP::Lite and it B<may> work with
4509 other toolkits/implementations also, but obviously I cannot guarantee that.
4510
4511 If you want to encode specific string as base64, just do
4512 C<< SOAP::Data->type(base64 => $string) >> either on client or on server
4513 side. If you want change behavior for specific instance of SOAP::Lite, you
4514 may subclass C<SOAP::Serializer>, override C<as_string()> method that is
4515 responsible for string encoding (take a look into C<as_base64Binary()>) and
4516 specify B<new> serializer class for your SOAP::Lite object with:
4517
4518 my $soap = new SOAP::Lite
4519 serializer => My::Serializer->new,
4520 ..... other parameters
4521
4522 or on server side:
4523
4524 my $server = new SOAP::Transport::HTTP::Daemon # or any other server
4525 serializer => My::Serializer->new,
4526 ..... other parameters
4527
4528 If you want to change this behavior for B<all> instances of SOAP::Lite, just
4529 substitute C<as_string()> method with C<as_base64Binary()> somewhere in your
4530 code B<after> C<use SOAP::Lite> and B<before> actual processing/sending:
4531
4532 *SOAP::Serializer::as_string = \&SOAP::Serializer::as_base64Binary;
4533
4534 Be warned that last two methods will affect B<all> strings and convert them
4535 into base64 encoded. It doesn't make any difference for SOAP::Lite, but it
4536 B<may> make a difference for other toolkits.
4537
4538 =back
4539
4540 =head1 BUGS AND LIMITATIONS
4541
4542 =over 4
4543
4544 =item *
4545
4546 No support for multidimensional, partially transmitted and sparse arrays (however arrays of arrays are supported, as well as any other data structures, and you can add your own implementation with SOAP::Data).
4547
4548 =item *
4549
4550 Limited support for WSDL schema.
4551
4552 =item *
4553
4554 XML::Parser::Lite relies on Unicode support in Perl and doesn't do entity decoding.
4555
4556 =item *
4557
4558 Limited support for mustUnderstand and Actor attributes.
4559
4560 =back
4561
4562 =head1 PLATFORM SPECIFICS
4563
4564 =over 4
4565
4566 =item MacOS
4567
4568 Information about XML::Parser for MacPerl could be found here:
4569
4570 http://bumppo.net/lists/macperl-modules/1999/07/msg00047.html
4571
4572 Compiled XML::Parser for MacOS could be found here:
4573
4574 http://www.perl.com/CPAN-local/authors/id/A/AS/ASANDSTRM/XML-Parser-2.27-bin-1-MacOS.tgz
4575
4576 =back
4577
4578 =head1 AVAILABILITY
4579
4580 You can download the latest version SOAP::Lite for Unix or SOAP::Lite for Win32 from the following sources:
4581
4582 * SOAP::Lite Homepage: http://soaplite.com/
4583 * CPAN: http://search.cpan.org/search?dist=SOAP-Lite
4584 * Sourceforge: http://sourceforge.net/projects/soaplite/
4585
4586 You are welcome to send e-mail to the maintainers of SOAP::Lite with your
4587 with your comments, suggestions, bug reports and complaints.
4588
4589 =head1 ACKNOWLEDGEMENTS
4590
4591 Special thanks to Randy J. Ray, author of I<Programming Web Services with Perl>,
4592 who has contributed greatly to the documentation effort of SOAP::Lite.
4593
4594 Special thanks to O'Reilly publishing which has graciously allowed SOAP::Lite
4595 to republish and redistribute the SOAP::Lite reference manual found in Appendix B
4596 of I<Programming Web Services with Perl>.
4597
4598 And special gratitude to all the developers who have contributed patches, ideas,
4599 time, energy, and help in a million different forms to the development of this
4600 software.
4601
4602 =head1 REPORTING BUGS
4603
4604 Please report all suspected SOAP::Lite bugs using Sourceforge. This ensures
4605 proper tracking of the issue and allows you the reporter to know when something
4606 gets fixed.
4607
4608 http://sourceforge.net/tracker/?group_id=66000&atid=513017
4609
4610 If under dire circumstances you need immediate assistance with the resolution of
4611 an issue, you are welcome to contact Byrne Reese at <byrne at majordojo dot com>.
4612
4613 =head1 COPYRIGHT
4614
4615 Copyright (C) 2000-2005 Paul Kulchenko. All rights reserved.
4616
4617 This library is free software; you can redistribute it and/or modify
4618 it under the same terms as Perl itself.
4619
4620 This text and all associated documentation for this library is made available
4621 under the Creative Commons Attribution-NoDerivs 2.0 license.
4622 http://creativecommons.org/licenses/by-nd/2.0/
4623
4624 =head1 AUTHORS
4625
4626 Paul Kulchenko (paulclinger@yahoo.com)
4627
4628 Randy J. Ray (rjray@blackperl.com)
4629
4630 Byrne Reese (byrne@majordojo.com)
4631
4632 =cut

  ViewVC Help
Powered by ViewVC 1.1.26