You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
401 lines
10 KiB
401 lines
10 KiB
# ======================================================================
|
|
#
|
|
# Copyright (C) 2000-2001 Paul Kulchenko ([email protected])
|
|
# SOAP::Lite is free software; you can redistribute it
|
|
# and/or modify it under the same terms as Perl itself.
|
|
#
|
|
# $Id: XMLRPC::Lite.pm,v 0.51 2001/07/18 15:15:14 $
|
|
#
|
|
# ======================================================================
|
|
|
|
package XMLRPC::Lite;
|
|
|
|
use SOAP::Lite;
|
|
use strict;
|
|
use vars qw($VERSION);
|
|
$VERSION = '0.51';
|
|
|
|
# ======================================================================
|
|
|
|
package XMLRPC::Constants;
|
|
|
|
BEGIN {
|
|
no strict 'refs';
|
|
for (qw(
|
|
FAULT_CLIENT FAULT_SERVER
|
|
HTTP_ON_SUCCESS_CODE HTTP_ON_FAULT_CODE
|
|
DO_NOT_USE_XML_PARSER DO_NOT_USE_CHARSET
|
|
)) {
|
|
*$_ = \${'SOAP::Constants::' . $_}
|
|
}
|
|
}
|
|
|
|
# ======================================================================
|
|
|
|
package XMLRPC::Data;
|
|
|
|
@XMLRPC::Data::ISA = qw(SOAP::Data);
|
|
|
|
# ======================================================================
|
|
|
|
package XMLRPC::Serializer;
|
|
|
|
@XMLRPC::Serializer::ISA = qw(SOAP::Serializer);
|
|
|
|
sub new {
|
|
my $self = shift;
|
|
|
|
unless (ref $self) {
|
|
my $class = ref($self) || $self;
|
|
$self = $class->SUPER::new(
|
|
typelookup => {
|
|
base64 => [10, sub {$_[0] =~ /[^\x09\x0a\x0d\x20-\x7f]/}, 'as_base64'],
|
|
int => [20, sub {$_[0] =~ /^[+-]?\d+$/}, 'as_int'],
|
|
double => [30, sub {$_[0] =~ /^(-?(?:\d+(?:\.\d*)?|\.\d+|NaN|INF)|([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?)$/}, 'as_double'],
|
|
dateTime => [35, sub {$_[0] =~ /^\d{8}T\d\d:\d\d:\d\d$/}, 'as_dateTime'],
|
|
string => [40, sub {1}, 'as_string'],
|
|
},
|
|
attr => {},
|
|
namespaces => {},
|
|
@_,
|
|
);
|
|
}
|
|
return $self;
|
|
}
|
|
|
|
sub envelope {
|
|
my $self = shift->new;
|
|
my $type = shift;
|
|
|
|
my($body);
|
|
if ($type eq 'method' || $type eq 'response') {
|
|
my $method = shift or die "Unspecified method for XMLRPC call\n";
|
|
if ($type eq 'response') {
|
|
$body = XMLRPC::Data->name(methodResponse => \XMLRPC::Data->value(
|
|
XMLRPC::Data->type(params => [@_])
|
|
));
|
|
} else {
|
|
$body = XMLRPC::Data->name(methodCall => \XMLRPC::Data->value(
|
|
XMLRPC::Data->type(methodName => UNIVERSAL::isa($method => 'XMLRPC::Data') ? $method->name : $method),
|
|
XMLRPC::Data->type(params => [@_])
|
|
));
|
|
}
|
|
} elsif ($type eq 'fault') {
|
|
$body = XMLRPC::Data->name(methodResponse =>
|
|
\XMLRPC::Data->type(fault => {faultCode => $_[0], faultString => $_[1]}),
|
|
);
|
|
} else {
|
|
die "Wrong type of envelope ($type) for XMLRPC call\n";
|
|
}
|
|
|
|
$self->xmlize($self->encode_object($body));
|
|
}
|
|
|
|
sub encode_object {
|
|
my $self = shift;
|
|
my @encoded = $self->SUPER::encode_object(@_);
|
|
return $encoded[0]->[0] =~ /^(?:array|struct|i4|int|boolean|string|double|dateTime\.iso8601|base64)$/o
|
|
? ['value', {}, [@encoded]] : @encoded;
|
|
}
|
|
|
|
sub encode_scalar {
|
|
my $self = shift;
|
|
return ['value', {}] unless defined $_[0];
|
|
return $self->SUPER::encode_scalar(@_);
|
|
}
|
|
|
|
sub encode_array {
|
|
my($self, $array) = @_;
|
|
|
|
return ['array', {}, [
|
|
['data', {}, [map {$self->encode_object($_)} @$array]]
|
|
]];
|
|
}
|
|
|
|
sub encode_hash {
|
|
my($self, $hash) = @_;
|
|
|
|
return ['struct', {}, [
|
|
map {
|
|
['member', {}, [['name', {}, $_], $self->encode_object($hash->{$_})]]
|
|
} keys %$hash
|
|
]];
|
|
}
|
|
|
|
sub as_methodName {
|
|
my $self = shift;
|
|
my($value, $name, $type, $attr) = @_;
|
|
return ['methodName', $attr, $value];
|
|
}
|
|
|
|
sub as_params {
|
|
my $self = shift;
|
|
my($params, $name, $type, $attr) = @_;
|
|
|
|
return ['params', $attr, [
|
|
map {
|
|
['param', {}, [$self->encode_object($_)]]
|
|
} @$params
|
|
]];
|
|
}
|
|
|
|
sub as_fault {
|
|
my($self, $fault) = @_;
|
|
|
|
return ['fault', {}, [$self->encode_object($fault)]];
|
|
}
|
|
|
|
sub BEGIN {
|
|
no strict 'refs';
|
|
for my $type (qw(double i4 int)) {
|
|
my $method = 'as_' . $type;
|
|
*$method = sub {
|
|
my($self, $value) = @_;
|
|
return [$type, {}, $value];
|
|
}
|
|
}
|
|
}
|
|
|
|
sub as_base64 {
|
|
my $self = shift;
|
|
my $value = shift;
|
|
require MIME::Base64;
|
|
return ['base64', {}, MIME::Base64::encode_base64($value,'')];
|
|
}
|
|
|
|
sub as_string {
|
|
my $self = shift;
|
|
my $value = shift;
|
|
return ['string', {}, SOAP::Utils::encode_data($value)];
|
|
}
|
|
|
|
sub as_dateTime {
|
|
my $self = shift;
|
|
my $value = shift;
|
|
return ['dateTime.iso8601', {}, $value];
|
|
}
|
|
|
|
sub as_boolean {
|
|
my $self = shift;
|
|
my $value = shift;
|
|
return ['boolean', {}, $value ? 1 : 0];
|
|
}
|
|
|
|
# ======================================================================
|
|
|
|
package XMLRPC::SOM;
|
|
|
|
@XMLRPC::SOM::ISA = qw(SOAP::SOM);
|
|
|
|
sub BEGIN {
|
|
no strict 'refs';
|
|
my %path = (
|
|
root => '/',
|
|
envelope => '/[1]',
|
|
method => '/methodCall/methodName',
|
|
fault => '/methodResponse/fault',
|
|
);
|
|
for my $method (keys %path) {
|
|
*$method = sub {
|
|
my $self = shift;
|
|
ref $self or return $path{$method};
|
|
Carp::croak "Method '$method' is readonly and doesn't accept any parameters" if @_;
|
|
$self->valueof($path{$method});
|
|
};
|
|
}
|
|
my %fault = (
|
|
faultcode => 'faultCode',
|
|
faultstring => 'faultString',
|
|
);
|
|
for my $method (keys %fault) {
|
|
*$method = sub {
|
|
my $self = shift;
|
|
ref $self or Carp::croak "Method '$method' doesn't have shortcut";
|
|
Carp::croak "Method '$method' is readonly and doesn't accept any parameters" if @_;
|
|
defined $self->fault ? $self->fault->{$fault{$method}} : undef;
|
|
};
|
|
}
|
|
my %results = (
|
|
result => '/methodResponse/params/[1]',
|
|
paramsin => '/methodCall/params/param',
|
|
paramsall => '/methodResponse/params/param',
|
|
);
|
|
for my $method (keys %results) {
|
|
*$method = sub {
|
|
my $self = shift;
|
|
ref $self or return $results{$method};
|
|
Carp::croak "Method '$method' is readonly and doesn't accept any parameters" if @_;
|
|
defined $self->fault ? undef : $self->valueof($results{$method});
|
|
};
|
|
}
|
|
}
|
|
|
|
# ======================================================================
|
|
|
|
package XMLRPC::Deserializer;
|
|
|
|
@XMLRPC::Deserializer::ISA = qw(SOAP::Deserializer);
|
|
|
|
sub deserialize {
|
|
bless shift->SUPER::deserialize(@_) => 'XMLRPC::SOM';
|
|
}
|
|
|
|
sub decode_value {
|
|
my $self = shift;
|
|
my $ref = shift;
|
|
my($name, $attrs, $childs, $value) = @$ref;
|
|
|
|
if ($name eq 'value') {
|
|
$childs ? $self->decode_value($childs->[0]) : $value;
|
|
} elsif ($name eq 'array') {
|
|
return [map {scalar(($self->decode_object($_))[1])} @{$childs->[0]->[2] || []}];
|
|
} elsif ($name eq 'struct') {
|
|
return {map {
|
|
my %hash = map {$_->[0] => $_} @{$_->[2] || []};
|
|
# v----- scalar is required here, because 5.005 evaluates 'undef' in list context as empty array
|
|
($hash{name}->[3] => scalar(($self->decode_object($hash{value}))[1]));
|
|
} @{$childs || []}};
|
|
} elsif ($name eq 'base64') {
|
|
require MIME::Base64;
|
|
MIME::Base64::decode_base64($value);
|
|
} elsif ($name =~ /^(?:int|i4|boolean|string|double|dateTime\.iso8601|methodName)$/) {
|
|
return $value;
|
|
} elsif ($name =~ /^(?:params)$/) {
|
|
return [map {scalar(($self->decode_object($_))[1])} @{$childs || []}];
|
|
} elsif ($name =~ /^(?:methodResponse|methodCall)$/) {
|
|
return +{map {$self->decode_object($_)} @{$childs || []}};
|
|
} elsif ($name =~ /^(?:param|fault)$/) {
|
|
return scalar(($self->decode_object($childs->[0]))[1]);
|
|
} else {
|
|
die "wrong element '$name'\n";
|
|
}
|
|
}
|
|
|
|
# ======================================================================
|
|
|
|
package XMLRPC::Server;
|
|
|
|
@XMLRPC::Server::ISA = qw(SOAP::Server);
|
|
|
|
sub initialize {
|
|
return (
|
|
deserializer => XMLRPC::Deserializer->new,
|
|
serializer => XMLRPC::Serializer->new,
|
|
on_action => sub {},
|
|
on_dispatch => sub { return map {s!\.!/!; $_} shift->method =~ /^(?:(.*)\.)?(\w+)$/ },
|
|
);
|
|
}
|
|
|
|
# ======================================================================
|
|
|
|
package XMLRPC::Server::Parameters;
|
|
|
|
@XMLRPC::Server::Parameters::ISA = qw(SOAP::Server::Parameters);
|
|
|
|
# ======================================================================
|
|
|
|
package XMLRPC;
|
|
|
|
@XMLRPC::ISA = qw(SOAP);
|
|
|
|
# ======================================================================
|
|
|
|
package XMLRPC::Lite;
|
|
|
|
@XMLRPC::Lite::ISA = qw(SOAP::Lite);
|
|
|
|
sub new {
|
|
my $self = shift;
|
|
|
|
unless (ref $self) {
|
|
my $class = ref($self) || $self;
|
|
$self = $class->SUPER::new(
|
|
serializer => XMLRPC::Serializer->new,
|
|
deserializer => XMLRPC::Deserializer->new,
|
|
on_action => sub {return},
|
|
uri => 'http://unspecified/',
|
|
@_
|
|
);
|
|
}
|
|
return $self;
|
|
}
|
|
|
|
# ======================================================================
|
|
|
|
1;
|
|
|
|
__END__
|
|
|
|
=head1 NAME
|
|
|
|
XMLRPC::Lite - client and server implementation of XML-RPC protocol
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
=over 4
|
|
|
|
=item Client
|
|
|
|
use XMLRPC::Lite;
|
|
print XMLRPC::Lite
|
|
-> proxy('http://betty.userland.com/RPC2')
|
|
-> call('examples.getStateStruct', {state1 => 12, state2 => 28})
|
|
-> result;
|
|
|
|
=item CGI server
|
|
|
|
use XMLRPC::Transport::HTTP;
|
|
|
|
my $server = XMLRPC::Transport::HTTP::CGI
|
|
-> dispatch_to('methodName')
|
|
-> handle
|
|
;
|
|
|
|
=item Daemon server
|
|
|
|
use XMLRPC::Transport::HTTP;
|
|
|
|
my $daemon = XMLRPC::Transport::HTTP::Daemon
|
|
-> new (LocalPort => 80)
|
|
-> dispatch_to('methodName')
|
|
;
|
|
print "Contact to XMLRPC server at ", $daemon->url, "\n";
|
|
$daemon->handle;
|
|
|
|
=back
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
XMLRPC::Lite is a Perl modules which provides a simple nterface to the
|
|
XML-RPC protocol both on client and server side. Based on SOAP::Lite module,
|
|
it gives you access to all features and transports available in that module.
|
|
|
|
See F<t/26-xmlrpc.t> for client examples and F<examples/XMLRPC/*> for server
|
|
implementations.
|
|
|
|
=head1 DEPENDENCIES
|
|
|
|
SOAP::Lite
|
|
|
|
=head1 SEE ALSO
|
|
|
|
SOAP::Lite
|
|
|
|
=head1 CREDITS
|
|
|
|
The B<XML-RPC> standard is Copyright (c) 1998-2001, UserLand Software, Inc.
|
|
See <http://www.xmlrpc.com> for more information about the B<XML-RPC>
|
|
specification.
|
|
|
|
=head1 COPYRIGHT
|
|
|
|
Copyright (C) 2000-2001 Paul Kulchenko. All rights reserved.
|
|
|
|
This library is free software; you can redistribute it and/or modify
|
|
it under the same terms as Perl itself.
|
|
|
|
=head1 AUTHOR
|
|
|
|
Paul Kulchenko ([email protected])
|
|
|
|
=cut
|