ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Net-XMPP2/lib/Net/XMPP2/Client.pm
Revision: 1.7
Committed: Mon Jun 25 07:56:52 2007 UTC (19 years, 3 months ago) by elmex
Branch: MAIN
Changes since 1.6: +39 -3 lines
Log Message:
sime fixes and changes

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     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     sub update_connections {
120     my ($self) = @_;
121 elmex 1.1
122 elmex 1.2 for my $acc (values %{$self->{accounts}}) {
123     unless ($acc->is_connected) {
124     my $con = $acc->spawn_connection;
125    
126 elmex 1.5 $con->add_forward ($self, sub {
127     my ($con, $self, $ev, @arg) = @_;
128     $self->event ($ev, $acc, @arg);
129     });
130    
131 elmex 1.2 $con->reg_cb (
132     session_ready => sub {
133     my ($con) = @_;
134     $self->event (connected => $acc);
135     0 # do once
136     },
137     debug_recv => sub { print "RRRRRRRRECVVVVVV:\n"; _dumpxml ($_[1]); 1 },
138     debug_send => sub { print "SSSSSSSSENDDDDDD:\n"; _dumpxml ($_[1]); 1 },
139 elmex 1.7 disconnect => sub {
140     delete $self->{accounts}->{$acc};
141     0
142     }
143 elmex 1.2 );
144    
145 elmex 1.7 unless ($con->connect) {
146     $self->event (connect_error => "Couldn't connect to ".($acc->jid).": $!");
147     next
148     }
149 elmex 1.2 $con->init
150     }
151     }
152     }
153    
154 elmex 1.6 =item disconnect ($msg)
155    
156     Disconnect all accounts.
157    
158     =cut
159    
160     sub disconnect {
161     my ($self, $msg) = @_;
162     for my $acc (values %{$self->{accounts}}) {
163     if ($acc->is_connected) { $acc->connection ()->disconnect ($msg) }
164     }
165     }
166    
167     =item remove_accounts ($msg)
168    
169     Removes all accounts and disconnects.
170    
171     =cut
172    
173     sub remove_accounts {
174     my ($self, $msg) = @_;
175     for my $acc (keys %{$self->{accounts}}) {
176     my $acca = $self->{accounts}->{$acc};
177     if ($acca->is_connected) { $acca->connection ()->disconnect ($msg) }
178     delete $self->{accounts}->{$acc};
179     }
180     }
181    
182 elmex 1.7 =item remove_account ($acc)
183    
184     Removes and disconnects account C<$acc>.
185    
186     =cut
187    
188     sub remove_account {
189     my ($self, $acc, $reason) = @_;
190     if ($acc->is_connected) {
191     $acc->connection ()->disconnect ($reason);
192     }
193     delete $self->{accounts}->{$acc};
194     }
195    
196 elmex 1.2 =item send_message ($msg, $dest_jid, $src)
197    
198     Sends a message to the destination C<$dest_jid>.
199     C<$msg> can either be a string or a L<Net::XMPP2::IM::Message> object.
200     If C<$msg> is such an object C<$dest_jid> is optional, and will, when
201     passed, override the destination of the message.
202    
203     C<$src> is optional. It specifies which account to use
204     to send the message. If it is not passed L<Net::XMPP2::Client> will try
205     to find an account itself. First it will look through all rosters
206     to find C<$dest_jid> and if none found it will pick any of the accounts that
207     are connected.
208    
209     C<$src> can either be a JID or a L<Net::XMPP2::IM::Account> object as returned
210     by C<add_account> and C<get_account>.
211    
212     =cut
213    
214     sub send_message {
215     my ($self, $msg, $dest_jid, $src) = @_;
216    
217     unless (ref $msg) {
218     $msg = Net::XMPP2::IM::Message->new (body => $msg);
219     }
220    
221     if (defined $dest_jid) {
222     my $jid = stringprep_jid $dest_jid
223     or die "send_message: \$dest_jid is not a proper JID";
224     $msg->to ($jid);
225     }
226    
227     my $srcacc;
228     if (ref $src) {
229     $srcacc = $src;
230     } elsif (defined $src) {
231     $srcacc = $self->get_account ($src)
232     } else {
233     $srcacc = $self->find_account_for_dest_jid ($dest_jid);
234     }
235    
236     unless ($srcacc && $srcacc->is_connected) {
237     die "send_message: Couldn't get connected account for sending"
238     }
239    
240     $msg->send ($srcacc->connection)
241     }
242    
243     =item get_account ($jid)
244    
245     Returns the L<Net::XMPP2::IM::Account> account object for the JID C<$jid>
246     if there is any such account added. (returns undef otherwise).
247    
248     =cut
249    
250     sub get_account {
251     my ($self, $jid) = @_;
252     $self->{accounts}->{prep_bare_jid $jid}
253     }
254    
255 elmex 1.7 sub get_connected_accounts {
256     my ($self, $jid) = @_;
257     my (@a) = grep $_->is_connected, values %{$self->{accounts}};
258     @a
259     }
260    
261 elmex 1.2 sub find_account_for_dest_jid {
262     my ($self, $jid) = @_;
263    
264     my $any_acc;
265     for my $acc (values %{$self->{accounts}}) {
266     next unless $acc->is_connected;
267    
268     # take "first" active account
269     $any_acc = $acc unless defined $any_acc;
270    
271     my $roster = $acc->connection ()->get_roster;
272     if (my $c = $roster->get_contact ($jid)) {
273     return $acc;
274     }
275     }
276    
277     $any_acc
278 elmex 1.1 }
279    
280 elmex 1.7 sub get_contacts_for_jid {
281     my ($self, $jid) = @_;
282     my @cons;
283     for ($self->get_connected_accounts) {
284     my $roster = $_->connection ()->get_roster ();
285     my $con = $roster->get_contact ($jid);
286     push @cons, $con if $con;
287     }
288     return @cons;
289     }
290    
291 elmex 1.1 =head1 EVENTS
292    
293 elmex 1.2 In the following event descriptions the argument C<$account>
294     is always a L<Net::XMMP2::IM::Account> object.
295    
296 elmex 1.5 All events from L<Net::XMPP2::IM::Connection> are forwarded to the client,
297     only that the first argument for every event is a C<$account> object.
298    
299     Aside fom those, these events can be registered on with C<reg_cb>:
300    
301 elmex 1.1 =over 4
302    
303 elmex 1.2 =item connected => $account
304    
305     This event is sent when the C<$account> was successfully connected.
306    
307     =item connect_error => $account
308    
309     This event is emitted when an error occured in the connection process for the
310     account C<$account>.
311    
312     =item error => $account
313    
314     This event is emitted when any error occured while communicating
315     over the connection to the C<$account> - after a connection was established.
316    
317 elmex 1.1 =back
318    
319     =head1 AUTHOR
320    
321     Robin Redeker, C<< <elmex at ta-sa.org> >>
322    
323     =head1 COPYRIGHT & LICENSE
324    
325     Copyright 2007 Robin Redeker, all rights reserved.
326    
327     This program is free software; you can redistribute it and/or modify it
328     under the same terms as Perl itself.
329    
330     =cut
331    
332     1; # End of Net::XMPP2::Client