]> sipb.mit.edu Git - ikiwiki.git/blob - IkiWiki/Plugin/filecheck.pm
git: Fix gitweb historyurl examples so "diff to current" links work. (Thanks jrayhawk)
[ikiwiki.git] / IkiWiki / Plugin / filecheck.pm
1 #!/usr/bin/perl
2 package IkiWiki::Plugin::filecheck;
3
4 use warnings;
5 use strict;
6 use IkiWiki 3.00;
7
8 my %units=(             # size in bytes
9         B               => 1,
10         byte            => 1,
11         KB              => 2 ** 10,
12         kilobyte        => 2 ** 10,
13         K               => 2 ** 10,
14         KB              => 2 ** 10,
15         kilobyte        => 2 ** 10,
16         M               => 2 ** 20,
17         MB              => 2 ** 20,
18         megabyte        => 2 ** 20,
19         G               => 2 ** 30,
20         GB              => 2 ** 30,
21         gigabyte        => 2 ** 30,
22         T               => 2 ** 40,
23         TB              => 2 ** 40,
24         terabyte        => 2 ** 40,
25         P               => 2 ** 50,
26         PB              => 2 ** 50,
27         petabyte        => 2 ** 50,
28         E               => 2 ** 60,
29         EB              => 2 ** 60,
30         exabyte         => 2 ** 60,
31         Z               => 2 ** 70,
32         ZB              => 2 ** 70,
33         zettabyte       => 2 ** 70,
34         Y               => 2 ** 80,
35         YB              => 2 ** 80,
36         yottabyte       => 2 ** 80,
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.
39         #   -- Joey
40 );
41
42 sub import {
43         hook(type => "getsetup", id => "filecheck",  call => \&getsetup);
44 }
45
46 sub getsetup () {
47         return
48                 plugin => {
49                         safe => 1,
50                         rebuild => undef,
51                         section => "misc",
52                 },
53 }
54
55 sub parsesize ($) {
56         my $size=shift;
57
58         no warnings;
59         my $base=$size+0; # force to number
60         use warnings;
61         foreach my $unit (sort keys %units) {
62                 if ($size=~/[0-9\s]\Q$unit\E$/i) {
63                         return $base * $units{$unit};
64                 }
65         }
66         return $base;
67 }
68
69 # This is provided for other plugins that want to convert back the other way.
70 sub humansize ($) {
71         my $size=shift;
72
73         foreach my $unit (reverse sort { $units{$a} <=> $units{$b} || $b cmp $a } keys %units) {
74                 if ($size / $units{$unit} > 0.25) {
75                         return (int($size / $units{$unit} * 10)/10).$unit;
76                 }
77         }
78         return $size; # near zero, or negative
79 }
80
81 package IkiWiki::PageSpec;
82
83 sub match_maxsize ($$;@) {
84         my $page=shift;
85         my $maxsize=eval{IkiWiki::Plugin::filecheck::parsesize(shift)};
86         if ($@) {
87                 return IkiWiki::ErrorReason->new("unable to parse maxsize (or number too large)");
88         }
89
90         my %params=@_;
91         my $file=exists $params{file} ? $params{file} : IkiWiki::srcfile($IkiWiki::pagesources{$page});
92         if (! defined $file) {
93                 return IkiWiki::ErrorReason->new("file does not exist");
94         }
95
96         if (-s $file > $maxsize) {
97                 return IkiWiki::FailReason->new("file too large (".(-s $file)." >  $maxsize)");
98         }
99         else {
100                 return IkiWiki::SuccessReason->new("file not too large");
101         }
102 }
103
104 sub match_minsize ($$;@) {
105         my $page=shift;
106         my $minsize=eval{IkiWiki::Plugin::filecheck::parsesize(shift)};
107         if ($@) {
108                 return IkiWiki::ErrorReason->new("unable to parse minsize (or number too large)");
109         }
110
111         my %params=@_;
112         my $file=exists $params{file} ? $params{file} : IkiWiki::srcfile($IkiWiki::pagesources{$page});
113         if (! defined $file) {
114                 return IkiWiki::ErrorReason->new("file does not exist");
115         }
116
117         if (-s $file < $minsize) {
118                 return IkiWiki::FailReason->new("file too small");
119         }
120         else {
121                 return IkiWiki::SuccessReason->new("file not too small");
122         }
123 }
124
125 sub match_mimetype ($$;@) {
126         my $page=shift;
127         my $wanted=shift;
128
129         my %params=@_;
130         my $file=exists $params{file} ? $params{file} : IkiWiki::srcfile($IkiWiki::pagesources{$page});
131         if (! defined $file) {
132                 return IkiWiki::ErrorReason->new("file does not exist");
133         }
134
135         # Use ::magic to get the mime type, the idea is to only trust
136         # data obtained by examining the actual file contents.
137         eval q{use File::MimeInfo::Magic};
138         if ($@) {
139                 return IkiWiki::ErrorReason->new("failed to load File::MimeInfo::Magic ($@); cannot check MIME type");
140         }
141         my $mimetype=File::MimeInfo::Magic::magic($file);
142         if (! defined $mimetype) {
143                 $mimetype=File::MimeInfo::Magic::default($file);
144                 if (! defined $mimetype) {
145                         $mimetype="unknown";
146                 }
147         }
148
149         my $regexp=IkiWiki::glob2re($wanted);
150         if ($mimetype!~/^$regexp$/i) {
151                 return IkiWiki::FailReason->new("file MIME type is $mimetype, not $wanted");
152         }
153         else {
154                 return IkiWiki::SuccessReason->new("file MIME type is $mimetype");
155         }
156 }
157
158 sub match_virusfree ($$;@) {
159         my $page=shift;
160         my $wanted=shift;
161
162         my %params=@_;
163         my $file=exists $params{file} ? $params{file} : IkiWiki::srcfile($IkiWiki::pagesources{$page});
164         if (! defined $file) {
165                 return IkiWiki::ErrorReason->new("file does not exist");
166         }
167
168         if (! exists $IkiWiki::config{virus_checker} ||
169             ! length $IkiWiki::config{virus_checker}) {
170                 return IkiWiki::ErrorReason->new("no virus_checker configured");
171         }
172
173         # The file needs to be fed into the virus checker on stdin,
174         # because the file is not world-readable, and if clamdscan is
175         # used, clamd would fail to read it.
176         eval q{use IPC::Open2};
177         error($@) if $@;
178         open (IN, "<", $file) || return IkiWiki::ErrorReason->new("failed to read file");
179         binmode(IN);
180         my $sigpipe=0;
181         $SIG{PIPE} = sub { $sigpipe=1 };
182         my $pid=open2(\*CHECKER_OUT, "<&IN", $IkiWiki::config{virus_checker}); 
183         my $reason=<CHECKER_OUT>;
184         chomp $reason;
185         1 while (<CHECKER_OUT>);
186         close(CHECKER_OUT);
187         waitpid $pid, 0;
188         $SIG{PIPE}="DEFAULT";
189         if ($sigpipe || $?) {
190                 if (! length $reason) {
191                         $reason="virus checker $IkiWiki::config{virus_checker}; failed with no output";
192                 }
193                 return IkiWiki::FailReason->new("file seems to contain a virus ($reason)");
194         }
195         else {
196                 return IkiWiki::SuccessReason->new("file seems virusfree ($reason)");
197         }
198 }
199
200 sub match_ispage ($$;@) {
201         my $filename=shift;
202
203         if (defined IkiWiki::pagetype($filename)) {
204                 return IkiWiki::SuccessReason->new("file is a wiki page");
205         }
206         else {
207                 return IkiWiki::FailReason->new("file is not a wiki page");
208         }
209 }