File Coverage

lib/XML/eXistDB/RPC.pm
Criterion Covered Total %
statement 39 433 9.0
branch 0 200 0.0
condition 0 67 0.0
subroutine 13 99 13.1
pod 80 82 97.5
total 132 881 14.9


line stmt bran cond sub pod time code
1             # Copyrights 2010-2021 by [Mark Overmeer].
2             # For other contributors see ChangeLog.
3             # See the manual pages for details on the licensing terms.
4             # Pod stripped from pm file by OODoc 2.02.
5             # This code is part of distribution XML-ExistsDB. Meta-POD processed with
6             # OODoc into POD and HTML manual-pages. See README.md
7             # Copyright Mark Overmeer. Licensed under the same terms as Perl itself.
8              
9             package XML::eXistDB::RPC;
10 5     5   438927 use vars '$VERSION';
  5         59  
  5         312  
11             $VERSION = '0.99_1';
12              
13 5     5   32 use base 'XML::eXistDB';
  5         10  
  5         2092  
14              
15 5     5   41 use warnings;
  5         11  
  5         179  
16 5     5   29 use strict;
  5         7  
  5         157  
17              
18 5     5   28 use Log::Report 'xml-existdb', syntax => 'LONG';
  5         11  
  5         42  
19              
20 5     5   4616 use XML::Compile::RPC::Util;
  5         4607  
  5         412  
21 5     5   2788 use XML::Compile::RPC::Client ();
  5         253169  
  5         225  
22              
23 5     5   65 use XML::eXistDB::Util;
  5         8  
  5         374  
24 5     5   33 use XML::eXistDB;
  5         11  
  5         156  
25              
26 5     5   26 use Digest::MD5 qw/md5_base64 md5_hex/;
  5         10  
  5         284  
27 5     5   32 use Encode qw/encode/;
  5         12  
  5         290  
28 5     5   34 use MIME::Base64 qw/encode_base64/;
  5         8  
  5         285  
29              
30 5     5   29 use Data::Dumper;
  5         9  
  5         39975  
