ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Net-XMPP2/lib/Net/XMPP2/Connection.pm
Revision: 1.5
Committed: Fri Feb 2 23:24:39 2007 UTC (19 years, 7 months ago) by elmex
Branch: MAIN
Changes since 1.4: +25 -11 lines
Log Message:
lots of changes. added roster retrival

File Contents

# User Rev Content
1 elmex 1.1 package Net::XMPP2::Connection;
2     use warnings;
3     use strict;
4     use AnyEvent;
5     use IO::Socket::INET;
6     use Net::XMPP2::Parser;
7     use Net::XMPP2::Writer;
8     use Net::XMPP2::Util;
9     use Net::XMPP2::Namespaces qw/xmpp_ns/;
10     use Net::DNS;
11 elmex 1.2 use Net::SSLeay;
12    
13     BEGIN {
14     Net::SSLeay::load_error_strings ();
15     Net::SSLeay::SSLeay_add_ssl_algorithms ();
16     Net::SSLeay::randomize ();
17     }
18 elmex 1.1
19     our @ISA = qw/Net::XMPP2::SimpleConnection/;
20    
21     =head1 NAME
22    
23     Net::XMPP2::Connection - A XML stream that implements the XMPP RFC 3920.
24    
25     =head1 SYNOPSIS
26    
27     use Net::XMPP2::Connection;
28    
29     my $con =
30     Net::XMPP2::Connection->new (
31     username => "abc",
32     domain => "jabber.org",
33     resource => "Net::XMPP2"
34     );
35    
36     $con->connect or die "Couldn't connect to jabber.org: $!";
37     $con->init;
38     $con->reg_cb (stream_ready => sub { print "XMPP stream ready!\n" });
39    
40     =head1 DESCRIPTION
41    
42     This module represents a XMPP stream as described in RFC 3920. You can issue the basic
43     XMPP XML stanzas with methods like C<send_iq>, C<send_message> and C<send_presence>.
44    
45     And receive events with the C<reg_cb> event framework from the connection.
46    
47     If you need instant messaging stuff please take a look at C<Net::XMPP2::IM::Connection>.
48    
49     =head1 METHODS
50    
51     =head2 new (%args)
52    
53     Following arguments can be passed in C<%args>:
54    
55     =over 4
56    
57     =item language => $tag
58    
59     This should be the language of the human readable contents that
60     will be transmitted over the stream. The default will be 'en'.
61    
62     Please look in RFC 3066 how C<$tag> should look like.
63    
64     =item resource => $resource
65    
66     If this argument is given C<$resource> will be passed as desired
67     resource on resource binding.
68    
69     Note: You have to take care that the stringprep profile for
70     resources can be applied at: C<$resource>. Otherwise the server
71     might signal an error. See L<Net::XMPP2::Util> for utility functions
72     to check this.
73    
74     =item domain => $domain
75    
76     This is the destination host we are going to connect to.
77     As the connection won't be automatically connected use C<connect>
78     to initiate the connect.
79    
80     Note: A SRV RR lookup will be performed to discover the real hostname
81     and port to connect to. See also C<connect>.
82    
83     =item port => $port
84    
85     This is optional, the default port is 5222.
86    
87     Note: A SRV RR lookup will be performed to discover the real hostname
88     and port to connect to. See also C<connect>.
89    
90     =item username => $username
91    
92     This is your C<$username> (the userpart in the JID);
93    
94     Note: You have to take care that the stringprep profile for
95     nodes can be applied at: C<$username>. Otherwise the server
96     might signal an error. See L<Net::XMPP2::Util> for utility functions
97     to check this.
98    
99     =item password => $password
100    
101     This is the password for the C<username> above.
102    
103 elmex 1.5 =item disable_ssl => $bool
104    
105     If C<$bool> is true no SSL will be used.
106    
107 elmex 1.1 =back
108    
109     =cut
110    
111     sub new {
112     my $this = shift;
113     my $class = ref($this) || $this;
114     my $self = { language => 'en', @_ };
115     bless $self, $class;
116    
117     $self->{parser} = new Net::XMPP2::Parser;
118     $self->{writer} = Net::XMPP2::Writer->new (
119     write_cb => sub { $self->write_data ($_[0]) }
120     );
121    
122     $self->{parser}->set_stanza_cb (sub {
123     $self->handle_stanza (@_);
124     });
125    
126     $self->{iq_id} = 1;
127    
128     $self->{disconnect_cb} = sub {
129     my ($host, $port, $message) = @_;
130     $self->event (disconnect => $host, $port, $message);
131     };
132    
133     return $self;
134     }
135    
136     =head2 connect ($no_srv_rr)
137    
138     Try to connect to the domain and port passed in C<new>.
139    
140     A SRV RR lookup will be performed on the domain to discover
141     the host and port to use. If you don't want this set C<$no_srv_rr>
142     to a true value. C<$no_srv_rr> is false by default.
143    
144     As the SRV RR lookup might return multiple host and you fail to
145     connect to one you might just call this function again to try a
146     different host.
147    
148     If C<connect> was successful and we connected a true value is returned.
149     If the connect was unsuccessful undef is returned and C<$!> will be set
150     to the error that occured while connecting.
151    
152     If you want to know whether further connection attempts might be more
153     successful (as SRV RR lookup may return multiple hosts) call C<may_try_connect>
154     (see also C<may_try_connect>).
155    
156     Note that an internal list will be kept of tried hosts. Use
157     C<reset_connect_tries> to reset the internal list of tried hosts.
158    
159     =cut
160    
161     sub connect {
162     my ($self, $no_srv_rr) = @_;
163    
164     my ($host, $port) = ($self->{domain}, $self->{port} || 5222);
165    
166     unless ($no_srv_rr) {
167     my $res = Net::DNS::Resolver->new;
168     my $p = $res->query ('_xmpp-client._tcp.'.$host, 'SRV');
169     if ($p) {
170     my @srvs = grep { $_->type eq 'SRV' } $p->answer;
171     if (@srvs) {
172     @srvs = sort { $a->priority <=> $b->priority } @srvs;
173     @srvs = sort { $b->weight <=> $a->weight } @srvs; # TODO
174     $port = $srvs[0]->port;
175     $host = $srvs[0]->target;
176     }
177     }
178     }
179    
180     if ($self->SUPER::connect ($host, $port)) {
181     $self->event (connect => $host, $port);
182     return 1;
183     } else {
184     return undef;
185     }
186     }
187    
188     =head2 may_try_connect
189    
190     Returns the number of left alternatives of hosts to connect to for the
191     domain passed to C<new>.
192    
193     An internal list of tried hosts will be managed by C<connect> and those
194     hosts will be ignored by a SRV RR lookup (which will be done if you
195     call this function).
196    
197     Use C<reset_connect_tries> to reset the internal list of tried hosts.
198    
199     =cut
200    
201     sub may_try_connect {
202     # TODO
203     }
204    
205     =head2 reset_connect_tries
206    
207     This function resets the internal list of tried hosts for C<connect>.
208     See also C<connect>.
209    
210     =cut
211    
212     sub reset_connect_tries {
213     # TODO
214     }
215    
216     sub handle_data {
217     my ($self, $buf) = @_;
218     $self->event (debug_recv => $$buf);
219     $self->{parser}->feed (substr $$buf, 0, (length $$buf), '');
220     }
221    
222 elmex 1.5 sub debug_wrote_data {
223     my ($self, $data) = @_;
224     $self->event (debug_send => $data);
225     }
226    
227 elmex 1.1 sub write_data {
228     my ($self, $data) = @_;
229     $self->SUPER::write_data ($data);
230     }
231    
232     =item reg_cb ($eventname1, $cb1, [$eventname2, $cb2, ...])
233    
234     This method registers a callback C<$cb1> for the event with the
235     name C<$eventname1>. You can also pass multiple of these eventname => callback
236     pairs.
237    
238     To see a documentation of emitted events please take a look at the EVENTS section
239     below.
240    
241     =cut
242    
243     sub reg_cb {
244     my ($self, %regs) = @_;
245    
246     for my $cmd (keys %regs) {
247     my $cb = $regs{$cmd};
248     push @{$self->{events}->{$cmd}}, $cb;
249     }
250    
251     1;
252     }
253    
254     sub event {
255     my ($self, $ev, @arg) = @_;
256    
257     my $nxt = [];
258    
259 elmex 1.4 my $handled;
260 elmex 1.1 for (@{$self->{events}->{lc $ev}}) {
261     $_->($self, @arg) and push @$nxt, $_;
262     }
263    
264     $self->{events}->{lc $ev} = $nxt;
265     }
266    
267     sub handle_stanza {
268     my ($self, $p, $node) = @_;
269    
270     if ($node->eq (stream => 'features')) {
271     $self->event (stream_features => $node);
272     $self->handle_stream_features ($node);
273 elmex 1.4 $self->{features} = $node;
274 elmex 1.5
275 elmex 1.2 } elsif ($node->eq (tls => 'proceed')) {
276     $self->enable_ssl;
277     $self->{parser}->init;
278     $self->{writer}->init;
279     $self->{writer}->send_init_stream ($self->{language}, $self->{domain});
280    
281 elmex 1.1 } elsif ($node->eq (sasl => 'challenge')) {
282     $self->handle_sasl_challenge ($node);
283     } elsif ($node->eq (sasl => 'success')) {
284     $self->handle_sasl_success ($node);
285     } elsif ($node->eq (client => 'iq')) {
286     $self->handle_iq ($node);
287 elmex 1.4 } elsif ($node->eq (client => 'message')) {
288     $self->event (message => $node);
289     } elsif ($node->eq (client => 'presence')) {
290     $self->event (presence => $node);
291 elmex 1.1 } elsif ($node->eq (stream => 'error')) {
292     $self->handle_error ($node);
293     } else {
294     warn "Didn't understood stanza: '" . $node->name . "'";
295     }
296     }
297    
298     =head2 init ($domain)
299    
300     Initiate the XML stream.
301    
302     =cut
303    
304     sub init {
305     my ($self) = @_;
306     $self->{writer}->send_init_stream ($self->{language}, $self->{domain});
307     }
308    
309     =head2 send_iq ($type, $create_cb, $result_cb, %attrs)
310    
311     This method sends an IQ XMPP request.
312    
313     Please take a look at the documentation for C<send_iq> in Net::XMPP2::Writer
314     about the meaning of C<$type>, C<$create_cb> and C<%attrs>.
315    
316 elmex 1.4 C<$result_cb> will be called when a result was received. The first argument to
317     C<$result_cb> will be a Net::XMPP2::Node instance containing the IQ result
318     stanza contents.
319 elmex 1.1
320     If the IQ resulted in a stanza error the second argument to C<$result_cb> will
321     be C<undef> (if the error type was not 'continue') and the third argument will
322     be a Net::XMPP2::Node containg the IQ error stanza. And the fourth argument
323     will be a array reference with following contents:
324    
325 elmex 1.4 This method returns the newly generated id for this iq request.
326    
327 elmex 1.1 =over 4
328    
329     =item index 0: error type
330    
331     This will be one of: 'cancel', 'continue', 'modify', 'auth' and 'wait'.
332    
333     =item index 1: error condition element
334    
335     This might be undefined if other XMPP speakers don't play nice i guess.
336    
337     =item index 2: error text
338    
339     This will be the human readable form of the error which is maybe undef if
340     not supplied.
341    
342     =item index 3: error code
343    
344     If the error element had an 'code' attribute it will be put here,
345     the RFC says that this is for backward compatibility :)
346    
347     =back
348    
349     =cut
350    
351     sub send_iq {
352     my ($self, $type, $create_cb, $result_cb, %attrs) = @_;
353     my $id = $self->{iq_id}++;
354     $self->{iqs}->{$id} = $result_cb;
355     $self->{writer}->send_iq ($id, $type, $create_cb, %attrs);
356 elmex 1.4 $id
357     }
358    
359     =head2 reply_iq_result ($req_iq_node, $create_cb, %attrs)
360    
361     This method will generate a result reply to the iq request C<Net::XMPP2::Node>
362     in C<$req_iq_node>.
363    
364     Please take a look at the documentation for C<send_iq> in Net::XMPP2::Writer
365     about the meaning C<$create_cb> and C<%attrs>.
366    
367     The type for this iq reply is 'result'.
368    
369     =cut
370    
371     sub reply_iq_result {
372     my ($self, $iqnode, $create_cb, %attrs) = @_;
373     $self->{writer}->send_iq ($iqnode->attr ('id'), 'result', $create_cb, %attrs);
374     }
375    
376     =head2 reply_iq_error ($req_iq_node, $error_type, $error, %attrs)
377    
378     This method will generate an error reply to the iq request C<Net::XMPP2::Node>
379     in C<$req_iq_node>.
380    
381     C<$error_type> is one of 'cancel', 'continue', 'modify', 'auth' and 'wait'.
382     C<$error> is one of the defined error conditions described in
383     L<Net::XMPP2::Writer::write_error_tag>.
384    
385     Please take a look at the documentation for C<send_iq> in Net::XMPP2::Writer
386     about the meaning C<$create_cb> and C<%attrs>.
387    
388     The type for this iq reply is 'error'.
389    
390     =cut
391    
392     sub reply_iq_error {
393     my ($self, $iqnode, $errtype, $error, %attrs) = @_;
394    
395     $self->{writer}->send_iq (
396     $iqnode->attr ('id'), 'error',
397     sub { $self->{writer}->write_error_tag ($iqnode, $errtype, $error) },
398     %attrs
399     );
400 elmex 1.1 }
401    
402     sub handle_iq {
403     my ($self, $node) = @_;
404    
405 elmex 1.4 my $type = $node->attr ('type');
406    
407     if ($type eq 'result') {
408     if (my $cb = delete $self->{iqs}->{$node->attr ('id')}) {
409 elmex 1.1 $cb->($node);
410     }
411 elmex 1.4 } elsif ($type eq 'error') {
412     if (my $cb = delete $self->{iqs}->{$node->attr ('id')}) {
413 elmex 1.1
414     my $error = $self->filter_error_stanza ($node);
415     $cb->(($error->[0] eq 'continue' ? $node : undef), $node, $error);
416     }
417 elmex 1.4
418     } else {
419     my $handled = 0;
420     $self->event ("iq_${type}_request" => $node, \$handled);
421    
422     my @from;
423     push @from, (to => $node->attr ('from')) if $node->attr ('from');
424    
425     unless ($handled) {
426 elmex 1.5 $self->reply_iq_error ($node, undef, 'feature-not-implemented', @from);
427 elmex 1.4 }
428 elmex 1.1 }
429     }
430    
431     sub filter_error_stanza {
432     my ($self, $node) = @_;
433     my $p = $self->{parser};
434     my @error;
435     my ($err) = $node->find_all ([qw/client error/]);
436     $error[0] = $err->attr ('type');
437     $error[3] = $err->attr ('code');
438     if ($err) {
439     if (my ($txt) = $err->find_all ([qw/stanzas text/])) {
440     $error[2] = $txt->text;
441     }
442     for my $er (
443     qw/bad-request conflict feature-not-implemented forbidden
444     gone internal-server-error item-not-found jid-malformed
445     not-acceptable not-allowed not-authorized payment-required
446     recipient-unavailable redirect registration-required
447     remote-server-not-found remote-server-timeout resource-constraint
448     service-unavailable subscription-required undefined-condition
449     unexpected-request/)
450     {
451     if (my ($el) = $err->find_all ([stanzas => $er])) {
452     $error[1] = $el;
453     last;
454     }
455     }
456     } else {
457     warn "no error element found in error stanza!";
458     }
459     return \@error
460     }
461    
462     sub handle_stream_features {
463     my ($self, $node) = @_;
464     my @mechs = $node->find_all ([qw/sasl mechanisms/], [qw/sasl mechanism/]);
465     my @bind = $node->find_all ([qw/bind bind/]);
466 elmex 1.2 my @tls = $node->find_all ([qw/tls starttls/]);
467 elmex 1.1
468 elmex 1.5 if (not ($self->{disable_ssl}) && not ($self->{ssl_enabled}) && @tls) {
469 elmex 1.2 $self->{writer}->send_starttls;
470    
471     } elsif (not ($self->{authenticated}) and @mechs) {
472 elmex 1.1 $self->{writer}->send_sasl_auth (
473     (join ' ', map { $_->text } @mechs),
474     $self->{username}, $self->{domain}, $self->{password}
475     );
476    
477     } elsif (@bind) {
478     $self->do_rebind ($self->{resource});
479     }
480     }
481    
482     sub handle_sasl_challenge {
483     my ($self, $node) = @_;
484     $self->{writer}->send_sasl_response ($node->text);
485     }
486    
487     sub handle_sasl_success {
488     my ($self, $node) = @_;
489     $self->{authenticated} = 1;
490     $self->{parser}->init;
491     $self->{writer}->init;
492     $self->{writer}->send_init_stream ($self->{language}, $self->{domain});
493     }
494    
495     sub handle_error {
496     my ($self, $node) = @_;
497     my @txt = $node->find_all ([qw/stream text/]);
498     my $error;
499     for my $er (
500     qw/bad-format bad-namespace-prefix conflict connection-timeout host-gone
501     host-unknown improper-addressing internal-server-error invalid-from
502     invalid-id invalid-namespace invalid-xml not-authorized policy-violation
503     remote-connection-failed resource-constraint restricted-xml
504     see-other-host system-shutdown undefined-condition unsupported-stanza-type
505     unsupported-version xml-not-well-formed/)
506     {
507     for ($node->nodes) {
508     if ($node->eq (streams => $er)) {
509     $error = $_->name;
510     last
511     }
512     }
513     }
514     unless ($error) {
515     warn "got undefined error stanza, trying to find any undefined error...";
516     for ($node->nodes) {
517     if ($node->eq_ns ('streams')) {
518     $error = $node->name;
519     }
520     }
521     }
522     $self->event (stream_error => $error, (@txt ? $txt[0]->text : ''));
523     $self->{writer}->send_end_of_stream;
524     }
525    
526 elmex 1.4 =head2 send_presence ($type, $create_cb, %attrs)
527    
528     This method sends a presence stanza, for the meanings
529     of C<$type>, C<$create_cb> and C<%attrs> please take a look
530     at the documentation for L<Net::XMPP2::Writer::send_presence>.
531    
532     This methods does attach an id attribute to the message stanza and
533     will return the id that was used (so you can react on possible replies).
534    
535     =cut
536    
537     sub send_presence {
538     my ($self, $type, $create_cb, %attrs) = @_;
539     my $id = $self->{iq_id}++;
540     $self->{writer}->send_presence ($id, $type, $create_cb, %attrs);
541     $id
542     }
543    
544     =head2 send_message ($to, $type, $create_cb, %attrs)
545    
546     This method sends a presence stanza, for the meanings
547     of C<$to>, C<$type>, C<$create_cb> and C<%attrs> please take a look
548     at the documentation for L<Net::XMPP2::Writer::send_message>.
549    
550     This methods does attach an id attribute to the message stanza and
551     will return the id that was used (so you can react on possible replies).
552    
553     =cut
554    
555     sub send_message {
556     my ($self, $to, $type, $create_cb, %attrs) = @_;
557     my $id = $self->{iq_id}++;
558     $self->{writer}->send_message ($id, $to, $type, $create_cb, %attrs);
559     $id
560     }
561    
562 elmex 1.1 =head2 do_rebind ($resource)
563    
564     In case you got a C<bind_error> event and want to retry
565     binding you can call this function to set a new C<$resource>
566     and retry binding.
567    
568     If it fails again you can call this again. Becareful not to
569     end up in a loop!
570    
571     If binding was successful the C<stream_ready> event will be generated.
572    
573     =cut
574    
575     sub do_rebind {
576     my ($self, $resource) = @_;
577     $self->{resource} = $resource;
578     $self->send_iq (
579     set =>
580     sub {
581     my ($w) = @_;
582     if ($self->{resource}) {
583     $w->startTag ([xmpp_ns ('bind'), 'bind']);
584     $w->startTag ([xmpp_ns ('bind'), 'resource']);
585     $w->characters ($self->{resource});
586     $w->endTag;
587     $w->endTag;
588     } else {
589     $w->emptyTag ([xmpp_ns ('bind'), 'bind'])
590     }
591     },
592     sub {
593     my ($ret_iq, $err_iq, $err) = @_;
594    
595     if ($err) {
596     my ($res) = $err_iq->find_all ([qw/bind bind/], [qw/bind resource/]);
597     $self->event (bind_error => $err->[0], ($res ? $res : $self->{resource}));
598    
599     } else {
600     my @jid = $ret_iq->find_all ([qw/bind bind/], [qw/bind jid/]);
601     my $jid = $jid[0]->text;
602     unless ($jid) { die "Got empty JID tag from server!\n" }
603     $self->{jid} = $jid;
604    
605     $self->event (stream_ready => $jid);
606     }
607     }
608     );
609     }
610    
611     =head2 jid
612    
613     After the stream has been bound to a resource the JID can be retrieved via this
614     method.
615    
616     =cut
617    
618     sub jid { $_[0]->{jid} }
619    
620 elmex 1.4 =head2 features
621    
622     Returns the last received <features> tag in form of an L<Net::XMPP2::Node> object.
623    
624     =cut
625    
626     sub features { $_[0]->{features} }
627    
628     #sub enable_extension {
629     # my ($self, @exts) = @_;
630     # for (@exts) {
631     # if (/^xep-(\d+)$/i) {
632     # $self->{ext}->{''.(1*$1)} = 1;
633     # }
634     # }
635     #}
636     #
637     #sub check_extension {
638     # my ($self, $extnum) = @_;
639     # return $self->{ext}->{"$extnum"} || $Net::XMPP2::EXTENSION_ENABLED{"$extnum"};
640     #}
641    
642 elmex 1.1 =head1 EVENTS
643    
644     These events can be registered on with C<reg_cb>:
645    
646     =over 4
647    
648     =item stream_features => $node
649    
650 elmex 1.4 This event is sent when a stream feature (<features>) tag is received. C<$node> is the
651     L<Net::XMPP2::Node> object that represents the <features> tag.
652 elmex 1.1
653     =item stream_ready => $jid
654    
655     This event is sent if the XML stream has been established (and
656     resources have been bound) and is ready for transmitting regular stanzas.
657    
658     C<$jid> is the bound jabber id.
659    
660     =item bind_error => $error_name, $resource
661    
662     This event is generated when the stream was unable to bind to
663     any or the in C<new> specified resource. C<$error_name>
664     may be 'bad-request', 'not-allowed' or 'conflict'.
665    
666     Node: this is untested, i couldn't get the server to send a bind error
667     to test this.
668    
669     =item connect => $host, $port
670    
671     This event is generated when a successful connect was performed to
672     the domain passed to C<new>.
673    
674     Note: C<$host> and C<$port> might be different from the domain you passed to
675     C<new> if C<connect> performed a SRV RR lookup.
676    
677     If this connection is lost a C<disconnect> will be generated with the same
678     C<$host> and C<$port>.
679    
680     =item disconnect => $host, $port, $message
681    
682     This event is generated when the connection was lost or another error
683     occured while writing or reading from it.
684    
685     C<$message> is a humand readable error message for the failure.
686     C<$host> and C<$port> were the host and port we were connected to.
687    
688     Note: C<$host> and C<$port> might be different from the domain you passed to
689     C<new> if C<connect> performed a SRV RR lookup.
690    
691 elmex 1.4 =item presence => $node
692    
693     This event is sent when a presence stanza is received. C<$node> is the
694     L<Net::XMPP2::Node> object that represents the <presence> tag.
695    
696     =item message => $node
697    
698     This event is sent when a message stanza is received. C<$node> is the
699     L<Net::XMPP2::Node> object that represents the <message> tag.
700    
701     =item iq_set_request => $node, $handled_ref
702    
703     =item iq_get_request => $node, $handled_ref
704    
705     These events are sent when an iq request stanza of type 'get' or 'set' is received.
706     C<$type> will either be 'get' or 'set' and C<$node> will be the L<Net::XMPP2::Node>
707     object of the iq tag.
708    
709     If C<$$handled_ref> is true an event handler should not handle this message anymore.
710    
711     If one of the event handlers handled this message the scalar pointed at by
712     the reference in C<$handled_ref> should be set to 1 true value. If C<$$handled_ref>
713     is still false after all event handlers were executed an error iq will be generated.
714    
715 elmex 1.1 =back
716    
717     =head1 AUTHOR
718    
719     Robin Redeker, C<< <elmex at ta-sa.org> >>
720    
721     =head1 BUGS
722    
723     Please report any bugs or feature requests to
724     C<bug-net-xmpp2 at rt.cpan.org>, or through the web interface at
725     L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Net-XMPP2>.
726     I will be notified, and then you'll automatically be notified of progress on
727     your bug as I make changes.
728    
729     =head1 SUPPORT
730    
731     You can find documentation for this module with the perldoc command.
732    
733     perldoc Net::XMPP2
734    
735     You can also look for information at:
736    
737     =over 4
738    
739     =item * AnnoCPAN: Annotated CPAN documentation
740    
741     L<http://annocpan.org/dist/Net-XMPP2>
742    
743     =item * CPAN Ratings
744    
745     L<http://cpanratings.perl.org/d/Net-XMPP2>
746    
747     =item * RT: CPAN's request tracker
748    
749     L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Net-XMPP2>
750    
751     =item * Search CPAN
752    
753     L<http://search.cpan.org/dist/Net-XMPP2>
754    
755     =back
756    
757     =head1 ACKNOWLEDGEMENTS
758    
759     =head1 COPYRIGHT & LICENSE
760    
761     Copyright 2007 Robin Redeker, all rights reserved.
762    
763     This program is free software; you can redistribute it and/or modify it
764     under the same terms as Perl itself.
765    
766     =cut
767    
768     package Net::XMPP2::SimpleConnection;
769     use IO::Socket::INET;
770 elmex 1.2 use Errno;
771 elmex 1.1 use Fcntl;
772 elmex 1.2 use Encode;
773 elmex 1.1
774     sub new {
775     my $this = shift;
776     my $class = ref($this) || $this;
777     my $self = { disconnect_cb => sub {}, @_ };
778     bless $self, $class;
779     return $self;
780     }
781    
782 elmex 1.2 sub set_block {
783     my ($self) = @_;
784     my $flags = 0;
785     fcntl($self->{socket}, F_GETFL, $flags)
786     or die "Couldn't get flags for HANDLE : $!\n";
787     $flags &= ~O_NONBLOCK;
788     fcntl($self->{socket}, F_SETFL, $flags)
789     or die "Couldn't set flags for HANDLE: $!\n";
790     }
791    
792     sub set_noblock {
793     my ($self) = @_;
794     my $flags = 0;
795     fcntl($self->{socket}, F_GETFL, $flags)
796     or die "Couldn't get flags for HANDLE : $!\n";
797     $flags |= O_NONBLOCK;
798     fcntl($self->{socket}, F_SETFL, $flags)
799     or die "Couldn't set flags for HANDLE: $!\n";
800     }
801    
802 elmex 1.1 sub connect {
803     my ($self, $host, $port) = @_;
804    
805     $self->{socket}
806     and return 1;
807    
808     my $sock = IO::Socket::INET->new (
809     PeerAddr => $host,
810     PeerPort => $port,
811     Proto => 'tcp',
812     Blocking => 1
813     );
814     return undef unless $sock;;
815    
816     $self->{socket} = $sock;
817     $self->{host} = $host;
818     $self->{port} = $port;
819    
820 elmex 1.2 $self->set_noblock;
821 elmex 1.1
822     binmode $sock, ":utf8";
823    
824     $self->{r} =
825     AnyEvent->io (poll => 'r', fh => $sock, cb => sub {
826     my $l = sysread $sock, my $data, 1024;
827    
828 elmex 1.4 if ($l) {
829     $self->{read_buffer} .= $data;
830     $self->handle_data (\$self->{read_buffer});
831    
832     } else {
833 elmex 1.3 return if $! == Errno::EAGAIN;
834 elmex 1.1 if (defined $l) {
835 elmex 1.2 $self->{disconnect_cb}->($self->{host}, $self->{port}, "EOF from server '$self->{host}:$self->{port}'");
836     $self->end_sockets;
837 elmex 1.1 return;
838    
839     } else {
840 elmex 1.2 $self->{disconnect_cb}->($self->{host}, $self->{port}, "Error while reading from server '$self->{host}:$port': $!");
841     $self->end_sockets;
842 elmex 1.1 return;
843     }
844     }
845     });
846     return 1;
847     }
848    
849 elmex 1.2 sub end_sockets {
850     my ($self) = @_;
851     delete $self->{r};
852     delete $self->{w};
853     delete $self->{socket};
854 elmex 1.4 if (delete $self->{ssl_enabled}) {
855     Net::SSLeay::free ($self->{ssl});
856     delete $self->{ssl};
857     Net::SSLeay::CTX_free ($self->{ctx});
858     delete $self->{ctx};
859     }
860 elmex 1.2 }
861    
862     sub dumpbio {
863     my ($self) = @_;
864    
865     print "er: ".Net::SSLeay::BIO_should_retry (Net::SSLeay::get_rbio ($self->{ssl}));
866     print " ew: ".Net::SSLeay::BIO_should_retry (Net::SSLeay::get_wbio ($self->{ssl}));
867     print " rr: ".Net::SSLeay::BIO_should_read (Net::SSLeay::get_rbio ($self->{ssl}));
868     print " rw: ".Net::SSLeay::BIO_should_read (Net::SSLeay::get_wbio ($self->{ssl}));
869     print " wr: ".Net::SSLeay::BIO_should_write (Net::SSLeay::get_rbio ($self->{ssl}));
870     print " ww: ".Net::SSLeay::BIO_should_write (Net::SSLeay::get_wbio ($self->{ssl}))."\n";
871    
872     my $e = Net::SSLeay::BIO_should_retry (Net::SSLeay::get_wbio ($self->{ssl}))
873     | Net::SSLeay::BIO_should_retry (Net::SSLeay::get_wbio ($self->{ssl}));
874     my $w = Net::SSLeay::BIO_should_read (Net::SSLeay::get_wbio ($self->{ssl}))
875     | Net::SSLeay::BIO_should_write (Net::SSLeay::get_wbio ($self->{ssl}));
876     my $r = Net::SSLeay::BIO_should_read (Net::SSLeay::get_rbio ($self->{ssl}))
877     | Net::SSLeay::BIO_should_write (Net::SSLeay::get_rbio ($self->{ssl}));
878    
879     print "TEST:$e $w $r\n";
880     # delete $self->{r};
881     # delete $self->{w};
882     # if ($w) { $self->make_ssl_write_watcher }
883     # if ($r) { $self->make_ssl_read_watcher }
884     # unless ($e) {
885     # $self->make_ssl_read_watcher;
886     # $self->make_ssl_write_watcher;
887     # }
888     }
889    
890     sub try_ssl_write {
891     my ($self) = @_;
892 elmex 1.5
893     unless ($self->{ssl_out_buffer}) { # refill buffer
894     $self->{ssl_out_buffer} = $self->{write_buffer};
895     $self->{write_buffer} = "";
896     }
897    
898 elmex 1.2 unless ($self->{ssl_out_buffer}) {
899     delete $self->{w};
900     return;
901     }
902    
903     my $l = Net::SSLeay::write_nb ($self->{ssl},
904     $self->{ssl_out_buffer}, length ($self->{ssl_out_buffer}));
905    
906     if ($l <= 0) {
907     if ($l == 0) {
908     $self->{disconnect_cb}->($self->{host}, $self->{port},
909     "unexpected EOF from server (ssl) '$self->{host}:$self->{port}'");
910     $self->end_sockets;
911     return;
912    
913     } else {
914     my $err2 = Net::SSLeay::get_error $self->{ssl}, $l;
915     #d# warn "write err[$err2]\n"; $self->dumpbio;
916     if ($err2 == 2 || $err2 == 3) {
917     delete $self->{w};
918     $self->make_ssl_write_watcher ($err2 == 2 ? 'r' : 'w');
919     return;
920     }
921    
922 elmex 1.3 if ($! != Errno::EAGAIN
923 elmex 1.2 or my $err = Net::SSLeay::ERR_get_error) {
924    
925     $self->{disconnect_cb}->($self->{host}, $self->{port},
926     sprintf (
927     "Error while writing from server '$self->{host}:$self->{port}': (%d|%s|%s)",
928     $err2, (Net::SSLeay::ERR_error_string $err), "$!")
929     );
930     $self->end_sockets;
931     return;
932     }
933     }
934     $self->make_ssl_read_watcher;
935     return;
936 elmex 1.5 } else {
937     $self->debug_wrote_data (substr $self->{ssl_out_buffer}, 0, $l);
938     $self->{ssl_out_buffer} = substr $self->{ssl_out_buffer}, $l;
939 elmex 1.2 }
940     }
941    
942     sub try_ssl_read {
943     my ($self) = @_;
944     my $l = Net::SSLeay::read_nb ($self->{ssl}, $self->{ssl_read_data});
945    
946     if ($l <= 0) {
947     if ($l == 0) {
948     $self->{disconnect_cb}->($self->{host}, $self->{port},
949     "unexpected EOF from server (ssl) '$self->{host}:$self->{port}'");
950     $self->end_sockets;
951     return;
952    
953     } else {
954     my $err2 = Net::SSLeay::get_error $self->{ssl}, $l;
955     #d# warn "read err[$err2]\n"; $self->dumpbio;
956     if ($err2 == 2 || $err2 == 3) {
957     delete $self->{r};
958     $self->make_ssl_read_watcher ($err2 == 2 ? 'r' : 'w');
959     return;
960     }
961    
962 elmex 1.3 if ($! != Errno::EAGAIN
963 elmex 1.2 or my $err = Net::SSLeay::ERR_get_error) {
964    
965     $self->{disconnect_cb}->($self->{host}, $self->{port},
966     sprintf (
967     "Error while reading from server '$self->{host}:$self->{port}':"
968     ."(%d|%s|%s)",
969     $err2, (Net::SSLeay::ERR_error_string $err), "$!")
970     );
971     $self->end_sockets;
972     return;
973     }
974     }
975 elmex 1.4 } else {
976     $self->{read_buffer} .= decode_utf8 ($self->{ssl_read_data});
977     $self->handle_data (\$self->{read_buffer});
978     $self->{ssl_read_data} = "";
979 elmex 1.2 }
980    
981     }
982    
983     sub make_ssl_read_watcher {
984     my ($self, $poll) = @_;
985     return if $self->{r};
986    
987     $poll ||= 'r';
988     $self->{r} =
989 elmex 1.3 AnyEvent->io (poll => $poll, fh => $self->{socket}, cb => sub {
990     #d# warn "read cb [$poll]\n";
991     $self->try_ssl_read;
992     });
993 elmex 1.2 }
994    
995     sub make_ssl_write_watcher {
996     my ($self, $poll) = @_;
997     return if $self->{w};
998    
999     $poll ||= 'w';
1000     $self->{w} =
1001     AnyEvent->io (poll => $poll, fh => $self->{socket}, cb => sub {
1002 elmex 1.5 #warn "write cb [$poll]\n";
1003 elmex 1.2 $self->try_ssl_write;
1004 elmex 1.5 1;
1005 elmex 1.2 });
1006     }
1007    
1008 elmex 1.1 sub write_data {
1009     my ($self, $data) = @_;
1010 elmex 1.5 #return unless $self->{r};
1011 elmex 1.1
1012     my $cl = $self->{socket};
1013     $self->{write_buffer} .= $data;
1014    
1015     unless ($self->{w}) {
1016 elmex 1.2 if (not $self->{ssl_enabled}) {
1017     $self->{w} =
1018     AnyEvent->io (poll => 'w', fh => $cl, cb => sub {
1019     if (my $data = $self->{write_buffer}) {
1020     my $len = syswrite $cl, $data;
1021     unless ($len) {
1022 elmex 1.3 return if $! == Errno::EAGAIN;
1023 elmex 1.2 if (not defined $len) {
1024     warn "error when writing data on $self->{host}:$self->{port}: $!";
1025     return;
1026     } else {
1027     delete $self->{w};
1028     }
1029     }
1030    
1031     if ($len == length $self->{write_buffer}) {
1032 elmex 1.1 delete $self->{w};
1033     }
1034    
1035 elmex 1.5 $self->debug_wrote_data (substr $self->{write_buffer}, 0, $len);
1036 elmex 1.2 $self->{write_buffer} = substr $self->{write_buffer}, $len;
1037 elmex 1.1 }
1038 elmex 1.2 });
1039 elmex 1.1
1040 elmex 1.2 } else {
1041     unless ($self->{ssl_out_buffer}) {
1042     $self->{ssl_out_buffer} = encode_utf8 ($self->{write_buffer});
1043     $self->{write_buffer} = "";
1044     $self->make_ssl_write_watcher;
1045     }
1046     }
1047 elmex 1.1 }
1048     }
1049    
1050 elmex 1.2 sub enable_ssl {
1051     my ($self) = @_;
1052    
1053     $Net::SSLeay::ssl_version = 10; # Insist on TLSv1
1054    
1055     $self->{ssl_enabled} = 1;
1056    
1057     warn "START TLS!\n";
1058    
1059     $self->{r} = undef;
1060     $self->{w} = undef;
1061    
1062     $self->{ctx} = Net::SSLeay::CTX_new ();
1063     Net::SSLeay::CTX_set_mode($self->{ctx}, 1);
1064     $self->{ssl} = Net::SSLeay::new ($self->{ctx});
1065    
1066     Net::SSLeay::set_fd ($self->{ssl}, fileno $self->{socket});
1067     #d# warn "CONNECT\n";
1068     Net::SSLeay::connect $self->{ssl};
1069     #d# warn "CONNECT END\n";
1070     binmode $self->{socket}, ":bytes";
1071    
1072     $self->{ssl_read_data} = "";
1073    
1074     $self->make_ssl_read_watcher;
1075     }
1076    
1077 elmex 1.1 1; # End of Net::XMPP2