DJabberd
view release on metacpan or search on metacpan
lib/DJabberd/Connection.pm view on Meta::CPAN
'ssl', # undef when not in ssl mode, else the $ssl object from Net::SSLeay
'stream_id', # undef until set first time
'to_host', # undef until stream start
'version', # the DJabberd::StreamVersion we negotiated
'rcvd_features', # the features stanza we've received from the other party
'log', # Log::Log4perl object for this connection
'xmllog', # Log::Log4perl object that controls raw xml logging
'id', # connection id, used for logging purposes
'write_when_readable', # arrayref/bool, for SSL: as boolean, we're only readable so we can write again.
# but bool true is actually an arrayref of previous watch_read state
'iqctr', # iq counter. incremented whenever we SEND an iq to the party (roster pushes, etc)
'in_stream', # bool: true if we're in a stream tag
'counted_close', # bool: temporary here to track down the overcounting of disconnects
'disconnect_handlers', # array of coderefs to call when this connection is closed for any reason
'sasl', # the sasl connection object, when sasl has been or is being negotiated
);
our $connection_id = 1;
use XML::SAX ();
use DJabberd::XMLParser;
use Digest::SHA1 qw(sha1_hex);
use DJabberd::SAXHandler;
use DJabberd::JID;
use DJabberd::IQ;
use DJabberd::Message;
use DJabberd::Util qw(exml tsub);
use Data::Dumper;
use Carp qw(croak);
use DJabberd::Log;
our $hook_logger = DJabberd::Log->get_logger("DJabberd::Hook");
use constant POLLIN => 1;
use constant POLLOUT => 4;
BEGIN {
my $xmldebug = $ENV{XMLDEBUG};
if ($xmldebug) {
eval 'use constant XMLDEBUG => "' . quotemeta($xmldebug) . '"';
die "XMLDEBUG path '$xmldebug' needs to be a directory writable by the user you are running $0 as\n" unless -w $xmldebug;
} else {
eval "use constant XMLDEBUG => ''";
}
}
our %LOGMAP;
sub new {
my ($class, $sock, $server) = @_;
my $self = $class->SUPER::new($sock);
croak("Server param not a DJabberd (server) object, '" . ref($server) . "'")
unless $server->isa("DJabberd");
$self->{vhost} = undef; # set once we get a stream start header from them.
$self->{server} = $server;
Scalar::Util::weaken($self->{server});
$self->{log} = DJabberd::Log->get_logger($class);
# hack to inject XML after Connection:: in the logger category
my $xml_category = $class;
$xml_category =~ s/Connection::/Connection::XML::/;
$self->{xmllog} = DJabberd::Log->get_logger($xml_category);
my $fromip = $self->peer_ip_string || "<undef>";
# a health check monitor doesn't get an id assigned/wasted on it, and doesn't log
# so it's less annoying to look at:
unless ($server->is_monitor_host($fromip)) {
$self->{id} = $connection_id++;
$self->log->debug("New connection '$self->{id}' from $fromip");
}
if (XMLDEBUG) {
system("mkdir -p " . XMLDEBUG ."/$$/");
my $handle = IO::Handle->new;
no warnings;
my $from = $fromip || "outbound";
my $filename = "+>" . XMLDEBUG . "/$$/$from-$self->{id}";
open ($handle, $filename) || die "Cannot open $filename: $!";
$handle->autoflush(1);
$LOGMAP{$self} = $handle;
}
return $self;
}
sub log {
return $_[0]->{log};
}
sub xmllog {
return $_[0]->{xmllog};
}
sub handler {
return $_[0]->{saxhandler};
}
sub vhost {
my DJabberd::Connection $self = $_[0];
return $self->{vhost};
}
sub server {
my DJabberd::Connection $self = $_[0];
return $self->{server};
}
sub bound_jid {
my DJabberd::Connection $self = $_[0];
return $self->{bound_jid};
}
sub new_iq_id {
my DJabberd::Connection $self = shift;
lib/DJabberd/Connection.pm view on Meta::CPAN
$self->{rcvd_features} = $feat_stanza;
}
sub set_bound_jid {
my ($self, $jid) = @_;
die unless $jid && $jid->isa('DJabberd::JID');
$self->{bound_jid} = $jid;
}
sub set_to_host {
my ($self, $host) = @_;
$self->{to_host} = $host;
}
sub to_host {
my $self = shift;
return $self->{to_host} or
die "To host accessed before it was set";
}
sub set_version {
my ($self, $verob) = @_;
$self->{version} = $verob;
}
sub version {
my $self = shift;
return $self->{version} or
die "Version accessed before it was set";
}
sub stream_id {
my $self = shift;
return $self->{stream_id} ||= Digest::SHA1::sha1_hex(rand() . rand() . rand());
}
# only use this run_hook_chain when
sub run_hook_chain {
my $self = shift;
my %opts = @_;
$opts{hook_invocant} = $self;
my $known_deprecated = delete $opts{deprecated};
my ($pkg, $filename, $line) = caller;
my $vhost = $self->vhost;
unless ($known_deprecated) {
warn("DEPRECATED caller ($pkg/$filename/$line) of run_hook_chain on a connection\n");
}
return DJabberd::VHost::run_hook_chain($vhost, %opts);
}
# this can fail to signal that this connection can't work on this
# vhost for instance, this vhost doesn't support s2s, so a serverin or
# dialback subclass can override this to return 0 when s2s isn't
# enabled for the vhost
sub set_vhost {
my ($self, $vhost) = @_;
Carp::croak("Not a DJabberd::VHost: $vhost") unless UNIVERSAL::isa($vhost, "DJabberd::VHost");
$self->{vhost} = $vhost;
Scalar::Util::weaken($self->{vhost});
return 1;
}
# called by DJabberd::SAXHandler
sub on_stanza_received {
my ($self, $node) = @_;
die "SUBCLASSES MUST OVERRIDE 'on_stanza_received' for $self\n";
}
# subclasses should override returning 0 or 1
sub is_server {
die "Undefined 'is_server' for $_[0]";
}
sub process_incoming_stanza_from_s2s_out {
my ($self, $node) = @_;
my %stanzas = (
"{urn:ietf:params:xml:ns:xmpp-tls}starttls" => 'DJabberd::Stanza::StartTLS',
"{http://etherx.jabber.org/streams}features" => 'DJabberd::Stanza::StreamFeatures',
);
my $class = $stanzas{$node->element};
unless ($class) {
warn "Unknown/handled stanza: " . $node->element . " on connection ($self->{id}), " . ref($self) . "\n";
return;
}
my $obj = $class->downbless($node, $self);
$obj->on_recv_from_server($self);
}
sub send_stanza {
my ($self, $stanza) = @_;
# getter subref for pre_stanza_write hooks to
# get at their own private copy of the stanza
my $cloned;
my $getter = sub {
return $cloned if $cloned;
if ($self != $stanza->connection) {
$cloned = $stanza->clone;
$cloned->set_connection($self);
} else {
$cloned = $stanza;
}
return $cloned;
};
$self->vhost->hook_chain_fast("pre_stanza_write",
[ $getter ],
{
# TODO: implement.
},
sub {
# if any hooks called the $getter, instantiating
# the $cloned copy, then that's what we write.
# as an optimization (the fast path), we just
# write the untouched, uncloned original.
$self->write_stanza($cloned || $stanza);
( run in 1.235 second using v1.01-cache-2.11-cpan-75ffa21a3d4 )