AnyEvent-XMPP

 view release on metacpan or  search on metacpan

lib/AnyEvent/XMPP/Connection.pm  view on Meta::CPAN


Returns true if the connection is still connected and stanzas can be
sent.

=cut

sub is_connected {
   my ($self) = @_;
   $self->{authenticated}
}

=item B<set_default_iq_timeout ($seconds)>

This sets the default timeout for IQ requests. If the timeout runs out
the request will be aborted and the callback called with a L<AnyEvent::XMPP::Error::IQ> object
where the C<condition> method returns a special value (see also C<condition> method of L<AnyEvent::XMPP::Error::IQ>).

The default timeout for IQ is 60 seconds.

=cut

sub set_default_iq_timeout {
   my ($self, $sec) = @_;
   $self->{default_iq_timeout} = $sec;
}

=item B<send_iq ($type, $create_cb, $result_cb, %attrs)>

This method sends an IQ XMPP B<request>.

If you want to B<respond> to a IQ request you received via the C<iq_set_request_xml>,
and C<iq_get_request_xml> events you have to use the C<reply_iq_result> or
C<reply_iq_error> methods documented below.

Please take a look at the documentation for C<send_iq> in AnyEvent::XMPP::Writer
about the meaning of C<$type>, C<$create_cb> and C<%attrs> (with the exception
of the 'timeout' key of C<%attrs>, see below).

C<$result_cb> will be called when a result was received or the timeout reached.
The first argument to C<$result_cb> will be a AnyEvent::XMPP::Node instance
containing the IQ result stanza contents.

If the IQ resulted in a stanza error the second argument to C<$result_cb> will
be C<undef> (if the error type was not 'continue') and the third argument will
be a L<AnyEvent::XMPP::Error::IQ> object.

The timeout can be set by C<set_default_iq_timeout> or passed separately
in the C<%attrs> array as the value for the key C<timeout> (timeout in seconds btw.).

This method returns the newly generated id for this iq request.

=cut

sub send_iq {
   my ($self, $type, $create_cb, $result_cb, %attrs) = @_;
   my $id = $self->{iq_id}++;
   $self->{iqs}->{$id} = $result_cb;

   my $timeout = delete $attrs{timeout} || $self->{default_iq_timeout};
   if ($timeout) {
      $self->{iq_timers}->{$id} =
         AnyEvent->timer (after => $timeout, cb => sub {
            delete $self->{iq_timers}->{$id};
            my $cb = delete $self->{iqs}->{$id};
            $cb->(undef, AnyEvent::XMPP::Error::IQ->new)
         });
   }

   $self->{writer}->send_iq ($id, $type, $create_cb, %attrs);
   $id
}

=item B<next_iq_id>

This method returns the next IQ id that will be used.

=cut

sub next_iq_id {
   $_[0]->{iq_id};
}

=item B<reply_iq_result ($req_iq_node, $create_cb, %attrs)>

This method will generate a result reply to the iq request C<AnyEvent::XMPP::Node>
in C<$req_iq_node>.

Please take a look at the documentation for C<send_iq> in L<AnyEvent::XMPP::Writer>
about the meaning C<$create_cb> and C<%attrs>.

Use C<$create_cb> to create the XML for the result.

The type for this iq reply is 'result'.

The C<to> attribute of the reply stanza will be set to the C<from>
attribute of the C<$req_iq_node>. If C<$req_iq_node> had no C<from>
node it won't be set. If you want to overwrite the C<to> field just
pass it via C<%attrs>.

=cut

sub reply_iq_result {
   my ($self, $iqnode, $create_cb, %attrs) = @_;
   
   return $self->_reply_iq(
      $iqnode,
      'result',
      $create_cb,
      %attrs
   );
}

=item B<reply_iq_error ($req_iq_node, $error_type, $error, %attrs)>

This method will generate an error reply to the iq request C<AnyEvent::XMPP::Node>
in C<$req_iq_node>.

C<$error_type> is one of 'cancel', 'continue', 'modify', 'auth' and 'wait'.
C<$error> is one of the defined error conditions described in
C<write_error_tag> method of L<AnyEvent::XMPP::Writer>.

Please take a look at the documentation for C<send_iq> in AnyEvent::XMPP::Writer
about the meaning of C<%attrs>.

The type for this iq reply is 'error'.

The C<to> attribute of the reply stanza will be set to the C<from>
attribute of the C<$req_iq_node>. If C<$req_iq_node> had no C<from>
node it won't be set. If you want to overwrite the C<to> field just
pass it via C<%attrs>.

=cut

sub reply_iq_error {
   my ($self, $iqnode, $errtype, $error, %attrs) = @_;

   return $self->_reply_iq(
      $iqnode,
      'error',
      sub { $self->{writer}->write_error_tag ($iqnode, $errtype, $error) },
      %attrs
   );
}

sub _reply_iq {
   my ($self, $iqnode, $type, $create_cb, %attrs) = @_;

   return $self->{writer}->send_iq (
      $iqnode->attr ('id'), $type, $create_cb,
      (defined $iqnode->attr ('from') ? (to => $iqnode->attr ('from')) : ()),
      (defined $iqnode->attr ('to') ? (from => $iqnode->attr ('to')) : ()),
      %attrs
   );
}

