our
$AUTHORITY
=
'cpan:HINRIK'
;
$POE::Filter::IRC::Compat::VERSION
=
'6.93'
;
my
%irc_cmds
= (
qr/^\d{3}$/
=>
sub
{
my
(
$self
,
$event
,
$line
) =
@_
;
$event
->{args}->[0] = _decolon(
$line
->{prefix} );
shift
@{
$line
->{params} };
if
(
$line
->{params}->[0] &&
$line
->{params}->[0] =~ /\x20/ ) {
$event
->{args}->[1] =
$line
->{params}->[0];
}
else
{
$event
->{args}->[1] =
join
(
' '
, (
map
{ /\x20/ ?
":$_"
:
$_
} @{
$line
->{params} } ) );
}
$event
->{args}->[2] =
$line
->{params};
},
qr/^cap$/
=>
sub
{
my
(
$self
,
$event
,
$line
) =
@_
;
for
(
my
$i
= 0; ;
$i
++) {
last
if
!
defined
$line
->{params}[
$i
+1];
$event
->{args}[
$i
] =
$line
->{params}[
$i
+1];
}
},
qr/^notice$/
=>
sub
{
my
(
$self
,
$event
,
$line
) =
@_
;
if
(
defined
$line
->{prefix} &&
$line
->{prefix} =~ /!/) {
$event
->{args} = [
_decolon(
$line
->{prefix} ),
[
split
/,/,
$line
->{params}->[0]],
(
$self
->{identifymsg}
? _split_idmsg(
$line
->{params}->[1])
:
$line
->{params}->[1]
),
];
}
else
{
$event
->{name} =
'snotice'
;
$event
->{args} = [
$line
->{params}->[1],
$line
->{params}->[0],
(
defined
$line
->{prefix} ? _decolon(
$line
->{prefix}) : ()),
];
}
},
qr/^privmsg$/
=>
sub
{
my
(
$self
,
$event
,
$line
) =
@_
;
if
(
grep
{
index
(
$line
->{params}->[0],
$_
) >= 0 } @{
$self
->{chantypes} } ) {
$event
->{args} = [
_decolon(
$line
->{prefix} ),
[
split
/,/,
$line
->{params}->[0]],
(
$self
->{identifymsg}
? _split_idmsg(
$line
->{params}->[1])
:
$line
->{params}->[1]
),
];
$event
->{name} =
'public'
;
}
else
{
$event
->{args} = [
_decolon(
$line
->{prefix} ),
[
split
/,/,
$line
->{params}->[0]],
(
$self
->{identifymsg}
? _split_idmsg(
$line
->{params}->[1])
:
$line
->{params}->[1]
),
];
$event
->{name} =
'msg'
;
}
},
qr/^invite$/
=>
sub
{
my
(
$self
,
$event
,
$line
) =
@_
;
shift
( @{
$line
->{params} } );
unshift
( @{
$line
->{params} }, _decolon(
$line
->{prefix} ||
''
) )
if
$line
->{prefix};
$event
->{args} =
$line
->{params};
},
);
my
%dcc_types
= (
qr/^(?:CHAT|SEND)$/
=>
sub
{
my
(
$nick
,
$type
,
$args
) =
@_
;
my
(
$file
,
$addr
,
$port
,
$size
);
return
if
!((
$file
,
$addr
,
$port
,
$size
) =
$args
=~ /^(
".+"
|[^ ]+) +(\d+) +(\d+)(?: +(\d+))?/);
if
(
$file
=~ s/^"//) {
$file
=~ s/"$//;
$file
=~ s/\\
"/"
/g;
}
$file
= fileparse(
$file
);
return
(
$port
,
{
nick
=>
$nick
,
type
=>
$type
,
file
=>
$file
,
size
=>
$size
,
addr
=>
$addr
,
port
=>
$port
,
},
$file
,
$size
,
$addr
,
);
},
qr/^(?:ACCEPT|RESUME)$/
=>
sub
{
my
(
$nick
,
$type
,
$args
) =
@_
;
my
(
$file
,
$port
,
$position
);
return
if
!((
$file
,
$port
,
$position
) =
$args
=~ /^(
".+"
|[^ ]+) +(\d+) +(\d+)/);
$file
=~ s/^
"|"
$//g;
$file
= fileparse(
$file
);
return
(
$port
,
{
nick
=>
$nick
,
type
=>
$type
,
file
=>
$file
,
size
=>
$position
,
port
=>
$port
,
},
$file
,
$position
,
);
},
);
sub
new {
my
(
$package
,
%self
) =
@_
;
$self
{
lc
$_
} =
delete
$self
{
$_
}
for
keys
%self
;
$self
{BUFFER} = [ ];
$self
{_ircd} = POE::Filter::IRCD->new();
$self
{chantypes} = [
'#'
,
'&'
]
if
ref
$self
{chantypes} ne
'ARRAY'
;
return
bless
\
%self
,
$package
;
}
sub
clone {
my
$self
=
shift
;
my
$nself
= { };
$nself
->{
$_
} =
$self
->{
$_
}
for
keys
%{
$self
};
$nself
->{BUFFER} = [ ];
return
bless
$nself
,
ref
$self
;
}
sub
debug {
my
(
$self
,
$flag
) =
@_
;
if
(
defined
$flag
) {
$self
->{debug} =
$flag
;
$self
->{_ircd}->debug(
$flag
);
}
return
$self
->{debug};
}
sub
chantypes {
my
(
$self
,
$ref
) =
@_
;
return
if
ref
$ref
ne
'ARRAY'
|| !@{
$ref
};
$self
->{chantypes} =
$ref
;
return
1;
}
sub
identifymsg {
my
(
$self
,
$switch
) =
@_
;
$self
->{identifymsg} =
$switch
;
return
;
}
sub
_split_idmsg {
my
(
$line
) =
@_
;
my
(
$identified
,
$msg
) =
split
//,
$line
, 2;
$identified
=
$identified
eq
'+'
? 1 : 0;
return
$msg
,
$identified
;
}
sub
get_one {
my
(
$self
) =
@_
;
my
$line
=
shift
@{
$self
->{BUFFER} } or
return
[ ];
if
(
ref
$line
ne
'HASH'
|| !
$line
->{command} || !
$line
->{params}) {
warn
"Received line '$line' that is not IRC protocol\n"
if
$self
->{debug};
return
[ ];
}
if
(
$line
->{command} =~ /^PRIVMSG|NOTICE$/ &&
$line
->{params}->[1] =~
tr
/\001//) {
return
$self
->_get_ctcp(
$line
);
}
my
$event
= {
name
=>
lc
$line
->{command},
raw_line
=>
$line
->{raw_line},
};
for
my
$cmd
(
keys
%irc_cmds
) {
if
(
$event
->{name} =~
$cmd
) {
$irc_cmds
{
$cmd
}->(
$self
,
$event
,
$line
);
return
[
$event
];
}
}
unshift
( @{
$line
->{params} }, _decolon(
$line
->{prefix} ||
''
) )
if
$line
->{prefix};
$event
->{args} =
$line
->{params};
return
[
$event
];
}
sub
get_one_start {
my
(
$self
,
$lines
) =
@_
;
push
@{
$self
->{BUFFER} },
@$lines
;
return
;
}
sub
put {
my
(
$self
,
$lineref
) =
@_
;
my
$quoted
= [ ];
push
@$quoted
, _ctcp_quote(
$_
)
for
@$lineref
;
return
$quoted
;
}
sub
_ctcp_quote {
my
(
$line
) =
@_
;
$line
= _low_quote(
$line
);
$line
=~ s/\001/\\a/g;
return
"\001$line\001"
;
}
sub
_ctcp_dequote {
my
(
$msg
) =
@_
;
my
(
@chunks
,
$ctcp
,
$text
);
if
(!
defined
$msg
) {
croak
'Not enough arguments to POE::Filter::IRC::Compat::_ctcp_dequote'
;
}
$msg
= _low_dequote(
$msg
);
substr
(
$msg
,
rindex
(
$msg
,
"\001"
), 1,
'\\a'
)
if
(
$msg
=~
tr
/\001//) % 2 != 0;
return
if
$msg
!~
tr
/\001//;
@chunks
=
split
/\001/,
$msg
;
shift
@chunks
if
!
length
$chunks
[0];
for
(
@chunks
) {
s/\\([^\\a])/$1/g;
s/\\\\/\\/g;
s/\\a/\001/g;
}
if
(
$msg
=~ /^\001/) {
push
@$ctcp
,
shift
@chunks
;
}
while
(
@chunks
) {
push
@$text
,
shift
@chunks
;
push
@$ctcp
,
shift
@chunks
if
@chunks
;
}
return
(
$ctcp
,
$text
);
}
sub
_decolon {
my
(
$line
) =
@_
;
$line
=~ s/^://;
return
$line
;
}
sub
_get_ctcp {
my
(
$self
,
$line
) =
@_
;
my
$ctcp_type
=
$line
->{command} eq
'PRIVMSG'
?
'ctcp'
:
'ctcpreply'
;
my
(
$msg
,
$identified
) = (
$line
->{params}->[1],
undef
);
(
$msg
,
$identified
) = _split_idmsg(
$msg
)
if
$self
->{identifymsg} &&
$msg
=~ /^.ACTION/;
my
$events
= [ ];
my
(
$ctcp
,
$text
) = _ctcp_dequote(
$msg
);
if
(!
defined
$ctcp
) {
warn
"Received malformed CTCP message: $msg\n"
if
$self
->{debug};
return
$events
;
}
my
$nick
=
defined
$line
->{prefix} ? (
split
/!/,
$line
->{prefix})[0] :
undef
;
my
(
$name
,
$args
);
CTCP:
for
my
$string
(
$ctcp
->[0]) {
if
(!((
$name
,
$args
) =
$string
=~ /^(\w+)(?: +(.*))?/)) {
defined
$nick
?
do
{
warn
"Received malformed CTCP message from $nick: $string\n"
if
$self
->{debug} }
:
do
{
warn
"Trying to send malformed CTCP message: $string\n"
if
$self
->{debug} }
;
last
CTCP;
}
if
(
lc
$name
eq
'dcc'
) {
my
(
$dcc_type
,
$rest
);
if
(!((
$dcc_type
,
$rest
) =
$args
=~ /^(\w+) +(.+)/)) {
defined
$nick
?
do
{
warn
"Received malformed DCC request from $nick: $args\n"
if
$self
->{debug} }
:
do
{
warn
"Trying to send malformed DCC request: $args\n"
if
$self
->{debug} }
;
last
CTCP;
}
$dcc_type
=
uc
$dcc_type
;
my
(
$handler
) =
grep
{
$dcc_type
=~ /
$_
/ }
keys
%dcc_types
;
if
(!
$handler
) {
warn
"Unhandled DCC $dcc_type request: $rest\n"
if
$self
->{debug};
last
CTCP;
}
my
@dcc_args
=
$dcc_types
{
$handler
}->(
$nick
,
$dcc_type
,
$rest
);
if
(!
@dcc_args
) {
defined
$nick
?
do
{
warn
"Received malformed DCC $dcc_type request from $nick: $rest\n"
if
$self
->{debug} }
:
do
{
warn
"Trying to send malformed DCC $dcc_type request: $rest\n"
if
$self
->{debug} }
;
last
CTCP;
}
push
@$events
, {
name
=>
'dcc_request'
,
args
=> [
$line
->{prefix},
$dcc_type
,
@dcc_args
,
],
raw_line
=>
$line
->{raw_line},
};
}
else
{
push
@$events
, {
name
=>
$ctcp_type
.
'_'
.
lc
$name
,
args
=> [
$line
->{prefix},
[
split
/,/,
$line
->{params}->[0]],
(
defined
$args
?
$args
:
''
),
(
defined
$identified
?
$identified
: () ),
],
raw_line
=>
$line
->{raw_line},
};
}
}
return
$events
;
}
sub
_low_quote {
my
(
$line
) =
@_
;
my
%enquote
= (
"\012"
=>
'n'
,
"\015"
=>
'r'
,
"\0"
=>
'0'
,
"\cP"
=>
"\cP"
);
if
(!
defined
$line
) {
croak
'Not enough arguments to POE::Filter::IRC::Compat->_low_quote'
;
}
if
(
$line
=~
tr
/[\012\015\0\cP]//) {
$line
=~ s/([\012\015\0\cP])/\cP
$enquote
{$1}/g;
}
return
$line
;
}
sub
_low_dequote {
my
(
$line
) =
@_
;
my
%dequote
= (
n
=>
"\012"
,
r
=>
"\015"
,
0
=>
"\0"
,
"\cP"
=>
"\cP"
);
if
(!
defined
$line
) {
croak
'Not enough arguments to POE::Filter::IRC::Compat->_low_dequote'
;
}
if
(
$line
=~
tr
/\cP//) {
$line
=~ s/\cP([nr0\cP])/
$dequote
{$1}/g;
}
return
$line
;
}
1;