- #!/usr/bin/perl
- # Demo external plugin. Kinda pointless, since it's a perl script, but
- # useful for testing or as an hint of how to write an external plugin in
- # other languages.
- use warnings;
- use strict;
- print STDERR "externaldemo plugin running as pid $$\n";
- use RPC::XML;
- use RPC::XML::Parser;
- use IO::Handle;
- # autoflush stdout
- $|=1;
- # Used to build up RPC calls as they're read from stdin.
- my $accum="";
- sub rpc_read {
- # Read stdin, a line at a time, until a whole RPC call is accumulated.
- # Parse to XML::RPC object and return.
- while (<>) {
- $accum.=$_;
- # Kinda hackish approach to parse a single XML RPC out of the
- # accumulated input. Perl's RPC::XML library doesn't
- # provide a better way to do it. Relies on calls always ending
- # with a newline, which ikiwiki's protocol requires be true.
- if ($accum =~ /^\s*(<\?xml\s.*?<\/(?:methodCall|methodResponse)>)\n(.*)/s) {
- $accum=$2; # the rest
-
- # Now parse the XML RPC.
- my $r = RPC::XML::Parser->new->parse($1);
- if (! ref $r) {
- die "error: XML RPC parse failure $r";
- }
- return $r;
- }
- }
- return undef;
- }
- sub rpc_handle {
- # Handle an incoming XML RPC command.
- my $r=rpc_read();
- if (! defined $r) {
- return 0;
- }
- if ($r->isa("RPC::XML::request")) {
- my $name=$r->name;
- my @args=map { $_->value } @{$r->args};
- # Dispatch the requested function. This could be
- # done with a switch statement on the name, or
- # whatever. I'll use eval to call the function with
- # the name.
- my $ret = eval $name.'(@args)';
- die $@ if $@;
-
- # Now send the repsonse from the function back,
- # followed by a newline.
- my $resp=RPC::XML::response->new($ret);
- $resp->serialize(\*STDOUT);
- print "\n";
- # stdout needs to be flushed here. If it isn't,
- # things will deadlock. Perl flushes it
- # automatically when $| is set.
- return 1;
- }
- elsif ($r->isa("RPC::XML::response")) {
- die "protocol error; got a response when expecting a request";
- }
- }
- sub rpc_call {
- # Make an XML RPC call and return the result.
- my $command=shift;
- my @params=@_;
- my $req=RPC::XML::request->new($command, @params);
- $req->serialize(\*STDOUT);
- print "\n";
- # stdout needs to be flushed here to prevent deadlock. Perl does it
- # automatically when $| is set.
-
- my $r=rpc_read();
- if ($r->isa("RPC::XML::response")) {
- return $r->value->value;
- }
- else {
- die "protocol error; got a request when expecting a response";
- }
- }
- # Now on with the actual plugin. Let's do a simple preprocessor plugin.
- sub import {
- # The import function will be called by ikiwiki when the plugin is
- # loaded. When it's imported, it needs to hook into the preprocessor
- # stage of ikiwiki.
- rpc_call("hook", type => "preprocess", id => "externaldemo", call => "preprocess");
- # Here's an example of how to inject an arbitrary function into
- # ikiwiki. Ikiwiki will be able to call bob() just like any other
- # function. Note use of automatic memoization.
- rpc_call("inject", name => "IkiWiki::bob", call => "bob",
- memoize => 1);
- # Here's an exmaple of how to access values in %IkiWiki::config.
- print STDERR "url is set to: ".
- rpc_call("getvar", "config", "url")."\n";
- print STDERR "externaldemo plugin successfully imported\n";
- }
- sub preprocess {
- # This function will be called when ikiwiki wants to preprocess
- # something.
- my %params=@_;
- # Let's use IkiWiki's pagetitle function to turn the page name into
- # a title.
- my $title=rpc_call("pagetitle", $params{page});
- return "externaldemo plugin preprocessing on $title!";
- }
- sub bob {
- print STDERR "externaldemo plugin's bob called via RPC";
- }
- # Now all that's left to do is loop and handle each incoming RPC request.
- while (rpc_handle()) { print STDERR "externaldemo plugin handled RPC request\n" }
|