BEGIN
{
use
vars
qw( $VERSION $TEMPLATES $ESC $MAGIC_DATA $MAGIC_DATA_SOURCE )
;
our
$VERSION
=
'v0.1.3'
;
our
$TEMPLATES
=
{
'byte'
=> [
'c'
, 1 ],
'ubyte'
=> [
'C'
, 1 ],
'char'
=> [
'c'
, 1 ],
'uchar'
=> [
'C'
, 1 ],
'short'
=> [
's'
, 2 ],
'ushort'
=> [
'S'
, 2 ],
'long'
=> [
'l'
, 4 ],
'ulong'
=> [
'L'
, 4 ],
'date'
=> [
'l'
, 4 ],
'ubeshort'
=> [
'n'
, 2 ],
'beshort'
=> [ [
'n'
,
'S'
,
's'
], 2 ],
'ubelong'
=> [
'N'
, 4 ],
'belong'
=> [ [
'N'
,
'I'
,
'i'
], 4 ],
'bedate'
=> [
'N'
, 4 ],
'uleshort'
=> [
'v'
, 2 ],
'leshort'
=> [ [
'v'
,
'S'
,
's'
], 2 ],
'ulelong'
=> [
'V'
, 4 ],
'lelong'
=> [ [
'V'
,
'I'
,
'i'
], 4 ],
'ledate'
=> [
'V'
, 4 ],
'string'
=>
undef
(),
};
our
$ESC
=
{
'n'
=>
"\n"
,
'r'
=>
"\r"
,
'b'
=>
"\b"
,
't'
=>
"\t"
,
'f'
=>
"\f"
};
our
$MAGIC_DATA
= [];
our
$MAGIC_DATA_SOURCE
=
''
;
};
sub
init
{
my
$self
=
shift
(
@_
);
my
$file
;
$file
=
shift
(
@_
)
if
(
@_
% 2 );
my
$opts
=
$self
->_get_args_as_hash(
@_
);
$opts
->{magic} =
$file
if
(
length
(
$file
) );
$self
->{follow_links} = 1;
$self
->{check_magic} = 0;
$self
->{error_returns_undef} = 0;
$self
->{default_type} =
'text/plain'
;
$self
->{_init_strict_use_sub} = 1;
$self
->SUPER::init(
@_
);
$self
->{magic} = {};
$self
->{magic_data} = [];
my
$load_json_data
=
sub
{
my
$json_file
=
shift
(
@_
) ||
return
;
my
$io
= IO::File->new(
"<$json_file"
) ||
return
(
$self
->error(
"Unable to open our own json magic file \"$json_file\": $!"
) );
local
$/;
my
$buf
=
scalar
( <
$io
> );
$io
->
close
;
local
$@;
my
$rv
=
eval
{
my
$j
= JSON->new->relaxed->allow_nonref;
$MAGIC_DATA
=
$self
->{magic_data} =
$j
->decode(
$buf
);
return
(1);
};
if
( $@ )
{
return
(
$self
->error(
"An error occured while trying to json decode "
,
length
(
$buf
),
" bytes of json data: $@"
) );
}
return
(
$rv
);
};
if
(
$opts
->{magic} )
{
$file
=
$opts
->{magic};
my
$file_abs
= URI::file->new_abs(
$file
)->file( $^O );
if
(
$file_abs
eq
$MAGIC_DATA_SOURCE
&&
scalar
(
@$MAGIC_DATA
) )
{
$self
->{magic_data} =
$MAGIC_DATA
;
}
else
{
my
$checksum
= Digest::MD5::md5_hex(
$file_abs
);
my
$base
= File::Basename::basename(
$file
);
my
$path
= File::Spec->catpath( File::Spec->tmpdir,
$base
.
"_${checksum}.json"
);
if
( -e(
$path
) && -s(
$path
) )
{
$load_json_data
->(
$path
) ||
return
;
}
else
{
return
(
$self
->error(
"Magic file provided \"$file\" does not exist."
) )
if
( !-e(
$file
) );
my
$io
= IO::File->new(
"<$file"
) ||
return
(
$self
->error(
"Unable to open magic file provided \"$file\": $!"
) );
$io
->
binmode
;
$self
->parse_magic_file(
$io
);
$MAGIC_DATA
=
$self
->{magic_data};
$io
->
close
;
my
$json
=
$self
->as_json ||
return
;
my
$fh
= IO::File->new(
">$path"
) ||
return
(
$self
->error(
"Unable to write to magic cache json data file \"$path\": $!"
) );
$fh
->
binmode
;
$fh
->
print
(
$json
);
$fh
->
close
;
}
$MAGIC_DATA_SOURCE
=
$file_abs
;
}
}
elsif
(
$MAGIC_DATA
&&
scalar
(
@$MAGIC_DATA
) )
{
$self
->{magic_data} =
$MAGIC_DATA
;
}
else
{
$file
= __FILE__;
$file
=~ s/\.pm/\.json/;
return
(
$self
->error(
"Apache2::SSI magic file \"$file\" does not exist."
) )
if
( !-e(
$file
) );
$load_json_data
->(
$file
) ||
return
;
}
$self
->{SPECIALS} =
{
'message/rfc822'
=>
[
'^Received:'
,
'^>From '
,
'^From '
,
'^To: '
,
'^Return-Path: '
,
'^Cc: '
,
'^X-Mailer: '
],
'message/news'
=>
[
'^Newsgroups: '
,
'^Path: '
,
'^X-Newsreader: '
],
'text/html'
=>
[
'<html[^>]*>'
,
'<HTML[^>]*>'
,
'<head[^>]*>'
,
'<HEAD[^>]*>'
,
'<body[^>]*>'
,
'<BODY[^>]*>'
,
'<title[^>]*>'
,
'<TITLE[^>]*>'
,
'<h1[^>]*>'
,
'<H1[^>]*>'
,
],
'text/x-roff'
=>
[
"^\\.SH"
,
"^\\.PP"
,
"^\\.TH"
,
"^\\.BR"
,
"^\\.SS"
,
"^\\.TP"
,
"^\\.IR"
,
],
};
$self
->{FILE_EXTS} =
{
qr/\.gz$/
=>
'application/x-gzip'
,
qr/\.bz2$/
=>
'application/x-bzip2'
,
qr/\.Z$/
=>
'application/x-compress'
,
qr/\.txt$/
=>
'text/plain'
,
qr/\.html$/
=>
'text/html'
,
qr/\.htm$/
=>
'text/html'
,
};
return
(
$self
);
}
sub
as_json
{
my
$self
=
shift
(
@_
);
my
$data
=
$self
->{magic_data};
my
$j
= JSON->new->relaxed->allow_nonref;
my
$json
=
$j
->pretty->encode(
$data
);
return
(
$json
);
}
sub
check
{
my
$self
=
shift
(
@_
);
my
$file
=
shift
(
@_
);
my
$prev
=
$self
->check_magic;
$self
->check_magic( 1 );
my
$io
= IO::File->new(
"<$file"
) ||
return
(
$self
->error(
"Unable to open magic file \"$file\": $!"
) );
$io
->
binmode
;
$self
->{magic}->{io} =
$io
;
my
$data
= [];
while
( !
$io
->
eof
() )
{
$self
->read_magic_entry(
$data
);
}
$io
->
close
();
$self
->
dump
(
$data
);
$self
->check_magic(
$prev
);
return
(
$self
);
}
sub
check_magic {
return
(
shift
->_set_get_boolean(
'check_magic'
,
@_
) ); }
sub
data
{
my
$self
=
shift
(
@_
);
my
$data
=
shift
(
@_
);
my
$type
=
''
;
if
(
length
(
$data
) <= 0 )
{
return
(
$self
->{default_type} ?
'application/octet-stream'
:
''
);
}
$type
=
$self
->with_magic(
$data
);
if
( !
defined
(
$type
) )
{
$type
=
$self
->with_data(
$data
);
}
if
( !
defined
(
$type
) )
{
$type
=
$self
->{default_type} ?
'text/plain'
:
''
;
}
return
(
$type
);
}
sub
default_type {
return
(
shift
->_set_get_scalar(
'default_type'
,
@_
) ); }
sub
dump
{
my
$self
=
shift
(
@_
);
my
$data
=
shift
(
@_
) ||
$self
->{magic_data};
my
$depth
=
shift
(
@_
);
$data
= []
unless
(
defined
(
$data
) );
$depth
= 0
unless
(
defined
(
$depth
) );
our
$err
= IO::File->new;
$err
->autoflush( 1 );
$err
->fdopen(
fileno
( STDERR ),
'w'
) ||
return
(
$self
->error(
"Cannot write to STDERR: $!"
) );
$err
->
binmode
;
foreach
my
$entry
(
@$data
)
{
$entry
=
$self
->parse_magic_line(
@$entry
)
if
(
scalar
(
@$entry
) == 3 );
next
if
( !
defined
(
$entry
) );
my
(
$offtype
,
$offset
,
$numbytes
,
$type
,
$mask
,
$op
,
$testval
,
$template
,
$message
,
$subtests
) =
@$entry
;
$err
->
print
(
'>'
x
$depth
);
if
(
$offtype
== 1 )
{
$offset
->[2] =~
tr
/c/b/;
$err
->
printf
(
"(%s.%s%s)"
,
$offset
->[0],
$offset
->[2],
$offset
->[3] );
}
elsif
(
$offtype
== 2 )
{
$err
->
print
(
"&"
,
$offset
);
}
else
{
$err
->
print
(
$offset
);
}
$err
->
print
(
"\t"
,
$type
);
if
(
$mask
)
{
$err
->
print
(
"&"
,
$mask
);
}
$err
->
print
(
"\t"
,
$op
,
$testval
,
"\t"
,
$message
,
"\n"
);
if
(
$subtests
)
{
$self
->
dump
(
$subtests
,
$depth
+ 1 );
}
}
}
sub
error_returns_undef {
return
(
shift
->_set_get_boolean(
'error_returns_undef'
,
@_
) ); }
sub
file
{
my
$self
=
shift
(
@_
);
my
$desc
=
''
;
my
$type
=
''
;
my
$file
=
shift
(
@_
) ||
do
{
if
(
$self
->{error_returns_undef} )
{
return
(
$self
->error(
"Missing file arguement. Usage: \$magic->file( \$some_file_name )"
) );
}
else
{
$desc
.=
"no file provided."
;
return
(
"x-system/x-error; $desc"
);
}
};
my
$base_file
= File::Basename::basename(
$file
);
if
( !-e(
$file
) )
{
if
(
$self
->{error_returns_undef} )
{
return
(
$self
->error(
"File $file does not exist."
) );
}
else
{
$desc
.=
"file '$file' does not exist."
;
return
(
"x-system/x-error; $desc"
);
}
}
elsif
( !-r(
$file
) )
{
if
(
$self
->{error_returns_undef} )
{
return
(
$self
->error(
"Unable to read file '$file'; lacking permission"
) );
}
else
{
$desc
.=
"unable to read '$base_file': Permission denied."
;
return
(
"x-system/x-error; $desc"
);
}
}
if
(
$self
->follow_links )
{
CORE::
stat
(
$file
);
}
else
{
CORE::
lstat
(
$file
);
}
if
( !-f( _ ) || -z( _ ) )
{
if
( !
$self
->follow_links && -l( _ ) )
{
return
(
'application/x-link'
);
}
elsif
( -d( _ ) ) {
return
(
'application/x-directory'
); }
elsif
( -p( _ ) ) {
return
(
'application/x-pipe'
); }
elsif
( -S( _ ) ) {
return
(
'application/x-socket'
); }
elsif
( -b( _ ) ) {
return
(
'application/x-block'
); }
elsif
( -c( _ ) ) {
return
(
'application/x-character'
); }
elsif
( -z( _ ) ) {
return
(
'application/x-empty'
); }
else
{
return
(
$self
->{default_type} ?
$self
->{default_type} :
'application/x-unknown'
);
}
}
my
$io
;
$io
= IO::File->new(
"<$file"
) ||
do
{
if
(
$self
->{error_returns_undef} )
{
return
(
$self
->error(
"Unable to open file '$file': $!"
) );
}
else
{
return
(
"x-system/x-error; $base_file: $!"
);
}
};
$io
->
binmode
;
my
$default
;
if
( -x(
$file
) && -T( _ ) )
{
my
$line1
=
$io
->getline;
if
(
$line1
=~ /^\
{
my
$bin_name
= File::Basename::basename( $1 );
$default
=
"text/x-${bin_name}"
;
}
}
my
$out
=
$self
->handle(
$io
,
$desc
, {
default
=>
$default
} );
$io
->
close
;
return
(
$out
);
}
sub
follow_links {
return
(
shift
->_set_get_boolean(
'follow_links'
,
@_
) ); }
sub
handle
{
my
$self
=
shift
(
@_
);
my
$io
=
shift
(
@_
);
my
$desc
=
shift
(
@_
);
my
$opts
=
$self
->_get_args_as_hash(
@_
);
$opts
->{
default
} =
$self
->default_type
if
( !
length
(
$opts
->{
default
} ) );
my
$type
=
''
;
my
$match_found
= 0;
for
(
my
$m
= 0;
$m
<= $
{
my
$test
;
if
( (
$test
=
$self
->_magic_match(
$self
->{magic_data}->[
$m
], \
$desc
,
$io
) ) )
{
if
(
defined
(
$desc
) &&
$desc
ne
''
)
{
$match_found
= 1;
$type
=
$desc
;
last
;
}
}
elsif
( !
defined
(
$test
) )
{
warnings::
warn
(
"Error occurred while checking for match: "
,
$self
->error )
if
( warnings::enabled() &&
$self
->debug );
}
if
(
$m
== $
$self
->{magic}->{io} &&
!
$self
->{magic}->{io}->
eof
)
{
$self
->read_magic_entry();
}
}
if
( !
$match_found
)
{
my
$data
=
''
;
$io
->
seek
( 0, 0 );
$io
->
read
(
$data
, 0x8564 );
$type
=
$self
->with_data(
$data
);
}
if
( !
defined
(
$type
) )
{
$type
=
$opts
->{
default
} ?
$opts
->{
default
} :
''
;
}
return
(
$type
);
}
sub
parse_magic_file
{
my
$self
=
shift
(
@_
);
my
$io
=
shift
(
@_
);
$self
->{magic}->{io} =
$io
;
$self
->{magic}->{buffer} =
undef
();
$self
->{magic}->{count} = 0;
while
( !
$io
->
eof
() )
{
$self
->read_magic_entry();
}
seek
(
$io
, 0, 0 );
}
sub
parse_magic_line
{
my
$self
=
shift
(
@_
);
my
(
$line
,
$line_num
,
$subtests
) =
@_
;
my
(
$offtype
,
$offset
,
$numbytes
,
$type
,
$mask
,
$operator
,
$testval
,
$template
,
$message
);
if
(
$line
=~ s/^>*([&\(]?[a-flsx\.\+\-\d]+\)?)[[:blank:]\h]+(\S+)[[:blank:]\h]+// )
{
(
$offset
,
$type
) = ( $1, $2 );
if
(
$offset
=~ /^\(/ )
{
$offtype
= 1;
my
(
$o1
,
$type
,
$o2
);
if
( (
$o1
,
$type
,
$o2
) = (
$offset
=~ /\((\d+)(\.[bsl])?([\+\-]?\d+)?\)/ ) )
{
$o1
=
oct
(
$o1
)
if
(
$o1
=~ /^0/o );
$o2
=
oct
(
$o2
)
if
(
$o2
=~ /^0/o );
$type
=~ s/\.//;
$type
=
'l'
if
(
$type
eq
''
);
$type
=~
tr
/b/c/;
my
$sz
=
$type
;
$sz
=~
tr
/csl/124/;
$offset
= [
$o1
,
$sz
,
$type
,
int
(
$o2
) ];
}
else
{
return
(
$self
->error(
"Bad indirect offset at line $line_num. '$offset'"
) );
}
}
elsif
(
$offset
=~ /^&/o )
{
$offtype
= 2;
$offset
=
substr
(
$offset
, 1 );
$offset
=
oct
(
$offset
)
if
(
$offset
=~ /^0/o );
}
else
{
$offtype
= 0;
$offset
=
oct
(
$offset
)
if
(
$offset
=~ /^0/o );
}
}
else
{
return
(
$self
->error(
"Bad Offset/Type at line $line_num. '$line'"
) );
}
if
(
$type
=~ s/&(.*)// )
{
$mask
= $1;
$mask
=
oct
(
$mask
)
if
(
$mask
=~ /^0/o );
}
if
( !
exists
(
$TEMPLATES
->{
$type
} ) )
{
return
(
$self
->error(
"Invalid type '$type' at line $line_num"
) );
}
if
(
$line
=~ s/([^\\])\s+(.*)/$1/ )
{
$message
= $2;
}
else
{
return
(
$self
->error(
"Missing or invalid test condition/message at line $line_num"
) );
}
$line
=~ s/\n$//o;
if
(
$line
=~ s/^([><&^=!])//o )
{
$operator
= $1;
}
elsif
(
$line
eq
'x'
)
{
$operator
=
'x'
;
}
else
{
$operator
=
'='
;
}
if
(
$type
eq
'string'
)
{
$testval
=
$line
;
$testval
=~ s/\\([x0-7][0-7]?[0-7]?)/
chr
(
oct
( $1 ) )/eg;
$testval
=~ s/\\(.)/
$ESC
->{ $1 }||$1/eg;
if
(
$operator
=~ /[>x]/o )
{
$numbytes
= 0;
}
elsif
(
$operator
=~ /[=<]/o )
{
$numbytes
=
length
(
$testval
);
}
elsif
(
$operator
eq
'!'
)
{
$testval
=
$operator
.
$testval
;
$numbytes
=
length
(
$testval
);
$operator
=
'='
;
}
else
{
return
(
$self
->error(
"Invalid operator '$operator' for type 'string' at line $line_num."
) );
}
}
else
{
if
(
$operator
ne
'x'
)
{
if
(
$line
=~ /^0/o )
{
$testval
=
oct
(
$line
);
}
else
{
$testval
=
int
(
$line
);
}
}
(
$template
,
$numbytes
) = @{
$TEMPLATES
->{
$type
}};
if
(
ref
(
$template
) )
{
$template
=
$template
->[0]
unless
(
$operator
eq
'>'
||
$operator
eq
'<'
);
}
}
return
( [
$offtype
,
$offset
,
$numbytes
,
$type
,
$mask
,
$operator
,
$testval
,
$template
,
$message
,
$subtests
] );
}
sub
read_magic_entry
{
my
$self
=
shift
(
@_
);
my
$data
=
shift
(
@_
) ||
$self
->{magic_data};
my
$depth
=
shift
(
@_
);
my
$magic
=
$self
->{magic};
my
$io
=
$magic
->{io};
my
$entry
= [];
my
$line
=
''
;
$line
=
$magic
->{buffer};
while
(1)
{
$line
=
''
if
( !
defined
(
$line
) );
if
(
$line
=~ /^\
{
last
if
(
$io
->
eof
);
$line
= <
$io
>;
$magic
->{count}++;
next
;
}
my
$this_depth
= (
$line
=~ /^(>+)/ )[0];
$this_depth
=
''
if
( !
defined
(
$this_depth
) );
$depth
= 0
if
( !
defined
(
$depth
) );
if
(
length
(
$this_depth
) >
$depth
)
{
$magic
->{buffer} =
$line
;
if
(
$self
->read_magic_entry(
$entry
->[2],
$depth
+ 1 ) <
$depth
||
$io
->
eof
)
{
return
;
}
$line
=
$magic
->{buffer};
}
elsif
(
length
(
$this_depth
) <
$depth
)
{
$magic
->{buffer} =
$line
;
return
(
length
(
$this_depth
) );
}
elsif
(
@$entry
)
{
$magic
->{buffer} =
$line
;
return
(
length
(
$this_depth
) );
}
else
{
$entry
= [
$line
,
$magic
->{count}, [] ];
push
(
@$data
,
$entry
);
last
if
(
$io
->
eof
() );
$line
= <
$io
>;
my
$tmp
=
$line
;
$tmp
=~ s/\n$//gs;
$magic
->{count}++;
}
}
}
sub
with_data
{
my
$self
=
shift
(
@_
);
my
$data
=
shift
(
@_
);
my
$type
=
undef
();
return
if
(
length
(
$data
) <= 0 );
$data
=
substr
(
$data
, 0, 0x8564 );
if
( _is_binary(
$data
) )
{
$type
=
'application/octet-stream'
;
}
else
{
my
(
$token
,
%val
);
foreach
my
$type
(
keys
( %{
$self
->{SPECIALS}} ) )
{
my
$token
=
'('
. (
join
(
'|'
,
sort
{
length
(
$a
) <=>
length
(
$b
) } @{
$self
->{SPECIALS}->{
$type
} } ) ) .
')'
;
my
$tdata
=
$data
;
if
(
$tdata
=~ /
$token
/mg )
{
$val
{
$type
} =
pos
(
$tdata
);
}
}
if
(
scalar
(
keys
(
%val
) ) )
{
my
@skeys
=
sort
{
$val
{
$a
} <=>
$val
{
$b
} }
keys
(
%val
);
$type
=
$skeys
[0];
}
}
return
(
$type
);
}
sub
with_filename
{
my
$self
=
shift
(
@_
);
my
$fname
=
shift
(
@_
);
my
$type
=
''
;
my
$file
=
$fname
;
$fname
=~ s/^.*\///;
for
my
$regex
(
keys
( %{
$self
->{FILE_EXTS}} ) )
{
if
(
$fname
=~ /
$regex
/i )
{
if
( (
defined
(
$type
) &&
$type
!~ /;/ ) ||
!
defined
(
$type
) )
{
$type
=
$self
->{FILE_EXTS}->{
$regex
};
}
}
}
return
(
$type
);
}
sub
with_magic
{
my
$self
=
shift
(
@_
);
my
$data
=
shift
(
@_
);
my
$desc
=
''
;
my
$type
=
''
;
return
(
'application/octet-stream'
)
if
(
length
(
$data
) <= 0 );
for
(
my
$m
= 0;
$m
<= $
{
if
(
$self
->_magic_match_str(
$self
->{magic_data}->[
$m
], \
$desc
,
$data
) )
{
if
(
defined
(
$desc
) &&
$desc
ne
''
)
{
$type
=
$desc
;
last
;
}
}
if
(
$m
== $
{
$self
->read_magic_entry();
}
}
return
(
$type
);
}
sub
_is_binary
{
my
(
$data
) =
@_
;
my
$len
=
length
(
$data
);
my
$count
= (
$data
=~
tr
/[\x00-\x08\x0b-\x0c\x0e-\x1a\x1c-\x1f]// );
return
( 1 )
if
(
$len
<= 0 );
return
( 1 )
if
( (
$count
/
$len
) > 0.1 );
return
( 0 );
}
sub
_magic_match
{
my
$self
=
shift
(
@_
);
my
(
$item
,
$p_desc
,
$io
) =
@_
;
return
unless
(
defined
(
$item
) &&
ref
(
$item
//
''
) eq
'ARRAY'
);
$item
=
$self
->parse_magic_line(
@$item
)
if
(
@$item
== 3 );
return
(
$self
->error(
"File handle is not defined."
) )
unless
(
defined
(
$io
) );
my
(
$offtype
,
$offset
,
$numbytes
,
$type
,
$mask
,
$op
,
$testval
,
$template
,
$message
,
$subtests
) =
@$item
;
$self
->{trick}++;
if
(
$self
->{trick} > 186 &&
$self
->{trick} < 192 )
{
my
$c
= -1;
}
my
$data
=
''
;
my
$match
= 0;
if
(
$offtype
== 1 )
{
my
(
$off1
,
$sz
,
$template
,
$off2
) =
@$offset
;
$io
->
seek
(
$off1
, 0 ) ||
return
(
$self
->error(
"Unable to seek to offset $off1 in file"
) );
return
if
(
$io
->
read
(
$data
,
$sz
) !=
$sz
);
$off2
+=
unpack
(
$template
,
$data
);
$io
->
seek
(
$off2
, 0 ) ||
return
(
$self
->error(
"Unable to seek to offset $off2 in file."
) );
}
elsif
(
$offtype
== 2 )
{
$io
->
seek
(
$offset
, 1 ) ||
return
(
$self
->error(
"Unable to seek to offset $offset in file"
) );
}
else
{
$io
->
seek
(
$offset
, 0 ) ||
return
(
$self
->error(
"Unable to seek to offset $offset in file"
) );
}
if
(
$type
eq
'string'
)
{
if
(
$numbytes
> 0 )
{
return
if
(
$io
->
read
(
$data
,
$numbytes
) !=
$numbytes
);
}
else
{
my
$ch
=
$io
->
getc
();
while
(
defined
(
$ch
) &&
$ch
ne
"\0"
&&
$ch
ne
"\n"
)
{
$data
.=
$ch
;
$ch
=
$io
->
getc
();
}
}
if
(
$op
eq
'='
)
{
$match
= (
$data
eq
$testval
);
}
elsif
(
$op
eq
'<'
)
{
$match
= (
$data
lt
$testval
);
}
elsif
(
$op
eq
'>'
)
{
$match
= (
$data
gt
$testval
);
}
if
(
$self
->check_magic )
{
print
( STDERR
"STRING: $data $op $testval => $match\n"
);
}
}
else
{
return
if
(
$io
->
read
(
$data
,
$numbytes
) !=
$numbytes
);
if
(
ref
(
$template
) )
{
$data
=
unpack
(
$template
->[2],
pack
(
$template
->[1],
unpack
(
$template
->[0],
$data
) ) );
}
else
{
$data
=
unpack
(
$template
,
$data
);
}
if
(
defined
(
$mask
) )
{
$data
&=
$mask
;
}
if
(
$op
eq
'='
)
{
$match
= (
$data
==
$testval
);
}
elsif
(
$op
eq
'x'
)
{
$match
= 1;
}
elsif
(
$op
eq
'!'
)
{
$match
= (
$data
!=
$testval
);
}
elsif
(
$op
eq
'&'
)
{
$match
= ( (
$data
&
$testval
) ==
$testval
);
}
elsif
(
$op
eq
'^'
)
{
$match
= ( ( ~
$data
&
$testval
) ==
$testval
);
}
elsif
(
$op
eq
'<'
)
{
$match
= (
$data
<
$testval
);
}
elsif
(
$op
eq
'>'
)
{
$match
= (
$data
>
$testval
);
}
if
(
$self
->check_magic )
{
print
( STDERR
"NUMERIC: $data $op $testval => $match\n"
);
}
}
if
(
$match
)
{
if
(
$message
=~ s/^\\b// )
{
$$p_desc
.= (
index
(
$message
,
'%s'
) != -1 ?
sprintf
(
$message
,
$data
) :
$message
);
}
else
{
$$p_desc
.= (
index
(
$message
,
'%s'
) != -1 ?
sprintf
(
$message
,
$data
) :
$message
)
if
(
$message
);
}
foreach
my
$subtest
(
@$subtests
)
{
$self
->_magic_match(
$subtest
,
$p_desc
,
$io
);
}
return
( 1 );
}
}
sub
_magic_match_str
{
my
$self
=
shift
(
@_
);
my
(
$item
,
$p_desc
,
$str
) =
@_
;
my
$origstr
=
$str
;
$item
=
$self
->parse_magic_line(
@$item
)
if
(
@$item
== 3 );
return
unless
(
defined
(
$item
) );
return
unless
(
defined
(
$str
) );
return
if
(
$str
eq
''
);
my
(
$offtype
,
$offset
,
$numbytes
,
$type
,
$mask
,
$op
,
$testval
,
$template
,
$message
,
$subtests
) =
@$item
;
return
unless
(
defined
(
$op
) );
my
$data
=
''
;
my
$match
= 0;
if
(
$offtype
== 1 )
{
my
(
$off1
,
$sz
,
$template
,
$off2
) =
@$offset
;
return
if
(
length
(
$str
) <
$off1
);
$data
=
pack
(
"a$sz"
,
$str
);
$off2
+=
unpack
(
$template
,
$data
);
return
if
(
length
(
$str
) <
$off2
);
}
elsif
(
$offtype
== 2 )
{
return
;
}
else
{
return
if
(
$offset
>
length
(
$str
) );
$str
=
substr
(
$str
,
$offset
);
}
if
(
$type
eq
'string'
)
{
if
(
$numbytes
> 0 )
{
$data
=
pack
(
"a$numbytes"
,
$str
);
}
else
{
$str
=~ /^(.*)\0|$/;
$data
= $1;
}
if
(
$op
eq
'='
)
{
$match
= (
$data
eq
$testval
);
}
elsif
(
$op
eq
'<'
)
{
$match
= (
$data
lt
$testval
);
}
elsif
(
$op
eq
'>'
)
{
$match
= (
$data
gt
$testval
);
}
if
(
$self
->check_magic )
{
print
( STDERR
"STRING: $data $op $testval => $match\n"
);
}
}
else
{
$data
=
substr
(
$str
, 0, 4 );
if
(
ref
(
$template
) )
{
$data
=
unpack
(
$template
->[2],
pack
(
$template
->[1],
unpack
(
$template
->[0],
$data
) ) );
}
else
{
$data
=
unpack
(
$template
,
$data
);
}
if
(
defined
(
$mask
) )
{
$data
&=
$mask
;
}
if
(
$op
eq
'='
)
{
$match
= (
$data
==
$testval
);
}
elsif
(
$op
eq
'x'
)
{
$match
= 1;
}
elsif
(
$op
eq
'!'
)
{
$match
= (
$data
!=
$testval
);
}
elsif
(
$op
eq
'&'
)
{
$match
= ( (
$data
&
$testval
) ==
$testval
);
}
elsif
(
$op
eq
'^'
)
{
$match
= ( ( ~
$data
&
$testval
) ==
$testval
);
}
elsif
(
$op
eq
'<'
)
{
$match
= (
$data
<
$testval
);
}
elsif
(
$op
eq
'>'
)
{
$match
= (
$data
>
$testval
);
}
if
(
$self
->check_magic )
{
print
( STDERR
"NUMERIC: $data $op $testval => $match\n"
);
}
}
if
(
$match
)
{
if
(
$message
=~ s/^\\b// )
{
$$p_desc
.=
sprintf
(
$message
,
$data
);
}
else
{
$$p_desc
.=
sprintf
(
$message
,
$data
)
if
(
$message
);
}
foreach
my
$subtest
(
@$subtests
)
{
$self
->_magic_match_str(
$subtest
,
$p_desc
,
$origstr
);
}
return
( 1 );
}
}
sub
add_specials
{
my
$self
=
shift
(
@_
);
my
$type
=
shift
(
@_
);
$self
->{SPECIALS}->{
$type
} = [
@_
];
return
(
$self
);
}
sub
add_file_exts
{
my
$self
=
shift
(
@_
);
my
$filepat
=
shift
(
@_
);
my
$type
=
shift
(
@_
);
$self
->{FILE_EXTS}->{
$filepat
} =
$type
;
return
(
$self
);
}
sub
add_magic_entry
{
my
$self
=
shift
(
@_
);
my
$entry
=
shift
(
@_
);
unshift
( @{
$self
->{magic_data}}, [
$entry
, -1, [] ] );
return
(
$self
);
}
1;
Hide Show 179 lines of Pod