sub handle_iq {
   my ($self, $node) = @_;

   my $type = $node->attr ('type');

   my $id = $node->attr ('id');
   delete $self->{iq_timers}->{$id} if defined $id;

   if ($type eq 'result') {
      if (my $cb = delete $self->{iqs}->{$id}) {
         eval {
            $cb->($node);
         };
         if ($@) { $self->event (iq_result_cb_exception => $@) }
      }

   } elsif ($type eq 'error') {
      if (my $cb = delete $self->{iqs}->{$id}) {

         my $error = AnyEvent::XMPP::Error::IQ->new (node => $node);

         eval {
            $cb->(($error->type eq 'continue' ? $node : undef), $error);
         };
         if ($@) { $self->event (iq_result_cb_exception => $@) }
      }

   } else {
      my $handled = 0;
      $self->event ("iq_${type}_request_xml" => $node, \$handled);
      $handled or $self->reply_iq_error ($node, undef, 'service-unavailable');
   }
}

sub send_sasl_auth {
   my ($self, @mechs) = @_;

   for (qw/username password domain/) {
      die "No '$_' argument given to new, but '$_' is required\n"
         unless defined $self->{$_};
   }

   $self->{writer}->send_sasl_auth (
      [map { $_->text } @mechs],
      $self->{username},
      ($self->{use_host_as_sasl_hostname}
         ? $self->{host}
         : $self->{domain}),
      $self->{password}
   );
}

sub handle_stream_features {
   my ($self, $node) = @_;
   my @bind  = $node->find_all ([qw/bind bind/]);
   my @tls   = $node->find_all ([qw/tls starttls/]);

   # and yet another weird thingie: in XEP-0077 it's said that
   # the register feature MAY be advertised by the server. That means:
   # it MAY not be advertised even if it is available... so we don't
   # care about it...
   # my @reg   = $node->find_all ([qw/register register/]);

   if (not ($self->{disable_ssl}) && not ($self->{ssl_enabled}) && @tls) {
      $self->{writer}->send_starttls;

   } elsif (not $self->{authenticated}) {

lib/AnyEvent/XMPP/Connection.pm  view on Meta::CPAN

binding you can call this function to set a new C<$resource>
and retry binding.

If it fails again you can call this again. Becareful not to
end up in a loop!

If binding was successful the C<stream_ready> event will be generated.

=cut

sub do_rebind {
   my ($self, $resource) = @_;
   $self->{resource} = $resource;
   $self->send_iq (
      set =>
         sub {
            my ($w) = @_;
            if ($self->{resource}) {
               simxml ($w,
                  defns => 'bind',
                  node => {
                     name => 'bind',
                     childs => [ { name => 'resource', childs => [ $self->{resource} ] } ]
                  }
               )
            } else {
               simxml ($w, defns => 'bind', node => { name => 'bind' })
            }
         },
         sub {
            my ($ret_iq, $error) = @_;

            if ($error) {
               # TODO: make bind error into a seperate error class?
               if ($error->xml_node ()) {
                  my ($res) = $error->xml_node ()->find_all ([qw/bind bind/], [qw/bind resource/]);
                  $self->event (bind_error => $error, ($res ? $res : $self->{resource}));
               } else {
                  $self->event (bind_error => $error);
               }

            } else {
               my @jid = $ret_iq->find_all ([qw/bind bind/], [qw/bind jid/]);
               my $jid = $jid[0]->text;
               unless ($jid) { die "Got empty JID tag from server!\n" }
               $self->{jid} = $jid;

               $self->event (stream_ready => $jid);
            }
         }
   );
}


sub _start_whitespace_ping {
   my ($self) = @_;

   return unless $self->{whitespace_ping_interval} > 0;

   $self->{_ws_ping} =
      AnyEvent->timer (after => $self->{whitespace_ping_interval}, cb => sub {
         $self->{writer}->send_whitespace_ping;
         $self->_start_whitespace_ping;
      });
}

sub _stop_whitespace_ping {
   delete $_[0]->{_ws_ping};
}


=item B<jid>

After the stream has been bound to a resource the JID can be retrieved via this
method.

=cut

sub jid { $_[0]->{jid} }

=item B<features>

Returns the last received <features> tag in form of an L<AnyEvent::XMPP::Node> object.

=cut

sub features { $_[0]->{features} }

=item B<stream_id>

This is the ID of this stream that was given us by the server.

=cut

sub stream_id { $_[0]->{stream_id} }

=back

=head1 EVENTS

The L<AnyEvent::XMPP::Connection> class is derived from the L<Object::Event> class,
and thus inherits the event callback registering system from it. Consult the
documentation of L<Object::Event> about more details.

NODE: Every callback gets as it's first argument the L<AnyEvent::XMPP::Connection>
object. The further callback arguments are described in the following listing of
events.

These events can be registered on with C<reg_cb>:

=over 4

=item stream_features => $node

This event is sent when a stream feature (<features>) tag is received. C<$node> is the
L<AnyEvent::XMPP::Node> object that represents the <features> tag.

=item stream_pre_authentication

This event is emitted after TLS/SSL was initiated (if enabled) and before any
authentication happened.



( run in 0.988 second using v1.01-cache-2.11-cpan-39bf76dae61 )