From 78e34fbdc255fd3f71f5b43ce1acd1fa0678e1ce Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Mon, 25 Aug 2008 19:52:34 -0400 Subject: [PATCH] filecheck: New plugin factoring out the PageSpec additions that were originally part of the attachment plugin. --- IkiWiki/Plugin/attachment.pm | 185 +---------------- IkiWiki/Plugin/filecheck.pm | 192 ++++++++++++++++++ debian/changelog | 2 + doc/ikiwiki/pagespec/attachment.mdwn | 2 +- .../pagespec/attachment/discussion.mdwn | 2 + doc/plugins/attachment.mdwn | 13 +- doc/plugins/filecheck.mdwn | 16 ++ 7 files changed, 217 insertions(+), 195 deletions(-) create mode 100644 IkiWiki/Plugin/filecheck.pm create mode 100644 doc/plugins/filecheck.mdwn diff --git a/IkiWiki/Plugin/attachment.pm b/IkiWiki/Plugin/attachment.pm index c78a1c177..f1f792a5a 100644 --- a/IkiWiki/Plugin/attachment.pm +++ b/IkiWiki/Plugin/attachment.pm @@ -10,6 +10,7 @@ sub import { #{{{ hook(type => "checkconfig", id => "attachment", call => \&checkconfig); hook(type => "formbuilder_setup", id => "attachment", call => \&formbuilder_setup); hook(type => "formbuilder", id => "attachment", call => \&formbuilder); + IkiWiki::loadplugin("filecheck"); } # }}} sub getsetup () { #{{{ @@ -288,192 +289,8 @@ sub attachment_list ($) { #{{{ return sort { $b->{mtime_raw} <=> $a->{mtime_raw} || $a->{link} cmp $b->{link} } @ret; } #}}} -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=; - chomp $reason; - 1 while (); - 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"); - } -} #}}} - sub match_user ($$;@) { #{{{ shift; my $user=shift; diff --git a/IkiWiki/Plugin/filecheck.pm b/IkiWiki/Plugin/filecheck.pm new file mode 100644 index 000000000..6f71be301 --- /dev/null +++ b/IkiWiki/Plugin/filecheck.pm @@ -0,0 +1,192 @@ +#!/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=; + chomp $reason; + 1 while (); + 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"); + } +} #}}} diff --git a/debian/changelog b/debian/changelog index 648a0b41c..c6f5fa6ee 100644 --- a/debian/changelog +++ b/debian/changelog @@ -14,6 +14,8 @@ ikiwiki (2.62) UNRELEASED; urgency=low chain will be used. (willu) * Drop suggests on texlive-science, add suggests on dvipng. * listdirectives: New plugin. (willu) + * filecheck: New plugin factoring out the PageSpec additions that were + originally part of the attachment plugin. -- Joey Hess Thu, 21 Aug 2008 16:20:58 -0400 diff --git a/doc/ikiwiki/pagespec/attachment.mdwn b/doc/ikiwiki/pagespec/attachment.mdwn index de7c7417f..6b86c510b 100644 --- a/doc/ikiwiki/pagespec/attachment.mdwn +++ b/doc/ikiwiki/pagespec/attachment.mdwn @@ -13,7 +13,7 @@ check all attachments for virii, something like this could be used: virusfree() and ((user(joey) and podcast/*.mp3 and mimetype(audio/mpeg) and maxsize(15mb)) or (!ispage() and maxsize(50kb))) -The regular [[ikiwiki/PageSpec]] syntax is expanded with thw following +The regular [[ikiwiki/PageSpec]] syntax is expanded with the following additional tests: * maxsize(size) diff --git a/doc/ikiwiki/pagespec/attachment/discussion.mdwn b/doc/ikiwiki/pagespec/attachment/discussion.mdwn index ab9cb0653..373242b3f 100644 --- a/doc/ikiwiki/pagespec/attachment/discussion.mdwn +++ b/doc/ikiwiki/pagespec/attachment/discussion.mdwn @@ -11,3 +11,5 @@ I am interested for [[todo/mbox]] --[[DavidBremner]] >> I don't think `ip()` and `user()` necessarily make sense for a mail box >> that is already on the disk, so no, I don't think I'll miss >> them. --[[DavidBremner]] + +>>> Done, [[plugins/filecheck]] --[[Joey]] diff --git a/doc/plugins/attachment.mdwn b/doc/plugins/attachment.mdwn index b04a04ad2..2aa8490ed 100644 --- a/doc/plugins/attachment.mdwn +++ b/doc/plugins/attachment.mdwn @@ -22,13 +22,6 @@ Bear in mind that if you let anyone upload a particular kind of file If you enable this plugin, be sure to lock it down, via the `allowed_attachments` setup file option. This is a special -[[enhanced_PageSpec|ikiwiki/pagespec/attachment]]. - -This plugin will use the [[!cpan File::MimeInfo::Magic]] perl module, if -available, for mimetype checking. - -The `virusfree` [[PageSpec|ikiwiki/pagespec/attachment]] requires that -ikiwiki be configured with a virus scanner program via the `virus_checker` -option in the setup file. If using `clamav`, with `clamd`, set it to -"clamdscan -". Or to use clamav without the `clamd` daemon, you -could set it to "clamscan -". +[[enhanced_PageSpec|ikiwiki/pagespec/attachment]] using tests provided by +the [[filecheck]] plugin. That plugin will be automatically enabled when +this plugin is enabled. diff --git a/doc/plugins/filecheck.mdwn b/doc/plugins/filecheck.mdwn new file mode 100644 index 000000000..f4563d58e --- /dev/null +++ b/doc/plugins/filecheck.mdwn @@ -0,0 +1,16 @@ +[[!template id=plugin name=filecheck core=0 author="[[Joey]]"]] +[[!tag type/useful]] + +This plugin enhances the regular [[ikiwiki/PageSpec]] syntax with +some additional tests, for things like file size, mime type, and virus +status. These tests are mostly useful for the [[attachment]] plugin, and +are documented [[here|ikiwiki/pagespec/attachment]]. + +This plugin will use the [[!cpan File::MimeInfo::Magic]] perl module, if +available, for mimetype checking. + +The `virusfree` [[PageSpec|ikiwiki/pagespec/attachment]] requires that +ikiwiki be configured with a virus scanner program via the `virus_checker` +option in the setup file. If using `clamav`, with `clamd`, set it to +"clamdscan -". Or to use clamav without the `clamd` daemon, you +could set it to "clamscan -". -- 2.44.0