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

# 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 elmex 1.13 Net::XMPP2::Client - XMPP Client abstraction
29 elmex 1.1
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.15 =head2 send_message ($msg, $dest_jid, $src, $type)
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 elmex 1.15 If C<$msg> is such an object C<$dest_jid> is optional, but will, when
207 elmex 1.2 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 elmex 1.15 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 elmex 1.2 =cut
226    
227     sub send_message {
228 elmex 1.15 my ($self, $msg, $dest_jid, $src, $type) = @_;
229 elmex 1.2
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 elmex 1.15 $msg->type ($type) if defined $type;
241    
242 elmex 1.2 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 elmex 1.11 =head2 get_account ($jid)
259 elmex 1.2
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 elmex 1.11 =head2 get_accounts ()
271    
272     Returns a list of L<Net::XMPP2::IM::Account>s.
273    
274     =cut
275    
276 elmex 1.9 sub get_accounts {
277     my ($self) = @_;
278     values %{$self->{accounts}}
279     }
280    
281 elmex 1.11 =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 elmex 1.7 sub get_connected_accounts {
292     my ($self, $jid) = @_;
293     my (@a) = grep $_->is_connected, values %{$self->{accounts}};
294     @a
295     }
296    
297 elmex 1.11 =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 elmex 1.2 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 elmex 1.1 }
326    
327 elmex 1.11 =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 elmex 1.7 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 elmex 1.11 =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 elmex 1.8 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 elmex 1.11 =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 elmex 1.12 C<send_presence> method of L<Net::XMPP2::Writer>.
379 elmex 1.11
380     =cut
381    
382 elmex 1.9 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 elmex 1.1 =head1 EVENTS
397    
398 elmex 1.2 In the following event descriptions the argument C<$account>
399 elmex 1.12 is always a L<Net::XMPP2::IM::Account> object.
400 elmex 1.2
401 elmex 1.5 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 elmex 1.1 =over 4
407    
408 elmex 1.2 =item connected => $account
409    
410     This event is sent when the C<$account> was successfully connected.
411    
412 elmex 1.14 =item connect_error => $account, $reason
413 elmex 1.2
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 elmex 1.1 =back
423    
424     =head1 AUTHOR
425    
426 elmex 1.11 Robin Redeker, C<< <elmex at ta-sa.org> >>, JID: C<< <elmex at jabber.org> >>
427 elmex 1.1
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