ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Net-XMPP2/lib/Net/XMPP2/Client.pm
Revision: 1.12
Committed: Thu Jul 5 17:33:59 2007 UTC (19 years, 2 months ago) by elmex
Branch: MAIN
Changes since 1.11: +2 -2 lines
Log Message:
fixed some broken links in the documentation and moved errors
to seperate files.

File Contents

# User Rev Content
1 elmex 1.1 package Net::XMPP2::Client;
2     use strict;
3     use AnyEvent;
4 elmex 1.2 use Net::XMPP2::IM::Connection;
5     use Net::XMPP2::Util qw/stringprep_jid prep_bare_jid/;
6     use Net::XMPP2::Namespaces qw/xmpp_ns/;
7 elmex 1.1 use Net::XMPP2::Event;
8 elmex 1.2 use Net::XMPP2::IM::Account;
9    
10 elmex 1.11 #use XML::Twig;
11     #
12     #sub _dumpxml {
13     # my $data = shift;
14     # my $t = XML::Twig->new;
15     # if ($t->safe_parse ("<deb>$data</deb>")) {
16     # $t->set_pretty_print ('indented');
17     # $t->print;
18     # print "\n";
19     # } else {
20     # print "[$data]\n";
21     # }
22     #}
23 elmex 1.1
24     our @ISA = qw/Net::XMPP2::Event/;
25    
26     =head1 NAME
27    
28     Net::XMPP2::Client - A XMPP Client abstraction
29    
30     =head1 SYNOPSIS
31    
32     use Net::XMPP2::Client;
33     use AnyEvent;
34    
35     my $j = AnyEvent->condvar;
36    
37     my $cl = Net::XMPP2::Client->new;
38     $cl->start;
39    
40     $j->wait;
41    
42     =head1 DESCRIPTION
43    
44     This module tries to implement a straight forward and easy to
45     use API to communicate with XMPP entities. L<Net::XMPP2::Client>
46     handles connections and timeouts and all such stuff for you.
47    
48     For more flexibility please have a look at L<Net::XMPP2::Connection>
49     and L<Net::XMPP2::IM::Connection>, they allow you to control what
50     and how something is being sent more precisely.
51    
52     =head1 METHODS
53    
54     =head2 new (%args)
55    
56     Following arguments can be passed in C<%args>:
57    
58     =over 4
59    
60     =back
61    
62     =cut
63    
64     sub new {
65     my $this = shift;
66     my $class = ref($this) || $this;
67     my $self = { @_ };
68     bless $self, $class;
69 elmex 1.2 return $self;
70     }
71    
72     =head2 add_account ($jid, $password, $host, $port)
73    
74     This method adds a jabber account for connection with the JID C<$jid>
75     and the password C<$password>.
76    
77     C<$host> and C<$port> are optional and can be undef. C<$host> overrides the
78     host to connect to.
79    
80     Returns 1 on success and undef when the account already exists.
81    
82     =cut
83    
84     sub add_account {
85     my ($self, $jid, $password, $host, $port) = @_;
86    
87     $jid = stringprep_jid $jid;
88     my $bj = prep_bare_jid $jid;
89    
90     return if exists $self->{accounts}->{$bj};
91    
92     my $acc =
93     $self->{accounts}->{$bj} =
94     Net::XMPP2::IM::Account->new (
95     jid => $jid,
96     password => $password,
97     host => $host,
98     port => $port,
99     );
100    
101     $self->update_connections
102     if $self->{started};
103    
104     $acc
105     }
106    
107     =head2 start ()
108    
109     This method initiates the connections to the XMPP servers.
110    
111     =cut
112    
113     sub start {
114     my ($self) = @_;
115     $self->{started} = 1;
116     $self->update_connections;
117     }
118    
119 elmex 1.11 =head2 update_connections ()
120    
121     This method tries to connect all unconnected accounts.
122    
123     =cut
124    
125 elmex 1.2 sub update_connections {
126     my ($self) = @_;
127 elmex 1.1
128 elmex 1.2 for my $acc (values %{$self->{accounts}}) {
129     unless ($acc->is_connected) {
130     my $con = $acc->spawn_connection;
131    
132 elmex 1.5 $con->add_forward ($self, sub {
133     my ($con, $self, $ev, @arg) = @_;
134     $self->event ($ev, $acc, @arg);
135     });
136    
137 elmex 1.2 $con->reg_cb (
138     session_ready => sub {
139     my ($con) = @_;
140     $self->event (connected => $acc);
141     0 # do once
142     },
143 elmex 1.10 # debug_recv => sub { print "RRRRRRRRECVVVVVV:\n"; _dumpxml ($_[1]); 1 },
144     # debug_send => sub { print "SSSSSSSSENDDDDDD:\n"; _dumpxml ($_[1]); 1 },
145 elmex 1.7 disconnect => sub {
146     delete $self->{accounts}->{$acc};
147     0
148     }
149 elmex 1.2 );
150    
151 elmex 1.7 unless ($con->connect) {
152     $self->event (connect_error => "Couldn't connect to ".($acc->jid).": $!");
153     next
154     }
155 elmex 1.2 $con->init
156     }
157     }
158     }
159    
160 elmex 1.11 =head2 disconnect ($msg)
161 elmex 1.6
162     Disconnect all accounts.
163    
164     =cut
165    
166     sub disconnect {
167     my ($self, $msg) = @_;
168     for my $acc (values %{$self->{accounts}}) {
169     if ($acc->is_connected) { $acc->connection ()->disconnect ($msg) }
170     }
171     }
172    
173 elmex 1.11 =head2 remove_accounts ($msg)
174 elmex 1.6
175     Removes all accounts and disconnects.
176    
177     =cut
178    
179     sub remove_accounts {
180     my ($self, $msg) = @_;
181     for my $acc (keys %{$self->{accounts}}) {
182     my $acca = $self->{accounts}->{$acc};
183     if ($acca->is_connected) { $acca->connection ()->disconnect ($msg) }
184     delete $self->{accounts}->{$acc};
185     }
186     }
187    
188 elmex 1.11 =head2 remove_account ($acc)
189 elmex 1.7
190     Removes and disconnects account C<$acc>.
191    
192     =cut
193    
194     sub remove_account {
195     my ($self, $acc, $reason) = @_;
196     if ($acc->is_connected) {
197     $acc->connection ()->disconnect ($reason);
198     }
199     delete $self->{accounts}->{$acc};
200     }
201    
202 elmex 1.11 =head2 send_message ($msg, $dest_jid, $src)
203 elmex 1.2
204     Sends a message to the destination C<$dest_jid>.
205     C<$msg> can either be a string or a L<Net::XMPP2::IM::Message> object.
206     If C<$msg> is such an object C<$dest_jid> is optional, and will, when
207     passed, override the destination of the message.
208    
209     C<$src> is optional. It specifies which account to use
210     to send the message. If it is not passed L<Net::XMPP2::Client> will try
211     to find an account itself. First it will look through all rosters
212     to find C<$dest_jid> and if none found it will pick any of the accounts that
213     are connected.
214    
215     C<$src> can either be a JID or a L<Net::XMPP2::IM::Account> object as returned
216     by C<add_account> and C<get_account>.
217    
218     =cut
219    
220     sub send_message {
221     my ($self, $msg, $dest_jid, $src) = @_;
222    
223     unless (ref $msg) {
224     $msg = Net::XMPP2::IM::Message->new (body => $msg);
225     }
226    
227     if (defined $dest_jid) {
228     my $jid = stringprep_jid $dest_jid
229     or die "send_message: \$dest_jid is not a proper JID";
230     $msg->to ($jid);
231     }
232    
233     my $srcacc;
234     if (ref $src) {
235     $srcacc = $src;
236     } elsif (defined $src) {
237     $srcacc = $self->get_account ($src)
238     } else {
239     $srcacc = $self->find_account_for_dest_jid ($dest_jid);
240     }
241    
242     unless ($srcacc && $srcacc->is_connected) {
243     die "send_message: Couldn't get connected account for sending"
244     }
245    
246     $msg->send ($srcacc->connection)
247     }
248    
249 elmex 1.11 =head2 get_account ($jid)
250 elmex 1.2
251     Returns the L<Net::XMPP2::IM::Account> account object for the JID C<$jid>
252     if there is any such account added. (returns undef otherwise).
253    
254     =cut
255    
256     sub get_account {
257     my ($self, $jid) = @_;
258     $self->{accounts}->{prep_bare_jid $jid}
259     }
260    
261 elmex 1.11 =head2 get_accounts ()
262    
263     Returns a list of L<Net::XMPP2::IM::Account>s.
264    
265     =cut
266    
267 elmex 1.9 sub get_accounts {
268     my ($self) = @_;
269     values %{$self->{accounts}}
270     }
271    
272 elmex 1.11 =head2 get_accounts ()
273    
274     Returns a list of connected L<Net::XMPP2::IM::Account>s.
275    
276     Same as:
277    
278     grep { $_->is_connected } $client->get_accounts ();
279    
280     =cut
281    
282 elmex 1.7 sub get_connected_accounts {
283     my ($self, $jid) = @_;
284     my (@a) = grep $_->is_connected, values %{$self->{accounts}};
285     @a
286     }
287    
288 elmex 1.11 =head2 find_account_for_dest_jid ($jid)
289    
290     This method tries to find any account that has the contact C<$jid>
291     on his roster. If no account with C<$jid> on his roster was found
292     it takes the first one that is connected. (Return value is a L<Net::XMPP2::IM::Account>
293     object).
294    
295     If no account is connected it returns undef.
296    
297     =cut
298    
299 elmex 1.2 sub find_account_for_dest_jid {
300     my ($self, $jid) = @_;
301    
302     my $any_acc;
303     for my $acc (values %{$self->{accounts}}) {
304     next unless $acc->is_connected;
305    
306     # take "first" active account
307     $any_acc = $acc unless defined $any_acc;
308    
309     my $roster = $acc->connection ()->get_roster;
310     if (my $c = $roster->get_contact ($jid)) {
311     return $acc;
312     }
313     }
314    
315     $any_acc
316 elmex 1.1 }
317    
318 elmex 1.11 =head2 get_contacts_for_jid ($jid)
319    
320     This method returns all contacts that we are connected to.
321     That means: It joins the contact lists of all account's rosters
322     that we are connected to.
323    
324     =cut
325    
326 elmex 1.7 sub get_contacts_for_jid {
327     my ($self, $jid) = @_;
328     my @cons;
329     for ($self->get_connected_accounts) {
330     my $roster = $_->connection ()->get_roster ();
331     my $con = $roster->get_contact ($jid);
332     push @cons, $con if $con;
333     }
334     return @cons;
335     }
336    
337 elmex 1.11 =head2 get_priority_presence_for_jid ($jid)
338    
339     This method returns the presence for the contact C<$jid> with the highest
340     priority.
341    
342     If the contact C<$jid> is on multiple account's rosters it's undefined which
343     roster the presence belongs to.
344    
345     =cut
346    
347 elmex 1.8 sub get_priority_presence_for_jid {
348     my ($self, $jid) = @_;
349    
350     my $lpres;
351     for ($self->get_connected_accounts) {
352     my $roster = $_->connection ()->get_roster ();
353     my $con = $roster->get_contact ($jid);
354     next unless defined $con;
355     my $pres = $con->get_priority_presence ($jid);
356     next unless defined $pres;
357     if ((not defined $lpres) || $lpres->priority < $pres->priority) {
358     $lpres = $pres;
359     }
360     }
361    
362     $lpres
363     }
364    
365 elmex 1.11 =head2 set_presence ($show, $status, $priority)
366    
367     This sets the presence of all accounts. For a meaning of C<$show>, C<$status>
368     and C<$priority> see the description of the C<%attrs> hash in
369 elmex 1.12 C<send_presence> method of L<Net::XMPP2::Writer>.
370 elmex 1.11
371     =cut
372    
373 elmex 1.9 sub set_presence {
374     my ($self, $show, $status, $priority) = @_;
375    
376     for my $ac ($self->get_connected_accounts) {
377     my $con = $ac->connection ();
378     $con->send_presence (
379     undef, undef,
380     show => $show,
381     status => $status,
382     priority => $priority
383     );
384     }
385     }
386    
387 elmex 1.1 =head1 EVENTS
388    
389 elmex 1.2 In the following event descriptions the argument C<$account>
390 elmex 1.12 is always a L<Net::XMPP2::IM::Account> object.
391 elmex 1.2
392 elmex 1.5 All events from L<Net::XMPP2::IM::Connection> are forwarded to the client,
393     only that the first argument for every event is a C<$account> object.
394    
395     Aside fom those, these events can be registered on with C<reg_cb>:
396    
397 elmex 1.1 =over 4
398    
399 elmex 1.2 =item connected => $account
400    
401     This event is sent when the C<$account> was successfully connected.
402    
403     =item connect_error => $account
404    
405     This event is emitted when an error occured in the connection process for the
406     account C<$account>.
407    
408     =item error => $account
409    
410     This event is emitted when any error occured while communicating
411     over the connection to the C<$account> - after a connection was established.
412    
413 elmex 1.1 =back
414    
415     =head1 AUTHOR
416    
417 elmex 1.11 Robin Redeker, C<< <elmex at ta-sa.org> >>, JID: C<< <elmex at jabber.org> >>
418 elmex 1.1
419     =head1 COPYRIGHT & LICENSE
420    
421     Copyright 2007 Robin Redeker, all rights reserved.
422    
423     This program is free software; you can redistribute it and/or modify it
424     under the same terms as Perl itself.
425    
426     =cut
427    
428     1; # End of Net::XMPP2::Client