RPC.pm 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. package XML::RPC;
  2. use strict;
  3. use XML::TreePP;
  4. use Data::Dumper;
  5. use vars qw($VERSION $faultCode);
  6. no strict 'refs';
  7. $VERSION = 0.5;
  8. sub new {
  9. my $package = shift;
  10. my $self = { };
  11. bless $self, $package;
  12. $self->{url} = shift;
  13. $self->{tpp} = XML::TreePP->new(@_);
  14. return $self;
  15. }
  16. sub call {
  17. my $self = shift;
  18. my ( $methodname, @params ) = @_;
  19. die 'no url' if ( !$self->{url} );
  20. $faultCode = 0;
  21. my $xml = $self->create_call_xml( $methodname, @params );
  22. #print STDERR $xml;
  23. my $result = $self->{tpp}->parsehttp(
  24. POST => $self->{url},
  25. $xml,
  26. {
  27. 'Content-Type' => 'text/xml',
  28. 'User-Agent' => 'XML-RPC/' . $VERSION,
  29. 'Content-Length' => length($xml)
  30. }
  31. );
  32. my @data = $self->unparse_response($result);
  33. return @data == 1 ? $data[0] : @data;
  34. }
  35. sub receive {
  36. my $self = shift;
  37. my $result = eval {
  38. my $xml = shift || die 'no xml';
  39. my $handler = shift || die 'no handler';
  40. my $hash = $self->{tpp}->parse($xml);
  41. my ( $methodname, @params ) = $self->unparse_call($hash);
  42. $self->create_response_xml( $handler->( $methodname, @params ) );
  43. };
  44. return $self->create_fault_xml($@) if ($@);
  45. return $result;
  46. }
  47. sub create_fault_xml {
  48. my $self = shift;
  49. my $error = shift;
  50. chomp($error);
  51. return $self->{tpp}
  52. ->write( { methodResponse => { fault => $self->parse( { faultString => $error, faultCode => $faultCode } ) } } );
  53. }
  54. sub create_call_xml {
  55. my $self = shift;
  56. my ( $methodname, @params ) = @_;
  57. return $self->{tpp}->write(
  58. {
  59. methodCall => {
  60. methodName => $methodname,
  61. params => { param => [ map { $self->parse($_) } @params ] }
  62. }
  63. }
  64. );
  65. }
  66. sub create_response_xml {
  67. my $self = shift;
  68. my @params = @_;
  69. return $self->{tpp}->write( { methodResponse => { params => { param => [ map { $self->parse($_) } @params ] } } } );
  70. }
  71. sub parse {
  72. my $self = shift;
  73. my $p = shift;
  74. my $result;
  75. if ( ref($p) eq 'HASH' ) {
  76. $result = $self->parse_struct($p);
  77. }
  78. elsif ( ref($p) eq 'ARRAY' ) {
  79. $result = $self->parse_array($p);
  80. }
  81. else {
  82. $result = $self->parse_scalar($p);
  83. }
  84. return { value => $result };
  85. }
  86. sub parse_scalar {
  87. my $self = shift;
  88. my $scalar = shift;
  89. local $^W = undef;
  90. if ( ( $scalar =~ m/^[\-+]?\d+$/ )
  91. && ( abs($scalar) <= ( 0xffffffff >> 1 ) ) )
  92. {
  93. return { i4 => $scalar };
  94. }
  95. elsif ( $scalar =~ m/^[\-+]?\d+\.\d+$/ ) {
  96. return { double => $scalar };
  97. }
  98. else {
  99. return { string => \$scalar };
  100. }
  101. }
  102. sub parse_struct {
  103. my $self = shift;
  104. my $hash = shift;
  105. my @members;
  106. while ( my ( $k, $v ) = each(%$hash) ) {
  107. push @members, { name => $k, %{ $self->parse($v) } };
  108. }
  109. return { struct => { member => \@members } };
  110. }
  111. sub parse_array {
  112. my $self = shift;
  113. my $array = shift;
  114. return { array => { data => { value => [ map { $self->parse($_)->{value} } $self->list($array) ] } } };
  115. }
  116. sub unparse_response {
  117. my $self = shift;
  118. my $hash = shift;
  119. my $response = $hash->{methodResponse} || die 'no data';
  120. if ( $response->{fault} ) {
  121. return $self->unparse_value( $response->{fault}->{value} );
  122. }
  123. else {
  124. return map { $self->unparse_value( $_->{value} ) } $self->list( $response->{params}->{param} );
  125. }
  126. }
  127. sub unparse_call {
  128. my $self = shift;
  129. my $hash = shift;
  130. my $response = $hash->{methodCall} || die 'no data';
  131. my $methodname = $response->{methodName};
  132. my @args =
  133. map { $self->unparse_value( $_->{value} ) } $self->list( $response->{params}->{param} );
  134. return ( $methodname, @args );
  135. }
  136. sub unparse_value {
  137. my $self = shift;
  138. my $value = shift;
  139. my $result;
  140. return $value if ( ref($value) ne 'HASH' ); # for unspecified params
  141. if ( $value->{struct} ) {
  142. $result = $self->unparse_struct( $value->{struct} );
  143. return !%$result
  144. ? undef
  145. : $result; # fix for empty hashrefs from XML::TreePP
  146. }
  147. elsif ( $value->{array} ) {
  148. return $self->unparse_array( $value->{array} );
  149. }
  150. else {
  151. return $self->unparse_scalar($value);
  152. }
  153. }
  154. sub unparse_scalar {
  155. my $self = shift;
  156. my $scalar = shift;
  157. my ($result) = values(%$scalar);
  158. return ( ref($result) eq 'HASH' && !%$result )
  159. ? undef
  160. : $result; # fix for empty hashrefs from XML::TreePP
  161. }
  162. sub unparse_struct {
  163. my $self = shift;
  164. my $struct = shift;
  165. return { map { $_->{name} => $self->unparse_value( $_->{value} ) } $self->list( $struct->{member} ) };
  166. }
  167. sub unparse_array {
  168. my $self = shift;
  169. my $array = shift;
  170. my $data = $array->{data};
  171. return [ map { $self->unparse_value($_) } $self->list( $data->{value} ) ];
  172. }
  173. sub list {
  174. my $self = shift;
  175. my $param = shift;
  176. return () if ( !$param );
  177. return @$param if ( ref($param) eq 'ARRAY' );
  178. return ($param);
  179. }
  180. 1;