#!/usr/bin/perl use warnings; use strict; use RPC::XML; use RPC::XML::Client; use Data::Dumper; use Getopt::Std; use XML::LibXML; use Carp qw(croak); use IO::Prompt; use constant GEONOTE_API_VERSION => 1; sub usage { die @_,<new($URL) or die "Couldn't create client"; # Get our session, logging in unless one was provided our $sessid; if ($opt{s}) { $sessid = $opt{s}; } elsif ($opt{c}) { if ($cmd ne 'login') { open(my $sess_file, '<', $opt{c}) or die "Error reading session file: $!\n"; defined($sessid = <$sess_file>) or die "Error reading session file (have you logged in?)\n"; chomp($sessid); close($sess_file) or die "Error closing session file: $!\n"; } } if (!$sessid || $cmd eq 'login') { if ($opt{a}) { $sessid = drupal_anonymous($client) or die "Couldn't create anonymous session\n"; } elsif ($cmd eq 'login' && @ARGV) { $sessid = drupal_login($client, $ARGV[0], pass_or_prompt($ARGV[1])) or die "Couldn't create session\n"; } else { die "No username given"; } if ($cmd eq 'login') { if ($opt{c}) { open(my $sess_file, '>', $opt{c}) or die "Error writing session file: $!\n"; print $sess_file $sessid,"\n" or die "Error writing session file: $!\n"; close($sess_file) or die "Error closing session file: $!\n"; } else { print $sessid,"\n"; } } else { if (!$opt{a}) { $autologout = 1; } } # A brief pause here seems necessary # Otherwise we sometimes get access denied on the next command # Drupal may be returning before everything is committed to the database. sleep(1); } # Whenever we quit, log out. END { drupal_logout($client) if ($autologout && $client && $sessid); }; my $resp; # Now take any further actions if ($cmd eq 'login') { print "Logged in succesfully.\n"; } elsif ($cmd eq 'logout') { drupal_logout($client); if ($opt{c}) { unlink($opt{c}) or die "Error removing session file: $!\n"; } } elsif ($cmd eq 'relsearch') { my $req = RPC::XML::request->new('user_relationships.search', $sessid, 1, makesearch(@ARGV)); print "SEARCH REQUEST:\n",as_xml($req) if ($ENV{VERBOSE}); resp_check($resp = $client->send_request($req)); print "SEARCH RESPONSE:\n" if ($ENV{VERBOSE}); print as_xml($resp); } elsif ($cmd eq 'reldel') { my $req = RPC::XML::request->new('user_relationships.delete.matching', $sessid, 1, makesearch(@ARGV), "vague distaste"); print "SEARCH REQUEST:\n",as_xml($req) if ($ENV{VERBOSE}); resp_check($resp = $client->send_request($req)); print "SEARCH RESPONSE:\n" if ($ENV{VERBOSE}); print as_xml($resp); } elsif ($cmd eq 'relreq') { my @who; foreach (@ARGV) { my %thisreq; foreach my $clause (split(/,/)) { if ($clause =~ /^(uid|type)=(.*)$/) { $thisreq{$1}=$2; } else { die "Couldn't parse request clause '$_'"; } } push(@who, \%thisreq); } my $req = RPC::XML::request->new('user_relationships.request.batch', $sessid, 1, \@who); print "RELATIONSHIP REQUEST:\n",as_xml($req) if ($ENV{VERBOSE}); resp_check($resp = $client->send_request($req)); print "RELATIONSHIP RESPONSE:\n" if ($ENV{VERBOSE}); print as_xml($resp); } elsif ($cmd eq 'invite') { my @who; my %msg; foreach (@ARGV) { if (/^(subject|message)=(.*)$/) { $msg{$1}=$2; } elsif (/^(mail)=(.*)$/) { my %thisaddr; foreach my $clause (split(/,/)) { if ($clause =~ /^(mail|relationship)=(.*)$/) { $thisaddr{$1}=$2; } else { die "Couldn't parse invite clause '$_'"; } } push(@who, \%thisaddr); } elsif (/^[^=]+$/) { push(@who, { mail => $_ }); } else { die "Couldn't parse invite clause '$_'"; } } my $req = RPC::XML::request->new('invite.send', $sessid, 1, \@who, \%msg); print "INVITE REQUEST:\n",as_xml($req) if ($ENV{VERBOSE}); resp_check($resp = $client->send_request($req)); print "INVITE RESPONSE:\n" if ($ENV{VERBOSE}); print as_xml($resp); } else { usage("Unknown command '$cmd'\n"); } exit(0); sub drupal_anonymous { my($client) = @_; my $sessid; my $req1 = RPC::XML::request->new('system.connect') or die "Couldn't create request"; print "SESSION REQUEST\n",as_xml($req1) if ($ENV{VERBOSE}); my $resp1 = $client->send_request($req1); resp_check($resp1); print "SESSION RESPONSE\n",as_xml($resp1) if ($ENV{VERBOSE}); $sessid = $resp1->value->{sessid} or die "No session in system.connect!\n"; print "anonymous session is '$sessid'\n" if ($ENV{VERBOSE}); return $sessid; } sub drupal_login { my($client,$user,$pass) = @_; my $sessid; # First get an anonymous session $sessid = drupal_anonymous($client); # Now get an authenticated session my $req2 = RPC::XML::request->new('user.login', $sessid, $user, $pass) or die "Couldn't create request 2\n"; print "LOGIN REQUEST\n",as_xml($req2) if ($ENV{VERBOSE}); my $resp2 = $client->send_request($req2) or die "Couldn't send request"; resp_check($resp2); print "LOGIN RESPONSE\n",as_xml($resp2) if ($ENV{VERBOSE}); $sessid = $resp2->value->{sessid} or die "No session!"; print "authenticated session is '$sessid'\n" if ($ENV{VERBOSE}); return $sessid; } sub drupal_logout { my($client) = @_; my $req = RPC::XML::request->new('user.logout', $sessid) or die "Couldn't create request"; print "LOGOUT REQUEST\n",as_xml($req) if ($ENV{VERBOSE}); my $resp = $client->send_request($req); print "LOGOUT RESPONSE\n",as_xml($resp) if ($ENV{VERBOSE}); resp_check($resp); } our $xml_parser; sub as_xml { my($r) = @_; $xml_parser ||= XML::LibXML->new(); my $xml = $xml_parser->parse_string($r->as_string) or die "Couldn't parse XML\n"; return $xml->toString(1); } sub resp_check { my($r) = @_; if (!$r) { croak "No response"; } elsif (!ref($r)) { croak "Bad response: $r"; } if ($r->is_fault) { croak "Faulty response: " . $r->string . " (#" . $r->code . ")"; } else { return $r; } } sub makesearch { my @q; while (@_) { my $search = shift; my $val; if ($search eq 'OR' || $search eq 'AND' || $search eq 'NOT') { $val = &makesearch; } elsif ($search eq 'END') { last; } else { $val = shift; } push(@q, { search => $search, value => $val }); } return \@q; } sub pass_or_prompt { my $pass = shift; my $prompt = shift || 'Password'; if ($pass) { return $pass; } else { $pass = prompt($prompt.': ', -e => '*', -t => ''); return $pass->{value}; } } sub pass_or_prompt_confirm { my $nomatch = ''; while(1) { my $v1 = pass_or_prompt($_[0],$nomatch.$_[1]); my $v2 = pass_or_prompt($_[0],"Re-enter ".$_[1]); if ($v1 eq $v2) { return $v1; } else { $nomatch = "Passwords did not match.\n"; } } }