File Coverage

blib/lib/AnyEvent/XMPP/IM/Connection.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             package AnyEvent::XMPP::IM::Connection;
2 19     19   1946 use strict;
  19         41  
  19         706  
3 19     19   103 no warnings;
  19         38  
  19         732  
4 19     19   16202 use AnyEvent::XMPP::Connection;
  0            
  0            
5             use AnyEvent::XMPP::Namespaces qw/xmpp_ns/;
6             use AnyEvent::XMPP::IM::Roster;
7             use AnyEvent::XMPP::IM::Message;
8             use AnyEvent::XMPP::Util qw/cmp_bare_jid/;
9             our @ISA = qw/AnyEvent::XMPP::Connection/;
10              
11             =head1 NAME
12              
13             AnyEvent::XMPP::IM::Connection - "XML" stream that implements the XMPP RFC 3921.
14              
15             =head1 SYNOPSIS
16              
17             use AnyEvent::XMPP::Connection;
18              
19             my $con = AnyEvent::XMPP::Connection->new;
20              
21             =head1 DESCRIPTION
22              
23             This module represents a XMPP instant messaging connection and implements
24             RFC 3921.
25              
26             This module is a subclass of C and inherits all methods.
27             For example C and the stanza sending routines.
28              
29             For additional events that can be registered to look below in the EVENTS section.
30              
31             =head1 METHODS
32              
33             =over 4
34              
35             =item B
36              
37             This is the constructor. It takes the same arguments as
38             the constructor of L along with a
39             few others:
40              
41             =over 4
42              
43             =item dont_retrieve_roster => $bool
44              
45             Set this to a true value if no roster should be requested on connection
46             establishment. You can retrieve the roster later if you want to
47             with the C method.
48              
49             The internal roster will be set even if this option is active, and
50             even presences will be stored in there, except that the C
51             method on the roster object won't return anything as there are
52             no roster items.
53              
54             =item initial_presence => $priority
55              
56             This sets whether the initial presence should be sent. C<$priority>
57             should be the priority of the initial presence. The default value
58             for the initial presence C<$priority> is 10.
59              
60             If you pass a undefined value as C<$priority> no initial presence will
61             be sent!
62              
63             =back
64              
65             =cut
66              
67             sub new {
68             my $this = shift;
69             my $class = ref($this) || $this;
70              
71             my %args = @_;
72              
73             unless (exists $args{initial_presence}) {
74             $args{initial_presence} = 10;
75             }
76              
77             my $self = $class->SUPER::new (%args);
78              
79             $self->{roster} = AnyEvent::XMPP::IM::Roster->new (connection => $self);
80              
81             $self->reg_cb (message_xml =>
82             sub { shift @_; $self->handle_message (@_); });
83             $self->reg_cb (presence_xml =>
84             sub { shift @_; $self->handle_presence (@_); });
85             $self->reg_cb (iq_set_request_xml =>
86             sub { shift @_; $self->handle_iq_set (@_); });
87             $self->reg_cb (disconnect =>
88             sub { shift @_; $self->handle_disconnect (@_); });
89              
90             $self->reg_cb (stream_ready => sub {
91             my ($jid) = @_;
92             if ($self->features ()->find_all ([qw/session session/])) {
93             $self->send_session_iq;
94             } else {
95             $self->init_connection;
96             }
97             });
98              
99             my $proxy_cb = sub {
100             my ($self, $er) = @_;
101             $self->event (error => $er);
102             };
103              
104             $self->reg_cb (
105             session_error => $proxy_cb,
106             roster_error => $proxy_cb,
107             presence_error => $proxy_cb,
108             message_error => $proxy_cb,
109             );
110              
111             $self
112             }
113              
114             sub send_session_iq {
115             my ($self) = @_;
116              
117             $self->send_iq (set => sub {
118             my ($w) = @_;
119             $w->addPrefix (xmpp_ns ('session'), '');
120             $w->emptyTag ([xmpp_ns ('session'), 'session']);
121              
122             }, sub {
123             my ($node, $error) = @_;
124             if ($node) {
125             $self->init_connection;
126             } else {
127             $self->event (session_error => $error);
128             }
129             });
130             }
131              
132             sub init_connection {
133             my ($self) = @_;
134             if ($self->{dont_retrieve_roster}) {
135             $self->initial_presence;
136             $self->{session_active} = 1;
137             $self->event ('session_ready');
138              
139             } else {
140             $self->retrieve_roster (sub {
141             $self->initial_presence; # XXX: is this the right order? after roster fetch?
142             $self->{session_active} = 1;
143             $self->event ('session_ready');
144             });
145             }
146             }
147              
148             sub initial_presence {
149             my ($self) = @_;
150             if (defined $self->{initial_presence}) {
151             $self->send_presence (undef, undef, priority => $self->{initial_presence});
152             }
153             # else do nothing
154             }
155              
156             =item B
157              
158             This method initiates a roster request. If you set C
159             when creating this connection no roster was retrieved.
160             You can do that with this method. The coderef in C<$cb> will be
161             called after the roster was retrieved.
162              
163             The first argument of the callback in C<$cb> will be the roster
164             and the second will be a L object when
165             an error occurred while retrieving the roster.
166              
167             =cut
168              
169             sub retrieve_roster {
170             my ($self, $cb) = @_;
171              
172             $self->send_iq (get => sub {
173             my ($w) = @_;
174             $w->addPrefix (xmpp_ns ('roster'), '');
175             $w->emptyTag ([xmpp_ns ('roster'), 'query']);
176              
177             }, sub {
178             my ($node, $error) = @_;
179             if ($node) {
180             $self->{roster}->set_retrieved;
181             $self->store_roster ($node);
182             } else {
183             $self->event (roster_error => $error);
184             }
185              
186             $cb->($self, $self->{roster}, $error) if $cb
187             });
188             }
189              
190             sub store_roster {
191             my ($self, $node) = @_;
192             my @upd = $self->{roster}->update ($node);
193             $self->event (roster_update => $self->{roster}, \@upd);
194             }
195              
196             =item B
197              
198             Returns the roster object of type L.
199              
200             =cut
201              
202             sub get_roster {
203             my ($self) = @_;
204             $self->{roster}
205             }
206              
207             sub handle_iq_set {
208             my ($self, $node, $handled) = @_;
209              
210             if ($node->find_all ([qw/roster query/])) {
211             $self->store_roster ($node);
212             $self->reply_iq_result ($node);
213             $$handled = 1;
214             }
215             }
216              
217             sub handle_presence {
218             my ($self, $node) = @_;
219             if (defined ($node->attr ('to')) && !cmp_bare_jid ($node->attr ('to'), $self->jid)) {
220             return; # ignore presence that is not for us
221             }
222              
223             if ($node->attr ('type') eq 'error') {
224             my $error = AnyEvent::XMPP::Error::Presence->new (node => $node);
225             $self->event (presence_error => $error);
226             return if $error->type ne 'continue';
227             }
228              
229             my ($contact, $old, $new) = $self->{roster}->update_presence ($node);
230             $self->event (presence_update => $self->{roster}, $contact, $old, $new)
231             }
232              
233             sub handle_message {
234             my ($self, $node) = @_;
235              
236             if ($node->attr ('type') eq 'error') {
237             my $error = AnyEvent::XMPP::Error::Message->new (node => $node);
238             $self->event (message_error => $error);
239             return if $error->type ne 'continue';
240             }
241              
242             my $msg = AnyEvent::XMPP::IM::Message->new (connection => $self);
243             $msg->from_node ($node);
244             $self->event (message => $msg);
245             }
246              
247             sub handle_disconnect {
248             my ($self) = @_;
249             delete $self->{roster};
250             }
251              
252             =back
253              
254             =head1 EVENTS
255              
256             These additional events can be registered on with C:
257              
258             In the following events C<$roster> is the L
259             object you get by calling C.
260              
261             NODE: The first argument to each callback is always the L
262             object itself. Also see L for more information about registering
263             callbacks.
264              
265             =over 4
266              
267             =item session_ready
268              
269             This event is generated when the session has been fully established and
270             can be used to send around messages and other stuff.
271              
272             =item session_error => $error
273              
274             If an error happened during establishment of the session this
275             event will be generated. C<$error> will be an L
276             error object.
277              
278             =item roster_update => $roster, $contacts
279              
280             This event is emitted when a roster update has been received.
281             C<$contacts> is an array reference of L objects
282             which have changed. If a contact was removed it will return 'remove'
283             when you call the C method on it.
284              
285             The first time this event is sent is when the roster was received
286             for the first time.
287              
288             =item roster_error => $error
289              
290             If an error happened during retrieval of the roster this event will
291             be generated.
292             C<$error> will be an L error object.
293              
294             =item presence_update => $roster, $contact, $old_presence, $new_presence
295              
296             This event is emitted when the presence of a contact has changed.
297             C<$contact> is the L object which presence status
298             has changed.
299             C<$old_presence> is a L object which represents the
300             presence prior to the change.
301             C<$new_presence> is a L object which represents the
302             presence after to the change. The new presence might be undef if the new presence
303             is 'unavailable'.
304              
305             =item presence_error => $error
306              
307             This event is emitted when a presence stanza error was received.
308             C<$error> will be an L error object.
309              
310             =item message => $msg
311              
312             This event is emitted when a message was received.
313             C<$msg> is a L object.
314              
315             =item message_error => $error
316              
317             This event is emitted when a message stanza error was received.
318             C<$error> will be an L error object.
319              
320             =item contact_request_subscribe => $roster, $contact, $message
321              
322             This event is generated when the C<$contact> wants to subscribe
323             to your presence.
324              
325             If you want to accept or decline the request, call
326             C method of L or
327             C method of L on C<$contact>.
328              
329             If you want to start a mutual subscription you have to call C
330             B you accepted or declined with C/C.
331             Calling it in the opposite order gets some servers confused!
332              
333             If a C element was transmitted with the subscription
334             it's contents will be in C<$message>. Which is usually a text written
335             from the one who requests subscription.
336              
337             =item contact_subscribed => $roster, $contact, $message
338              
339             This event is generated when C<$contact> subscribed you to his presence successfully.
340              
341             If a C element was transmitted with the subscribed presence
342             it's contents will be in C<$message>.
343              
344             =item contact_did_unsubscribe => $roster, $contact, $message
345              
346             This event is generated when C<$contact> unsubscribes from your presence.
347              
348             If you want to unsubscribe from him call the C method
349             of L on C<$contact>.
350              
351             If a C element was transmitted with the unsubscription
352             it's contents will be in C<$message>. Which is usually a text written
353             from the one who unsubscribes.
354              
355             =item contact_unsubscribed => $roster, $contact, $message
356              
357             This event is generated when C<$contact> unsubscribed you from his presence.
358              
359             If you want to unsubscribe him from your presence call the C
360             method of L on C<$contact>.
361              
362             If a C element was transmitted with the unsubscription
363             it's contents will be in C<$message>.
364              
365             =back
366              
367             =head1 AUTHOR
368              
369             Robin Redeker, C<< >>, JID: C<< >>
370              
371             =head1 COPYRIGHT & LICENSE
372              
373             Copyright 2007, 2008 Robin Redeker, all rights reserved.
374              
375             This program is free software; you can redistribute it and/or modify it
376             under the same terms as Perl itself.
377              
378             =cut
379              
380             1; # End of AnyEvent::XMPP