31             $Data::Dumper::Indent = 1;
32              
33             my $dateTime = 'dateTime.iso8601'; # too high chance on typos
34              
35              
36             sub init($)
37 0     0 0   { my ($self, $args) = @_;
38              
39 0           my $rpc = $args->{rpc};
40 0 0         unless($rpc)
41             { my $dest = $args->{destination}
42 0 0         or report ERROR =>
43             __x"{pkg} object required option `rpc' or `destination'"
44             , pkg => ref $self;
45 0           $rpc = XML::Compile::RPC::Client->new(destination => $dest);
46             }
47 0   0       $args->{schemas} ||= $rpc->schemas;
48              
49 0           $self->SUPER::init($args);
50              
51 0           $self->{rpc} = $rpc;
52             $self->{repository}
53 0 0         = exists $args->{repository} ? $args->{repository} : '/db';
54 0   0       $self->{compr_up} = $args->{compress_upload} // 128;
55 0   0       $self->{chunks} = $args->{chunk_size} // 32;
56              
57 0   0       $self->login($args->{user} // 'guest', $args->{password} // 'guest');
      0        
58 0 0         $self->{pp_up} = $args->{prettyprint_upload} ? 1 : 0;
59              
60 0   0       my $f = $args->{format} || [];
61 0 0         $self->{format} = [ ref $f eq 'HASH' ? %$f : @$f ];
62 0           $self;
63             }
64              
65             #-----------------
66              
67 0     0 0   sub rpcClient() {shift->{rpc}}
68              
69             #-----------------
70              
71             # private method; "options" is an overloaded term, abused by eXist.
72             sub _format(@)
73 0     0     { my $self = shift;
74 0           my %args = (@{$self->{format}}, @_);
  0            
75              
76 0 0         if(my $sp = delete $args{'stylesheet-params'})
77 0           { while(my($k,$v) = each %$sp)
78 0           { $args{"stylesheet-param.$k"} = $v;
79             }
80             }
81 0           struct_from_hash string => \%args;
82             }
83              
84             sub _date_options($$)
85 0     0     { my ($created, $modified) = @_;
86              
87 0 0 0       !($created || $modified) ? ()
    0 0        
88             : ($created && $modified) ? ($dateTime => $created, $dateTime => $modified)
89             : report ERROR => "either both or neither creation and modification date";
90             }
91              
92             # in Perl, any value is either true or false, in rpc only 0 and 1
93 0 0   0     sub _bool($) { $_[0] ? 0 : 1 }
94              
95              
96             sub _document($)
97 0     0     { my $self = shift;
98              
99             return $_[0]->toString($self->{pp_up})
100 0 0         if UNIVERSAL::isa($_[0], 'XML::LibXML::Document');
101              
102 0 0         return encode 'utf-8', ${$_[0]}
  0            
103             if ref $_[0] eq 'SCALAR';
104              
105 0 0         return encode 'utf-8', $_[0]
106             if $_[0] =~ m/^\s*\
107              
108 0 0 0       if($_[0] !~ m/[\r\n]/ && -f $_[0])
109 0           { local *DOC;
110 0 0         open DOC, '<:raw', $_[0]
111             or report FAULT => "cannot read document from file $_[0]";
112 0           local $/ = undef;
113 0           my $xml = ;
114 0 0         close DOC
115             or report FAULT => "read error for document from file $_[0]";
116 0           return $xml;
117             }
118              
119 0           report ERROR => "do not understand document via $_[0]";
120             }
121              
122             #-----------------
123              
124             #T
125 0     0 1   sub hasCollection($) { $_[0]->rpcClient->hasCollection(string => $_[1]) }
126              
127              
128 0     0 1   sub hasDocument($) { $_[0]->rpcClient->hasDocument(string => $_[1]) }
129              
130              
131             #T
132 0     0 1   sub isXACMLEnabled() { shift->rpcClient->isXACMLEnabled }
133              
134              
135             sub backup($$$$)
136 0     0 1   { $_[0]->rpcClient->backup(string => $_[1], string => $_[2]
137             , string => $_[3], string => $_[4]);
138             }
139              
140              
141             sub shutdown(;$)
142 0     0 1   { my $self = shift;
143 0 0         $self->rpcClient->shutdown(@_ ? (int => shift) : ());
144             }
145              
146              
147 0     0 1   sub sync() { shift->rpcClient->sync }
148              
149             #-----------------
150              
151             #T
152             sub createCollection($;$)
153 0     0 1   { my ($self, $coll, $date) = @_;
154 0 0         my @date = $date ? ($dateTime => $date) : ();
155 0           $self->rpcClient->createCollection(string => $coll, @date);
156             }
157              
158              
159             #T
160             sub configureCollection($$%)
161 0     0 1   { my ($self, $coll, $conf, %args) = @_;
162             my $format = (exists $args{beautify} ? $args{beautify} : $self->{pp_up})
163 0 0         ? 1 : 0;
    0          
164 0           my $config;
165              
166 0 0 0       if(UNIVERSAL::isa($conf, 'XML::LibXML::Document'))
    0          
167             { # ready document, hopefully correct
168 0           $config = $conf->toString($format);
169             }
170             elsif(!ref $conf && $conf =~ m/^\s*\
171             { # preformatted xml
172 0           $config = $conf;
173             }
174             else
175 0           { $config = $self->createCollectionConfig($conf, %args);
176             }
177              
178 0           $self->rpcClient->configureCollection(string => $coll, string => $config);
179             }
180              
181              
182             sub copyCollection($$;$)
183 0     0 1   { my ($self, $from, $sec) = (shift, shift, shift);
184 0           my @param = (string => $from, string => $sec);
185 0 0         push @param, string => shift if @_;
186 0           $self->rpcClient->copyCollection(@param);
187             }
188              
189              
190             # the two params version is missing from the interface description, so
191             # we use a little work-around
192             sub moveCollection($$;$)
193 0     0 1   { my ($self, $from, $tocoll, $subcoll) = @_;
194 0 0         defined $subcoll
195             or ($tocoll, $subcoll) = $tocoll =~ m! ^ (.*) / ([^/]+) $ !x;
196              
197 0           $self->rpcClient->moveCollection(string => $from, string => $tocoll
198             , string => $subcoll);
199             }
200              
201              
202             #T
203             sub describeCollection(;$%)
204 0     0 1   { my $self = shift;
205 0 0         my $coll = @_ % 2 ? shift : $self->{repository};
206 0           my %args = @_;
207             my ($rc, $data, $trace) = $args{documents}
208 0 0         ? $self->rpcClient->getCollectionDesc(string => $coll)
209             : $self->rpcClient->describeCollection(string => $coll);
210 0 0         $rc==0 or return ($rc, $data, $trace);
211              
212 0           my $h = struct_to_hash $data;
213 0           $h->{collections} = [ rpcarray_values $h->{collections} ];
214 0 0         if(my $docs = $h->{documents})
215 0           { my %docs;
216 0           foreach (rpcarray_values $docs)
217 0           { my $h = struct_to_hash $_;
218 0           $docs{$h->{name}} = $h;
219             }
220 0           $h->{documents} =\%docs;
221             }
222 0           (0, $h, $trace);
223             }
224              
225              
226             #T
227             sub subCollections(;$)
228 0     0 1   { my ($self, $coll) = @_;
229 0   0       $coll ||= $self->{repository};
230 0           my ($rc, $data, $trace) = $_[0]->describeCollection($coll, documents => 0);
231 0 0         $rc==0 or return ($rc, $data, $trace);
232              
233 0 0         my @coll = map "$data->{name}/$_", @{$data->{collections} || []};
  0            
234 0           (0, \@coll, $trace);
235             }
236              
237              
238             #T
239             sub collectionCreationDate(;$)
240 0     0 1   { my ($self, $coll) = @_;
241 0   0       $coll ||= $self->{repository};
242 0           $self->rpcClient->getCreationDate(string => $coll);
243             }
244              
245              
246             #T
247             sub listResources(;$)
248 0     0 1   { my ($self, $coll) = @_;
249 0   0       $coll ||= $self->{repository};
250 0 0         my ($rc, $details, $trace)
251             = $self->rpcClient->getDocumentListing($coll ? (string => $coll) : ());
252 0 0         $rc==0 or return ($rc, $details, $trace);
253              
254 0           ($rc, [rpcarray_values $details], $trace);
255             }
256              
257              
258             #T
259             sub reindexCollection($)
260 0     0 1   { my ($self, $coll) = @_;
261 0           $self->rpcClient->reindexCollection(string => $coll);
262             }
263              
264              
265             #T
266             sub removeCollection($)
267 0     0 1   { my ($self, $coll) = @_;
268 0           $self->rpcClient->removeCollection(string => $coll);
269             }
270              
271             #-----------------
272              
273             #T
274             sub login($;$)
275 0     0 1   { my ($self, $user, $password) = @_;
276 0           $self->{user} = $user;
277 0 0         $self->{password} = defined $password ? $password : '';
278 0           $self->rpcClient->headers->header(Authorization => 'Basic '
279             . encode_base64("$user:$password", ''));
280 0           (0);
281             }
282              
283              
284             #T
285             sub listGroups()
286 0     0 1   { my ($rc, $details, $trace) = shift->rpcClient->getGroups;
287 0 0         $rc==0 or return ($rc, $details, $trace);
288 0           (0, [rpcarray_values $details], $trace);
289             }
290              
291              
292             #T
293             sub describeResourcePermissions($)
294 0     0 1   { my ($rc, $details, $trace) = $_[0]->rpcClient->getPermissions(string => $_[1]);
295 0 0         $rc==0 or return ($rc, $details, $trace);
296 0           ($rc, struct_to_hash $details, $trace);
297             }
298              
299              
300             #T
301             sub listDocumentPermissions($)
302 0     0 1   { my ($self, $coll) = @_;
303 0   0       $coll ||= $self->{repository};
304 0           my ($rc, $details, $trace)
305             = $self->rpcClient->listDocumentPermissions(string => $coll);
306              
307 0 0         $rc==0 or return ($rc, $details, $trace);
308 0           my $h = struct_to_hash $details;
309 0           my %h;
310 0           while( my ($k,$v) = each %$h)
311 0           { $h{$k} = [ rpcarray_values $v ];
312             }
313 0           (0, \%h, $trace);
314             }
315              
316              
317             #T
318             sub describeAccount($)
319 0     0 1   { my ($self, $user) = @_;
320              
321 0 0         my $call = $self->serverVersion lt "3.0" ? 'getUser' : 'getAccount';
322 0           my ($rc, $details, $trace) = $self->rpcClient->$call(string => $user);
323 0 0         $rc==0 or return ($rc, $details, $trace);
324              
325 0           my $h = struct_to_hash $details;
326 0           $h->{groups} = [ rpcarray_values $h->{groups} ];
327 0           $h->{metadata} = struct_to_hash $h->{metadata};
328 0           (0, $h, $trace);
329             }
330              
331              
332             *describeUser = \&describeAccount;
333              
334              
335             #T
336             sub listAccounts()
337 0     0 1   { my $self = shift;
338 0 0         my $call = $self->serverVersion lt "3.0" ? 'getUsers' : 'getAccounts';
339              
340 0           my ($rc, $details, $trace) = $self->rpcClient->$call;
341 0 0         $rc==0 or return ($rc, $details, $trace);
342 0           my %h;
343 0           foreach my $user (rpcarray_values $details)
344 0           { my $u = struct_to_hash $user;
345 0           $u->{groups} = [ rpcarray_values $u->{groups} ];
346 0           $u->{metadata} = struct_to_hash $u->{metadata};
347 0           $h{$u->{name}} = $u;
348             }
349 0           (0, \%h, $trace);
350             }
351              
352              
353             *listUsers = \&listAccounts;
354              
355              
356              
357             #T
358             sub removeAccount($)
359 0     0 1   { my ($self, $username) = @_;
360 0 0         my $call = $self->serverVersion lt "3.0" ? 'removeUser' : 'removeAccount';
361 0           $_[0]->rpcClient->$call(string => $username);
362             }
363             *removeUser = \&removeAccount;
364              
365              
366             sub setPermissions($$;$$)
367 0     0 1   { my ($self, $target, $perms, $user, $group) = @_;
368              
369 0 0 0       my @chown = ($user && $group) ? (string => $user, string => $group) : ();
370 0 0         $self->rpcClient->setPermissions(string => $target, @chown
371             , ($perms =~ m/\D/ ? 'string' : 'int') => $perms);
372             }
373              
374              
375             #T
376             sub addAccount($$$;$)
377 0     0 1   { my ($self, $user, $password, $groups, $home) = @_;
378 0 0         my @groups = ref $groups eq 'ARRAY' ? @$groups : $groups;
379              
380 0 0         my $call = $self->serverVersion lt '3.0' ? 'setUser' : 'addAccount';
381              
382 0 0         $self->rpcClient->$call(string => $user
383             , string => md5_base64($password)
384             , string => md5_hex("$user:exist:$password")
385             , rpcarray_from(string => @groups)
386             , ($home ? (string => $home) : ())
387             );
388             }
389             *setUser = \&addAccount;
390              
391              
392              
393             #T
394             sub describeCollectionPermissions(;$)
395 0     0 1   { my ($self, $coll) = @_;
396 0   0       $coll ||= $self->{repository};
397 0           my ($rc, $data, $trace)
398             = $self->rpcClient->listCollectionPermissions(string => $coll);
399 0 0         $rc==0 or return ($rc, $data, $trace);
400              
401 0           my $h = struct_to_hash $data;
402 0           my %p;
403 0           foreach my $relname (keys %$h)
404 0           { my %perms;
405 0           @perms{ qw/user group mode/ } = rpcarray_values $h->{$relname};
406 0           $p{"$coll/$relname"} = \%perms;
407             }
408 0           ($rc, \%p, $trace);
409             }
410              
411             #-----------------
412              
413             ### need two-arg version?
414             sub copyResource($$$)
415 0     0 1   { my $self = shift;
416 0           $self->rpcClient->copyResource(string=> $_[0], string=> $_[1], string=> $_[2]);
417             }
418              
419              
420             #T
421             sub uniqueResourceName(;$)
422 0     0 1   { my ($self, $coll) = @_;
423 0   0       $coll ||= $self->{repository};
424 0           $self->rpcClient->createResourceId(string => $coll);
425             }
426              
427              
428             sub describeResource($)
429 0     0 1   { my ($self, $resource) = @_;
430              
431 0           my ($rc, $details, $trace)
432             = $self->rpcClient->describeResource(string => $resource);
433 0 0         $rc==0 or return ($rc, $details, $trace);
434              
435 0           ($rc, struct_to_hash $details, $trace);
436             }
437              
438              
439             #T
440             sub countResources(;$)
441 0     0 1   { my ($self, $coll) = @_;
442 0   0       $coll ||= $self->{repository};
443 0           $self->rpcClient->getResourceCount(string => $coll);
444             }
445              
446              
447             ### two-params version needed?
448             sub moveResource($$$)
449 0     0 1   { my $self = shift;
450 0           $self->rpcClient->moveResource(string=> $_[0], string=> $_[1], string=> $_[2]);
451             }
452              
453              
454             #T
455             sub getDocType($)
456 0     0 1   { my ($rc, $details, $trace) = $_[0]->rpcClient->getDocType(string => $_[1]);
457 0 0         $rc==0 or return ($rc, $details, $trace);
458              
459 0           my @d = rpcarray_values $details;
460 0           ($rc, +{docname => $d[0], public_id => $d[1], system_id => $d[2]}, $trace);
461             }
462              
463              
464             #T
465             sub setDocType($$$$)
466 0     0 1   { my ($self, $doc, $name, $pub, $sys) = @_;
467 0           $self->rpcClient->setDocType(string => $doc
468             , string => $name, string => $pub, string => $sys);
469             }
470              
471              
472 0     0 1   sub whoLockedResource($) {$_[0]->rpcClient->hasUserLock(string => $_[1]) }
473              
474              
475 0     0 1   sub unlockResource($) {$_[0]->rpcClient->unlockResource(string => $_[1]) }
476              
477              
478             sub lockResource($;$)
479 0     0 1   { my ($self, $resource, $user) = @_;
480             $user ||= $self->{user}
481 0 0 0       or report ERROR => "no default username set nor specified for lock";
482 0           $self->rpcClient->lockResource(string => $resource, string => $user);
483             }
484              
485              
486 0     0 1   sub removeResource($) { $_[0]->rpcClient->remove(string => $_[1]) }
487              
488             #--------------------
489              
490             #T
491             sub downloadDocument($@)
492 0     0 1   { my $self = shift;
493 0           my ($rc, $chunk, $trace) = $self->getDocumentData(@_);
494 0           my @trace = $trace;
495              
496 0 0         $rc==0 or return ($rc, $chunk, \@trace);
497              
498 0           my @data = \$chunk->{data};
499 0   0       while($rc==0 && $chunk->{offset})
500             { ($rc, $chunk, $trace) = $chunk->{'supports-long-offset'}
501             ? $self->getNextExtendedChunk($chunk->{handle}, $chunk->{offset})
502 0 0         : $self->getNextChunk($chunk->{handle}, $chunk->{offset});
503              
504 0 0         $rc or push @data, \$chunk->{data};
505 0           push @trace, $trace;
506             }
507 0 0         $rc==0 or return ($rc, $chunk, \@trace);
508              
509 0           (0, (join '', map $$_, @data), \@trace);
510             }
511              
512             # does this also work for binary resources?
513              
514              
515             sub listResourceTimestamps($)
516 0     0 1   { my ($self, $resource) = @_;
517 0           my ($rc, $stamps, $trace)
518             = $self->rpcClient->getTimestamps(string => $resource);
519              
520 0 0         $rc==0 or return ($rc, $stamps, $trace);
521              
522 0           my @s = rpcarray_values $stamps;
523 0           (0, +{created => $s[0], modified => $s[1]}, $trace);
524             }
525              
526             #-----------------
527              
528             #T
529             sub uploadDocument($$@)
530 0     0 1   { my ($self, $resource, undef, %args) = @_;
531 0           my $doc = $self->_document($_[2]);
532              
533 0 0         my $chunks = exists $args{chunk_size} ? $args{chunk_size} : $self->{chunks};
534 0 0         my $compr = exists $args{compress} ? $args{compress} : $args{compr_upload};
535 0 0         for ($chunks, $compr) { $_ *= 1024 if defined $_ }
  0            
536              
537 0           my @dates = _date_options $args{creation_date}, $args{modify_date};
538 0   0       my $replace = $args{replace} || 0;
539 0   0       my $mime = $args{mime_type} || 'text/xml';
540              
541             # Send file in chunks
542 0           my $to_sent = length $doc;
543 0           my $sent = 0;
544              
545 0           my ($rc, $tmp, @trace);
546 0           while($sent < $to_sent)
547 0           { ($rc, $tmp, my $t) = $self->upload($tmp, substr($doc, $sent, $chunks));
548 0           push @trace, $t;
549 0 0         $rc==0 or return ($rc, $tmp, \@trace);
550              
551 0           $sent += $chunks;
552             }
553              
554 0           ($rc, my $d, my $t)
555             = $self->parseLocal($tmp, $resource, $replace, $mime, @dates);
556 0           push @trace, $t;
557 0           ($rc, $d, \@trace);
558             }
559              
560              
561 0     0 1   sub downloadBinary($) { $_[0]->rpcClient->getBinaryResource(string => $_[1]) }
562              
563              
564             sub uploadBinary($$$$;$$)
565 0     0 1   { my ($self, $resource, $bytes, $mime, $replace, $created, $modified) = @_;
566            
567 0 0         $self->rpcClient->storeBinary
568             ( base64 => (ref $bytes ? $$bytes : $bytes)
569             , string => $resource, string => $mime, boolean => _bool $replace
570             , _date_options($created, $modified)
571             );
572             }
573              
574             #-----------------
575              
576             #T
577             ### compile doesn't return anything
578             sub compile($@)
579 0     0 1   { my ($self, $query) = (shift, shift);
580 0           my @format = $self->_format(@_);
581              
582 0           my ($rc, $d, $trace) = $self->rpcClient->compile(base64 => $query, @format);
583 0 0         ($rc, ($rc==0 ? struct_to_hash($d) : $d), $trace);
584             }
585              
586              
587             #T
588             # printDiagnostics should accept a base64
589             sub describeCompile($@)
590 0     0 1   { my ($self, $query) = (shift, shift);
591 0           my @format = $self->_format(@_);
592 0           $self->rpcClient->printDiagnostics(string => $query, @format);
593             }
594              
595              
596             sub execute($@)
597 0     0 1   { my ($self, $handle) = (shift, shift);
598 0           my @format = $self->_format(@_);
599 0           my ($rc, $d, $trace) = $self->rpcClient->execute(string => $handle, @format);
600 0 0         ($rc, ($rc==0 ? struct_to_hash $d : $d), $trace);
601             }
602              
603             #-----------------
604              
605             sub executeQuery($@)
606 0     0 1   { my ($self, $query) = @_;
607 0           my @args = (base64 => $query);
608 0 0         push @args, string => shift if @_ %2;
609 0           push @args, $self->_format(@_);
610 0           $self->rpcClient->executeQuery(@args);
611             }
612              
613              
614 0     0 1   sub numberOfResults($) { $_[0]->rpcClient->getHits(int => $_[1]) }
615              
616              
617             #T
618             # what does "docid" mean?
619             sub describeResultSet($)
620 0     0 1   { my ($self, $set) = @_;
621              
622 0           my ($rc, $details,$trace) = $self->rpcClient->querySummary(int => $set);
623 0 0         $rc==0 or return ($rc, $details, $trace);
624 0           my $results = struct_to_hash $details;
625              
626 0 0         if(my $docs = delete $results->{documents})
627 0           { my @docs;
628 0           foreach my $result (rpcarray_values $docs)
629 0           { my ($name, $id, $hits) = rpcarray_values $result;
630 0           push @docs, +{ name => $name, docid => $id, hits => $hits };
631             }
632 0           $results->{documents} = \@docs;
633             }
634 0 0         if(my $types = delete $results->{doctypes})
635 0           { my @types;
636 0           foreach my $result (rpcarray_values $types)
637 0           { my ($class, $hits) = rpcarray_values $result;
638 0           push @types, +{ class => $class, hits => $hits };
639             }
640 0           $results->{doctypes} = \@types;
641             }
642 0           ($rc, $results, $trace);
643             }
644              
645              
646             #### what kind of params from %args?
647             #### releaseQueryResult(int $resultid, int $hash) INT?
648             sub releaseResultSet($@)
649 0     0 1   { my ($self, $results, %args) = @_;
650 0           $self->rpcClient->releaseQueryResult(int => $results, int => 0);
651             }
652              
653              
654             sub retrieveResult($$@)
655 0     0 1   { my ($self, $set, $pos) = (shift, shift, shift);
656 0           my @format = $self->_format(@_);
657              
658 0           my ($rc, $bytes, $trace)
659             = $self->rpcClient->retrieve(int => $set, int => $pos, @format);
660 0 0         $rc==0 or return ($rc, $bytes, $trace);
661              
662 0           (0, $self->decodeXML($bytes), $trace);
663             }
664              
665              
666             # hitCount where describeResultSet() uses 'hits'
667             #T
668             sub retrieveResults($@)
669 0     0 1   { my ($self, $set) = (shift, shift);
670 0           my @format = $self->_format(@_);
671              
672 0           my ($rc, $bytes, $trace) = $self->rpcClient->retrieveAll(int => $set, @format);
673 0 0         $rc==0 or return ($rc, $bytes, $trace);
674              
675 0           (0, $self->decodeXML($bytes), $trace);
676             }
677              
678             #-----------------
679              
680             #T
681             # Vector query() is given as alternative but does not exist.
682             sub query($$$@)
683 0     0 1   { my ($self, $query, $limit) = (shift, shift, shift);
684 0 0         my $first = @_ % 2 ? shift : 1;
685 0           my @format = $self->_format(@_);
686              
687 0           my ($rc, $bytes, $trace) = $self->rpcClient
688             ->query(string => $query, int => $limit, int => $first, @format);
689 0 0         $rc==0 or return ($rc, $bytes, $trace);
690              
691 0           (0, $self->decodeXML($bytes), $trace);
692             }
693              
694              
695             sub queryXPath($;$$@)
696 0     0 1   { my ($self, $xpath, $doc, $node) = splice @_, 0, 4;
697 0           my @args = (base64 => $xpath);
698 0 0 0       push @args, string => $doc, string => $node // ''
699             if defined $doc;
700 0           push @args, $self->_format(@_);
701              
702 0           my ($rc, $data, $trace) = $self->rpcClient->queryP(@args);
703 0 0         $rc==0 or return ($rc, $data, $trace);
704              
705 0           my $h = struct_to_hash $data;
706 0           my @r;
707 0           foreach my $v (rpcarray_values $h->{results})
708 0 0         { if(ref $v eq 'HASH')
709             { #XXX is this correct?
710 0           my ($doc, $loc) = rpcarray_values $v;
711 0           push @r, +{document => $doc, node_id => $loc};
712             }
713 0           push @r, $v;
714             }
715 0           $h->{results} = \@r;
716              
717 0           (0, $h, $trace);
718             }
719            
720             #-----------------
721              
722             sub retrieveDocumentNode($$@)
723 0     0 1   { my $self = shift;
724 0           my ($rc, $chunk, $trace) = $self->rpcClient->retrieveFirstChunk(@_);
725              
726 0           my @data = \$chunk->{data};
727 0   0       while($rc==0 && $chunk->{offset})
728             { ($rc, $chunk) = $chunk->{'supports-long-offset'}
729             ? $self->getNextExtendedChunk($chunk->{handle}, $chunk->{offset})
730 0 0         : $self->getNextChunk($chunk->{handle}, $chunk->{offset});
731 0 0         $rc or push @data, \$chunk->{data};
732             }
733 0 0         $rc==0 or return ($rc, $chunk, $trace);
734              
735 0           (0, $self->decodeXML(join '', map $$_, @data), $trace);
736             }
737              
738             #-----------------
739              
740             ### What does the returned int mean?
741             sub updateResource($$;$)
742 0     0 1   { my ($self, $resource, $xupdate, $encoding) = @_;
743 0 0         $self->rpcClient->xupdateResource(string => $resource, string => $xupdate
744             , ($encoding ? (string => $encoding) : ()));
745             }
746              
747             ### What does the returned int mean?
748             ### Does this update the collection configuration?
749              
750             sub updateCollection($$)
751 0     0 1   { $_[0]->rpcClient->xupdate(string => $_[1], string => $_[2]);
752             }
753              
754             #-----------------
755              
756             sub scanIndexTerms($$$;$)
757 0     0 1   { my $self = shift;
758 0           my ($rc, $details, $trace);
759 0 0         if(@_==4)
760 0           { my ($coll, $begin, $end, $recurse) = @_;
761 0           ($rc, $details, $trace) = $self->rpcClient->scanIndexTerms(string => $coll
762             , string => $begin, string => $end, boolean => _bool $recurse);
763             }
764             else
765 0           { my ($xpath, $begin, $end) = @_;
766 0           ($rc, $details, $trace) = $self->rpcClient->scanIndexTerms(string => $xpath
767             , string => $begin, string => $end);
768             }
769              
770 0 0         $rc==0 or return ($rc, $details, $trace);
771              
772             # XXX this has not been tested. Probably we need to unpack each @occ
773             # via struct_to_hash
774 0           my @occ = rpcarray_values $details;
775 0           ($rc, \@occ, $trace);
776             }
777              
778              
779             sub indexedElements($$)
780 0     0 1   { my ($self, $coll, $recurse) = @_;
781 0           my ($rc, $details, $trace)
782             = $self->rpcClient->getIndexedElements(string => $coll
783             , boolean => _bool $recurse);
784 0 0         $rc==0 or return ($rc, $details, $trace);
785              
786             ### cleanup Vector $details. Per element:
787             # 1. name of the element
788             # 2. optional namespace URI
789             # 3. optional namespace prefix
790             # 4. number of occurrences of this element as an integer value
791              
792 0           (0, [rpcarray_values $details], $trace);
793             }
794              
795              
796             #-----------------
797              
798 0     0 1   sub trace() { shift->rpcClient->trace }
799              
800             #----------------
801              
802             #T
803             sub getCollectionDesc(;$)
804 0     0 1   { my ($self, $coll) = @_;
805 0   0       $coll ||= $self->{repository};
806 0           $self->describeCollection($coll, documents => 1);
807             }
808              
809             #---------
810              
811             sub getDocument($$;$$)
812 0     0 1   { my ($self, $resource) = (shift, shift);
813 0           my @args;
814 0 0         if(@_==3)
815 0           { my ($enc, $prettyprint, $style) = @_;
816 0 0         push @args, string => $enc, int => ($prettyprint ? 1 : 0);
817 0 0         push @args, string => $style if defined $style;
818             }
819             else
820 0           { @args = @_;
821             }
822 0           $self->rpcClient->getDocument(string => $resource, @args);
823             }
824              
825              
826             sub getDocumentAsString($$;$$)
827 0     0 1   { my ($self, $resource) = (shift, shift);
828 0           my @args;
829 0 0         if(@_==3)
830 0           { my ($enc, $prettyprint, $style) = @_;
831 0 0         push @args, string => $enc, int => ($prettyprint ? 1 : 0);
832 0 0         push @args, string => $style if defined $style;
833             }
834             else
835 0           { @args = @_;
836             }
837 0           $self->rpcClient->getDocumentAsString(string => $resource, @args);
838             }
839              
840              
841             sub getDocumentData($@)
842 0     0 1   { my ($self, $resource) = (shift, shift);
843 0           my @format = $self->_format(@_);
844              
845 0           my ($rc, $d, $trace)
846             = $self->rpcClient->getDocumentData(string => $resource, @format);
847              
848 0 0         ($rc, ($rc==0 ? struct_to_hash $d : $d), $trace);
849             }
850              
851              
852             sub getNextChunk($$)
853 0     0 1   { my ($self, $handle, $offset) = @_;
854 0           my ($rc, $d, $trace)
855             = $self->rpcClient->getNextChunk(string => $handle, int => $offset);
856 0 0         ($rc, ($rc==0 ? struct_to_hash $d : $d), $trace);
857             }
858              
859              
860             sub getNextExtendedChunk($$)
861 0     0 1   { my ($self, $handle, $offset) = @_;
862 0           my ($rc, $d, $trace)
863             = $self->rpcClient->getNextChunk(string => $handle, string => $offset);
864 0 0         ($rc, ($rc==0 ? struct_to_hash $d : $d), $trace);
865             }
866              
867             #---------
868              
869             sub parse($$;$$$)
870 0     0 1   { my ($self, $data, $resource, $replace, $created, $modified) = @_;
871            
872 0 0         $self->rpcClient->parse
873             ( base64 => $self->_document($data)
874             , string => $resource, int => ($replace ? 1 : 0)
875             , _date_options($created, $modified)
876             );
877             }
878              
879              
880             sub parseLocal($$$$;$$)
881 0     0 1   { my ($self, $fn, $resource, $replace, $mime, $created, $modified) = @_;
882            
883 0           $self->rpcClient->parseLocal
884             ( string => $fn, string => $resource, boolean => _bool $replace
885             , string => $mime, _date_options($created, $modified)
886             );
887             }
888              
889              
890             sub parseLocalExt($$$$;$$)
891 0     0 1   { my ($self, $fn, $res, $replace, $mime, $is_xml, $created, $modified) = @_;
892            
893 0           $self->rpcClient->parseLocal
894             ( string => $fn, string => $res, boolean => _bool $replace
895             , string => $mime, boolean => _bool $is_xml
896             , _date_options($created, $modified)
897             );
898             };
899              
900              
901             sub upload($;$)
902 0     0 1   { my $self = shift;
903 0 0         my $tmp = @_ == 2 ? shift : undef;
904 0 0         $self->rpcClient->upload(string => (defined $tmp ? $tmp : '')
905             , base64 => $_[0], int => length($_[0]));
906             }
907              
908              
909             sub uploadCompressed($;$)
910 0     0 1   { my $self = shift;
911 0 0         my $tmp = @_ == 3 ? shift : undef;
912              
913             ### Not sure whether each chunk is compressed separately or the
914             ### data is compressed as a whole.
915 0 0         $self->rpcClient->uploadCompressed
916             ( (defined $tmp ? (string => $tmp) : ())
917             , base64 => $_[0], int => length($_[1]));
918             }
919              
920              
921 0     0 1   sub storeBinary($$$$;$$) { $_[0]->uploadBinary( @_[2, 1, 3, 4, 5, 6] ) }
922              
923             #-------
924              
925             sub retrieveFirstChunk($$@)
926 0     0 1   { my $self = shift;
927 0           my @args;
928 0 0         if($_[0] =~ m/\D/)
929 0           { my ($docname, $id) = (shift, shift);
930 0           @args = (string => $docname, string => $id);
931             }
932             else
933 0           { my ($resultset, $pos) = (shift, shift);
934 0           @args = (int => $resultset, int => $pos);
935             }
936 0           my @format = $self->_format(@_);
937 0           my ($rc, $d, $trace) = $self->rpcClient->retrieveFirstChunk(@args, @format);
938 0 0         ($rc, ($rc==0 ? $d : struct_to_hash $d), $trace);
939             }
940              
941             #------------------
942              
943             sub retrieve($$@)
944 0     0 1   { my $self = shift;
945 0 0         my @args = $_[0] =~ m/\D/
946             ? (string => shift, string => shift)
947             : (int => shift, int => shift);
948 0           push @args, $self->_format(@_);
949              
950 0           my ($rc, $bytes, $trace) = $self->rpcClient->retrieve(@args);
951 0 0         ($rc, ($rc==0 ? $self->decodeXML($bytes) : $bytes), $trace);
952             }
953              
954              
955             sub retrieveAll($$@)
956 0     0 1   { my ($self, $set) = (shift, shift);
957 0           my @format = $self->_format(@_);
958              
959 0           my ($rc, $bytes, $trace)
960             = $self->rpcClient->retrieveAll(int => $set, @format);
961 0 0         ($rc, ($rc==0 ? $self->decodeXML($bytes) : $bytes), $trace);
962             }
963              
964              
965             sub retrieveAllFirstChunk($$@)
966 0     0 1   { my ($self, $result) = (shift, shift);
967 0           my @format = $self->_format(@_);
968              
969 0           my ($rc, $d, $trace)
970             = $self->rpcClient->retrieveAllFirstChunk(int => $result, @format);
971              
972 0 0         ($rc, ($rc==0 ? struct_to_hash($d) : $d), $trace);
973             }
974              
975              
976             sub isValidDocument($)
977 0     0 1   { my ($self, $doc) = (shift, shift);
978 0           $self->rpcClient->isValid(string => $doc);
979             }
980              
981              
982             sub initiateBackup($)
983 0     0 1   { my ($self, $s) = (shift, shift);
984 0           $self->rpcClient->dataBackup($s);
985             }
986              
987              
988             sub getDocumentChunked($@)
989 0     0 1   { my ($self, $doc) = (shift, shift);
990 0           my ($rc, $data, $trace) = $self->rpcClient->getDocumentChunk(string=> $doc);
991 0 0         $rc==0 or return ($rc, $data, $trace);
992              
993 0           my ($h, $l) = rpcarray_values $data;
994 0           (0, $h, $l, $trace);
995             }
996              
997              
998             sub getDocumentNextChunk($$$)
999 0     0 1   { my ($self, $handle, $start, $len) = @_;
1000 0           $self->rpcClient->getDocumentChunck(string => $handle
1001             , int => $start, int => $len);
1002             }
1003              
1004              
1005             sub retrieveAsString($$@)
1006 0     0 1   { my ($self, $doc, $node) = (shift, shift, shift);
1007 0           $self->rpcClient->retrieveAsString(string => $doc, string => $node
1008             , $self->_format(@_));
1009             }
1010              
1011             #----------------
1012              
1013             *createResourceId = \&uniqueResourceName;
1014             *dataBackup = \&initiateBackup;
1015             *getBinaryResource = \&downloadBinary;
1016             *getCreationDate = \&collectionCreationDate;
1017             *getDocumentListing = \&listResources;
1018             *getIndexedElements = \&indexedElements;
1019             *getGroups = \&listGroups;
1020             *getHits = \&numberOfResults;
1021             *getPermissions = \&describeResourcePermissions;
1022             *getResourceCount = \&countResources;
1023             *getTimestamps = \&listResourceTimestamps;
1024             *getUser = \&describeAccount;
1025             *getAccount = \&describeAccount;
1026             *getUsers = \&listUsers;
1027             *hasUserLock = \&whoLockedResource;
1028             *isValid = \&isValidDocument;
1029             *listCollectionPermissions = \&describeCollectionPermissions;
1030             *printDiagnostics = \&describeCompile;
1031             *querySummary = \&describeResultSet;
1032             *queryP = \&queryXPath;
1033             *releaseQueryResult = \&releaseResultSet;
1034             *remove = \&removeResource;
1035             *xupdate = \&xupdateCollection;
1036             *xupdateResource = \&xupdateResource;
1037              
1038             1;