2 package IkiWiki::Plugin::filecheck;
8 my %units=( #{{{ # size in bytes
37 # ikiwiki, if you find you need larger data quantities, either modify
38 # yourself to add them, or travel back in time to 2008 and kill me.
42 sub parsesize ($) { #{{{
46 my $base=$size+0; # force to number
48 foreach my $unit (sort keys %units) {
49 if ($size=~/[0-9\s]\Q$unit\E$/i) {
50 return $base * $units{$unit};
56 # This is provided for other plugins that want to convert back the other way.
57 sub humansize ($) { #{{{
60 foreach my $unit (reverse sort { $units{$a} <=> $units{$b} || $b cmp $a } keys %units) {
61 if ($size / $units{$unit} > 0.25) {
62 return (int($size / $units{$unit} * 10)/10).$unit;
65 return $size; # near zero, or negative
68 package IkiWiki::PageSpec;
70 sub match_maxsize ($$;@) { #{{{
72 my $maxsize=eval{IkiWiki::Plugin::filecheck::parsesize(shift)};
74 return IkiWiki::FailReason->new("unable to parse maxsize (or number too large)");
78 my $file=exists $params{file} ? $params{file} : $IkiWiki::pagesources{$page};
79 if (! defined $file) {
80 return IkiWiki::FailReason->new("no file specified");
83 if (-s $file > $maxsize) {
84 return IkiWiki::FailReason->new("file too large (".(-s $file)." > $maxsize)");
87 return IkiWiki::SuccessReason->new("file not too large");
91 sub match_minsize ($$;@) { #{{{
93 my $minsize=eval{IkiWiki::Plugin::filecheck::parsesize(shift)};
95 return IkiWiki::FailReason->new("unable to parse minsize (or number too large)");
99 my $file=exists $params{file} ? $params{file} : $IkiWiki::pagesources{$page};
100 if (! defined $file) {
101 return IkiWiki::FailReason->new("no file specified");
104 if (-s $file < $minsize) {
105 return IkiWiki::FailReason->new("file too small");
108 return IkiWiki::SuccessReason->new("file not too small");
112 sub match_mimetype ($$;@) { #{{{
117 my $file=exists $params{file} ? $params{file} : $IkiWiki::pagesources{$page};
118 if (! defined $file) {
119 return IkiWiki::FailReason->new("no file specified");
122 # Use ::magic to get the mime type, the idea is to only trust
123 # data obtained by examining the actual file contents.
124 eval q{use File::MimeInfo::Magic};
126 return IkiWiki::FailReason->new("failed to load File::MimeInfo::Magic ($@); cannot check MIME type");
128 my $mimetype=File::MimeInfo::Magic::magic($file);
129 if (! defined $mimetype) {
130 $mimetype=File::MimeInfo::Magic::default($file);
131 if (! defined $mimetype) {
136 my $regexp=IkiWiki::glob2re($wanted);
137 if ($mimetype!~/^$regexp$/i) {
138 return IkiWiki::FailReason->new("file MIME type is $mimetype, not $wanted");
141 return IkiWiki::SuccessReason->new("file MIME type is $mimetype");
145 sub match_virusfree ($$;@) { #{{{
150 my $file=exists $params{file} ? $params{file} : $IkiWiki::pagesources{$page};
151 if (! defined $file) {
152 return IkiWiki::FailReason->new("no file specified");
155 if (! exists $IkiWiki::config{virus_checker} ||
156 ! length $IkiWiki::config{virus_checker}) {
157 return IkiWiki::FailReason->new("no virus_checker configured");
160 # The file needs to be fed into the virus checker on stdin,
161 # because the file is not world-readable, and if clamdscan is
162 # used, clamd would fail to read it.
163 eval q{use IPC::Open2};
165 open (IN, "<", $file) || return IkiWiki::FailReason->new("failed to read file");
168 $SIG{PIPE} = sub { $sigpipe=1 };
169 my $pid=open2(\*CHECKER_OUT, "<&IN", $IkiWiki::config{virus_checker});
170 my $reason=<CHECKER_OUT>;
172 1 while (<CHECKER_OUT>);
175 $SIG{PIPE}="DEFAULT";
176 if ($sigpipe || $?) {
177 if (! length $reason) {
178 $reason="virus checker $IkiWiki::config{virus_checker}; failed with no output";
180 return IkiWiki::FailReason->new("file seems to contain a virus ($reason)");
183 return IkiWiki::SuccessReason->new("file seems virusfree ($reason)");
187 sub match_ispage ($$;@) { #{{{
190 if (defined IkiWiki::pagetype($filename)) {
191 return IkiWiki::SuccessReason->new("file is a wiki page");
194 return IkiWiki::FailReason->new("file is not a wiki page");