193 lines
4.8 KiB
Perl
193 lines
4.8 KiB
Perl
#!/usr/bin/perl
|
|
package IkiWiki::Plugin::filecheck;
|
|
|
|
use warnings;
|
|
use strict;
|
|
use IkiWiki 2.00;
|
|
|
|
my %units=( #{{{ # size in bytes
|
|
B => 1,
|
|
byte => 1,
|
|
KB => 2 ** 10,
|
|
kilobyte => 2 ** 10,
|
|
K => 2 ** 10,
|
|
KB => 2 ** 10,
|
|
kilobyte => 2 ** 10,
|
|
M => 2 ** 20,
|
|
MB => 2 ** 20,
|
|
megabyte => 2 ** 20,
|
|
G => 2 ** 30,
|
|
GB => 2 ** 30,
|
|
gigabyte => 2 ** 30,
|
|
T => 2 ** 40,
|
|
TB => 2 ** 40,
|
|
terabyte => 2 ** 40,
|
|
P => 2 ** 50,
|
|
PB => 2 ** 50,
|
|
petabyte => 2 ** 50,
|
|
E => 2 ** 60,
|
|
EB => 2 ** 60,
|
|
exabyte => 2 ** 60,
|
|
Z => 2 ** 70,
|
|
ZB => 2 ** 70,
|
|
zettabyte => 2 ** 70,
|
|
Y => 2 ** 80,
|
|
YB => 2 ** 80,
|
|
yottabyte => 2 ** 80,
|
|
# ikiwiki, if you find you need larger data quantities, either modify
|
|
# yourself to add them, or travel back in time to 2008 and kill me.
|
|
# -- Joey
|
|
); #}}}
|
|
|
|
sub parsesize ($) { #{{{
|
|
my $size=shift;
|
|
|
|
no warnings;
|
|
my $base=$size+0; # force to number
|
|
use warnings;
|
|
foreach my $unit (sort keys %units) {
|
|
if ($size=~/[0-9\s]\Q$unit\E$/i) {
|
|
return $base * $units{$unit};
|
|
}
|
|
}
|
|
return $base;
|
|
} #}}}
|
|
|
|
sub humansize ($) { #{{{
|
|
my $size=shift;
|
|
|
|
foreach my $unit (reverse sort { $units{$a} <=> $units{$b} || $b cmp $a } keys %units) {
|
|
if ($size / $units{$unit} > 0.25) {
|
|
return (int($size / $units{$unit} * 10)/10).$unit;
|
|
}
|
|
}
|
|
return $size; # near zero, or negative
|
|
} #}}}
|
|
|
|
package IkiWiki::PageSpec;
|
|
|
|
sub match_maxsize ($$;@) { #{{{
|
|
my $page=shift;
|
|
my $maxsize=eval{IkiWiki::Plugin::attachment::parsesize(shift)};
|
|
if ($@) {
|
|
return IkiWiki::FailReason->new("unable to parse maxsize (or number too large)");
|
|
}
|
|
|
|
my %params=@_;
|
|
my $file=exists $params{file} ? $params{file} : $IkiWiki::pagesources{$page};
|
|
if (! defined $file) {
|
|
return IkiWiki::FailReason->new("no file specified");
|
|
}
|
|
|
|
if (-s $file > $maxsize) {
|
|
return IkiWiki::FailReason->new("file too large (".(-s $file)." > $maxsize)");
|
|
}
|
|
else {
|
|
return IkiWiki::SuccessReason->new("file not too large");
|
|
}
|
|
} #}}}
|
|
|
|
sub match_minsize ($$;@) { #{{{
|
|
my $page=shift;
|
|
my $minsize=eval{IkiWiki::Plugin::attachment::parsesize(shift)};
|
|
if ($@) {
|
|
return IkiWiki::FailReason->new("unable to parse minsize (or number too large)");
|
|
}
|
|
|
|
my %params=@_;
|
|
my $file=exists $params{file} ? $params{file} : $IkiWiki::pagesources{$page};
|
|
if (! defined $file) {
|
|
return IkiWiki::FailReason->new("no file specified");
|
|
}
|
|
|
|
if (-s $file < $minsize) {
|
|
return IkiWiki::FailReason->new("file too small");
|
|
}
|
|
else {
|
|
return IkiWiki::SuccessReason->new("file not too small");
|
|
}
|
|
} #}}}
|
|
|
|
sub match_mimetype ($$;@) { #{{{
|
|
my $page=shift;
|
|
my $wanted=shift;
|
|
|
|
my %params=@_;
|
|
my $file=exists $params{file} ? $params{file} : $IkiWiki::pagesources{$page};
|
|
if (! defined $file) {
|
|
return IkiWiki::FailReason->new("no file specified");
|
|
}
|
|
|
|
# Use ::magic to get the mime type, the idea is to only trust
|
|
# data obtained by examining the actual file contents.
|
|
eval q{use File::MimeInfo::Magic};
|
|
if ($@) {
|
|
return IkiWiki::FailReason->new("failed to load File::MimeInfo::Magic ($@); cannot check MIME type");
|
|
}
|
|
my $mimetype=File::MimeInfo::Magic::magic($file);
|
|
if (! defined $mimetype) {
|
|
$mimetype="unknown";
|
|
}
|
|
|
|
my $regexp=IkiWiki::glob2re($wanted);
|
|
if ($mimetype!~/^$regexp$/i) {
|
|
return IkiWiki::FailReason->new("file MIME type is $mimetype, not $wanted");
|
|
}
|
|
else {
|
|
return IkiWiki::SuccessReason->new("file MIME type is $mimetype");
|
|
}
|
|
} #}}}
|
|
|
|
sub match_virusfree ($$;@) { #{{{
|
|
my $page=shift;
|
|
my $wanted=shift;
|
|
|
|
my %params=@_;
|
|
my $file=exists $params{file} ? $params{file} : $IkiWiki::pagesources{$page};
|
|
if (! defined $file) {
|
|
return IkiWiki::FailReason->new("no file specified");
|
|
}
|
|
|
|
if (! exists $IkiWiki::config{virus_checker} ||
|
|
! length $IkiWiki::config{virus_checker}) {
|
|
return IkiWiki::FailReason->new("no virus_checker configured");
|
|
}
|
|
|
|
# The file needs to be fed into the virus checker on stdin,
|
|
# because the file is not world-readable, and if clamdscan is
|
|
# used, clamd would fail to read it.
|
|
eval q{use IPC::Open2};
|
|
error($@) if $@;
|
|
open (IN, "<", $file) || return IkiWiki::FailReason->new("failed to read file");
|
|
binmode(IN);
|
|
my $sigpipe=0;
|
|
$SIG{PIPE} = sub { $sigpipe=1 };
|
|
my $pid=open2(\*CHECKER_OUT, "<&IN", $IkiWiki::config{virus_checker});
|
|
my $reason=<CHECKER_OUT>;
|
|
chomp $reason;
|
|
1 while (<CHECKER_OUT>);
|
|
close(CHECKER_OUT);
|
|
waitpid $pid, 0;
|
|
$SIG{PIPE}="DEFAULT";
|
|
if ($sigpipe || $?) {
|
|
if (! length $reason) {
|
|
$reason="virus checker $IkiWiki::config{virus_checker}; failed with no output";
|
|
}
|
|
return IkiWiki::FailReason->new("file seems to contain a virus ($reason)");
|
|
}
|
|
else {
|
|
return IkiWiki::SuccessReason->new("file seems virusfree ($reason)");
|
|
}
|
|
} #}}}
|
|
|
|
sub match_ispage ($$;@) { #{{{
|
|
my $filename=shift;
|
|
|
|
if (defined IkiWiki::pagetype($filename)) {
|
|
return IkiWiki::SuccessReason->new("file is a wiki page");
|
|
}
|
|
else {
|
|
return IkiWiki::FailReason->new("file is not a wiki page");
|
|
}
|
|
} #}}}
|