File Coverage

lib/XML/eXistDB/RPC.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


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