File Coverage

blib/lib/AnyEvent/XMPP/Ext/Disco.pm
Criterion Covered Total %
statement 4 6 66.6
branch n/a
condition n/a
subroutine 2 2 100.0
pod n/a
total 6 8 75.0


line stmt bran cond sub pod time code
1             package AnyEvent::XMPP::Ext::Disco;
2 1     1   1561 use AnyEvent::XMPP::Namespaces qw/xmpp_ns/;
  1         3  
  1         66  
3 1     1   71 use AnyEvent::XMPP::Util qw/simxml/;
  0            
  0            
4             use AnyEvent::XMPP::Ext::Disco::Items;
5             use AnyEvent::XMPP::Ext::Disco::Info;
6             use AnyEvent::XMPP::Ext;
7             use strict;
8              
9             our @ISA = qw/AnyEvent::XMPP::Ext/;
10              
11             =head1 NAME
12              
13             AnyEvent::XMPP::Ext::Disco - Service discovery manager class for XEP-0030
14              
15             =head1 SYNOPSIS
16              
17             use AnyEvent::XMPP::Ext::Disco;
18              
19             my $con = AnyEvent::XMPP::IM::Connection->new (...);
20             $con->add_extension (my $disco = AnyEvent::XMPP::Ext::Disco->new);
21             $disco->request_items ($con, 'romeo@montague.net', undef,
22             sub {
23             my ($disco, $items, $error) = @_;
24             if ($error) { print "ERROR:" . $error->string . "\n" }
25             else {
26             ... do something with the $items ...
27             }
28             }
29             );
30              
31             =head1 DESCRIPTION
32              
33             This module represents a service discovery manager class.
34             You make instances of this class and get a handle to send
35             discovery requests like described in XEP-0030.
36              
37             It also allows you to setup a disco-info/items tree
38             that others can walk and also lets you publish disco information.
39              
40             This class is derived from L and can be added as extension to
41             objects that implement the L interface or derive from
42             it.
43              
44             =head1 METHODS
45              
46             =over 4
47              
48             =item B
49              
50             Creates a new disco handle.
51              
52             =cut
53              
54             sub new {
55             my $this = shift;
56             my $class = ref($this) || $this;
57             my $self = bless { @_ }, $class;
58             $self->init;
59             $self
60             }
61              
62             sub init {
63             my ($self) = @_;
64              
65             $self->set_identity (client => console => 'AnyEvent::XMPP');
66             $self->enable_feature (xmpp_ns ('disco_info'));
67             $self->enable_feature (xmpp_ns ('disco_items'));
68              
69             # and features supported by AnyEvent::XMPP in general:
70             $self->enable_feature (AnyEvent::XMPP::Ext::disco_feature_standard ());
71              
72             $self->{cb_id} = $self->reg_cb (
73             iq_get_request_xml => sub {
74             my ($self, $con, $node, $handled) = @_;
75              
76             if ($self->handle_disco_query ($con, $node)) {
77             $$handled = 1;
78             }
79             }
80             );
81             }
82              
83             =item B
84              
85             This sets the identity of the top info node.
86              
87             C<$name> is optional and can be undef. Please note that C<$name> will
88             overwrite all previous set names! If C<$name> is undefined then
89             no previous set name is overwritten.
90              
91             For a list of valid identites look at:
92              
93             http://www.xmpp.org/registrar/disco-categories.html
94              
95             Valid identity C<$type>s for C<$category = "client"> may be:
96              
97             bot
98             console
99             handheld
100             pc
101             phone
102             web
103              
104             =cut
105              
106             sub set_identity {
107             my ($self, $category, $type, $name) = @_;
108             $self->{iden_name} = $name;
109             $self->{iden}->{$category}->{$type} = 1;
110             }
111              
112             =item B
113              
114             This function removes the identity C<$category> and C<$type>.
115              
116             =cut
117              
118             sub unset_identity {
119             my ($self, $category, $type) = @_;
120             delete $self->{iden}->{$category}->{$type};
121             }
122              
123             =item B
124              
125             This method enables the feature C<$uri>, where C<$uri>
126             should be one of the values from the B column on:
127              
128             http://www.xmpp.org/registrar/disco-features.html
129              
130             These features are enabled by default:
131              
132             http://jabber.org/protocol/disco#info
133             http://jabber.org/protocol/disco#items
134              
135             You can pass also a list of features you want to enable to C!
136              
137             =cut
138              
139             sub enable_feature {
140             my ($self, @feature) = @_;
141             $self->{feat}->{$_} = 1 for @feature;
142             }
143              
144             =item B
145              
146             This method enables the feature C<$uri>, where C<$uri>
147             should be one of the values from the B column on:
148              
149             http://www.xmpp.org/registrar/disco-features.html
150              
151             You can pass also a list of features you want to disable to C!
152              
153             =cut
154              
155             sub disable_feature {
156             my ($self, @feature) = @_;
157             delete $self->{feat}->{$_} for @feature;
158             }
159              
160             sub write_feature {
161             my ($self, $w, $var) = @_;
162              
163             $w->emptyTag ([xmpp_ns ('disco_info'), 'feature'], var => $var);
164             }
165              
166             sub write_identity {
167             my ($self, $w, $cat, $type, $name) = @_;
168              
169             $w->emptyTag ([xmpp_ns ('disco_info'), 'identity'],
170             category => $cat,
171             type => $type,
172             (defined $name ? (name => $name) : ())
173             );
174             }
175              
176             sub handle_disco_query {
177             my ($self, $con, $node) = @_;
178              
179             my $q;
180             if (($q) = $node->find_all ([qw/disco_info query/])) {
181             $con->reply_iq_result (
182             $node, sub {
183             my ($w) = @_;
184              
185             if ($q->attr ('node')) {
186             simxml ($w, defns => 'disco_info', node => {
187             ns => 'disco_info', name => 'query',
188             attrs => [ node => $q->attr ('node') ]
189             });
190              
191             } else {
192             $w->addPrefix (xmpp_ns ('disco_info'), '');
193             $w->startTag ([xmpp_ns ('disco_info'), 'query']);
194             for my $cat (keys %{$self->{iden}}) {
195             for my $type (keys %{$self->{iden}->{$cat}}) {
196             $self->write_identity ($w,
197             $cat, $type, $self->{iden_name}
198             );
199             }
200             }
201             for (sort grep { $self->{feat}->{$_} } keys %{$self->{feat}}) {
202             $self->write_feature ($w, $_);
203             }
204             $w->endTag;
205             }
206             }
207             );
208              
209             return 1
210              
211             } elsif (($q) = $node->find_all ([qw/disco_items query/])) {
212             $con->reply_iq_result (
213             $node, sub {
214             my ($w) = @_;
215              
216             if ($q->attr ('node')) {
217             simxml ($w, defns => 'disco_items', node => {
218             ns => 'disco_items',
219             name => 'query',
220             attrs => [ node => $q->attr ('node') ]
221             });
222              
223             } else {
224             simxml ($w, defns => 'disco_items', node => {
225             ns => 'disco_items',
226             name => 'query'
227             });
228             }
229             }
230             );
231              
232             return 1
233             }
234              
235             0
236             }
237              
238             sub DESTROY {
239             my ($self) = @_;
240             $self->unreg_cb ($self->{cb_id})
241             }
242              
243              
244             =item B
245              
246             This method does send a items request to the JID entity C<$from>.
247             C<$node> is the optional node to send the request to, which can be
248             undef.
249              
250             C<$con> must be an instance of L or a subclass of it.
251             The callback C<$cb> will be called when the request returns with 3 arguments:
252             the disco handle, an L object (or undef)
253             and an L object when an error occured and no items
254             were received.
255              
256             The timeout of the request is the IQ timeout of the connection C<$con>.
257              
258             $disco->request_items ($con, 'a@b.com', undef, sub {
259             my ($disco, $items, $error) = @_;
260             die $error->string if $error;
261              
262             # do something with the items here ;_)
263             });
264              
265             =cut
266              
267             sub request_items {
268             my ($self, $con, $dest, $node, $cb) = @_;
269              
270             $con->send_iq (
271             get => sub {
272             my ($w) = @_;
273             $w->addPrefix (xmpp_ns ('disco_items'), '');
274             $w->emptyTag ([xmpp_ns ('disco_items'), 'query'],
275             (defined $node ? (node => $node) : ())
276             );
277             },
278             sub {
279             my ($xmlnode, $error) = @_;
280             my $items;
281              
282             if ($xmlnode) {
283             my (@query) = $xmlnode->find_all ([qw/disco_items query/]);
284             $items = AnyEvent::XMPP::Ext::Disco::Items->new (
285             jid => $dest,
286             node => $node,
287             xmlnode => $query[0]
288             )
289             }
290              
291             $cb->($self, $items, $error)
292             },
293             to => $dest
294             );
295             }
296              
297             =item B
298              
299             This method does send a info request to the JID entity C<$from>.
300             C<$node> is the optional node to send the request to, which can be
301             undef.
302              
303             C<$con> must be an instance of L or a subclass of it.
304             The callback C<$cb> will be called when the request returns with 3 arguments:
305             the disco handle, an L object (or undef)
306             and an L object when an error occured and no items
307             were received.
308              
309             The timeout of the request is the IQ timeout of the connection C<$con>.
310              
311             $disco->request_info ($con, 'a@b.com', undef, sub {
312             my ($disco, $info, $error) = @_;
313             die $error->string if $error;
314              
315             # do something with info here ;_)
316             });
317              
318             =cut
319              
320             sub request_info {
321             my ($self, $con, $dest, $node, $cb) = @_;
322              
323             $con->send_iq (
324             get => sub {
325             my ($w) = @_;
326             $w->addPrefix (xmpp_ns ('disco_info'), '');
327             $w->emptyTag ([xmpp_ns ('disco_info'), 'query'],
328             (defined $node ? (node => $node) : ())
329             );
330             },
331             sub {
332             my ($xmlnode, $error) = @_;
333             my $info;
334              
335             if ($xmlnode) {
336             my (@query) = $xmlnode->find_all ([qw/disco_info query/]);
337             $info = AnyEvent::XMPP::Ext::Disco::Info->new (
338             jid => $dest,
339             node => $node,
340             xmlnode => $query[0]
341             )
342             }
343              
344             $cb->($self, $info, $error)
345             },
346             to => $dest
347             );
348             }
349              
350             =back
351              
352             =head1 AUTHOR
353              
354             Robin Redeker, C<< >>, JID: C<< >>
355              
356             =head1 COPYRIGHT & LICENSE
357              
358             Copyright 2007, 2008 Robin Redeker, all rights reserved.
359              
360             This program is free software; you can redistribute it and/or modify it
361             under the same terms as Perl itself.
362              
363             =cut
364              
365             1;