ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/Net-XMPP2/lib/Net/XMPP2/Connection.pm
Revision: 1.14
Committed: Tue Apr 24 18:11:09 2007 UTC (19 years, 5 months ago) by elmex
Branch: MAIN
Changes since 1.13: +0 -14 lines
Log Message:
removed extension mechanisms which don't seem neccessary
to me anymore. noone wants XMPP without the XEPs (at least not me).

File Contents

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