ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/Net-XMPP2/lib/Net/XMPP2/IM/Roster.pm
Revision: 1.8
Committed: Tue Apr 24 18:11:10 2007 UTC (19 years, 5 months ago) by elmex
Branch: MAIN
Changes since 1.7: +1 -1 lines
Log Message:
removed extension mechanisms which don't seem neccessary
to me anymore. noone wants XMPP without the XEPs (at least not me).

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