File Coverage

blib/lib/JOAP.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             # JOAP - Perl Extension for the Jabber Object Access Protocol
2             #
3             # Copyright (c) 2003, Evan Prodromou
4             #
5             # This library is free software; you can redistribute it and/or
6             # modify it under the terms of the GNU Lesser General Public
7             # License as published by the Free Software Foundation; either
8             # version 2.1 of the License, or (at your option) any later version.
9             #
10             # This library is distributed in the hope that it will be useful,
11             # but WITHOUT ANY WARRANTY; without even the implied warranty of
12             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13             # Lesser General Public License for more details.
14             #
15             # You should have received a copy of the GNU Lesser General Public
16             # License along with this library; if not, write to the Free Software
17             # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18              
19             # tag: JOAP main module
20              
21             package JOAP;
22 3     3   109660 use base qw/Exporter/;
  3         8  
  3         499  
23              
24 3     3   104 use 5.008;
  3         15  
  3         128  
25 3     3   16 use strict;
  3         134  
  3         150  
26 3     3   20 use warnings;
  3         4  
  3         157  
27              
28             BEGIN {
29 3     3   1496 use Net::Jabber::Protocol;
  0            
  0            
30              
31             our %EXPORT_TAGS = ( 'all' => [ qw// ] );
32             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
33             our @EXPORT = qw//;
34              
35             our $VERSION = '0.01';
36             our $NS = 'http://www.jabber.org/jeps/jep-0075.html#0.3';
37              
38             my %spaces =
39             ($JOAP::NS =>
40             [{name=>'Read',
41             type=>'master'},
42             {name=>'Edit',
43             type=>'master'},
44             {name=>'Add',
45             type=>'master'},
46             {name=>'Delete',
47             type=>'master'},
48             {name=>'Search',
49             type=>'master'},
50             {name=>'Describe',
51             type=>'master'},
52             {name=>'Name', # read req
53             type=>'array',
54             path=>'name/text()'},
55             {name=>'Attribute', # read resp, edit/add/search req
56             type=>'children',
57             path=>'attribute',
58             child=>['Query', '__netjabber__:' . $JOAP::NS . ':attribute'],
59             calls=>['Get', 'Defined', 'Add']},
60             {name=>'Timestamp',
61             path=>'timestamp/text()'}, # read/describe resp
62             {name=>'NewAddress', # add/edit resp
63             path=>'newAddress/text()'},
64             {name=>'Item', # search resp
65             type=>'array',
66             path=>'item/text()'},
67             {name=>'Desc', # describe resp
68             type=>'array',
69             path=>'desc/text()'},
70             {name=>'Class', # describe resp
71             type=>'array',
72             path=>'class/text()'},
73             {name=>'Superclass', # describe resp
74             type=>'array',
75             path=>'superclass/text()'},
76             {name=>'AttributeDescription', # describe resp
77             type=>'children',
78             path=>'attributeDescription',
79             child=>['Query', '__netjabber__:' . $JOAP::NS . ':attributeDescription'],
80             calls=>['Get', 'Defined', 'Add']},
81             {name=>'MethodDescription', # describe resp
82             type=>'children',
83             path=>'methodDescription',
84             child=>['Query', '__netjabber__:' . $JOAP::NS . ':methodDescription'],
85             calls=>['Get', 'Defined', 'Add']}],
86              
87             '__netjabber__:' . $JOAP::NS . ':attribute' =>
88             [{name=>'Attribute',
89             type=>'master'},
90             {name=>'Name',
91             path=>'name/text()'},
92             {name=>'Value',
93             path=>'value',
94             type=>'children',
95             child=>['Query', '__netjabber__:iq:rpc:value'],
96             calls=>['Get', 'Add', 'Defined']},
97             {name=>'RPCValue',
98             path=>'value',
99             type=>'children',
100             child=>['Query', '__netjabber__:iq:rpc:value'],
101             calls=>['Get', 'Add', 'Defined']}],
102              
103             '__netjabber__:' . $JOAP::NS . ':attributeDescription' =>
104             [{name=>'AttributeDescription',
105             type=>'master'},
106             {name=>'Name',
107             path=>'name/text()'},
108             {name=>'Type',
109             path=>'type/text()'},
110             {name=>'Desc',
111             type=>'array',
112             path=>'desc/text()'},
113             {name=>'Required',
114             path=>'@required'},
115             {name=>'Writable',
116             path=>'@writable'},
117             {name=>'Allocation',
118             path=>'@allocation'}],
119              
120             '__netjabber__:' . $JOAP::NS . ':methodDescription' =>
121             [{name=>'MethodDescription',
122             type=>'master'},
123             {name=>'Name',
124             path=>'name/text()'},
125             {name=>'ReturnType',
126             path=>'returnType/text()'},
127             {name=>'Desc',
128             path=>'desc/text()'},
129             {name=>'Params',
130             path=>'params',
131             type=>'children',
132             child=>['Query', '__netjabber__:' . $JOAP::NS . ':params'],
133             calls=>['Get','Defined','Add']},
134             {name=>'Allocation',
135             path=>'@allocation'}],
136              
137             '__netjabber__:' . $JOAP::NS . ':params' =>
138             [{name=>'Params',
139             path=>'param',
140             type=>'children',
141             child=>['Query', '__netjabber__:' . $JOAP::NS . ':param'],
142             calls=>['Get', 'Defined']},
143             {name=>'Param',
144             path=>'param',
145             type=>'node',
146             child=>['Query', '__netjabber__:' . $JOAP::NS . ':param'],
147             calls=>['Add']}],
148              
149             '__netjabber__:' . $JOAP::NS . ':param' =>
150             [{name=>'Param',
151             type=>'master'},
152             {name=>'Name',
153             path=>'name/text()'},
154             {name=>'Type',
155             path=>'type/text()'},
156             {name=>'Desc',
157             path=>'desc/text()'}]
158             );
159              
160             my ($ns, $funcs);
161              
162             while (($ns, $funcs) = each %spaces) {
163              
164             # XXX: This is stupid. Trying to fake out the OO syntax, we're
165             # gonna get bit.
166              
167             Net::Jabber::Protocol->DefineNamespace(undef,
168             xmlns=>$ns,
169             type=>'Query',
170             functions=>$funcs);
171             }
172              
173             %spaces = ();
174             }
175              
176             # A regular expression for XML Schema dateTime stuff
177              
178             my $dt = '^(-?\d{4,})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(\.\d+)?(Z|[+-]\d{2}:\d{2})?$';
179              
180             # utilities to encode datur
181              
182             sub encode {
183              
184             my $self = shift;
185             my $type = shift;
186             my $value = shift;
187              
188             if (!$type) {
189             $type = $self->guess_type($value);
190             }
191              
192             my $jval = new Net::Jabber::Query('value');
193             $jval->SetXMLNS('__netjabber__:iq:rpc:value');
194              
195             my $realvalue = JOAP->coerce($type, $value);
196              
197             if ($type eq 'int' || $type eq 'i4') {
198             $jval->SetI4($realvalue);
199             } elsif ($type eq 'boolean') {
200             $jval->SetBoolean($realvalue);
201             } elsif ($type eq 'dateTime.iso8601') {
202             $jval->SetDateTime($realvalue); # XXX: deal with numbers, arrays, Date::* objects, etc
203             } elsif ($type eq 'double') {
204             $jval->SetDouble($realvalue);
205             } elsif ($type eq 'string') {
206             $jval->SetString($realvalue);
207             } elsif ($type eq 'array') {
208             my $arr = $jval->AddArray();
209             my $do = $arr->AddData();
210              
211             foreach my $data (@$value) {
212             $do->AddValue($self->guess_type($data) => $data);
213             }
214             } elsif ($type eq 'struct') {
215             my $str = $jval->AddStruct();
216             my $name;
217             my $val;
218              
219             while (($name, $val) = each %$value) {
220             $str->AddMember(name => $name)->AddValue($self->guess_type($val) => $val);
221             }
222             }
223             else {
224             throw Error::Simple("Unknown type: $type\n");
225             }
226              
227             return $jval;
228             }
229              
230             sub guess_type {
231              
232             my($self) = shift;
233             my($value) = shift;
234             my($type);
235              
236             if ($value =~ /^[+-]?\d+$/) {
237             $type = 'i4';
238             } elsif ($value =~ /^(-?(?:\d+(?:\.\d*)?|\.\d+)|([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?)$/) {
239             $type = 'double';
240             } elsif ($value =~ /$dt/) {
241             $type = 'dateTime.iso8601';
242             } else {
243             $type = 'string';
244             }
245              
246             return $type;
247             }
248              
249             sub decode {
250              
251             my($self) = shift;
252             my($jval) = shift;
253              
254             if ($jval->DefinedI4()) {
255             return $jval->GetI4() + 0;
256             } elsif ($jval->DefinedDouble()) {
257             return $jval->GetDouble() + 0.0;
258             } elsif ($jval->DefinedDateTime()) {
259             return $jval->GetDateTime();
260             } elsif ($jval->DefinedBoolean()) {
261             return ($jval->GetBoolean()) ? 1 : 0;
262             } elsif ($jval->DefinedString()) {
263             return $jval->GetString();
264             } elsif ($jval->DefinedStruct()) {
265             my $results = {};
266             foreach my $member ($jval->GetStruct->GetMembers()) {
267             $results->{$member->GetName} = $self->decode($member->GetValue);
268             }
269             return $results;
270             } elsif ($jval->DefinedArray()) {
271             my $results = [];
272             foreach my $value ($jval->GetArray()->GetDatas()->GetValue()) {
273             push @$results, $self->decode($value);
274             }
275             return $results;
276             }
277             }
278              
279             sub copy_value {
280              
281             my $self = shift;
282             my $from = shift;
283             my $to = shift;
284              
285             if ($from->DefinedI4()) {
286             $to->SetI4($from->GetI4() + 0);
287             } elsif ($from->DefinedDouble()) {
288             $to->SetDouble($from->GetDouble() + 0.0);
289             } elsif ($from->DefinedDateTime()) {
290             $to->SetDateTime($from->GetDateTime());
291             } elsif ($from->DefinedBoolean()) {
292             $to->SetBoolean(($from->GetBoolean()) ? 1 : 0);
293             } elsif ($from->DefinedString()) {
294             $to->SetString($from->GetString());
295             } elsif ($from->DefinedStruct()) {
296             my $str = $to->AddStruct;
297             foreach my $member ($from->GetStruct->GetMembers()) {
298             my $v = $str->AddMember(name => $member->GetName)->AddValue;
299             $self->copy_value($member->GetValue, $v);
300             }
301             } elsif ($from->DefinedArray()) {
302             my $arr = $to->AddArray;
303             my $d = $arr->AddData;
304             foreach my $value ($from->GetArray()->GetDatas()->GetValue()) {
305             my $v = $d->AddValue;
306             $self->copy_value($value, $v);
307             }
308             }
309             }
310              
311             sub value_type {
312              
313             my $self = shift;
314             my $value = shift;
315              
316             if ($value->DefinedI4) {
317             return 'i4';
318             }
319             elsif ($value->DefinedBoolean) {
320             return 'boolean';
321             }
322             elsif ($value->DefinedString) {
323             return 'string';
324             }
325             elsif ($value->DefinedDouble) {
326             return 'double';
327             }
328             elsif ($value->DefinedDateTime) {
329             return 'dateTime.iso8601';
330             }
331             elsif ($value->DefinedStruct) {
332             return 'struct';
333             }
334             elsif ($value->DefinedBase64) {
335             return 'base64';
336             }
337             elsif ($value->DefinedArray) {
338             return 'array';
339             }
340             }
341              
342             # coerce a perl value into the right shape for the given type
343              
344             sub coerce {
345              
346             my($self) = shift;
347             my($type) = shift;
348             my $value = shift;
349              
350             if ($type eq 'string') {
351             return (defined $value) ? $value . '' : '';
352             } elsif ($type eq 'int' || $type eq 'i4') {
353             no warnings; # turn off non-numeric warnings
354             return (defined $value) ? ($value + 0) : 0;
355             } elsif ($type eq 'double') {
356             no warnings; # turn off non-numeric warnings
357             return (defined $value) ? ($value + 0.0) : 0.0;
358             } elsif ($type eq 'boolean') {
359             return ($value) ? 1 : 0;
360             } elsif ($type eq 'array') {
361             return (defined $value) ? $value : [];
362             } elsif ($type eq 'struct') {
363             return (defined $value) ? $value : {};
364             } elsif ($type eq 'base64') {
365             return $value; # FIXME: base64 the thing
366             } elsif ($type eq 'dateTime.iso8601') {
367             my $default = '0001-00-00T00:00:00Z';
368             if (!defined($value)) {
369             return $default;
370             } elsif (ref($value) eq "ARRAY") {
371             if (scalar(@$value) >= 6) { # looks more or less like a gmtime thingy
372             return $self->array_to_datetime($value);
373             } else {
374             return $default;
375             }
376             } elsif (ref($value)) { # we don't know what to do with other refs
377             return $default;
378             } elsif ($value =~ /$dt/) {
379             return $value;
380             } elsif ($value =~ /^\d+/) { # looks like an int
381             return $self->int_to_datetime($value);
382             } else {
383             return $default;
384             }
385             } else {
386             throw Error::Simple("Unknown type: $type");
387             }
388             }
389              
390             sub int_to_datetime {
391             my $self = shift;
392             my $val = shift;
393             my $int = $self->coerce('i4', $val);
394              
395             return $self->array_to_datetime([gmtime($int)]);
396             }
397              
398             sub array_to_datetime {
399              
400             my $self = shift;
401             my $arr = shift;
402              
403             return sprintf("%04d-%02d-%02dT%02d:%02d:%02dZ", $arr->[5] + 1900,
404             $arr->[4] + 1, $arr->[3], $arr->[2], $arr->[1],
405             $arr->[0]);
406             }
407              
408             # converts a datetime string to a gmtime-style array
409              
410             sub datetime_to_array {
411              
412             my $self = shift;
413             my $in = shift;
414              
415             my @parts = $in =~ /$dt/;
416              
417             if (!@parts) {
418             return ();
419             } else {
420             return ($parts[5], $parts[4], $parts[3],
421             $parts[2], $parts[1] - 1, $parts[0] - 1900);
422             }
423             }
424              
425             1;
426              
427             __END__