File Coverage

lib/XML/eXistDB/RPC.pm
Criterion Covered Total %
statement 16 18 88.8
branch n/a
condition n/a
subroutine 6 6 100.0
pod n/a
total 22 24 91.6


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