ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Net-XMPP2/lib/Net/XMPP2/Connection.pm
Revision: 1.12
Committed: Tue Apr 24 16:13:52 2007 UTC (19 years, 5 months ago) by elmex
Branch: MAIN
Changes since 1.11: +29 -5 lines
Log Message:
further subscription code and in-band-stuff is going to be implemented next

File Contents

# User Rev Content
1 elmex 1.1 package Net::XMPP2::Connection;
2     use strict;
3     use AnyEvent;
4     use IO::Socket::INET;
5     use Net::XMPP2::Parser;
6     use Net::XMPP2::Writer;
7 elmex 1.10 use Net::XMPP2::Util qw/split_jid/;
8 elmex 1.9 use Net::XMPP2::Event;
9     use Net::XMPP2::SimpleConnection;
10 elmex 1.1 use Net::XMPP2::Namespaces qw/xmpp_ns/;
11 elmex 1.10 use Net::XMPP2::Error;
12 elmex 1.1 use Net::DNS;
13 elmex 1.2
14 elmex 1.9 our @ISA = qw/Net::XMPP2::SimpleConnection Net::XMPP2::Event/;
15 elmex 1.1
16     =head1 NAME
17    
18     Net::XMPP2::Connection - A XML stream that implements the XMPP RFC 3920.
19    
20     =head1 SYNOPSIS
21    
22     use Net::XMPP2::Connection;
23    
24     my $con =
25     Net::XMPP2::Connection->new (
26     username => "abc",
27     domain => "jabber.org",
28     resource => "Net::XMPP2"
29     );
30    
31     $con->connect or die "Couldn't connect to jabber.org: $!";
32     $con->init;
33     $con->reg_cb (stream_ready => sub { print "XMPP stream ready!\n" });
34    
35     =head1 DESCRIPTION
36    
37     This module represents a XMPP stream as described in RFC 3920. You can issue the basic
38     XMPP XML stanzas with methods like C<send_iq>, C<send_message> and C<send_presence>.
39    
40     And receive events with the C<reg_cb> event framework from the connection.
41    
42     If you need instant messaging stuff please take a look at C<Net::XMPP2::IM::Connection>.
43    
44     =head1 METHODS
45    
46     =head2 new (%args)
47    
48     Following arguments can be passed in C<%args>:
49    
50     =over 4
51    
52     =item language => $tag
53    
54     This should be the language of the human readable contents that
55     will be transmitted over the stream. The default will be 'en'.
56    
57     Please look in RFC 3066 how C<$tag> should look like.
58    
59 elmex 1.10 =item jid => $jid
60    
61     This can be used to set the settings C<username>, C<domain>
62     (and optionally C<resource>) from a C<$jid>.
63    
64 elmex 1.12 =item register => $bool
65    
66     If C<$bool> is true the connection will attempt to automatically try
67     an in-band registration.
68    
69 elmex 1.1 =item resource => $resource
70    
71     If this argument is given C<$resource> will be passed as desired
72     resource on resource binding.
73    
74     Note: You have to take care that the stringprep profile for
75     resources can be applied at: C<$resource>. Otherwise the server
76     might signal an error. See L<Net::XMPP2::Util> for utility functions
77     to check this.
78    
79     =item domain => $domain
80    
81     This is the destination host we are going to connect to.
82     As the connection won't be automatically connected use C<connect>
83     to initiate the connect.
84    
85     Note: A SRV RR lookup will be performed to discover the real hostname
86     and port to connect to. See also C<connect>.
87    
88 elmex 1.9 =item override_host => $host
89     =item override_port => $port
90    
91     This will be used as override to connect to.
92    
93 elmex 1.1 =item port => $port
94    
95     This is optional, the default port is 5222.
96    
97     Note: A SRV RR lookup will be performed to discover the real hostname
98     and port to connect to. See also C<connect>.
99    
100     =item username => $username
101    
102     This is your C<$username> (the userpart in the JID);
103    
104     Note: You have to take care that the stringprep profile for
105     nodes can be applied at: C<$username>. Otherwise the server
106     might signal an error. See L<Net::XMPP2::Util> for utility functions
107     to check this.
108    
109     =item password => $password
110    
111     This is the password for the C<username> above.
112    
113 elmex 1.5 =item disable_ssl => $bool
114    
115     If C<$bool> is true no SSL will be used.
116    
117 elmex 1.1 =back
118    
119     =cut
120    
121     sub new {
122     my $this = shift;
123     my $class = ref($this) || $this;
124     my $self = { language => 'en', @_ };
125     bless $self, $class;
126    
127     $self->{parser} = new Net::XMPP2::Parser;
128     $self->{writer} = Net::XMPP2::Writer->new (
129     write_cb => sub { $self->write_data ($_[0]) }
130     );
131    
132     $self->{parser}->set_stanza_cb (sub {
133     $self->handle_stanza (@_);
134     });
135    
136     $self->{iq_id} = 1;
137    
138     $self->{disconnect_cb} = sub {
139     my ($host, $port, $message) = @_;
140 elmex 1.7 delete $self->{authenticated};
141     delete $self->{ssl_enabled};
142 elmex 1.1 $self->event (disconnect => $host, $port, $message);
143     };
144    
145 elmex 1.10 if ($self->{jid}) {
146     my ($user, $host, $res) = split_jid ($self->{jid});
147     $self->{username} = $user;
148     $self->{domain} = $host;
149     $self->{resource} = $res if defined $res;
150     }
151    
152 elmex 1.7 for (qw/username password domain/) {
153     die "No '$_' argument given to new, but '$_' is required\n"
154     unless $self->{$_};
155     }
156    
157 elmex 1.1 return $self;
158     }
159    
160     =head2 connect ($no_srv_rr)
161    
162     Try to connect to the domain and port passed in C<new>.
163    
164     A SRV RR lookup will be performed on the domain to discover
165     the host and port to use. If you don't want this set C<$no_srv_rr>
166     to a true value. C<$no_srv_rr> is false by default.
167    
168     As the SRV RR lookup might return multiple host and you fail to
169     connect to one you might just call this function again to try a
170     different host.
171    
172     If C<connect> was successful and we connected a true value is returned.
173     If the connect was unsuccessful undef is returned and C<$!> will be set
174     to the error that occured while connecting.
175    
176     If you want to know whether further connection attempts might be more
177     successful (as SRV RR lookup may return multiple hosts) call C<may_try_connect>
178     (see also C<may_try_connect>).
179    
180     Note that an internal list will be kept of tried hosts. Use
181     C<reset_connect_tries> to reset the internal list of tried hosts.
182    
183     =cut
184    
185     sub connect {
186     my ($self, $no_srv_rr) = @_;
187    
188     my ($host, $port) = ($self->{domain}, $self->{port} || 5222);
189 elmex 1.9 if ($self->{override_host}) {
190     ($host, $port) = ($self->{override_host}, $self->{override_port} || 5222);
191 elmex 1.1
192 elmex 1.9 } else {
193     unless ($no_srv_rr) {
194     my $res = Net::DNS::Resolver->new;
195     my $p = $res->query ('_xmpp-client._tcp.'.$host, 'SRV');
196     if ($p) {
197     my @srvs = grep { $_->type eq 'SRV' } $p->answer;
198     if (@srvs) {
199     @srvs = sort { $a->priority <=> $b->priority } @srvs;
200     @srvs = sort { $b->weight <=> $a->weight } @srvs; # TODO
201     $port = $srvs[0]->port;
202     $host = $srvs[0]->target;
203     }
204 elmex 1.1 }
205     }
206     }
207    
208     if ($self->SUPER::connect ($host, $port)) {
209     $self->event (connect => $host, $port);
210     return 1;
211     } else {
212     return undef;
213     }
214     }
215    
216     =head2 may_try_connect
217    
218     Returns the number of left alternatives of hosts to connect to for the
219     domain passed to C<new>.
220    
221     An internal list of tried hosts will be managed by C<connect> and those
222     hosts will be ignored by a SRV RR lookup (which will be done if you
223     call this function).
224    
225     Use C<reset_connect_tries> to reset the internal list of tried hosts.
226    
227     =cut
228    
229     sub may_try_connect {
230     # TODO
231     }
232    
233     =head2 reset_connect_tries
234    
235     This function resets the internal list of tried hosts for C<connect>.
236     See also C<connect>.
237    
238     =cut
239    
240     sub reset_connect_tries {
241     # TODO
242     }
243    
244     sub handle_data {
245     my ($self, $buf) = @_;
246     $self->event (debug_recv => $$buf);
247     $self->{parser}->feed (substr $$buf, 0, (length $$buf), '');
248     }
249    
250 elmex 1.5 sub debug_wrote_data {
251     my ($self, $data) = @_;
252     $self->event (debug_send => $data);
253     }
254    
255 elmex 1.1 sub write_data {
256     my ($self, $data) = @_;
257     $self->SUPER::write_data ($data);
258     }
259    
260     sub handle_stanza {
261     my ($self, $p, $node) = @_;
262    
263     if ($node->eq (stream => 'features')) {
264     $self->event (stream_features => $node);
265     $self->handle_stream_features ($node);
266 elmex 1.4 $self->{features} = $node;
267 elmex 1.5
268 elmex 1.2 } elsif ($node->eq (tls => 'proceed')) {
269     $self->enable_ssl;
270     $self->{parser}->init;
271     $self->{writer}->init;
272     $self->{writer}->send_init_stream ($self->{language}, $self->{domain});
273    
274 elmex 1.11 } elsif ($node->eq (tls => 'failure')) {
275     $self->event ('tls_error');
276     $self->disconnect ('TLS failure on TLS negotiation.');
277    
278 elmex 1.1 } elsif ($node->eq (sasl => 'challenge')) {
279     $self->handle_sasl_challenge ($node);
280 elmex 1.11
281 elmex 1.1 } elsif ($node->eq (sasl => 'success')) {
282     $self->handle_sasl_success ($node);
283 elmex 1.11
284     } elsif ($node->eq (sasl => 'failure')) {
285     my $error = Net::XMPP2::Error::SASL->new (node => $node);
286     $self->event (sasl_error => $error);
287    
288 elmex 1.1 } elsif ($node->eq (client => 'iq')) {
289     $self->handle_iq ($node);
290 elmex 1.11
291 elmex 1.4 } elsif ($node->eq (client => 'message')) {
292 elmex 1.6 $self->event (message_xml => $node);
293 elmex 1.11
294 elmex 1.4 } elsif ($node->eq (client => 'presence')) {
295 elmex 1.6 $self->event (presence_xml => $node);
296 elmex 1.11
297 elmex 1.1 } elsif ($node->eq (stream => 'error')) {
298     $self->handle_error ($node);
299 elmex 1.11
300 elmex 1.1 } else {
301     warn "Didn't understood stanza: '" . $node->name . "'";
302     }
303     }
304    
305 elmex 1.10 =head2 init ()
306 elmex 1.1
307     Initiate the XML stream.
308    
309     =cut
310    
311     sub init {
312     my ($self) = @_;
313     $self->{writer}->send_init_stream ($self->{language}, $self->{domain});
314     }
315    
316 elmex 1.10 =head2 is_connected ()
317    
318     Returns true if the connection is still connected and stanzas can be
319     sent.
320    
321     =cut
322    
323     sub is_connected {
324     my ($self) = @_;
325     $self->{authenticated}
326     }
327    
328 elmex 1.1 =head2 send_iq ($type, $create_cb, $result_cb, %attrs)
329    
330     This method sends an IQ XMPP request.
331    
332     Please take a look at the documentation for C<send_iq> in Net::XMPP2::Writer
333     about the meaning of C<$type>, C<$create_cb> and C<%attrs>.
334    
335 elmex 1.4 C<$result_cb> will be called when a result was received. The first argument to
336     C<$result_cb> will be a Net::XMPP2::Node instance containing the IQ result
337     stanza contents.
338 elmex 1.1
339     If the IQ resulted in a stanza error the second argument to C<$result_cb> will
340     be C<undef> (if the error type was not 'continue') and the third argument will
341 elmex 1.10 be a L<Net::XMPP2::Error::IQ> object.
342 elmex 1.1
343 elmex 1.4 This method returns the newly generated id for this iq request.
344    
345 elmex 1.1 =cut
346    
347     sub send_iq {
348     my ($self, $type, $create_cb, $result_cb, %attrs) = @_;
349     my $id = $self->{iq_id}++;
350     $self->{iqs}->{$id} = $result_cb;
351     $self->{writer}->send_iq ($id, $type, $create_cb, %attrs);
352 elmex 1.4 $id
353     }
354    
355     =head2 reply_iq_result ($req_iq_node, $create_cb, %attrs)
356    
357     This method will generate a result reply to the iq request C<Net::XMPP2::Node>
358     in C<$req_iq_node>.
359    
360     Please take a look at the documentation for C<send_iq> in Net::XMPP2::Writer
361     about the meaning C<$create_cb> and C<%attrs>.
362    
363 elmex 1.6 Use C<$create_cb> to create the XML for the result.
364    
365 elmex 1.4 The type for this iq reply is 'result'.
366    
367     =cut
368    
369     sub reply_iq_result {
370     my ($self, $iqnode, $create_cb, %attrs) = @_;
371     $self->{writer}->send_iq ($iqnode->attr ('id'), 'result', $create_cb, %attrs);
372     }
373    
374     =head2 reply_iq_error ($req_iq_node, $error_type, $error, %attrs)
375    
376     This method will generate an error reply to the iq request C<Net::XMPP2::Node>
377     in C<$req_iq_node>.
378    
379     C<$error_type> is one of 'cancel', 'continue', 'modify', 'auth' and 'wait'.
380     C<$error> is one of the defined error conditions described in
381     L<Net::XMPP2::Writer::write_error_tag>.
382    
383     Please take a look at the documentation for C<send_iq> in Net::XMPP2::Writer
384 elmex 1.6 about the meaning of C<%attrs>.
385 elmex 1.4
386     The type for this iq reply is 'error'.
387    
388     =cut
389    
390     sub reply_iq_error {
391     my ($self, $iqnode, $errtype, $error, %attrs) = @_;
392    
393     $self->{writer}->send_iq (
394     $iqnode->attr ('id'), 'error',
395     sub { $self->{writer}->write_error_tag ($iqnode, $errtype, $error) },
396     %attrs
397     );
398 elmex 1.1 }
399    
400     sub handle_iq {
401     my ($self, $node) = @_;
402    
403 elmex 1.4 my $type = $node->attr ('type');
404    
405     if ($type eq 'result') {
406     if (my $cb = delete $self->{iqs}->{$node->attr ('id')}) {
407 elmex 1.1 $cb->($node);
408     }
409 elmex 1.9
410 elmex 1.4 } elsif ($type eq 'error') {
411     if (my $cb = delete $self->{iqs}->{$node->attr ('id')}) {
412 elmex 1.1
413 elmex 1.10 my $error = Net::XMPP2::Error::IQ->new (node => $node);
414     $cb->(($error->type eq 'continue' ? $node : undef), $error);
415 elmex 1.1 }
416 elmex 1.4
417     } else {
418     my $handled = 0;
419 elmex 1.6 $self->event ("iq_${type}_request_xml" => $node, \$handled);
420 elmex 1.4
421     my @from;
422     push @from, (to => $node->attr ('from')) if $node->attr ('from');
423    
424     unless ($handled) {
425 elmex 1.5 $self->reply_iq_error ($node, undef, 'feature-not-implemented', @from);
426 elmex 1.4 }
427 elmex 1.1 }
428     }
429    
430 elmex 1.12 sub send_sasl_auth {
431     my ($self, @mechs) = @_;
432     $self->{writer}->send_sasl_auth (
433     (join ' ', map { $_->text } @mechs),
434     $self->{username}, $self->{domain}, $self->{password}
435     );
436     }
437    
438 elmex 1.1 sub handle_stream_features {
439     my ($self, $node) = @_;
440     my @mechs = $node->find_all ([qw/sasl mechanisms/], [qw/sasl mechanism/]);
441     my @bind = $node->find_all ([qw/bind bind/]);
442 elmex 1.2 my @tls = $node->find_all ([qw/tls starttls/]);
443 elmex 1.12 my @reg = $node->find_all ([qw/register register/]);
444 elmex 1.1
445 elmex 1.5 if (not ($self->{disable_ssl}) && not ($self->{ssl_enabled}) && @tls) {
446 elmex 1.2 $self->{writer}->send_starttls;
447    
448 elmex 1.12 } elsif (not $self->{authenticated}) {
449     if ($self->{register} and @reg) {
450     # XXX: Continue here!!!
451     $self->do_in_band_register (sub {
452     my ($self, $error) = @_;
453     if ($error) {
454     $self->event (in_band_register_error => $error);
455     } else {
456     $self->event ('in_band_register_successful');
457     $self->send_sasl_auth (@mechs) if @mechs;
458     }
459     });
460     } else {
461     $self->send_sasl_auth (@mechs) if @mechs;
462     }
463 elmex 1.1
464     } elsif (@bind) {
465     $self->do_rebind ($self->{resource});
466     }
467     }
468    
469     sub handle_sasl_challenge {
470     my ($self, $node) = @_;
471     $self->{writer}->send_sasl_response ($node->text);
472     }
473    
474     sub handle_sasl_success {
475     my ($self, $node) = @_;
476     $self->{authenticated} = 1;
477     $self->{parser}->init;
478     $self->{writer}->init;
479     $self->{writer}->send_init_stream ($self->{language}, $self->{domain});
480     }
481    
482     sub handle_error {
483     my ($self, $node) = @_;
484 elmex 1.10 my $error = Net::XMPP2::Error::Stream->new (node => $node);
485    
486     $self->event (stream_error => $error);
487 elmex 1.1 $self->{writer}->send_end_of_stream;
488     }
489    
490 elmex 1.4 =head2 send_presence ($type, $create_cb, %attrs)
491    
492     This method sends a presence stanza, for the meanings
493     of C<$type>, C<$create_cb> and C<%attrs> please take a look
494     at the documentation for L<Net::XMPP2::Writer::send_presence>.
495    
496     This methods does attach an id attribute to the message stanza and
497     will return the id that was used (so you can react on possible replies).
498    
499     =cut
500    
501     sub send_presence {
502     my ($self, $type, $create_cb, %attrs) = @_;
503     my $id = $self->{iq_id}++;
504     $self->{writer}->send_presence ($id, $type, $create_cb, %attrs);
505     $id
506     }
507    
508     =head2 send_message ($to, $type, $create_cb, %attrs)
509    
510     This method sends a presence stanza, for the meanings
511     of C<$to>, C<$type>, C<$create_cb> and C<%attrs> please take a look
512     at the documentation for L<Net::XMPP2::Writer::send_message>.
513    
514     This methods does attach an id attribute to the message stanza and
515     will return the id that was used (so you can react on possible replies).
516    
517     =cut
518    
519     sub send_message {
520     my ($self, $to, $type, $create_cb, %attrs) = @_;
521     my $id = $self->{iq_id}++;
522     $self->{writer}->send_message ($id, $to, $type, $create_cb, %attrs);
523     $id
524     }
525    
526 elmex 1.1 =head2 do_rebind ($resource)
527    
528     In case you got a C<bind_error> event and want to retry
529     binding you can call this function to set a new C<$resource>
530     and retry binding.
531    
532     If it fails again you can call this again. Becareful not to
533     end up in a loop!
534    
535     If binding was successful the C<stream_ready> event will be generated.
536    
537     =cut
538    
539     sub do_rebind {
540     my ($self, $resource) = @_;
541     $self->{resource} = $resource;
542     $self->send_iq (
543     set =>
544     sub {
545     my ($w) = @_;
546     if ($self->{resource}) {
547     $w->startTag ([xmpp_ns ('bind'), 'bind']);
548     $w->startTag ([xmpp_ns ('bind'), 'resource']);
549     $w->characters ($self->{resource});
550     $w->endTag;
551     $w->endTag;
552     } else {
553     $w->emptyTag ([xmpp_ns ('bind'), 'bind'])
554     }
555     },
556     sub {
557 elmex 1.10 my ($ret_iq, $error, $err) = @_;
558 elmex 1.1
559     if ($err) {
560 elmex 1.10 my ($res) = $error->xml_node ()->find_all ([qw/bind bind/], [qw/bind resource/]);
561     $self->event (bind_error => $error, ($res ? $res : $self->{resource}));
562 elmex 1.1
563     } else {
564     my @jid = $ret_iq->find_all ([qw/bind bind/], [qw/bind jid/]);
565     my $jid = $jid[0]->text;
566     unless ($jid) { die "Got empty JID tag from server!\n" }
567     $self->{jid} = $jid;
568    
569     $self->event (stream_ready => $jid);
570     }
571     }
572     );
573     }
574    
575     =head2 jid
576    
577     After the stream has been bound to a resource the JID can be retrieved via this
578     method.
579    
580     =cut
581    
582     sub jid { $_[0]->{jid} }
583    
584 elmex 1.4 =head2 features
585    
586     Returns the last received <features> tag in form of an L<Net::XMPP2::Node> object.
587    
588     =cut
589    
590     sub features { $_[0]->{features} }
591    
592     #sub enable_extension {
593     # my ($self, @exts) = @_;
594     # for (@exts) {
595     # if (/^xep-(\d+)$/i) {
596     # $self->{ext}->{''.(1*$1)} = 1;
597     # }
598     # }
599     #}
600     #
601     #sub check_extension {
602     # my ($self, $extnum) = @_;
603     # return $self->{ext}->{"$extnum"} || $Net::XMPP2::EXTENSION_ENABLED{"$extnum"};
604     #}
605    
606 elmex 1.1 =head1 EVENTS
607    
608     These events can be registered on with C<reg_cb>:
609    
610     =over 4
611    
612 elmex 1.11 =item stream_features => $node
613 elmex 1.1
614 elmex 1.4 This event is sent when a stream feature (<features>) tag is received. C<$node> is the
615     L<Net::XMPP2::Node> object that represents the <features> tag.
616 elmex 1.1
617     =item stream_ready => $jid
618    
619     This event is sent if the XML stream has been established (and
620     resources have been bound) and is ready for transmitting regular stanzas.
621    
622     C<$jid> is the bound jabber id.
623    
624 elmex 1.10 =item stream_error => $error
625 elmex 1.6
626     This event is sent if a XML stream error occured. C<$error>
627 elmex 1.10 is a L<Net::XMPP2::Error::Stream> object.
628 elmex 1.6
629 elmex 1.11 =item tls_error
630    
631     This event is emitted when a TLS error occured on TLS negotiation.
632     After this the connection will be disconnected.
633    
634     =item sasl_error => $error
635    
636     This event is emitted on SASL authentication error.
637    
638 elmex 1.10 =item bind_error => $error, $resource
639 elmex 1.6
640 elmex 1.10 This event is generated when the stream was unable to bind to
641     any or the in C<new> specified resource. C<$error> is a L<Net::XMPP2::Error::IQ>
642     object. C<$resource> is the errornous resource string or undef if none
643     was received.
644 elmex 1.6
645 elmex 1.10 The C<condition> of the C<$error> might be one of: 'bad-request',
646     'not-allowed' or 'conflict'.
647 elmex 1.1
648     Node: this is untested, i couldn't get the server to send a bind error
649     to test this.
650    
651     =item connect => $host, $port
652    
653     This event is generated when a successful connect was performed to
654     the domain passed to C<new>.
655    
656     Note: C<$host> and C<$port> might be different from the domain you passed to
657     C<new> if C<connect> performed a SRV RR lookup.
658    
659     If this connection is lost a C<disconnect> will be generated with the same
660     C<$host> and C<$port>.
661    
662     =item disconnect => $host, $port, $message
663    
664     This event is generated when the connection was lost or another error
665     occured while writing or reading from it.
666    
667     C<$message> is a humand readable error message for the failure.
668     C<$host> and C<$port> were the host and port we were connected to.
669    
670     Note: C<$host> and C<$port> might be different from the domain you passed to
671     C<new> if C<connect> performed a SRV RR lookup.
672    
673 elmex 1.6 =item presence_xml => $node
674 elmex 1.4
675     This event is sent when a presence stanza is received. C<$node> is the
676     L<Net::XMPP2::Node> object that represents the <presence> tag.
677    
678 elmex 1.6 =item message_xml => $node
679 elmex 1.4
680     This event is sent when a message stanza is received. C<$node> is the
681     L<Net::XMPP2::Node> object that represents the <message> tag.
682    
683 elmex 1.6 =item iq_set_request_xml => $node, $handled_ref
684 elmex 1.4
685 elmex 1.6 =item iq_get_request_xml => $node, $handled_ref
686 elmex 1.4
687     These events are sent when an iq request stanza of type 'get' or 'set' is received.
688     C<$type> will either be 'get' or 'set' and C<$node> will be the L<Net::XMPP2::Node>
689     object of the iq tag.
690    
691     If C<$$handled_ref> is true an event handler should not handle this message anymore.
692    
693     If one of the event handlers handled this message the scalar pointed at by
694     the reference in C<$handled_ref> should be set to 1 true value. If C<$$handled_ref>
695     is still false after all event handlers were executed an error iq will be generated.
696    
697 elmex 1.1 =back
698    
699     =head1 AUTHOR
700    
701     Robin Redeker, C<< <elmex at ta-sa.org> >>
702    
703     =head1 COPYRIGHT & LICENSE
704    
705     Copyright 2007 Robin Redeker, all rights reserved.
706    
707     This program is free software; you can redistribute it and/or modify it
708     under the same terms as Perl itself.
709    
710     =cut
711    
712     1; # End of Net::XMPP2