DJabberd
view release on metacpan or search on metacpan
lib/DJabberd/Connection.pm view on Meta::CPAN
# our previous write wants us to become readable first.
# we then go back into the write path (by flushing the write
# buffer) and it then does a read on this socket.
if (my $ar = $self->{write_when_readable}) {
$self->{write_when_readable} = 0;
$self->watch_read($ar->[0]); # restore previous readability state
$self->watch_write(1);
return;
}
my $bref;
if (my $ssl = $self->{ssl}) {
my $data = Net::SSLeay::read($ssl);
my $errs = Net::SSLeay::print_errs('SSL_read');
if ($errs) {
warn "SSL Read error: $errs\n";
$self->close;
return;
}
# Net::SSLeays buffers internally, so if we didn't read anything, it's
# in its buffer
unless ($data && length $data) {
# a few of these in a row implies an EOF. else it could
# just be the underlying socket was readable, but there
# wasn't enough of an SSL packet for OpenSSL/etc to return
# any unencrypted data back to us.
# We call 'actual_error_on_empty_read' to avoid counting
# SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE as 'actual' errors
my $err = DJabberd::Stanza::StartTLS->actual_error_on_empty_read($ssl);
if ($err) {
$self->log->warn("SSL Read error: $err (assuming ssl_eof)");
$self->close('ssl_eof');
}
return;
}
$bref = \$data;
} else {
# non-ssl mode:
$bref = $self->read(20_000);
}
return $self->close unless defined $bref;
# clients send whitespace between stanzas as keep-alives. let's just ignore those,
# not going through the bother to checkout a parser and all.
return if ! $self->{parser} && $$bref !~ /\S/;
Carp::confess if ($self->{closed});
if (XMLDEBUG) {
my $time = Time::HiRes::time;
$LOGMAP{$self}->print("$time\t< $$bref\n");
}
my $p = $self->{parser} || $self->borrow_a_parser;
my $len = length $$bref;
#$self->log->debug("$self->{id} parsing $len bytes...") unless $len == 1;
# remove invalid low unicode code points which aren't allowed in XML,
# but both iChat and gaim have been observed to send in the wild, often
# when copy/pasting from bizarre sources. this probably isn't compliant,
# and there's a speed hit, so only regexp them out in quirks mode:
if ($self->{vhost} && $self->{vhost}{quirksmode}) {
$$bref =~ s/&\#([\da-f]{0,8});/DJabberd::Util::numeric_entity_clean($1)/ieg;
}
eval {
$p->parse_chunk_scalarref($bref);
};
if ($@) {
# FIXME: give them stream error before closing them,
# wait until they get the stream error written to them before closing
$self->discard_parser;
$self->log->error("$self->{id} disconnected $self because: $@");
$self->log->warn("$self->{id} parsing *****\n$$bref\n*******\n\n\n");
$self->close;
return;
}
# if we still have a handler and haven't already closed down (cleanly),
# then let's consider giving our xml parser/sax pair back, if we're at
# a good breaking point.
if ((my $handler = $self->handler) && ! $self->{closed}) {
my $depth = $handler->depth;
if ($depth == 0 && $$bref =~ m!>\s*$!) {
# if no errors and not inside a stanza, return our parser to save memory
$self->return_parser;
}
}
}
sub on_stream_start {
my DJabberd::Connection $self = shift;
my $ss = shift;
die "on_stream_start not defined for $self";
}
# when we're the client of a stream (we're talking first)
sub start_init_stream {
my DJabberd::Connection $self = shift;
my %opts = @_;
my $extra_attr = delete $opts{'extra_attr'} || "";
my $to = delete $opts{'to'} || Carp::croak("need 'to' domain");
my $xmlns = delete $opts{'xmlns'} || "jabber:server";
die if %opts;
# {=init-version-is-max} -- we must announce the highest version we support
my $our_version = $self->server->spec_version;
my $ver_attr = $our_version->as_attr_string;
# by default we send the optional to='' attribute in our stream, but we have support for
# disabling it for our test suite.
$to = "to='$to'";
$to = "" if $DJabberd::_T_NO_TO_IN_DIALBACKVERIFY_STREAM;
# {=xml-lang}
my $xml = qq{<?xml version="1.0" encoding="UTF-8"?><stream:stream $to xmlns:stream='http://etherx.jabber.org/streams' xmlns='}.exml($xmlns).qq{' xml:lang='en' $extra_attr $ver_attr>};
( run in 0.801 second using v1.01-cache-2.11-cpan-39bf76dae61 )