File Coverage

lib/Catalyst/Plugin/Server/XMLRPC.pm
Criterion Covered Total %
statement 176 197 89.3
branch 45 66 68.2
condition 25 50 50.0
subroutine 26 29 89.7
pod 0 5 0.0
total 272 347 78.4


line stmt bran cond sub pod time code
1             =head1 NAME
2            
3             Catalyst::Plugin::Server::XMLRPC -- Catalyst XMLRPC Server Plugin
4            
5             =head1 SYNOPSIS
6            
7             package MyApp;
8             use Catalyst qw/Server Server::XMLRPC/;
9            
10             package MyApp::Controller::Example;
11             use base 'Catalyst::Controller';
12            
13             sub echo : XMLRPC { # available as: example.echo
14             my ( $self, $c, @args ) = @_;
15             $c->stash->{xmlrpc} = join ', ', @args;
16             }
17            
18             sub ping : XMLRPCPath('/ping') { # available as: ping
19             my ( $self, $c ) = @_;
20             $c->stash->{xmlrpc} = 'Pong';
21             }
22            
23             sub world : XMLRPCRegex(/hello/) { # available as: *hello*
24             my ($self, $c) = @_;
25             $c->stash->{xmlrpc} = 'World';
26             }
27            
28             sub echo : XMLRPCLocal { # available as: example.echo
29             my ( $self, $c, @args ) = @_;
30             $c->stash->{xmlrpc} = join ', ', @args;
31             }
32            
33             sub ping : XMLRPCGlobal { # available as: ping
34             my ( $self, $c ) = @_;
35             $c->stash->{xmlrpc} = 'Pong';
36             }
37            
38             =head1 DESCRIPTION
39            
40             XMLRPC Plugin for Catalyst which we tried to make compatible with the
41             way Catalyst works with URLS. Main features are:
42            
43             =over 4
44            
45             =item * Split XMLRPC methodNames by STRING to find out Controller.
46            
47             =item * Single entrypoint for XMLRPC calls, like http://host.tld/rpc
48            
49             =item * DispatchTypes (attributes) which work much the same as Catalyst attrs
50            
51             =item * XMLRPC Parameter handling transparent to Catalyst parameter handling
52            
53             =back
54            
55             =head1 HOW IT WORKS
56            
57             The default behaviour will handle XMLRPC Requests sent to C</rpc> by creating
58             an OBJECT containing XMLRPC specific parameters in C<< $c->req->xmlrpc >>.
59            
60             Directly after, it will find out the Path of the Action to dispatch to, by
61             splitting methodName by C<.>:
62            
63             methodName: hello.world
64             path : /hello/world
65            
66             From this point, it will dispatch to '/hello/world' when it exists,
67             like Catalyst Urls would do. What means: you will be able to set Regexes,
68             Paths etc on subroutines to define the endpoint.
69            
70             We discuss these custom XMLRPC attributes below.
71            
72             When the request is dispatched, we will return $c->stash->{xmlrpc} to the
73             xmlrpc client, or, when it is not available, it will return $c->stash to
74             the client. There is also a way of defining $c->stash keys to be send back
75             to the client.
76            
77             =head1 ATTRIBUTES
78            
79             You can mark any method in your Catalyst application as being
80             available remotely by using one of the following attributes,
81             which can be added to any existing attributes, except Private.
82             Remember that one of the mentioned attributes below are automatically
83             also Privates...
84            
85             =over 4
86            
87             =item XMLRPC
88            
89             Make this method accessible via XMLRPC, the same way as Local does
90             when using catalyst by URL.
91            
92             The following example will be accessible by method C<< hello.world >>:
93            
94             package Catalyst::Controller::Hello
95             sub world : XMLRPC {}
96            
97             =item XMLRPCLocal
98            
99             Identical version of attribute C<XMLRPC>
100            
101             =item XMLRPCGlobal
102            
103             Make this method accessible via XMLRPC, the same way as GLOBAL does
104             when using catalyst by URL.
105            
106             The following example will be accessible by method C<< ping >>:
107            
108             package Catalyst::Controller::Hello
109             sub ping : XMLRPCGlobal {}
110            
111             =item XMLRPCPath('/say/hello')
112            
113             Make this method accessible via XMLRPC, the same way as Path does
114             when using catalyst by URL.
115            
116             The following example will be accessible by method C<< say.hello >>:
117            
118             package Catalyst::Controller::Hello
119             sub hello : XMLRPCPath('/say/hello') {}
120            
121             =item XMLRPCRegex('foo')
122            
123             Make this method accessible via XMLRPC, the same way as Regex does
124             when using catalyst by URL.
125            
126             The following example will be accessible by example methods:
127             C<< a.foo.method >>
128             C<< wedoofoohere >>
129             C<< foo.getaround >>
130            
131             package Catalyst::Controller::Hello
132             sub hello : XMLRPCPath('foo') {}
133            
134             =back
135            
136             =head1 ACCESSORS
137            
138             Once you've used the plugin, you'll have an $c->request->xmlrpc accessor
139             which will return an C<Catalyst::Plugin::Server::XMLRPC> object.
140            
141             You can query this object as follows:
142            
143             =over 4
144            
145             =item $c->req->xmlrpc->is_xmlrpc_request
146            
147             Boolean indicating whether the current request has been initiated
148             via XMLRPC
149            
150             =item $c->req->xmlrpc->config
151            
152             Returns a C<Catalyst::Plugin::Server::XMLRPC::Config> object. See the
153             C<CONFIGURATION> below on how to use and configure it.
154            
155             =item $c->req->xmlrpc->body
156            
157             The body of the original XMLRPC call
158            
159             =item $c->req->xmlrpc->method
160            
161             The name of the original method called via XMLRPC
162            
163             =item $c->req->xmlrpc->args
164            
165             A list of parameters supplied by the XMLRPC call
166            
167             =item $c->req->xmlrpc->result_as_string
168            
169             The XML body that will be sent back to the XMLRPC client
170            
171             =item $c->req->xmlrpc->error
172            
173             Allows you to set xmlrpc fault code and message
174            
175             =back
176            
177             =head1 Server Accessors
178            
179             The following accessors are always available, whether you're in a xmlrpc
180             specific request or not
181            
182             =over 4
183            
184             =item $c->server->xmlrpc->list_methods
185            
186             Returns a HASHREF containing the available xmlrpc methods in Catalyst as
187             a key, and the C<Catalyst::Action> object as a value.
188            
189             =back
190            
191             =head1 CATALYST REQUEST
192            
193             To make things transparent, we try to put XMLRPC params into the Request
194             object of Catalyst. But first we will explain something about the XMLRPC
195             specifications.
196            
197             A full draft of these specifications can be found on:
198             C<http://www.xmlrpc.com/spec>
199            
200             In short, a xmlrpc-request consists of a methodName, like a subroutine
201             name, and a list of parameters. This list of parameters may contain strings
202             (STRING), arrays (LIST) and structs (HASH). Off course, these can be nested.
203            
204             =over 4
205            
206             =item $c->req->arguments
207            
208             We will put the list of arguments into $c->req->arguments, thisway you can
209             fetch this list within your dispatched-to-subroutine:
210            
211             sub echo : XMLRPC {
212             my ($self, $c, @args) = @_;
213             $c->log->debug($arg[0]); # Prints first XMLRPC parameter
214             # to debug log
215             }
216            
217             =item $c->req->parameters
218            
219             Because XMLRPC parameters are a LIST, we can't B<just> fill
220             $c->req->paremeters. To keep things transparent, we made an extra config
221             option what tells the XMLRPC server we can assume the following conditions
222             on all XMLRPC requests:
223             - There is only one XMLRPC parameter
224             - This XMLRPC parameter is a struct (HASH)
225            
226             We will put this STRUCT as key-value pairs into $c->req->parameters.
227            
228             =item $c->req->params
229            
230             Alias of $c->req->parameters
231            
232             =item $c->req->param
233            
234             Alias of $c->req->parameters
235            
236             =back
237            
238             =cut
239              
240             {   package Catalyst::Plugin::Server::XMLRPC;
241              
242 7     7   230     use strict;
  7         179  
  7         145  
243 7     7   137     use warnings;
  7         64  
  7         112  
244 7     7   124     use attributes ();
  7         64  
  7         147  
245              
246 7     7   211     use Data::Dumper;
  7         69  
  7         192  
247              
248                 my $ServerClass = 'Catalyst::Plugin::Server::XMLRPC::Backend';
249              
250             ### only for development dumps!
251                 my $Debug = 0;
252              
253             ###
254             ### Catalyst loading and dispatching
255             ###
256              
257             ### Loads our xmlrpc backend class in $c->server->xmlrpc
258                 sub setup_engine {
259 6     6 0 62         my $class = shift;
260 6         128         $class->server->register_server(
261                                 'xmlrpc' => $ServerClass->new($class)
262                             );
263 6         191         $class->NEXT::setup_engine(@_);
264                 }
265              
266             ### Will load our customized DispatchTypes into Catalyst
267                 sub setup_dispatcher {
268 6     6 0 59         my $class = shift;
269              
270             ### Load custom DispatchTypes
271 6         2363         $class->NEXT::setup_dispatcher( @_ );
272 6         72         $class->dispatcher->preload_dispatch_types(
273 6         85             @{$class->dispatcher->preload_dispatch_types},
274                         qw/ +Catalyst::Plugin::Server::XMLRPC::DispatchType::XMLRPCPath
275             +Catalyst::Plugin::Server::XMLRPC::DispatchType::XMLRPCRegex/
276                     );
277              
278 6         108         return $class;
279                 }
280              
281             ### Loads the xmlrpc-server object, redispatch to the method
282                 sub prepare_action {
283 21     21 0 205         my $c = shift;
284 21         196         my @args = @_;
285              
286             ### set up the accessor to hold an xmlrpc server instance
287 21         260         $c->req->register_server(
288                         'xmlrpc' => Catalyst::Plugin::Server::XMLRPC::Request->new()
289                     );
290              
291             ### are we an xmlrpc call? check the path against a regex
292 21         873         my $path = $c->server->xmlrpc->config->path;
293 21 100       1978         if( $c->req->path =~ /$path/) {
294              
295 19         269             PREPARE: {
296             ### mark us as an xmlrpc request
297 19         1346                 $c->req->xmlrpc->is_xmlrpc_request(1);
298              
299 19 50       245                 $c->log->debug( 'PREPARE WITH $c ' . Dumper ($c ) ) if $Debug;
300              
301 19 100       248                 $c->req->xmlrpc->_deserialize_xml( $c ) or last PREPARE;
302              
303             ### CAVEAT: we consider backing up to a default for a
304             ### xml-rpc method when the method doesn't exist a security
305             ### risk. So when the exact method doesn't exist, we return
306             ### an error.
307             ### TODO ARGH Because of regex methods, this won't work
308              
309             ### set the new request path, the one we will forward to
310 17         231                 $c->req->path( $c->req->xmlrpc->forward_path );
311              
312             ### filter change dispatch types to our OWN
313 17   50     4277                 { my $saved_dt = $c->dispatcher->dispatch_types || [];
  17         264  
314 17         1191                     my $dp_ns
315                                     = 'Catalyst::Plugin::Server::XMLRPC::DispatchType::';
316              
317 102 100       5661                     $c->dispatcher->dispatch_types(
318                                     [ grep {
319 17         244                             UNIVERSAL::isa(
320                                                 $_, $dp_ns . 'XMLRPCPath'
321                                             ) or
322                                         UNIVERSAL::isa(
323                                                 $_, $dp_ns . 'XMLRPCRegex'
324                                             )
325                                         } @$saved_dt
326                                     ]
327                                 );
328              
329             ### run the rest of the prepare actions, we should have
330             ### an action object now
331 17         632                     $c->NEXT::prepare_action( @_ );
332              
333             ### restore the saved dispatchtypes
334 17         38710                     $c->dispatcher->dispatch_types( $saved_dt );
335                             }
336              
337             ### check if we have a c->action now
338             ### check if the NEW action isn't hte same as the
339             ### OLD action -- which mean no method was found
340             ### Not needed, don't have an action until we NEXT
341 17 100 66     6498                 if( (not $c->action) &&
342                                 !$c->server->xmlrpc->private_methods->{
343                                                             $c->req->xmlrpc->method
344                                                         }
345                             ) {
346 4         113                     $c->req->xmlrpc->_error(
347                                     $c, qq[Invalid XMLRPC request: No such method]
348                                 );
349 4         170                     last PREPARE;
350                             }
351                         }
352              
353             ### XMLRPC parameters and argument processing, see the Request
354             ### class below for information why we can't do it there.
355 19 50       770             $c->req->parameters( $c->req->xmlrpc->params )
356                                     if $c->server->xmlrpc->config->convert_params;
357              
358 19         1661             $c->req->args($c->req->xmlrpc->args );
359              
360             ### we're no xmlrpc request, so just let others handle it
361                     } else {
362 2         100             $c->NEXT::prepare_action( @_ );
363                     }
364                 }
365              
366             ### before we dispatch, make sure no xmlrpc errors have happened already,
367             ### or an internal method has been called.
368                 sub dispatch {
369 21     21 0 650         my $c = shift;
370              
371 21 100 100     436         if( $c->req->xmlrpc->is_xmlrpc_request and
  19 50 66     298  
372                         scalar( @{ $c->error } )
373                     ) {
374 6         153             1;
375                     } elsif (
376                             $c->req->xmlrpc->is_xmlrpc_request and
377                             $c->server->xmlrpc->private_methods->{$c->req->xmlrpc->method}
378                     ) {
379 0         0                 $c->req->xmlrpc->run_method($c);
380                     } else {
381 15         281             $c->NEXT::dispatch( @_ );
382                     }
383                 }
384              
385                 sub finalize {
386 21     21 0 5669         my $c = shift;
387              
388 21 100       271         if( $c->req->xmlrpc->is_xmlrpc_request ) {
389              
390             ### if we got an error anywhere, we'll return a fault
391             ### othwerise, the resultset will be returned
392             ### XXX TODO make error codes configurable ( done )
393             ### XXX TODO make messages customizable ( done )
394 19         160             my $res;
395 19         262             my $req_error = $c->req->xmlrpc->error;
396 19 100 100     173             if( scalar @{ $c->error } or $req_error ) {
  19         213  
397 7 50       183                 if ($c->server->xmlrpc->config->show_errors) {
398 7 100 66     112                     if ( $req_error && ref $req_error eq 'ARRAY' ) {
399 1         45                          $res = RPC::XML::fault->new( @{ $req_error } );
  1         20  
400                                 } else {
401 6         67                          $res = RPC::XML::fault->new( -1,
402 6         54                                 join $/, @{ $c->error }
403                                         );
404                                 }
405                             } else {
406 0 0 0     0                     if ( $req_error && ref $req_error eq 'ARRAY' ) {
407 0         0                         $res = RPC::XML::fault->new( @{ $req_error } );
  0         0  
408                                 } else {
409 0         0                         $c->log->debug("XMLRPC 500 Errors:\n" .
410 0         0                                         join("\n", @{ $c->error })
411                                                 );
412 0         0                         $res = RPC::XML::fault->new(
413                                                         500,
414                                                         'Internal Server Error'
415                                                     );
416                                 }
417                             }
418                         } else {
419 12 100       442                 if( exists $c->stash->{xmlrpc} ) {
420 7         192                     $res = $c->stash->{xmlrpc};
421                             } else {
422 5         118                     $res = $c->stash;
423                             }
424                         }
425              
426 19         1903             $c->res->body(
427                             $c->req->xmlrpc->_serialize_xmlrpc( $c, $res )
428                         );
429              
430             ### make sure to clear the error, so catalyst doesn't try
431             ### to deal with it
432 19         266             $c->error( 0 );
433                     }
434              
435 21 50       848         $c->log->debug( 'FINALIZE ' . Dumper ( $c, \@_ ) ) if $Debug;
436              
437             ### always call finalize at the end, so Catalyst's final handler
438             ### gets called as well
439 21         434         $c->NEXT::finalize( @_ );
440                 }
441             }
442              
443             ### The server implementation
444             {   package Catalyst::Plugin::Server::XMLRPC::Backend;
445              
446 7     7   196     use base qw/Class::Accessor::Fast/;
  7         73  
  7         120  
447 7     7   111     use Data::Dumper;
  7         64  
  7         889  
448              
449                 __PACKAGE__->mk_accessors( qw/
450             dispatcher
451             private_methods
452             c
453             config
454             /
455                                         );
456              
457                 sub new {
458 6     6   61         my $class = shift;
459 6         58         my $c = shift;
460 6         165         my $self = $class->SUPER::new( @_ );
461              
462 6         83         $self->c($c);
463 6         79         $self->config( Catalyst::Plugin::Server::XMLRPC::Config->new( $c ) );
464 6         82         $self->private_methods({});
465 6         74         $self->dispatcher({});
466              
467             ### Internal function
468                     $self->add_private_method(
469                         'system.listMethods' => sub {
470 0     0   0                 my ($c_ob, @args) = @_;
471                             return [ keys %{
472 0         0                     $c_ob->server->xmlrpc->list_methods;
  0         0  
473                                 } ];
474                         }
475 6         80         );
476              
477 6         88         return $self;
478                 }
479              
480                 sub add_private_method {
481 6     6   64         my ($self, $name, $sub) = @_;
482              
483 6 50 33     119         return unless ($name && UNIVERSAL::isa($sub,'CODE'));
484 6         73         $self->private_methods->{$name} = $sub;
485 6         61         return 1;
486                 }
487              
488                 sub list_methods {
489 0     0   0         my ($self) = @_;
490 0         0         return $self->dispatcher->{Path}->methods($self->c);
491                 }
492             }
493              
494             ### the config implementation ###
495             {   package Catalyst::Plugin::Server::XMLRPC::Config;
496 7     7   124     use base 'Class::Accessor::Fast';
  7         69  
  7         103  
497              
498             ### XXX change me to an ENTRYPOINT!
499                 my $DefaultPath = qr!^(/?)rpc(/|$)!i;
500                 my $DefaultAttr = 'XMLRPC';
501                 my $DefaultPrefix = '';
502                 my $DefaultSep = '.';
503                 my $DefaultShowErrors = 0;
504              
505             ### XXX add: stash_fields (to encode) stash_exclude_fields (grep -v)
506              
507                 __PACKAGE__->mk_accessors(
508                     qw/ path prefix seperator attribute convert_params
509             show_errors xml_encoding
510             /
511                 );
512              
513             ### return the cached version where possible
514                 my $Obj;
515                 sub new {
516 6 50   6   75         return $Obj if $Obj;
517              
518 6         90         my $class = shift;
519 6         90         my $c = shift;
520 6         144         my $self = $class->SUPER::new;
521              
522 6   33     201         $self->prefix( $c->config->{xmlrpc}->{prefix} || $DefaultPrefix);
523 6   33     81         $self->seperator($c->config->{xmlrpc}->{seperator} || $DefaultSep);
524 6   33     81         $self->path( $c->config->{xmlrpc}->{path} || $DefaultPath);
525 6   33     81         $self->show_errors( $c->config->{xmlrpc}->{show_errors}
526                                             || $DefaultShowErrors );
527 6 100       383         $self->xml_encoding( $c->config->{xmlrpc}->{xml_encoding} )
528                             if $c->config->{xmlrpc}->{xml_encoding};
529 6         79         $self->attribute($DefaultAttr);
530 6         122         $self->convert_params( 1 );
531              
532             ### cache it
533 6         88         return $Obj = $self;
534                 }
535             }
536              
537             ### the server class implementation ###
538             {   package Catalyst::Plugin::Server::XMLRPC::Request;
539              
540 7     7   178     use strict;
  7         76  
  7         111  
541 7     7   101     use warnings;
  7         89  
  7         135  
542              
543 7     7   281     use RPC::XML;
  7         70  
  7         173  
544 7     7   272     use RPC::XML::Parser;
  7         74  
  7         171  
545              
546 7     7   126     use Data::Dumper;
  7         66  
  7         202  
547 7     7   134     use Text::SimpleTable;
  7         65  
  7         242  
548              
549 7     7   109     use base 'Class::Data::Inheritable';
  7         64  
  7         119  
550 7     7   819     use base 'Class::Accessor::Fast';
  7         68  
  7         95  
551              
552                 __PACKAGE__->mk_accessors( qw[ forward_path args method body result
553             is_xmlrpc_request params
554             result_as_string internal_methods error
555             ] );
556              
557                 __PACKAGE__->mk_classdata( qw[_xmlrpc_parser]);
558                 __PACKAGE__->_xmlrpc_parser( RPC::XML::Parser->new );
559              
560                 *parameters = *params;
561              
562                 sub run_method {
563 0     0   0         my ($self, $c) = @_;
564              
565 0         0         $c->stash->{xmlrpc} =
566 0         0             &{$c->server->xmlrpc->private_methods->{$self->method}}($c, @{ $c->req->args });
  0         0  
567                 }
568              
569                 sub _deserialize_xml {
570 19     19   186         my ($self, $c) = @_;
571              
572 19 100       255         local $RPC::XML::ENCODING = $c->server->xmlrpc->config->xml_encoding
573                             if $c->server->xmlrpc->config->xml_encoding;
574              
575             ### the parser will die on failure, make sure we catch it
576 19         187         my $content; my $req;
  19         184  
577 19         173         eval {
578             ## Make sure we do not read from empty filehandle,
579             ## by sending empty string
580 19 100       159             $content = do { local $/; my $b = $c->req->body; $b ? <$b> : ''};
  19         214  
  19         335  
  19         336  
581 19         2661             $req = $self->_xmlrpc_parser->parse( $content );
582              
583             ### RPC::XML::Parser *returns* the error string on error
584             ### OR an object... *sigh*
585 19 100       3726             die $req unless ref $req;
586              
587             ### Because we will die when request is not valid XMLRPC,
588             ### we simply test it. XXX TODO This results in a malformed
589             ### xml detected error, maybe we should catch it.
590 18         269             $req->name;
591 17         367             $req->args;
592                     };
593              
594             ### parsing the request went fine
595 19 100 66     399         if ( not $@ and defined $req->name ) {
    50          
596              
597 17         448             $self->body( $content ); # original xml message
598 17         595             $self->method( $req->name ); # name of the method
599              
600             ### allow the args to be encoded as a HASH when requested
601             ### xmlrpc only knows a top level 'list', and we can not tell
602             ### if that is meant to be a hash or not
603             ### make sure to store args as an ARRAY REF! to be compatible
604             ### with catalyst
605 17         2505             my @args = map { $_->value } @{ $req->args };
  36         693  
  17         188  
606 17         562             $self->args( \@args ); # parsed arguments
607              
608             ### HEURISTIC! IF @args == 1 AND it's a HASHREF,
609             ### then we can assume it's key => value pairs in there
610             ### and we will map them to $c->req->params
611 17 100 66     596             $self->params(
612                             @args == 1 && UNIVERSAL::isa($args[0], 'HASH')
613                                 ? $args[0]
614                                 : {}
615                         );
616              
617             ### build the relevant namespace, action and path
618                         { ### construct the forward path -- this allows catalyst to
619             ### do the hard work of dispatching for us
620 17         365                 my $prefix = $c->server->xmlrpc->config->prefix;
  17         235  
621 17         473                 my ($sep) = map { qr/$_/ }
  17         8298  
622 17         845                               map { quotemeta $_ }
623                                                     $c->server->xmlrpc->config->seperator;
624              
625             ### error checks here
626 17 50       204                 if( $prefix =~ m|^/| ) {
627 0 0       0                     $c->log->debug( __PACKAGE__ . ": Your prefix starts with".
628                                                 " a / -- This is not recommended"
629                                             ) if $c->debug;
630                             }
631              
632 17 50       228                 unless( UNIVERSAL::isa( $sep, 'Regexp' ) ) {
633 0 0       0                     $c->log->debug( __PACKAGE__ . ": Your seperator is not a ".
634                                                 "Regexp object -- This is not recommended"
635                                             ) if $c->debug;
636                             }
637              
638             ### foo.bar => $prefix/foo/bar
639             ### DO NOT add a leading slash! uri.pm gets very upset
640 17         246                 my @parts = split( $sep, $self->method );
641 68 50       2745                 my $fwd_path = join '/',
642 17         543                                 grep { defined && length } $prefix, @parts;
643              
644              
645             ### Complete our object-instance
646 17         222                 $self->forward_path( $fwd_path );
647              
648             ### Notify system of called rpc method and arguments
649 17 50       641                 $c->log->debug('XML-RPC: Method called: ' . $self->method)
650                                  if $c->debug;
651 17 50 33     293                 if ($c->server->xmlrpc->config->convert_params &&
652                                     $self->params
653                             ) {
654 17         470                     my $params = Text::SimpleTable->new( [ 36, 'Key' ], [ 37, 'Value' ] );
655 17         3359                     foreach my $key (sort keys %{$self->params}) {
  17         216  
656 1         27                         my $value = $self->params->{$key};
657 1   33     23                         $value = ref($value) || $value;
658 1         13                         $params->row($key, $value);
659                                 }
660 0         0                     $c->log->debug("XML-RPC: Parameters:\n" . $params->draw)
661 17 50 33     1564                                 if ($c->debug && %{$self->params});
662                             }
663                         }
664              
665             ### an error in parsing the request
666                     } elsif ( $@ ) {
667 2         127             $self->_error( $c, qq[Invalid XMLRPC request "$@"] );
668 2         106             return;
669              
670             ### something is wrong, but who knows what...
671                     } else {
672 0         0             $self->_error( $c, qq[Invalid XMLRPC request: Unknown error] );
673 0         0             return;
674                     }
675              
676 17         647         return $self;
677                 }
678              
679             ### alias arguments to args
680                 *arguments = *args;
681              
682             ### Serializes the response to $c->res->body
683                 sub _serialize_xmlrpc {
684 19     19   187         my ( $self, $c, $status ) = @_;
685 19         333         my $res = RPC::XML::response->new($status);
686              
687 19         9096         $c->res->content_type('text/xml');
688              
689 19         5106         return $self->result_as_string( $res->as_string );
690                 }
691              
692             ### record errors in the error and debug log -- just for convenience
693                 sub _error {
694 6     6   68         my($self, $c, $msg) = @_;
695              
696 6 50       74         $c->log->debug( $msg ) if $c->debug;
697 6         133         $c->error( $msg );
698                 }
699             }
700              
701              
702             1;
703              
704             __END__
705            
706             =head1 INTERNAL XMLRPC FUNCTIONS
707            
708             The following system functions are available to the public.,
709            
710             =over 4
711            
712             =item system.listMethods
713            
714             returns a list of available RPC methods.
715            
716             =back
717            
718             =head1 DEFINING RETURN VALUES
719            
720             The XML-RPC response must contain a single parameter, which may contain
721             an array (LIST), struct (HASH) or a string (STRING). To define the return
722             values in your subroutine, you can alter $c->stash in three different ways.
723            
724             =head2 Defining $c->stash->{xmlrpc}
725            
726             When defining $c->stash->{xmlrpc}, the XMLRPC server will return these values
727             to the client.
728            
729             =head2 When there is no $c->stash->{xmlrpc}
730            
731             When there is no C<< $c->stash->{xmlrpc} >> set, it will return the complete
732             C<< $c->stash >>
733            
734             =head1 CONFIGURATION
735            
736             The XMLRPC Plugin accepts the following configuration options, which can
737             be set in the standard Catalyst way (See C<perldoc Catalyst> for details):
738            
739             Your::App->config( xmlrpc => { key => value } );
740            
741             You can look up any of the config parameters this package uses at runtime
742             by calling:
743            
744             $c->server->xmlrpc->config->KEY
745            
746             =over 4
747            
748             =item path
749            
750             This specifies the entry point for your xmlrpc server; all requests are
751             dispatched from there. This is the url any XMLRCP client should post to.
752             You can change this to any C<Regex> wish.
753            
754             The default is: C<qr!^(/?)rpc(/|$)!i>, which matches on a top-level path
755             begining with C<rpc> preceeded or followed by an optional C</>, like this:
756            
757             http://your-host.tld/rpc
758            
759             =item prefix
760            
761             This specifies the prefix of the forward url.
762            
763             For example, with a prefix of C<rpc>, and a method C<foo>, the forward
764             path would be come C</rpc/foo>.
765            
766             The default is '' (empty).
767            
768             =item seperator
769            
770             This is a STRING used to split your method on, allowing you to use
771             a hierarchy in your method calls.
772            
773             For example, with a seperator of C<.> the method call C<demo.echo>
774             would be forwarded to C</demo/echo>. To make C<demo_echo> forward to the
775             same path, you would change the seperator to C<_>,
776            
777             The default is C<.>, splitting methods on a single C<.>
778            
779             =item convert_params
780            
781             Make the arguments in C<< $c->req->xmlrpc->params >> available as
782             C<< $c->req->params >>.
783            
784             Defaults to true.
785            
786             =item show_errors
787            
788             Make system errors in C<< $c->error >> public to the rpc-caller in a XML-RPC
789             faultString. When show_errors is false, and your catalyst app generates a
790             fault, it will return an XML-RPC fault containing error number 500 and error
791             string: "Internal Server Error".
792            
793             Defaults to false.
794            
795             =item xml_encoding
796            
797             Change the xml encoding send over to the client. So you could change the
798             default encoding to C<UTF-8> for instance.
799            
800             Defaults to C<us-ascii> which is the default of C<RPC::XML>.
801            
802             =back
803            
804             =head1 DIAGNOSTICS
805            
806             =over 4
807            
808             =item Invalid XMLRPC request: No such method
809            
810             There is no corresponding method in your application that can be
811             forwarded to.
812            
813             =item Invalid XMLRPC request %s
814            
815             There was an error parsing the XMLRPC request
816            
817             =item Invalid XMLRPC request: Unknown error
818            
819             An unexpected error occurred
820            
821             =back
822            
823             =head1 TODO
824            
825             =over 4
826            
827             =item Make error messages configurable/filterable
828            
829             Right now, whatever ends up on $c->error gets returned to the client.
830             It would be nice to have a way to filter/massage these messages before
831             they are sent back to the client.
832            
833             =item Make stash filterable before returning
834            
835             Just like the error messages, it would be nice to be able to filter the
836             stash before returning so you can filter out keys you don't want to
837             return to the client, or just return a certain list of keys.
838             This all to make transparent use of XMLRPC and web easier.
839            
840             =back
841            
842             =head1 SEE ALSO
843            
844             L<Catalyst::Plugin::Server::XMLRPC::Tutorial>, L<Catalyst::Manual>,
845             L<Catalyst::Request>, L<Catalyst::Response>, L<RPC::XML>,
846             C<bin/rpc_client>
847            
848             =head1 ACKNOWLEDGEMENTS
849            
850             For the original implementation of this module:
851            
852             Marcus Ramberg, C<mramberg@cpan.org>
853             Christian Hansen
854             Yoshinori Sano
855            
856             =head1 AUTHORS
857            
858             Jos Boumans (kane@cpan.org)
859            
860             Michiel Ootjers (michiel@cpan.org)
861            
862             =head1 BUG REPORTS
863            
864             Please submit all bugs regarding C<Catalyst::Plugin::Server> to
865             C<bug-catalyst-plugin-server@rt.cpan.org>
866            
867             =head1 LICENSE
868            
869             This library is free software, you can redistribute it and/or modify
870             it under the same terms as Perl itself.
871            
872             =cut
873