<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">#!/usr/bin/perl
package IkiWiki::Plugin::filecheck;

use warnings;
use strict;
use IkiWiki 3.00;

my %units=(		# size in bytes
	B		=&gt; 1,
	byte		=&gt; 1,
	KB		=&gt; 2 ** 10,
	kilobyte 	=&gt; 2 ** 10,
	K		=&gt; 2 ** 10,
	KB		=&gt; 2 ** 10,
	kilobyte 	=&gt; 2 ** 10,
	M		=&gt; 2 ** 20,
	MB		=&gt; 2 ** 20,
	megabyte	=&gt; 2 ** 20,
	G		=&gt; 2 ** 30,
	GB		=&gt; 2 ** 30,
	gigabyte	=&gt; 2 ** 30,
	T		=&gt; 2 ** 40,
	TB		=&gt; 2 ** 40,
	terabyte	=&gt; 2 ** 40,
	P		=&gt; 2 ** 50,
	PB		=&gt; 2 ** 50,
	petabyte	=&gt; 2 ** 50,
	E		=&gt; 2 ** 60,
	EB		=&gt; 2 ** 60,
	exabyte		=&gt; 2 ** 60,
	Z		=&gt; 2 ** 70,
	ZB		=&gt; 2 ** 70,
	zettabyte	=&gt; 2 ** 70,
	Y		=&gt; 2 ** 80,
	YB		=&gt; 2 ** 80,
	yottabyte	=&gt; 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 import {
	hook(type =&gt; "getsetup", id =&gt; "filecheck",  call =&gt; \&amp;getsetup);
}

sub getsetup () {
	return
		plugin =&gt; {
			safe =&gt; 1,
			rebuild =&gt; undef,
		},
}

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} &lt;=&gt; $units{$b} || $b cmp $a } keys %units) {
		if ($size / $units{$unit} &gt; 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::ErrorReason-&gt;new("unable to parse maxsize (or number too large)");
	}

	my %params=@_;
	my $file=exists $params{file} ? $params{file} : IkiWiki::srcfile($IkiWiki::pagesources{$page});
	if (! defined $file) {
		return IkiWiki::ErrorReason-&gt;new("file does not exist");
	}

	if (-s $file &gt; $maxsize) {
		return IkiWiki::FailReason-&gt;new("file too large (".(-s $file)." &gt;  $maxsize)");
	}
	else {
		return IkiWiki::SuccessReason-&gt;new("file not too large");
	}
}

sub match_minsize ($$;@) {
	my $page=shift;
	my $minsize=eval{IkiWiki::Plugin::filecheck::parsesize(shift)};
	if ($@) {
		return IkiWiki::ErrorReason-&gt;new("unable to parse minsize (or number too large)");
	}

	my %params=@_;
	my $file=exists $params{file} ? $params{file} : IkiWiki::srcfile($IkiWiki::pagesources{$page});
	if (! defined $file) {
		return IkiWiki::ErrorReason-&gt;new("file does not exist");
	}

	if (-s $file &lt; $minsize) {
		return IkiWiki::FailReason-&gt;new("file too small");
	}
	else {
		return IkiWiki::SuccessReason-&gt;new("file not too small");
	}
}

sub match_mimetype ($$;@) {
	my $page=shift;
	my $wanted=shift;

	my %params=@_;
	my $file=exists $params{file} ? $params{file} : IkiWiki::srcfile($IkiWiki::pagesources{$page});
	if (! defined $file) {
		return IkiWiki::ErrorReason-&gt;new("file does not exist");
	}

	# Get the mime type.
	#
	# First, try File::Mimeinfo. This is fast, but doesn't recognise
	# all files.
	eval q{use File::MimeInfo::Magic};
	my $mimeinfo_ok=! $@;
	my $mimetype;
	if ($mimeinfo_ok) {
		$mimetype=File::MimeInfo::Magic::magic($file);
	}

	# Fall back to using file, which has a more complete
	# magic database.
	if (! defined $mimetype) {
		open(my $file_h, "-|", "file", "-bi", $file);
		$mimetype=&lt;$file_h&gt;;
		chomp $mimetype;
		close $file_h;
	}
	if (! defined $mimetype) {
		# Fall back to default value.
		$mimetype=File::MimeInfo::Magic::default($file)
			if $mimeinfo_ok;
		if (! defined $mimetype) {
			$mimetype="unknown";
		}
	}
	# Ignore any parameters, we only want the type itself
	$mimetype =~ s/;.*//;

	my $regexp=IkiWiki::glob2re($wanted);
	if ($mimetype!~$regexp) {
		return IkiWiki::FailReason-&gt;new("file MIME type is $mimetype, not $wanted");
	}
	else {
		return IkiWiki::SuccessReason-&gt;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::srcfile($IkiWiki::pagesources{$page});
	if (! defined $file) {
		return IkiWiki::ErrorReason-&gt;new("file does not exist");
	}

	if (! exists $IkiWiki::config{virus_checker} ||
	    ! length $IkiWiki::config{virus_checker}) {
		return IkiWiki::ErrorReason-&gt;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, "&lt;", $file) || return IkiWiki::ErrorReason-&gt;new("failed to read file");
	binmode(IN);
	my $sigpipe=0;
	$SIG{PIPE} = sub { $sigpipe=1 };
	my $pid=open2(\*CHECKER_OUT, "&lt;&amp;IN", $IkiWiki::config{virus_checker}); 
	my $reason=&lt;CHECKER_OUT&gt;;
	chomp $reason;
	1 while (&lt;CHECKER_OUT&gt;);
	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-&gt;new("file seems to contain a virus ($reason)");
	}
	else {
		return IkiWiki::SuccessReason-&gt;new("file seems virusfree ($reason)");
	}
}

sub match_ispage ($$;@) {
	my $filename=shift;

	if (defined IkiWiki::pagetype($filename)) {
		return IkiWiki::SuccessReason-&gt;new("file is a wiki page");
	}
	else {
		return IkiWiki::FailReason-&gt;new("file is not a wiki page");
	}
}
</pre></body></html>