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