File Coverage

blib/lib/JOAP/Server.pm
Criterion Covered Total %
statement 3 3 100.0
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 4 4 100.0


line stmt bran cond sub pod time code
1             # JOAP::Server -- Base Class for JOAP Object Servers
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 server class
20              
21             package JOAP::Server;
22 3     3   63168 use base qw/Exporter JOAP::Server::Object/;
  3         7  
  3         2404  
23              
24             use 5.008;
25             use strict;
26             use warnings;
27             use Net::Jabber qw/Component/;
28             use JOAP;
29             use JOAP::Server::Object;
30              
31             # necessary Exporter hoohaw
32              
33             our %EXPORT_TAGS = ( 'all' => [ ] );
34             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
35             our @EXPORT = qw();
36             our $VERSION = $JOAP::VERSION;
37              
38             JOAP::Server->mk_classdata('Name');
39             JOAP::Server->mk_classdata('Version');
40             JOAP::Server->mk_classdata('Classes');
41              
42             JOAP::Server->Name('JOAP::Server');
43             JOAP::Server->Version($VERSION);
44             JOAP::Server->Classes({});
45              
46             # These are a couple of default attributes, kinda just for show.
47              
48             sub version_info;
49              
50             JOAP::Server->Attributes(
51             {time => {type => 'dateTime.iso8601',
52             writable => 0,
53             getter => \&time_info,
54             desc => 'Current time at this server.'},
55             version => {type => 'struct',
56             writable => 0,
57             getter => \&version_info,
58             desc => 'Version info for this server. Name of software and version number.'},
59             });
60              
61             sub new {
62              
63             my($proto) = shift;
64             my($package) = ref($proto) || $proto;
65             my($self) = JOAP::Server::Object::new($package, @_);
66              
67             $self->{component} = new Net::Jabber::Component(@_)
68             unless $self->{component};
69              
70             $self->{component}->SetIQCallBacks($JOAP::NS =>
71             {
72             get => sub { $self->handle_joap($_[1]) },
73             set => sub { $self->handle_joap($_[1]) },
74             },
75             'jabber:iq:rpc' =>
76             {
77             set => sub { $self->handle_joap($_[1]) },
78             });
79              
80             $self->{component}->Info($self->name, $self->version);
81              
82             return $self;
83             }
84              
85             # Just pass these through to the component.
86              
87             sub execute { shift->{component}->Execute(@_) }
88             sub connect { shift->{component}->Connect(@_) }
89             sub disconnect { shift->{component}->Disconnect(@_) }
90             sub connected { shift->{component}->Connected(@_) }
91              
92             # translucent accessors
93              
94             sub name {
95              
96             my($self) = shift;
97              
98             if (ref($self)) {
99             $self->{name} = shift if @_;
100             return (defined $self->{name}) ?
101             $self->{name} : $self->Name;
102             } else {
103             return $self->Name(@_);
104             }
105             }
106              
107             sub version {
108              
109             my($self) = shift;
110              
111             if (ref($self)) {
112             $self->{version} = shift if @_;
113             return (defined $self->{version}) ?
114             $self->{version} : $self->Version;
115             } else {
116             return $self->Version(@_);
117             }
118             }
119              
120             sub classes {
121              
122             my($self) = shift;
123              
124             if (ref($self)) {
125             $self->{classes} = shift if @_;
126             return (defined $self->{classes}) ?
127             $self->{classes} : $self->Classes;
128             } else {
129             return $self->Classes(@_);
130             }
131             }
132              
133             sub version_info {
134             my $self = shift;
135             return {name => $self->name, version => $self->version};
136             }
137              
138             sub time_info {
139             my $self = shift;
140             return time;
141             }
142              
143             sub on_joap {
144              
145             my $self = shift;
146             my $iq = shift;
147              
148             my $recipient = $self->_jid_to_object($iq->GetTo('jid'));
149              
150             my $respiq = undef;
151              
152             if (!$recipient) {
153             $respiq = $self->reply($iq);
154             $respiq->SetType('error');
155             $respiq->SetErrorCode(404); # not found
156             $respiq->SetError('Not found');
157             }
158             else {
159             $respiq = $recipient->on_iq($iq);
160             }
161              
162             return $respiq;
163             }
164              
165             sub handle_joap {
166              
167             my $self = shift;
168             my $iq = shift;
169              
170             my $respiq = $self->on_joap($iq);
171              
172             $self->{component}->Send($respiq) if ($respiq);
173              
174             my $le = $self->log_entry($iq, $respiq);
175              
176             # XXX: use Net::Jabber::Log instead of debug
177              
178             $self->{component}->{DEBUG}->Log0($le);
179             }
180              
181             # We have to add classes.
182              
183             sub on_describe {
184              
185             my($self) = shift;
186             my($iq) = shift;
187             my($respiq) = $self->SUPER::on_describe($iq);
188              
189             my $addr = $iq->GetTo;
190              
191             if ($respiq->GetType() ne 'error') { # If that worked out OK...
192             my $qry = $respiq->GetQuery;
193             foreach my $class (keys %{$self->Classes}) {
194             my $jid = new Net::Jabber::JID($addr);
195             $jid->SetUserID($class);
196             $qry->SetClass($jid->GetJID('full'));
197             }
198             $qry->SetTimestamp($self->timestamp);
199             }
200              
201             return $respiq;
202             }
203              
204             sub make_address {
205              
206             my($self) = shift;
207             my(%args) = @_;
208              
209             my($jid) = new Net::Jabber::JID();
210              
211             $jid->SetServer(($args{server}) ? $args{server} : $self->componentname);
212              
213             $jid->SetUserID(($args{classname}) ? $args{classname} :
214             ($args{class}) ? $self->get_class($args{class}) :
215             ($args{instance}) ? $self->get_class(ref($args{instance})) : undef);
216              
217             $jid->SetResource(($args{instid}) ? $args{instid} :
218             ($args{instance}) ? $args{instance}->id() : undef);
219             }
220              
221             sub get_class {
222              
223             my($self) = shift;
224             my($classname) = shift;
225              
226             return $self->classes->{$classname};
227             }
228              
229             # Note: this is kind of dodgy, since more than one classname can come
230             # to a class.
231              
232             sub get_classname {
233              
234             my($self) = shift;
235             my($class) = shift;
236              
237             my %rev = (reverse %{$self->classes});
238              
239             return $rev{$class};
240             }
241              
242             sub _jid_to_object {
243              
244             my($self) = shift;
245             my($jid) = shift;
246              
247             my($classname) = $jid->GetUserID();
248              
249             if (!$classname) {
250             return $self; # Stuff without a classname is for the server
251             } else {
252              
253             my($class) = $self->get_class($classname);
254              
255             return undef unless $class;
256              
257             # XXX: require class here?
258              
259             my($instid) = $jid->GetResource();
260              
261             if ($instid) {
262             return $class->get($instid);
263             } else {
264             return $class;
265             }
266             }
267             }
268              
269             # XXX: make this work with jabberd component logging
270              
271             sub log_entry {
272              
273             my $self = shift;
274             my $iq = shift;
275             my $resp = shift;
276             my $le = {};
277              
278             my $timestamp = JOAP->int_to_datetime(time);
279             my $from = $iq->GetFrom;
280             my $to = $iq->GetTo;
281             my $input = $self->_summarize_input($iq);
282             my $output = $self->_summarize_output($resp);
283             my $error = $self->_error($resp);
284              
285             # FIXME: print to a configurable log file
286              
287             return sprintf("%s : \"%s\" - \"%s\" : %s -> %s (%s)", $timestamp, $from, $to,
288             $input, $output, $error);
289             }
290              
291             sub _error {
292              
293             my $self = shift;
294             my $iq = shift;
295              
296             if ($iq->GetType ne 'error') {
297             return "OK";
298             }
299             else {
300             return $iq->GetErrorCode;
301             }
302             }
303              
304             sub _summarize_input {
305              
306             my $self = shift;
307             my $iq = shift;
308             my $ns = $iq->GetQuery->GetXMLNS;
309              
310             if ($ns eq 'jabber:iq:rpc') {
311             return $self->_summarize_method_in($iq);
312             } elsif ($ns eq $JOAP::NS) {
313             my($verb) = $iq->GetQuery->GetTag;
314             if ($verb eq 'read') {
315             return $self->_summarize_read_in($iq);
316             } elsif ($verb eq 'edit') {
317             return $self->_summarize_edit_in($iq);
318             } elsif ($verb eq 'add') {
319             return $self->_summarize_add_in($iq);
320             } elsif ($verb eq 'search') {
321             return $self->_summarize_search_in($iq);
322             } elsif ($verb eq 'delete') {
323             return $self->_summarize_delete_in($iq);
324             } elsif ($verb eq 'describe') {
325             return $self->_summarize_describe_in($iq);
326             }
327             }
328              
329             return undef;
330             }
331              
332             sub _summarize_output {
333              
334             my $self = shift;
335             my $iq = shift;
336             my $ns = $iq->GetQuery->GetXMLNS;
337              
338             if ($iq->GetType eq 'error') {
339             return $iq->GetError;
340             } else {
341             if ($ns eq 'jabber:iq:rpc') {
342             return $self->_summarize_method_out($iq);
343             } elsif ($ns eq $JOAP::NS) {
344             my($verb) = $iq->GetQuery->GetTag;
345             if ($verb eq 'read') {
346             return $self->_summarize_read_out($iq);
347             } elsif ($verb eq 'edit') {
348             return $self->_summarize_edit_out($iq);
349             } elsif ($verb eq 'add') {
350             return $self->_summarize_add_out($iq);
351             } elsif ($verb eq 'search') {
352             return $self->_summarize_search_out($iq);
353             } elsif ($verb eq 'delete') {
354             return $self->_summarize_delete_out($iq);
355             } elsif ($verb eq 'describe') {
356             return $self->_summarize_describe_out($iq);
357             }
358             }
359             }
360              
361             return undef;
362             }
363              
364             sub _summarize_method_in {
365              
366             my $self = shift;
367             my $iq = shift;
368             my $qry = $iq->GetQuery;
369             my $call = $qry->GetMethodCall || return "(method -- bad format)";
370             my $name = $call->GetMethodName || return "(method -- bad format)";
371              
372             my @actuals = $call->GetParams->GetParams;
373              
374             my @params = map { $self->_summarize_param($_) } @actuals;
375              
376             return "method $name (" . join(", ", @params) . ")";
377             }
378              
379             sub _summarize_method_out {
380              
381             my $self = shift;
382             my $iq = shift;
383             my $qry = $iq->GetQuery;
384             my $resp = $qry->GetMethodResponse || return "(method -- bad format)";
385              
386             if ($resp->DefinedFault) {
387             my $fs = JOAP->decode($resp->GetFault->GetValue);
388             return ("FAULT #" . $fs->{faultCode} . ": " . $fs->{faultString});
389             }
390             else {
391             my @actuals = $resp->GetParams->GetParams;
392             my @params = map { $self->_summarize_param($_) } @actuals;
393             return "(" . join(", ", @params) . ")";
394             }
395             }
396              
397             sub _summarize_read_in {
398              
399             my $self = shift;
400             my $iq = shift;
401             my $qry = $iq->GetQuery;
402             my @names = $qry->GetName;
403              
404             return "read (" . ((@names) ? join(", ", @names) : "*") . ")";
405             }
406              
407             sub _summarize_read_out {
408              
409             my $self = shift;
410             my $iq = shift;
411             my $qry = $iq->GetQuery;
412             my @attrs = $qry->GetAttribute;
413              
414             my @attrsums = map { $self->_summarize_attr($_) } @attrs;
415              
416             return "(" . join(", ", @attrsums) . ")";
417             }
418              
419             sub _summarize_edit_in {
420              
421             my $self = shift;
422             my $iq = shift;
423             my $qry = $iq->GetQuery;
424             my @attrs = $qry->GetAttribute;
425              
426             my @attrsums = map { $self->_summarize_attr($_) } @attrs;
427              
428             return "edit (" . join(", ", @attrsums) . ")";
429             }
430              
431             sub _summarize_edit_out {
432              
433             my $self = shift;
434             my $iq = shift;
435             my $qry = $iq->GetQuery;
436              
437             if ($qry->DefinedNewAddress) {
438             return $qry->GetNewAddress;
439             } else {
440             return "";
441             }
442             }
443              
444             sub _summarize_add_in {
445              
446             my $self = shift;
447             my $iq = shift;
448             my $qry = $iq->GetQuery;
449             my @attrs = $qry->GetAttribute;
450              
451             my @attrsums = map { $self->_summarize_attr($_) } @attrs;
452              
453             return "add (" . join(", ", @attrsums) . ")";
454             }
455              
456             sub _summarize_add_out {
457              
458             my $self = shift;
459             my $iq = shift;
460             my $qry = $iq->GetQuery;
461              
462             return $qry->GetNewAddress;
463             }
464              
465             sub _summarize_delete_in {
466             return "delete";
467             }
468              
469             sub _summarize_delete_out {
470             return "";
471             }
472              
473             sub _summarize_search_in {
474              
475             my $self = shift;
476             my $iq = shift;
477             my $qry = $iq->GetQuery;
478             my @attrs = $qry->GetAttribute;
479              
480             my @attrsums = map { $self->_summarize_attr($_) } @attrs;
481              
482             return "search (" . ((@attrs) ? join(", ", @attrsums) : "*") . ")";
483             }
484              
485             sub _summarize_search_out {
486              
487             my $self = shift;
488             my $iq = shift;
489             my $qry = $iq->GetQuery;
490             my @items = $qry->GetItem;
491             my $size = scalar(@items);
492              
493             if ($size > 4) {
494             return "(" . join(",", @items[0,3]) . "... [$size total])";
495             } else {
496             return "(" . join(",", @items) . ")";
497             }
498             }
499              
500             sub _summarize_describe_in {
501             return "describe";
502             }
503              
504             sub _summarize_describe_out {
505             # FIXME: actually write out description, wimp!
506             return "[description]";
507             }
508              
509             sub _summarize_attr {
510              
511             my $self = shift;
512             my $attr = shift;
513             my $name = $attr->GetName;
514             my $value = $attr->GetValue;
515             my $type = JOAP->value_type($value);
516             my $val = JOAP->decode($value);
517              
518             return $name . " => \"" . $self->_ellipsize($val) . "\" [" . $type . "]";
519             }
520              
521             sub _summarize_param {
522              
523             my $self = shift;
524             my $param = shift;
525             my $value = $param->GetValue;
526             my $type = JOAP->value_type($value);
527             my $val = JOAP->decode($value);
528              
529             return "\"" . $self->_ellipsize($val) . "\" [" . $type . "]";
530             }
531              
532             sub _ellipsize {
533              
534             my $self = shift;
535             my $string = shift;
536              
537             if (length($string) > 32) {
538             return (substr($string, 29) . "...");
539             } else {
540             return $string;
541             }
542             }
543              
544             1;
545              
546             __END__