package Net::XMPP2::IM::Connection; use strict; use Net::XMPP2::Connection; use Net::XMPP2::Namespaces qw/xmpp_ns/; use Net::XMPP2::IM::Roster; use Net::XMPP2::IM::Message; our @ISA = qw/Net::XMPP2::Connection/; =head1 NAME Net::XMPP2::IM::Connection - A XML stream that implements the XMPP RFC 3921. =head1 SYNOPSIS use Net::XMPP2::Connection; my $con = Net::XMPP2::Connection->new; =head1 DESCRIPTION This module represents a XMPP instant messaging connection and implements RFC 3921. This module is a subclass of C and inherits all methods. For example C and the stanza sending routines. For additional events that can be registered to look below in the EVENTS section. =head1 METHODS =cut =head2 new (%args) This is the constructor. It takes the same arguments as the constructor of L along with a few others: =over 4 =item dont_retrieve_roster => $bool Set this to a true value if no roster should be requested on connection establishment. You can retrieve the roster later if you want to with the C method. The internal roster will be set even if this option is active, and even presences will be stored in there, except that the C method on the roster object won't return anything as there are no roster items. =back =cut sub new { my $this = shift; my $class = ref($this) || $this; my $self = $class->SUPER::new (@_); $self->{roster} = Net::XMPP2::IM::Roster->new (connection => $self); $self->reg_cb (message_xml => sub { shift @_; $self->handle_message (@_); 1 }); $self->reg_cb (presence_xml => sub { shift @_; $self->handle_presence (@_); 1 }); $self->reg_cb (iq_set_request_xml => sub { shift @_; $self->handle_iq_set (@_); 1 }); $self->reg_cb (disconnect => sub { shift @_; $self->handle_disconnect (@_); 1 }); $self->reg_cb (stream_ready => sub { my ($jid) = @_; if ($self->features ()->find_all ([qw/session session/])) { $self->send_session_iq; } else { $self->init_connection; } }); $self } sub send_session_iq { my ($self) = @_; $self->send_iq (set => sub { my ($w) = @_; $w->addPrefix (xmpp_ns ('session'), ''); $w->emptyTag ([xmpp_ns ('session'), 'session']); }, sub { my ($node, $error) = @_; if ($node) { $self->init_connection; } else { $self->event (session_error => $error); # TODO: make error obj } }); } sub init_connection { my ($self) = @_; $self->{session_active} = 1; if ($self->{dont_retrieve_roster}) { $self->send_presence; } else { $self->retrieve_roster (sub { $self->send_presence }); } $self->event ('session_ready'); } =head2 retrieve_roster ($cb) This method initiates a roster request. If you set C when creating this connection no roster was retrieved. You can do that with this method. The coderef in C<$cb> will be called after the roster was retrieved. The first argument of the callback in C<$cb> will be the roster and the second will be a L object when an error occured while retrieving the roster. =cut sub retrieve_roster { my ($self, $cb) = @_; $self->send_iq (get => sub { my ($w) = @_; $w->addPrefix (xmpp_ns ('roster'), ''); $w->emptyTag ([xmpp_ns ('roster'), 'query']); }, sub { my ($node, $error) = @_; if ($node) { $self->store_roster ($node); } else { $self->event (roster_error => $error); } $cb->($self, $self->{roster}, $error) if $cb }); } sub store_roster { my ($self, $node) = @_; my @upd = $self->{roster}->update ($node); $self->event (roster_update => $self->{roster}, \@upd); } sub get_roster { my ($self) = @_; $self->{roster} } sub handle_iq_set { my ($self, $node, $rhandled) = @_; if ($node->find_all ([qw/roster query/])) { $self->store_roster ($node); $self->reply_iq_result ($node); $$rhandled = 1 } } sub handle_presence { my ($self, $node) = @_; if ($node->attr ('type') eq 'error') { my $error = Net::XMPP2::Error::Presence->new (node => $node); $self->event (presence_error => $error); return if $error->type ne 'continue'; } my ($contact, $old, $new) = $self->{roster}->update_presence ($node); $self->event (presence_update => $self->{roster}, $contact, $old, $new) } sub handle_message { my ($self, $node) = @_; if ($node->attr ('type') eq 'error') { my $error = Net::XMPP2::Error::Message->new (node => $node); $self->event (message_error => $error); return if $error->type ne 'continue'; } my $from = $node->attr ('from'); my $to = $node->attr ('to'); my $type = $node->attr ('type'); my ($thread) = $node->find_all ([qw/client thread/]); my %bodies; my %subjects; $bodies{$_->attr ('lang') || ''} = $_->text for $node->find_all ([qw/client body/]); $subjects{$_->attr ('lang') || ''} = $_->text for $node->find_all ([qw/client subject/]); my $msg = Net::XMPP2::IM::Message->new ( connection => $self, from => $from, to => $to, type => $type, bodies => \%bodies, subjects => \%subjects, thread => $thread ); $self->event (message => $msg); } sub handle_disconnect { my ($self) = @_; delete $self->{roster}; } =head1 EVENTS These additional events can be registered on with C: In the following events C<$roster> is the L object you get by calling C. =over 4 =item session_ready This event is generated when the session has been fully established and can be used to send around messages and other stuff. =item session_error => $error If an error happened during establishment of the session this event will be generated. C<$error> will be an L error object. =item roster_update => $roster, $contacts This event is emitted when a roster update has been received. C<$contacts> is an array reference of L objects which have changed. If a contact was removed it will return 'remove' when you call the C method on it. The first time this event is sent is when the roster was received for the first time. =item roster_error => $error If an error happened during retrival of the roster this event will be generated. C<$error> will be an L error object. =item presence_update => $roster, $contact, $old_presence, $new_presence This event is emitted when the presence of a contact has changed. C<$contact> is the L object which presence status has changed. C<$old_presence> is a L object which represents the presence prior to the change. C<$new_presence> is a L object which represents the presence after to the change. =item presence_error => $error This event is emitted when a presence stanza error was received. C<$error> will be an L error object. =item message => $msg This event is emitted when a message was received. C<$msg> is a L object. =item message_error => $error This event is emitted when a message stanza error was received. C<$error> will be an L error object. =item contact_request_subscribe => $roster, $contact, $rdoit This event is generated when the C<$contact> wants to subscribe to your presence. C<$rdoit> is a reference to a scalar. Setting the referenced scalar to 1 will accept the subscription request and send a subscribed presence. If you want to accept or decline the request laster call L or L on C<$contact>. =item contact_subscribed => $roster, $contact This event is generated when C<$contact> subscribed to your presence successfully. =item contact_did_unsubscribe => $roster, $contact, $rdoit This event is generated when C<$contact> unsubscribes from your presence. Setting the in C<$rdoit> referenced scalar to 1 will also let you unsubscribe from his presence. If you want to unsubscribe later from him call L on C<$contact>. =item contact_unsubscribed => $roster, $contact This event is generated when C<$contact> unsubscribed you from his presence. =item =back =head1 AUTHOR Robin Redeker, C<< >> =head1 COPYRIGHT & LICENSE Copyright 2007 Robin Redeker, all rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut 1; # End of Net::XMPP2