ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/Net-XMPP2/lib/Net/XMPP2/IM/Roster.pm
Revision: 1.11
Committed: Mon Jun 25 14:45:08 2007 UTC (19 years, 4 months ago) by elmex
Branch: MAIN
Changes since 1.10: +4 -3 lines
Log Message:
fixed some bugs with subscription

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 elmex 1.11 return $contact unless defined $doit;
77 elmex 1.7
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.11 return $contact unless defined $doit;
91 elmex 1.7
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.11 return $contact->update_presence ($node)
101 elmex 1.6 }
102 elmex 1.11 return ($contact)
103 elmex 1.6 }
104    
105 elmex 1.1 sub touch_jid {
106     my ($self, $jid) = @_;
107 elmex 1.6 my $bjid = prep_bare_jid ($jid);
108 elmex 1.1
109     unless ($self->{contacts}->{$bjid}) {
110     $self->{contacts}->{$bjid} =
111     Net::XMPP2::IM::Contact->new (
112     connection => $self->{connection},
113     jid => Net::XMPP2::Util::bare_jid ($jid)
114     )
115     }
116    
117     $self->{contacts}->{$bjid}
118     }
119    
120 elmex 1.6 sub remove_contact {
121     my ($self, $jid) = @_;
122     my $bjid = prep_bare_jid ($jid);
123     delete $self->{contacts}->{$bjid};
124 elmex 1.1 }
125    
126 elmex 1.6 =head2 new_contact ($jid, $name, $groups, $cb)
127    
128     This method sends a roster item creation request to
129     the server. C<$jid> is the JID of the contact.
130     C<$name> is the nickname of the contact, which can be
131     undef. C<$groups> should be a array reference containing
132     the groups this contact should be in.
133    
134     The callback in C<$cb> will be called when the creation
135     is finished. The first argument will be an L<Net::XMPP2::Error::IQ>
136     object if the request resulted in an error.
137    
138     =cut
139    
140     sub new_contact {
141     my ($self, $jid, $name, $groups, $cb) = @_;
142    
143     my $c = Net::XMPP2::IM::Contact->new (
144     connection => $self->{connection},
145     jid => prep_bare_jid ($jid)
146     );
147     $c->send_update (
148     $cb,
149     (defined $name ? (name => $name) : ()),
150     groups => ($groups || [])
151     );
152     }
153    
154     =head2 delete_contact ($jid, $cb)
155    
156     This method will send a request to the server to delete this contact
157     from the roster. It will result in cancelling all subscriptions.
158    
159     C<$cb> will be called when the request was finished. The first argument
160     to the callback might be a L<Net::XMPP2::Error::IQ> object if the
161     request resulted in an error.
162    
163     =cut
164    
165     sub delete_contact {
166     my ($self, $jid, $cb) = @_;
167    
168     $jid = prep_bare_jid $jid;
169    
170     $self->{connection}->send_iq (
171     set => sub {
172     my ($w) = @_;
173     $w->addPrefix (xmpp_ns ('roster'), '');
174     $w->startTag ([xmpp_ns ('roster'), 'query']);
175     $w->emptyTag ([xmpp_ns ('roster'), 'item'],
176     jid => $jid,
177     subscription => 'remove'
178     );
179     $w->endTag;
180     },
181     sub {
182     my ($node, $error) = @_;
183     $cb->($error) if $cb
184     }
185     );
186 elmex 1.1 }
187    
188 elmex 1.3 =head2 get_contact ($jid)
189    
190     Returns the contact on the roster with the JID C<$jid>.
191     (If C<$jid> is not bare the resource part will be stripped
192     before searching)
193    
194     The return value is an instance of L<Net::XMPP2::IM::Contact>.
195    
196     =cut
197    
198 elmex 1.1 sub get_contact {
199     my ($self, $jid) = @_;
200 elmex 1.3 my $bjid = Net::XMPP2::Util::prep_bare_jid ($jid);
201     $self->{contacts}->{$bjid}
202     }
203    
204     =head2 get_contacts
205    
206     Returns the contacts that are on this roster as
207     L<Net::XMPP2::IM::Contact> objects.
208    
209     NOTE: This method only returns the contacts that have
210     a roster item. If you haven't retrieved the roster yet
211     the presence information is still stored but you have
212     to get the contacts without a roster item with the
213     C<get_contacts_off_roster> method. See below.
214    
215     =cut
216    
217     sub get_contacts {
218     my ($self) = @_;
219 elmex 1.4 grep { $_->is_on_roster } values %{$self->{contacts}}
220 elmex 1.3 }
221    
222     =head2 get_contacts_off_roster
223    
224     Returns the contacts that are not on the roster
225     but for which we have received presence.
226     Return value is a list of L<Net::XMPP2::IM::Contact> objects.
227    
228     See also documentation of L<Net::XMPP2::IM::Roster::get_contacts>
229     above.
230    
231     =cut
232    
233     sub get_contacts_off_roster {
234     my ($self) = @_;
235 elmex 1.4 grep { not $_->is_on_roster } values %{$self->{contacts}}
236 elmex 1.1 }
237    
238 elmex 1.5 =head2 subscribe ($jid)
239    
240     This method sends a subscription request to C<$jid>.
241     If the optional C<$not_mutual> paramenter is true
242     the subscription will not be mutual.
243    
244     =cut
245    
246     sub subscribe {
247     my ($self) = @_;
248     # FIXME / TODO
249     }
250    
251 elmex 1.3 =head2 debug_dump
252    
253     This prints the roster and all it's contacts
254     and their presences.
255    
256     =cut
257    
258 elmex 1.1 sub debug_dump {
259     my ($self) = @_;
260     print "### ROSTER BEGIN ###\n";
261     my %groups;
262 elmex 1.3 for my $contact ($self->get_contacts) {
263 elmex 1.1 push @{$groups{$_}}, $contact for $contact->groups;
264     push @{$groups{''}}, $contact unless $contact->groups;
265     }
266    
267     for my $grp (sort keys %groups) {
268     print "=== $grp ====\n";
269     $_->debug_dump for @{$groups{$grp}};
270     }
271 elmex 1.3 if ($self->get_contacts_off_roster) {
272     print "### OFF ROSTER ###\n";
273     %groups;
274     for my $contact ($self->get_contacts_off_roster) {
275     push @{$groups{$_}}, $contact for $contact->groups;
276     push @{$groups{''}}, $contact unless $contact->groups;
277     }
278    
279     for my $grp (sort keys %groups) {
280     print "=== $grp ====\n";
281 elmex 1.9 $_->debug_dump for grep { not $_->is_on_roster } @{$groups{$grp}};
282 elmex 1.3 }
283     }
284    
285 elmex 1.1 print "### ROSTER END ###\n";
286     }
287    
288     =head1 AUTHOR
289    
290     Robin Redeker, C<< <elmex at ta-sa.org> >>
291    
292 elmex 1.3 =head1 SEE ALSO
293 elmex 1.1
294 elmex 1.3 L<Net::XMPP2::IM::Connection>, L<Net::XMPP2::IM::Contact>, L<Net::XMPP2::IM::Presence>
295 elmex 1.1
296     =head1 COPYRIGHT & LICENSE
297    
298     Copyright 2007 Robin Redeker, all rights reserved.
299    
300     This program is free software; you can redistribute it and/or modify it
301     under the same terms as Perl itself.
302    
303     =cut
304    
305    
306    
307     1; # End of Net::XMPP2