ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/Net-XMPP2/lib/Net/XMPP2/IM/Roster.pm
Revision: 1.10
Committed: Sun Jun 24 10:41:58 2007 UTC (19 years, 4 months ago) by elmex
Branch: MAIN
Changes since 1.9: +4 -2 lines
Log Message:
finished subscription implementation and also added the possibility
of forwarding all events of an object to some other object
and forwarded all events from a IM::Connection to the Client.

File Contents

# User Rev Content
1 elmex 1.3 package Net::XMPP2::IM::Roster;
2     use Net::XMPP2::IM::Contact;
3     use Net::XMPP2::IM::Presence;
4 elmex 1.6 use Net::XMPP2::Util qw/prep_bare_jid/;
5     use Net::XMPP2::Namespaces qw/xmpp_ns/;
6 elmex 1.1 use strict;
7    
8     =head1 NAME
9    
10     Net::XMPP2::IM::Roster - A instant messaging roster for XMPP
11    
12     =head1 SYNOPSIS
13    
14     my $con = Net::XMPP2::IM::Connection->new (...);
15     ...
16 elmex 1.3 my $ro = $con->roster;
17     if (my $c = $ro->get_contact ('test@example.com')) {
18     $c->make_message ()->add_body ("Hello there!")->send;
19     }
20 elmex 1.1
21     =head1 DESCRIPTION
22    
23     This module represents a class for roster objects which contain
24     contact information.
25    
26 elmex 1.3 It manages the roster of a JID connected by an L<Net::XMPP2::IM::Connection>.
27     It manages also the presence information that is received.
28    
29     You get the roster by calling the C<roster> method on an L<Net::XMPP2::IM::Connection>
30     object. There is no other way.
31    
32 elmex 1.1 =cut
33    
34     sub new {
35     my $this = shift;
36     my $class = ref($this) || $this;
37     bless { @_ }, $class;
38     }
39    
40 elmex 1.6 sub update {
41     my ($self, $node) = @_;
42    
43     my ($query) = $node->find_all ([qw/roster query/]);
44     return unless $query;
45    
46     my @upd;
47    
48     for my $item ($query->find_all ([qw/roster item/])) {
49     my $jid = $item->attr ('jid');
50    
51     my $sub = $item->attr ('subscription'),
52     $self->touch_jid ($jid);
53    
54     if ($sub eq 'remove') {
55     my $c = $self->remove_contact ($jid);
56     $c->update ($item);
57     } else {
58     push @upd, $self->get_contact ($jid)->update ($item);
59     }
60     }
61    
62     @upd
63     }
64    
65     sub update_presence {
66     my ($self, $node) = @_;
67     my $jid = $node->attr ('from');
68     # XXX: should check whether C<$jid> is nice JID.
69    
70     my $type = $node->attr ('type');
71 elmex 1.7 my $contact = $self->touch_jid ($jid);
72 elmex 1.6
73     if ($type eq 'subscribe') {
74 elmex 1.7 my $doit;
75     $self->{connection}->event (contact_request_subscribe => $self, $contact, \$doit);
76     return unless defined $doit;
77    
78     if ($doit) {
79 elmex 1.10 $contact->send_subscribed;
80 elmex 1.7 } else {
81 elmex 1.10 $contact->send_unsubscribed;
82 elmex 1.7 }
83    
84 elmex 1.6 } elsif ($type eq 'subscribed') {
85 elmex 1.7 $self->{connection}->event (contact_subscribed => $self, $contact);
86    
87     } elsif ($type eq 'unsubscribe') {
88     my $doit;
89 elmex 1.10 $self->{connection}->event (contact_did_unsubscribe => $self, $contact, \$doit);
90 elmex 1.7 return unless defined $doit;
91    
92     if ($doit) {
93 elmex 1.10 $contact->send_unsubscribe;
94 elmex 1.6 }
95 elmex 1.7
96 elmex 1.6 } elsif ($type eq 'unsubscribed') {
97 elmex 1.8 $self->{connection}->event (contact_unsubscribed => $self, $contact);
98 elmex 1.7
99 elmex 1.6 } else {
100 elmex 1.7 $contact->update_presence ($node)
101 elmex 1.6 }
102     }
103    
104 elmex 1.1 sub touch_jid {
105     my ($self, $jid) = @_;
106 elmex 1.6 my $bjid = prep_bare_jid ($jid);
107 elmex 1.1
108     unless ($self->{contacts}->{$bjid}) {
109     $self->{contacts}->{$bjid} =
110     Net::XMPP2::IM::Contact->new (
111     connection => $self->{connection},
112     jid => Net::XMPP2::Util::bare_jid ($jid)
113     )
114     }
115    
116     $self->{contacts}->{$bjid}
117     }
118    
119 elmex 1.6 sub remove_contact {
120     my ($self, $jid) = @_;
121     my $bjid = prep_bare_jid ($jid);
122     delete $self->{contacts}->{$bjid};
123 elmex 1.1 }
124    
125 elmex 1.6 =head2 new_contact ($jid, $name, $groups, $cb)
126    
127     This method sends a roster item creation request to
128     the server. C<$jid> is the JID of the contact.
129     C<$name> is the nickname of the contact, which can be
130     undef. C<$groups> should be a array reference containing
131     the groups this contact should be in.
132    
133     The callback in C<$cb> will be called when the creation
134     is finished. The first argument will be an L<Net::XMPP2::Error::IQ>
135     object if the request resulted in an error.
136    
137     =cut
138    
139     sub new_contact {
140     my ($self, $jid, $name, $groups, $cb) = @_;
141    
142     my $c = Net::XMPP2::IM::Contact->new (
143     connection => $self->{connection},
144     jid => prep_bare_jid ($jid)
145     );
146     $c->send_update (
147     $cb,
148     (defined $name ? (name => $name) : ()),
149     groups => ($groups || [])
150     );
151     }
152    
153     =head2 delete_contact ($jid, $cb)
154    
155     This method will send a request to the server to delete this contact
156     from the roster. It will result in cancelling all subscriptions.
157    
158     C<$cb> will be called when the request was finished. The first argument
159     to the callback might be a L<Net::XMPP2::Error::IQ> object if the
160     request resulted in an error.
161    
162     =cut
163    
164     sub delete_contact {
165     my ($self, $jid, $cb) = @_;
166    
167     $jid = prep_bare_jid $jid;
168    
169     $self->{connection}->send_iq (
170     set => sub {
171     my ($w) = @_;
172     $w->addPrefix (xmpp_ns ('roster'), '');
173     $w->startTag ([xmpp_ns ('roster'), 'query']);
174     $w->emptyTag ([xmpp_ns ('roster'), 'item'],
175     jid => $jid,
176     subscription => 'remove'
177     );
178     $w->endTag;
179     },
180     sub {
181     my ($node, $error) = @_;
182     $cb->($error) if $cb
183     }
184     );
185 elmex 1.1 }
186    
187 elmex 1.3 =head2 get_contact ($jid)
188    
189     Returns the contact on the roster with the JID C<$jid>.
190     (If C<$jid> is not bare the resource part will be stripped
191     before searching)
192    
193     The return value is an instance of L<Net::XMPP2::IM::Contact>.
194    
195     =cut
196    
197 elmex 1.1 sub get_contact {
198     my ($self, $jid) = @_;
199 elmex 1.3 my $bjid = Net::XMPP2::Util::prep_bare_jid ($jid);
200     $self->{contacts}->{$bjid}
201     }
202    
203     =head2 get_contacts
204    
205     Returns the contacts that are on this roster as
206     L<Net::XMPP2::IM::Contact> objects.
207    
208     NOTE: This method only returns the contacts that have
209     a roster item. If you haven't retrieved the roster yet
210     the presence information is still stored but you have
211     to get the contacts without a roster item with the
212     C<get_contacts_off_roster> method. See below.
213    
214     =cut
215    
216     sub get_contacts {
217     my ($self) = @_;
218 elmex 1.4 grep { $_->is_on_roster } values %{$self->{contacts}}
219 elmex 1.3 }
220    
221     =head2 get_contacts_off_roster
222    
223     Returns the contacts that are not on the roster
224     but for which we have received presence.
225     Return value is a list of L<Net::XMPP2::IM::Contact> objects.
226    
227     See also documentation of L<Net::XMPP2::IM::Roster::get_contacts>
228     above.
229    
230     =cut
231    
232     sub get_contacts_off_roster {
233     my ($self) = @_;
234 elmex 1.4 grep { not $_->is_on_roster } values %{$self->{contacts}}
235 elmex 1.1 }
236    
237 elmex 1.5 =head2 subscribe ($jid)
238    
239     This method sends a subscription request to C<$jid>.
240     If the optional C<$not_mutual> paramenter is true
241     the subscription will not be mutual.
242    
243     =cut
244    
245     sub subscribe {
246     my ($self) = @_;
247     # FIXME / TODO
248     }
249    
250 elmex 1.3 =head2 debug_dump
251    
252     This prints the roster and all it's contacts
253     and their presences.
254    
255     =cut
256    
257 elmex 1.1 sub debug_dump {
258     my ($self) = @_;
259     print "### ROSTER BEGIN ###\n";
260     my %groups;
261 elmex 1.3 for my $contact ($self->get_contacts) {
262 elmex 1.1 push @{$groups{$_}}, $contact for $contact->groups;
263     push @{$groups{''}}, $contact unless $contact->groups;
264     }
265    
266     for my $grp (sort keys %groups) {
267     print "=== $grp ====\n";
268     $_->debug_dump for @{$groups{$grp}};
269     }
270 elmex 1.3 if ($self->get_contacts_off_roster) {
271     print "### OFF ROSTER ###\n";
272     %groups;
273     for my $contact ($self->get_contacts_off_roster) {
274     push @{$groups{$_}}, $contact for $contact->groups;
275     push @{$groups{''}}, $contact unless $contact->groups;
276     }
277    
278     for my $grp (sort keys %groups) {
279     print "=== $grp ====\n";
280 elmex 1.9 $_->debug_dump for grep { not $_->is_on_roster } @{$groups{$grp}};
281 elmex 1.3 }
282     }
283    
284 elmex 1.1 print "### ROSTER END ###\n";
285     }
286    
287     =head1 AUTHOR
288    
289     Robin Redeker, C<< <elmex at ta-sa.org> >>
290    
291 elmex 1.3 =head1 SEE ALSO
292 elmex 1.1
293 elmex 1.3 L<Net::XMPP2::IM::Connection>, L<Net::XMPP2::IM::Contact>, L<Net::XMPP2::IM::Presence>
294 elmex 1.1
295     =head1 COPYRIGHT & LICENSE
296    
297     Copyright 2007 Robin Redeker, all rights reserved.
298    
299     This program is free software; you can redistribute it and/or modify it
300     under the same terms as Perl itself.
301    
302     =cut
303    
304    
305    
306     1; # End of Net::XMPP2