ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/Net-XMPP2/lib/Net/XMPP2/Client.pm
Revision: 1.15
Committed: Fri Jul 6 22:22:21 2007 UTC (19 years, 3 months ago) by elmex
Branch: MAIN
Changes since 1.14: +12 -3 lines
Log Message:
implemented dataforms - phew! that was a bullet of work

File Contents

# Content
1 package Net::XMPP2::Client;
2 use strict;
3 use AnyEvent;
4 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 use Net::XMPP2::Event;
8 use Net::XMPP2::IM::Account;
9
10 #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
24 our @ISA = qw/Net::XMPP2::Event/;
25
26 =head1 NAME
27
28 Net::XMPP2::Client - 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 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 =head2 update_connections ()
120
121 This method tries to connect all unconnected accounts.
122
123 =cut
124
125 sub update_connections {
126 my ($self) = @_;
127
128 for my $acc (values %{$self->{accounts}}) {
129 unless ($acc->is_connected) {
130 my $con = $acc->spawn_connection;
131
132 $con->add_forward ($self, sub {
133 my ($con, $self, $ev, @arg) = @_;
134 $self->event ($ev, $acc, @arg);
135 });
136
137 $con->reg_cb (
138 session_ready => sub {
139 my ($con) = @_;
140 $self->event (connected => $acc);
141 0 # do once
142 },
143 # debug_recv => sub { print "RRRRRRRRECVVVVVV:\n"; _dumpxml ($_[1]); 1 },
144 # debug_send => sub { print "SSSSSSSSENDDDDDD:\n"; _dumpxml ($_[1]); 1 },
145 disconnect => sub {
146 delete $self->{accounts}->{$acc};
147 0
148 }
149 );
150
151 unless ($con->connect) {
152 $self->event (connect_error => "Couldn't connect to ".($acc->jid).": $!");
153 next
154 }
155 $con->init
156 }
157 }
158 }
159
160 =head2 disconnect ($msg)
161
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 =head2 remove_accounts ($msg)
174
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 =head2 remove_account ($acc)
189
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 =head2 send_message ($msg, $dest_jid, $src, $type)
203
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, but 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 C<$type> is optional but overrides the type of the message object in C<$msg>
219 if C<$msg> is such an object.
220
221 C<$type> should be 'chat' for normal chatter. If no C<$type> is specified
222 the type of the message defaults to the value documented in L<Net::XMPP2::IM::Message>
223 (should be 'normal').
224
225 =cut
226
227 sub send_message {
228 my ($self, $msg, $dest_jid, $src, $type) = @_;
229
230 unless (ref $msg) {
231 $msg = Net::XMPP2::IM::Message->new (body => $msg);
232 }
233
234 if (defined $dest_jid) {
235 my $jid = stringprep_jid $dest_jid
236 or die "send_message: \$dest_jid is not a proper JID";
237 $msg->to ($jid);
238 }
239
240 $msg->type ($type) if defined $type;
241
242 my $srcacc;
243 if (ref $src) {
244 $srcacc = $src;
245 } elsif (defined $src) {
246 $srcacc = $self->get_account ($src)
247 } else {
248 $srcacc = $self->find_account_for_dest_jid ($dest_jid);
249 }
250
251 unless ($srcacc && $srcacc->is_connected) {
252 die "send_message: Couldn't get connected account for sending"
253 }
254
255 $msg->send ($srcacc->connection)
256 }
257
258 =head2 get_account ($jid)
259
260 Returns the L<Net::XMPP2::IM::Account> account object for the JID C<$jid>
261 if there is any such account added. (returns undef otherwise).
262
263 =cut
264
265 sub get_account {
266 my ($self, $jid) = @_;
267 $self->{accounts}->{prep_bare_jid $jid}
268 }
269
270 =head2 get_accounts ()
271
272 Returns a list of L<Net::XMPP2::IM::Account>s.
273
274 =cut
275
276 sub get_accounts {
277 my ($self) = @_;
278 values %{$self->{accounts}}
279 }
280
281 =head2 get_accounts ()
282
283 Returns a list of connected L<Net::XMPP2::IM::Account>s.
284
285 Same as:
286
287 grep { $_->is_connected } $client->get_accounts ();
288
289 =cut
290
291 sub get_connected_accounts {
292 my ($self, $jid) = @_;
293 my (@a) = grep $_->is_connected, values %{$self->{accounts}};
294 @a
295 }
296
297 =head2 find_account_for_dest_jid ($jid)
298
299 This method tries to find any account that has the contact C<$jid>
300 on his roster. If no account with C<$jid> on his roster was found
301 it takes the first one that is connected. (Return value is a L<Net::XMPP2::IM::Account>
302 object).
303
304 If no account is connected it returns undef.
305
306 =cut
307
308 sub find_account_for_dest_jid {
309 my ($self, $jid) = @_;
310
311 my $any_acc;
312 for my $acc (values %{$self->{accounts}}) {
313 next unless $acc->is_connected;
314
315 # take "first" active account
316 $any_acc = $acc unless defined $any_acc;
317
318 my $roster = $acc->connection ()->get_roster;
319 if (my $c = $roster->get_contact ($jid)) {
320 return $acc;
321 }
322 }
323
324 $any_acc
325 }
326
327 =head2 get_contacts_for_jid ($jid)
328
329 This method returns all contacts that we are connected to.
330 That means: It joins the contact lists of all account's rosters
331 that we are connected to.
332
333 =cut
334
335 sub get_contacts_for_jid {
336 my ($self, $jid) = @_;
337 my @cons;
338 for ($self->get_connected_accounts) {
339 my $roster = $_->connection ()->get_roster ();
340 my $con = $roster->get_contact ($jid);
341 push @cons, $con if $con;
342 }
343 return @cons;
344 }
345
346 =head2 get_priority_presence_for_jid ($jid)
347
348 This method returns the presence for the contact C<$jid> with the highest
349 priority.
350
351 If the contact C<$jid> is on multiple account's rosters it's undefined which
352 roster the presence belongs to.
353
354 =cut
355
356 sub get_priority_presence_for_jid {
357 my ($self, $jid) = @_;
358
359 my $lpres;
360 for ($self->get_connected_accounts) {
361 my $roster = $_->connection ()->get_roster ();
362 my $con = $roster->get_contact ($jid);
363 next unless defined $con;
364 my $pres = $con->get_priority_presence ($jid);
365 next unless defined $pres;
366 if ((not defined $lpres) || $lpres->priority < $pres->priority) {
367 $lpres = $pres;
368 }
369 }
370
371 $lpres
372 }
373
374 =head2 set_presence ($show, $status, $priority)
375
376 This sets the presence of all accounts. For a meaning of C<$show>, C<$status>
377 and C<$priority> see the description of the C<%attrs> hash in
378 C<send_presence> method of L<Net::XMPP2::Writer>.
379
380 =cut
381
382 sub set_presence {
383 my ($self, $show, $status, $priority) = @_;
384
385 for my $ac ($self->get_connected_accounts) {
386 my $con = $ac->connection ();
387 $con->send_presence (
388 undef, undef,
389 show => $show,
390 status => $status,
391 priority => $priority
392 );
393 }
394 }
395
396 =head1 EVENTS
397
398 In the following event descriptions the argument C<$account>
399 is always a L<Net::XMPP2::IM::Account> object.
400
401 All events from L<Net::XMPP2::IM::Connection> are forwarded to the client,
402 only that the first argument for every event is a C<$account> object.
403
404 Aside fom those, these events can be registered on with C<reg_cb>:
405
406 =over 4
407
408 =item connected => $account
409
410 This event is sent when the C<$account> was successfully connected.
411
412 =item connect_error => $account, $reason
413
414 This event is emitted when an error occured in the connection process for the
415 account C<$account>.
416
417 =item error => $account
418
419 This event is emitted when any error occured while communicating
420 over the connection to the C<$account> - after a connection was established.
421
422 =back
423
424 =head1 AUTHOR
425
426 Robin Redeker, C<< <elmex at ta-sa.org> >>, JID: C<< <elmex at jabber.org> >>
427
428 =head1 COPYRIGHT & LICENSE
429
430 Copyright 2007 Robin Redeker, all rights reserved.
431
432 This program is free software; you can redistribute it and/or modify it
433 under the same terms as Perl itself.
434
435 =cut
436
437 1; # End of Net::XMPP2::Client