package PPM::Repository::PPMServer; use strict; use PPM::PPD; use PPM::Search; use PPM::Result qw(Ok Error Warning List); use base qw(PPM::Repository); use vars qw($VERSION @ISA); use Data::Dumper; $VERSION = '3.06'; #============================================================================= # Note: The server appears to expose this interface: # search_ppds('archname', 'package', 'searchtag') # fetch_ppd('package') # fetch_summary() # packages() #============================================================================= # The server-side search function only supports totally lame queries: sub search { my $o = shift; my $target = shift; my $qstring = $o->mod_to_pkg(shift); my ($q_type, $query, $searchtag) = parse_query($qstring); my $casei = shift; my @ppds; if ($q_type eq 'TRADITIONAL') { substr($query, 0, 0) = "(?i)" if $casei; my $archname = $target->config_get("ARCHITECTURE")->result; my $data = eval { $o->{client}->search_ppds($archname, $query, $searchtag)->result }; if ($@) { chomp $@; return Error("server-side search failed: $@"); } my $res = $o->parse_summary($data, $qstring); return $res unless $res->ok; @ppds = values %{$res->result}; } else { unless ($o->{full_summary}) { my $data = eval { $o->{client}->fetch_summary()->result }; if ($@) { chomp $@; return Error("server-side summary fetch failed: $@"); } $o->{full_summary} = $data; } my $res = $o->parse_summary($o->{full_summary}, 'full_summary'); return $res unless $res->ok; my $compiled = PPM::PPD::Search->new($query, $casei); return Error($compiled->error) unless $compiled->valid; @ppds = $compiled->search(values %{$res->result}); } $_->{is_complete} = 0 for @ppds; return List(@ppds); } sub describe { my $o = shift; my $target = shift; my $pkg = $o->mod_to_pkg(shift); my $ppd = $o->getppd($target, $pkg); return $ppd unless $ppd->ok; my $ppd_ref = PPM::PPD->new($ppd->result, $o, $pkg); $ppd_ref->{from} = 'repository'; return Ok($ppd_ref); } sub getppd { my $o = shift; my $target = shift; my $pkg = $o->mod_to_pkg(shift); my $ppd = eval { $o->{client}->fetch_ppd($pkg)->result }; if ($@) { chomp $@; return Error("server-side fetch-ppd failed: $@"); } elsif (not $ppd) { return Error("Package '$pkg' not found on server. " . "Please 'search' for it first."); } Ok($ppd); } sub load_pkg { } sub type_printable { "PPMServer 2.0" } #============================================================================= # This query parser decides what type of query we're getting: a traditional # query which searches by TITLE only, or an advanced query: #============================================================================= sub parse_query { my $query = shift; # If the query is '*', return everything: if ($query eq '*') { # Although we could do this with TRADITIONAL, it's actually faster to # request the whole summary, and just return it directly. It's also # more portable: the guy at theoryx5.uwinnipeg.ca decided not to # implement empty searches. He must have reverse-engineered the PPM # Server. return ('ADVANCED', ''); } # If there are only alphanumeric characters in it: if ($query =~ /^[-_A-Za-z0-9]+$/) { return ('TRADITIONAL', $query); } # If there's only 1 field spec: i.e. NAME=foo, or TITLE=bar if ($query =~ /^([A-Za-z]+)=([-_A-Za-z0-9]+)$/ && is_traditional($1)) { my ($f, $q) = (uc($1), $2); $f = 'TITLE' if $f eq 'NAME'; # Required for the server return ('TRADITIONAL', $q, $f); } # If there are only alphanumeric characters, plus '*' and '.' in it, # convert it to the same regular expression PPM::Search would use, and let # the server do it: if ($query =~ /^[-_A-Za-z0-9\*\.\?]+$/) { my $re = PPM::Search::glob_to_regex($query, 0); return ('TRADITIONAL', "$re"); } # Otherwise, get the whole summary and use PPM::Search return ('ADVANCED', $query); } sub is_traditional { my $field = uc(shift); return scalar grep { $field eq $_ } qw(ABSTRACT AUTHOR TITLE NAME); } #============================================================================= # The batch method, plus supporting setup_ and cleanup_ methods. #============================================================================= sub batch { my $o = shift; # The batch() method was introduced to PPM2 in protocol version 201 return $o->SUPER::batch(@_) unless $o->protocol >= 201; my $targ = shift; # ignored my @tasks = @_; # Because the PPMServer is quite lame, a lot of the PPMServer code is # executed client-side. That means a more complicated batch() method: # those methods which require client-side code are executed entirely # client side. Those which don't are dispatched to the server in one go. # The original task list is stitched back together after the call. my @batch; # this will actually be dispatched to the server. my @results; # the dispatched slots are left undefined, so that # Decide what stuff to dispatch. Each setup method is free to munge # arguments as needed. If $to_dispatch is false, the method will not be # dispatched to the server, and $task is assumed to contain the result. for my $i (0 .. $#tasks) { my $task = $tasks[$i]; my $meth = "setup_$task->[0]"; if ($o->can($meth)) { my $to_dispatch = $o->$meth($task); if ($to_dispatch) { push @batch, $task; $results[$i] = undef; } else { $results[$i] = $task; } } } # Dispatch the batch, then stitch and patch. Atchoo! my $response = eval { $o->{client}->batch(@batch)->result; }; if ($@) { chomp $@; return Error("batch method failed: $@"); } elsif (not defined $response) { return Error("batch method returned undefined results"); } # Stitch the results back together, calling cleanup methods as we go: for my $i (0 .. $#results) { $results[$i] = shift @$response unless defined $results[$i]; my $result = $results[$i]; if ($result->{error}) { $results[$i] = Error($result->{error}); } else { my $method = "cleanup_$tasks[$i][0]"; $results[$i] = $o->can($method) ? $o->$method($result->{result}) : Ok($result->{result}); } } List(@results); } # Just tell it to dispatch to the server sub setup_uptodate2 { 1 } sub cleanup_uptodate2 { my ($o, $result) = @_; my ($uptodate, $ppdtext) = @$result{qw(uptodate ppd)}; defined $uptodate and defined $ppdtext or return Error("uptodate2 method returned undefined results"); my $ppd_ref = PPM::PPD->new($ppdtext, $o); $ppd_ref->{id} = $ppd_ref->name; $ppd_ref->{from} = 'repository'; List($uptodate, $ppd_ref); } 1;