#!/usr/bin/perl package IkiWiki::Plugin::filecheck; use warnings; use strict; use IkiWiki 3.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; } # This is provided for other plugins that want to convert back the other way. 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::filecheck::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::filecheck::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=File::MimeInfo::Magic::default($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"); } }