use
vars
qw($VERSION $REVISION)
;
$VERSION
=
'1.36'
;
(
$REVISION
) = (
q$LastChangedRevision: 163 $
=~/(\d+)/g);
sub
spawn {
my
$package
=
shift
;
my
$self
=
$package
->create(
@_
);
$self
->{prefix} =
'ircd_'
;
$self
->{config}->{
uc
$_
} =
delete
$self
->{config}->{
$_
}
for
keys
%{
$self
->{config} };
$self
->configure();
$self
->_state_create();
$self
->{ircd} =
$self
;
return
$self
;
}
sub
_load_our_plugins {
my
$self
=
shift
;
$poe_kernel
->state(
'add_spoofed_nick'
,
$self
);
$poe_kernel
->state(
'del_spoofed_nick'
,
$self
);
$poe_kernel
->state(
"daemon_cmd_$_"
,
$self
,
'_spoofed_command'
)
for
qw(join part mode kick topic nick privmsg notice gline kline unkline rkline sjoin locops wallops operwall)
;
}
sub
IRCD_connection {
my
(
$self
,
$ircd
) =
splice
@_
,0 ,2;
my
(
$conn_id
,
$peeraddr
,
$peerport
,
$sockaddr
,
$sockport
) =
map
{ ${
$_
} }
@_
;
delete
$self
->{state}->{conns}->{
$conn_id
}
if
$self
->_connection_exists(
$conn_id
);
$self
->{state}->{conns}->{
$conn_id
}->{registered} = 0;
$self
->{state}->{conns}->{
$conn_id
}->{type} =
'u'
;
$self
->{state}->{conns}->{
$conn_id
}->{seen} =
time
();
$self
->{state}->{conns}->{
$conn_id
}->{
socket
} = [
$peeraddr
,
$peerport
,
$sockaddr
,
$sockport
];
$self
->_state_conn_stats();
return
PCSI_EAT_ALL;
}
sub
IRCD_connected {
my
(
$self
,
$ircd
) =
splice
@_
,0 ,2;
my
(
$conn_id
,
$peeraddr
,
$peerport
,
$sockaddr
,
$sockport
,
$name
) =
map
{ ${
$_
} }
@_
;
delete
$self
->{state}->{conns}->{
$conn_id
}
if
$self
->_connection_exists(
$conn_id
);
$self
->{state}->{conns}->{
$conn_id
}->{registered} = 0;
$self
->{state}->{conns}->{
$conn_id
}->{cntr} = 1;
$self
->{state}->{conns}->{
$conn_id
}->{type} =
'u'
;
$self
->{state}->{conns}->{
$conn_id
}->{seen} =
time
();
$self
->{state}->{conns}->{
$conn_id
}->{
socket
} = [
$peeraddr
,
$peerport
,
$sockaddr
,
$sockport
];
$self
->_state_conn_stats();
$self
->_state_send_credentials(
$conn_id
,
$name
);
return
PCSI_EAT_ALL;
}
sub
IRCD_connection_flood {
my
(
$self
,
$ircd
) =
splice
@_
,0 ,2;
my
(
$conn_id
) =
map
{ ${
$_
} }
@_
;
$self
->_terminate_conn_error(
$conn_id
,
'Excess Flood'
);
return
PCSI_EAT_ALL;
}
sub
IRCD_connection_idle {
my
(
$self
,
$ircd
) =
splice
@_
,0 ,2;
my
(
$conn_id
,
$interval
) =
map
{ ${
$_
} }
@_
;
return
PCSI_EAT_NONE
unless
$self
->_connection_exists(
$conn_id
);
my
$conn
=
$self
->{state}->{conns}->{
$conn_id
};
if
(
$conn
->{type} eq
'u'
) {
$self
->_terminate_conn_error(
$conn_id
,
'Connection Timeout'
);
return
PCSI_EAT_ALL;
}
if
(
$conn
->{pinged} ) {
my
$msg
=
'Ping timeout: '
. (
time
() -
$conn
->{seen} ) .
' seconds'
;
$self
->_terminate_conn_error(
$conn_id
,
$msg
);
return
PCSI_EAT_ALL;
}
$conn
->{pinged} = 1;
$self
->{ircd}->send_output( {
command
=>
'PING'
,
params
=> [
$self
->server_name() ] },
$conn_id
);
return
PCSI_EAT_ALL;
}
sub
IRCD_auth_done {
my
(
$self
,
$ircd
) =
splice
@_
,0 ,2;
my
(
$conn_id
,
$ref
) =
map
{ ${
$_
} }
@_
;
return
PCSI_EAT_ALL
unless
$self
->_connection_exists(
$conn_id
);
$self
->{state}->{conns}->{
$conn_id
}->{auth} =
$ref
;
$self
->_client_register(
$conn_id
);
return
PCSI_EAT_ALL;
}
sub
IRCD_disconnected {
my
(
$self
,
$ircd
) =
splice
@_
,0 ,2;
my
(
$conn_id
,
$errstr
) =
map
{ ${
$_
} }
@_
;
return
PCSI_EAT_ALL
unless
$self
->_connection_exists(
$conn_id
);
SWITCH: {
unless
(
$self
->_connection_registered(
$conn_id
) ) {
delete
$self
->{state}->{conns}->{
$conn_id
};
last
SWITCH;
}
if
(
$self
->_connection_is_peer(
$conn_id
) ) {
my
$peer
=
$self
->{state}->{conns}->{
$conn_id
}->{name};
$self
->{ircd}->send_output( @{
$self
->_daemon_peer_squit(
$conn_id
,
$peer
,
$errstr
) } );
delete
$self
->{state}->{conns}->{
$conn_id
};
last
SWITCH;
}
if
(
$self
->_connection_is_client(
$conn_id
) ) {
$self
->{ircd}->send_output( @{
$self
->_daemon_cmd_quit(
$self
->_client_nickname(
$conn_id
,
$errstr
),
$errstr
) } );
delete
$self
->{state}->{conns}->{
$conn_id
};
last
SWITCH;
}
}
return
PCSI_EAT_ALL;
}
sub
IRCD_compressed_conn {
my
(
$self
,
$ircd
) =
splice
@_
,0 ,2;
my
(
$conn_id
) =
map
{ ${
$_
} }
@_
;
$self
->_state_send_burst(
$conn_id
);
return
PCSI_EAT_ALL;
}
sub
_default {
my
(
$self
,
$ircd
,
$event
) =
splice
@_
, 0, 3;
return
PCSI_EAT_NONE
unless
$event
=~ /^IRCD_cmd_/;
my
(
$conn_id
,
$input
) =
map
{ ${
$_
} }
@_
;
return
PCSI_EAT_ALL
unless
$self
->_connection_exists(
$conn_id
);
$self
->{state}->{conns}->{
$conn_id
}->{seen} =
time
();
SWITCH: {
unless
(
$self
->_connection_registered(
$conn_id
) ) {
$self
->_cmd_from_unknown(
$conn_id
,
$input
);
last
SWITCH;
}
if
(
$self
->_connection_is_peer(
$conn_id
) ) {
$self
->_cmd_from_peer(
$conn_id
,
$input
);
last
SWITCH;
}
if
(
$self
->_connection_is_client(
$conn_id
) ) {
delete
$input
->{prefix};
$self
->_cmd_from_client(
$conn_id
,
$input
);
last
SWITCH;
}
};
return
PCSI_EAT_ALL;
}
sub
_auth_finished {
my
$self
=
shift
;
my
$conn_id
=
shift
||
return
undef
;
return
unless
$self
->_connection_exists(
$conn_id
);
return
$self
->{state}->{conns}->{
$conn_id
}->{auth};
}
sub
_connection_exists {
my
$self
=
shift
;
my
$conn_id
=
shift
||
return
0;
return
0
unless
defined
$self
->{state}->{conns}->{
$conn_id
};
return
1;
}
sub
_client_register {
my
$self
=
shift
;
my
$conn_id
=
shift
||
return
undef
;
return
unless
$self
->_connection_exists(
$conn_id
);
return
unless
$self
->{state}->{conns}->{
$conn_id
}->{nick};
return
unless
$self
->{state}->{conns}->{
$conn_id
}->{user};
my
$auth
=
$self
->_auth_finished(
$conn_id
);
return
unless
$auth
;
unless
(
$self
->_state_auth_client_conn(
$conn_id
) ) {
$self
->_terminate_conn_error(
$conn_id
,
'You are not authorized to use this server'
);
return
;
}
if
(
$self
->_state_user_matches_gline(
$conn_id
) ) {
$self
->_terminate_conn_error(
$conn_id
,
'G-Lined'
);
return
;
}
if
(
$self
->_state_user_matches_kline(
$conn_id
) ) {
$self
->_terminate_conn_error(
$conn_id
,
'K-Lined'
);
return
;
}
if
(
$self
->_state_user_matches_rkline(
$conn_id
) ) {
$self
->_terminate_conn_error(
$conn_id
,
'K-Lined'
);
return
;
}
$self
->_state_register_client(
$conn_id
);
my
$server
=
$self
->server_name();
my
$nick
=
$self
->_client_nickname(
$conn_id
);
my
$port
=
$self
->{state}->{conns}->{
$conn_id
}->{
socket
}->[3];
my
$version
=
$self
->server_version();
my
$network
=
$self
->server_config(
'NETWORK'
);
my
$server_is
=
$server
.
'['
.
$server
.
'/'
.
$port
.
']'
;
$self
->_send_output_to_client(
$conn_id
=> {
prefix
=>
$server
,
command
=>
'001'
,
params
=> [
$nick
,
"Welcome to the $network Internet Relay Chat network $nick"
] } );
$self
->_send_output_to_client(
$conn_id
=> {
prefix
=>
$server
,
command
=>
'002'
,
params
=> [
$nick
,
"Your host is $server_is, running version $version"
] } );
$self
->_send_output_to_client(
$conn_id
=> {
prefix
=>
$server
,
command
=>
'003'
,
params
=> [
$nick
,
$self
->server_created() ] } );
$self
->_send_output_to_client(
$conn_id
=> {
prefix
=>
$server
,
command
=>
'004'
,
params
=> [
$nick
,
$server
,
$version
,
'Dilowz'
,
'biklmnopstveIh'
,
'bkloveIh'
],
colonify
=> 0 } );
$self
->_send_output_to_client(
$conn_id
=>
$_
)
for
@{
$self
->_daemon_cmd_isupport(
$nick
) };
$self
->{state}->{conns}->{
$conn_id
}->{registered} = 1;
$self
->{state}->{conns}->{
$conn_id
}->{type} =
'c'
;
$self
->{ircd}->send_event(
'cmd_lusers'
=>
$conn_id
=> {
command
=>
'LUSERS'
} );
$self
->{ircd}->send_event(
'cmd_motd'
=>
$conn_id
=> {
command
=>
'MOTD'
} );
$self
->{ircd}->send_event(
'cmd_mode'
=>
$conn_id
=> {
command
=>
'MODE'
,
params
=> [
$nick
,
'+i'
] } );
return
1;
}
sub
_connection_registered {
my
$self
=
shift
;
my
$conn_id
=
shift
||
return
undef
;
return
unless
$self
->_connection_exists(
$conn_id
);
return
$self
->{state}->{conns}->{
$conn_id
}->{registered};
}
sub
_connection_is_peer {
my
$self
=
shift
;
my
$conn_id
=
shift
||
return
undef
;
return
unless
$self
->_connection_exists(
$conn_id
);
return
unless
$self
->{state}->{conns}->{
$conn_id
}->{registered};
return
1
if
$self
->{state}->{conns}->{
$conn_id
}->{type} eq
'p'
;
return
0;
}
sub
_connection_is_client {
my
$self
=
shift
;
my
$conn_id
=
shift
||
return
undef
;
return
unless
$self
->_connection_exists(
$conn_id
);
return
unless
$self
->{state}->{conns}->{
$conn_id
}->{registered};
return
1
if
$self
->{state}->{conns}->{
$conn_id
}->{type} eq
'c'
;
return
0;
}
sub
_cmd_from_unknown {
my
(
$self
,
$wheel_id
,
$input
) =
splice
@_
, 0, 3;
my
$cmd
=
uc
$input
->{command};
my
$params
=
$input
->{params} || [ ];
my
$pcount
=
scalar
@{
$params
};
my
$invalid
= 0;
SWITCH: {
if
(
$cmd
eq
'QUIT'
) {
$self
->_terminate_conn_error(
$wheel_id
,
'Client Quit'
);
last
SWITCH;
}
if
(
$cmd
=~ /^(PASS|NICK|SERVER)$/ and !
$pcount
) {
$self
->_send_output_to_client(
$wheel_id
=>
'461'
=>
$cmd
);
last
SWITCH;
}
if
(
$cmd
eq
'PASS'
and
$pcount
) {
$self
->{state}->{conns}->{
$wheel_id
}->{
lc
$cmd
} =
$params
->[0];
if
(
$params
->[1] and
$params
->[1] =~ /TS$/ ) {
$self
->{state}->{conns}->{
$wheel_id
}->{ts_server} = 1;
$self
->{ircd}->antiflood(
$wheel_id
=> 0 );
}
last
SWITCH;
}
if
(
$cmd
eq
'CAPAB'
and
$pcount
) {
$self
->{state}->{conns}->{
$wheel_id
}->{capab} = [
split
/\s+/,
$params
->[0] ];
last
SWITCH;
}
if
(
$cmd
eq
'SERVER'
and
$pcount
< 2 ) {
$self
->_send_output_to_client(
$wheel_id
=>
'461'
=>
$cmd
);
last
SWITCH;
}
if
(
$cmd
eq
'SERVER'
) {
my
$conn
=
$self
->{state}->{conns}->{
$wheel_id
};
$conn
->{name} =
$params
->[0];
$conn
->{hops} =
$params
->[1] || 1;
$conn
->{desc} =
$params
->[2] ||
''
;
if
( !
$conn
->{ts_server} ) {
$self
->_terminate_conn_error(
$wheel_id
,
'Non-TS server.'
);
last
SWITCH;
}
if
( !
$self
->_state_auth_peer_conn(
$wheel_id
,
$conn
->{name},
$conn
->{pass} ) ) {
$self
->_terminate_conn_error(
$wheel_id
,
'Unauthorised server.'
);
last
SWITCH;
}
if
(
$self
->state_peer_exists(
$conn
->{name} ) ) {
$self
->_terminate_conn_error(
$wheel_id
,
'Server exists.'
);
last
SWITCH;
}
$self
->_state_register_peer(
$wheel_id
);
if
(
$conn
->{zip} and
scalar
grep
{
$_
eq
'ZIP'
} @{
$conn
->{capab} } ) {
$self
->{ircd}->compressed_link(
$wheel_id
, 1,
$conn
->{cntr} );
}
else
{
$self
->_state_send_burst(
$wheel_id
);
}
$self
->{ircd}->send_event(
"daemon_capab"
,
$conn
->{name}, @{
$conn
->{capab} } );
last
SWITCH;
}
if
(
$cmd
eq
'NICK'
and
$pcount
) {
if
( !validate_nick_name(
$params
->[0] ) ) {
$self
->_send_output_to_client(
$wheel_id
=>
'432'
=>
$params
->[0] );
last
SWITCH;
}
if
(
$self
->state_nick_exists(
$params
->[0] ) ) {
$self
->_send_output_to_client(
$wheel_id
=>
'433'
=>
$params
->[0] );
last
SWITCH;
}
my
$nicklen
=
$self
->server_config(
'NICKLEN'
);
$params
->[0] =
substr
(
$params
->[0],0,
$nicklen
)
if
length
(
$params
->[0] ) >
$nicklen
;
$self
->{state}->{conns}->{
$wheel_id
}->{
lc
$cmd
} =
$params
->[0];
$self
->{state}->{pending}->{ u_irc
$params
->[0] } =
$wheel_id
;
$self
->_client_register(
$wheel_id
);
last
SWITCH;
}
if
(
$cmd
eq
'USER'
and
$pcount
< 4 ) {
$self
->_send_output_to_client(
$wheel_id
=>
'461'
=>
$cmd
);
last
SWITCH;
}
if
(
$cmd
eq
'USER'
) {
$self
->{state}->{conns}->{
$wheel_id
}->{user} =
$params
->[0];
$self
->{state}->{conns}->{
$wheel_id
}->{ircname} =
$params
->[3] ||
''
;
$self
->_client_register(
$wheel_id
);
last
SWITCH;
}
last
SWITCH
if
$self
->{state}->{conns}->{
$wheel_id
}->{cntr};
$invalid
= 1;
$self
->_send_output_to_client(
$wheel_id
=>
'451'
);
}
return
1
if
$invalid
;
$self
->_state_cmd_stat(
$cmd
,
$input
->{raw_line} );
return
1;
}
sub
_cmd_from_peer {
my
(
$self
,
$conn_id
,
$input
) =
splice
@_
, 0, 3;
my
$cmd
=
$input
->{command};
my
$params
=
$input
->{params};
my
$prefix
=
$input
->{prefix};
my
$invalid
= 0;
SWITCH: {
my
$method
=
'_daemon_peer_'
.
lc
$cmd
;
if
(
$cmd
eq
'SQUIT'
and !
$prefix
) {
$self
->_daemon_peer_squit(
$conn_id
, @{
$params
} );
last
SWITCH;
}
if
(
$cmd
=~ /\d{3}/ ) {
$self
->{ircd}->send_output(
$input
,
$self
->_state_user_route(
$params
->[0] ) );
last
SWITCH;
}
if
(
$cmd
eq
'QUIT'
) {
$self
->{ircd}->send_output( @{
$self
->_daemon_peer_quit(
$prefix
, @{
$params
},
$conn_id
) } );
last
SWITCH;
}
if
(
$cmd
=~ /^(PRIVMSG|NOTICE)$/ ) {
$self
->_send_output_to_client(
$conn_id
=>
$prefix
=> (
ref
$_
eq
'ARRAY'
? @{
$_
} :
$_
) )
for
$self
->_daemon_peer_message(
$conn_id
,
$prefix
,
$cmd
, @{
$params
} );
last
SWITCH;
}
if
(
$cmd
=~ /^(WHOIS|VERSION|TIME|NAMES|LINKS|ADMIN|INFO|MOTD|SQUIT)$/i ) {
my
$client_method
=
'_daemon_cmd_'
.
lc
$cmd
;
$self
->_send_output_to_client(
$conn_id
=>
$prefix
=> (
ref
$_
eq
'ARRAY'
? @{
$_
} :
$_
) )
for
$self
->
$client_method
(
$prefix
, @{
$params
} );
last
SWITCH;
}
if
(
$cmd
=~ /^(PING|PONG)$/i and
$self
->can(
$method
) ) {
$self
->
$method
(
$conn_id
, @{
$params
} );
last
SWITCH;
}
if
(
$cmd
=~ /^SVINFO$/i and
$self
->can(
$method
) ) {
$self
->
$method
(
$conn_id
, @{
$params
} );
my
$conn
=
$self
->{state}->{conns}->{
$conn_id
};
$self
->{ircd}->send_event(
"daemon_svinfo"
,
$conn
->{name}, @{
$params
} );
last
SWITCH;
}
$method
=
'_daemon_peer_umode'
if
$cmd
eq
'MODE'
and
$self
->state_nick_exists(
$params
->[0] );
if
(
$self
->can(
$method
) ) {
$self
->
$method
(
$conn_id
,
$prefix
, @{
$params
} );
last
SWITCH;
}
$invalid
= 1;
}
return
1
if
$invalid
;
$self
->_state_cmd_stat(
$cmd
,
$input
->{raw_line}, 1 );
return
1;
}
sub
_cmd_from_client {
my
(
$self
,
$wheel_id
,
$input
) =
splice
@_
, 0, 3;
my
$cmd
=
uc
$input
->{command};
my
$params
=
$input
->{params} || [ ];
my
$pcount
=
scalar
@{
$params
};
my
$server
=
$self
->server_name();
my
$nick
=
$self
->_client_nickname(
$wheel_id
);
my
$invalid
= 0;
SWITCH: {
my
$method
=
'_daemon_cmd_'
.
lc
$cmd
;
if
(
$cmd
eq
'QUIT'
) {
$self
->_terminate_conn_error(
$wheel_id
, (
$pcount
?
qq{"$params->[0]"}
:
'Client Quit'
) );
last
SWITCH;
}
if
(
$cmd
=~ /^(USERHOST|MODE)$/ and !
$pcount
) {
$self
->_send_output_to_client(
$wheel_id
=>
'461'
=>
$cmd
);
last
SWITCH;
}
if
(
$cmd
=~ /^(USERHOST)$/ ) {
$self
->_send_output_to_client(
$wheel_id
=>
$_
)
for
$self
->
$method
(
$nick
, (
$pcount
<= 5 ? @{
$params
} : @{
$params
}[0..5] ) );
last
SWITCH;
}
if
(
$cmd
=~ /^(PRIVMSG|NOTICE)$/ ) {
$self
->{state}->{conns}->{
$wheel_id
}->{idle_time} =
time
();
$self
->_send_output_to_client(
$wheel_id
=> (
ref
$_
eq
'ARRAY'
? @{
$_
} :
$_
) )
for
$self
->_daemon_cmd_message(
$nick
,
$cmd
, @{
$params
} );
last
SWITCH;
}
if
(
$cmd
eq
'MODE'
and
$self
->state_nick_exists(
$params
->[0] ) ) {
if
( ( u_irc
$nick
) ne ( u_irc
$params
->[0] ) ) {
$self
->_send_output_to_client(
$wheel_id
=>
'502'
);
last
SWITCH;
}
my
$modestring
=
join
(
''
, @{
$params
}[1..$
$modestring
=~ s/\s+//g;
$modestring
=~ s/[^a-zA-Z+-]+//g;
$modestring
=~ s/[^DGglwiozl+-]+//g;
$modestring
= unparse_mode_line
$modestring
;
$self
->_send_output_to_client(
$wheel_id
=>
$_
)
for
$self
->_daemon_cmd_umode(
$nick
,
$modestring
);
last
SWITCH;
}
if
(
$self
->can(
$method
) ) {
$self
->_send_output_to_client(
$wheel_id
=> (
ref
$_
eq
'ARRAY'
? @{
$_
} :
$_
) )
for
$self
->
$method
(
$nick
, @{
$params
} );
last
SWITCH;
}
$invalid
= 1;
$self
->_send_output_to_client(
$wheel_id
=>
'421'
=>
$cmd
);
}
return
1
if
$invalid
;
$self
->_state_cmd_stat(
$cmd
,
$input
->{raw_line} );
return
1;
}
sub
_daemon_cmd_message {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
my
$type
=
shift
||
return
;
my
$ref
= [ ];
my
$args
= [
@_
];
my
$count
=
scalar
@{
$args
};
SWITCH: {
if
( !
$count
) {
push
@{
$ref
}, [
'461'
,
$type
];
last
SWITCH;
}
if
(
$count
< 2 or !
$args
->[1] ) {
push
@{
$ref
}, [
'412'
];
last
SWITCH;
}
my
$targets
= 0;
my
$max_targets
=
$self
->server_config(
'MAXTARGETS'
);
my
$full
=
$self
->state_user_full(
$nick
);
my
$targs
=
$self
->_state_parse_msg_targets(
$args
->[0] );
LOOP:
foreach
my
$target
(
keys
%{
$targs
} ) {
my
$targ_type
=
shift
@{
$targs
->{
$target
} };
if
(
$targ_type
=~ /(server|host)mask/ and !
$self
->state_user_is_operator(
$nick
) ) {
push
@{
$ref
}, [
'481'
];
next
LOOP;
}
if
(
$targ_type
=~ /(server|host)mask/ and
$targs
->{
$target
}->[0] !~ /\./ ) {
push
@{
$ref
}, [
'413'
,
$target
];
next
LOOP;
}
if
(
$targ_type
=~ /(server|host)mask/ and
$targs
->{
$target
}->[0] =~ /\x2E.*[\x2A\x3F]+.*$/ ) {
push
@{
$ref
}, [
'414'
,
$target
];
next
LOOP;
}
if
(
$targ_type
eq
'channel_ext'
and !
$self
->state_chan_exists(
$targs
->{
$target
}->[1] ) ) {
push
@{
$ref
}, [
'401'
,
$targs
->{
$target
}->[1] ];
next
LOOP;
}
if
(
$targ_type
eq
'channel'
and !
$self
->state_chan_exists(
$target
) ) {
push
@{
$ref
}, [
'401'
,
$target
];
next
LOOP;
}
if
(
$targ_type
eq
'nick'
and !
$self
->state_nick_exists(
$target
) ) {
push
@{
$ref
}, [
'401'
,
$target
];
next
LOOP;
}
if
(
$targ_type
eq
'nick_ext'
and !
$self
->state_peer_exists(
$targs
->{
$target
}->[1] ) ) {
push
@{
$ref
}, [
'402'
,
$targs
->{
$target
}->[1] ];
next
LOOP;
}
$targets
++;
if
(
$targets
>
$max_targets
) {
push
@{
$ref
}, [
'407'
,
$target
];
last
SWITCH;
}
if
(
$targ_type
eq
'servermask'
) {
my
$us
= 0;
my
%targets
;
my
$ucserver
=
uc
$self
->server_name();
foreach
my
$peer
(
keys
%{
$self
->{state}->{peers} } ) {
if
( matches_mask(
$targs
->{
$target
}->[0],
$peer
) ) {
if
(
$ucserver
eq
$peer
) {
$us
= 1;
}
else
{
$targets
{
$self
->_state_peer_route(
$peer
) }++;
}
}
}
$self
->{ircd}->send_output( {
prefix
=>
$nick
,
command
=>
$type
,
params
=> [
$target
,
$args
->[1] ] },
keys
%targets
);
if
(
$us
) {
my
$local
=
$self
->{state}->{peers}->{
uc
$self
->server_name() }->{users};
my
@local
;
my
$spoofed
= 0;
foreach
my
$luser
(
values
%{
$local
} ) {
if
(
$luser
->{route_id} eq
'spoofed'
) {
$spoofed
= 1;
}
else
{
push
@local
,
$luser
->{route_id};
}
}
$self
->{ircd}->send_output( {
prefix
=>
$full
,
command
=>
$type
,
params
=> [
$target
,
$args
->[1] ] },
@local
);
$self
->{ircd}->send_event(
"daemon_"
.
lc
$type
,
$full
,
$target
,
$args
->[1] )
if
$spoofed
;
}
next
LOOP;
}
if
(
$targ_type
eq
'hostmask'
) {
my
$spoofed
= 0;
my
%targets
;
my
@local
;
HOST:
foreach
my
$luser
(
values
%{
$self
->{state}->{users} } ) {
next
HOST
unless
matches_mask(
$targs
->{
$target
}->[0],
$luser
->{auth}->{hostname} );
if
(
$luser
->{route_id} eq
'spoofed'
) {
$spoofed
= 1;
}
elsif
(
$luser
->{type} eq
'r'
) {
$targets
{
$luser
->{route_id} }++;
}
else
{
push
@local
,
$luser
->{route_id};
}
}
$self
->{ircd}->send_output( {
prefix
=>
$nick
,
command
=>
$type
,
params
=> [
$target
,
$args
->[1] ] },
keys
%targets
);
$self
->{ircd}->send_output( {
prefix
=>
$full
,
command
=>
$type
,
params
=> [
$target
,
$args
->[1] ] },
@local
);
$self
->{ircd}->send_event(
"daemon_"
.
lc
$type
,
$full
,
$target
,
$args
->[1] )
if
$spoofed
;
next
LOOP;
}
if
(
$targ_type
eq
'nick_ext'
) {
$targs
->{
$target
}->[1] =
$self
->_state_peer_name(
$targs
->{
$target
}->[1] );
if
(
$targs
->{
$target
}->[2] and !
$self
->state_user_is_operator(
$nick
) ) {
push
@{
$ref
}, [
'481'
];
next
LOOP;
}
if
(
$targs
->{
$target
}->[1] ne
$self
->server_name() ) {
$self
->{ircd}->send_output( {
prefix
=>
$nick
,
command
=>
$type
,
params
=> [
$target
,
$args
->[1] ] },
$self
->_state_peer_route(
$targs
->{
$target
}->[1] ) );
next
LOOP;
}
if
(
uc
(
$targs
->{
$target
}->[0] ) eq
'OPERS'
) {
unless
(
$self
->state_user_is_operator(
$nick
) ) {
push
@{
$ref
}, [
'481'
];
next
LOOP;
}
$self
->{ircd}->send_output( {
prefix
=>
$full
,
command
=>
$type
,
params
=> [
$target
,
$args
->[1] ] },
keys
%{
$self
->{state}->{localops} } );
next
LOOP;
}
my
@local
=
$self
->_state_find_user_host(
$targs
->{
$target
}->[0],
$targs
->{
$target
}->[2] );
if
(
scalar
@local
== 1 ) {
my
$ref
=
shift
@local
;
if
(
$ref
->[0] eq
'spoofed'
) {
$self
->{ircd}->send_event(
"daemon_"
.
lc
$type
,
$full
,
$ref
->[1],
$args
->[1] );
}
else
{
$self
->{ircd}->send_output( {
prefix
=>
$full
,
command
=>
$type
,
params
=> [
$target
,
$args
->[1] ] },
$ref
->[0] );
}
}
else
{
push
@{
$ref
}, [
'407'
,
$target
];
next
LOOP;
}
}
my
$channel
;
my
$status_msg
;
if
(
$targ_type
eq
'channel'
) {
$channel
=
$self
->_state_chan_name(
$target
);
}
if
(
$targ_type
eq
'channel_ext'
) {
$channel
=
$self
->_state_chan_name(
$targs
->{target}->[1] );
$status_msg
=
$targs
->{target}->[0];
}
if
(
$channel
and
$status_msg
and !
$self
->state_user_chan_mode(
$nick
,
$channel
) ) {
push
@{
$ref
}, [
'482'
,
$target
];
next
LOOP;
}
if
(
$channel
and
$self
->state_chan_mode_set(
$channel
,
'n'
) and !
$self
->state_is_chan_member(
$nick
,
$channel
) ) {
push
@{
$ref
}, [
'404'
,
$channel
];
next
LOOP;
}
if
(
$channel
and
$self
->state_chan_mode_set(
$channel
,
'm'
) and !
$self
->state_user_chan_mode(
$nick
,
$channel
) ) {
push
@{
$ref
}, [
'404'
,
$channel
];
next
LOOP;
}
if
(
$channel
and
$self
->_state_user_banned(
$nick
,
$channel
) and !
$self
->state_user_chan_mode(
$nick
,
$channel
) ) {
push
@{
$ref
}, [
'404'
,
$channel
];
next
LOOP;
}
if
(
$channel
) {
my
$common
= { };
my
$msg
= {
command
=>
$type
,
params
=> [ (
$status_msg
?
$target
:
$channel
),
$args
->[1] ] };
foreach
my
$member
(
$self
->state_chan_list(
$channel
,
$status_msg
) ) {
next
if
$self
->_state_user_is_deaf(
$member
);
$common
->{
$self
->_state_user_route(
$member
) }++;
}
delete
$common
->{
$self
->_state_user_route(
$nick
) };
foreach
my
$route_id
(
keys
%{
$common
} ) {
$msg
->{prefix} =
$nick
;
$msg
->{prefix} =
$full
if
$self
->_connection_is_client(
$route_id
);
unless
(
$route_id
eq
'spoofed'
) {
$self
->{ircd}->send_output(
$msg
,
$route_id
);
}
else
{
my
$tmsg
=
$type
eq
'PRIVMSG'
?
'public'
:
'notice'
;
$self
->{ircd}->send_event(
"daemon_$tmsg"
,
$full
,
$channel
,
$args
->[1] );
}
}
next
LOOP;
}
my
$server
=
$self
->server_name();
if
(
$self
->state_nick_exists(
$target
) ) {
$target
=
$self
->state_user_nick(
$target
);
if
(
my
$away
=
$self
->_state_user_away_msg(
$target
) ) {
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'301'
,
params
=> [
$nick
,
$target
,
$away
] };
}
my
$targ_umode
=
$self
->state_user_umode(
$target
);
if
(
$targ_umode
and
$targ_umode
=~ /[Gg]/ ) {
my
$targ_rec
=
$self
->{state}->{users}->{ u_irc
$target
};
if
( (
$targ_umode
=~ /G/ and ( !
$self
->state_users_share_chan(
$target
,
$nick
) or !
$targ_rec
->{accepts}->{ u_irc
$nick
} ) ) or (
$targ_umode
=~ /g/ and !
$targ_rec
->{accepts}->{ u_irc
$nick
} ) ) {
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'716'
,
params
=> [
$nick
,
$target
,
'is in +g mode (server side ignore)'
] };
if
( !
$targ_rec
->{last_caller} or (
time
() -
$targ_rec
->{last_caller} ) >= 60 ) {
my
(
$n
,
$uh
) =
split
/!/,
$self
->state_user_full(
$nick
);
$self
->{ircd}->send_output( {
prefix
=>
$server
,
command
=>
'718'
,
params
=> [
$target
,
"$n\[$uh\]"
,
'is messaging you, and you are umode +g.'
] },
$targ_rec
->{route_id} )
unless
$targ_rec
->{route_id} eq
'spoofed'
;
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'717'
,
params
=> [
$nick
,
$target
,
'has been informed that you messaged them.'
] };
}
$targ_rec
->{last_caller} =
time
();
next
LOOP;
}
}
my
$msg
= {
prefix
=>
$nick
,
command
=>
$type
,
params
=> [
$target
,
$args
->[1] ] };
my
$route_id
=
$self
->_state_user_route(
$target
);
if
(
$route_id
eq
'spoofed'
) {
$msg
->{prefix} =
$full
;
$self
->{ircd}->send_event(
"daemon_"
.
lc
$type
,
$full
,
$target
,
$args
->[1] );
}
else
{
$msg
->{prefix} =
$full
if
$self
->_connection_is_client(
$route_id
);
$self
->{ircd}->send_output(
$msg
,
$route_id
);
}
next
LOOP;
}
}
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_cmd_accept {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
my
$server
=
$self
->server_name();
my
$ref
= [ ];
my
$args
= [
@_
];
my
$count
=
scalar
@{
$args
};
SWITCH: {
if
( !
$count
or !
$args
->[0] or
$args
->[0] eq
'*'
) {
my
$record
=
$self
->{state}->{users}->{ u_irc
$nick
};
my
@list
;
foreach
my
$accept
(
keys
%{
$record
->{accepts} } ) {
unless
(
$self
->state_nick_exists(
$accept
) ) {
delete
$record
->{accepts}->{
$accept
};
next
;
}
push
@list
,
$self
->state_user_nick(
$accept
);
}
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'281'
,
params
=> [
$nick
,
join
(
' '
,
@list
) ] }
if
@list
;
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'282'
,
params
=> [
$nick
,
'End of /ACCEPT list'
] };
last
SWITCH;
}
}
my
$record
=
$self
->{state}->{users}->{ u_irc
$nick
};
for
(
keys
%{
$record
->{accepts} } ) {
delete
$record
->{accepts}->{
$_
}
unless
$self
->state_nick_exists(
$_
);
}
OUTER:
foreach
my
$target
(
split
/,/,
$args
->[0] ) {
if
(
my
(
$foo
) =
$target
=~ /^\-(.+)$/ ) {
my
$dfoo
=
delete
$record
->{accepts}->{ u_irc
$foo
};
unless
(
$dfoo
) {
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'458'
,
params
=> [
$nick
,
$foo
,
"doesn\'t exist"
] };
}
delete
$self
->{state}->{accepts}->{ u_irc
$foo
}->{ u_irc
$nick
};
delete
$self
->{state}->{accepts}->{ u_irc
$foo
}
unless
keys
%{
$self
->{state}->{accepts}->{ u_irc
$foo
} };
next
OUTER;
}
unless
(
$self
->state_nick_exists(
$target
) ) {
push
@{
$ref
}, [
'401'
,
$target
];
next
OUTER;
}
if
(
$record
->{accepts}->{ u_irc
$target
} ) {
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'457'
,
params
=> [
$nick
,
$self
->state_user_nick(
$target
),
'already exists'
] };
next
OUTER;
}
if
(
$record
->{umode} and
$record
->{umode} =~ /G/ and
$self
->_state_users_share_chan(
$nick
,
$target
) ) {
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'457'
,
params
=> [
$nick
,
$self
->state_user_nick(
$target
),
'already exists'
] };
next
OUTER;
}
$self
->{state}->{accepts}->{ u_irc
$target
}->{ u_irc
$nick
} =
$record
->{accepts}->{ u_irc
$target
} =
time
();
my
@list
=
map
{
$self
->state_user_nick(
$_
) }
keys
%{
$record
->{accepts} };
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'281'
,
params
=> [
$nick
,
join
(
' '
,
@list
) ] }
if
@list
;
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'282'
,
params
=> [
$nick
,
'End of /ACCEPT list'
] };
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_cmd_quit {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
my
$qmsg
=
shift
||
'Client Quit'
;
my
$ref
= [ ];
my
$full
=
$self
->state_user_full(
$nick
);
$nick
= u_irc
$nick
;
my
$record
=
delete
$self
->{state}->{peers}->{
uc
$self
->server_name() }->{users}->{
$nick
};
$self
->{ircd}->send_output( {
prefix
=>
$record
->{nick},
command
=>
'QUIT'
,
params
=> [
$qmsg
] },
$self
->_state_connected_peers() )
unless
$record
->{killed};
push
@{
$ref
}, {
prefix
=>
$full
,
command
=>
'QUIT'
,
params
=> [
$qmsg
] };
$self
->{ircd}->send_event(
"daemon_quit"
,
$full
,
$qmsg
);
delete
$self
->{state}->{users}->{
$_
}->{accepts}->{ u_irc
$nick
}
for
keys
%{
$record
->{accepts} };
my
$common
= { };
foreach
my
$uchan
(
keys
%{
$record
->{chans} } ) {
delete
$self
->{state}->{chans}->{
$uchan
}->{users}->{
$nick
};
foreach
my
$user
(
$self
->state_chan_list(
$uchan
) ) {
next
unless
$self
->_state_is_local_user(
$user
);
$common
->{
$user
} =
$self
->_state_user_route(
$user
);
}
unless
(
scalar
keys
%{
$self
->{state}->{chans}->{
$uchan
}->{users} } ) {
delete
$self
->{state}->{chans}->{
$uchan
};
}
}
push
( @{
$ref
},
$common
->{
$_
} )
for
keys
%{
$common
};
$self
->{state}->{stats}->{ops_online}--
if
$record
->{umode} =~ /o/;
$self
->{state}->{stats}->{invisible}--
if
$record
->{umode} =~ /i/;
delete
$self
->{state}->{users}->{
$nick
}
unless
$record
->{nick_collision};
delete
$self
->{state}->{localops}->{
$record
->{route_id} };
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_cmd_ping {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
my
$server
=
$self
->server_name();
my
$args
= [
@_
];
my
$count
=
scalar
@{
$args
};
my
$ref
= [ ];
SWITCH: {
if
( !
$count
) {
push
@{
$ref
}, [
'409'
];
last
SWITCH;
}
if
(
$count
>= 2 and !
$self
->state_peer_exists(
$args
->[1] ) ) {
push
@{
$ref
}, [
'402'
,
$args
->[1] ];
last
SWITCH;
}
if
(
$count
>= 2 and (
uc
$args
->[1] ne
uc
$server
) ) {
my
$target
=
$self
->_state_peer_name(
$args
->[1] );
$self
->{ircd}->send_output( {
command
=>
'PING'
,
params
=> [
$nick
,
$target
] },
$self
->_state_peer_route(
$args
->[1] ) );
last
SWITCH;
}
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'PONG'
,
params
=> [
$server
,
$args
->[0] ] };
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_cmd_pong {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
my
$server
=
uc
$self
->server_name();
my
$args
= [
@_
];
my
$count
=
scalar
@{
$args
};
my
$ref
= [ ];
SWITCH: {
if
( !
$count
) {
push
@{
$ref
}, [
'409'
];
last
SWITCH;
}
if
(
$count
>= 2 and !
$self
->state_peer_exists(
$args
->[1] ) ) {
push
@{
$ref
}, [
'402'
,
$args
->[1] ];
last
SWITCH;
}
if
(
$count
>= 2 and (
uc
$args
->[1] ne
uc
$server
) ) {
my
$target
=
$self
->_state_peer_name(
$args
->[1] );
$self
->{ircd}->send_output( {
command
=>
'PONG'
,
params
=> [
$nick
,
$target
] },
$self
->_state_peer_route(
$args
->[1] ) );
last
SWITCH;
}
delete
$self
->{state}->{users}->{ u_irc
$nick
}->{pinged};
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_cmd_pass {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
my
$server
=
uc
$self
->server_name();
my
$ref
= [ [
'462'
] ];
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_cmd_user {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
my
$server
=
uc
$self
->server_name();
my
$ref
= [ [
'462'
] ];
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_cmd_oper {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
my
$server
=
$self
->server_name();
my
$ref
= [ ];
my
$args
= [
@_
];
my
$count
=
scalar
@{
$args
};
SWITCH: {
last
SWITCH
if
$self
->state_user_is_operator(
$nick
);
if
( !
$count
or
$count
< 2 ) {
push
@{
$ref
}, [
'461'
,
'OPER'
];
last
SWITCH;
}
my
$result
=
$self
->_state_o_line(
$nick
, @{
$args
} );
if
( !
$result
or
$result
<= 0 ) {
push
@{
$ref
}, [
'491'
];
last
SWITCH;
}
$self
->{stats}->{ops}++;
my
$record
=
$self
->{state}->{users}->{ u_irc
$nick
};
$record
->{umode} .=
'o'
;
$self
->{state}->{stats}->{ops_online}++;
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'381'
,
params
=> [
$nick
,
'You are now an IRC operator'
] };
my
$reply
= {
prefix
=>
$nick
,
command
=>
'MODE'
,
params
=> [
$nick
,
'+o'
] };
$self
->{ircd}->send_output(
$reply
,
$self
->_state_connected_peers() );
$self
->{ircd}->send_event(
"daemon_umode"
,
$self
->state_user_full(
$nick
),
'+o'
);
my
$route_id
=
$self
->_state_user_route(
$nick
);
$self
->{state}->{localops}->{
$route_id
} =
time
();
$self
->{ircd}->antiflood(
$route_id
, 0 );
push
@{
$ref
},
$reply
;
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_cmd_die {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
my
$server
=
$self
->server_name();
my
$ref
= [ ];
SWITCH: {
if
( !
$self
->state_user_is_operator(
$nick
) ) {
push
@{
$ref
}, [
'481'
];
last
SWITCH;
}
$self
->{ircd}->send_event(
"daemon_die"
,
$nick
);
$self
->{ircd}->
shutdown
();
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_cmd_rehash {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
my
$server
=
$self
->server_name();
my
$ref
= [ ];
SWITCH: {
if
( !
$self
->state_user_is_operator(
$nick
) ) {
push
@{
$ref
}, [
'481'
];
last
SWITCH;
}
$self
->{ircd}->send_event(
"daemon_rehash"
,
$nick
);
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'383'
,
params
=> [
$nick
,
'ircd.conf'
,
'Rehashing'
] };
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_cmd_locops {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
my
$server
=
$self
->server_name();
my
$ref
= [ ];
my
$args
= [
@_
];
my
$count
=
scalar
@{
$args
};
SWITCH: {
if
( !
$self
->state_user_is_operator(
$nick
) ) {
push
@{
$ref
}, [
'481'
];
last
SWITCH;
}
if
( !
$count
) {
push
@{
$ref
}, [
'461'
,
'LOCOPS'
];
last
SWITCH;
}
my
$full
=
$self
->state_user_full(
$nick
);
$self
->{ircd}->send_output( {
prefix
=>
$full
,
command
=>
'WALLOPS'
,
params
=> [
'LOCOPS - '
.
$args
->[0] ] },
keys
%{
$self
->{state}->{locops} } );
$self
->{ircd}->send_event(
"daemon_locops"
,
$full
,
$args
->[0] );
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_cmd_wallops {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
my
$server
=
$self
->server_name();
my
$ref
= [ ];
my
$args
= [
@_
];
my
$count
=
scalar
@{
$args
};
SWITCH: {
if
( !
$self
->state_user_is_operator(
$nick
) ) {
push
@{
$ref
}, [
'481'
];
last
SWITCH;
}
if
( !
$count
) {
push
@{
$ref
}, [
'461'
,
'WALLOPS'
];
last
SWITCH;
}
my
$full
=
$self
->state_user_full(
$nick
);
$self
->{ircd}->send_output( {
prefix
=>
$nick
,
command
=>
'WALLOPS'
,
params
=> [
$args
->[0] ] },
$self
->_state_connected_peers() );
$self
->{ircd}->send_output( {
prefix
=>
$full
,
command
=>
'WALLOPS'
,
params
=> [
'OPERWALL - '
.
$args
->[0] ] },
keys
%{
$self
->{state}->{operwall} } );
$self
->{ircd}->send_event(
"daemon_operwall"
,
$full
,
$args
->[0] );
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_cmd_operwall {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
my
$server
=
$self
->server_name();
my
$ref
= [ ];
my
$args
= [
@_
];
my
$count
=
scalar
@{
$args
};
SWITCH: {
if
( !
$self
->state_user_is_operator(
$nick
) ) {
push
@{
$ref
}, [
'481'
];
last
SWITCH;
}
if
( !
$count
) {
push
@{
$ref
}, [
'461'
,
'OPERWALL'
];
last
SWITCH;
}
my
$full
=
$self
->state_user_full(
$nick
);
$self
->{ircd}->send_output( {
prefix
=>
$nick
,
command
=>
'WALLOPS'
,
params
=> [
$args
->[0] ] },
$self
->_state_connected_peers() );
$self
->{ircd}->send_output( {
prefix
=>
$full
,
command
=>
'WALLOPS'
,
params
=> [
'OPERWALL - '
.
$args
->[0] ] },
keys
%{
$self
->{state}->{operwall} } );
$self
->{ircd}->send_event(
"daemon_operwall"
,
$full
,
$args
->[0] );
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_cmd_connect {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
my
$server
=
$self
->server_name();
my
$ref
= [ ];
my
$args
= [
@_
];
my
$count
=
scalar
@{
$args
};
SWITCH: {
if
( !
$self
->state_user_is_operator(
$nick
) ) {
push
@{
$ref
}, [
'481'
];
last
SWITCH;
}
if
( !
$count
) {
push
@{
$ref
}, [
'461'
,
'CONNECT'
];
last
SWITCH;
}
if
(
$count
>= 3 and !
$self
->state_peer_exists(
$args
->[2] ) ) {
push
@{
$ref
}, [
'402'
,
$args
->[2] ];
last
SWITCH;
}
if
(
$count
>= 3 and (
uc
$server
ne
uc
$args
->[2] ) ) {
$args
->[2] =
$self
->_state_peer_name(
$args
->[2] );
$self
->{ircd}->send_output( {
prefix
=>
$nick
,
command
=>
'CONNECT'
,
params
=>
$args
},
$self
->_state_peer_route(
$args
->[2] ) );
last
SWITCH;
}
if
( !
$self
->{config}->{peers}->{
uc
$args
->[0] } or
$self
->{config}->{peers}->{
uc
$args
->[0] }->{type} ne
'r'
) {
push
@{
$ref
}, {
command
=>
'NOTICE'
,
params
=> [
$nick
,
"Connect: Host $args->[0] is not listed in ircd.conf"
] };
last
SWITCH;
}
if
(
my
$peer_name
=
$self
->_state_peer_name(
$args
->[0] ) ) {
push
@{
$ref
}, {
command
=>
'NOTICE'
,
params
=> [
$nick
,
"Connect: Server $args->[0] already exists from $peer_name."
] };
last
SWITCH;
}
my
$connector
=
$self
->{config}->{peers}->{
uc
$args
->[0] };
my
$name
=
$connector
->{name};
my
$rport
=
$args
->[1] ||
$connector
->{rport};
my
$raddr
=
$connector
->{raddress};
$self
->{ircd}->add_connector(
remoteaddress
=>
$raddr
,
remoteport
=>
$rport
,
name
=>
$name
);
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_cmd_squit {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
my
$server
=
$self
->server_name();
my
$ref
= [ ];
my
$args
= [
@_
];
my
$count
=
scalar
@{
$args
};
SWITCH: {
if
( !
$self
->state_user_is_operator(
$nick
) ) {
push
@{
$ref
}, [
'481'
];
last
SWITCH;
}
if
( !
$count
) {
push
@{
$ref
}, [
'461'
,
'SQUIT'
];
last
SWITCH;
}
if
( !
$self
->state_peer_exists(
$args
->[0] ) or (
uc
$server
eq
uc
$args
->[0] ) ) {
push
@{
$ref
}, [
'402'
,
$args
->[0] ];
last
SWITCH;
}
my
$peer
=
uc
$args
->[0];
my
$reason
=
$args
->[1] ||
'No Reason'
;
$args
->[0] =
$self
->_state_peer_name(
$peer
);
$args
->[1] =
$reason
;
unless
(
scalar
grep
{
$_
eq
$peer
}
keys
%{
$self
->{state}->{peers}->{
uc
$server
}->{peers} } ) {
$self
->{ircd}->send_output( {
prefix
=>
$nick
,
command
=>
'SQUIT'
,
params
=>
$args
},
$self
->_state_peer_route(
$args
->[0] ) );
last
SWITCH;
}
my
$conn_id
=
$self
->_state_peer_route(
$peer
);
$self
->{ircd}->disconnect(
$conn_id
,
$reason
);
$self
->{ircd}->send_output( {
command
=>
'ERROR'
,
params
=> [
join
' '
,
'Closing Link:'
,
$self
->_client_ip(
$conn_id
),
$args
->[0],
"($nick)"
] },
$conn_id
);
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_cmd_rkline {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
my
$server
=
$self
->server_name();
my
$ref
= [ ];
my
$args
= [
@_
];
my
$count
=
scalar
@{
$args
};
SWITCH: {
if
( !
$self
->state_user_is_operator(
$nick
) ) {
push
@{
$ref
}, [
'481'
];
last
SWITCH;
}
if
( !
$count
or
$count
< 1 ) {
push
@{
$ref
}, [
'461'
,
'RKLINE'
];
last
SWITCH;
}
my
$duration
= 0;
if
(
$args
->[0] =~ /^\d+$/ ) {
$duration
=
shift
@{
$args
};
$duration
= 14400
if
$duration
> 14400;
}
my
$mask
=
shift
@{
$args
};
unless
(
$mask
) {
push
@{
$ref
}, [
'461'
,
'RKLINE'
];
last
SWITCH;
}
my
(
$user
,
$host
) =
split
/\@/,
$mask
;
unless
(
$user
and
$host
) {
last
SWITCH;
}
my
$full
=
$self
->state_user_full(
$nick
);
my
$us
= 0;
my
$ucserver
=
uc
$server
;
if
(
$args
->[0] and
uc
$args
->[0] eq
'ON'
and
scalar
@{
$args
} < 2 ) {
push
@{
$ref
}, [
'461'
,
'RKLINE'
];
last
SWITCH;
}
my
(
$target
,
$reason
);
if
(
$args
->[0] and
uc
$args
->[0] eq
'ON'
) {
$target
=
shift
@{
$args
};
$reason
=
shift
@{
$args
} ||
'No Reason'
;
my
%targets
;
foreach
my
$peer
(
keys
%{
$self
->{state}->{peers} } ) {
if
( matches_mask(
$target
,
$peer
) ) {
if
(
$ucserver
eq
$peer
) {
$us
= 1;
}
else
{
$targets
{
$self
->_state_peer_route(
$peer
) }++;
}
}
}
$self
->{ircd}->send_output( {
prefix
=>
$nick
,
command
=>
'RKLINE'
,
params
=> [
$target
,
$duration
,
$user
,
$host
,
$reason
],
colonify
=> 0 },
grep
{
$self
->_state_peer_capab(
$_
,
'KLN'
) }
keys
%targets
);
}
else
{
$us
= 1;
}
if
(
$us
) {
$target
=
$server
unless
$target
;
unless
(
$reason
) {
$reason
=
pop
@{
$args
} ||
'No Reason'
;
}
$self
->{ircd}->send_event(
"daemon_rkline"
,
$full
,
$target
,
$duration
,
$user
,
$host
,
$reason
);
push
@{
$self
->{state}->{rklines} }, {
setby
=>
$full
,
setat
=>
time
(),
target
=>
$target
,
duration
=>
$duration
,
user
=>
$user
,
host
=>
$host
,
reason
=>
$reason
};
$self
->_terminate_conn_error(
$_
,
'K-Lined'
)
for
$self
->_state_local_users_match_rkline(
$user
,
$host
);
}
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_cmd_kline {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
my
$server
=
$self
->server_name();
my
$ref
= [ ];
my
$args
= [
@_
];
my
$count
=
scalar
@{
$args
};
SWITCH: {
if
( !
$self
->state_user_is_operator(
$nick
) ) {
push
@{
$ref
}, [
'481'
];
last
SWITCH;
}
if
( !
$count
or
$count
< 1 ) {
push
@{
$ref
}, [
'461'
,
'KLINE'
];
last
SWITCH;
}
my
$duration
= 0;
if
(
$args
->[0] =~ /^\d+$/ ) {
$duration
=
shift
@{
$args
};
$duration
= 14400
if
$duration
> 14400;
}
my
$mask
=
shift
@{
$args
};
unless
(
$mask
) {
push
@{
$ref
}, [
'461'
,
'KLINE'
];
last
SWITCH;
}
my
(
$user
,
$host
);
if
(
$mask
!~ /\@/ ) {
if
(
my
$rogue
=
$self
->_state_user_full(
$mask
) ) {
(
$user
,
$host
) = (
split
/[!\@]/,
$rogue
)[1..2]
}
else
{
push
@{
$ref
}, [
'401'
,
$mask
];
last
SWITCH;
}
}
else
{
(
$user
,
$host
) =
split
/\@/,
$mask
;
}
my
$full
=
$self
->state_user_full(
$nick
);
my
$us
= 0;
my
$ucserver
=
uc
$server
;
if
(
$args
->[0] and
uc
$args
->[0] eq
'ON'
and
scalar
@{
$args
} < 2 ) {
push
@{
$ref
}, [
'461'
,
'KLINE'
];
last
SWITCH;
}
my
(
$target
,
$reason
);
if
(
$args
->[0] and
uc
$args
->[0] eq
'ON'
) {
$target
=
shift
@{
$args
};
$reason
=
shift
@{
$args
} ||
'No Reason'
;
my
%targets
;
foreach
my
$peer
(
keys
%{
$self
->{state}->{peers} } ) {
if
( matches_mask(
$target
,
$peer
) ) {
if
(
$ucserver
eq
$peer
) {
$us
= 1;
}
else
{
$targets
{
$self
->_state_peer_route(
$peer
) }++;
}
}
}
$self
->{ircd}->send_output( {
prefix
=>
$nick
,
command
=>
'KLINE'
,
params
=> [
$target
,
$duration
,
$user
,
$host
,
$reason
],
colonify
=> 0 },
grep
{
$self
->_state_peer_capab(
$_
,
'KLN'
) }
keys
%targets
);
}
else
{
$us
= 1;
}
if
(
$us
) {
$target
=
$server
unless
$target
;
unless
(
$reason
) {
$reason
=
pop
@{
$args
} ||
'No Reason'
;
}
$self
->{ircd}->send_event(
"daemon_kline"
,
$full
,
$target
,
$duration
,
$user
,
$host
,
$reason
);
push
@{
$self
->{state}->{klines} }, {
setby
=>
$full
,
setat
=>
time
(),
target
=>
$target
,
duration
=>
$duration
,
user
=>
$user
,
host
=>
$host
,
reason
=>
$reason
};
$self
->_terminate_conn_error(
$_
,
'K-Lined'
)
for
$self
->_state_local_users_match_gline(
$user
,
$host
);
}
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_cmd_unkline {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
my
$server
=
$self
->server_name();
my
$ref
= [ ];
my
$args
= [
@_
];
my
$count
=
scalar
@{
$args
};
SWITCH: {
if
( !
$self
->state_user_is_operator(
$nick
) ) {
push
@{
$ref
}, [
'481'
];
last
SWITCH;
}
if
( !
$count
or
$count
< 1 ) {
push
@{
$ref
}, [
'461'
,
'UNKLINE'
];
last
SWITCH;
}
my
(
$user
,
$host
);
if
(
$args
->[0] !~ /\@/ ) {
if
(
my
$rogue
=
$self
->state_user_full(
$args
->[0] ) ) {
(
$user
,
$host
) = (
split
/[!\@]/,
$rogue
)[1..2]
}
else
{
push
@{
$ref
}, [
'401'
,
$args
->[0] ];
last
SWITCH;
}
}
else
{
(
$user
,
$host
) =
split
/\@/,
$args
->[0];
}
my
$full
=
$self
->state_user_full(
$nick
);
my
$us
= 0;
my
$ucserver
=
uc
$server
;
if
(
$count
> 1 and
uc
$args
->[2] eq
'ON'
and
$count
< 3 ) {
push
@{
$ref
}, [
'461'
,
'UNKLINE'
];
last
SWITCH;
}
if
(
$count
> 1 and
$args
->[2] and
uc
$args
->[2] eq
'ON'
) {
my
$target
=
$args
->[2];
my
%targets
;
foreach
my
$peer
(
keys
%{
$self
->{state}->{peers} } ) {
if
( matches_mask(
$target
,
$peer
) ) {
if
(
$ucserver
eq
$peer
) {
$us
= 1;
}
else
{
$targets
{
$self
->_state_peer_route(
$peer
) }++;
}
}
}
$self
->{ircd}->send_output( {
prefix
=>
$nick
,
command
=>
'UNKLINE'
,
params
=> [
$target
,
$user
,
$host
],
colonify
=> 0 },
grep
{
$self
->_state_peer_capab(
$_
,
'UNKLN'
) }
keys
%targets
);
}
else
{
$us
= 1;
}
if
(
$us
) {
my
$target
=
$args
->[3] ||
$server
;
$self
->{ircd}->send_event(
"daemon_unkline"
,
$full
,
$target
,
$user
,
$host
);
my
$i
= 0;
for
( @{
$self
->{state}->{klines} } ) {
splice
( @{
$self
->{state}->{klines} },
$i
, 1),
last
if
$_
->{user} eq
$user
and
$_
->{host} eq
$host
;
++
$i
;
}
}
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_cmd_gline {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
my
$server
=
$self
->server_name();
my
$ref
= [ ];
my
$args
= [
@_
];
my
$count
=
scalar
@{
$args
};
SWITCH: {
if
( !
$self
->state_user_is_operator(
$nick
) ) {
push
@{
$ref
}, [
'481'
];
last
SWITCH;
}
if
( !
$count
or
$count
< 2 ) {
push
@{
$ref
}, [
'461'
,
'GLINE'
];
last
SWITCH;
}
if
(
$args
->[0] !~ /\@/ and !
$self
->state_nick_exists(
$args
->[0] ) ) {
push
@{
$ref
}, [
'401'
,
$args
->[0] ];
last
SWITCH;
}
my
(
$user_part
,
$host_part
);
if
(
$args
->[0] =~ /\@/ ) {
(
$user_part
,
$host_part
) = (
split
/[!@]/,
$self
->state_user_full(
$args
->[0] ) )[1..2];
}
else
{
(
$user_part
,
$host_part
) =
split
/\@/,
$args
->[0];
}
my
$time
=
time
();
my
$reason
=
join
' '
,
$args
->[1], strftime(
"(%c)"
,
localtime
(
$time
) );
my
$full
=
$self
->state_user_full(
$nick
);
push
@{
$self
->{state}->{glines} }, {
setby
=>
$full
,
setat
=>
time
(),
user
=>
$user_part
,
host
=>
$host_part
,
reason
=>
$reason
};
$self
->{ircd}->send_output( {
prefix
=>
$nick
,
command
=>
'GLINE'
,
params
=> [
$user_part
,
$host_part
,
$reason
],
colonify
=> 0 },
grep
{
$self
->_state_peer_capab(
$_
,
'GLN'
) }
$self
->_state_connected_peers() );
$self
->{ircd}->send_event(
"daemon_gline"
,
$full
,
$user_part
,
$host_part
,
$reason
);
$self
->_terminate_conn_error(
$_
,
'G-Lined'
)
for
$self
->_state_local_users_match_gline(
$user_part
,
$host_part
);
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_cmd_kill {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
my
$server
=
$self
->server_name();
my
$ref
= [ ];
my
$args
= [
@_
];
my
$count
=
scalar
@{
$args
};
SWITCH: {
if
( !
$self
->state_user_is_operator(
$nick
) ) {
push
@{
$ref
}, [
'481'
];
last
SWITCH;
}
if
( !
$count
) {
push
@{
$ref
}, [
'461'
,
'KILL'
];
last
SWITCH;
}
if
(
$self
->state_peer_exists(
$args
->[0] ) ) {
push
@{
$ref
}, [
'483'
];
last
SWITCH;
}
if
( !
$self
->state_nick_exists(
$args
->[0] ) ) {
push
@{
$ref
}, [
'401'
,
$args
->[0] ];
last
SWITCH;
}
my
$target
=
$self
->state_user_nick(
$args
->[0] );
my
$comment
=
$args
->[1] ||
'<No reason given>'
;
if
(
$self
->_state_is_local_user(
$target
) ) {
my
$route_id
=
$self
->_state_user_route(
$target
);
$self
->{ircd}->send_output( {
prefix
=>
$nick
,
command
=>
'KILL'
,
params
=> [
$target
,
join
(
'!'
,
$server
,
$nick
) .
" ($comment)"
] },
$self
->_state_connected_peers() );
$self
->{ircd}->send_output( {
prefix
=>
$self
->state_user_full(
$nick
),
command
=>
'KILL'
,
params
=> [
$target
,
$comment
] },
$route_id
);
if
(
$route_id
eq
'spoofed'
) {
$self
->call(
'del_spoofed_nick'
,
$target
,
"Killed ($comment)"
);
}
else
{
$self
->{state}->{conns}->{
$route_id
}->{killed} = 1;
$self
->_terminate_conn_error(
$route_id
,
"Killed ($comment)"
);
}
}
else
{
$self
->{state}->{users}->{ u_irc
$target
}->{killed} = 1;
$self
->{ircd}->send_output( {
prefix
=>
$nick
,
command
=>
'KILL'
,
params
=> [
$target
,
join
(
'!'
,
$server
,
$nick
) .
" ($comment)"
] },
$self
->_state_connected_peers() );
$self
->{ircd}->send_output( @{
$self
->_daemon_peer_quit(
$target
,
"Killed ($nick ($comment))"
) } );
}
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_cmd_nick {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
my
$new
=
shift
;
my
$server
=
uc
$self
->server_name();
my
$ref
= [ ];
SWITCH: {
if
( !
$new
) {
push
@{
$ref
}, [
'431'
];
last
SWITCH;
}
my
$nicklen
=
$self
->server_config(
'NICKLEN'
);
$new
=
substr
(
$new
,0,
$nicklen
)
if
length
(
$new
) >
$nicklen
;
if
(
$nick
eq
$new
) {
last
SWITCH;
}
if
( !validate_nick_name(
$new
) ) {
push
@{
$ref
}, [
'432'
,
$new
];
last
SWITCH;
}
my
$unick
= u_irc
$nick
;
my
$unew
= u_irc
$new
;
if
(
$self
->state_nick_exists(
$new
) and
$unick
ne
$unew
) {
push
@{
$ref
}, [
'433'
,
$new
];
last
SWITCH;
}
my
$full
=
$self
->state_user_full(
$nick
);
my
$record
=
$self
->{state}->{users}->{
$unick
};
my
$common
= {
$nick
=>
$record
->{route_id} };
foreach
my
$chan
(
keys
%{
$record
->{chans} } ) {
foreach
my
$user
(
$self
->state_chan_list(
$chan
) ) {
next
unless
$self
->_state_is_local_user(
$user
);
$common
->{
$user
} =
$self
->_state_user_route(
$user
);
}
}
if
(
$unick
eq
$unew
) {
$record
->{nick} =
$new
;
$record
->{ts} =
time
();
}
else
{
$record
->{nick} =
$new
;
$record
->{ts} =
time
();
delete
$self
->{state}->{users}->{
$_
}->{accepts}->{
$unick
}
for
keys
%{
$record
->{accepts} };
delete
$record
->{accepts};
delete
$self
->{state}->{users}->{
$unick
};
$self
->{state}->{users}->{
$unew
} =
$record
;
delete
$self
->{state}->{peers}->{
$server
}->{users}->{
$unick
};
$self
->{state}->{peers}->{
$server
}->{users}->{
$unew
} =
$record
;
foreach
my
$chan
(
keys
%{
$record
->{chans} } ) {
$self
->{state}->{chans}->{
$chan
}->{users}->{
$unew
} =
delete
$self
->{state}->{chans}->{
$chan
}->{users}->{
$unick
};
}
}
my
@peers
=
$self
->_state_connected_peers();
$self
->{ircd}->send_output( {
prefix
=>
$nick
,
command
=>
'NICK'
,
params
=> [
$new
,
$record
->{ts} ] },
@peers
);
$self
->{ircd}->send_output( {
prefix
=>
$full
,
command
=>
'NICK'
,
params
=> [
$new
] },
map
{
$common
->{
$_
} }
keys
%{
$common
} );
$self
->{ircd}->send_event(
"daemon_nick"
,
$full
,
$new
);
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_cmd_away {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
my
$msg
=
shift
;
my
$server
=
$self
->server_name();
my
$ref
= [ ];
SWITCH: {
my
$record
=
$self
->{state}->{users}->{ u_irc
$nick
};
if
( !
$msg
) {
delete
$record
->{away};
$self
->{ircd}->send_output( {
prefix
=>
$nick
,
command
=>
'AWAY'
,
colonify
=> 0 },
$self
->_state_connected_peers() );
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'305'
,
params
=> [
'You are no longer marked as being away'
] };
last
SWITCH;
}
$record
->{away} =
$msg
;
$self
->{ircd}->send_output( {
prefix
=>
$nick
,
command
=>
'AWAY'
,
params
=> [
$msg
],
colonify
=> 0 },
$self
->_state_connected_peers() );
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'306'
,
params
=> [
'You have been marked as being away'
] };
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_cmd_isupport {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
my
$server
=
$self
->server_name();
my
$ref
= [ ];
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'005'
,
params
=> [
$nick
,
join
(
' '
,
map
{ (
defined
(
$self
->{config}->{isupport}->{
$_
} ) ?
join
(
'='
,
$_
,
$self
->{config}->{isupport}->{
$_
} ) :
$_
) }
qw(CALLERID EXCEPTS INVEX MAXCHANNELS MAXBANS MAXTARGETS NICKLEN TOPICLEN KICKLEN)
),
'are supported by this server'
] };
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'005'
,
params
=> [
$nick
,
join
(
' '
,
map
{ (
defined
(
$self
->{config}->{isupport}->{
$_
} ) ?
join
(
'='
,
$_
,
$self
->{config}->{isupport}->{
$_
} ) :
$_
) }
qw(CHANTYPES PREFIX CHANMODES NETWORK CASEMAPPING DEAF)
),
'are supported by this server'
] };
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_cmd_info {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
my
$target
=
shift
;
my
$server
=
$self
->server_name();
my
$ref
= [ ];
SWITCH: {
if
(
$target
and !
$self
->state_peer_exists(
$target
) ) {
push
@{
$ref
}, [
'402'
,
$target
];
last
SWITCH;
}
if
(
$target
and (
uc
$server
ne
uc
$target
) ) {
$self
->{ircd}->send_output( {
prefix
=>
$nick
,
command
=>
'INFO'
,
params
=> [
$self
->_state_peer_name(
$target
) ] },
$self
->_state_peer_route(
$target
) );
last
SWITCH;
}
push
( @{
$ref
}, {
prefix
=>
$server
,
command
=>
'371'
,
params
=> [
$nick
,
$_
] } )
for
@{
$self
->server_config(
'Info'
) };
push
( @{
$ref
}, {
prefix
=>
$server
,
command
=>
'374'
,
params
=> [
$nick
,
'End of /INFO list.'
] } );
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_cmd_version {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
my
$server
=
$self
->server_name();
my
$ref
= [ ];
my
$target
=
shift
;
SWITCH: {
if
(
$target
and !
$self
->state_peer_exists(
$target
) ) {
push
@{
$ref
}, [
'402'
,
$target
];
last
SWITCH;
}
if
(
$target
and (
uc
$server
ne
uc
$target
) ) {
$self
->{ircd}->send_output( {
prefix
=>
$nick
,
command
=>
'VERSION'
,
params
=> [
$self
->_state_peer_name(
$target
) ] },
$self
->_state_peer_route(
$target
) );
last
SWITCH;
}
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'351'
,
params
=> [
$nick
,
$self
->server_version(),
$server
,
'eGHIMZ TS5ow'
] };
push
@{
$ref
},
$_
for
@{
$self
->_daemon_cmd_isupport(
$nick
) };
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_cmd_admin {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
my
$target
=
shift
;
my
$server
=
$self
->server_name();
my
$ref
= [ ];
my
$admin
=
$self
->server_config(
'Admin'
);
SWITCH: {
if
(
$target
and !
$self
->state_peer_exists(
$target
) ) {
push
@{
$ref
}, [
'402'
,
$target
];
last
SWITCH;
}
if
(
$target
and (
uc
$server
ne
uc
$target
) ) {
$self
->{ircd}->send_output( {
prefix
=>
$nick
,
command
=>
'ADMIN'
,
params
=> [
$self
->_state_peer_name(
$target
) ] },
$self
->_state_peer_route(
$target
) );
last
SWITCH;
}
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'256'
,
params
=> [
$nick
,
$server
,
'Administrative Info'
] };
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'257'
,
params
=> [
$nick
,
$admin
->[0] ] };
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'258'
,
params
=> [
$nick
,
$admin
->[1] ] };
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'259'
,
params
=> [
$nick
,
$admin
->[2] ] };
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_cmd_summon {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
my
$server
=
$self
->server_name();
my
$ref
= [ ];
push
@{
$ref
},
'445'
;
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_cmd_time {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
my
$server
=
$self
->server_name();
my
$target
=
shift
;
my
$ref
= [ ];
SWITCH: {
if
(
$target
and !
$self
->state_peer_exists(
$target
) ) {
push
@{
$ref
}, [
'402'
,
$target
];
last
SWITCH;
}
if
(
$target
and (
uc
$server
ne
uc
$target
) ) {
$self
->{ircd}->send_output( {
prefix
=>
$nick
,
command
=>
'TIME'
,
params
=> [
$self
->_state_peer_name(
$target
) ] },
$self
->_state_peer_route(
$target
) );
last
SWITCH;
}
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'391'
,
params
=> [
$nick
,
$server
, strftime(
"%A %B %m %Y -- %T %z"
,
localtime
) ] };
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_cmd_users {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
my
$server
=
$self
->server_name();
my
$ref
= [ ];
my
$global
=
scalar
keys
%{
$self
->{state}->{users} };
my
$local
=
scalar
keys
%{
$self
->{state}->{peers}->{
uc
$server
}->{users} };
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'265'
,
params
=> [
$nick
,
"Current local users: $local Max: "
.
$self
->{state}->{stats}->{maxlocal} ] };
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'266'
,
params
=> [
$nick
,
"Current global users: $global Max: "
.
$self
->{state}->{stats}->{maxglobal} ] };
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_cmd_lusers {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
my
$server
=
$self
->server_name();
my
$ref
= [ ];
my
$invisible
=
$self
->{state}->{stats}->{invisible};
my
$users
=
scalar
(
keys
%{
$self
->{state}->{users} } ) -
$invisible
;
my
$servers
=
scalar
keys
%{
$self
->{state}->{peers} };
my
$chans
=
scalar
keys
%{
$self
->{state}->{chans} };
my
$local
=
scalar
keys
%{
$self
->{state}->{peers}->{
uc
$server
}->{users} };
my
$peers
=
scalar
keys
%{
$self
->{state}->{peers}->{
uc
$server
}->{peers} };
my
$totalconns
=
$self
->{state}->{stats}->{conns_cumlative};
my
$mlocal
=
$self
->{state}->{stats}->{maxlocal};
my
$conns
=
$self
->{state}->{stats}->{maxconns};
push
( @{
$ref
}, {
prefix
=>
$server
,
command
=>
'251'
,
params
=>[
$nick
,
"There are $users users and $invisible invisible on $servers servers"
] } );
$servers
--;
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'252'
,
params
=> [
$nick
,
$self
->{state}->{stats}->{ops_online},
"IRC Operators online"
] }
if
$self
->{state}->{stats}->{ops_online};
push
( @{
$ref
}, {
prefix
=>
$server
,
command
=>
'254'
,
params
=>[
$nick
,
$chans
,
"channels formed"
] } )
if
$chans
;
push
( @{
$ref
}, {
prefix
=>
$server
,
command
=>
'255'
,
params
=>[
$nick
,
"I have $local clients and $peers servers"
] } );
push
@{
$ref
},
$_
for
$self
->_daemon_cmd_users(
$nick
);
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'250'
,
params
=> [
$nick
,
"Highest connection count: $conns ($mlocal clients) ($totalconns connections received)"
] };
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_cmd_motd {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
my
$target
=
shift
;
my
$server
=
$self
->server_name();
my
$ref
= [ ];
my
$motd
=
$self
->server_config(
'MOTD'
);
SWITCH: {
if
(
$target
and !
$self
->state_peer_exists(
$target
) ) {
push
@{
$ref
}, [
'402'
,
$target
];
last
SWITCH;
}
if
(
$target
and (
uc
$server
ne
uc
$target
) ) {
$self
->{ircd}->send_output( {
prefix
=>
$nick
,
command
=>
'MOTD'
,
params
=> [
$self
->_state_peer_name(
$target
) ] },
$self
->_state_peer_route(
$target
) );
last
SWITCH;
}
if
(
$motd
and
ref
$motd
eq
'ARRAY'
) {
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'375'
,
params
=> [
$nick
,
"- $server Message of the day - "
] };
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'372'
,
params
=> [
$nick
,
"- $_"
] }
for
@{
$motd
};
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'376'
,
params
=> [
$nick
,
"End of MOTD command"
] };
}
else
{
push
@{
$ref
},
'422'
;
}
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_cmd_stats {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
my
$char
=
shift
;
my
$target
=
shift
;
my
$server
=
$self
->server_name();
my
$ref
= [ ];
SWITCH: {
unless
(
$char
) {
push
@{
$ref
}, [
'461'
,
'STATS'
];
last
SWITCH;
}
$char
=
substr
$char
, 0, 1;
unless
(
$char
=~ /[ump]/ ) {
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'263'
,
params
=> [
$nick
,
'Server load is temporarily too heavy. Please wait a while and try again.'
] };
last
SWITCH;
}
if
(
$target
and !
$self
->state_peer_exists(
$target
) ) {
push
@{
$ref
}, [
'402'
,
$target
];
last
SWITCH;
}
if
(
$target
and (
uc
$server
ne
uc
$target
) ) {
$self
->{ircd}->send_output( {
prefix
=>
$nick
,
command
=>
'STATS'
,
params
=> [
$char
,
$self
->_state_peer_name(
$target
) ] },
$self
->_state_peer_route(
$target
) );
last
SWITCH;
}
SWITCH2: {
if
(
$char
eq
'u'
) {
my
$uptime
=
time
() -
$self
->server_config(
'created'
);
my
$days
=
int
$uptime
/ 86400;
my
$remain
=
$uptime
% 86400;
my
$hours
=
int
$remain
/ 3600;
$remain
%= 3600;
my
$mins
=
int
$remain
/ 60;
$remain
%= 60;
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'242'
,
params
=> [
$nick
,
sprintf
(
"Server Up %d days, %2.2d:%2.2d:%2.2d"
,
$days
,
$hours
,
$mins
,
$remain
) ] };
my
$totalconns
=
$self
->{state}->{stats}->{conns_cumlative};
my
$local
=
$self
->{state}->{stats}->{maxlocal};
my
$conns
=
$self
->{state}->{stats}->{maxconns};
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'250'
,
params
=> [
$nick
,
"Highest connection count: $conns ($local clients) ($totalconns connections received)"
] };
last
SWITCH2;
}
if
(
$char
eq
'm'
) {
my
$cmds
=
$self
->{state}->{stats}->{cmds};
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'212'
,
params
=> [
$nick
,
$_
,
$cmds
->{
$_
}->{
local
},
$cmds
->{
$_
}->{bytes},
$cmds
->{
$_
}->{remote} ] }
for
sort
keys
%{
$cmds
};
last
SWITCH2;
}
if
(
$char
eq
'p'
) {
my
@ops
=
map
{
$self
->_client_nickname(
$_
) }
keys
%{
$self
->{state}->{localops} };
foreach
my
$op
(
sort
@ops
) {
my
$record
=
$self
->{state}->{users}->{ u_irc
$op
};
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'249'
,
params
=> [
$nick
,
sprintf
(
"[O] %s (%s\@%s) Idle: %u"
,
$record
->{nick},
$record
->{auth}->{ident},
$record
->{auth}->{hostname},
time
() -
$record
->{idle_time} ) ] };
}
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'249'
,
params
=> [
$nick
,
scalar
@ops
.
" OPER(s)"
] };
last
SWITCH2;
}
}
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'219'
,
params
=> [
$nick
,
$char
,
'End of /STATS report'
] };
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_cmd_userhost {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
my
$server
=
$self
->server_name();
my
$ref
= [ ];
my
$str
=
''
;
foreach
my
$query
(
@_
) {
my
(
$proper
,
$userhost
) =
split
/!/,
$self
->state_user_full(
$query
);
$str
=
join
(
' '
,
$str
,
$proper
. (
$self
->state_user_is_operator(
$proper
) ?
'*'
:
''
) .
'='
. (
$self
->_state_user_away(
$proper
) ?
'-'
:
'+'
) .
$userhost
)
if
$proper
and
$userhost
;
}
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'302'
,
params
=> [
$nick
, (
$str
?
$str
:
':'
) ] };
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_cmd_ison {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
my
$server
=
$self
->server_name();
my
$ref
= [ ];
my
$args
= [
@_
];
my
$count
=
scalar
@{
$args
};
SWITCH: {
if
( !
$count
) {
push
@{
$ref
}, [
'461'
,
'ISON'
];
last
SWITCH;
}
my
$string
=
''
;
$string
=
join
' '
,
map
{
$self
->{state}->{users}->{ u_irc
$_
}->{nick} }
grep
{
$self
->state_nick_exists(
$_
) } @{
$args
};
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'303'
,
params
=> [
$nick
, (
$string
=~ /\s+/ ?
$string
:
":$string"
) ] };
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_cmd_list {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
my
$server
=
$self
->server_name();
my
$ref
= [ ];
my
$args
= [
@_
];
my
$count
=
scalar
@{
$args
};
SWITCH: {
my
@chans
;
if
( !
$count
) {
@chans
=
map
{
$self
->_state_chan_name(
$_
) }
keys
%{
$self
->{state}->{chans} };
}
my
$last
=
pop
@{
$args
};
if
(
$count
and
$last
!~ /^(\x23|\x26)/ and !
$self
->state_peer_exists(
$last
) ) {
push
@{
$ref
}, [
'401'
,
$last
];
last
SWITCH;
}
if
(
$count
and
$last
!~ /^(\x23|\x26)/ and (
uc
$last
ne
uc
$server
) ) {
$self
->{ircd}->send_output( {
prefix
=>
$self
->state_user_full(
$nick
),
command
=>
'LIST'
,
params
=> [ @{
$args
},
$self
->_state_peer_name(
$last
) ] },
$self
->_state_peer_route(
$last
) );
last
SWITCH;
}
if
(
$count
and
$last
!~ /^(\x23|\x26)/ and
scalar
@{
$args
} == 0 ) {
@chans
=
map
{
$self
->_state_chan_name(
$_
) }
keys
%{
$self
->{state}->{chans} };
}
if
(
$count
and
$last
!~ /^(\x23|\x26)/ and
scalar
@{
$args
} == 1 ) {
$last
=
pop
@{
$args
};
}
if
(
$count
and
$last
=~ /^(\x23|\x26)/ ) {
@chans
=
split
/,/,
$last
;
}
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'321'
,
params
=> [
$nick
,
'Channel'
,
'Users Name'
] };
my
$count
= 0;
INNER:
foreach
my
$chan
(
@chans
) {
unless
( validate_chan_name(
$chan
) and
$self
->state_chan_exists(
$chan
) ) {
unless
(
$count
) {
push
@{
$ref
}, [
'401'
,
$chan
];
last
INNER;
}
$count
++;
next
INNER;
}
$count
++;
next
INNER
if
$self
->state_chan_mode_set(
$chan
,
'p'
) or
$self
->state_chan_mode_set(
$chan
,
's'
) and !
$self
->state_is_chan_member(
$nick
,
$chan
);
my
$record
=
$self
->{state}->{chans}->{ u_irc
$chan
};
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'322'
,
params
=> [
$nick
,
$record
->{name},
scalar
keys
%{
$record
->{users} }, (
defined
$record
->{topic} ?
$record
->{topic}->[0] :
''
) ] };
}
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'323'
,
params
=> [
$nick
,
'End of /LIST'
] };
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_cmd_names {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
my
$server
=
$self
->server_name();
my
$ref
= [ ];
my
$args
= [
@_
];
my
$count
=
scalar
@{
$args
};
SWITCH: {
my
@chans
;
my
$query
;
if
( !
$count
) {
@chans
=
$self
->state_user_chans(
$nick
);
$query
=
'*'
;
}
my
$last
=
pop
@{
$args
};
if
(
$count
and
$last
!~ /^(\x23|\x26)/ and !
$self
->state_peer_exists(
$last
) ) {
push
@{
$ref
}, [
'401'
,
$last
];
last
SWITCH;
}
if
(
$count
and
$last
!~ /^(\x23|\x26)/ and (
uc
$last
ne
uc
$server
) ) {
$self
->{ircd}->send_output( {
prefix
=>
$nick
,
command
=>
'NAMES'
,
params
=> [ @{
$args
},
$self
->_state_peer_name(
$last
) ] },
$self
->_state_peer_route(
$last
) );
last
SWITCH;
}
if
(
$count
and
$last
!~ /^(\x23|\x26)/ and
scalar
@{
$args
} == 0 ) {
@chans
=
$self
->state_user_chans(
$nick
);
$query
=
'*'
;
}
if
(
$count
and
$last
!~ /^(\x23|\x26)/ and
scalar
@{
$args
} == 1 ) {
$last
=
pop
@{
$args
};
}
if
(
$count
and
$last
=~ /^(\x23|\x26)/ ) {
my
(
$chan
) =
grep
{
$_
&&
$self
->state_chan_exists(
$_
) &&
$self
->state_is_chan_member(
$nick
,
$_
)
}
split
/,/,
$last
;
@chans
= ();
if
(
$chan
) {
push
@chans
,
$chan
;
$query
=
$self
->_state_chan_name(
$chan
);
}
else
{
$query
=
'*'
;
}
}
foreach
my
$chan
(
@chans
) {
my
$record
=
$self
->{state}->{chans}->{ u_irc
$chan
};
my
$type
=
'='
;
$type
=
'@'
if
$record
->{mode} =~ /s/;
$type
=
'*'
if
$record
->{mode} =~ /p/;
my
$length
=
length
(
$server
) + 3 +
length
(
$chan
) +
length
(
$nick
) + 7;
my
$buffer
=
''
;
foreach
my
$name
(
sort
$self
->state_chan_list_prefixed(
$record
->{name} ) ) {
if
(
length
(
join
' '
,
$buffer
,
$name
) +
$length
> 510 ) {
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'353'
,
params
=> [
$nick
,
$type
,
$record
->{name},
$buffer
] };
$buffer
=
$name
;
next
;
}
if
(
$buffer
) {
$buffer
=
join
' '
,
$buffer
,
$name
;
}
else
{
$buffer
=
$name
;
}
}
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'353'
,
params
=> [
$nick
,
$type
,
$record
->{name},
$buffer
] };
}
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'366'
,
params
=> [
$nick
,
$query
,
'End of NAMES list'
] };
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_cmd_whois {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
my
(
$first
,
$second
) =
@_
;
my
$server
=
$self
->server_name();
my
$ref
= [ ];
SWITCH: {
if
( !
$first
and !
$second
) {
push
@{
$ref
}, [
'431'
];
last
SWITCH;
}
if
( !
$second
and
$first
) {
$second
= (
split
/,/,
$first
)[0];
$first
=
$server
;
}
if
(
$first
and
$second
) {
$second
= (
split
/,/,
$second
)[0];
}
if
( u_irc(
$first
) eq u_irc(
$second
) and
$self
->state_nick_exists(
$second
) ) {
$first
=
$self
->state_user_server(
$second
);
}
my
$query
;
my
$target
;
$query
=
$first
unless
$second
;
$query
=
$second
if
$second
;
$target
=
$first
if
$second
and
uc
(
$first
) ne
uc
(
$server
);
if
(
$target
and !
$self
->state_peer_exists(
$target
) ) {
push
@{
$ref
}, [
'402'
,
$target
];
last
SWITCH;
}
if
(
$target
) {
$self
->{ircd}->send_output( {
prefix
=>
$nick
,
command
=>
'WHOIS'
,
params
=> [
$self
->_state_peer_name(
$target
),
$second
] },
$self
->_state_peer_route(
$target
) );
last
SWITCH;
}
if
( !
$self
->state_nick_exists(
$query
) ) {
push
@{
$ref
}, [
'401'
,
$query
];
}
else
{
my
$record
=
$self
->{state}->{users}->{ u_irc
$query
};
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'311'
,
params
=> [
$nick
,
$record
->{nick},
$record
->{auth}->{ident},
$record
->{auth}->{hostname},
'*'
,
$record
->{ircname} ] };
my
@chans
;
LOOP:
foreach
my
$chan
(
keys
%{
$record
->{chans} } ) {
next
LOOP
if
$self
->{state}->{chans}->{
$chan
}->{mode} =~ /[ps]/ and !
$self
->state_is_chan_member(
$nick
,
$chan
);
my
$prefix
=
''
;
$prefix
.=
'@'
if
$record
->{chans}->{
$chan
} =~ /o/;
$prefix
.=
'%'
if
$record
->{chans}->{
$chan
} =~ /h/;
$prefix
.=
'+'
if
$record
->{chans}->{
$chan
} =~ /v/;
push
@chans
,
$prefix
.
$self
->{state}->{chans}->{
$chan
}->{name};
}
if
(
@chans
) {
my
$buffer
=
''
;
my
$length
=
length
(
$server
) + 3 +
length
(
$nick
) +
length
(
$record
->{nick} ) + 7;
LOOP2:
foreach
my
$chan
(
@chans
) {
if
(
length
(
join
' '
,
$buffer
,
$chan
) +
$length
> 510 ) {
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'319'
,
params
=> [
$nick
,
$record
->{nick},
$buffer
] };
$buffer
=
$chan
;
next
LOOP2;
}
if
(
$buffer
) {
$buffer
=
join
' '
,
$buffer
,
$chan
;
}
else
{
$buffer
=
$chan
;
}
}
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'319'
,
params
=> [
$nick
,
$record
->{nick},
$buffer
] };
}
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'312'
,
params
=> [
$nick
,
$record
->{nick},
$record
->{server},
$self
->_state_peer_desc(
$record
->{server} ) ] };
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'301'
,
params
=> [
$nick
,
$record
->{nick},
$record
->{away} ] }
if
$record
->{type} eq
'c'
and
$record
->{away};
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'313'
,
params
=> [
$nick
,
$record
->{nick},
'is an IRC Operator'
] }
if
$record
->{umode} and
$record
->{umode} =~ /o/;
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'338'
,
params
=> [
$nick
,
$record
->{nick},
$record
->{
socket
}->[0],
'actually using host'
] }
if
$record
->{type} eq
'c'
and (
$self
->server_config(
'whoisactually'
) or
$self
->state_user_is_operator(
$nick
) );
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'317'
,
params
=> [
$nick
,
$record
->{nick}, (
time
() -
$record
->{idle_time} ),
$record
->{conn_time},
'seconds idle, signon time'
] }
if
$record
->{type} eq
'c'
;
}
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'318'
,
params
=> [
$nick
,
$query
,
'End of /WHOIS list.'
] };
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_cmd_who {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
my
$server
=
$self
->server_name();
my
(
$who
,
$op_only
) =
splice
@_
, 0, 2;
my
$ref
= [ ];
my
$orig
=
$who
;
SWITCH: {
if
( !
$who
) {
push
@{
$ref
}, [
'461'
,
'WHO'
];
last
SWITCH;
}
if
(
$self
->state_chan_exists(
$who
) and
$self
->state_is_chan_member(
$nick
,
$who
) ) {
my
$record
=
$self
->{state}->{chans}->{ u_irc
$who
};
$who
=
$record
->{name};
foreach
my
$member
(
keys
%{
$record
->{users} } ) {
my
$rpl_who
= {
prefix
=>
$server
,
command
=>
'352'
,
params
=> [
$nick
,
$who
] };
my
$memrec
=
$self
->{state}->{users}->{
$member
};
push
@{
$rpl_who
->{params} },
$memrec
->{auth}->{ident};
push
@{
$rpl_who
->{params} },
$memrec
->{auth}->{hostname};
push
@{
$rpl_who
->{params} },
$memrec
->{server};
push
@{
$rpl_who
->{params} },
$memrec
->{nick};
my
$status
= (
$memrec
->{away} ?
'G'
:
'H'
);
$status
.=
'*'
if
$memrec
->{umode} =~ /o/;
$status
.=
'@'
if
$record
->{users}->{
$member
} =~ /o/;
$status
.=
'%'
if
$record
->{users}->{
$member
} =~ /h/;
$status
.=
'+'
if
$record
->{users}->{
$member
} !~ /o/ and
$record
->{users}->{
$member
} =~ /v/;
push
@{
$rpl_who
->{params} },
$status
;
push
@{
$rpl_who
->{params} },
$memrec
->{hops} .
' '
.
$memrec
->{ircname};
push
@{
$ref
},
$rpl_who
;
}
}
if
(
$self
->state_nick_exists(
$who
) ) {
my
$nickrec
=
$self
->{state}->{users}->{ u_irc
$who
};
$who
=
$nickrec
->{nick};
my
$rpl_who
= {
prefix
=>
$server
,
command
=>
'352'
,
params
=> [
$nick
,
'*'
] };
push
@{
$rpl_who
->{params} },
$nickrec
->{auth}->{ident};
push
@{
$rpl_who
->{params} },
$nickrec
->{auth}->{hostname};
push
@{
$rpl_who
->{params} },
$nickrec
->{server};
push
@{
$rpl_who
->{params} },
$nickrec
->{nick};
my
$status
= (
$nickrec
->{away} ?
'G'
:
'H'
);
$status
.=
'*'
if
$nickrec
->{umode} =~ /o/;
push
@{
$rpl_who
->{params} },
$status
;
push
@{
$rpl_who
->{params} },
$nickrec
->{hops} .
' '
.
$nickrec
->{ircname};
push
@{
$ref
},
$rpl_who
;
}
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'315'
,
params
=> [
$nick
,
$orig
,
'End of WHO list'
] };
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_cmd_mode {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
my
$chan
=
shift
;
my
$server
=
$self
->server_name();
my
$maxmodes
=
$self
->server_config(
'MODES'
);
my
$ref
= [ ];
my
$args
= [
@_
];
my
$count
=
scalar
@{
$args
};
SWITCH: {
if
( !
$self
->state_chan_exists(
$chan
) ) {
push
@{
$ref
}, [
'403'
,
$chan
];
last
SWITCH;
}
my
$record
=
$self
->{state}->{chans}->{ u_irc
$chan
};
$chan
=
$record
->{name};
if
( !
$count
and !
$self
->state_is_chan_member(
$nick
,
$chan
) ) {
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'324'
,
params
=> [
$nick
,
$chan
,
'+'
.
$record
->{mode} ],
colonify
=> 0 };
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'329'
,
params
=> [
$nick
,
$chan
,
$record
->{ts} ],
colonify
=> 0 };
last
SWITCH;
}
if
( !
$count
) {
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'324'
,
params
=> [
$nick
,
$chan
,
'+'
.
$record
->{mode}, (
$record
->{ckey} || () ), (
$record
->{climit} || () ) ],
colonify
=> 0 };
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'329'
,
params
=> [
$nick
,
$chan
,
$record
->{ts} ],
colonify
=> 0 };
last
SWITCH;
}
my
$unknown
= 0;
my
$notop
= 0;
my
$nick_is_op
=
$self
->state_is_chan_op(
$nick
,
$chan
);
my
$nick_is_hop
=
$self
->state_is_chan_hop(
$nick
,
$chan
);
my
$reply
;
my
@reply_args
;
my
$parsed_mode
= parse_mode_line( @{
$args
} );
my
$mode_count
= 0;
while
(
my
$mode
=
shift
@{
$parsed_mode
->{modes} } ) {
if
(
$mode
!~ /[eIbklimnpstohv]/ ) {
push
@{
$ref
}, [
'472'
, (
split
//,
$mode
)[1],
$chan
]
unless
$unknown
;
$unknown
++;
next
;
}
my
$arg
;
$arg
=
shift
@{
$parsed_mode
->{args} }
if
$mode
=~ /^(\+[ohvklbIe]|-[ohvbIe])/;
if
(
$mode
=~ /(\+|-)b/ and !
defined
$arg
) {
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'367'
,
params
=> [
$nick
,
$chan
, @{
$record
->{bans}->{
$_
} } ] }
for
keys
%{
$record
->{bans} };
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'368'
,
params
=> [
$nick
,
$chan
,
'End of Channel Ban List'
] };
next
;
}
unless
(
$nick_is_op
or
$nick_is_hop
) {
push
@{
$ref
}, [
'482'
,
$chan
]
unless
$notop
;
$notop
++;
next
;
}
if
(
$mode
=~ /(\+|-)I/ and !
defined
$arg
) {
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'346'
,
params
=> [
$nick
,
$chan
, @{
$record
->{invex}->{
$_
} } ] }
for
keys
%{
$record
->{invex} };
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'347'
,
params
=> [
$nick
,
$chan
,
'End of Channel Invite List'
] };
next
;
}
if
(
$mode
=~ /(\+|-)e/ and !
defined
$arg
) {
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'348'
,
params
=> [
$nick
,
$chan
, @{
$record
->{excepts}->{
$_
} } ] }
for
keys
%{
$record
->{excepts} };
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'349'
,
params
=> [
$nick
,
$chan
,
'End of Channel Exception List'
] };
next
;
}
if
( !
$nick_is_op
and
$nick_is_hop
and
$mode
=~ /[op]/ ) {
push
@{
$ref
}, [
'482'
,
$chan
]
unless
$notop
;
$notop
++;
next
;
}
if
( !
$nick_is_op
and
$nick_is_hop
and
$record
->{mode} =~ /p/ and
$mode
=~ /h/ ) {
push
@{
$ref
}, [
'482'
,
$chan
]
unless
$notop
;
$notop
++;
next
;
}
if
( (
$mode
=~ /^(\+|-)([ohv])/ or
$mode
=~ /^\+[lk]/ ) and !
defined
$arg
) {
next
;
}
if
(
$mode
=~ /^(\+|-)([ohv])/ and !
$self
->state_nick_exists(
$arg
) ) {
next
if
++
$mode_count
>
$maxmodes
;
push
@{
$ref
}, [
'401'
,
$arg
];
next
;
}
if
(
$mode
=~ /^(\+|-)([ohv])/ and !
$self
->state_is_chan_member(
$arg
,
$chan
) ) {
next
if
++
$mode_count
>
$maxmodes
;
push
@{
$ref
}, [
'441'
,
$chan
,
$self
->state_user_nick(
$arg
) ];
next
;
}
if
(
my
(
$flag
,
$char
) =
$mode
=~ /^(\+|-)([ohv])/ ) {
next
if
++
$mode_count
>
$maxmodes
;
if
(
$flag
eq
'+'
and
$record
->{users}->{ u_irc
$arg
} !~ /
$char
/ ) {
$arg
= u_irc
$arg
;
next
if
$mode
eq
'+h'
and
$record
->{users}->{
$arg
} =~ /o/;
if
(
$char
eq
'h'
and
$record
->{users}->{
$arg
} =~ /v/ ) {
$record
->{users}->{
$arg
} =~ s/v//g;
$reply
.=
'-v'
;
push
@reply_args
,
$self
->state_user_nick(
$arg
);
}
if
(
$char
eq
'o'
and
$record
->{users}->{
$arg
} =~ /h/ ) {
$record
->{users}->{
$arg
} =~ s/h//g;
$reply
.=
'-h'
;
push
@reply_args
,
$self
->state_user_nick(
$arg
);
}
$record
->{users}->{
$arg
} =
join
(
''
,
sort
split
//,
$record
->{users}->{
$arg
} .
$char
);
$self
->{state}->{users}->{
$arg
}->{chans}->{ u_irc
$chan
} =
$record
->{users}->{
$arg
};
$reply
.=
$mode
;
push
@reply_args
,
$self
->state_user_nick(
$arg
);
}
if
(
$flag
eq
'-'
and
$record
->{users}->{ u_irc
$arg
} =~ /
$char
/ ) {
$arg
= u_irc
$arg
;
$record
->{users}->{
$arg
} =~ s/
$char
//g;
$self
->{state}->{users}->{
$arg
}->{chans}->{ u_irc
$chan
} =
$record
->{users}->{
$arg
};
$reply
.=
$mode
;
push
@reply_args
,
$self
->state_user_nick(
$arg
);
}
next
;
}
if
(
$mode
eq
'+l'
and
$arg
=~ /^\d+$/ and
$arg
> 0 ) {
next
if
++
$mode_count
>
$maxmodes
;
$reply
.=
$mode
;
push
@reply_args
,
$arg
;
$record
->{mode} =
join
(
''
,
sort
split
//,
$record
->{mode} .
'l'
)
unless
$record
->{mode} =~ /l/;
$record
->{climit} =
$arg
;
next
;
}
if
(
$mode
eq
'-l'
and
$record
->{mode} =~ /l/ ) {
$record
->{mode} =~ s/l//g;
delete
$record
->{climit};
$reply
.=
$mode
;
next
;
}
if
(
$mode
eq
'+k'
and
$arg
) {
next
if
++
$mode_count
>
$maxmodes
;
$reply
.=
$mode
;
push
@reply_args
,
$arg
;
$record
->{mode} =
join
(
''
,
sort
split
//,
$record
->{mode} .
'k'
)
unless
$record
->{mode} =~ /k/;
$record
->{ckey} =
$arg
;
next
;
}
if
(
$mode
eq
'-k'
and
$record
->{mode} =~ /k/ ) {
$reply
.=
$mode
;
push
@reply_args
,
'*'
;
$record
->{mode} =~ s/k//g;
delete
$record
->{ckey};
next
;
}
if
(
my
(
$flag
) =
$mode
=~ /(\+|-)b/ ) {
next
if
++
$mode_count
>
$maxmodes
;
my
$mask
= parse_ban_mask(
$arg
);
my
$umask
= u_irc
$mask
;
if
(
$flag
eq
'+'
and !
$record
->{bans}->{
$umask
} ) {
$record
->{bans}->{
$umask
} = [
$mask
,
$self
->state_user_full(
$nick
),
time
() ];
$reply
.=
$mode
;
push
@reply_args
,
$mask
;
}
if
(
$flag
eq
'-'
and
$record
->{bans}->{
$umask
} ) {
delete
$record
->{bans}->{
$umask
};
$reply
.=
$mode
;
push
@reply_args
,
$mask
;
}
next
;
}
if
(
my
(
$flag
) =
$mode
=~ /(\+|-)I/ ) {
next
if
++
$mode_count
>
$maxmodes
;
my
$mask
= parse_ban_mask(
$arg
);
my
$umask
= u_irc
$mask
;
if
(
$flag
eq
'+'
and !
$record
->{invex}->{
$umask
} ) {
$record
->{invex}->{
$umask
} = [
$mask
,
$self
->state_user_full(
$nick
),
time
() ];
$reply
.=
$mode
;
push
@reply_args
,
$mask
;
}
if
(
$flag
eq
'-'
and
$record
->{invex}->{
$umask
} ) {
delete
$record
->{invex}->{
$umask
};
$reply
.=
$mode
;
push
@reply_args
,
$mask
;
}
next
;
}
if
(
my
(
$flag
) =
$mode
=~ /(\+|-)e/ ) {
next
if
++
$mode_count
>
$maxmodes
;
my
$mask
= parse_ban_mask(
$arg
);
my
$umask
= u_irc
$mask
;
if
(
$flag
eq
'+'
and !
$record
->{excepts}->{
$umask
} ) {
$record
->{excepts}->{
$umask
} = [
$mask
,
$self
->state_user_full(
$nick
),
time
() ];
$reply
.=
$mode
;
push
@reply_args
,
$mask
;
}
if
(
$flag
eq
'-'
and
$record
->{excepts}->{
$umask
} ) {
delete
$record
->{excepts}->{
$umask
};
$reply
.=
$mode
;
push
@reply_args
,
$mask
;
}
next
;
}
my
(
$flag
,
$char
) =
split
//,
$mode
;
if
(
$flag
eq
'+'
and
$record
->{mode} !~ /
$char
/ ) {
$reply
.=
$mode
;
$record
->{mode} =
join
(
''
,
sort
split
//,
$record
->{mode} .
$char
);
next
;
}
if
(
$flag
eq
'-'
and
$record
->{mode} =~ /
$char
/ ) {
$reply
.=
$mode
;
$record
->{mode} =~ s/
$char
//g;
next
;
}
}
if
(
$reply
) {
$reply
= unparse_mode_line(
$reply
);
my
$output
= {
prefix
=>
$self
->state_user_full(
$nick
),
command
=>
'MODE'
,
params
=> [
$chan
,
$reply
,
@reply_args
],
colonify
=> 0 };
$self
->_send_output_to_channel(
$chan
,
$output
);
}
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_cmd_join {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
my
$server
=
$self
->server_name();
my
$ref
= [ ];
my
$args
= [
@_
];
my
$count
=
scalar
@{
$args
};
my
$route_id
=
$self
->_state_user_route(
$nick
);
my
$unick
= u_irc
$nick
;
SWITCH: {
my
@channels
;
my
@chankeys
;
if
( !
$count
) {
push
@{
$ref
}, [
'461'
,
'JOIN'
];
last
SWITCH;
}
@channels
=
split
/,/,
$args
->[0];
@chankeys
=
split
/,/,
$args
->[1]
if
(
$args
->[1] );
my
$channel_length
=
$self
->server_config(
'CHANNELLEN'
);
LOOP:
foreach
my
$channel
(
@channels
) {
my
$uchannel
= u_irc
$channel
;
if
(
$channel
eq
'0'
and
my
@chans
=
$self
->state_user_chans(
$nick
) ) {
$self
->_send_output_to_client(
$route_id
=> (
ref
$_
eq
'ARRAY'
? @{
$_
} :
$_
) )
for
(
map
{
$self
->_daemon_cmd_part(
$nick
,
$_
) }
@chans
);
next
LOOP;
}
if
( !validate_chan_name(
$channel
) or
length
(
$channel
) >
$channel_length
) {
$self
->_send_output_to_client(
$route_id
=>
'403'
=>
$channel
);
next
LOOP;
}
if
(
scalar
$self
->state_user_chans(
$nick
) >=
$self
->server_config(
'MAXCHANNELS'
) and !
$self
->state_user_is_operator(
$nick
) ) {
$self
->_send_output_to_client(
$route_id
=>
'405'
=>
$channel
);
next
LOOP;
}
unless
(
$self
->state_chan_exists(
$channel
) ) {
my
$record
= {
name
=>
$channel
,
ts
=>
time
(),
mode
=>
'nt'
,
users
=> {
$unick
=>
'o'
}, };
$self
->{state}->{chans}->{
$uchannel
} =
$record
;
$self
->{state}->{users}->{
$unick
}->{chans}->{
$uchannel
} =
'o'
;
my
@peers
=
$self
->_state_connected_peers();
$self
->{ircd}->send_output( {
command
=>
'SJOIN'
,
params
=> [
$record
->{ts},
$channel
,
'+'
.
$record
->{mode},
'@'
.
$nick
] },
@peers
)
unless
$channel
=~ /^\&/;
my
$output
= {
prefix
=>
$self
->state_user_full(
$nick
),
command
=>
'JOIN'
,
params
=> [
$channel
] };
$self
->{ircd}->send_output(
$output
,
$route_id
);
$self
->{ircd}->send_event(
"daemon_join"
,
$output
->{prefix},
$channel
);
$self
->{ircd}->send_output( {
prefix
=>
$server
,
command
=>
'MODE'
,
params
=> [
$channel
,
'+'
.
$record
->{mode} ] },
$route_id
);
$self
->_send_output_to_client(
$route_id
=> (
ref
$_
eq
'ARRAY'
? @{
$_
} :
$_
) )
for
$self
->_daemon_cmd_names(
$nick
,
$channel
);
$self
->_send_output_to_client(
$route_id
=> (
ref
$_
eq
'ARRAY'
? @{
$_
} :
$_
) )
for
$self
->_daemon_cmd_topic(
$nick
,
$channel
);
next
LOOP;
}
if
(
$self
->state_is_chan_member(
$nick
,
$channel
) ) {
next
LOOP;
}
my
$chanrec
=
$self
->{state}->{chans}->{
$uchannel
};
my
$bypass
;
if
(
$self
->state_user_is_operator(
$nick
) and
$self
->{config}->{OPHACKS} ) {
$bypass
= 1;
}
if
( !
$bypass
and
$chanrec
->{mode} =~ /l/ and
scalar
keys
%{
$chanrec
} >=
$chanrec
->{climit} ) {
$self
->_send_output_to_client(
$route_id
=>
'471'
=>
$channel
);
next
LOOP;
}
my
$chankey
;
$chankey
=
shift
@chankeys
if
$chanrec
->{mode} =~ /k/;
if
( !
$bypass
and
$chanrec
->{mode} =~ /k/ and ( !
$chankey
or (
$chankey
ne
$chanrec
->{ckey} ) ) ) {
$self
->_send_output_to_client(
$route_id
=>
'475'
=>
$channel
);
next
LOOP;
}
if
( !
$bypass
and
$chanrec
->{mode} =~ /i/ and !
$self
->_state_user_invited(
$nick
,
$channel
) ) {
$self
->_send_output_to_client(
$route_id
=>
'473'
=>
$channel
);
next
LOOP;
}
if
( !
$bypass
and
$self
->_state_user_banned(
$nick
,
$channel
) ) {
$self
->_send_output_to_client(
$route_id
=>
'474'
=>
$channel
);
next
LOOP;
}
delete
$self
->{state}->{users}->{
$unick
}->{invites}->{
$uchannel
};
$self
->{state}->{users}->{
$unick
}->{chans}->{
$uchannel
} =
''
;
$self
->{state}->{chans}->{
$uchannel
}->{users}->{
$unick
} =
''
;
$self
->{ircd}->send_output( {
prefix
=>
$server
,
command
=>
'SJOIN'
,
params
=> [
$chanrec
->{ts},
$channel
,
'+'
,
$nick
] },
$self
->_state_connected_peers() )
unless
$channel
=~ /^\&/;
my
$output
= {
prefix
=>
$self
->state_user_full(
$nick
),
command
=>
'JOIN'
,
params
=> [
$channel
] };
$self
->_send_output_to_client(
$route_id
=>
$output
);
$self
->_send_output_to_channel(
$channel
,
$output
,
$route_id
);
$self
->_send_output_to_client(
$route_id
=> (
ref
$_
eq
'ARRAY'
? @{
$_
} :
$_
) )
for
$self
->_daemon_cmd_names(
$nick
,
$channel
);
$self
->_send_output_to_client(
$route_id
=> (
ref
$_
eq
'ARRAY'
? @{
$_
} :
$_
) )
for
$self
->_daemon_cmd_topic(
$nick
,
$channel
);
}
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_cmd_part {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
my
$chan
=
shift
;
my
$server
=
$self
->server_name();
my
$ref
= [ ];
my
$args
= [
@_
];
my
$count
=
scalar
@{
$args
};
SWITCH: {
if
( !
$chan
) {
push
@{
$ref
}, [
'461'
,
'PART'
];
last
SWITCH;
}
if
( !
$self
->state_chan_exists(
$chan
) ) {
push
@{
$ref
}, [
'403'
,
$chan
];
last
SWITCH;
}
if
( !
$self
->state_is_chan_member(
$nick
,
$chan
) ) {
push
@{
$ref
}, [
'442'
,
$chan
];
last
SWITCH;
}
$self
->_send_output_to_channel(
$chan
, {
prefix
=>
$self
->state_user_full(
$nick
),
command
=>
'PART'
,
params
=> [
$chan
, (
$args
->[0] ||
$nick
) ] } );
$nick
= u_irc
$nick
;
$chan
= u_irc
$chan
;
delete
$self
->{state}->{chans}->{
$chan
}->{users}->{
$nick
};
delete
$self
->{state}->{users}->{
$nick
}->{chans}->{
$chan
};
unless
(
scalar
keys
%{
$self
->{state}->{chans}->{
$chan
}->{users} } ) {
delete
$self
->{state}->{chans}->{
$chan
};
}
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_cmd_kick {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
my
$server
=
$self
->server_name();
my
$ref
= [ ];
my
$args
= [
@_
];
my
$count
=
scalar
@{
$args
};
SWITCH: {
if
( !
$count
or
$count
< 2 ) {
push
@{
$ref
}, [
'461'
,
'KICK'
];
last
SWITCH;
}
my
$chan
= (
split
/,/,
$args
->[0] )[0];
my
$who
= (
split
/,/,
$args
->[1] )[0];
if
( !
$self
->state_chan_exists(
$chan
) ) {
push
@{
$ref
}, [
'403'
,
$chan
];
last
SWITCH;
}
$chan
=
$self
->_state_chan_name(
$chan
);
if
( !
$self
->state_nick_exists(
$who
) ) {
push
@{
$ref
}, [
'401'
,
$who
];
last
SWITCH;
}
$who
=
$self
->state_user_nick(
$who
);
if
( !
$self
->state_is_chan_op(
$nick
,
$chan
) ) {
push
@{
$ref
}, [
'482'
,
$chan
];
last
SWITCH;
}
if
( !
$self
->state_is_chan_member(
$who
,
$chan
) ) {
push
@{
$ref
}, [
'441'
,
$who
,
$chan
];
last
SWITCH;
}
my
$comment
=
$args
->[2] ||
$who
;
$self
->_send_output_to_channel(
$chan
, {
prefix
=>
$self
->state_user_full(
$nick
),
command
=>
'KICK'
,
params
=> [
$chan
,
$who
,
$comment
] } );
$who
= u_irc
$who
;
$chan
= u_irc
$chan
;
delete
$self
->{state}->{chans}->{
$chan
}->{users}->{
$who
};
delete
$self
->{state}->{users}->{
$who
}->{chans}->{
$chan
};
unless
(
scalar
keys
%{
$self
->{state}->{chans}->{
$chan
}->{users} } ) {
delete
$self
->{state}->{chans}->{
$chan
};
}
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_cmd_remove {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
my
$server
=
$self
->server_name();
my
$ref
= [ ];
my
$args
= [
@_
];
my
$count
=
scalar
@{
$args
};
SWITCH: {
if
( !
$count
or
$count
< 2 ) {
push
@{
$ref
}, [
'461'
,
'REMOVE'
];
last
SWITCH;
}
my
$chan
= (
split
/,/,
$args
->[0] )[0];
my
$who
= (
split
/,/,
$args
->[1] )[0];
if
( !
$self
->state_chan_exists(
$chan
) ) {
push
@{
$ref
}, [
'403'
,
$chan
];
last
SWITCH;
}
$chan
=
$self
->_state_chan_name(
$chan
);
if
( !
$self
->state_nick_exists(
$who
) ) {
push
@{
$ref
}, [
'401'
,
$who
];
last
SWITCH;
}
my
$fullwho
=
$self
->state_user_full(
$who
);
$who
= (
split
/!/,
$fullwho
)[0];
if
( !
$self
->state_is_chan_op(
$nick
,
$chan
) ) {
push
@{
$ref
}, [
'482'
,
$chan
];
last
SWITCH;
}
if
( !
$self
->state_is_chan_member(
$who
,
$chan
) ) {
push
@{
$ref
}, [
'441'
,
$who
,
$chan
];
last
SWITCH;
}
my
$comment
=
"Requested by $nick"
;
$comment
.=
" \"$args->[2]\""
if
$args
->[2];
$self
->_send_output_to_channel(
$chan
, {
prefix
=>
$fullwho
,
command
=>
'PART'
,
params
=> [
$chan
,
$comment
] } );
$who
= u_irc
$who
;
$chan
= u_irc
$chan
;
delete
$self
->{state}->{chans}->{
$chan
}->{users}->{
$who
};
delete
$self
->{state}->{users}->{
$who
}->{chans}->{
$chan
};
unless
(
scalar
keys
%{
$self
->{state}->{chans}->{
$chan
}->{users} } ) {
delete
$self
->{state}->{chans}->{
$chan
};
}
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_cmd_invite {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
my
$server
=
$self
->server_name();
my
$ref
= [ ];
my
$args
= [
@_
];
my
$count
=
scalar
@{
$args
};
SWITCH: {
if
( !
$count
or
$count
< 2 ) {
push
@{
$ref
}, [
'461'
,
'INVITE'
];
last
SWITCH;
}
my
(
$who
,
$chan
) = @{
$args
};
if
( !
$self
->state_nick_exists(
$who
) ) {
push
@{
$ref
}, [
'401'
,
$who
];
last
SWITCH;
}
$who
=
$self
->state_user_nick(
$who
);
if
( !
$self
->state_chan_exists(
$chan
) ) {
push
@{
$ref
}, [
'403'
,
$chan
];
last
SWITCH;
}
$chan
=
$self
->_state_chan_name(
$chan
);
if
( !
$self
->state_is_chan_member(
$nick
,
$chan
) ) {
push
@{
$ref
}, [
'442'
,
$chan
];
last
SWITCH;
}
if
(
$self
->state_is_chan_member(
$who
,
$chan
) ) {
push
@{
$ref
}, [
'443'
,
$who
,
$chan
];
last
SWITCH;
}
if
(
$self
->state_chan_mode_set(
$chan
,
'i'
) and !
$self
->state_is_chan_op(
$nick
,
$chan
) ) {
push
@{
$ref
}, [
'482'
,
$chan
];
last
SWITCH;
}
my
$local
;
if
(
$self
->_state_is_local_user(
$who
) ) {
my
$record
=
$self
->{state}->{users}->{ u_irc
$who
};
$record
->{invites}->{ u_irc
$chan
} =
time
();
$local
= 1;
}
my
$away
=
$self
->_state_user_away_msg(
$who
);
my
$route_id
=
$self
->_state_user_route(
$who
);
my
$output
= {
prefix
=>
$self
->state_user_full(
$nick
),
command
=>
'INVITE'
,
params
=> [
$who
,
$chan
],
colonify
=> 0 };
if
(
$route_id
eq
'spoofed'
) {
$self
->{ircd}->send_event(
"daemon_invite"
,
$output
->{prefix}, @{
$output
->{params} } );
}
else
{
unless
(
$local
) {
$output
->{prefix} =
$nick
;
push
@{
$output
->{params} },
time
();
}
$self
->{ircd}->send_output(
$output
,
$route_id
);
}
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'341'
,
params
=> [
$chan
,
$who
] };
if
(
defined
$away
) {
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'301'
,
params
=> [
$nick
,
$who
,
$away
] };
}
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_cmd_umode {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
my
$umode
=
shift
;
my
$server
=
$self
->server_name();
my
$ref
= [ ];
my
$record
=
$self
->{state}->{users}->{ u_irc
$nick
};
unless
(
$umode
) {
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'221'
,
params
=> [
$nick
,
'+'
.
$record
->{umode} ] };
}
else
{
my
$peer_ignore
;
my
$parsed_mode
= parse_mode_line(
$umode
);
my
$route_id
=
$self
->_state_user_route(
$nick
);
my
$previous
=
$record
->{umode};
while
(
my
$mode
=
shift
@{
$parsed_mode
->{modes} } ) {
next
if
$mode
eq
'+o'
;
my
(
$action
,
$char
) =
split
//,
$mode
;
if
(
$action
eq
'+'
and
$record
->{umode} !~ /
$char
/ ) {
next
if
$char
=~ /[wzl]/ and
$record
->{umode} !~ /o/;
$record
->{umode} .=
$char
;
if
(
$char
eq
'i'
) {
$self
->{state}->{stats}->{invisible}++;
$peer_ignore
=
delete
$record
->{_ignore_i_umode};
}
if
(
$char
eq
'w'
) {
$self
->{state}->{wallops}->{
$route_id
} =
time
();
}
if
(
$char
eq
'z'
) {
$self
->{state}->{operwall}->{
$route_id
} =
time
();
}
if
(
$char
eq
'l'
) {
$self
->{state}->{locops}->{
$route_id
} =
time
();
}
}
if
(
$action
eq
'-'
and
$record
->{umode} =~ /
$char
/ ) {
$record
->{umode} =~ s/
$char
//g;
$self
->{state}->{stats}->{invisible}--
if
$char
eq
'i'
;
if
(
$char
eq
'o'
) {
$self
->{state}->{stats}->{ops_online}--;
delete
$self
->{state}->{localops}->{
$route_id
};
$self
->{ircd}->antiflood(
$route_id
, 1 );
}
if
(
$char
eq
'w'
) {
delete
$self
->{state}->{wallops}->{
$route_id
};
}
if
(
$char
eq
'z'
) {
delete
$self
->{state}->{operwall}->{
$route_id
};
}
if
(
$char
eq
'l'
) {
delete
$self
->{state}->{locops}->{
$route_id
};
}
}
}
$record
->{umode} =
join
''
,
sort
split
//,
$record
->{umode};
my
$peerprev
=
$previous
;
my
$peerumode
=
$record
->{umode};
$peerprev
=~ s/[^aiow]//g;
$peerumode
=~ s/[^aiow]//g;
my
$pset
= gen_mode_change(
$peerprev
,
$peerumode
);
my
$set
= gen_mode_change(
$previous
,
$record
->{umode} );
if
(
$pset
and !
$peer_ignore
) {
my
$hashref
= {
prefix
=>
$nick
,
command
=>
'MODE'
,
params
=> [
$nick
,
$pset
] };
$self
->{ircd}->send_output(
$hashref
,
$self
->_state_connected_peers() );
}
if
(
$set
) {
my
$hashref
= {
prefix
=>
$nick
,
command
=>
'MODE'
,
params
=> [
$nick
,
$set
] };
$self
->{ircd}->send_event(
"daemon_umode"
,
$self
->state_user_full(
$nick
),
$set
)
unless
$peer_ignore
;
push
@{
$ref
},
$hashref
;
}
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_cmd_topic {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
my
$server
=
$self
->server_name();
my
$ref
= [ ];
my
$args
= [
@_
];
my
$count
=
scalar
@{
$args
};
SWITCH:{
if
( !
$count
) {
push
@{
$ref
}, [
'461'
,
'TOPIC'
];
last
SWITCH;
}
if
( !
$self
->state_chan_exists(
$args
->[0] ) ) {
push
@{
$ref
}, [
'403'
,
$args
->[0] ];
last
SWITCH;
}
if
(
$self
->state_chan_mode_set(
$args
->[0],
's'
) and !
$self
->state_is_chan_member(
$nick
,
$args
->[0] ) ) {
push
@{
$ref
}, [
'442'
,
$args
->[0] ];
last
SWITCH;
}
my
$chan_name
=
$self
->_state_chan_name(
$args
->[0] );
if
(
$count
== 1 and
my
$topic
=
$self
->state_chan_topic(
$args
->[0] ) ) {
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'332'
,
params
=> [
$nick
,
$chan_name
,
$topic
->[0] ] };
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'333'
,
params
=> [
$nick
,
$chan_name
, @{
$topic
}[1..2] ] };
last
SWITCH;
}
if
(
$count
== 1 ) {
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'331'
,
params
=> [
$nick
,
$chan_name
,
'No topic is set'
] };
last
SWITCH;
}
if
( !
$self
->state_is_chan_member(
$nick
,
$args
->[0] ) ) {
push
@{
$ref
}, [
'442'
,
$args
->[0] ];
last
SWITCH;
}
if
(
$self
->state_chan_mode_set(
$args
->[0],
't'
) and !
$self
->state_is_chan_op(
$nick
,
$args
->[0] ) ) {
push
@{
$ref
}, [
'482'
,
$args
->[0] ];
last
SWITCH;
}
my
$record
=
$self
->{state}->{chans}->{ u_irc
$args
->[0] };
my
$topic_length
=
$self
->server_config(
'TOPICLEN'
);
$args
->[1] =
substr
(
$args
->[0],0,
$topic_length
)
if
length
(
$args
->[0] ) >
$topic_length
;
if
(
$args
->[1] eq
''
) {
delete
$record
->{topic};
}
else
{
$record
->{topic} = [
$args
->[1],
$self
->state_user_full(
$nick
),
time
() ];
}
$self
->_send_output_to_channel(
$args
->[0], {
prefix
=>
$self
->state_user_full(
$nick
),
command
=>
'TOPIC'
,
params
=> [
$chan_name
,
$args
->[1] ] } );
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_cmd_links {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
my
$target
=
shift
;
my
$server
=
$self
->server_name();
my
$ref
= [ ];
SWITCH:{
if
(
$target
and !
$self
->state_peer_exists(
$target
) ) {
push
@{
$ref
}, [
'402'
,
$target
];
last
SWITCH;
}
if
(
$target
and (
uc
$server
ne
uc
$target
) ) {
$self
->{ircd}->send_output( {
prefix
=>
$nick
,
command
=>
'LINKS'
,
params
=> [
$self
->_state_peer_name(
$target
) ] },
$self
->_state_peer_route(
$target
) );
last
SWITCH;
}
push
@{
$ref
},
$_
for
$self
->_state_server_links(
$server
,
$server
,
$nick
);
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'364'
,
params
=> [
$nick
,
$server
,
$server
,
join
(
' '
,
'0'
,
$self
->server_config(
'serverdesc'
) ) ] };
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'365'
,
params
=> [
$nick
,
'*'
,
'End of /LINKS list.'
] };
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_peer_squit {
my
$self
=
shift
;
my
$peer_id
=
shift
||
return
;
my
$ref
= [ ];
my
$args
= [
@_
];
my
$count
=
scalar
@{
$args
};
return
unless
$self
->state_peer_exists(
$args
->[0] );
SWITCH: {
if
(
$peer_id
ne
$self
->_state_peer_route(
$args
->[0] ) ) {
$self
->{ircd}->send_output( {
command
=>
'SQUIT'
,
params
=>
$args
},
$self
->_state_peer_route(
$args
->[0] ) );
last
SWITCH;
}
if
(
$peer_id
eq
$self
->_state_peer_route(
$args
->[0] ) ) {
$self
->{ircd}->send_output( {
command
=>
'SQUIT'
,
params
=>
$args
},
grep
{
$_
ne
$peer_id
}
$self
->_state_connected_peers() );
$self
->{ircd}->send_event(
"daemon_squit"
, @{
$args
} );
my
$quit_msg
=
join
' '
,
$self
->_state_peer_for_peer(
$args
->[0] ),
$args
->[0];
foreach
my
$nick
(
$self
->_state_server_squit(
$args
->[0] ) ) {
my
$output
= {
prefix
=>
$self
->state_user_full(
$nick
),
command
=>
'QUIT'
,
params
=> [
$quit_msg
] };
my
$common
= { };
foreach
my
$uchan
(
$self
->state_user_chans(
$nick
) ) {
$uchan
= u_irc
$uchan
;
delete
$self
->{state}->{chans}->{
$uchan
}->{users}->{
$nick
};
foreach
my
$user
(
$self
->state_chan_list(
$uchan
) ) {
next
unless
$self
->_state_is_local_user(
$user
);
$common
->{
$user
} =
$self
->_state_user_route(
$user
);
}
unless
(
scalar
keys
%{
$self
->{state}->{chans}->{
$uchan
}->{users} } ) {
delete
$self
->{state}->{chans}->{
$uchan
};
}
}
$self
->{ircd}->send_output(
$output
,
values
%{
$common
} );
$self
->{ircd}->send_event(
"daemon_quit"
,
$output
->{prefix},
$output
->{params}->[0] );
my
$record
=
delete
$self
->{state}->{users}->{
$nick
};
$self
->{state}->{stats}->{ops_online}--
if
$record
->{umode} =~ /o/;
$self
->{state}->{stats}->{invisible}--
if
$record
->{umode} =~ /i/;
}
last
SWITCH;
}
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_peer_rkline {
my
$self
=
shift
;
my
$peer_id
=
shift
||
return
;
my
$nick
=
shift
||
return
;
my
$server
=
$self
->server_name();
my
$ref
= [ ];
my
$args
= [
@_
];
my
$count
=
scalar
@{
$args
};
SWITCH: {
if
( !
$count
or
$count
< 5 ) {
last
SWITCH;
}
my
$full
=
$self
->state_user_full(
$nick
);
my
$target
=
$args
->[0];
my
$us
= 0;
my
$ucserver
=
uc
$server
;
my
%targets
;
foreach
my
$peer
(
keys
%{
$self
->{state}->{peers} } ) {
if
( matches_mask(
$target
,
$peer
) ) {
if
(
$ucserver
eq
$peer
) {
$us
= 1;
}
else
{
$targets
{
$self
->_state_peer_route(
$peer
) }++;
}
}
}
delete
$targets
{
$peer_id
};
$self
->{ircd}->send_output( {
prefix
=>
$nick
,
command
=>
'RKLINE'
,
params
=>
$args
,
colonify
=> 0 },
grep
{
$self
->_state_peer_capab(
$_
,
'KLN'
) }
keys
%targets
);
if
(
$us
) {
$self
->{ircd}->send_event(
"daemon_rkline"
,
$full
, @{
$args
} );
push
@{
$self
->{state}->{rklines} }, {
setby
=>
$full
,
setat
=>
time
(),
target
=>
$args
->[0],
duration
=>
$args
->[1],
user
=>
$args
->[2],
host
=>
$args
->[3],
reason
=>
$args
->[4] };
$self
->_terminate_conn_error(
$_
,
'K-Lined'
)
for
$self
->_state_local_users_match_rkline(
$args
->[2],
$args
->[3] );
}
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_peer_kline {
my
$self
=
shift
;
my
$peer_id
=
shift
||
return
;
my
$nick
=
shift
||
return
;
my
$server
=
$self
->server_name();
my
$ref
= [ ];
my
$args
= [
@_
];
my
$count
=
scalar
@{
$args
};
SWITCH: {
if
( !
$count
or
$count
< 5 ) {
last
SWITCH;
}
my
$full
=
$self
->state_user_full(
$nick
);
my
$target
=
$args
->[0];
my
$us
= 0;
my
$ucserver
=
uc
$server
;
my
%targets
;
foreach
my
$peer
(
keys
%{
$self
->{state}->{peers} } ) {
if
( matches_mask(
$target
,
$peer
) ) {
if
(
$ucserver
eq
$peer
) {
$us
= 1;
}
else
{
$targets
{
$self
->_state_peer_route(
$peer
) }++;
}
}
}
delete
$targets
{
$peer_id
};
$self
->{ircd}->send_output( {
prefix
=>
$nick
,
command
=>
'KLINE'
,
params
=>
$args
,
colonify
=> 0 },
grep
{
$self
->_state_peer_capab(
$_
,
'KLN'
) }
keys
%targets
);
if
(
$us
) {
$self
->{ircd}->send_event(
"daemon_kline"
,
$full
, @{
$args
} );
push
@{
$self
->{state}->{klines} }, {
setby
=>
$full
,
setat
=>
time
(),
target
=>
$args
->[0],
duration
=>
$args
->[1],
user
=>
$args
->[2],
host
=>
$args
->[3],
reason
=>
$args
->[4] };
$self
->_terminate_conn_error(
$_
,
'K-Lined'
)
for
$self
->_state_local_users_match_gline(
$args
->[2],
$args
->[3] );
}
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_peer_unkline {
my
$self
=
shift
;
my
$peer_id
=
shift
||
return
;
my
$nick
=
shift
||
return
;
my
$server
=
$self
->server_name();
my
$ref
= [ ];
my
$args
= [
@_
];
my
$count
=
scalar
@{
$args
};
SWITCH: {
if
( !
$count
or
$count
< 3 ) {
last
SWITCH;
}
my
$full
=
$self
->state_user_full(
$nick
);
my
$target
=
$args
->[0];
my
$us
= 0;
my
$ucserver
=
uc
$server
;
my
%targets
;
foreach
my
$peer
(
keys
%{
$self
->{state}->{peers} } ) {
if
( matches_mask(
$target
,
$peer
) ) {
if
(
$ucserver
eq
$peer
) {
$us
= 1;
}
else
{
$targets
{
$self
->_state_peer_route(
$peer
) }++;
}
}
}
delete
$targets
{
$peer_id
};
$self
->{ircd}->send_output( {
prefix
=>
$nick
,
command
=>
'UNKLINE'
,
params
=>
$args
,
colonify
=> 0 },
grep
{
$self
->_state_peer_capab(
$_
,
'UNKLN'
) }
keys
%targets
);
if
(
$us
) {
$self
->{ircd}->send_event(
"daemon_unkline"
,
$full
, @{
$args
} );
my
$i
= 0;
for
( @{
$self
->{state}->{klines} } ) {
splice
( @{
$self
->{state}->{klines} },
$i
, 1),
last
if
$_
->{user} eq
$args
->[1] and
$_
->{host} eq
$args
->[2];
++
$i
;
}
}
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_peer_gline {
my
$self
=
shift
;
my
$peer_id
=
shift
||
return
;
my
$nick
=
shift
||
return
;
my
$server
=
$self
->server_name();
my
$ref
= [ ];
my
$args
= [
@_
];
my
$count
=
scalar
@{
$args
};
SWITCH: {
if
( !
$count
or
$count
< 3 ) {
last
SWITCH;
}
my
$full
=
$self
->state_user_full(
$nick
);
push
@{
$self
->{state}->{glines} }, {
setby
=>
$full
,
setat
=>
time
(),
user
=>
$args
->[0],
host
=>
$args
->[1],
reason
=>
$args
->[2] };
$self
->{ircd}->send_output( {
prefix
=>
$nick
,
command
=>
'GLINE'
,
params
=>
$args
,
colonify
=> 0 },
grep
{
$_
ne
$peer_id
and
$self
->_state_peer_capab(
$_
,
'GLN'
) }
$self
->_state_connected_peers() );
$self
->{ircd}->send_event(
"daemon_gline"
,
$full
, @{
$args
} );
$self
->_terminate_conn_error(
$_
,
'G-Lined'
)
for
$self
->_state_local_users_match_gline(
$args
->[0],
$args
->[1] );
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_peer_wallops {
my
$self
=
shift
;
my
$peer_id
=
shift
||
return
;
my
$prefix
=
shift
||
return
;
my
$ref
= [ ];
my
$args
= [
@_
];
my
$count
=
scalar
@{
$args
};
SWITCH: {
my
$full
=
$self
->state_user_full(
$prefix
) ||
$prefix
;
$self
->{ircd}->send_output( {
prefix
=>
$prefix
,
command
=>
'WALLOPS'
,
params
=> [
$args
->[0] ] },
grep
{
$_
ne
$peer_id
}
$self
->_state_connected_peers() );
if
(
$self
->state_peer_exists(
$full
) ) {
$self
->{ircd}->send_output( {
prefix
=>
$full
,
command
=>
'WALLOPS'
,
params
=> [
'OPERWALL - '
.
$args
->[0] ] },
keys
%{
$self
->{state}->{wallops} } );
$self
->{ircd}->send_event(
"daemon_wallops"
,
$full
,
$args
->[0] );
}
else
{
$self
->{ircd}->send_output( {
prefix
=>
$full
,
command
=>
'WALLOPS'
,
params
=> [
'OPERWALL - '
.
$args
->[0] ] },
keys
%{
$self
->{state}->{operwall} } );
$self
->{ircd}->send_event(
"daemon_operwall"
,
$full
,
$args
->[0] );
}
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_peer_operwall {
my
$self
=
shift
;
my
$peer_id
=
shift
||
return
;
my
$prefix
=
shift
||
return
;
my
$ref
= [ ];
my
$args
= [
@_
];
my
$count
=
scalar
@{
$args
};
SWITCH: {
my
$full
=
$self
->state_user_full(
$prefix
) ||
$prefix
;
$self
->{ircd}->send_output( {
prefix
=>
$prefix
,
command
=>
'WALLOPS'
,
params
=> [
$args
->[0] ] },
grep
{
$_
ne
$peer_id
}
$self
->_state_connected_peers() );
if
(
$self
->state_peer_exists(
$full
) ) {
$self
->{ircd}->send_output( {
prefix
=>
$full
,
command
=>
'WALLOPS'
,
params
=> [
'OPERWALL - '
.
$args
->[0] ] },
keys
%{
$self
->{state}->{wallops} } );
$self
->{ircd}->send_event(
"daemon_wallops"
,
$full
,
$args
->[0] );
}
else
{
$self
->{ircd}->send_output( {
prefix
=>
$full
,
command
=>
'WALLOPS'
,
params
=> [
'OPERWALL - '
.
$args
->[0] ] },
keys
%{
$self
->{state}->{operwall} } );
$self
->{ircd}->send_event(
"daemon_operwall"
,
$full
,
$args
->[0] );
}
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_peer_eob {
my
$self
=
shift
;
my
$peer_id
=
shift
||
return
;
my
$peer
=
shift
||
return
;
my
$ref
= [ ];
$self
->{ircd}->send_event(
"daemon_eob"
,
$peer
);
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_peer_kill {
my
$self
=
shift
;
my
$peer_id
=
shift
||
return
;
my
$nick
=
shift
||
return
;
my
$server
=
$self
->server_name();
my
$ref
= [ ];
my
$args
= [
@_
];
my
$count
=
scalar
@{
$args
};
SWITCH: {
if
(
$self
->state_peer_exists(
$args
->[0] ) ) {
last
SWITCH;
}
if
( !
$self
->state_nick_exists(
$args
->[0] ) ) {
last
SWITCH;
}
my
$target
=
$self
->state_user_nick(
$args
->[0] );
my
$comment
=
$args
->[1];
if
(
$self
->_state_is_local_user(
$target
) ) {
my
$route_id
=
$self
->_state_user_route(
$target
);
$self
->{ircd}->send_output( {
prefix
=>
$nick
,
command
=>
'KILL'
,
params
=> [
$target
,
join
(
'!'
,
$server
,
$comment
) ] },
grep
{
$_
ne
$peer_id
}
$self
->_state_connected_peers() );
$self
->{ircd}->send_output( {
prefix
=>
$self
->state_user_full(
$nick
),
command
=>
'KILL'
,
params
=> [
$target
,
join
(
'!'
,
$server
,
$comment
) ] },
$route_id
);
if
(
$route_id
eq
'spoofed'
) {
$self
->call(
'del_spoofed_nick'
,
$target
,
"Killed ($comment)"
);
}
else
{
$self
->{state}->{conns}->{
$route_id
}->{killed} = 1;
$self
->_terminate_conn_error(
$route_id
,
"Killed ($comment)"
);
}
}
else
{
$self
->{state}->{users}->{ u_irc
$target
}->{killed} = 1;
$self
->{ircd}->send_output( {
prefix
=>
$nick
,
command
=>
'KILL'
,
params
=> [
$target
,
join
(
'!'
,
$server
,
$comment
) ] },
grep
{
$_
ne
$peer_id
}
$self
->_state_connected_peers() );
$self
->{ircd}->send_output( @{
$self
->_daemon_peer_quit(
$target
,
"Killed ($nick ($comment))"
) } );
}
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_peer_svinfo {
my
$self
=
shift
;
my
$peer_id
=
shift
||
return
;
my
$ref
= [ ];
my
$args
= [
@_
];
my
$count
=
scalar
@{
$args
};
$self
->{state}->{conns}->{
$peer_id
}->{svinfo} =
$args
;
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_peer_ping {
my
$self
=
shift
;
my
$peer_id
=
shift
||
return
;
my
$server
=
$self
->server_name();
my
$ref
= [ ];
my
$args
= [
@_
];
my
$count
=
scalar
@{
$args
};
SWITCH: {
if
( !
$count
) {
last
SWITCH;
}
if
(
$count
>= 2 and (
uc
$server
ne
uc
$args
->[1] ) ) {
$self
->{ircd}->send_output( {
command
=>
'PING'
,
params
=>
$args
},
$self
->_state_peer_route(
$args
->[1] ) )
if
$self
->state_peer_exists(
$args
->[1] );
$self
->{ircd}->send_output( {
command
=>
'PING'
,
params
=>
$args
},
$self
->_state_user_route(
$args
->[1] ) )
if
$self
->state_nick_exists(
$args
->[1] );
last
SWITCH;
}
$self
->{ircd}->send_output( {
command
=>
'PONG'
,
params
=> [
$server
,
$args
->[0] ] },
$peer_id
);
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_peer_pong {
my
$self
=
shift
;
my
$peer_id
=
shift
||
return
;
my
$ref
= [ ];
my
$args
= [
@_
];
my
$count
=
scalar
@{
$args
};
SWITCH: {
if
( !
$count
) {
last
SWITCH;
}
if
(
$count
>= 2 and (
uc
$self
->server_name() ne
uc
$args
->[1] ) ) {
$self
->{ircd}->send_output( {
command
=>
'PONG'
,
params
=>
$args
},
$self
->_state_peer_route(
$args
->[1] ) )
if
$self
->state_peer_exists(
$args
->[1] );
$self
->{ircd}->send_output( {
command
=>
'PONG'
,
params
=>
$args
},
$self
->_state_user_route(
$args
->[1] ) )
if
$self
->state_nick_exists(
$args
->[1] );
last
SWITCH;
}
delete
$self
->{state}->{conns}->{
$peer_id
}->{pinged};
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_peer_server {
my
$self
=
shift
;
my
$peer_id
=
shift
||
return
;
my
$prefix
=
shift
||
return
;
my
$server
=
$self
->server_name();
my
$ref
= [ ];
my
$args
= [
@_
];
my
$count
=
scalar
@{
$args
};
my
$peer
=
$self
->{state}->{conns}->{
$peer_id
}->{name};
SWITCH: {
if
( !
$count
or
$count
< 2 ) {
last
SWITCH;
}
if
(
$self
->state_peer_exists(
$args
->[0] ) ) {
$self
->_terminate_conn_error(
$peer_id
,
'Server exists'
);
last
SWITCH;
}
my
$record
= {
name
=>
$args
->[0],
hops
=>
$args
->[1],
desc
=> (
$args
->[2] ||
''
),
route_id
=>
$peer_id
,
type
=>
'r'
,
peer
=>
$prefix
,
peers
=> { },
users
=> { },
};
my
$uname
=
uc
$record
->{name};
$self
->{state}->{peers}->{
$uname
} =
$record
;
$self
->{state}->{peers}->{
uc
$prefix
}->{peers}->{
$uname
} =
$record
;
$self
->{ircd}->send_output( {
prefix
=>
$prefix
,
command
=>
'SERVER'
,
params
=> [
$record
->{name},
$record
->{hops} + 1,
$record
->{desc} ] },
grep
{
$_
ne
$peer_id
}
$self
->_state_connected_peers() );
$self
->{ircd}->send_event(
"daemon_server"
,
$record
->{name},
$prefix
,
$record
->{hops},
$record
->{desc} );
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_peer_quit {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
my
$qmsg
=
shift
||
'Client Quit'
;
my
$conn_id
=
shift
;
my
$ref
= [ ];
my
$full
=
$self
->state_user_full(
$nick
);
$nick
= u_irc
$nick
;
my
$record
=
delete
$self
->{state}->{users}->{
$nick
};
return
$ref
unless
$record
;
$self
->{ircd}->send_output( {
prefix
=>
$record
->{nick},
command
=>
'QUIT'
,
params
=> [
$qmsg
] },
grep
{ !
$conn_id
or
$_
ne
$conn_id
}
$self
->_state_connected_peers() )
unless
$record
->{killed};
push
@{
$ref
}, {
prefix
=>
$full
,
command
=>
'QUIT'
,
params
=> [
$qmsg
] };
$self
->{ircd}->send_event(
"daemon_quit"
,
$full
,
$qmsg
);
delete
$self
->{state}->{users}->{
$_
}->{accepts}->{ u_irc
$nick
}
for
keys
%{
$record
->{accepts} };
my
$common
= { };
foreach
my
$uchan
(
keys
%{
$record
->{chans} } ) {
delete
$self
->{state}->{chans}->{
$uchan
}->{users}->{
$nick
};
foreach
my
$user
(
$self
->state_chan_list(
$uchan
) ) {
next
unless
$self
->_state_is_local_user(
$user
);
$common
->{
$user
} =
$self
->_state_user_route(
$user
);
}
unless
(
scalar
keys
%{
$self
->{state}->{chans}->{
$uchan
}->{users} } ) {
delete
$self
->{state}->{chans}->{
$uchan
};
}
}
push
( @{
$ref
},
$common
->{
$_
} )
for
keys
%{
$common
};
$self
->{state}->{stats}->{ops_online}--
if
$record
->{umode} =~ /o/;
$self
->{state}->{stats}->{invisible}--
if
$record
->{umode} =~ /i/;
delete
$self
->{state}->{peers}->{
uc
$record
->{server} }->{users}->{
$nick
};
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_peer_nick {
my
$self
=
shift
;
my
$peer_id
=
shift
||
return
;
my
$prefix
=
shift
;
my
$server
=
$self
->server_name();
my
$ref
= [ ];
my
$args
= [
@_
];
my
$count
=
scalar
@{
$args
};
my
$peer
=
$self
->{state}->{conns}->{
$peer_id
}->{name};
my
$nicklen
=
$self
->server_config(
'NICKLEN'
);
SWITCH: {
if
( !
$count
or (
$count
< 8 and !
$prefix
) ) {
$self
->_terminate_conn_error(
$peer_id
,
'Not enough arguments to server command.'
);
last
SWITCH;
}
if
(
$prefix
and
$self
->state_nick_exists(
$args
->[0] ) ) {
$self
->{ircd}->send_output( {
prefix
=>
$server
,
command
=>
'KILL'
,
params
=> [
$args
->[0],
"$server (Nick exists)"
] },
$peer_id
);
my
$unick
= u_irc
$prefix
;
$self
->{state}->{users}->{
$unick
}->{nick_collision} = 1;
$self
->daemon_server_kill(
$prefix
,
'Nick Collision'
,
$peer_id
);
last
SWITCH;
}
if
(
$prefix
and
length
(
$args
->[0] ) >
$nicklen
) {
$self
->{ircd}->send_output( {
prefix
=>
$server
,
command
=>
'KILL'
,
params
=> [
$args
->[0],
"$server (Bad nickname)"
] },
$peer_id
);
my
$unick
= u_irc
$prefix
;
$self
->{state}->{users}->{
$unick
}->{nick_collision} = 1;
$self
->daemon_server_kill(
$prefix
,
'Nick Collision'
,
$peer_id
);
last
SWITCH;
}
if
(
$prefix
) {
my
$full
=
$self
->state_user_full(
$prefix
);
my
$unick
= u_irc
$prefix
;
my
$new
=
$args
->[0];
my
$unew
= u_irc
$new
;
my
$ts
=
$args
->[1] ||
time
();
my
$record
=
$self
->{state}->{users}->{
$unick
};
my
$server
=
uc
$record
->{server};
if
(
$unick
eq
$unew
) {
$record
->{nick} =
$new
;
$record
->{ts} =
$ts
;
}
else
{
$record
->{nick} =
$new
;
$record
->{ts} =
$ts
;
delete
$self
->{state}->{users}->{
$_
}->{accepts}->{
$unick
}
for
keys
%{
$record
->{accepts} };
delete
$record
->{accepts};
delete
$self
->{state}->{users}->{
$unick
};
$self
->{state}->{users}->{
$unew
} =
$record
;
delete
$self
->{state}->{peers}->{
$server
}->{users}->{
$unick
};
$self
->{state}->{peers}->{
$server
}->{users}->{
$unew
} =
$record
;
foreach
my
$chan
(
keys
%{
$record
->{chans} } ) {
$self
->{state}->{chans}->{
$chan
}->{users}->{
$unew
} =
delete
$self
->{state}->{chans}->{
$chan
}->{users}->{
$unick
};
}
}
my
$common
= { };
foreach
my
$chan
(
keys
%{
$record
->{chans} } ) {
foreach
my
$user
(
$self
->state_chan_list(
$chan
) ) {
next
unless
$self
->_state_is_local_user(
$user
);
$common
->{
$user
} =
$self
->_state_user_route(
$user
);
}
}
$self
->{ircd}->send_output( {
prefix
=>
$prefix
,
command
=>
'NICK'
,
params
=>
$args
},
grep
{
$_
ne
$peer_id
}
$self
->_state_connected_peers() );
$self
->{ircd}->send_output( {
prefix
=>
$full
,
command
=>
'NICK'
,
params
=> [
$new
] },
map
{
$common
->{
$_
} }
keys
%{
$common
} );
$self
->{ircd}->send_event(
"daemon_nick"
,
$full
,
$new
);
last
SWITCH;
}
if
(
$self
->state_nick_exists(
$args
->[0] ) and
my
(
$nick
,
$userhost
) =
split
/!/,
$self
->state_user_full(
$args
->[0] ) ) {
my
$unick
= u_irc
$nick
;
my
$incoming
=
join
'@'
, @{
$args
}[4..5];
if
(
$userhost
eq
$incoming
) {
my
$ts
=
$self
->{state}->{users}->{
$unick
}->{ts};
if
(
$args
->[2] >
$ts
) {
$self
->{state}->{users}->{
$unick
}->{nick_collision} = 1;
$self
->daemon_server_kill(
$nick
,
'Nick Collision'
,
$peer_id
);
}
else
{
last
SWITCH;
}
}
else
{
my
$ts
=
$self
->{state}->{users}->{
$unick
}->{ts};
if
(
$args
->[2] <
$ts
) {
$self
->{state}->{users}->{
$unick
}->{nick_collision} = 1;
$self
->daemon_server_kill(
$nick
,
'Nick Collision'
,
$peer_id
);
}
else
{
last
SWITCH;
}
}
}
if
( !
$self
->state_peer_exists(
$args
->[6] ) ) {
last
SWITCH;
}
if
(
length
(
$args
->[0] ) >
$nicklen
) {
$self
->{ircd}->send_output( {
prefix
=>
$server
,
command
=>
'KILL'
,
params
=> [
$args
->[0],
"$server (Bad nickname)"
] },
$peer_id
);
last
SWITCH;
}
my
$unick
= u_irc
$args
->[0];
$args
->[3] =~ s/^\+//g;
my
$record
= {
nick
=>
$args
->[0],
hops
=>
$args
->[1],
ts
=>
$args
->[2],
type
=>
'r'
,
umode
=>
$args
->[3],
auth
=> {
ident
=>
$args
->[4],
hostname
=>
$args
->[5] },
route_id
=>
$peer_id
,
server
=>
$args
->[6],
ircname
=> (
$args
->[7] ||
''
),
};
$self
->{state}->{users}->{
$unick
} =
$record
;
$self
->{state}->{stats}->{ops_online}++
if
$record
->{umode} =~ /o/;
$self
->{state}->{stats}->{invisible}++
if
$record
->{umode} =~ /i/;
$self
->{state}->{peers}->{
uc
$record
->{server} }->{users}->{
$unick
} =
$record
;
$self
->_state_update_stats();
$self
->{ircd}->send_output( {
command
=>
'NICK'
,
params
=>
$args
},
grep
{
$_
ne
$peer_id
}
$self
->_state_connected_peers() );
$self
->{ircd}->send_event(
"daemon_nick"
, @{
$args
} );
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_peer_part {
my
$self
=
shift
;
my
$peer_id
=
shift
||
return
;
my
$nick
=
shift
||
return
;
my
$chan
=
shift
;
my
$ref
= [ ];
my
$args
= [
@_
];
my
$count
=
scalar
@{
$args
};
SWITCH: {
if
( !
$chan
) {
last
SWITCH;
}
if
( !
$self
->state_chan_exists(
$chan
) ) {
last
SWITCH;
}
if
( !
$self
->state_is_chan_member(
$nick
,
$chan
) ) {
last
SWITCH;
}
$self
->_send_output_to_channel(
$chan
, {
prefix
=>
$self
->state_user_full(
$nick
),
command
=>
'PART'
,
params
=> [
$chan
, (
$args
->[0] ||
$nick
) ] },
$peer_id
);
$nick
= u_irc
$nick
;
$chan
= u_irc
$chan
;
delete
$self
->{state}->{chans}->{
$chan
}->{users}->{
$nick
};
delete
$self
->{state}->{users}->{
$nick
}->{chans}->{
$chan
};
unless
(
scalar
keys
%{
$self
->{state}->{chans}->{
$chan
}->{users} } ) {
delete
$self
->{state}->{chans}->{
$chan
};
}
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_peer_kick {
my
$self
=
shift
;
my
$peer_id
=
shift
||
return
;
my
$nick
=
shift
||
return
;
my
$ref
= [ ];
my
$args
= [
@_
];
my
$count
=
scalar
@{
$args
};
SWITCH: {
if
( !
$count
or
$count
< 2 ) {
last
SWITCH;
}
my
$chan
= (
split
/,/,
$args
->[0] )[0];
my
$who
= (
split
/,/,
$args
->[1] )[0];
if
( !
$self
->state_chan_exists(
$chan
) ) {
last
SWITCH;
}
$chan
=
$self
->_state_chan_name(
$chan
);
if
( !
$self
->state_nick_exists(
$who
) ) {
last
SWITCH;
}
$who
=
$self
->state_user_nick(
$who
);
if
( !
$self
->state_is_chan_op(
$nick
,
$chan
) ) {
last
SWITCH;
}
if
( !
$self
->state_is_chan_member(
$who
,
$chan
) ) {
last
SWITCH;
}
my
$comment
=
$args
->[2] ||
$who
;
$self
->_send_output_to_channel(
$chan
, {
prefix
=>
$self
->state_user_full(
$nick
),
command
=>
'KICK'
,
params
=> [
$chan
,
$who
,
$comment
] },
$peer_id
);
$who
= u_irc
$who
;
$chan
= u_irc
$chan
;
delete
$self
->{state}->{chans}->{
$chan
}->{users}->{
$who
};
delete
$self
->{state}->{users}->{
$who
}->{chans}->{
$chan
};
unless
(
scalar
keys
%{
$self
->{state}->{chans}->{
$chan
}->{users} } ) {
delete
$self
->{state}->{chans}->{
$chan
};
}
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_peer_sjoin {
my
$self
=
shift
;
my
$peer_id
=
shift
||
return
;
my
$prefix
=
shift
;
my
$ref
= [ ];
my
$args
= [
@_
];
my
$count
=
scalar
@{
$args
};
SWITCH: {
if
( !
$count
or
$count
< 4 ) {
last
SWITCH;
}
my
$ts
=
$args
->[0];
my
$chan
=
$args
->[1];
my
$nicks
=
pop
@{
$args
};
my
$ignore_modes
= 0;
if
( !
$self
->state_chan_exists(
$chan
) ) {
my
$server
=
$self
->server_name();
my
$chanrec
= {
name
=>
$chan
,
ts
=>
$ts
};
my
@args
= @{
$args
}[2..$
my
$cmode
=
shift
@args
;
$cmode
=~ s/^\+//g;
$chanrec
->{mode} =
$cmode
;
foreach
my
$mode
(
split
//,
$cmode
) {
my
$arg
;
$arg
=
shift
@args
if
$mode
=~ /[lk]/;
$chanrec
->{climit} =
$arg
if
$mode
eq
'l'
;
$chanrec
->{ckey} =
$arg
if
$mode
eq
'k'
;
}
push
@{
$args
},
$nicks
;
my
$uchan
= u_irc
$chanrec
->{name};
foreach
my
$nick
(
split
/\s+/,
$nicks
) {
my
$umode
=
''
;
$umode
.=
'o'
if
$nick
=~ s/\@//g;
$umode
=
'h'
if
$nick
=~ s/\%//g;
$umode
.=
'v'
if
$nick
=~ s/\+//g;
my
$unick
= u_irc
$nick
;
$chanrec
->{users}->{
$unick
} =
$umode
;
$self
->{state}->{users}->{
$unick
}->{chans}->{
$uchan
} =
$umode
;
$self
->{ircd}->send_event(
"daemon_join"
,
$self
->state_user_full(
$nick
),
$chan
);
$self
->{ircd}->send_event(
"daemon_mode"
,
$server
,
$chan
,
'+'
.
$umode
,
$nick
)
if
$umode
;
}
$self
->{state}->{chans}->{
$uchan
} =
$chanrec
;
$self
->{ircd}->send_output( {
prefix
=>
$prefix
,
command
=>
'SJOIN'
,
params
=>
$args
},
grep
{
$_
ne
$peer_id
}
$self
->_state_connected_peers() );
last
SWITCH;
}
my
$chanrec
=
$self
->{state}->{chans}->{ u_irc
$chan
};
my
@local_users
=
map
{
$self
->_state_user_route(
$_
) }
grep
{
$self
->_state_is_local_user(
$_
) }
keys
%{
$chanrec
->{users} };
if
(
$ts
<
$chanrec
->{ts} ) {
if
(
$nicks
=~ /^\@/ ) {
my
@deop
;
my
@deop_list
;
my
$common
= { };
foreach
my
$user
(
keys
%{
$chanrec
->{users} } ) {
$common
->{
$user
} =
$self
->_state_user_route(
$user
)
if
$self
->_state_is_local_user(
$user
);
next
unless
$chanrec
->{users}->{
$user
};
my
$current
=
$chanrec
->{users}->{
$user
};
my
$proper
=
$self
->state_user_nick(
$user
);
$chanrec
->{users}->{
$user
} =
''
;
$self
->{state}->{users}->{
$user
}->{chans}->{ u_irc
$chanrec
->{name} } =
''
;
push
@deop
,
"-$current"
;
push
@deop_list
,
$proper
for
split
//,
$current
;
}
if
(
scalar
keys
%{
$common
} and
scalar
@deop
) {
my
$server
=
$self
->server_name();
$self
->{ircd}->send_event(
"daemon_mode"
,
$server
,
$chanrec
->{name}, unparse_mode_line(
join
''
,
@deop
),
@deop_list
);
my
@output_modes
;
my
$length
=
length
(
$server
) + 4 +
length
(
$chan
) + 4;
my
@buffer
= (
''
,
''
);
foreach
my
$deop
(
@deop
) {
my
$arg
=
shift
@deop_list
;
my
$mode_line
= unparse_mode_line(
$buffer
[0] .
$deop
);
if
(
length
(
join
' '
,
$mode_line
,
$buffer
[1],
$arg
) +
$length
> 510 ) {
push
@output_modes
, {
prefix
=>
$server
,
command
=>
'MODE'
,
params
=> [
$chanrec
->{name},
$buffer
[0],
split
/\s+/,
$buffer
[1] ],
colonify
=> 0 };
$buffer
[0] =
$deop
;
$buffer
[1] =
$arg
;
next
;
}
$buffer
[0] =
$mode_line
;
if
(
$buffer
[1] ) {
$buffer
[1] =
join
' '
,
$buffer
[1],
$arg
;
}
else
{
$buffer
[1] =
$arg
;
}
}
push
@output_modes
, {
prefix
=>
$server
,
command
=>
'MODE'
,
params
=> [
$chanrec
->{name},
$buffer
[0],
split
/\s+/,
$buffer
[1] ],
colonify
=> 0 };
$self
->{ircd}->send_output(
$_
,
values
%{
$common
} )
for
@output_modes
;
}
my
$origmode
=
$chanrec
->{mode};
my
@args
= @{
$args
}[2..$
my
$chanmode
=
shift
@args
;
my
$reply
=
''
;
my
@reply_args
;
foreach
my
$mode
(
grep
{
$_
ne
'+'
}
split
//,
$chanmode
) {
my
$arg
;
$arg
=
shift
@args
if
$mode
=~ /[lk]/;
if
(
$mode
eq
'l'
and (
$chanrec
->{mode} !~ /l/ or
$arg
ne
$chanrec
->{climit} ) ) {
$reply
.=
'+'
.
$mode
;
push
@reply_args
,
$arg
;
$chanrec
->{mode} .=
$mode
unless
$chanrec
->{mode} =~ /
$mode
/;
$chanrec
->{mode} =
join
''
,
sort
split
//,
$chanrec
->{mode};
$chanrec
->{climit} =
$arg
;
}
elsif
(
$mode
eq
'k'
and (
$chanrec
->{mode} !~ /k/ or
$arg
ne
$chanrec
->{ckey} ) ) {
$reply
.=
'+'
.
$mode
;
push
@reply_args
,
$arg
;
$chanrec
->{mode} .=
$mode
unless
$chanrec
->{mode} =~ /
$mode
/;
$chanrec
->{mode} =
join
''
,
sort
split
//,
$chanrec
->{mode};
$chanrec
->{ckey} =
$arg
;
}
elsif
(
$chanrec
->{mode} !~ /
$mode
/ ) {
$reply
.=
'+'
.
$mode
;
$chanrec
->{mode} .=
$mode
unless
$chanrec
->{mode} =~ /
$mode
/;
$chanrec
->{mode} =
join
''
,
sort
split
//,
$chanrec
->{mode};
}
}
if
(
scalar
keys
%{
$common
} and (
$reply
or
$origmode
) ) {
$origmode
=
join
''
,
grep
{
$chanmode
!~ /
$_
/ }
split
//, (
$origmode
||
''
);
$chanrec
->{mode} =~ s/[
$origmode
]//g
if
$origmode
;
$reply
=
'-'
.
$origmode
.
$reply
if
$origmode
;
if
(
$origmode
and
$origmode
=~ /k/ ) {
unshift
@reply_args
,
'*'
;
delete
$chanrec
->{ckey};
}
delete
$chanrec
->{climit}
if
$origmode
and
$origmode
=~ /l/;
$self
->{ircd}->send_output( {
prefix
=>
$self
->server_name(),
command
=>
'MODE'
,
params
=> [
$chanrec
->{name}, unparse_mode_line(
$reply
),
@reply_args
],
colonify
=> 0 },
values
%{
$common
} )
if
$reply
;
}
$self
->{ircd}->send_output( {
prefix
=>
$self
->server_name(),
command
=>
'NOTICE'
,
params
=> [
$chanrec
->{name},
"*** Notice -- TS for "
.
$chanrec
->{name} .
" changed from "
.
$chanrec
->{ts} .
" to $ts"
] },
@local_users
);
$chanrec
->{ts} =
$ts
;
}
elsif
(
scalar
grep
{ /^\@/ }
$self
->state_chan_list_prefixed(
$chan
) ) {
$args
->[0] =
$chanrec
->{ts};
}
else
{
$self
->{ircd}->send_output( {
prefix
=>
$self
->server_name(),
command
=>
'NOTICE'
,
params
=> [
$chanrec
->{name},
"*** Notice -- TS for "
.
$chanrec
->{name} .
" changed from "
.
$chanrec
->{ts} .
" to $ts"
] },
@local_users
);
$chanrec
->{ts} =
$ts
;
}
}
elsif
(
$ts
>
$chanrec
->{ts} ) {
if
(
$nicks
!~ /^\@/ ) {
$args
->[0] =
$chanrec
->{ts};
}
elsif
(
scalar
grep
{ /^\@/ }
$self
->state_chan_list_prefixed(
$chan
) ) {
pop
@{
$args
}
while
$
$args
->[2] =
'+'
;
$args
->[0] =
$chanrec
->{ts};
$nicks
=
join
' '
,
map
{ s/[@%+]//g;
$_
; }
split
/\s+/,
$nicks
;
}
else
{
$chanrec
->{ts} =
$ts
;
}
}
push
@{
$args
},
$nicks
;
$self
->{ircd}->send_output( {
prefix
=>
$prefix
,
command
=>
'SJOIN'
,
params
=>
$args
},
grep
{
$_
ne
$peer_id
}
$self
->_state_connected_peers() );
my
$uchan
= u_irc
$chanrec
->{name};
my
$modes
;
my
@mode_parms
;
foreach
my
$nick
(
split
/\s+/,
$nicks
) {
my
$proper
=
$nick
;
$proper
=~ s/[@%+]//g;
$nick
= u_irc
$nick
;
my
$umode
=
''
;
my
@op_list
;
$umode
.=
'o'
if
$nick
=~ s/\@//g;
$umode
=
'h'
if
$nick
=~ s/\%//g;
$umode
.=
'v'
if
$nick
=~ s/\+//g;
$chanrec
->{users}->{
$nick
} =
$umode
;
$self
->{state}->{users}->{
$nick
}->{chans}->{
$uchan
} =
$umode
;
push
@op_list
,
$proper
for
split
//,
$umode
;
my
$output
= {
prefix
=>
$self
->state_user_full(
$nick
),
command
=>
'JOIN'
,
params
=> [
$chanrec
->{name} ] };
$self
->{ircd}->send_output(
$output
,
@local_users
);
$self
->{ircd}->send_event(
"daemon_join"
,
$output
->{prefix},
$chanrec
->{name} );
if
(
$umode
) {
$modes
.=
$umode
;
push
@mode_parms
,
@op_list
;
}
}
if
(
$modes
) {
my
$server
=
$self
->server_name();
$self
->{ircd}->send_event(
"daemon_mode"
,
$server
,
$chanrec
->{name},
'+'
.
$modes
,
@mode_parms
);
my
@output_modes
;
my
$length
=
length
(
$server
) + 4 +
length
(
$chan
) + 4;
my
@buffer
= (
'+'
,
''
);
foreach
my
$umode
(
split
//,
$modes
) {
my
$arg
=
shift
@mode_parms
;
if
(
length
(
join
' '
,
@buffer
,
$arg
) +
$length
> 510 ) {
push
@output_modes
, {
prefix
=>
$server
,
command
=>
'MODE'
,
params
=> [
$chanrec
->{name},
$buffer
[0],
split
/\s+/,
$buffer
[1] ],
colonify
=> 0 };
$buffer
[0] =
"+$umode"
;
$buffer
[1] =
$arg
;
next
;
}
$buffer
[0] .=
$umode
;
if
(
$buffer
[1] ) {
$buffer
[1] =
join
' '
,
$buffer
[1],
$arg
;
}
else
{
$buffer
[1] =
$arg
;
}
}
push
@output_modes
, {
prefix
=>
$server
,
command
=>
'MODE'
,
params
=> [
$chanrec
->{name},
$buffer
[0],
split
/\s+/,
$buffer
[1] ],
colonify
=> 0 };
$self
->{ircd}->send_output(
$_
,
@local_users
)
for
@output_modes
;
}
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_peer_mode {
my
$self
=
shift
;
my
$peer_id
=
shift
||
return
;
my
$nick
=
shift
||
return
;
my
$chan
=
shift
;
my
$server
=
$self
->server_name();
my
$ref
= [ ];
my
$args
= [
@_
];
my
$count
=
scalar
@{
$args
};
SWITCH: {
if
( !
$self
->state_chan_exists(
$chan
) ) {
last
SWITCH;
}
my
$record
=
$self
->{state}->{chans}->{ u_irc
$chan
};
$chan
=
$record
->{name};
my
$full
;
$full
=
$self
->state_user_full(
$nick
)
if
$self
->state_nick_exists(
$nick
);
my
$reply
;
my
@reply_args
;
my
$parsed_mode
= parse_mode_line( @{
$args
} );
while
(
my
$mode
=
shift
( @{
$parsed_mode
->{modes} } ) ) {
my
$arg
;
$arg
=
shift
( @{
$parsed_mode
->{args} } )
if
(
$mode
=~ /^(\+[ohvklbIe]|-[ohvbIe])/ );
if
(
my
(
$flag
,
$char
) =
$mode
=~ /^(\+|-)([ohv])/ ) {
if
(
$flag
eq
'+'
and
$record
->{users}->{ u_irc
$arg
} !~ /
$char
/ ) {
$arg
= u_irc
$arg
;
next
if
(
$mode
eq
'+h'
and
$record
->{users}->{
$arg
} =~ /o/ );
if
(
$char
eq
'h'
and
$record
->{users}->{
$arg
} =~ /v/ ) {
$record
->{users}->{
$arg
} =~ s/v//g;
$reply
.=
'-v'
;
push
@reply_args
,
$self
->state_user_nick(
$arg
);
}
if
(
$char
eq
'o'
and
$record
->{users}->{
$arg
} =~ /h/ ) {
$record
->{users}->{
$arg
} =~ s/h//g;
$reply
.=
'-h'
;
push
@reply_args
,
$self
->state_user_nick(
$arg
);
}
$record
->{users}->{
$arg
} =
join
(
''
,
sort
split
//,
$record
->{users}->{
$arg
} .
$char
);
$self
->{state}->{users}->{
$arg
}->{chans}->{ u_irc
$chan
} =
$record
->{users}->{
$arg
};
$reply
.=
"+$char"
;
push
@reply_args
,
$self
->state_user_nick(
$arg
);
}
if
(
$flag
eq
'-'
and
$record
->{users}->{ u_irc
$arg
} =~ /
$char
/ ) {
$arg
= u_irc
$arg
;
$record
->{users}->{
$arg
} =~ s/
$char
//g;
$self
->{state}->{users}->{
$arg
}->{chans}->{ u_irc
$chan
} =
$record
->{users}->{
$arg
};
$reply
.=
"-$char"
;
push
@reply_args
,
$self
->state_user_nick(
$arg
);
}
next
;
}
if
(
$mode
eq
'+l'
and
$arg
=~ /^\d+$/ and
$arg
> 0 ) {
$record
->{mode} =
join
(
''
,
sort
split
//,
$record
->{mode} .
'l'
)
unless
$record
->{mode} =~ /l/;
$record
->{climit} =
$arg
;
$reply
.=
'+l'
;
push
@reply_args
,
$arg
;
next
;
}
if
(
$mode
eq
'-l'
and
$record
->{mode} =~ /l/ ) {
$record
->{mode} =~ s/l//g;
delete
$record
->{climit};
$reply
.=
'-l'
;
next
;
}
if
(
$mode
eq
'+k'
and
$arg
) {
$record
->{mode} =
join
(
''
,
sort
split
//,
$record
->{mode} .
'k'
)
unless
$record
->{mode} =~ /k/;
$record
->{ckey} =
$arg
;
$reply
.=
'+k'
;
push
@reply_args
,
$arg
;
next
;
}
if
(
$mode
eq
'-k'
and
$record
->{mode} =~ /k/ ) {
$record
->{mode} =~ s/k//g;
delete
$record
->{ckey};
$reply
.=
'-k'
;
next
;
}
if
(
my
(
$flag
) =
$mode
=~ /(\+|-)b/ ) {
my
$mask
= parse_ban_mask(
$arg
);
my
$umask
= u_irc
$mask
;
if
(
$flag
eq
'+'
and !
$record
->{bans}->{
$umask
} ) {
$record
->{bans}->{
$umask
} = [
$mask
, (
$full
||
$server
),
time
() ];
$reply
.=
'+b'
;
push
@reply_args
,
$mask
;
}
if
(
$flag
eq
'-'
and
$record
->{bans}->{
$umask
} ) {
delete
$record
->{bans}->{
$umask
};
$reply
.=
'-b'
;
push
@reply_args
,
$mask
;
}
next
;
}
if
(
my
(
$flag
) =
$mode
=~ /(\+|-)I/ ) {
my
$mask
= parse_ban_mask(
$arg
);
my
$umask
= u_irc
$mask
;
if
(
$flag
eq
'+'
and !
$record
->{invex}->{
$umask
} ) {
$record
->{invex}->{
$umask
} = [
$mask
, (
$full
||
$server
),
time
() ];
$reply
.=
'+I'
;
push
@reply_args
,
$mask
;
}
if
(
$flag
eq
'-'
and
$record
->{invex}->{
$umask
} ) {
delete
$record
->{invex}->{
$umask
};
$reply
.=
'-I'
;
push
@reply_args
,
$mask
;
}
next
;
}
if
(
my
(
$flag
) =
$mode
=~ /(\+|-)e/ ) {
my
$mask
= parse_ban_mask(
$arg
);
my
$umask
= u_irc
$mask
;
if
(
$flag
eq
'+'
and !
$record
->{excepts}->{
$umask
} ) {
$record
->{excepts}->{
$umask
} = [
$mask
, (
$full
||
$server
),
time
() ];
$reply
.=
'+e'
;
push
@reply_args
,
$mask
;
}
if
(
$flag
eq
'-'
and
$record
->{excepts}->{
$umask
} ) {
delete
$record
->{excepts}->{
$umask
};
$reply
.=
'-e'
;
push
@reply_args
,
$mask
;
}
next
;
}
my
(
$flag
,
$char
) =
split
//,
$mode
;
if
(
$flag
eq
'+'
and
$record
->{mode} !~ /
$char
/ ) {
$record
->{mode} =
join
(
''
,
sort
split
//,
$record
->{mode} .
$char
);
$reply
.=
"+$char"
;
next
;
}
if
(
$flag
eq
'-'
and
$record
->{mode} =~ /
$char
/ ) {
$record
->{mode} =~ s/
$char
//g;
$reply
.=
"-$char"
;
next
;
}
}
unshift
@{
$args
},
$record
->{name};
if
(
$reply
) {
my
$parsed_line
= unparse_mode_line
$reply
;
$self
->{ircd}->send_output( {
prefix
=>
$nick
,
command
=>
'MODE'
,
params
=> [
$record
->{name},
$parsed_line
,
@reply_args
],
colonify
=> 0 },
grep
{
$_
ne
$peer_id
}
$self
->_state_connected_peers() );
$self
->{ircd}->send_output( {
prefix
=> (
$full
||
$server
),
command
=>
'MODE'
,
params
=> [
$record
->{name},
$parsed_line
,
@reply_args
],
colonify
=> 0 },
map
{
$self
->_state_user_route(
$_
) }
grep
{
$self
->_state_is_local_user(
$_
) }
keys
%{
$record
->{users} } );
$self
->{ircd}->send_event(
"daemon_mode"
, (
$full
||
$server
),
$record
->{name},
$parsed_line
,
@reply_args
);
}
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_peer_umode {
my
$self
=
shift
;
my
$peer_id
=
shift
||
return
;
my
$prefix
=
shift
||
return
;
my
$nick
=
shift
||
return
;
my
$umode
=
shift
;
my
$server
=
$self
->server_name();
my
$ref
= [ ];
my
$record
=
$self
->{state}->{users}->{ u_irc
$nick
};
my
$parsed_mode
= parse_mode_line(
$umode
);
while
(
my
$mode
=
shift
@{
$parsed_mode
->{modes} } ) {
my
(
$action
,
$char
) =
split
//,
$mode
;
if
(
$action
eq
'+'
and
$record
->{umode} !~ /
$char
/ ) {
$record
->{umode} .=
$char
;
$self
->{state}->{stats}->{invisible}++
if
$char
eq
'i'
;
if
(
$char
eq
'o'
) {
$self
->{state}->{stats}->{ops_online}++;
}
}
if
(
$action
eq
'-'
and
$record
->{umode} =~ /
$char
/ ) {
$record
->{umode} =~ s/
$char
//g;
$self
->{state}->{stats}->{invisible}--
if
$char
eq
'i'
;
if
(
$char
eq
'o'
) {
$self
->{state}->{stats}->{ops_online}--;
}
}
}
$self
->{ircd}->send_output( {
prefix
=>
$prefix
,
command
=>
'MODE'
,
params
=> [
$nick
,
$umode
] },
grep
{
$_
ne
$peer_id
}
$self
->_state_connected_peers() );
$self
->{ircd}->send_event(
"daemon_umode"
,
$self
->state_user_full(
$nick
),
$umode
);
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_peer_message {
my
$self
=
shift
;
my
$peer_id
=
shift
||
return
;
my
$nick
=
shift
||
return
;
my
$type
=
shift
||
return
;
my
$ref
= [ ];
my
$args
= [
@_
];
my
$count
=
scalar
@{
$args
};
SWITCH: {
if
( !
$count
) {
push
@{
$ref
}, [
'461'
,
$type
];
last
SWITCH;
}
if
(
$count
< 2 or !
$args
->[1] ) {
push
@{
$ref
}, [
'412'
];
last
SWITCH;
}
my
$targets
= 0;
my
$max_targets
=
$self
->server_config(
'MAXTARGETS'
);
my
$full
=
$self
->state_user_full(
$nick
);
my
$targs
=
$self
->_state_parse_msg_targets(
$args
->[0] );
LOOP:
foreach
my
$target
(
keys
%{
$targs
} ) {
my
$targ_type
=
shift
@{
$targs
->{
$target
} };
if
(
$targ_type
=~ /(server|host)mask/ and !
$self
->state_user_is_operator(
$nick
) ) {
push
@{
$ref
}, [
'481'
];
next
LOOP;
}
if
(
$targ_type
=~ /(server|host)mask/ and
$targs
->{
$target
}->[0] !~ /\./ ) {
push
@{
$ref
}, [
'413'
,
$target
];
next
LOOP;
}
if
(
$targ_type
=~ /(server|host)mask/ and
$targs
->{
$target
}->[0] !~ /\x2E.*[\x2A\x3F]+.*$/ ) {
push
@{
$ref
}, [
'414'
,
$target
];
next
LOOP;
}
if
(
$targ_type
eq
'channel_ext'
and !
$self
->state_chan_exists(
$targs
->{
$target
}->[1] ) ) {
push
@{
$ref
}, [
'401'
,
$targs
->{
$target
}->[1] ];
next
LOOP;
}
if
(
$targ_type
eq
'channel'
and !
$self
->state_chan_exists(
$target
) ) {
push
@{
$ref
}, [
'401'
,
$target
];
next
LOOP;
}
if
(
$targ_type
eq
'nick'
and !
$self
->state_nick_exists(
$target
) ) {
push
@{
$ref
}, [
'401'
,
$target
];
next
LOOP;
}
if
(
$targ_type
eq
'nick_ext'
and !
$self
->state_peer_exists(
$targs
->{
$target
}->[1] ) ) {
push
@{
$ref
}, [
'402'
,
$targs
->{
$target
}->[1] ];
next
LOOP;
}
$targets
++;
if
(
$targets
>
$max_targets
) {
push
@{
$ref
}, [
'407'
,
$target
];
last
SWITCH;
}
if
(
$targ_type
eq
'servermask'
) {
my
$us
= 0;
my
%targets
;
my
$ucserver
=
uc
$self
->server_name();
foreach
my
$peer
(
keys
%{
$self
->{state}->{peers} } ) {
if
( matches_mask(
$targs
->{
$target
}->[0],
$peer
) ) {
if
(
$ucserver
eq
$peer
) {
$us
= 1;
}
else
{
$targets
{
$self
->_state_peer_route(
$peer
) }++;
}
}
}
delete
$targets
{
$peer_id
};
$self
->{ircd}->send_output( {
prefix
=>
$nick
,
command
=>
$type
,
params
=> [
$target
,
$args
->[1] ] },
keys
%targets
);
if
(
$us
) {
my
$local
=
$self
->{state}->{peers}->{
uc
$self
->server_name() }->{users};
my
@local
;
my
$spoofed
= 0;
foreach
my
$luser
(
values
%{
$local
} ) {
if
(
$luser
->{route_id} eq
'spoofed'
) {
$spoofed
= 1;
}
else
{
push
@local
,
$luser
->{route_id};
}
}
$self
->{ircd}->send_output( {
prefix
=>
$full
,
command
=>
$type
,
params
=> [
$target
,
$args
->[1] ] },
@local
);
$self
->{ircd}->send_event(
"daemon_"
.
lc
$type
,
$full
,
$target
,
$args
->[1] )
if
$spoofed
;
}
next
LOOP;
}
if
(
$targ_type
eq
'hostmask'
) {
my
$spoofed
= 0;
my
%targets
;
my
@local
;
HOST:
foreach
my
$luser
(
values
%{
$self
->{state}->{users} } ) {
next
HOST
unless
matches_mask(
$targs
->{
$target
}->[0],
$luser
->{auth}->{hostname} );
if
(
$luser
->{route_id} eq
'spoofed'
) {
$spoofed
= 1;
}
elsif
(
$luser
->{type} eq
'r'
) {
$targets
{
$luser
->{route_id} }++;
}
else
{
push
@local
,
$luser
->{route_id};
}
}
delete
$targets
{
$peer_id
};
$self
->{ircd}->send_output( {
prefix
=>
$nick
,
command
=>
$type
,
params
=> [
$target
,
$args
->[1] ] },
keys
%targets
);
$self
->{ircd}->send_output( {
prefix
=>
$full
,
command
=>
$type
,
params
=> [
$target
,
$args
->[1] ] },
@local
);
$self
->{ircd}->send_event(
"daemon_"
.
lc
$type
,
$full
,
$target
,
$args
->[1] )
if
$spoofed
;
next
LOOP;
}
if
(
$targ_type
eq
'nick_ext'
) {
$targs
->{
$target
}->[1] =
$self
->_state_peer_name(
$targs
->{
$target
}->[1] );
if
(
$targs
->{
$target
}->[2] and !
$self
->state_user_is_operator(
$nick
) ) {
push
@{
$ref
}, [
'481'
];
next
LOOP;
}
if
(
$targs
->{
$target
}->[1] ne
$self
->server_name() ) {
$self
->{ircd}->send_output( {
prefix
=>
$nick
,
command
=>
$type
,
params
=> [
$target
,
$args
->[1] ] },
$self
->_state_peer_route(
$targs
->{
$target
}->[1] ) );
next
LOOP;
}
if
(
uc
(
$targs
->{
$target
}->[0] ) eq
'OPERS'
) {
unless
(
$self
->state_user_is_operator(
$nick
) ) {
push
@{
$ref
}, [
'481'
];
next
LOOP;
}
$self
->{ircd}->send_output( {
prefix
=>
$full
,
command
=>
$type
,
params
=> [
$target
,
$args
->[1] ] },
keys
%{
$self
->{state}->{localops} } );
next
LOOP;
}
my
@local
=
$self
->_state_find_user_host(
$targs
->{
$target
}->[0],
$targs
->{
$target
}->[2] );
if
(
scalar
@local
== 1 ) {
my
$ref
=
shift
@local
;
if
(
$ref
->[0] eq
'spoofed'
) {
$self
->{ircd}->send_event(
"daemon_"
.
lc
$type
,
$full
,
$ref
->[1],
$args
->[1] );
}
else
{
$self
->{ircd}->send_output( {
prefix
=>
$full
,
command
=>
$type
,
params
=> [
$target
,
$args
->[1] ] },
$ref
->[0] );
}
}
else
{
push
@{
$ref
}, [
'407'
,
$target
];
next
LOOP;
}
}
my
$channel
;
my
$status_msg
;
if
(
$targ_type
eq
'channel'
) {
$channel
=
$self
->_state_chan_name(
$target
);
}
if
(
$targ_type
eq
'channel_ext'
) {
$channel
=
$self
->_state_chan_name(
$targs
->{target}->[1] );
$status_msg
=
$targs
->{target}->[0];
}
if
(
$channel
and
$status_msg
and !
$self
->state_user_chan_mode(
$nick
,
$channel
) ) {
push
@{
$ref
}, [
'482'
,
$target
];
next
LOOP;
}
if
(
$channel
and
$self
->state_chan_mode_set(
$channel
,
'n'
) and !
$self
->state_is_chan_member(
$nick
,
$channel
) ) {
push
@{
$ref
}, [
'404'
,
$channel
];
next
LOOP;
}
if
(
$channel
and
$self
->state_chan_mode_set(
$channel
,
'm'
) and !
$self
->state_user_chan_mode(
$nick
,
$channel
) ) {
push
@{
$ref
}, [
'404'
,
$channel
];
next
LOOP;
}
if
(
$channel
and
$self
->_state_user_banned(
$nick
,
$channel
) and !
$self
->state_user_chan_mode(
$nick
,
$channel
) ) {
push
@{
$ref
}, [
'404'
,
$channel
];
next
LOOP;
}
if
(
$channel
) {
my
$common
= { };
my
$msg
= {
command
=>
$type
,
params
=> [ (
$status_msg
?
$target
:
$channel
),
$args
->[1] ] };
foreach
my
$member
(
$self
->state_chan_list(
$channel
,
$status_msg
) ) {
next
if
$self
->_state_user_is_deaf(
$member
);
$common
->{
$self
->_state_user_route(
$member
) }++;
}
delete
$common
->{
$peer_id
};
foreach
my
$route_id
(
keys
%{
$common
} ) {
$msg
->{prefix} =
$nick
;
$msg
->{prefix} =
$full
if
$self
->_connection_is_client(
$route_id
);
unless
(
$route_id
eq
'spoofed'
) {
$self
->{ircd}->send_output(
$msg
,
$route_id
);
}
else
{
my
$tmsg
=
$type
eq
'PRIVMSG'
?
'public'
:
'notice'
;
$self
->{ircd}->send_event(
"daemon_$tmsg"
,
$full
,
$channel
,
$args
->[1] );
}
}
next
LOOP;
}
my
$server
=
$self
->server_name();
if
(
$self
->state_nick_exists(
$target
) ) {
$target
=
$self
->state_user_nick(
$target
);
if
(
my
$away
=
$self
->_state_user_away_msg(
$target
) ) {
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'301'
,
params
=> [
$nick
,
$target
,
$away
] };
}
my
$targ_umode
=
$self
->state_user_umode(
$target
);
if
(
$targ_umode
and
$targ_umode
=~ /[Gg]/ ) {
my
$targ_rec
=
$self
->{state}->{users}->{ u_irc
$target
};
if
( (
$targ_umode
=~ /G/ and ( !
$self
->state_users_share_chan(
$target
,
$nick
) or !
$targ_rec
->{accepts}->{ u_irc
$nick
} ) ) or (
$targ_umode
=~ /g/ and !
$targ_rec
->{accepts}->{ u_irc
$nick
} ) ) {
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'716'
,
params
=> [
$nick
,
$target
,
'is in +g mode (server side ignore)'
] };
if
( !
$targ_rec
->{last_caller} or (
time
() -
$targ_rec
->{last_caller} ) >= 60 ) {
my
(
$n
,
$uh
) =
split
/!/,
$self
->state_user_full(
$nick
);
$self
->{ircd}->send_output( {
prefix
=>
$server
,
command
=>
'718'
,
params
=> [
$target
,
"$n\[$uh\]"
,
'is messaging you, and you are umode +g.'
] },
$targ_rec
->{route_id} )
unless
$targ_rec
->{route_id} eq
'spoofed'
;
push
@{
$ref
}, {
prefix
=>
$server
,
command
=>
'717'
,
params
=> [
$nick
,
$target
,
'has been informed that you messaged them.'
] };
}
$targ_rec
->{last_caller} =
time
();
next
LOOP;
}
}
my
$msg
= {
prefix
=>
$nick
,
command
=>
$type
,
params
=> [
$target
,
$args
->[1] ] };
my
$route_id
=
$self
->_state_user_route(
$target
);
if
(
$route_id
eq
'spoofed'
) {
$msg
->{prefix} =
$full
;
$self
->{ircd}->send_event(
"daemon_"
.
lc
$type
,
$full
,
$target
,
$args
->[1] );
}
else
{
$msg
->{prefix} =
$full
if
$self
->_connection_is_client(
$route_id
);
$self
->{ircd}->send_output(
$msg
,
$route_id
);
}
next
LOOP;
}
}
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_peer_topic {
my
$self
=
shift
;
my
$peer_id
=
shift
||
return
;
my
$nick
=
shift
||
return
;
my
$server
=
$self
->server_name();
my
$ref
= [ ];
my
$args
= [
@_
];
my
$count
=
scalar
@{
$args
};
SWITCH:{
if
( !
$count
) {
last
SWITCH;
}
if
( !
$self
->state_chan_exists(
$args
->[0] ) ) {
last
SWITCH;
}
my
$chan_name
=
$self
->_state_chan_name(
$args
->[0] );
my
$record
=
$self
->{state}->{chans}->{ u_irc
$args
->[0] };
$record
->{topic} = [
$args
->[1],
$self
->state_user_full(
$nick
),
time
() ];
$self
->_send_output_to_channel(
$args
->[0], {
prefix
=>
$self
->state_user_full(
$nick
),
command
=>
'TOPIC'
,
params
=> [
$chan_name
,
$args
->[1] ] },
$peer_id
);
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_peer_invite {
my
$self
=
shift
;
my
$peer_id
=
shift
||
return
;
my
$nick
=
shift
||
return
;
my
$server
=
$self
->server_name();
my
$ref
= [ ];
my
$args
= [
@_
];
my
$count
=
scalar
@{
$args
};
SWITCH: {
if
( !
$count
or
$count
< 3 ) {
last
SWITCH;
}
my
(
$who
,
$chan
) = @{
$args
};
$who
=
$self
->state_user_nick(
$who
);
$chan
=
$self
->_state_chan_name(
$chan
);
my
$local
;
if
(
$self
->_state_is_local_user(
$who
) ) {
my
$record
=
$self
->{state}->{users}->{ u_irc
$who
};
$record
->{invites}->{ u_irc
$chan
} =
time
();
$local
= 1;
}
my
$route_id
=
$self
->_state_user_route(
$who
);
my
$output
= {
prefix
=>
$self
->state_user_full(
$nick
),
command
=>
'INVITE'
,
params
=> [
$who
,
$chan
],
colonify
=> 0 };
if
(
$route_id
eq
'spoofed'
) {
$self
->{ircd}->send_event(
"daemon_invite"
,
$output
->{prefix}, @{
$output
->{params} } );
}
else
{
unless
(
$local
) {
$output
->{prefix} =
$nick
;
push
@{
$output
->{params} },
$args
->[2];
}
$self
->{ircd}->send_output(
$output
,
$route_id
);
}
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_daemon_peer_away {
my
$self
=
shift
;
my
$peer_id
=
shift
||
return
;
my
$nick
=
shift
||
return
;
my
$msg
=
shift
;
my
$server
=
$self
->server_name();
my
$ref
= [ ];
SWITCH: {
my
$record
=
$self
->{state}->{users}->{ u_irc
$nick
};
if
( !
$msg
) {
delete
$record
->{away};
$self
->{ircd}->send_output( {
prefix
=>
$nick
,
command
=>
'AWAY'
,
colonify
=> 0 },
grep
{
$_
ne
$peer_id
}
$self
->_state_connected_peers() );
last
SWITCH;
}
$record
->{away} =
$msg
;
$self
->{ircd}->send_output( {
prefix
=>
$nick
,
command
=>
'AWAY'
,
params
=> [
$msg
],
colonify
=> 0 },
grep
{
$_
ne
$peer_id
}
$self
->_state_connected_peers() );
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_state_create {
my
$self
=
shift
;
$self
->_state_delete();
$self
->{state}->{conns} = { };
$self
->{state}->{users} = { };
$self
->{state}->{peers} = { };
$self
->{state}->{chans} = { };
$self
->{state}->{peers}->{
uc
$self
->server_name() } = {
name
=>
$self
->server_name(),
hops
=> 0,
desc
=>
$self
->{config}->{SERVERDESC} };
$self
->{state}->{stats} = {
maxconns
=> 0,
maxlocal
=> 0,
maxglobal
=> 0,
ops_online
=> 0,
invisible
=> 0,
cmds
=> { },
};
return
1;
}
sub
_state_delete {
my
$self
=
shift
;
delete
$self
->{state};
return
1;
}
sub
_state_update_stats {
my
$self
=
shift
;
my
$server
=
$self
->server_name();
my
$global
=
scalar
keys
%{
$self
->{state}->{users} };
my
$local
=
scalar
keys
%{
$self
->{state}->{peers}->{
uc
$server
}->{users} };
$self
->{state}->{stats}->{maxglobal} =
$global
if
$global
>
$self
->{state}->{stats}->{maxglobal};
$self
->{state}->{stats}->{maxlocal} =
$local
if
$local
>
$self
->{state}->{stats}->{maxlocal};
return
1;
}
sub
_state_conn_stats {
my
$self
=
shift
;
$self
->{state}->{stats}->{conns_cumlative}++;
my
$conns
=
scalar
keys
%{
$self
->{state}->{conns} };
$self
->{state}->{stats}->{maxconns} =
$conns
if
$conns
>
$self
->{state}->{stats}->{maxconns};
return
1;
}
sub
_state_cmd_stat {
my
$self
=
shift
;
my
$cmd
=
shift
||
return
;
my
$line
=
shift
||
return
;
my
$remote
=
shift
;
my
$record
=
$self
->{state}->{stats}->{cmds}->{
$cmd
} || {
remote
=> 0,
local
=> 0,
bytes
=> 0 };
$record
->{
local
}++
unless
$remote
;
$record
->{remote}++
if
$remote
;
$record
->{bytes} +=
length
$line
;
$self
->{state}->{stats}->{cmds}->{
$cmd
} =
$record
;
return
1;
}
sub
_state_find_user_host {
my
$self
=
shift
;
my
$luser
=
shift
||
return
;
my
$host
=
shift
||
'*'
;
my
$local
=
$self
->{state}->{peers}->{
uc
$self
->server_name() }->{users};
my
@conns
;
foreach
my
$user
(
values
%{
$local
} ) {
push
@conns
, [
$user
->{route_id},
$user
->{nick} ]
if
matches_mask(
$host
,
$user
->{auth}->{hostname} ) and matches_mask(
$luser
,
$user
->{auth}->{ident} );
}
return
@conns
;
}
sub
_state_local_users_match_rkline {
my
$self
=
shift
;
my
$luser
=
shift
||
return
;
my
$host
=
shift
||
return
;
my
$local
=
$self
->{state}->{peers}->{
uc
$self
->server_name() }->{users};
my
@conns
;
foreach
my
$user
(
values
%{
$local
} ) {
next
if
$user
->{route_id} eq
'spoofed'
;
next
if
$user
->{umode} and
$user
->{umode} =~ /o/;
eval
{
push
@conns
,
$user
->{route_id}
if
(
$user
->{
socket
}->[0] =~ /
$host
/ or
$user
->{auth}->{hostname} =~ /
$host
/ ) and
$user
->{auth}->{ident} =~ /
$luser
/;
};
}
return
@conns
;
}
sub
_state_local_users_match_gline {
my
$self
=
shift
;
my
$luser
=
shift
||
return
;
my
$host
=
shift
||
return
;
my
$local
=
$self
->{state}->{peers}->{
uc
$self
->server_name() }->{users};
my
@conns
;
if
(
my
$netmask
= Net::Netmask->new2(
$host
) ) {
foreach
my
$user
(
values
%{
$local
} ) {
next
if
$user
->{route_id} eq
'spoofed'
;
next
if
$user
->{umode} and
$user
->{umode} =~ /o/;
push
@conns
,
$user
->{route_id}
if
$netmask
->match(
$user
->{
socket
}->[0]) and matches_mask(
$luser
,
$user
->{auth}->{ident} );
}
}
else
{
foreach
my
$user
(
values
%{
$local
} ) {
next
if
$user
->{route_id} eq
'spoofed'
;
next
if
$user
->{umode} and
$user
->{umode} =~ /o/;
push
@conns
,
$user
->{route_id}
if
( matches_mask(
$host
,
$user
->{
socket
}->[0] ) or matches_mask(
$host
,
$user
->{auth}->{hostname} ) ) and matches_mask(
$luser
,
$user
->{auth}->{ident} );
}
}
return
@conns
;
}
sub
_state_user_matches_rkline {
my
$self
=
shift
;
my
$conn_id
=
shift
||
return
;
my
$record
=
$self
->{state}->{conns}->{
$conn_id
};
my
$host
=
$record
->{auth}->{hostname} ||
$record
->{
socket
}->[0];
my
$user
=
$record
->{auth}->{ident} ||
"~"
.
$record
->{user};
my
$ip
=
$record
->{
socket
}->[0];
foreach
my
$gline
( @{
$self
->{state}->{rklines} } ) {
eval
{
return
1
if
(
$host
=~ /
$gline
->{host}/ or
$ip
=~ /
$gline
->{host}/ ) and
$user
=~ /
$gline
->{user}/;
};
}
return
0;
}
sub
_state_user_matches_kline {
my
$self
=
shift
;
my
$conn_id
=
shift
||
return
;
my
$record
=
$self
->{state}->{conns}->{
$conn_id
};
my
$host
=
$record
->{auth}->{hostname} ||
$record
->{
socket
}->[0];
my
$user
=
$record
->{auth}->{ident} ||
"~"
.
$record
->{user};
my
$ip
=
$record
->{
socket
}->[0];
foreach
my
$gline
( @{
$self
->{state}->{klines} } ) {
if
(
my
$netmask
= Net::Netmask->new2(
$gline
->{host}) ) {
return
1
if
$netmask
->match(
$ip
) and matches_mask(
$gline
->{user},
$user
);
}
else
{
return
1
if
( matches_mask(
$gline
->{host},
$host
) or matches_mask(
$gline
->{host},
$ip
) ) and matches_mask(
$gline
->{user},
$user
);
}
}
return
0;
}
sub
_state_user_matches_gline {
my
$self
=
shift
;
my
$conn_id
=
shift
||
return
;
my
$record
=
$self
->{state}->{conns}->{
$conn_id
};
my
$host
=
$record
->{auth}->{hostname} ||
$record
->{
socket
}->[0];
my
$user
=
$record
->{auth}->{ident} ||
"~"
.
$record
->{user};
my
$ip
=
$record
->{
socket
}->[0];
foreach
my
$gline
( @{
$self
->{state}->{glines} } ) {
if
(
my
$netmask
= Net::Netmask->new2(
$gline
->{host}) ) {
return
1
if
$netmask
->match(
$ip
) and matches_mask(
$gline
->{user},
$user
);
}
else
{
return
1
if
( matches_mask(
$gline
->{host},
$host
) or matches_mask(
$gline
->{host},
$ip
) ) and matches_mask(
$gline
->{user},
$user
);
}
}
return
0;
}
sub
_state_auth_client_conn {
my
$self
=
shift
;
my
$conn_id
=
shift
||
return
;
return
1
unless
$self
->{config}->{auth} and
scalar
@{
$self
->{config}->{auth} };
my
$record
=
$self
->{state}->{conns}->{
$conn_id
};
my
$host
=
$record
->{auth}->{hostname} ||
$record
->{
socket
}->[0];
my
$user
=
$record
->{auth}->{ident} ||
"~"
.
$record
->{user};
my
$uh
=
join
'@'
,
$user
,
$host
;
my
$ui
=
join
'@'
,
$user
,
$record
->{
socket
}->[0];
foreach
my
$auth
( @{
$self
->{config}->{auth} } ) {
if
( matches_mask(
$auth
->{mask},
$uh
) or matches_mask(
$auth
->{mask},
$ui
) ) {
return
0
if
$auth
->{password} and ( !
$record
->{pass} or
$auth
->{password} ne
$record
->{pass} );
$record
->{auth}->{hostname} =
$auth
->{spoof}
if
$auth
->{spoof};
$record
->{auth}->{ident} =
$record
->{user}
if
!
$record
->{auth}->{ident} and
$auth
->{no_tilde};
return
1;
}
}
return
0;
}
sub
_state_auth_peer_conn {
my
$self
=
shift
;
my
(
$conn_id
,
$name
,
$pass
) =
@_
;
return
unless
$conn_id
and
$self
->_connection_exists(
$conn_id
);
return
unless
$name
and
$pass
;
my
$peers
=
$self
->{config}->{peers};
return
0
unless
$peers
->{
uc
$name
} or
$peers
->{
uc
$name
}->{pass} ne
$pass
;
my
$conn
=
$self
->{state}->{conns}->{
$conn_id
};
return
1
if
!
$peers
->{
uc
$name
}->{ipmask} and
$conn
->{
socket
}->[0] =~ /^127\./;
return
0
unless
$peers
->{
uc
$name
}->{ipmask};
my
$client_ip
=
$conn
->{
socket
}->[0];
if
(
ref
$peers
->{
uc
$name
}->{ipmask} eq
'ARRAY'
) {
foreach
my
$block
(
grep
{
$_
->isa(
'Net::Netmask'
) } @{
$peers
->{
uc
$name
}->{ipmask} } ) {
return
1
if
$block
->match(
$client_ip
);
}
}
return
1
if
matches_mask(
$peers
->{
uc
$name
}->{ipmask},
$client_ip
);
return
0;
}
sub
_state_send_credentials {
my
$self
=
shift
;
my
$conn_id
=
shift
||
return
;
my
$name
=
shift
||
return
;
return
unless
$self
->_connection_exists(
$conn_id
);
return
unless
$self
->{config}->{peers}->{
uc
$name
};
my
$peer
=
$self
->{config}->{peers}->{
uc
$name
};
$self
->{ircd}->send_output( {
command
=>
'PASS'
,
params
=> [
$peer
->{rpass},
'TS'
] },
$conn_id
);
$self
->{ircd}->send_output( {
command
=>
'CAPAB'
,
params
=> [
join
(
' '
, @{
$self
->{config}->{capab} }, (
$peer
->{zip} ?
'ZIP'
: () ) ) ] },
$conn_id
);
my
$rec
=
$self
->{state}->{peers}->{
uc
$self
->server_name() };
$self
->{ircd}->send_output( {
command
=>
'SERVER'
,
params
=> [
$rec
->{name},
$rec
->{hops} + 1,
$rec
->{desc} ] },
$conn_id
);
$self
->{ircd}->send_output( {
command
=>
'SVINFO'
,
params
=> [ 5, 5, 0,
time
() ] },
$conn_id
);
$self
->{state}->{conns}->{
$conn_id
}->{zip} =
$peer
->{zip};
return
1;
}
sub
_state_send_burst {
my
$self
=
shift
;
my
$conn_id
=
shift
||
return
;
return
unless
$self
->_connection_exists(
$conn_id
);
my
$server
=
$self
->server_name();
my
$conn
=
$self
->{state}->{conns}->{
$conn_id
};
my
$burst
=
scalar
grep
{ /^EOB$/i } @{
$conn
->{capab} };
my
$invex
=
scalar
grep
{ /^IE$/i } @{
$conn
->{capab} };
my
$excepts
=
scalar
grep
{ /^EX$/i } @{
$conn
->{capab} };
my
%map
=
qw(bans b excepts e invex I)
;
my
@lists
=
qw(bans)
;
push
@lists
,
'excepts'
if
$excepts
;
push
@lists
,
'invex'
if
$invex
;
$self
->{ircd}->send_output(
$_
,
$conn_id
)
for
$self
->_state_server_burst(
$server
,
$conn
->{name} );
foreach
my
$nick
(
keys
%{
$self
->{state}->{users} } ) {
my
$record
=
$self
->{state}->{users}->{
$nick
};
next
if
$record
->{route_id} eq
$conn_id
;
my
$umode_fixed
=
$record
->{umode};
$umode_fixed
=~ s/[^aiow]//g;
my
$arrayref
= [
$record
->{nick},
$record
->{hops} + 1,
$record
->{ts}, (
'+'
.
$umode_fixed
),
$record
->{auth}->{ident},
$record
->{auth}->{hostname},
$record
->{server},
$record
->{ircname} ];
$self
->{ircd}->send_output( {
command
=>
'NICK'
,
params
=>
$arrayref
},
$conn_id
);
}
foreach
my
$chan
(
keys
%{
$self
->{state}->{chans} } ) {
next
if
$chan
=~ /^\&/;
my
$chanrec
=
$self
->{state}->{chans}->{
$chan
};
my
@nicks
=
map
{
$_
->[1] }
sort
{
$a
->[0] cmp
$b
->[0] }
map
{
my
$w
=
$_
;
$w
=~
tr
/@%+/ABC/; [
$w
,
$_
]; }
$self
->state_chan_list_prefixed(
$chan
);
my
$arrayref2
= [
$chanrec
->{ts},
$chanrec
->{name},
'+'
.
$chanrec
->{mode}, (
$chanrec
->{ckey} || () ), (
$chanrec
->{climit} || () ),
join
' '
,
@nicks
];
$self
->{ircd}->send_output( {
prefix
=>
$server
,
command
=>
'SJOIN'
,
params
=>
$arrayref2
},
$conn_id
);
my
@output_modes
;
OUTER:
foreach
my
$type
(
@lists
) {
my
$length
=
length
(
$server
) + 4 +
length
(
$chan
) + 4;
my
@buffer
= (
''
,
''
);
INNER:
foreach
my
$thing
(
keys
%{
$chanrec
->{
$type
} } ) {
$thing
=
$chanrec
->{
$type
}->{
$thing
}->[0];
if
(
length
(
join
' '
,
@buffer
,
$thing
) +
$length
+ 1 > 510 ) {
$buffer
[0] =
'+'
.
$buffer
[0];
push
@output_modes
, {
prefix
=>
$server
,
command
=>
'MODE'
,
params
=> [
$chanrec
->{name},
$buffer
[0],
split
/\s+/,
$buffer
[1] ],
colonify
=> 0 };
$buffer
[0] =
'+'
.
$map
{
$type
};
$buffer
[1] =
$thing
;
next
INNER;
}
if
(
$buffer
[1] ) {
$buffer
[0] .=
$map
{
$type
};
$buffer
[1] =
join
' '
,
$buffer
[1],
$thing
;
}
else
{
$buffer
[0] =
'+'
.
$map
{
$type
};
$buffer
[1] =
$thing
;
}
}
push
@output_modes
, {
prefix
=>
$server
,
command
=>
'MODE'
,
params
=> [
$chanrec
->{name},
$buffer
[0],
split
/\s+/,
$buffer
[1] ],
colonify
=> 0 }
if
$buffer
[0];
}
$self
->{ircd}->send_output(
$_
,
$conn_id
)
for
@output_modes
;
}
$self
->{ircd}->send_output( {
prefix
=>
$server
,
command
=>
'EOB'
},
$conn_id
)
if
$burst
;
return
1;
}
sub
_state_server_burst {
my
$self
=
shift
;
my
$peer
=
shift
||
return
;
my
$targ
=
shift
||
return
;
return
unless
$self
->state_peer_exists(
$peer
) and
$self
->state_peer_exists(
$targ
);
my
$ref
= [ ];
$peer
=
$self
->_state_peer_name(
$peer
);
my
$upeer
=
uc
$peer
;
my
$utarg
=
uc
$targ
;
foreach
my
$server
(
keys
%{
$self
->{state}->{peers}->{
$upeer
}->{peers} } ) {
next
if
$server
eq
$utarg
;
my
$rec
=
$self
->{state}->{peers}->{
$server
};
push
@{
$ref
}, {
prefix
=>
$peer
,
command
=>
'SERVER'
,
params
=> [
$rec
->{name},
$rec
->{hops} + 1,
$rec
->{desc} ] };
push
@{
$ref
},
$_
for
$self
->_state_server_burst(
$rec
->{name},
$targ
);
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_state_server_links {
my
$self
=
shift
;
my
$peer
=
shift
||
return
;
my
$orig
=
shift
||
return
;
my
$nick
=
shift
||
return
;
return
unless
$self
->state_peer_exists(
$peer
);
my
$ref
= [ ];
$peer
=
$self
->_state_peer_name(
$peer
);
my
$upeer
=
uc
$peer
;
foreach
my
$server
(
keys
%{
$self
->{state}->{peers}->{
$upeer
}->{peers} } ) {
my
$rec
=
$self
->{state}->{peers}->{
$server
};
push
@{
$ref
},
$_
for
$self
->_state_server_links(
$rec
->{name},
$orig
,
$nick
);
push
@{
$ref
}, {
prefix
=>
$orig
,
command
=>
'364'
,
params
=> [
$nick
,
$rec
->{name},
$peer
,
join
(
' '
,
$rec
->{hops},
$rec
->{desc} ) ] };
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_state_peer_for_peer {
my
$self
=
shift
;
my
$peer
=
shift
||
return
;
return
unless
$self
->state_peer_exists(
$peer
);
$peer
=
uc
$peer
;
return
$self
->{state}->{peers}->{
$peer
}->{peer};
}
sub
_state_server_squit {
my
$self
=
shift
;
my
$peer
=
shift
||
return
;
return
unless
$self
->state_peer_exists(
$peer
);
my
$ref
= [ ];
my
$upeer
=
uc
$peer
;
push
@{
$ref
},
$_
for
keys
%{
$self
->{state}->{peers}->{
$upeer
}->{users} };
foreach
my
$server
(
keys
%{
$self
->{state}->{peers}->{
$upeer
}->{peers} } ) {
push
@{
$ref
},
$_
for
$self
->_state_server_squit(
$server
);
}
delete
$self
->{state}->{peers}->{
$upeer
};
delete
$self
->{state}->{peers}->{
uc
$self
->server_name() }->{peers}->{
$upeer
};
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
_state_register_peer {
my
$self
=
shift
;
my
$conn_id
=
shift
||
return
;
return
unless
$self
->_connection_exists(
$conn_id
);
my
$server
=
$self
->server_name();
my
$record
=
$self
->{state}->{conns}->{
$conn_id
};
$self
->_state_send_credentials(
$conn_id
,
$record
->{name} )
unless
$record
->{cntr};
$record
->{burst} =
$record
->{registered} = 1;
$record
->{type} =
'p'
;
$record
->{route_id} =
$conn_id
;
$record
->{peer} =
$server
;
$record
->{users} = { };
$record
->{peers} = { };
$self
->{state}->{peers}->{
uc
$server
}->{peers}->{
uc
$record
->{name} } =
$record
;
$self
->{state}->{peers}->{
uc
$record
->{name} } =
$record
;
$self
->{ircd}->antiflood(
$conn_id
=> 0 );
$self
->{ircd}->send_output( {
prefix
=>
$server
,
command
=>
'SERVER'
,
params
=> [
$record
->{name},
$record
->{hops} + 1,
$record
->{desc} ] },
grep
{
$_
ne
$conn_id
}
$self
->_state_connected_peers() );
$self
->{ircd}->send_event(
"daemon_server"
,
$record
->{name},
$server
,
$record
->{hops},
$record
->{desc} );
return
1;
}
sub
_state_register_client {
my
$self
=
shift
;
my
$conn_id
=
shift
||
return
;
return
unless
$self
->_connection_exists(
$conn_id
);
my
$record
=
$self
->{state}->{conns}->{
$conn_id
};
$record
->{server} =
$self
->server_name();
$record
->{hops} = 0;
$record
->{route_id} =
$conn_id
;
$record
->{umode} =
''
;
$record
->{_ignore_i_umode} = 1;
$record
->{ts} =
$record
->{idle_time} =
$record
->{conn_time} =
time
();
$record
->{auth}->{ident} =
'~'
.
$record
->{user}
unless
$record
->{auth}->{ident};
$record
->{auth}->{hostname} =
$self
->server_name()
if
$record
->{auth}->{hostname} eq
'localhost'
or ( !
$record
->{auth}->{hostname} and
$record
->{
socket
}->[0] =~ /^127\./ );
$record
->{auth}->{hostname} =
$record
->{
socket
}->[0]
unless
$record
->{auth}->{hostname};
$self
->{state}->{users}->{ u_irc
$record
->{nick} } =
$record
;
$self
->{state}->{peers}->{
uc
$record
->{server} }->{users}->{ u_irc
$record
->{nick} } =
$record
;
my
$arrayref
= [
$record
->{nick},
$record
->{hops} + 1,
$record
->{ts},
'+i'
,
$record
->{auth}->{ident},
$record
->{auth}->{hostname},
$record
->{server},
$record
->{ircname} ];
delete
$self
->{state}->{pending}->{ u_irc
$record
->{nick} };
$self
->{ircd}->send_output( {
command
=>
'NICK'
,
params
=>
$arrayref
},
$self
->_state_connected_peers() );
$self
->{ircd}->send_event(
"daemon_nick"
, @{
$arrayref
} );
$self
->_state_update_stats();
return
1;
}
sub
state_nicks {
my
$self
=
shift
;
return
map
{
$self
->{state}->{users}->{
$_
}->{nick} }
keys
%{
$self
->{state}->{users} };
}
sub
state_nick_exists {
my
$self
=
shift
;
my
$nick
=
shift
||
return
1;
$nick
= u_irc
$nick
;
return
0
unless
defined
$self
->{state}->{users}->{
$nick
} or
defined
$self
->{state}->{pending}->{
$nick
};
return
1;
}
sub
state_chans {
my
$self
=
shift
;
return
map
{
$self
->{state}->{chans}->{
$_
}->{name} }
keys
%{
$self
->{state}->{chans} };
}
sub
state_chan_exists {
my
$self
=
shift
;
my
$chan
=
shift
||
return
;
return
0
unless
defined
$self
->{state}->{chans}->{ u_irc
$chan
};
return
1;
}
sub
state_peers {
my
$self
=
shift
;
return
map
{
$self
->{state}->{peers}->{
$_
}->{name} }
keys
%{
$self
->{state}->{peers} };
}
sub
state_peer_exists {
my
$self
=
shift
;
my
$peer
=
shift
||
return
;
return
0
unless
defined
$self
->{state}->{peers}->{
uc
$peer
};
return
1;
}
sub
_state_peer_name {
my
$self
=
shift
;
my
$peer
=
shift
||
return
;
return
unless
$self
->state_peer_exists(
$peer
);
return
$self
->{state}->{peers}->{
uc
$peer
}->{name};
}
sub
_state_peer_desc {
my
$self
=
shift
;
my
$peer
=
shift
||
return
;
return
unless
$self
->state_peer_exists(
$peer
);
return
$self
->{state}->{peers}->{
uc
$peer
}->{desc};
}
sub
_state_peer_capab {
my
$self
=
shift
;
my
$conn_id
=
shift
||
return
;
my
$capab
=
shift
||
return
;
$capab
=
uc
$capab
;
return
unless
$self
->_connection_is_peer(
$conn_id
);
my
$conn
=
$self
->{state}->{conns}->{
$conn_id
};
return
scalar
grep
{
$_
eq
$capab
} @{
$conn
->{capab} };
}
sub
state_user_full {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
return
unless
$self
->state_nick_exists(
$nick
);
my
$record
=
$self
->{state}->{users}->{ u_irc
$nick
};
return
$record
->{nick} .
'!'
.
$record
->{auth}->{ident} .
'@'
.
$record
->{auth}->{hostname};
}
sub
state_user_nick {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
return
unless
$self
->state_nick_exists(
$nick
);
return
$self
->{state}->{users}->{ u_irc
$nick
}->{nick};
}
sub
_state_user_ip {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
return
unless
$self
->state_nick_exists(
$nick
) and
$self
->_state_is_local_user(
$nick
);
my
$record
=
$self
->{state}->{users}->{ u_irc
$nick
};
return
$record
->{
socket
}->[0];
}
sub
_state_user_away {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
return
unless
$self
->state_nick_exists(
$nick
);
return
1
if
defined
$self
->{state}->{users}->{ u_irc
$nick
}->{away};
return
0;
}
sub
_state_user_away_msg {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
return
unless
$self
->state_nick_exists(
$nick
);
return
$self
->{state}->{users}->{ u_irc
$nick
}->{away};
}
sub
state_user_umode {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
return
unless
$self
->state_nick_exists(
$nick
);
return
$self
->{state}->{users}->{ u_irc
$nick
}->{umode};
}
sub
state_user_is_operator {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
return
unless
$self
->state_nick_exists(
$nick
);
return
0
unless
$self
->{state}->{users}->{ u_irc
$nick
}->{umode} =~ /o/;
return
1;
}
sub
_state_user_is_deaf {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
return
unless
$self
->state_nick_exists(
$nick
);
return
0
unless
$self
->{state}->{users}->{ u_irc
$nick
}->{umode} =~ /D/;
return
1;
}
sub
state_user_chans {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
return
unless
$self
->state_nick_exists(
$nick
);
my
$record
=
$self
->{state}->{users}->{ u_irc(
$nick
) };
return
map
{
$self
->{state}->{chans}->{
$_
}->{name} }
keys
%{
$record
->{chans} };
}
sub
_state_user_route {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
return
unless
$self
->state_nick_exists(
$nick
);
my
$record
=
$self
->{state}->{users}->{ u_irc(
$nick
) };
return
$record
->{route_id};
}
sub
state_user_server {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
return
unless
$self
->state_nick_exists(
$nick
);
my
$record
=
$self
->{state}->{users}->{ u_irc(
$nick
) };
return
$record
->{server};
}
sub
_state_peer_route {
my
$self
=
shift
;
my
$peer
=
shift
||
return
;
return
unless
$self
->state_peer_exists(
$peer
);
my
$record
=
$self
->{state}->{peers}->{
uc
$peer
};
return
$record
->{route_id};
}
sub
_state_connected_peers {
my
$self
=
shift
;
my
$server
=
uc
$self
->server_name();
return
unless
scalar
keys
%{
$self
->{state}->{peers} } > 1;
my
$record
=
$self
->{state}->{peers}->{
$server
};
return
map
{
$record
->{peers}->{
$_
}->{route_id} }
keys
%{
$record
->{peers} };
}
sub
state_chan_list {
my
$self
=
shift
;
my
$chan
=
shift
||
return
;
my
$status_msg
=
shift
||
''
;
return
unless
$self
->state_chan_exists(
$chan
);
$status_msg
=~ s/[^@%+]//g;
my
$record
=
$self
->{state}->{chans}->{ u_irc
$chan
};
return
map
{
$self
->{state}->{users}->{
$_
}->{nick} }
keys
%{
$record
->{users} }
unless
$status_msg
;
my
%map
=
qw(o 3 h 2 v 1)
;
my
%sym
=
qw(@ 3 % 2 + 1)
;
my
$lowest
= (
sort
map
{
$sym
{
$_
} }
split
//,
$status_msg
)[0];
return
map
{
$self
->{state}->{users}->{
$_
}->{nick} }
grep
{
$record
->{users}->{
$_
}
and (
reverse
sort
map
{
$map
{
$_
} }
split
//,
$record
->{users}->{
$_
} )[0] >=
$lowest
}
keys
%{
$record
->{users} };
}
sub
state_chan_list_prefixed {
my
$self
=
shift
;
my
$chan
=
shift
||
return
;
return
unless
$self
->state_chan_exists(
$chan
);
my
$record
=
$self
->{state}->{chans}->{ u_irc(
$chan
) };
return
map
{
my
$n
=
$self
->{state}->{users}->{
$_
}->{nick};
my
$m
=
$record
->{users}->{
$_
};
my
$p
=
''
;
$p
=
'@'
if
$m
=~ /o/;
$p
=
'%'
if
$m
=~ /h/ and !
$p
;
$p
=
'+'
if
$m
=~ /v/ and !
$p
;
$p
.
$n
;
}
keys
%{
$record
->{users} };
}
sub
_state_chan_timestamp {
my
$self
=
shift
;
my
$chan
=
shift
||
return
;
return
unless
$self
->state_chan_exists(
$chan
);
return
$self
->{state}->{chans}->{ u_irc
$chan
}->{ts};
}
sub
state_chan_topic {
my
$self
=
shift
;
my
$chan
=
shift
||
return
;
return
unless
$self
->state_chan_exists(
$chan
);
my
$record
=
$self
->{state}->{chans}->{ u_irc(
$chan
) };
return
unless
$record
->{topic};
return
[ @{
$record
->{topic} } ];
}
sub
_state_is_local_user {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
return
unless
$self
->state_nick_exists(
$nick
);
my
$record
=
$self
->{state}->{peers}->{
uc
$self
->server_name() };
return
1
if
defined
$record
->{users}->{ u_irc
$nick
};
return
0;
}
sub
_state_chan_name {
my
$self
=
shift
;
my
$chan
=
shift
||
return
;
return
unless
$self
->state_chan_exists(
$chan
);
return
$self
->{state}->{chans}->{ u_irc
$chan
}->{name};
}
sub
state_chan_mode_set {
my
$self
=
shift
;
my
$chan
=
shift
||
return
;
my
$mode
=
shift
||
return
;
return
unless
$self
->state_chan_exists(
$chan
);
$mode
=~ s/[^a-zA-Z]+//g;
$mode
= (
split
//,
$mode
)[0]
if
length
$mode
> 1;
my
$record
=
$self
->{state}->{chans}->{ u_irc
$chan
};
return
1
if
$record
->{mode} =~ /
$mode
/;
return
0;
}
sub
_state_user_invited {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
my
$chan
=
shift
||
return
;
return
unless
$self
->state_nick_exists(
$nick
);
return
0
unless
$self
->state_chan_exists(
$chan
);
my
$nickrec
=
$self
->{state}->{users}->{ u_irc
$nick
};
return
1
if
$nickrec
->{invites}->{ u_irc
$chan
};
return
1
if
$self
->_state_user_matches_list(
$nick
,
$chan
,
'invex'
);
return
0;
}
sub
_state_user_banned {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
my
$chan
=
shift
||
return
;
return
0
unless
$self
->_state_user_matches_list(
$nick
,
$chan
,
'bans'
);
return
1
unless
$self
->_state_user_matches_list(
$nick
,
$chan
,
'excepts'
);
return
0;
}
sub
_state_user_matches_list {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
my
$chan
=
shift
||
return
;
my
$list
=
shift
||
'bans'
;
return
unless
$self
->state_nick_exists(
$nick
);
return
0
unless
$self
->state_chan_exists(
$chan
);
my
$full
=
$self
->state_user_full(
$nick
);
my
$record
=
$self
->{state}->{chans}->{ u_irc
$chan
};
foreach
my
$mask
(
keys
%{
$record
->{
$list
} } ) {
return
1
if
matches_mask(
$mask
,
$full
);
}
return
0;
}
sub
state_is_chan_member {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
my
$chan
=
shift
||
return
;
return
unless
$self
->state_nick_exists(
$nick
);
return
0
unless
$self
->state_chan_exists(
$chan
);
my
$record
=
$self
->{state}->{users}->{ u_irc
$nick
};
return
1
if
defined
(
$record
->{chans}->{ u_irc
$chan
} );
return
0;
}
sub
state_user_chan_mode {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
my
$chan
=
shift
||
return
;
return
unless
$self
->state_is_chan_member(
$nick
,
$chan
);
return
$self
->{state}->{users}->{ u_irc
$nick
}->{chans}->{ u_irc
$chan
};
}
sub
state_is_chan_op {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
my
$chan
=
shift
||
return
;
return
unless
$self
->state_is_chan_member(
$nick
,
$chan
);
my
$record
=
$self
->{state}->{users}->{ u_irc
$nick
};
return
1
if
$record
->{chans}->{ u_irc
$chan
} =~ /o/;
return
1
if
$self
->{config}->{OPHACKS} and
$record
->{umode} =~ /o/;
return
0;
}
sub
state_is_chan_hop {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
my
$chan
=
shift
||
return
;
return
unless
$self
->state_is_chan_member(
$nick
,
$chan
);
my
$record
=
$self
->{state}->{users}->{ u_irc
$nick
};
return
1
if
$record
->{chans}->{ u_irc
$chan
} =~ /h/;
return
0;
}
sub
state_has_chan_voice {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
my
$chan
=
shift
||
return
;
return
unless
$self
->state_is_chan_member(
$nick
,
$chan
);
my
$record
=
$self
->{state}->{users}->{ u_irc
$nick
};
return
1
if
$record
->{chans}->{ u_irc
$chan
} =~ /v/;
return
0;
}
sub
_state_o_line {
my
$self
=
shift
;
my
$nick
=
shift
||
return
;
my
(
$user
,
$pass
) =
@_
;
return
unless
$self
->state_nick_exists(
$nick
);
return
unless
$user
and
$pass
;
my
$ops
=
$self
->{config}->{ops};
return
unless
$ops
->{
$user
};
return
-1
unless
chkpasswd (
$pass
,
$ops
->{
$user
}->{password} );
my
$client_ip
=
$self
->_state_user_ip(
$nick
);
return
unless
$client_ip
;
return
1
if
( !
$ops
->{
$user
}->{ipmask} and (
$client_ip
and
$client_ip
=~ /^127\./ ) );
return
0
unless
$ops
->{
$user
}->{ipmask};
if
(
ref
$ops
->{
$user
}->{ipmask} eq
'ARRAY'
) {
foreach
my
$block
(
grep
{
$_
->isa(
'Net::Netmask'
) } @{
$ops
->{
$user
}->{ipmask} } ) {
return
1
if
$block
->match(
$client_ip
);
}
}
return
1
if
matches_mask(
$ops
->{
$user
}->{ipmask},
$client_ip
);
return
0;
}
sub
_state_users_share_chan {
my
$self
=
shift
;
my
$nick1
=
shift
||
return
;
my
$nick2
=
shift
||
return
;
return
unless
$self
->state_nick_exists(
$nick1
) and
$self
->state_nick_exists(
$nick2
);
my
$rec1
=
$self
->{state}->{users}->{ u_irc
$nick1
};
my
$rec2
=
$self
->{state}->{users}->{ u_irc
$nick2
};
foreach
my
$chan
(
keys
%{
$rec1
->{chans} } ) {
return
1
if
$rec2
->{chans}->{
$chan
};
}
return
0;
}
sub
_state_parse_msg_targets {
my
$self
=
shift
;
my
$targets
=
shift
||
return
;
my
%results
;
foreach
my
$target
(
split
/,/,
$targets
) {
if
(
$target
=~ /^(\x23|\x26)/ ) {
$results
{
$target
} = [
'channel'
];
next
;
}
if
(
$target
=~ /^([\x40\x25\x2B]+)([\x23\x26].+)$/ ) {
$results
{
$target
} = [
'channel_ext'
, $1, $2 ];
next
;
}
if
(
$target
=~ /^\x24{2}(.+)$/ ) {
$results
{
$target
} = [
'servermask'
, $1 ];
next
;
}
if
(
$target
=~ /^\x24\x23(.+)$/ ) {
$results
{
$target
} = [
'hostmask'
, $1 ];
next
;
}
if
(
$target
=~ /\x40/ ) {
my
(
$nick
,
$server
) =
split
/\x40/,
$target
, 2;
my
$host
;
(
$nick
,
$host
) =
split
( /\x25/,
$nick
, 2 )
if
$nick
=~ /\x25/;
$results
{
$target
} = [
'nick_ext'
,
$nick
,
$server
,
$host
];
next
;
}
$results
{
$target
} = [
'nick'
];
}
return
\
%results
;
}
sub
server_name {
return
$_
[0]->server_config(
'ServerName'
);
}
sub
server_version {
return
$_
[0]->server_config(
'Version'
);
}
sub
server_created {
return
strftime(
"This server was created %a %b %d %Y at %H:%M:%S %Z"
,
localtime
(
$_
[0]->server_config(
'created'
)));
}
sub
_client_nickname {
my
$self
=
shift
;
my
$wheel_id
=
$_
[0] ||
return
undef
;
return
'*'
unless
$self
->{state}->{conns}->{
$wheel_id
}->{nick};
return
$self
->{state}->{conns}->{
$wheel_id
}->{nick};
}
sub
_client_ip {
my
$self
=
shift
;
my
$wheel_id
=
shift
||
return
''
;
return
$self
->{state}->{conns}->{
$wheel_id
}->{
socket
}->[0];
}
sub
server_config {
my
$self
=
shift
;
my
$value
=
shift
||
return
;
return
$self
->{config}->{
uc
$value
};
}
sub
configure {
my
$self
=
shift
;
my
$options
;
if
(
ref
$_
[0] eq
'HASH'
) {
$options
=
$_
[0];
}
else
{
$options
= {
@_
};
}
$self
->{config}->{
uc
$_
} =
$options
->{
$_
}
for
keys
%{
$options
};
$self
->{config}->{CREATED} =
time
();
$self
->{config}->{CASEMAPPING} =
'rfc1459'
;
$self
->{config}->{SERVERNAME} =
'poco.server.irc'
unless
$self
->{config}->{SERVERNAME};
$self
->{config}->{SERVERNAME} =~ s/[^a-zA-Z0-9\-.]//g;
$self
->{config}->{SERVERNAME} .=
'.'
unless
$self
->{config}->{SERVERNAME} =~ /\./;
$self
->{config}->{SERVERDESC} =
'Poco? POCO? POCO!'
unless
$self
->{config}->{SERVERDESC};
$self
->{config}->{VERSION} =
ref
(
$self
) .
'-'
.
$VERSION
unless
$self
->{config}->{VERSION};
$self
->{config}->{NETWORK} =
'poconet'
unless
$self
->{config}->{NETWORK};
$self
->{config}->{HOSTLEN} = 63
unless
(
defined
(
$self
->{config}->{HOSTLEN} ) and
$self
->{config}->{HOSTLEN} > 63 );
$self
->{config}->{NICKLEN} = 9
unless
(
defined
(
$self
->{config}->{NICKLEN} ) and
$self
->{config}->{NICKLEN} > 9 );
$self
->{config}->{KICKLEN} = 120
unless
(
defined
(
$self
->{config}->{KICKLEN} ) and
$self
->{config}->{KICKLEN} < 120 );
$self
->{config}->{USERLEN} = 10
unless
(
defined
(
$self
->{config}->{USERLEN} ) and
$self
->{config}->{USERLEN} > 10 );
$self
->{config}->{REALLEN} = 50
unless
(
defined
(
$self
->{config}->{REALLEN} ) and
$self
->{config}->{REALLEN} > 50 );
$self
->{config}->{TOPICLEN} = 80
unless
(
defined
(
$self
->{config}->{TOPICLEN} ) and
$self
->{config}->{TOPICLEN} > 80 );
$self
->{config}->{AWAYLEN} = 160
unless
(
defined
(
$self
->{config}->{AWAYLEN} ) and
$self
->{config}->{AWAYLEN} < 160 );
$self
->{config}->{CHANNELLEN} = 50
unless
(
defined
(
$self
->{config}->{CHANNELLEN} ) and
$self
->{config}->{CHANNELLEN} > 50 );
$self
->{config}->{PASSWDLEN} = 20
unless
(
defined
(
$self
->{config}->{PASSWDLEN} ) and
$self
->{config}->{PASSWDLEN} > 20 );
$self
->{config}->{KEYLEN} = 23
unless
(
defined
(
$self
->{config}->{KEYLEN} ) and
$self
->{config}->{KEYLEN} > 23 );
$self
->{config}->{MAXCHANNELS} = 15
unless
(
defined
(
$self
->{config}->{MAXCHANNELS} ) and
$self
->{config}->{MAXCHANNELS} > 15 );
$self
->{config}->{MAXACCEPT} = 20
unless
(
defined
(
$self
->{config}->{MAXACCEPT} ) and
$self
->{config}->{MAXACCEPT} > 20 );
$self
->{config}->{MODES} = 4
unless
(
defined
(
$self
->{config}->{MODES} ) and
$self
->{config}->{MODES} > 4 );
$self
->{config}->{MAXTARGETS} = 4
unless
(
defined
(
$self
->{config}->{MAXTARGETS} ) and
$self
->{config}->{MAXTARGETS} > 4 );
$self
->{config}->{MAXBANS} = 25
unless
(
defined
(
$self
->{config}->{MAXBANS} ) and
$self
->{config}->{MAXBANS} > 30 );
$self
->{config}->{MAXBANLENGTH} = 1024
unless
(
defined
(
$self
->{config}->{MAXBANLENGTH} ) and
$self
->{config}->{MAXBANLENGTH} < 1024 );
$self
->{config}->{BANLEN} =
$self
->{config}->{USERLEN} +
$self
->{config}->{NICKLEN} +
$self
->{config}->{HOSTLEN} + 3;
$self
->{config}->{USERHOST_REPLYLEN} =
$self
->{config}->{USERLEN} +
$self
->{config}->{NICKLEN} +
$self
->{config}->{HOSTLEN} + 5;
$self
->{config}->{AUTH} = 1
unless
(
defined
(
$self
->{config}->{AUTH} ) and
$self
->{config}->{AUTH} eq
'0'
);
$self
->{config}->{ANTIFLOOD} = 1
unless
(
defined
(
$self
->{config}->{ANTIFLOOD} ) and
$self
->{config}->{ANTIFLOOD} eq
'0'
);
if
( ( not
defined
(
$self
->{config}->{ADMIN} ) ) or (
ref
$self
->{config}->{ADMIN} ne
'ARRAY'
) or (
scalar
( @{
$self
->{config}->{ADMIN} } ) != 3 ) ) {
$self
->{config}->{ADMIN}->[0] =
'Somewhere, Somewhere, Somewhere'
;
$self
->{config}->{ADMIN}->[1] =
'Some Institution'
;
$self
->{config}->{ADMIN}->[2] =
'someone@somewhere'
;
}
if
( ( not
defined
(
$self
->{config}->{INFO} ) ) or (
ref
$self
->{config}->{INFO} eq
'ARRAY'
) or (
scalar
( @{
$self
->{config}->{INFO} } ) >= 1 ) ) {
$self
->{config}->{INFO}->[0] =
'# POE::Component::Server::IRC'
;
$self
->{config}->{INFO}->[1] =
'#'
;
$self
->{config}->{INFO}->[2] =
'# Author: Chris "BinGOs" Williams'
;
$self
->{config}->{INFO}->[3] =
'#'
;
$self
->{config}->{INFO}->[4] =
'# Filter-IRCD Written by Hachi'
;
$self
->{config}->{INFO}->[5] =
'#'
;
$self
->{config}->{INFO}->[6] =
'# This module may be used, modified, and distributed under the same'
;
$self
->{config}->{INFO}->[7] =
'# terms as Perl itself. Please see the license that came with your Perl'
;
$self
->{config}->{INFO}->[8] =
'# distribution for details.'
;
$self
->{config}->{INFO}->[9] =
'#'
;
}
$self
->{config}->{WHOISACTUALLY} = 1
unless
defined
$self
->{config}->{WHOISACTUALLY} and
$self
->{config}->{WHOISACTUALLY} eq
'0'
;
$self
->{config}->{OPHACKS} = 0
unless
$self
->{config}->{OPHACKS};
$self
->{Error_Codes} = {
401
=> [ 1,
"No such nick/channel"
],
402
=> [ 1,
"No such server"
],
403
=> [ 1,
"No such channel"
],
404
=> [ 1,
"Cannot send to channel"
],
405
=> [ 1,
"You have joined too many channels"
],
406
=> [ 1,
"There was no such nickname"
],
407
=> [ 1,
"Too many targets"
],
408
=> [ 1,
"No such service"
],
409
=> [ 1,
"No origin specified"
],
411
=> [ 0,
"No recipient given (%s)"
],
412
=> [ 0,
"No text to send"
],
413
=> [ 1,
"No toplevel domain specified"
],
414
=> [ 1,
"Wildcard in toplevel domain"
],
415
=> [ 1,
"Bad server/host mask"
],
421
=> [ 1,
"Unknown command"
],
422
=> [ 0,
"MOTD File is missing"
],
423
=> [ 1,
"No administrative info available"
],
424
=> [ 1,
"File error doing % on %"
],
431
=> [ 1,
"No nickname given"
],
432
=> [ 1,
"Erroneous nickname"
],
433
=> [ 1,
"Nickname is already in use"
],
436
=> [ 1,
"Nickname collision KILL from %s\@%s"
],
437
=> [ 1,
"Nick/channel is temporarily unavailable"
],
441
=> [ 1,
"They aren\'t on that channel"
],
442
=> [ 1,
"You\'re not on that channel"
],
443
=> [ 2,
"is already on channel"
],
444
=> [ 1,
"User not logged in"
],
445
=> [ 0,
"SUMMON has been disabled"
],
446
=> [ 0,
"USERS has been disabled"
],
451
=> [ 0,
"You have not registered"
],
461
=> [ 1,
"Not enough parameters"
],
462
=> [ 0,
"Unauthorised command (already registered)"
],
463
=> [ 0,
"Your host isn\'t among the privileged"
],
464
=> [ 0,
"Password mismatch"
],
465
=> [ 0,
"You are banned from this server"
],
466
=> [ 0,
"You will be banned from this server"
],
467
=> [ 1,
"Channel key already set"
],
471
=> [ 1,
"Cannot join channel (+l)"
],
472
=> [ 1,
"is unknown mode char to me for %s"
],
473
=> [ 1,
"Cannot join channel (+i)"
],
474
=> [ 1,
"Cannot join channel (+b)"
],
475
=> [ 1,
"Cannot join channel (+k)"
],
476
=> [ 1,
"Bad Channel Mask"
],
477
=> [ 1,
"Channel doesn\'t support modes"
],
478
=> [ 2,
"Channel list is full"
],
481
=> [ 0,
"Permission Denied- You\'re not an IRC operator"
],
482
=> [ 1,
"You\'re not channel operator"
],
483
=> [ 0,
"You can\'t kill a server!"
],
484
=> [ 0,
"Your connection is restricted!"
],
485
=> [ 0,
"You\'re not the original channel operator"
],
491
=> [ 0,
"No O-lines for your host"
],
501
=> [ 0,
"Unknown MODE flag"
],
502
=> [ 0,
"Cannot change mode for other users"
],
};
$self
->{config}->{isupport} = {
INVEX
=>
undef
,
EXCEPT
=>
undef
,
CALLERID
=>
undef
,
CHANTYPES
=>
'#&'
,
PREFIX
=>
'(ohv)@%+'
,
CHANMODES
=>
'eIb,k,l,imnpst'
,
STATUSMSG
=>
'@%+'
,
DEAF
=>
'D'
,
MAXLIST
=>
'beI:'
.
$self
->{config}->{MAXBANS},
map
{ (
uc
$_
,
$self
->{config}->{
$_
} ) }
qw(MAXCHANNELS MAXTARGETS NICKLEN TOPICLEN KICKLEN CASEMAPPING NETWORK MODES AWAYLEN)
,
};
$self
->{config}->{capab} = [
qw(QS EX CHW IE HOPS UNKLN KLN GLN EOB)
];
return
1;
}
sub
_send_output_to_client {
my
$self
=
shift
;
my
$wheel_id
=
shift
||
return
0;
my
$nick
=
$self
->_client_nickname(
$wheel_id
);
$nick
=
shift
if
$self
->_connection_is_peer(
$wheel_id
);
my
$err
=
shift
||
return
0;
return
unless
$self
->_connection_exists(
$wheel_id
);
SWITCH: {
if
(
ref
$err
eq
'HASH'
) {
$self
->{ircd}->send_output(
$err
,
$wheel_id
);
last
SWITCH;
}
if
(
defined
(
$self
->{Error_Codes}->{
$err
} ) ) {
my
$input
= {
command
=>
$err
,
prefix
=>
$self
->server_name(),
params
=> [
$nick
] };
if
(
$self
->{Error_Codes}->{
$err
}->[0] > 0 ) {
for
(
my
$i
= 1;
$i
<=
$self
->{Error_Codes}->{
$err
}->[0];
$i
++ ) {
push
@{
$input
->{params} },
shift
;
}
}
if
(
$self
->{Error_Codes}->{
$err
}->[1] =~ /%/ ) {
push
( @{
$input
->{params} },
sprintf
(
$self
->{Error_Codes}->{
$err
}->[1],
@_
) );
}
else
{
push
( @{
$input
->{params} },
$self
->{Error_Codes}->{
$err
}->[1] );
}
$self
->{ircd}->send_output(
$input
,
$wheel_id
);
}
}
return
1;
}
sub
_send_output_to_channel {
my
$self
=
shift
;
my
$channel
=
shift
||
return
;
my
$output
=
shift
||
return
;
my
$conn_id
=
shift
||
''
;
return
unless
$self
->state_chan_exists(
$channel
);
my
$ref
= [ ];
my
$peers
= { };
$peers
->{
$_
}++
for
$self
->_state_connected_peers();
delete
$peers
->{
$conn_id
}
if
$conn_id
;
push
@{
$ref
},
$self
->_state_user_route(
$_
)
for
grep
{
$self
->_state_is_local_user(
$_
) }
$self
->state_chan_list(
$channel
);
@{
$ref
} =
grep
{
$_
ne
$conn_id
} @{
$ref
};
if
(
$channel
!~ /^\&/ and
scalar
(
keys
%{
$peers
} ) and
$output
->{command} ne
'JOIN'
) {
my
$full
=
$output
->{prefix};
my
$nick
= (
split
/!/,
$full
)[0];
my
$output2
= { %{
$output
} };
$output2
->{prefix} =
$nick
;
$self
->{ircd}->send_output(
$output2
,
keys
%{
$peers
} );
}
$self
->{ircd}->send_output(
$output
, @{
$ref
} );
$self
->{ircd}->send_event(
"daemon_"
.
lc
$output
->{command},
$output
->{prefix}, @{
$output
->{params} } );
return
1;
}
sub
add_operator {
my
$self
=
shift
;
my
$ref
;
if
(
ref
$_
[0] eq
'HASH'
) {
$ref
=
$_
[0];
}
else
{
$ref
= {
@_
};
}
$ref
->{
lc
$_
} =
delete
$ref
->{
$_
}
for
keys
%{
$ref
};
unless
(
$ref
->{username} and
$ref
->{password} ) {
warn
"Not enough parameters\n"
;
return
;
}
my
$record
=
$self
->{state}->{peers}->{
uc
$self
->server_name() };
my
$user
=
delete
$ref
->{username};
$self
->{config}->{ops}->{
$user
} =
$ref
;
return
1;
}
sub
del_operator {
my
$self
=
shift
;
my
$user
=
shift
||
return
;
return
unless
defined
$self
->{config}->{ops}->{
$user
};
delete
$self
->{config}->{ops}->{
$user
};
}
sub
add_auth {
my
$self
=
shift
;
my
$parms
;
if
(
ref
$_
[0] eq
'HASH'
) {
$parms
=
$_
[0];
}
else
{
$parms
= {
@_
};
}
$parms
->{
lc
$_
} =
delete
$parms
->{
$_
}
for
keys
%{
$parms
};
unless
(
$parms
->{mask} ) {
warn
"Not enough parameters specified\n"
;
return
;
}
push
@{
$self
->{config}->{auth} },
$parms
;
return
1;
}
sub
del_auth {
my
$self
=
shift
;
my
$mask
=
shift
||
return
;
my
$i
= 0;
for
( @{
$self
->{config}->{auth} } ) {
splice
( @{
$self
->{config}->{auth} },
$i
, 1 ),
last
if
$_
->{mask} eq
$mask
;
++
$i
;
}
}
sub
add_peer {
my
$self
=
shift
;
my
$parms
;
if
(
ref
$_
[0] eq
'HASH'
) {
$parms
=
$_
[0];
}
else
{
$parms
= {
@_
};
}
$parms
->{
lc
$_
} =
delete
$parms
->{
$_
}
for
keys
%{
$parms
};
unless
(
$parms
->{name} and
$parms
->{pass} and
$parms
->{rpass} ) {
warn
"Not enough parameters specified\n"
;
return
;
}
$parms
->{type} =
'c'
unless
$parms
->{type} and
lc
(
$parms
->{type} ) eq
'r'
;
$parms
->{type} =
lc
$parms
->{type};
$parms
->{rport} = 6667
if
$parms
->{type} eq
'r'
and !
$parms
->{rport};
foreach
(
qw(sockport sockaddr)
) {
$parms
->{
$_
} =
'*'
unless
$parms
->{
$_
};
}
$parms
->{ipmask} =
$parms
->{raddress}
if
$parms
->{raddress};
$parms
->{zip} = 0
unless
$parms
->{zip};
my
$name
=
$parms
->{name};
$self
->{config}->{peers}->{
uc
$name
} =
$parms
;
$self
->{ircd}->add_connector(
remoteaddress
=>
$parms
->{raddress},
remoteport
=>
$parms
->{rport},
name
=>
$name
)
if
$parms
->{type} eq
'r'
and
$parms
->{auto};
return
1;
}
sub
del_peer {
my
$self
=
shift
;
my
$name
=
shift
||
return
;
return
unless
defined
$self
->{config}->{peers}->{
uc
$name
};
delete
$self
->{config}->{peers}->{
uc
$name
};
}
sub
_terminate_conn_error {
my
$self
=
shift
;
my
$conn_id
=
shift
||
return
;
return
unless
$self
->_connection_exists(
$conn_id
);
my
$msg
=
shift
;
$self
->{ircd}->disconnect(
$conn_id
,
$msg
);
$self
->{ircd}->send_output( {
command
=>
'ERROR'
,
params
=> [
'Closing Link: '
.
$self
->_client_ip(
$conn_id
) .
' ('
.
$msg
.
')'
] },
$conn_id
);
return
1;
}
sub
daemon_server_kill {
my
$self
=
shift
;
my
$server
=
$self
->server_name();
my
$ref
= [ ];
my
$args
= [
@_
];
my
$count
=
scalar
@{
$args
};
SWITCH: {
if
( !
$count
) {
last
SWITCH;
}
if
(
$self
->state_peer_exists(
$args
->[0] ) ) {
last
SWITCH;
}
if
( !
$self
->state_nick_exists(
$args
->[0] ) ) {
last
SWITCH;
}
my
$target
=
$self
->state_user_nick(
$args
->[0] );
my
$comment
=
$args
->[1] ||
'<No reason given>'
;
my
$conn_id
= (
$args
->[2] and
$self
->_connection_exists(
$args
->[2] ) ?
$args
->[2] :
''
);
if
(
$self
->_state_is_local_user(
$target
) ) {
my
$route_id
=
$self
->_state_user_route(
$target
);
$self
->{ircd}->send_output( {
prefix
=>
$server
,
command
=>
'KILL'
,
params
=> [
$target
,
$comment
] },
$route_id
);
$self
->_terminate_conn_error(
$route_id
,
"Killed ($server ($comment))"
);
if
(
$route_id
eq
'spoofed'
) {
$self
->call(
'del_spoofed_nick'
,
$target
,
"Killed ($server ($comment))"
);
}
else
{
$self
->{state}->{conns}->{
$route_id
}->{killed} = 1;
$self
->_terminate_conn_error(
$route_id
,
"Killed ($server ($comment))"
);
}
}
else
{
$self
->{state}->{users}->{ u_irc
$target
}->{killed} = 1;
$self
->{ircd}->send_output( {
prefix
=>
$server
,
command
=>
'KILL'
,
params
=> [
$target
,
"$server ($comment)"
] },
grep
{ !
$conn_id
||
$_
ne
$conn_id
}
$self
->_state_connected_peers() );
$self
->{ircd}->send_output( @{
$self
->_daemon_peer_quit(
$target
,
"Killed ($server ($comment))"
) } );
}
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
daemon_server_mode {
my
$self
=
shift
;
my
$chan
=
shift
;
my
$server
=
$self
->server_name();
my
$ref
= [ ];
my
$args
= [
@_
];
my
$count
=
scalar
@{
$args
};
SWITCH: {
if
( !
$self
->state_chan_exists(
$chan
) ) {
last
SWITCH;
}
my
$record
=
$self
->{state}->{chans}->{ u_irc
$chan
};
$chan
=
$record
->{name};
my
$full
=
$server
;
my
$parsed_mode
= parse_mode_line( @{
$args
} );
while
(
my
$mode
=
shift
( @{
$parsed_mode
->{modes} } ) ) {
my
$arg
;
$arg
=
shift
( @{
$parsed_mode
->{args} } )
if
(
$mode
=~ /^(\+[ohvklbIe]|-[ohvbIe])/ );
if
(
my
(
$flag
,
$char
) =
$mode
=~ /^(\+|-)([ohv])/ ) {
next
unless
$self
->state_is_chan_member(
$arg
,
$chan
);
if
(
$flag
eq
'+'
and
$record
->{users}->{ u_irc
$arg
} !~ /
$char
/ ) {
$arg
= u_irc
$arg
;
next
if
(
$mode
eq
'+h'
and
$record
->{users}->{
$arg
} =~ /o/ );
if
(
$char
eq
'h'
and
$record
->{users}->{
$arg
} =~ /v/ ) {
$record
->{users}->{
$arg
} =~ s/v//g;
}
if
(
$char
eq
'o'
and
$record
->{users}->{
$arg
} =~ /h/ ) {
$record
->{users}->{
$arg
} =~ s/h//g;
}
$record
->{users}->{
$arg
} =
join
(
''
,
sort
split
//,
$record
->{users}->{
$arg
} .
$char
);
$self
->{state}->{users}->{
$arg
}->{chans}->{ u_irc
$chan
} =
$record
->{users}->{
$arg
};
}
if
(
$flag
eq
'-'
and
$record
->{users}->{ u_irc
$arg
} =~ /
$char
/ ) {
$arg
= u_irc
$arg
;
$record
->{users}->{
$arg
} =~ s/
$char
//g;
$self
->{state}->{users}->{
$arg
}->{chans}->{ u_irc
$chan
} =
$record
->{users}->{
$arg
};
}
next
;
}
if
(
$mode
eq
'+l'
and
$arg
=~ /^\d+$/ and
$arg
> 0 ) {
$record
->{mode} =
join
(
''
,
sort
split
//,
$record
->{mode} .
'l'
)
unless
$record
->{mode} =~ /l/;
$record
->{climit} =
$arg
;
next
;
}
if
(
$mode
eq
'-l'
and
$record
->{mode} =~ /l/ ) {
$record
->{mode} =~ s/l//g;
delete
$record
->{climit};
next
;
}
if
(
$mode
eq
'+k'
and
$arg
) {
$record
->{mode} =
join
(
''
,
sort
split
//,
$record
->{mode} .
'k'
)
unless
$record
->{mode} =~ /k/;
$record
->{ckey} =
$arg
;
next
;
}
if
(
$mode
eq
'-k'
and
$record
->{mode} =~ /k/ ) {
$record
->{mode} =~ s/k//g;
delete
$record
->{ckey};
next
;
}
if
(
my
(
$flag
) =
$mode
=~ /(\+|-)b/ ) {
my
$mask
= parse_ban_mask(
$arg
);
my
$umask
= u_irc
$mask
;
if
(
$flag
eq
'+'
and !
$record
->{bans}->{
$umask
} ) {
$record
->{bans}->{
$umask
} = [
$mask
, (
$full
||
$server
),
time
() ];
}
if
(
$flag
eq
'-'
and
$record
->{bans}->{
$umask
} ) {
delete
$record
->{bans}->{
$umask
};
}
next
;
}
if
(
my
(
$flag
) =
$mode
=~ /(\+|-)I/ ) {
my
$mask
= parse_ban_mask(
$arg
);
my
$umask
= u_irc
$mask
;
if
(
$flag
eq
'+'
and !
$record
->{invex}->{
$umask
} ) {
$record
->{invex}->{
$umask
} = [
$mask
, (
$full
||
$server
),
time
() ];
}
if
(
$flag
eq
'-'
and
$record
->{invex}->{
$umask
} ) {
delete
$record
->{invex}->{
$umask
};
}
next
;
}
if
(
my
(
$flag
) =
$mode
=~ /(\+|-)e/ ) {
my
$mask
= parse_ban_mask(
$arg
);
my
$umask
= u_irc
$mask
;
if
(
$flag
eq
'+'
and !
$record
->{excepts}->{
$umask
} ) {
$record
->{excepts}->{
$umask
} = [
$mask
, (
$full
||
$server
),
time
() ];
}
if
(
$flag
eq
'-'
and
$record
->{excepts}->{
$umask
} ) {
delete
$record
->{excepts}->{
$umask
};
}
next
;
}
my
(
$flag
,
$char
) =
split
//,
$mode
;
if
(
$flag
eq
'+'
and
$record
->{mode} !~ /
$char
/ ) {
$record
->{mode} =
join
(
''
,
sort
split
//,
$record
->{mode} .
$char
);
next
;
}
if
(
$flag
eq
'-'
and
$record
->{mode} =~ /
$char
/ ) {
$record
->{mode} =~ s/
$char
//g;
next
;
}
}
unshift
@{
$args
},
$record
->{name};
$self
->{ircd}->send_output( {
prefix
=>
$server
,
command
=>
'MODE'
,
params
=>
$args
,
colonify
=> 0 },
$self
->_state_connected_peers() );
$self
->{ircd}->send_output( {
prefix
=> (
$full
||
$server
),
command
=>
'MODE'
,
params
=>
$args
,
colonify
=> 0 },
map
{
$self
->_state_user_route(
$_
) }
grep
{
$self
->_state_is_local_user(
$_
) }
keys
%{
$record
->{users} } );
$self
->{ircd}->send_event(
"daemon_mode"
,
$server
, @{
$args
} );
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
daemon_server_kick {
my
$self
=
shift
;
my
$server
=
$self
->server_name();
my
$ref
= [ ];
my
$args
= [
@_
];
my
$count
=
scalar
@{
$args
};
SWITCH: {
if
( !
$count
or
$count
< 2 ) {
last
SWITCH;
}
my
$chan
= (
split
/,/,
$args
->[0] )[0];
my
$who
= (
split
/,/,
$args
->[1] )[0];
if
( !
$self
->state_chan_exists(
$chan
) ) {
last
SWITCH;
}
$chan
=
$self
->_state_chan_name(
$chan
);
if
( !
$self
->state_nick_exists(
$who
) ) {
last
SWITCH;
}
$who
=
$self
->state_user_nick(
$who
);
if
( !
$self
->state_is_chan_member(
$who
,
$chan
) ) {
last
SWITCH;
}
my
$comment
=
$args
->[2] ||
$who
;
$self
->_send_output_to_channel(
$chan
, {
prefix
=>
$server
,
command
=>
'KICK'
,
params
=> [
$chan
,
$who
,
$comment
] } );
$who
= u_irc
$who
;
$chan
= u_irc
$chan
;
delete
$self
->{state}->{chans}->{
$chan
}->{users}->{
$who
};
delete
$self
->{state}->{users}->{
$who
}->{chans}->{
$chan
};
unless
(
scalar
keys
%{
$self
->{state}->{chans}->{
$chan
}->{users} } ) {
delete
$self
->{state}->{chans}->{
$chan
};
}
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
daemon_server_remove {
my
$self
=
shift
;
my
$server
=
$self
->server_name();
my
$ref
= [ ];
my
$args
= [
@_
];
my
$count
=
scalar
@{
$args
};
SWITCH: {
if
( !
$count
or
$count
< 2 ) {
last
SWITCH;
}
my
$chan
= (
split
/,/,
$args
->[0] )[0];
my
$who
= (
split
/,/,
$args
->[1] )[0];
if
( !
$self
->state_chan_exists(
$chan
) ) {
last
SWITCH;
}
$chan
=
$self
->_state_chan_name(
$chan
);
if
( !
$self
->state_nick_exists(
$who
) ) {
last
SWITCH;
}
my
$fullwho
=
$self
->state_user_full(
$who
);
$who
= (
split
/!/,
$who
)[0];
if
( !
$self
->state_is_chan_member(
$who
,
$chan
) ) {
last
SWITCH;
}
my
$comment
=
'Enforced PART'
;
$comment
.=
" \"$args->[2]\""
if
$args
->[2];
$self
->_send_output_to_channel(
$chan
, {
prefix
=>
$fullwho
,
command
=>
'PART'
,
params
=> [
$chan
,
$comment
] } );
$who
= u_irc
$who
;
$chan
= u_irc
$chan
;
delete
$self
->{state}->{chans}->{
$chan
}->{users}->{
$who
};
delete
$self
->{state}->{users}->{
$who
}->{chans}->{
$chan
};
unless
(
scalar
keys
%{
$self
->{state}->{chans}->{
$chan
}->{users} } ) {
delete
$self
->{state}->{chans}->{
$chan
};
}
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
daemon_server_wallops {
my
$self
=
shift
;
my
$server
=
$self
->server_name();
my
$ref
= [ ];
my
$args
= [
@_
];
my
$count
=
scalar
@{
$args
};
SWITCH: {
if
( !
$count
) {
last
SWITCH;
}
$self
->{ircd}->send_output( {
prefix
=>
$server
,
command
=>
'WALLOPS'
,
params
=> [
$args
->[0] ] },
$self
->_state_connected_peers(),
keys
%{
$self
->{state}->{operwall} } );
$self
->{ircd}->send_event(
"daemon_wallops"
,
$server
,
$args
->[0] );
}
return
@{
$ref
}
if
wantarray
();
return
$ref
;
}
sub
add_spoofed_nick {
my
(
$kernel
,
$self
) =
@_
[KERNEL,OBJECT];
my
$ref
;
if
(
ref
$_
[ARG0] eq
'HASH'
) {
$ref
=
$_
[ARG0];
}
else
{
$ref
= {
@_
[ARG0..
$#_
] };
}
$ref
->{
lc
$_
} =
delete
$ref
->{
$_
}
for
keys
%{
$ref
};
return
unless
$ref
->{nick};
return
if
$self
->state_nick_exists(
$ref
->{nick} );
my
$record
=
$ref
;
$record
->{ts} =
time
()
unless
$record
->{ts};
$record
->{type} =
's'
;
$record
->{server} =
$self
->server_name();
$record
->{hops} = 0;
$record
->{route_id} =
'spoofed'
;
$record
->{umode} =
'i'
unless
$record
->{umode};
$record
->{ircname} =
"* I'm too lame to read the documentation *"
unless
$record
->{ircname};
$self
->{state}->{stats}->{invisible}++
if
$record
->{umode} =~ /i/;
$self
->{state}->{stats}->{ops_online}++
if
$record
->{umode} =~ /o/;
$record
->{idle_time} =
$record
->{conn_time} =
$record
->{ts};
$record
->{auth}->{ident} =
delete
$record
->{user} ||
$record
->{nick};
$record
->{auth}->{hostname} =
delete
$record
->{hostname} ||
$self
->server_name();
$self
->{state}->{users}->{ u_irc
$record
->{nick} } =
$record
;
$self
->{state}->{peers}->{
uc
$record
->{server} }->{users}->{ u_irc
$record
->{nick} } =
$record
;
my
$arrayref
= [
$record
->{nick},
$record
->{hops} + 1,
$record
->{ts},
'+'
.
$record
->{umode},
$record
->{auth}->{ident},
$record
->{auth}->{hostname},
$record
->{server},
$record
->{ircname} ];
$self
->{ircd}->send_output( {
command
=>
'NICK'
,
params
=>
$arrayref
},
$self
->_state_connected_peers() );
$self
->{ircd}->send_event(
"daemon_nick"
, @{
$arrayref
} );
$self
->_state_update_stats();
undef
;
}
sub
del_spoofed_nick {
my
(
$kernel
,
$self
,
$nick
) =
@_
[KERNEL,OBJECT,ARG0];
return
unless
$self
->state_nick_exists(
$nick
);
return
unless
$self
->_state_user_route(
$nick
) eq
'spoofed'
;
my
$message
=
$_
[ARG1] ||
'Client Quit'
;
$self
->{ircd}->send_output( @{
$self
->_daemon_cmd_quit(
$nick
,
qq{"$message"}
) },
qq{"$message"}
);
undef
;
}
sub
_spoofed_command {
my
(
$kernel
,
$self
,
$state
,
$nick
) =
@_
[KERNEL,OBJECT,STATE,ARG0];
return
unless
$self
->state_nick_exists(
$nick
);
return
unless
$self
->_state_user_route(
$nick
) eq
'spoofed'
;
$nick
=
$self
->state_user_nick(
$nick
);
$state
=~ s/daemon_cmd_//;
my
$command
=
"_daemon_cmd_"
.
$state
;
if
(
$state
=~ /^(privmsg|notice)$/ ) {
my
$type
=
uc
$1;
$self
->_daemon_cmd_message(
$nick
,
$type
,
@_
[ARG1 ..
$#_
] );
return
;
}
if
(
$state
eq
'sjoin'
) {
my
$chan
=
$_
[ARG1];
return
unless
$chan
and
$self
->state_chan_exists(
$chan
);
return
if
$self
->state_is_chan_member(
$nick
,
$chan
);
$chan
=
$self
->_state_chan_name(
$chan
);
my
$ts
=
$self
->_state_chan_timestamp(
$chan
) - 10;
$self
->_daemon_peer_sjoin(
'spoofed'
,
$self
->server_name(),
$ts
,
$chan
,
'+nt'
,
'@'
.
$nick
);
return
;
}
$self
->
$command
(
$nick
,
@_
[ARG1 ..
$#_
] )
if
$self
->can(
$command
);
undef
;
}
1;